tabstrips

This commit is contained in:
Jan Prochazka
2022-02-13 08:22:23 +01:00
parent 488b200fcb
commit e0b8eb3e79
10 changed files with 354 additions and 291 deletions

View File

@@ -19,8 +19,8 @@ module.exports = [
label: 'Window',
submenu: [
{ command: 'new.query', hideDisabled: true },
{ command: 'new.modelCompare', hideDisabled: true },
{ command: 'new.freetable', hideDisabled: true },
{ command: 'new.shell', hideDisabled: true },
{ divider: true },
{ command: 'tabs.closeTab', hideDisabled: true },
{ command: 'tabs.closeAll', hideDisabled: true },
@@ -50,6 +50,14 @@ module.exports = [
{ command: 'settings.show' },
],
},
{
label: 'Tools',
submenu: [
{ command: 'sql.generator' },
{ command: 'file.import' },
{ command: 'new.modelCompare' },
],
},
{
label: 'Help',
submenu: [

View File

@@ -12,11 +12,12 @@
export let command;
export let component = ToolStripButton;
export let hideDisabled = false;
$: cmd = Object.values($commandsCustomized).find((x: any) => x.id == command) as any;
</script>
{#if cmd}
{#if cmd && (!hideDisabled || cmd.enabled)}
<svelte:component
this={component}
title={getCommandTitle(cmd)}

View File

@@ -103,6 +103,7 @@ registerCommand({
category: 'New',
icon: 'img shell',
name: 'JavaScript Shell',
menuName:' New JavaScript shell',
onClick: () => {
openNewTab({
title: 'Shell #',
@@ -539,6 +540,7 @@ export function registerFileCommands({
category,
name: 'Undo',
group: 'undo',
icon: 'icon undo',
testEnabled: () => getCurrentEditor()?.canUndo(),
onClick: () => getCurrentEditor().undo(),
});
@@ -547,6 +549,7 @@ export function registerFileCommands({
category,
group: 'redo',
name: 'Redo',
icon: 'icon redo',
testEnabled: () => getCurrentEditor()?.canRedo(),
onClick: () => getCurrentEditor().redo(),
});

View File

@@ -356,6 +356,7 @@
return {};
}, {});
registerMenu(
{ command: 'dataForm.refresh' },
{ placeTag: 'switch' },
{ command: 'dataForm.copyToClipboard' },
{ divider: true },

View File

@@ -1,60 +1,60 @@
<script lang="ts" context="module">
const getCurrentEditor = () => getActiveComponent('TableEditor');
// registerCommand({
// id: 'tableEditor.addColumn',
// category: 'Table editor',
// name: 'Add column',
// icon: 'icon add-column',
// toolbar: true,
// isRelatedToTab: true,
// testEnabled: () => getCurrentEditor()?.writable(),
// onClick: () => getCurrentEditor().addColumn(),
// });
registerCommand({
id: 'tableEditor.addColumn',
category: 'Table editor',
name: 'Add column',
icon: 'icon add-column',
toolbar: true,
isRelatedToTab: true,
testEnabled: () => getCurrentEditor()?.writable(),
onClick: () => getCurrentEditor().addColumn(),
});
// registerCommand({
// id: 'tableEditor.addPrimaryKey',
// category: 'Table editor',
// name: 'Add primary key',
// icon: 'icon add-key',
// toolbar: true,
// isRelatedToTab: true,
// testEnabled: () => getCurrentEditor()?.allowAddPrimaryKey(),
// onClick: () => getCurrentEditor().addPrimaryKey(),
// });
registerCommand({
id: 'tableEditor.addPrimaryKey',
category: 'Table editor',
name: 'Add primary key',
icon: 'icon add-key',
toolbar: true,
isRelatedToTab: true,
testEnabled: () => getCurrentEditor()?.allowAddPrimaryKey(),
onClick: () => getCurrentEditor().addPrimaryKey(),
});
// registerCommand({
// id: 'tableEditor.addForeignKey',
// category: 'Table editor',
// name: 'Add foreign key',
// icon: 'icon add-key',
// toolbar: true,
// isRelatedToTab: true,
// testEnabled: () => getCurrentEditor()?.writable(),
// onClick: () => getCurrentEditor().addForeignKey(),
// });
registerCommand({
id: 'tableEditor.addForeignKey',
category: 'Table editor',
name: 'Add foreign key',
icon: 'icon add-key',
toolbar: true,
isRelatedToTab: true,
testEnabled: () => getCurrentEditor()?.writable(),
onClick: () => getCurrentEditor().addForeignKey(),
});
// registerCommand({
// id: 'tableEditor.addIndex',
// category: 'Table editor',
// name: 'Add index',
// icon: 'icon add-key',
// toolbar: true,
// isRelatedToTab: true,
// testEnabled: () => getCurrentEditor()?.writable(),
// onClick: () => getCurrentEditor().addIndex(),
// });
registerCommand({
id: 'tableEditor.addIndex',
category: 'Table editor',
name: 'Add index',
icon: 'icon add-key',
toolbar: true,
isRelatedToTab: true,
testEnabled: () => getCurrentEditor()?.writable(),
onClick: () => getCurrentEditor().addIndex(),
});
// registerCommand({
// id: 'tableEditor.addUnique',
// category: 'Table editor',
// name: 'Add unique',
// icon: 'icon add-key',
// toolbar: true,
// isRelatedToTab: true,
// testEnabled: () => getCurrentEditor()?.writable(),
// onClick: () => getCurrentEditor().addUnique(),
// });
registerCommand({
id: 'tableEditor.addUnique',
category: 'Table editor',
name: 'Add unique',
icon: 'icon add-key',
toolbar: true,
isRelatedToTab: true,
testEnabled: () => getCurrentEditor()?.writable(),
onClick: () => getCurrentEditor().addUnique(),
});
</script>
<script lang="ts">
@@ -105,9 +105,9 @@
});
}
// export function allowAddPrimaryKey() {
// return writable() && !tableInfo?.primaryKey;
// }
export function allowAddPrimaryKey() {
return writable() && !tableInfo?.primaryKey;
}
export function addPrimaryKey() {
showModal(PrimaryKeyEditorModal, {

View File

@@ -162,6 +162,8 @@
import { useArchiveFolders, useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders';
import resolveApi from '../utility/resolveApi';
import { showSnackbarSuccess } from '../utility/snackbar';
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte';
export let tabid;
@@ -336,226 +338,234 @@
const menu = getContextMenu();
</script>
<div class="wrapper" use:contextMenu={menu}>
<VerticalSplitter>
<div slot="1" class="flexcol">
<FormProviderCore {values}>
<div class="topbar">
<div class="col-3">
<FormConnectionSelect
name="sourceConid"
label="Source server"
templateProps={{ noMargin: true }}
isNative
allowChooseModel
notSelected
/>
</div>
<div class="col-3">
{#if $values?.sourceConid == '__model'}
<FormSelectField
name="sourceDatabase"
label="Source DB model"
<ToolStripContainer>
<div class="wrapper" use:contextMenu={menu}>
<VerticalSplitter>
<div slot="1" class="flexcol">
<FormProviderCore {values}>
<div class="topbar">
<div class="col-3">
<FormConnectionSelect
name="sourceConid"
label="Source server"
templateProps={{ noMargin: true }}
isNative
options={($archiveFolders || []).map(x => ({ label: x.name, value: `archive:${x.name}` }))}
allowChooseModel
notSelected
/>
{:else}
</div>
<div class="col-3">
{#if $values?.sourceConid == '__model'}
<FormSelectField
name="sourceDatabase"
label="Source DB model"
templateProps={{ noMargin: true }}
isNative
options={($archiveFolders || []).map(x => ({ label: x.name, value: `archive:${x.name}` }))}
notSelected
/>
{:else}
<FormDatabaseSelect
conidName="sourceConid"
name="sourceDatabase"
label="Source database"
templateProps={{ noMargin: true }}
isNative
notSelected
/>
{/if}
</div>
<div class="deployButton">
<InlineButton on:click={deploy}>
<div class="arrow">
<FontIcon icon="icon arrow-right-bold" />
</div>
Deploy (experimental)
</InlineButton>
</div>
<div class="col-3">
<FormConnectionSelect
name="targetConid"
label="Target server"
templateProps={{ noMargin: true }}
isNative
notSelected
/>
</div>
<div class="col-3">
<FormDatabaseSelect
conidName="sourceConid"
name="sourceDatabase"
label="Source database"
conidName="targetConid"
name="targetDatabase"
label="Target database"
templateProps={{ noMargin: true }}
isNative
notSelected
/>
{/if}
</div>
</div>
<div class="deployButton">
<InlineButton on:click={deploy}>
<div class="arrow">
<FontIcon icon="icon arrow-right-bold" />
</div>
Deploy (experimental)
</InlineButton>
</div>
<div class="col-3">
<FormConnectionSelect
name="targetConid"
label="Target server"
templateProps={{ noMargin: true }}
isNative
notSelected
/>
</div>
<div class="col-3">
<FormDatabaseSelect
conidName="targetConid"
name="targetDatabase"
label="Target database"
templateProps={{ noMargin: true }}
isNative
notSelected
/>
</div>
</div>
<div class="filters">
<SearchInput placeholder="Search tables or objects" bind:value={filter} />
<div class="filters">
<SearchInput placeholder="Search tables or objects" bind:value={filter} />
<RowsFilterSwitcher
icon="img add"
label="Added"
{values}
field="hideAdded"
count={filterDiffRowsByFlag(
diffRowsAll.filter(x => x.state == 'added'),
$values,
'added'
).length}
/>
<RowsFilterSwitcher
icon="img minus"
label="Removed"
{values}
field="hideRemoved"
count={filterDiffRowsByFlag(
diffRowsAll.filter(x => x.state == 'removed'),
$values,
'removed'
).length}
/>
<RowsFilterSwitcher
icon="img changed"
label="Changed"
{values}
field="hideChanged"
count={filterDiffRowsByFlag(
diffRowsAll.filter(x => x.state == 'changed'),
$values,
'changed'
).length}
/>
<RowsFilterSwitcher
icon="img equal"
label="Equal"
{values}
field="hideEqual"
count={filterDiffRowsByFlag(
diffRowsAll.filter(x => x.state == 'equal'),
$values,
'equal'
).length}
/>
{#each _.keys(DbDiffCompareDefs) as objectTypeField}
<RowsFilterSwitcher
icon={DbDiffCompareDefs[objectTypeField].icon}
label={DbDiffCompareDefs[objectTypeField].plural}
icon="img add"
label="Added"
{values}
field={'hide_' + objectTypeField}
field="hideAdded"
count={filterDiffRowsByFlag(
diffRowsAll.filter(x => x.objectTypeField == objectTypeField),
diffRowsAll.filter(x => x.state == 'added'),
$values,
objectTypeField
'added'
).length}
/>
<RowsFilterSwitcher
icon="img minus"
label="Removed"
{values}
field="hideRemoved"
count={filterDiffRowsByFlag(
diffRowsAll.filter(x => x.state == 'removed'),
$values,
'removed'
).length}
/>
<RowsFilterSwitcher
icon="img changed"
label="Changed"
{values}
field="hideChanged"
count={filterDiffRowsByFlag(
diffRowsAll.filter(x => x.state == 'changed'),
$values,
'changed'
).length}
/>
<RowsFilterSwitcher
icon="img equal"
label="Equal"
{values}
field="hideEqual"
count={filterDiffRowsByFlag(
diffRowsAll.filter(x => x.state == 'equal'),
$values,
'equal'
).length}
/>
{/each}
</div>
</FormProviderCore>
<div class="tableWrapper">
<ScrollableTableControl
rows={diffRows}
bind:selectedIndex={pairIndex}
selectable
disableFocusOutline
columns={[
{ fieldName: 'isChecked', header: '', width: '50px', slot: 1, headerSlot: 2 },
{ fieldName: 'type', header: 'Type', width: '100px', slot: 3 },
{ fieldName: 'sourceSchemaName', header: 'Schema' },
{ fieldName: 'sourcePureName', header: 'Name' },
{ fieldName: 'state', header: 'Action', width: '100px' },
{ fieldName: 'targetSchemaName', header: 'Schema' },
{ fieldName: 'targetPureName', header: 'Name' },
]}
>
<input
type="checkbox"
slot="1"
let:row
disabled={row.state == 'equal'}
checked={!!$values[`isChecked_${row['identifier']}`]}
on:change={e => {
// @ts-ignore
$values = { ...$values, [`isChecked_${row.identifier}`]: e.target.checked };
}}
/>
<svelte:fragment slot="2">
<InlineButton on:click={handleCheckAll}>
<FontIcon icon="icon check-all" />
</InlineButton>
</svelte:fragment>
<svelte:fragment slot="3" let:row>
<FontIcon icon={row.typeIcon} />
{row.typeName}
</svelte:fragment>
</ScrollableTableControl>
</div>
</div>
{#each _.keys(DbDiffCompareDefs) as objectTypeField}
<RowsFilterSwitcher
icon={DbDiffCompareDefs[objectTypeField].icon}
label={DbDiffCompareDefs[objectTypeField].plural}
{values}
field={'hide_' + objectTypeField}
count={filterDiffRowsByFlag(
diffRowsAll.filter(x => x.objectTypeField == objectTypeField),
$values,
objectTypeField
).length}
/>
{/each}
</div>
</FormProviderCore>
<svelte:fragment slot="2">
<TabControl
tabs={[
{
label: 'DDL',
slot: 1,
},
{
label: 'Synchronize script',
slot: 2,
},
{
label: 'Columns',
slot: 3,
},
]}
>
<svelte:fragment slot="1">
<DiffView
leftTitle={diffRows[pairIndex]?.target?.pureName}
rightTitle={diffRows[pairIndex]?.source?.pureName}
leftText={getCreateObjectScript(diffRows[pairIndex]?.target, driver)}
rightText={getCreateObjectScript(diffRows[pairIndex]?.source, driver)}
/>
</svelte:fragment>
<svelte:fragment slot="2">
<SqlEditor readOnly value={sqlPreview} />
</svelte:fragment>
<svelte:fragment slot="3">
<div class="tableWrapper">
<ScrollableTableControl
rows={diffColumns}
rows={diffRows}
bind:selectedIndex={pairIndex}
selectable
disableFocusOutline
columns={[
{ fieldName: 'sourceColumnName', header: 'Name', width: '100px' },
{ fieldName: 'sourceDataType', header: 'Type' },
{ fieldName: 'sourceNotNull', header: 'Not null', slot: 1 },
{ fieldName: 'isChecked', header: '', width: '50px', slot: 1, headerSlot: 2 },
{ fieldName: 'type', header: 'Type', width: '100px', slot: 3 },
{ fieldName: 'sourceSchemaName', header: 'Schema' },
{ fieldName: 'sourcePureName', header: 'Name' },
{ fieldName: 'state', header: 'Action', width: '100px' },
{ fieldName: 'targetColumnName', header: 'Schema' },
{ fieldName: 'targetDataType', header: 'Name' },
{ fieldName: 'targetNotNull', header: 'Not null', slot: 2 },
{ fieldName: 'targetSchemaName', header: 'Schema' },
{ fieldName: 'targetPureName', header: 'Name' },
]}
>
<input type="checkbox" disabled slot="1" let:row checked={!!row.sourceNotNull} />
<input type="checkbox" disabled slot="2" let:row checked={!!row.targetNotNull} />
<input
type="checkbox"
slot="1"
let:row
disabled={row.state == 'equal'}
checked={!!$values[`isChecked_${row['identifier']}`]}
on:change={e => {
// @ts-ignore
$values = { ...$values, [`isChecked_${row.identifier}`]: e.target.checked };
}}
/>
<svelte:fragment slot="2">
<InlineButton on:click={handleCheckAll}>
<FontIcon icon="icon check-all" />
</InlineButton>
</svelte:fragment>
<svelte:fragment slot="3" let:row>
<FontIcon icon={row.typeIcon} />
{row.typeName}
</svelte:fragment>
</ScrollableTableControl>
</svelte:fragment>
</TabControl>
</svelte:fragment>
</VerticalSplitter>
</div>
</div>
</div>
<svelte:fragment slot="2">
<TabControl
tabs={[
{
label: 'DDL',
slot: 1,
},
{
label: 'Synchronize script',
slot: 2,
},
{
label: 'Columns',
slot: 3,
},
]}
>
<svelte:fragment slot="1">
<DiffView
leftTitle={diffRows[pairIndex]?.target?.pureName}
rightTitle={diffRows[pairIndex]?.source?.pureName}
leftText={getCreateObjectScript(diffRows[pairIndex]?.target, driver)}
rightText={getCreateObjectScript(diffRows[pairIndex]?.source, driver)}
/>
</svelte:fragment>
<svelte:fragment slot="2">
<SqlEditor readOnly value={sqlPreview} />
</svelte:fragment>
<svelte:fragment slot="3">
<ScrollableTableControl
rows={diffColumns}
disableFocusOutline
columns={[
{ fieldName: 'sourceColumnName', header: 'Name', width: '100px' },
{ fieldName: 'sourceDataType', header: 'Type' },
{ fieldName: 'sourceNotNull', header: 'Not null', slot: 1 },
{ fieldName: 'state', header: 'Action', width: '100px' },
{ fieldName: 'targetColumnName', header: 'Schema' },
{ fieldName: 'targetDataType', header: 'Name' },
{ fieldName: 'targetNotNull', header: 'Not null', slot: 2 },
]}
>
<input type="checkbox" disabled slot="1" let:row checked={!!row.sourceNotNull} />
<input type="checkbox" disabled slot="2" let:row checked={!!row.targetNotNull} />
</ScrollableTableControl>
</svelte:fragment>
</TabControl>
</svelte:fragment>
</VerticalSplitter>
</div>
<svelte:fragment slot="toolstrip">
<ToolStripCommandButton command="compareModels.reportDiff" />
<ToolStripCommandButton command="compareModels.swap" />
<ToolStripCommandButton command="compareModels.deploy" />
<ToolStripCommandButton command="compareModels.refresh" />
</svelte:fragment>
</ToolStripContainer>
<style>
.wrapper {

View File

@@ -23,6 +23,9 @@
import { findEngineDriver } from 'dbgate-tools';
import createActivator, { getActiveComponent } from '../utility/createActivator';
import DiagramDesigner from '../designer/DiagramDesigner.svelte';
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte';
import invalidateCommands from '../commands/invalidateCommands';
export let tabid;
export let conid;
@@ -47,6 +50,7 @@
export function undo() {
dispatchModel({ type: 'undo' });
invalidateCommands();
}
export function canRedo() {
@@ -55,20 +59,24 @@
export function redo() {
dispatchModel({ type: 'redo' });
invalidateCommands();
}
const handleChange = (value, skipUndoChain) =>
const handleChange = (value, skipUndoChain) => {
// @ts-ignore
dispatchModel({
type: 'compute',
useMerge: skipUndoChain,
compute: v => (_.isFunction(value) ? value(v) : value),
});
invalidateCommands();
};
const { editorState, editorValue, setEditorData } = useEditorData({
tabid,
onInitialData: value => {
dispatchModel({ type: 'reset', value });
invalidateCommands();
},
});
@@ -90,4 +98,13 @@
}
</script>
<DiagramDesigner value={$modelState.value || {}} {conid} {database} onChange={handleChange} menu={createMenu} />
<ToolStripContainer>
<DiagramDesigner value={$modelState.value || {}} {conid} {database} onChange={handleChange} menu={createMenu} />
<svelte:fragment slot="toolstrip">
<ToolStripCommandButton command="designer.arrange" />
<ToolStripCommandButton command="diagram.save" />
<ToolStripCommandButton command="diagram.export" />
<ToolStripCommandButton command="diagram.undo" />
<ToolStripCommandButton command="diagram.redo" />
</svelte:fragment>
</ToolStripContainer>

View File

@@ -37,6 +37,8 @@
<script lang="ts">
import { getContext } from 'svelte';
import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte';
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
import invalidateCommands from '../commands/invalidateCommands';
import registerCommand from '../commands/registerCommand';
@@ -211,21 +213,29 @@
}
</script>
<VerticalSplitter>
<svelte:fragment slot="1">
<AceEditor
value={$editorState.value || ''}
menu={createMenu()}
on:input={e => setEditorData(e.detail)}
on:focus={() => {
activator.activate();
invalidateCommands();
}}
bind:this={domEditor}
mode="javascript"
/>
<ToolStripContainer>
<VerticalSplitter>
<svelte:fragment slot="1">
<AceEditor
value={$editorState.value || ''}
menu={createMenu()}
on:input={e => setEditorData(e.detail)}
on:focus={() => {
activator.activate();
invalidateCommands();
}}
bind:this={domEditor}
mode="javascript"
/>
</svelte:fragment>
<svelte:fragment slot="2">
<RunnerOutputPane {runnerId} {executeNumber} />
</svelte:fragment>
</VerticalSplitter>
<svelte:fragment slot="toolstrip">
<ToolStripCommandButton command="shell.execute" />
<ToolStripCommandButton command="shell.kill" />
<ToolStripCommandButton command="shell.save" />
<ToolStripCommandButton command="shell.copyNodeScript" />
</svelte:fragment>
<svelte:fragment slot="2">
<RunnerOutputPane {runnerId} {executeNumber} />
</svelte:fragment>
</VerticalSplitter>
</ToolStripContainer>

View File

@@ -137,10 +137,13 @@
/>
<svelte:fragment slot="toolstrip">
<ToolStripCommandButton command="dataGrid.refresh" />
<ToolStripCommandButton command="dataGrid.refresh" hideDisabled />
<ToolStripCommandButton command="dataForm.refresh" hideDisabled />
<ToolStripCommandButton command="tableData.save" />
<ToolStripCommandButton command="dataGrid.insertNewRow" />
<ToolStripCommandButton command="dataGrid.deleteSelectedRows" />
<ToolStripCommandButton command="dataGrid.insertNewRow" hideDisabled />
<ToolStripCommandButton command="dataGrid.deleteSelectedRows" hideDisabled />
<ToolStripCommandButton command="dataGrid.switchToForm" hideDisabled />
<ToolStripCommandButton command="dataGrid.switchToTable" hideDisabled />
<ToolStripExportButton {quickExportHandlerRef} />
</svelte:fragment>
</ToolStripContainer>

View File

@@ -58,6 +58,8 @@
import StatusBarTabItem from '../widgets/StatusBarTabItem.svelte';
import openNewTab from '../utility/openNewTab';
import { apiCall } from '../utility/api';
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte';
export let tabid;
export let conid;
@@ -162,26 +164,34 @@
// }
</script>
<TableEditor
bind:this={domEditor}
tableInfo={showTable}
dbInfo={$dbInfo}
{driver}
setTableInfo={objectTypeField == 'tables'
? tableInfoUpdater =>
setEditorData(tbl =>
tbl
? {
base: tbl.base,
current: tableInfoUpdater(tbl.current),
}
: {
base: tableInfoWithPairingId,
current: tableInfoUpdater(tableInfoWithPairingId),
}
)
: null}
/>
<ToolStripContainer>
<TableEditor
bind:this={domEditor}
tableInfo={showTable}
dbInfo={$dbInfo}
{driver}
setTableInfo={objectTypeField == 'tables'
? tableInfoUpdater =>
setEditorData(tbl =>
tbl
? {
base: tbl.base,
current: tableInfoUpdater(tbl.current),
}
: {
base: tableInfoWithPairingId,
current: tableInfoUpdater(tableInfoWithPairingId),
}
)
: null}
/>
<svelte:fragment slot="toolstrip">
<ToolStripCommandButton command="tableStructure.save" />
<ToolStripCommandButton command="tableStructure.reset" />
<ToolStripCommandButton command="tableEditor.addColumn" />
<ToolStripCommandButton command="tableEditor.addIndex" />
</svelte:fragment>
</ToolStripContainer>
{#if objectTypeField == 'tables'}
<StatusBarTabItem