From 7afbc8ec028722b3f01d0f696754566fd3925b6e Mon Sep 17 00:00:00 2001 From: Naumov Alexey Date: Mon, 5 Apr 2021 14:05:33 +0300 Subject: [PATCH 1/4] fix(plugin-data-spreadsheet-new): CB-743 don't copy null values to clipboard --- .../DataGrid/CellRenderer/CellRenderer.tsx | 8 +++- .../src/DataGrid/DataGridTable.tsx | 3 +- .../src/DataGrid/useGridSelectedCellsCopy.ts | 40 +++++++++++-------- .../src/styles/themes/_base-react-grid.scss | 8 +++- 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/CellRenderer/CellRenderer.tsx b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/CellRenderer/CellRenderer.tsx index 5778bceec0..1c3a726840 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/CellRenderer/CellRenderer.tsx +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/CellRenderer/CellRenderer.tsx @@ -30,7 +30,13 @@ export const CellRenderer: React.FC> = observer(function const { rowIdx, column } = props; if (selectionContext?.isSelected(rowIdx, column.idx)) { - classes.push('rdg-cell-custom-selected'); + const columnIndex = tableDataContext?.getDataColumnIndexFromKey(column.key) ?? null; + + if (columnIndex !== null && editor?.getCell(rowIdx, columnIndex) === null) { + classes.push('rdg-cell-custom-null-selected'); + } else { + classes.push('rdg-cell-custom-selected'); + } } if (editingContext?.isEditing({ idx: column.idx, rowIdx })) { 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 95488c2ed6..5b74accbd6 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx @@ -51,7 +51,6 @@ export const DataGridTable: React.FC = observer(function DataGridTable({ const [columnResize] = useState(() => new Executor()); const selectionAction = model.source.getAction(resultIndex, ResultSetSelectAction); - const modelResultData = model.getResult(resultIndex); const tableData = useTableData(model, resultIndex); @@ -78,7 +77,7 @@ export const DataGridTable: React.FC = observer(function DataGridTable({ }, }); - const { onKeydownHandler } = useGridSelectedCellsCopy(modelResultData, gridSelectionContext); + const { onKeydownHandler } = useGridSelectedCellsCopy(model, resultIndex, gridSelectionContext); const { onMouseDownHandler, onMouseMoveHandler } = useGridDragging({ onDragStart: startPosition => { dataGridRef.current?.selectCell({ idx: startPosition.colIdx, rowIdx: startPosition.rowIdx }); diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/useGridSelectedCellsCopy.ts b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/useGridSelectedCellsCopy.ts index 4314ed4c6d..217007e541 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/useGridSelectedCellsCopy.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/useGridSelectedCellsCopy.ts @@ -9,9 +9,8 @@ import { useCallback } from 'react'; import { useObjectRef } from '@cloudbeaver/core-blocks'; -import type { SqlResultSet } from '@cloudbeaver/core-sdk'; import { copyToClipboard } from '@cloudbeaver/core-utils'; -import type { IDatabaseDataResult } from '@cloudbeaver/plugin-data-viewer'; +import { IDatabaseDataModel, IDatabaseResultSet, ResultSetFormatAction } from '@cloudbeaver/plugin-data-viewer'; import type { IDataGridSelectionContext } from './DataGridSelection/DataGridSelectionContext'; @@ -19,11 +18,12 @@ const EVENT_KEY_CODE = { C: 'KeyC', }; -function copyGridSelectedDataToClipboard(modelData: SqlResultSet, selectedCells: Map) { - if (!modelData.rows) { - return; - } - +function copyGridSelectedDataToClipboard( + model: IDatabaseDataModel, + resultIndex: number, + selectedCells: Map +) { + const format = model.source.getAction(resultIndex, ResultSetFormatAction); const orderedSelectedCells: Map = new Map([...selectedCells].sort((a, b) => a[0] - b[0])); const selectedColumns: Set = new Set(); @@ -34,7 +34,7 @@ function copyGridSelectedDataToClipboard(modelData: SqlResultSet, selectedCells: } } - const columns = [...selectedColumns].sort(); + const columns = [...selectedColumns].sort((a, b) => a - b); let data = ''; for (const [rowIdx, colIndexes] of orderedSelectedCells) { @@ -44,8 +44,12 @@ function copyGridSelectedDataToClipboard(modelData: SqlResultSet, selectedCells: } if (colIndexes.includes(column)) { - const value = modelData.rows?.[rowIdx][column]; - data += value; + const cell = model.source.getEditor(resultIndex).getCell(rowIdx, column); + const cellValue = format.get(cell); + if (cellValue === null) { + continue; + } + data += cellValue; } } data += '\r\n'; @@ -58,17 +62,19 @@ function copyGridSelectedDataToClipboard(modelData: SqlResultSet, selectedCells: type IKeyboardEvent = React.KeyboardEvent & KeyboardEvent; export function useGridSelectedCellsCopy( - modelResultData: IDatabaseDataResult | null, + model: IDatabaseDataModel, + resultIndex: number, selectionContext: IDataGridSelectionContext ) { - const props = useObjectRef({ modelResultData, selectionContext }); - const onKeydownHandler = useCallback((event: IKeyboardEvent) => { - if (!props.modelResultData) { - return; - } + const props = useObjectRef({ model, resultIndex, selectionContext }); + const onKeydownHandler = useCallback((event: IKeyboardEvent) => { if ((event.ctrlKey || event.metaKey) && event.code === EVENT_KEY_CODE.C) { - copyGridSelectedDataToClipboard(props.modelResultData.data, props.selectionContext.selectedCells); + copyGridSelectedDataToClipboard( + props.model, + props.resultIndex, + props.selectionContext.selectedCells + ); } }, []); diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/styles/themes/_base-react-grid.scss b/webapp/packages/plugin-data-spreadsheet-new/src/styles/themes/_base-react-grid.scss index 52afbbd717..56491db925 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/styles/themes/_base-react-grid.scss +++ b/webapp/packages/plugin-data-spreadsheet-new/src/styles/themes/_base-react-grid.scss @@ -16,9 +16,14 @@ $edited-color: #ff99006b; .rdg-cell-custom-selected:not(.rdg-cell-custom-editing) { background-color: rgba(0, 145, 234, 0.3); } + .rdg-cell-custom-null-selected:not(.rdg-cell-custom-editing) { + background-color: rgba(0, 145, 234, 0.1); + } + .rdg-cell-selected { box-shadow: inset 0 0 0 1px #0091ea; } + .rdg-cell-custom-editing { box-shadow: none; } @@ -56,7 +61,8 @@ $edited-color: #ff99006b; @include mdc-theme-prop(background-color, surface, false); } - .rdg-cell-custom-selected { + .rdg-cell-custom-selected, + .rdg-cell-custom-null-selected { background-color: rgba(150, 150, 150, 0.3); box-shadow: none; } From 6aeb8b4d563082376f27c648390adf2bcee146d5 Mon Sep 17 00:00:00 2001 From: Naumov Alexey Date: Mon, 5 Apr 2021 17:00:48 +0300 Subject: [PATCH 2/4] fix(plugin-data-spreadsheet-new): CB-743 remove unnecessary css class --- .../src/DataGrid/CellRenderer/CellRenderer.tsx | 8 +------- .../src/styles/themes/_base-react-grid.scss | 6 +----- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/CellRenderer/CellRenderer.tsx b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/CellRenderer/CellRenderer.tsx index 1c3a726840..5778bceec0 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/CellRenderer/CellRenderer.tsx +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/CellRenderer/CellRenderer.tsx @@ -30,13 +30,7 @@ export const CellRenderer: React.FC> = observer(function const { rowIdx, column } = props; if (selectionContext?.isSelected(rowIdx, column.idx)) { - const columnIndex = tableDataContext?.getDataColumnIndexFromKey(column.key) ?? null; - - if (columnIndex !== null && editor?.getCell(rowIdx, columnIndex) === null) { - classes.push('rdg-cell-custom-null-selected'); - } else { - classes.push('rdg-cell-custom-selected'); - } + classes.push('rdg-cell-custom-selected'); } if (editingContext?.isEditing({ idx: column.idx, rowIdx })) { diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/styles/themes/_base-react-grid.scss b/webapp/packages/plugin-data-spreadsheet-new/src/styles/themes/_base-react-grid.scss index 56491db925..5ffd6cf884 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/styles/themes/_base-react-grid.scss +++ b/webapp/packages/plugin-data-spreadsheet-new/src/styles/themes/_base-react-grid.scss @@ -16,9 +16,6 @@ $edited-color: #ff99006b; .rdg-cell-custom-selected:not(.rdg-cell-custom-editing) { background-color: rgba(0, 145, 234, 0.3); } - .rdg-cell-custom-null-selected:not(.rdg-cell-custom-editing) { - background-color: rgba(0, 145, 234, 0.1); - } .rdg-cell-selected { box-shadow: inset 0 0 0 1px #0091ea; @@ -61,8 +58,7 @@ $edited-color: #ff99006b; @include mdc-theme-prop(background-color, surface, false); } - .rdg-cell-custom-selected, - .rdg-cell-custom-null-selected { + .rdg-cell-custom-selected { background-color: rgba(150, 150, 150, 0.3); box-shadow: none; } From 562a5abd342fe7c864b624c9dc794f7958e227ba Mon Sep 17 00:00:00 2001 From: Naumov Alexey Date: Mon, 5 Apr 2021 17:28:44 +0300 Subject: [PATCH 3/4] fix(plugin-data-spreadsheet-new): CB-743 moved getEditor() from for loop --- .../src/DataGrid/useGridSelectedCellsCopy.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/useGridSelectedCellsCopy.ts b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/useGridSelectedCellsCopy.ts index 217007e541..93dc194b28 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/useGridSelectedCellsCopy.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/useGridSelectedCellsCopy.ts @@ -24,6 +24,7 @@ function copyGridSelectedDataToClipboard( selectedCells: Map ) { const format = model.source.getAction(resultIndex, ResultSetFormatAction); + const editor = model.source.getEditor(resultIndex); const orderedSelectedCells: Map = new Map([...selectedCells].sort((a, b) => a[0] - b[0])); const selectedColumns: Set = new Set(); @@ -44,7 +45,7 @@ function copyGridSelectedDataToClipboard( } if (colIndexes.includes(column)) { - const cell = model.source.getEditor(resultIndex).getCell(rowIdx, column); + const cell = editor.getCell(rowIdx, column); const cellValue = format.get(cell); if (cellValue === null) { continue; From 57a2ccccae3ab6d7bd2b55f8cb6ce596cd46ea72 Mon Sep 17 00:00:00 2001 From: Naumov Alexey Date: Tue, 6 Apr 2021 17:19:31 +0300 Subject: [PATCH 4/4] fix(plugin-data-spreadsheet-new): CB-743 rewrite copy logic --- .../src/DataGrid/DataGridTable.tsx | 2 +- .../src/DataGrid/useGridSelectedCellsCopy.ts | 35 ++++++++++--------- 2 files changed, 20 insertions(+), 17 deletions(-) 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 5b74accbd6..65f5f7ab62 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx @@ -77,7 +77,7 @@ export const DataGridTable: React.FC = observer(function DataGridTable({ }, }); - const { onKeydownHandler } = useGridSelectedCellsCopy(model, resultIndex, gridSelectionContext); + const { onKeydownHandler } = useGridSelectedCellsCopy(model, resultIndex, tableData, gridSelectionContext); const { onMouseDownHandler, onMouseMoveHandler } = useGridDragging({ onDragStart: startPosition => { dataGridRef.current?.selectCell({ idx: startPosition.colIdx, rowIdx: startPosition.rowIdx }); diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/useGridSelectedCellsCopy.ts b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/useGridSelectedCellsCopy.ts index 93dc194b28..02cb63fefa 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/useGridSelectedCellsCopy.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/useGridSelectedCellsCopy.ts @@ -13,6 +13,7 @@ import { copyToClipboard } from '@cloudbeaver/core-utils'; import { IDatabaseDataModel, IDatabaseResultSet, ResultSetFormatAction } from '@cloudbeaver/plugin-data-viewer'; import type { IDataGridSelectionContext } from './DataGridSelection/DataGridSelectionContext'; +import type { ITableData } from './TableDataContext'; const EVENT_KEY_CODE = { C: 'KeyC', @@ -21,42 +22,42 @@ const EVENT_KEY_CODE = { function copyGridSelectedDataToClipboard( model: IDatabaseDataModel, resultIndex: number, + tableData: ITableData, selectedCells: Map ) { const format = model.source.getAction(resultIndex, ResultSetFormatAction); const editor = model.source.getEditor(resultIndex); + const orderedSelectedCells: Map = new Map([...selectedCells].sort((a, b) => a[0] - b[0])); const selectedColumns: Set = new Set(); - for (const colIndexes of orderedSelectedCells.values()) { for (const colIdx of colIndexes) { selectedColumns.add(colIdx); } } - const columns = [...selectedColumns].sort((a, b) => a - b); - - let data = ''; + const rowsValues: string[] = []; for (const [rowIdx, colIndexes] of orderedSelectedCells) { - for (const column of columns) { - if (column !== columns[0]) { - data += '\t'; + const rowCellsValues: string[] = []; + for (const column of tableData.columns) { + const columnIdx = tableData.getDataColumnIndexFromKey(column.key); + if (columnIdx === null || !selectedColumns.has(columnIdx)) { + continue; } - if (colIndexes.includes(column)) { - const cell = editor.getCell(rowIdx, column); + if (colIndexes.includes(columnIdx)) { + const cell = editor.getCell(rowIdx, columnIdx); const cellValue = format.get(cell); - if (cellValue === null) { - continue; - } - data += cellValue; + rowCellsValues.push(cellValue ?? ''); + } else { + rowCellsValues.push(''); } } - data += '\r\n'; + rowsValues.push(rowCellsValues.join('\t')); } - copyToClipboard(data); + copyToClipboard(rowsValues.join('\r\n')); } // needed for event.code @@ -65,15 +66,17 @@ type IKeyboardEvent = React.KeyboardEvent & KeyboardEvent; export function useGridSelectedCellsCopy( model: IDatabaseDataModel, resultIndex: number, + tableData: ITableData, selectionContext: IDataGridSelectionContext ) { - const props = useObjectRef({ model, resultIndex, selectionContext }); + const props = useObjectRef({ model, resultIndex, tableData, selectionContext }); const onKeydownHandler = useCallback((event: IKeyboardEvent) => { if ((event.ctrlKey || event.metaKey) && event.code === EVENT_KEY_CODE.C) { copyGridSelectedDataToClipboard( props.model, props.resultIndex, + props.tableData, props.selectionContext.selectedCells ); }