diff --git a/packages/web/src/datagrid/DataGrid.svelte b/packages/web/src/datagrid/DataGrid.svelte index 8b34add6f..1df5d3790 100644 --- a/packages/web/src/datagrid/DataGrid.svelte +++ b/packages/web/src/datagrid/DataGrid.svelte @@ -7,11 +7,15 @@ export let config; export let gridCoreComponent; + export let formViewComponent; + export let formDisplay; export let isDetailView = false; export let showReferences = false; let managerSize; + + $: isFormView = !!(formDisplay && formDisplay.config && formDisplay.config.isFormView); @@ -33,7 +37,15 @@ - + {#if isFormView} + + {:else} + + {/if} diff --git a/packages/web/src/datagrid/DataGridCell.svelte b/packages/web/src/datagrid/DataGridCell.svelte index 28334752e..222f87973 100644 --- a/packages/web/src/datagrid/DataGridCell.svelte +++ b/packages/web/src/datagrid/DataGridCell.svelte @@ -23,6 +23,7 @@ export let rowIndex; export let col; export let rowData; + export let colIndex = undefined; export let hintFieldsAllowed = undefined; export let isSelected = false; @@ -39,7 +40,7 @@ getCurrentDataGrid().exportGrid(), }); + registerCommand({ + id: 'dataGrid.switchToForm', + category: 'Data grid', + name: 'Switch to form', + keyText: 'F4', + testEnabled: () => getCurrentDataGrid()?.formViewEnabled(), + onClick: () => getCurrentDataGrid().switchToForm(), + }); + function getRowCountInfo(selectedCells, grider, realColumnUniqueNames, selectedRowData, allRowCount) { if (selectedCells.length > 1 && selectedCells.every(x => _.isNumber(x[0]) && _.isNumber(x[1]))) { let sum = _.sumBy(selectedCells, cell => { @@ -193,6 +202,7 @@ export let onSave; export let focusOnVisible = false; export let onExportGrid = null; + export let formViewAvailable = false; export let isLoadedAll; export let loadedTime; @@ -329,6 +339,16 @@ } } + export function formViewEnabled() { + return formViewAvailable && display.baseTable && display.baseTable.primaryKey; + } + + export function switchToForm() { + const cell = currentCell; + const rowData = isRegularCell(cell) ? grider.getRowData(cell[0]) : null; + display.switchToFormView(rowData); + } + // export function getGeneralAllowSave() { // return generalAllowSave; // } @@ -869,6 +889,7 @@ { command: 'dataGrid.refresh' }, { command: 'dataGrid.copyToClipboard' }, { command: 'dataGrid.export' }, + { command: 'dataGrid.switchToForm' }, { divider: true }, { command: 'dataGrid.save' }, { command: 'dataGrid.revertRowChanges' }, diff --git a/packages/web/src/datagrid/TableDataGrid.svelte b/packages/web/src/datagrid/TableDataGrid.svelte index 6b4db07c4..d36db7df3 100644 --- a/packages/web/src/datagrid/TableDataGrid.svelte +++ b/packages/web/src/datagrid/TableDataGrid.svelte @@ -13,6 +13,7 @@ import DataGrid from './DataGrid.svelte'; import ReferenceHeader from './ReferenceHeader.svelte'; import SqlDataGridCore from './SqlDataGridCore.svelte'; + import SqlFormView from '../formview/SqlFormView.svelte'; export let conid; export let database; @@ -44,16 +45,19 @@ setCache, $dbinfo ) - : // ? new TableFormViewDisplay( - // { schemaName, pureName }, - // findEngineDriver(connection, $extensions), - // $config, - // config.update, - // $cache, - // cache.update, - // $dbinfo - // ) - null; + : null; + + $: formDisplay = connection + ? new TableFormViewDisplay( + { schemaName, pureName }, + findEngineDriver($connection, $extensions), + config, + setConfig, + cache, + setCache, + $dbinfo + ) + : null; const setChildConfig = (value, reference = undefined) => { if (_.isFunction(value)) { @@ -111,7 +115,9 @@ { diff --git a/packages/web/src/formview/ChangeSetFormer.ts b/packages/web/src/formview/ChangeSetFormer.ts new file mode 100644 index 000000000..e9f645d13 --- /dev/null +++ b/packages/web/src/formview/ChangeSetFormer.ts @@ -0,0 +1,94 @@ +import { + ChangeSet, + changeSetContainsChanges, + changeSetInsertNewRow, + createChangeSet, + deleteChangeSetRows, + findExistingChangeSetItem, + getChangeSetInsertedRows, + TableFormViewDisplay, + revertChangeSetRowChanges, + setChangeSetValue, + ChangeSetRowDefinition, +} from 'dbgate-datalib'; +import Former from './Former'; + +export default class ChangeSetFormer extends Former { + public changeSet: ChangeSet; + public setChangeSet: Function; + private batchChangeSet: ChangeSet; + public rowDefinition: ChangeSetRowDefinition; + public rowStatus; + public rowData: {}; + + constructor( + public sourceRow: any, + public changeSetState, + public dispatchChangeSet, + public display: TableFormViewDisplay + ) { + super(); + this.changeSet = changeSetState && changeSetState.value; + this.setChangeSet = value => dispatchChangeSet({ type: 'set', value }); + this.batchChangeSet = null; + this.rowDefinition = display.getChangeSetRow(sourceRow); + const [matchedField, matchedChangeSetItem] = findExistingChangeSetItem(this.changeSet, this.rowDefinition); + this.rowData = matchedChangeSetItem ? { ...sourceRow, ...matchedChangeSetItem.fields } : sourceRow; + let status = 'regular'; + if (matchedChangeSetItem && matchedField == 'updates') status = 'updated'; + if (matchedField == 'deletes') status = 'deleted'; + this.rowStatus = { + status, + modifiedFields: + matchedChangeSetItem && matchedChangeSetItem.fields ? new Set(Object.keys(matchedChangeSetItem.fields)) : null, + }; + } + + applyModification(changeSetReducer) { + if (this.batchChangeSet) { + this.batchChangeSet = changeSetReducer(this.batchChangeSet); + } else { + this.setChangeSet(changeSetReducer(this.changeSet)); + } + } + + setCellValue(uniqueName: string, value: any) { + const row = this.sourceRow; + const definition = this.display.getChangeSetField(row, uniqueName); + this.applyModification(chs => setChangeSetValue(chs, definition, value)); + } + + deleteRow(index: number) { + this.applyModification(chs => deleteChangeSetRows(chs, this.rowDefinition)); + } + + beginUpdate() { + this.batchChangeSet = this.changeSet; + } + endUpdate() { + this.setChangeSet(this.batchChangeSet); + this.batchChangeSet = null; + } + + revertRowChanges() { + this.applyModification(chs => revertChangeSetRowChanges(chs, this.rowDefinition)); + } + revertAllChanges() { + this.applyModification(chs => createChangeSet()); + } + undo() { + this.dispatchChangeSet({ type: 'undo' }); + } + redo() { + this.dispatchChangeSet({ type: 'redo' }); + } + get canUndo() { + return this.changeSetState.canUndo; + } + get canRedo() { + return this.changeSetState.canRedo; + } + get containsChanges() { + return changeSetContainsChanges(this.changeSet); + } +} diff --git a/packages/web/src/formview/FormView.svelte b/packages/web/src/formview/FormView.svelte new file mode 100644 index 000000000..83556dfeb --- /dev/null +++ b/packages/web/src/formview/FormView.svelte @@ -0,0 +1,171 @@ + + + + +
+
+ {#each columnChunks as chunk, chunkIndex} + + {#each chunk as col, rowIndex} + + {#if chunkIndex == 0 && rowIndex == 0} + + {:else} + + {/if} + + + {/each} +
+ + + +
+ {/each} + { + lastFocusedFormView = instance; + invalidateCommands(); + }} + /> +
+
+ + diff --git a/packages/web/src/formview/Former.ts b/packages/web/src/formview/Former.ts new file mode 100644 index 000000000..cbd1508d2 --- /dev/null +++ b/packages/web/src/formview/Former.ts @@ -0,0 +1,53 @@ +// export interface GriderRowStatus { +// status: 'regular' | 'updated' | 'deleted' | 'inserted'; +// modifiedFields?: Set; +// insertedFields?: Set; +// deletedFields?: Set; +// } + +export default abstract class Former { + public rowData: any; + + // getRowStatus(index): GriderRowStatus { + // const res: GriderRowStatus = { + // status: 'regular', + // }; + // return res; + // } + beginUpdate() {} + endUpdate() {} + setCellValue(uniqueName: string, value: any) {} + revertRowChanges() {} + revertAllChanges() {} + undo() {} + redo() {} + get editable() { + return false; + } + get canInsert() { + return false; + } + get allowSave() { + return this.containsChanges; + } + get canUndo() { + return false; + } + get canRedo() { + return false; + } + get containsChanges() { + return false; + } + get disableLoadNextPage() { + return false; + } + get errors() { + return null; + } + updateRow(changeObject) { + for (const key of Object.keys(changeObject)) { + this.setCellValue(key, changeObject[key]); + } + } +} diff --git a/packages/web/src/formview/SqlFormView.svelte b/packages/web/src/formview/SqlFormView.svelte new file mode 100644 index 000000000..a92df581f --- /dev/null +++ b/packages/web/src/formview/SqlFormView.svelte @@ -0,0 +1,109 @@ + + + + + diff --git a/packages/web/src/formview/openReferenceForm.ts b/packages/web/src/formview/openReferenceForm.ts new file mode 100644 index 000000000..370ba66f5 --- /dev/null +++ b/packages/web/src/formview/openReferenceForm.ts @@ -0,0 +1,31 @@ +import _ from 'lodash'; +import openNewTab from '../utility/openNewTab'; + +export default function openReferenceForm(rowData, column, conid, database) { + const formViewKey = _.fromPairs( + column.foreignKey.columns.map(({ refColumnName, columnName }) => [refColumnName, rowData[columnName]]) + ); + openNewTab( + { + title: column.foreignKey.refTableName, + icon: 'img table', + tabComponent: 'TableDataTab', + props: { + schemaName: column.foreignKey.refSchemaName, + pureName: column.foreignKey.refTableName, + conid, + database, + objectTypeField: 'tables', + }, + }, + { + grid: { + isFormView: true, + formViewKey, + }, + }, + { + forceNewTab: true, + } + ); +} diff --git a/packages/web/src/tabs/MarkdownEditorTab.svelte b/packages/web/src/tabs/MarkdownEditorTab.svelte index c2cf8e807..c33877ed4 100644 --- a/packages/web/src/tabs/MarkdownEditorTab.svelte +++ b/packages/web/src/tabs/MarkdownEditorTab.svelte @@ -35,8 +35,10 @@ import AceEditor from '../query/AceEditor.svelte'; import useEditorData from '../query/useEditorData'; - import { getActiveTabId } from '../stores'; + import { getActiveTabId, openedTabs } from '../stores'; import invalidateCommands from '../commands/invalidateCommands'; + import openNewTab from '../utility/openNewTab'; + import { setSelectedTab } from '../utility/common'; export let tabid; @@ -70,7 +72,25 @@ return tabid; } - const { editorState, editorValue, setEditorData } = useEditorData({ tabid }); + export async function preview() { + await saveToStorage(); + const existing = ($openedTabs || []).find(x => x.props && x.props.sourceTabId == tabid && x.closedTime == null); + if (existing) { + setSelectedTab(existing.tabid); + } else { + const thisTab = ($openedTabs || []).find(x => x.tabid == tabid); + openNewTab({ + title: thisTab.title, + icon: 'img preview', + tabComponent: 'MarkdownPreviewTab', + props: { + sourceTabId: tabid, + }, + }); + } + } + + const { editorState, editorValue, setEditorData, saveToStorage } = useEditorData({ tabid }); function createMenu() { return [ diff --git a/packages/web/src/tabs/MarkdownPreviewTab.svelte b/packages/web/src/tabs/MarkdownPreviewTab.svelte new file mode 100644 index 000000000..414ffc072 --- /dev/null +++ b/packages/web/src/tabs/MarkdownPreviewTab.svelte @@ -0,0 +1,41 @@ + + +{#if $editorState.isLoading} +
+ +
+{:else} +
+ {#key data} + + {/key} +
+{/if} + + diff --git a/packages/web/src/tabs/index.js b/packages/web/src/tabs/index.js index bdc1e460e..331b70860 100644 --- a/packages/web/src/tabs/index.js +++ b/packages/web/src/tabs/index.js @@ -9,7 +9,7 @@ import * as PluginTab from './PluginTab.svelte'; import * as ChartTab from './ChartTab.svelte'; import * as MarkdownEditorTab from './MarkdownEditorTab.svelte'; import * as MarkdownViewTab from './MarkdownViewTab.svelte'; -// import MarkdownPreviewTab from './MarkdownPreviewTab'; +import * as MarkdownPreviewTab from './MarkdownPreviewTab.svelte'; import * as FavoriteEditorTab from './FavoriteEditorTab.svelte'; import * as QueryDesignTab from './QueryDesignTab.svelte'; @@ -25,7 +25,7 @@ export default { ChartTab, MarkdownEditorTab, MarkdownViewTab, - // MarkdownPreviewTab, + MarkdownPreviewTab, FavoriteEditorTab, QueryDesignTab, };