mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-19 07:16:01 +00:00
Merge branch 'master' into sqlite
This commit is contained in:
@@ -19,6 +19,13 @@
|
||||
onConfirm: () => axiosInstance.post('connections/delete', data),
|
||||
});
|
||||
};
|
||||
const handleDuplicate = () => {
|
||||
axiosInstance.post('connections/save', {
|
||||
...data,
|
||||
_id: undefined,
|
||||
displayName: `${data.displayName || data.server} - copy`,
|
||||
});
|
||||
};
|
||||
const handleCreateDatabase = () => {
|
||||
showModal(InputTextModal, {
|
||||
header: 'Create database',
|
||||
@@ -54,6 +61,10 @@
|
||||
text: 'Delete',
|
||||
onClick: handleDelete,
|
||||
},
|
||||
{
|
||||
text: 'Duplicate',
|
||||
onClick: handleDuplicate,
|
||||
},
|
||||
],
|
||||
!data.singleDatabase && [
|
||||
!$openedConnections.includes(data._id) && {
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface GlobalCommand {
|
||||
menuName?: string;
|
||||
toolbarOrder?: number;
|
||||
disableHandleKeyText?: string;
|
||||
isRelatedToTab?: boolean,
|
||||
}
|
||||
|
||||
export default function registerCommand(command: GlobalCommand) {
|
||||
|
||||
@@ -251,6 +251,7 @@ export function registerFileCommands({
|
||||
// keyText: 'Ctrl+S',
|
||||
icon: 'icon save',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
testEnabled: () => getCurrentEditor() != null,
|
||||
onClick: () => saveTabFile(getCurrentEditor(), false, folder, format, fileExtension),
|
||||
});
|
||||
@@ -271,6 +272,7 @@ export function registerFileCommands({
|
||||
name: 'Execute',
|
||||
icon: 'icon run',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
keyText: 'F5 | Ctrl+Enter',
|
||||
testEnabled: () => getCurrentEditor() != null && !getCurrentEditor()?.isBusy(),
|
||||
onClick: () => getCurrentEditor().execute(),
|
||||
@@ -281,6 +283,7 @@ export function registerFileCommands({
|
||||
name: 'Kill',
|
||||
icon: 'icon close',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
testEnabled: () => getCurrentEditor()?.canKill && getCurrentEditor().canKill(),
|
||||
onClick: () => getCurrentEditor().kill(),
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
name: 'Refresh',
|
||||
keyText: 'F5',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
icon: 'icon reload',
|
||||
testEnabled: () => getCurrentDataGrid()?.getDisplay()?.supportsReload,
|
||||
onClick: () => getCurrentDataGrid().refresh(),
|
||||
@@ -63,6 +64,7 @@
|
||||
group: 'undo',
|
||||
icon: 'icon undo',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
testEnabled: () => getCurrentDataGrid()?.getGrider()?.canUndo,
|
||||
onClick: () => getCurrentDataGrid().undo(),
|
||||
});
|
||||
@@ -74,6 +76,7 @@
|
||||
group: 'redo',
|
||||
icon: 'icon redo',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
testEnabled: () => getCurrentDataGrid()?.getGrider()?.canRedo,
|
||||
onClick: () => getCurrentDataGrid().redo(),
|
||||
});
|
||||
|
||||
@@ -54,6 +54,9 @@ export function countColumnSizes(grider: Grider, columns, containerWidth, displa
|
||||
context.font = '14px Helvetica';
|
||||
for (let rowIndex = 0; rowIndex < Math.min(grider.rowCount, 20); rowIndex += 1) {
|
||||
const row = grider.getRowData(rowIndex);
|
||||
if (!row) {
|
||||
continue;
|
||||
}
|
||||
for (let colIndex = 0; colIndex < columns.length; colIndex++) {
|
||||
const uqName = columns[colIndex].uniqueName;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
name: 'Refresh',
|
||||
keyText: 'F5',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
icon: 'icon reload',
|
||||
testEnabled: () => getCurrentDataForm() != null,
|
||||
onClick: () => getCurrentDataForm().refresh(),
|
||||
@@ -58,6 +59,7 @@
|
||||
group: 'undo',
|
||||
icon: 'icon undo',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
testEnabled: () => getCurrentDataForm()?.getFormer()?.canUndo,
|
||||
onClick: () => getCurrentDataForm().getFormer().undo(),
|
||||
});
|
||||
@@ -69,6 +71,7 @@
|
||||
group: 'redo',
|
||||
icon: 'icon redo',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
testEnabled: () => getCurrentDataForm()?.getFormer()?.canRedo,
|
||||
onClick: () => getCurrentDataForm().getFormer().redo(),
|
||||
});
|
||||
@@ -104,6 +107,7 @@
|
||||
name: 'First',
|
||||
keyText: 'Ctrl+Home',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
icon: 'icon arrow-begin',
|
||||
testEnabled: () => getCurrentDataForm() != null,
|
||||
onClick: () => getCurrentDataForm().navigate('begin'),
|
||||
@@ -115,6 +119,7 @@
|
||||
name: 'Previous',
|
||||
keyText: 'Ctrl+ArrowUp',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
icon: 'icon arrow-left',
|
||||
testEnabled: () => getCurrentDataForm() != null,
|
||||
onClick: () => getCurrentDataForm().navigate('previous'),
|
||||
@@ -126,6 +131,7 @@
|
||||
name: 'Next',
|
||||
keyText: 'Ctrl+ArrowDown',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
icon: 'icon arrow-right',
|
||||
testEnabled: () => getCurrentDataForm() != null,
|
||||
onClick: () => getCurrentDataForm().navigate('next'),
|
||||
@@ -137,6 +143,7 @@
|
||||
name: 'Last',
|
||||
keyText: 'Ctrl+End',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
icon: 'icon arrow-end',
|
||||
testEnabled: () => getCurrentDataForm() != null,
|
||||
onClick: () => getCurrentDataForm().navigate('end'),
|
||||
|
||||
@@ -15,11 +15,7 @@
|
||||
const { values, setFieldValue } = getFormContext();
|
||||
$: dbinfo = useDatabaseInfo({ conid: $values[conidName], database: $values[databaseName] });
|
||||
|
||||
$: tablesOptions = [
|
||||
...(($dbinfo && $dbinfo.tables) || []),
|
||||
...(($dbinfo && $dbinfo.views) || []),
|
||||
...(($dbinfo && $dbinfo.collections) || []),
|
||||
]
|
||||
$: tablesOptions = _.compact([...($dbinfo?.tables || []), ...($dbinfo?.views || []), ...($dbinfo?.collections || [])])
|
||||
.filter(x => !$values[schemaName] || x.schemaName == $values[schemaName])
|
||||
.map(x => ({
|
||||
value: x.pureName,
|
||||
@@ -31,18 +27,20 @@
|
||||
<FormSelectField {...$$restProps} {name} options={tablesOptions} isMulti templateProps={{ noMargin: true }} />
|
||||
|
||||
<div>
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value="All tables"
|
||||
on:click={() =>
|
||||
setFieldValue(name, _.uniq([...($values[name] || []), ...($dbinfo && $dbinfo.tables.map(x => x.pureName))]))}
|
||||
/>
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value="All views"
|
||||
on:click={() =>
|
||||
setFieldValue(name, _.uniq([...($values[name] || []), ...($dbinfo && $dbinfo.views.map(x => x.pureName))]))}
|
||||
/>
|
||||
{#each ['tables', 'views', 'collections'] as field}
|
||||
{#if $dbinfo && $dbinfo[field]?.length > 0}
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value={`All ${field}`}
|
||||
on:click={() =>
|
||||
setFieldValue(
|
||||
name,
|
||||
_.compact(_.uniq([...($values[name] || []), ...($dbinfo[field]?.map(x => x.pureName) || [])]))
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<FormStyledButton type="button" value="Remove all" on:click={() => setFieldValue(name, [])} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#each plugins as packageManifest (packageManifest.name)}
|
||||
{#each plugins || [] as packageManifest (packageManifest.name)}
|
||||
<div class="wrapper" on:click={() => openPlugin(packageManifest)}>
|
||||
<img class="icon" src={extractPluginIcon(packageManifest)} />
|
||||
<div class="ml-2">
|
||||
|
||||
@@ -11,10 +11,15 @@
|
||||
import 'ace-builds/src-noconflict/mode-json';
|
||||
import 'ace-builds/src-noconflict/mode-javascript';
|
||||
import 'ace-builds/src-noconflict/mode-markdown';
|
||||
import 'ace-builds/src-noconflict/theme-github';
|
||||
import 'ace-builds/src-noconflict/theme-twilight';
|
||||
import 'ace-builds/src-noconflict/ext-searchbox';
|
||||
import 'ace-builds/src-noconflict/ext-language_tools';
|
||||
|
||||
import 'ace-builds/src-noconflict/theme-github';
|
||||
// import 'ace-builds/src-noconflict/theme-sqlserver';
|
||||
|
||||
import 'ace-builds/src-noconflict/theme-twilight';
|
||||
// import 'ace-builds/src-noconflict/theme-monokai';
|
||||
|
||||
import { currentDropDownMenu, currentThemeDefinition } from '../stores';
|
||||
import _ from 'lodash';
|
||||
import { handleCommandKeyDown } from '../commands/CommandListener.svelte';
|
||||
|
||||
@@ -19,6 +19,8 @@ function getParsedLocalStorage(key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const saveHandlersList = [];
|
||||
|
||||
export default function useEditorData({ tabid, reloadToken = 0, loadFromArgs = null, onInitialData = null }) {
|
||||
const localStorageKey = `tabdata_editor_${tabid}`;
|
||||
let changeCounter = 0;
|
||||
@@ -90,6 +92,11 @@ export default function useEditorData({ tabid, reloadToken = 0, loadFromArgs = n
|
||||
}));
|
||||
};
|
||||
|
||||
const saveToStorageIfNeeded = async () => {
|
||||
if (savedCounter == changeCounter) return; // all saved
|
||||
await saveToStorage();
|
||||
};
|
||||
|
||||
const saveToStorage = async () => {
|
||||
if (value == null) return;
|
||||
try {
|
||||
@@ -128,11 +135,13 @@ export default function useEditorData({ tabid, reloadToken = 0, loadFromArgs = n
|
||||
onMount(() => {
|
||||
window.addEventListener('beforeunload', saveToStorageSync);
|
||||
initialLoad();
|
||||
saveHandlersList.push(saveToStorageIfNeeded);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
saveToStorage();
|
||||
window.removeEventListener('beforeunload', saveToStorageSync);
|
||||
_.remove(saveHandlersList, x => x == saveToStorageIfNeeded);
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -144,3 +153,9 @@ export default function useEditorData({ tabid, reloadToken = 0, loadFromArgs = n
|
||||
initialLoad,
|
||||
};
|
||||
}
|
||||
|
||||
export async function saveAllPendingEditorData() {
|
||||
for (const item of saveHandlersList) {
|
||||
await item();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import FormProvider from '../forms/FormProvider.svelte';
|
||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||
import FormTextField from '../forms/FormTextField.svelte';
|
||||
import FormValues from '../forms/FormValues.svelte';
|
||||
|
||||
import ModalBase from '../modals/ModalBase.svelte';
|
||||
import { closeCurrentModal } from '../modals/modalTools';
|
||||
@@ -32,17 +33,32 @@
|
||||
<ModalBase {...$$restProps}>
|
||||
<div slot="header">Settings</div>
|
||||
|
||||
<div class="heading">Appearance</div>
|
||||
<FormCheckboxField name=":visibleToolbar" label="Show toolbar" defaultValue={true} />
|
||||
<FormValues let:values>
|
||||
<div class="heading">Appearance</div>
|
||||
<FormCheckboxField name=":visibleToolbar" label="Show toolbar" defaultValue={true} />
|
||||
|
||||
<div class="heading">Data grid</div>
|
||||
<FormCheckboxField name="dataGrid.hideLeftColumn" label="Hide left column by default" />
|
||||
<FormTextField
|
||||
name="dataGrid.pageSize"
|
||||
label="Page size (number of rows for incremental loading, must be between 5 and 1000)"
|
||||
defaultValue="100"
|
||||
/>
|
||||
<FormCheckboxField name="dataGrid.showHintColumns" label="Show foreign key hints" defaultValue={true} />
|
||||
<div class="heading">Data grid</div>
|
||||
<FormCheckboxField name="dataGrid.hideLeftColumn" label="Hide left column by default" />
|
||||
<FormTextField
|
||||
name="dataGrid.pageSize"
|
||||
label="Page size (number of rows for incremental loading, must be between 5 and 1000)"
|
||||
defaultValue="100"
|
||||
/>
|
||||
<FormCheckboxField name="dataGrid.showHintColumns" label="Show foreign key hints" defaultValue={true} />
|
||||
|
||||
<div class="heading">Connection</div>
|
||||
<FormCheckboxField
|
||||
name="connection.autoRefresh"
|
||||
label="Automatic refresh of database model on background"
|
||||
defaultValue={true}
|
||||
/>
|
||||
<FormTextField
|
||||
name="connection.autoRefreshInterval"
|
||||
label="Interval between automatic refreshes in seconds"
|
||||
defaultValue="30"
|
||||
disabled={values['connection.autoRefresh'] === false}
|
||||
/>
|
||||
</FormValues>
|
||||
|
||||
<div slot="footer">
|
||||
<FormSubmit value="OK" on:click={handleOk} />
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
name: 'Save',
|
||||
// keyText: 'Ctrl+S',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
icon: 'icon save',
|
||||
testEnabled: () => getCurrentEditor()?.canSave(),
|
||||
onClick: () => getCurrentEditor().save(),
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
name: 'Save',
|
||||
// keyText: 'Ctrl+S',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
icon: 'icon save',
|
||||
testEnabled: () => getCurrentEditor() != null,
|
||||
onClick: () => getCurrentEditor().save(),
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
name: 'Preview',
|
||||
icon: 'icon run',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
keyText: 'F5 | Ctrl+Enter',
|
||||
testEnabled: () => getCurrentEditor() != null,
|
||||
onClick: () => getCurrentEditor().preview(),
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
name: 'Save',
|
||||
// keyText: 'Ctrl+S',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
icon: 'icon save',
|
||||
testEnabled: () => getCurrentEditor()?.canSave(),
|
||||
onClick: () => getCurrentEditor().save(),
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
console.log('CRASH DETECTED!!!');
|
||||
const lastDbGateCrashJson = localStorage.getItem('lastDbGateCrash');
|
||||
const lastDbGateCrash = lastDbGateCrashJson ? JSON.parse(lastDbGateCrashJson) : null;
|
||||
// let detail = e?.reason?.stack || '';
|
||||
// if (detail) detail = '\n\n' + detail;
|
||||
|
||||
if (lastDbGateCrash && new Date().getTime() - lastDbGateCrash < 30 * 1000) {
|
||||
if (
|
||||
|
||||
@@ -6,6 +6,7 @@ import tabs from '../tabs';
|
||||
import { setSelectedTabFunc } from './common';
|
||||
import localforage from 'localforage';
|
||||
import stableStringify from 'json-stable-stringify';
|
||||
import { saveAllPendingEditorData } from '../query/useEditorData';
|
||||
|
||||
function findFreeNumber(numbers: number[]) {
|
||||
if (numbers.length == 0) return 1;
|
||||
@@ -74,9 +75,9 @@ export default async function openNewTab(newTab, initialData = undefined, option
|
||||
openedTabs.update(files => [
|
||||
...(files || []).map(x => ({ ...x, selected: false })),
|
||||
{
|
||||
...newTab,
|
||||
tabid,
|
||||
selected: true,
|
||||
...newTab,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -91,3 +92,35 @@ export default async function openNewTab(newTab, initialData = undefined, option
|
||||
// },
|
||||
// ]);
|
||||
}
|
||||
|
||||
export async function duplicateTab(tab) {
|
||||
await saveAllPendingEditorData();
|
||||
|
||||
let title = tab.title;
|
||||
const mtitle = title.match(/^(.*#)[\d]+$/);
|
||||
if (mtitle) title = mtitle[1];
|
||||
|
||||
const keyRegex = /^tabdata_([^_]+)_([^_]+)$/;
|
||||
const initialData = {};
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
const m = key.match(keyRegex);
|
||||
if (m && m[2] == tab.tabid) {
|
||||
initialData[m[1]] = JSON.parse(localStorage.getItem(key));
|
||||
}
|
||||
}
|
||||
for (const key of await localforage.keys()) {
|
||||
const m = key.match(keyRegex);
|
||||
if (m && m[2] == tab.tabid) {
|
||||
initialData[m[1]] = await localforage.getItem(key);
|
||||
}
|
||||
}
|
||||
openNewTab(
|
||||
{
|
||||
..._.omit(tab, ['tabid']),
|
||||
title,
|
||||
},
|
||||
initialData,
|
||||
{ forceNewTab: true }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<ErrorInfo message={$status.message} icon="img error" />
|
||||
<InlineButton on:click={handleRefreshDatabase}>Refresh</InlineButton>
|
||||
</WidgetsInnerContainer>
|
||||
{:else if objectList.length == 0 && $status && $status.name != 'pending' && $objects}
|
||||
{:else if objectList.length == 0 && $status && $status.name != 'pending' && $status.name != 'checkStructure' && $status.name != 'loadStructure' && $objects}
|
||||
<WidgetsInnerContainer>
|
||||
<ErrorInfo
|
||||
message={`Database ${database} is empty or structure is not loaded, press Refresh button to reload structure`}
|
||||
@@ -56,7 +56,7 @@
|
||||
<InlineButton on:click={handleRefreshDatabase}>Refresh</InlineButton>
|
||||
</SearchBoxWrapper>
|
||||
<WidgetsInnerContainer>
|
||||
{#if ($status && $status.name == 'pending' && $objects) || !$objects}
|
||||
{#if ($status && ($status.name == 'pending' || $status.name == 'checkStructure' || $status.name == 'loadStructure') && $objects) || !$objects}
|
||||
<LoadingInfo message="Loading database structure" />
|
||||
{:else}
|
||||
<AppObjectList
|
||||
|
||||
@@ -49,6 +49,10 @@
|
||||
<div class="item">
|
||||
{#if $status.name == 'pending'}
|
||||
<FontIcon icon="icon loading" /> Loading
|
||||
{:else if $status.name == 'checkStructure'}
|
||||
<FontIcon icon="icon loading" /> Checking model
|
||||
{:else if $status.name == 'loadStructure'}
|
||||
<FontIcon icon="icon loading" /> Loading model
|
||||
{:else if $status.name == 'ok'}
|
||||
<FontIcon icon="img ok-inv" /> Connected
|
||||
{:else if $status.name == 'error'}
|
||||
|
||||
@@ -87,9 +87,9 @@
|
||||
registerCommand({
|
||||
id: 'tabs.addToFavorites',
|
||||
category: 'Tabs',
|
||||
name: 'Favorites',
|
||||
icon: 'icon favorite',
|
||||
toolbar: true,
|
||||
name: 'Add current tab to favorites',
|
||||
// icon: 'icon favorite',
|
||||
// toolbar: true,
|
||||
testEnabled: () =>
|
||||
getActiveTab()?.tabComponent &&
|
||||
tabs[getActiveTab()?.tabComponent] &&
|
||||
@@ -113,6 +113,7 @@
|
||||
import { setSelectedTab } from '../utility/common';
|
||||
import contextMenu from '../utility/contextMenu';
|
||||
import { getConnectionInfo } from '../utility/metadataLoaders';
|
||||
import { duplicateTab } from '../utility/openNewTab';
|
||||
|
||||
$: currentDbKey =
|
||||
$currentDatabase && $currentDatabase.name && $currentDatabase.connection
|
||||
@@ -146,9 +147,10 @@
|
||||
}
|
||||
};
|
||||
|
||||
const getContextMenu = (tabid, props) => () => {
|
||||
const getContextMenu = tab => () => {
|
||||
const { tabid, props, tabComponent } = tab;
|
||||
const { conid, database } = props || {};
|
||||
const res = [
|
||||
return [
|
||||
{
|
||||
text: 'Close',
|
||||
onClick: () => closeTab(tabid),
|
||||
@@ -161,20 +163,33 @@
|
||||
text: 'Close others',
|
||||
onClick: () => closeOthers(tabid),
|
||||
},
|
||||
{
|
||||
text: 'Duplicate',
|
||||
onClick: () => duplicateTab(tab),
|
||||
},
|
||||
tabComponent &&
|
||||
tabs[tabComponent] &&
|
||||
tabs[tabComponent].allowAddToFavorites &&
|
||||
tabs[tabComponent].allowAddToFavorites(props) && [
|
||||
{ divider: true },
|
||||
{
|
||||
text: 'Add to favorites',
|
||||
onClick: () => showModal(FavoriteModal, { savingTab: tab }),
|
||||
},
|
||||
],
|
||||
conid &&
|
||||
database && [
|
||||
{ divider: true },
|
||||
{
|
||||
text: `Close with same DB - ${database}`,
|
||||
onClick: () => closeWithSameDb(tabid),
|
||||
},
|
||||
{
|
||||
text: `Close with other DB than ${database}`,
|
||||
onClick: () => closeWithOtherDb(tabid),
|
||||
},
|
||||
],
|
||||
];
|
||||
if (conid && database) {
|
||||
res.push(
|
||||
{
|
||||
text: `Close with same DB - ${database}`,
|
||||
onClick: () => closeWithSameDb(tabid),
|
||||
},
|
||||
{
|
||||
text: `Close with other DB than ${database}`,
|
||||
onClick: () => closeWithOtherDb(tabid),
|
||||
}
|
||||
);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
const handleSetDb = async props => {
|
||||
@@ -216,7 +231,7 @@
|
||||
class:selected={tab.selected}
|
||||
on:click={e => handleTabClick(e, tab.tabid)}
|
||||
on:mouseup={e => handleMouseUp(e, tab.tabid)}
|
||||
use:contextMenu={getContextMenu(tab.tabid, tab.props)}
|
||||
use:contextMenu={getContextMenu(tab)}
|
||||
>
|
||||
<FontIcon icon={tab.busy ? 'icon loading' : tab.icon} />
|
||||
<span class="file-name">
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
import _ from 'lodash';
|
||||
import { openFavorite } from '../appobj/FavoriteFileAppObject.svelte';
|
||||
import runCommand from '../commands/runCommand';
|
||||
import { commands, commandsCustomized } from '../stores';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import { activeTab, commands, commandsCustomized } from '../stores';
|
||||
import getElectron from '../utility/getElectron';
|
||||
import { useFavorites } from '../utility/metadataLoaders';
|
||||
import ToolbarButton from './ToolbarButton.svelte';
|
||||
@@ -25,26 +26,48 @@
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
{#if !electron}
|
||||
<ToolbarButton externalImage="logo192.png" on:click={() => runCommand('about.show')} />
|
||||
{/if}
|
||||
{#each ($favorites || []).filter(x => x.showInToolbar) as item}
|
||||
<ToolbarButton on:click={() => openFavorite(item)} icon={item.icon || 'icon favorite'}>
|
||||
{item.title}
|
||||
</ToolbarButton>
|
||||
{/each}
|
||||
<div class="root">
|
||||
<div class="container">
|
||||
{#if !electron}
|
||||
<ToolbarButton externalImage="logo192.png" on:click={() => runCommand('about.show')} />
|
||||
{/if}
|
||||
{#each ($favorites || []).filter(x => x.showInToolbar) as item}
|
||||
<ToolbarButton on:click={() => openFavorite(item)} icon={item.icon || 'icon favorite'}>
|
||||
{item.title}
|
||||
</ToolbarButton>
|
||||
{/each}
|
||||
|
||||
{#each list as command}
|
||||
<ToolbarButton
|
||||
icon={command.icon}
|
||||
on:click={command.onClick}
|
||||
disabled={!command.enabled}
|
||||
title={getCommandTitle(command)}
|
||||
>
|
||||
{command.toolbarName || command.name}
|
||||
</ToolbarButton>
|
||||
{/each}
|
||||
{#each list.filter(x => !x.isRelatedToTab) as command}
|
||||
<ToolbarButton
|
||||
icon={command.icon}
|
||||
on:click={command.onClick}
|
||||
disabled={!command.enabled}
|
||||
title={getCommandTitle(command)}
|
||||
>
|
||||
{command.toolbarName || command.name}
|
||||
</ToolbarButton>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="container">
|
||||
{#if $activeTab && list.filter(x => x.isRelatedToTab).length > 0}
|
||||
<div class="activeTab">
|
||||
<div class="activeTabInner">
|
||||
<FontIcon icon={$activeTab.icon} />
|
||||
{$activeTab.title}:
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#each list.filter(x => x.isRelatedToTab) as command}
|
||||
<ToolbarButton
|
||||
icon={command.icon}
|
||||
on:click={command.onClick}
|
||||
disabled={!command.enabled}
|
||||
title={getCommandTitle(command)}
|
||||
>
|
||||
{command.toolbarName || command.name}
|
||||
</ToolbarButton>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -54,4 +77,21 @@
|
||||
align-items: stretch;
|
||||
height: var(--dim-toolbar-height);
|
||||
}
|
||||
.root {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.activeTab {
|
||||
background-color: var(--theme-bg-2);
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.activeTabInner {
|
||||
align-self: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user