diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx index dab77bef8c..457c943d2b 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx @@ -14,7 +14,7 @@ import styled from 'reshadow'; import { Executor } from '@cloudbeaver/core-executor'; import { useStyles } from '@cloudbeaver/core-theming'; -import { DatabaseDataSelectActionsData, IDatabaseResultSet, IDataPresentationProps, IResultSetEditActionData, IResultSetElementKey, IResultSetPartialKey, ResultSetDataKeysUtils, ResultSetSelectAction } from '@cloudbeaver/plugin-data-viewer'; +import { DatabaseDataSelectActionsData, DatabaseEditChangeType, IDatabaseResultSet, IDataPresentationProps, IResultSetEditActionData, IResultSetElementKey, IResultSetPartialKey, ResultSetDataKeysUtils, ResultSetSelectAction } from '@cloudbeaver/plugin-data-viewer'; import { CellPosition, EditingContext } from '../Editing/EditingContext'; import { useEditing } from '../Editing/useEditing'; @@ -109,12 +109,19 @@ export const DataGridTable = observer { function syncEditor(data: IResultSetEditActionData) { const editor = tableData.editor; - if (data.resultId !== editor.result.id || !data.value) { + if ( + data.resultId !== editor.result.id + || !data.value + || data.value.length === 0 + || data.type === DatabaseEditChangeType.delete + ) { return; } - const idx = tableData.getColumnIndexFromColumnKey(data.value.key.column); - const rowIdx = tableData.getRowIndexFromKey(data.value.key.row); + const key = data.value[data.value.length - 1].key; + + const idx = tableData.getColumnIndexFromColumnKey(key.column); + const rowIdx = tableData.getRowIndexFromKey(key.row); if (data.revert) { editingContext.closeEditor({ @@ -123,7 +130,7 @@ export const DataGridTable = observer { revert: boolean; type?: DatabaseEditChangeType; resultId: string; - value?: IDatabaseDataEditActionValue; + value?: Array>; } export interface IDatabaseDataEditAction diff --git a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetEditAction.ts b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetEditAction.ts index 38722e1f4e..08961625e1 100644 --- a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetEditAction.ts +++ b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetEditAction.ts @@ -15,7 +15,7 @@ import type { IDatabaseDataSource } from '../../IDatabaseDataSource'; import type { IDatabaseResultSet } from '../../IDatabaseResultSet'; import { databaseDataAction } from '../DatabaseDataActionDecorator'; import { DatabaseEditAction } from '../DatabaseEditAction'; -import { DatabaseEditChangeType, IDatabaseDataEditActionData } from '../IDatabaseDataEditAction'; +import { DatabaseEditChangeType, IDatabaseDataEditActionData, IDatabaseDataEditActionValue } from '../IDatabaseDataEditAction'; import type { IResultSetColumnKey, IResultSetElementKey, IResultSetRowKey } from './IResultSetDataKey'; import { isResultSetContentValue } from './isResultSetContentValue'; import { ResultSetDataAction } from './ResultSetDataAction'; @@ -156,11 +156,11 @@ export class ResultSetEditAction resultId: this.result.id, type: update.type, revert: false, - value: { + value: [{ key, prevValue, value, - }, + }], }); this.removeEmptyUpdate(update); @@ -199,18 +199,50 @@ export class ResultSetEditAction resultId: this.result.id, type: update.type, revert: false, - value: { + value: [{ key: { column, row }, - }, + }], }); } } - delete(key: IResultSetElementKey): void { - this.deleteRow(key.row, key.column); + delete(...keys: IResultSetElementKey[]): void { + const reverted: Array> = []; + const deleted: Array> = []; + + for (const key of keys) { + const serializedKey = ResultSetDataKeysUtils.serialize(key.row); + const update = this.editorData.get(serializedKey); + + if (update?.type === DatabaseEditChangeType.add) { + reverted.push({ key }); + this.editorData.delete(serializedKey); + } else { + this.deleteRow(key.row, key.column, true); + deleted.push({ key }); + } + } + + if (reverted.length > 0) { + this.action.execute({ + resultId: this.result.id, + type: DatabaseEditChangeType.add, + revert: true, + value: reverted, + }); + } + + if (deleted.length > 0) { + this.action.execute({ + resultId: this.result.id, + type: DatabaseEditChangeType.delete, + revert: false, + value: deleted, + }); + } } - deleteRow(key: IResultSetRowKey, column?: IResultSetColumnKey): void { + deleteRow(key: IResultSetRowKey, column?: IResultSetColumnKey, silent?: boolean): void { const serializedKey = ResultSetDataKeysUtils.serialize(key); const update = this.editorData.get(serializedKey); @@ -225,24 +257,24 @@ export class ResultSetEditAction if (update?.type !== DatabaseEditChangeType.add) { const [update, created] = this.getOrCreateUpdate(key, DatabaseEditChangeType.delete); - if (created) { + if (created && !silent) { this.action.execute({ resultId: this.result.id, type: update.type, revert: false, - value: { + value: [{ key: { column, row: key }, - }, + }], }); } - } else { + } else if (!silent) { this.action.execute({ resultId: this.result.id, type: update.type, revert: true, - value: { + value: [{ key: { column, row: key }, - }, + }], }); } } @@ -318,11 +350,11 @@ export class ResultSetEditAction resultId: this.result.id, type: update.type, revert: true, - value: { + value: [{ key, prevValue, value, - }, + }], }); this.removeEmptyUpdate(update); diff --git a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetSelectAction.ts b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetSelectAction.ts index 623a0889c3..b833251cf8 100644 --- a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetSelectAction.ts +++ b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetSelectAction.ts @@ -6,7 +6,7 @@ * you may not use this file except in compliance with the License. */ -import { computed, IReactionDisposer, makeObservable, observable, observe, toJS } from 'mobx'; +import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx'; import { Executor, IExecutor } from '@cloudbeaver/core-executor'; import { ResultDataFormat } from '@cloudbeaver/core-sdk'; @@ -37,7 +37,7 @@ export class ResultSetSelectAction extends DatabaseSelectAction void; + private validationDisposer: IReactionDisposer; constructor(source: IDatabaseDataSource, result: IDatabaseResultSet) { super(source, result); @@ -51,16 +51,12 @@ export class ResultSetSelectAction extends DatabaseSelectAction { - const previous = change.oldValue as IResultSetRowKey[] | undefined; - const current = change.newValue as IResultSetRowKey[]; - - if (!previous) { - return; - } - + this.validationDisposer = reaction(() => this.view.rowKeys, (current, previous) => { if (this.focusedElement) { const focus = this.focusedElement; const focusIndex = previous.findIndex(key => ResultSetDataKeysUtils.isEqual(key, focus.row)); @@ -274,22 +270,22 @@ export class ResultSetSelectAction extends DatabaseSelectAction 0) { + this.focus(data.value[data.value.length - 1].key); } this.clear(); } break; case DatabaseEditChangeType.delete: - if (data.value) { - this.focus(data.value.key); + if (data.value && data.value.length > 0) { + this.focus(data.value[data.value.length - 1].key); this.clear(); } break; case DatabaseEditChangeType.update: - if (data.value) { - this.focus(data.value.key); + if (data.value && data.value.length > 0) { + this.focus(data.value[data.value.length - 1].key); } break; } diff --git a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetViewAction.ts b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetViewAction.ts index ade6435731..42d42bf86b 100644 --- a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetViewAction.ts +++ b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetViewAction.ts @@ -6,7 +6,7 @@ * you may not use this file except in compliance with the License. */ -import { computed, extendObservable, IComputedValue, makeObservable, observe } from 'mobx'; +import { computed, makeObservable } from 'mobx'; import { DataTypeLogicalOperation, ResultDataFormat, SqlResultColumn } from '@cloudbeaver/core-sdk'; diff --git a/webapp/packages/plugin-data-viewer/src/TableViewer/TableFooter/TableFooterMenu/TableFooterMenuService.ts b/webapp/packages/plugin-data-viewer/src/TableViewer/TableFooter/TableFooterMenu/TableFooterMenuService.ts index bbdb4d22fa..3a4004a473 100644 --- a/webapp/packages/plugin-data-viewer/src/TableViewer/TableFooter/TableFooterMenu/TableFooterMenuService.ts +++ b/webapp/packages/plugin-data-viewer/src/TableViewer/TableFooter/TableFooterMenu/TableFooterMenuService.ts @@ -44,23 +44,23 @@ export class TableFooterMenuService { return context.contextType === TableFooterMenuService.nodeContextType; }, isHidden(context) { + if (context.data.model.isReadonly()) { + return true; + } + const editor = context.data.model.source.getActionImplementation( context.data.resultIndex, DatabaseEditAction ); - return ( - context.data.model.isReadonly() - || context.data.model.isDisabled(context.data.resultIndex) - || !editor?.hasFeature('add') - ); + return !editor?.hasFeature('add'); }, isDisabled(context) { - if (context.data.model.isLoading()) { - return true; - } - - return false; + return ( + context.data.model.isLoading() + || context.data.model.isDisabled(context.data.resultIndex) + || !context.data.model.source.hasResult(context.data.resultIndex) + ); }, onClick(context) { const editor = context.data.model.source.getActionImplementation( @@ -89,19 +89,23 @@ export class TableFooterMenuService { return context.contextType === TableFooterMenuService.nodeContextType; }, isHidden(context) { + if (context.data.model.isReadonly()) { + return true; + } + const editor = context.data.model.source.getActionImplementation( context.data.resultIndex, DatabaseEditAction ); - return ( - context.data.model.isReadonly() - || context.data.model.isDisabled(context.data.resultIndex) - || !editor?.hasFeature('delete') - ); + return !editor?.hasFeature('delete'); }, isDisabled(context) { - if (context.data.model.isLoading()) { + if ( + context.data.model.isLoading() + || context.data.model.isDisabled(context.data.resultIndex) + || !context.data.model.source.hasResult(context.data.resultIndex) + ) { return true; } @@ -130,9 +134,7 @@ export class TableFooterMenuService { const selectedElements = getActiveElements(context.data.model, context.data.resultIndex); - for (const key of selectedElements) { - editor?.delete(key); - } + editor?.delete(...selectedElements); }, }); this.registerMenuItem({ @@ -144,10 +146,7 @@ export class TableFooterMenuService { return context.contextType === TableFooterMenuService.nodeContextType; }, isHidden(context) { - if ( - context.data.model.isReadonly() - || context.data.model.isDisabled(context.data.resultIndex) - ) { + if (context.data.model.isReadonly()) { return true; } @@ -159,6 +158,14 @@ export class TableFooterMenuService { return !editor; }, isDisabled(context) { + if ( + context.data.model.isLoading() + || context.data.model.isDisabled(context.data.resultIndex) + || !context.data.model.source.hasResult(context.data.resultIndex) + ) { + return true; + } + const editor = context.data.model.source.getActionImplementation( context.data.resultIndex, DatabaseEditAction @@ -167,8 +174,7 @@ export class TableFooterMenuService { const selectedElements = getActiveElements(context.data.model, context.data.resultIndex); return ( - context.data.model.isLoading() - || !editor + !editor || selectedElements.length === 0 || !selectedElements.some(key => { const state = editor.getElementState(key); @@ -189,9 +195,7 @@ export class TableFooterMenuService { const selectedElements = getActiveElements(context.data.model, context.data.resultIndex); - for (const element of selectedElements) { - editor?.revert(element); - } + editor?.revert(...selectedElements); }, }); this.registerMenuItem({ @@ -202,6 +206,9 @@ export class TableFooterMenuService { isPresent(context) { return context.contextType === TableFooterMenuService.nodeContextType; }, + isHidden(context) { + return context.data.model.isReadonly(); + }, isDisabled(context) { if ( context.data.model.isLoading() @@ -230,6 +237,9 @@ export class TableFooterMenuService { isPresent(context) { return context.contextType === TableFooterMenuService.nodeContextType; }, + isHidden(context) { + return context.data.model.isReadonly(); + }, isDisabled(context) { if ( context.data.model.isLoading()