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 c16ac60789..96905cc303 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 @@ -11,15 +11,19 @@ import { useCallback, useContext } from 'react'; import type { CellRendererProps } from 'react-data-grid'; import { Cell } from 'react-data-grid'; +import { ResultSetFormatAction } from '@cloudbeaver/plugin-data-viewer'; + import { EditingContext } from '../../Editing/EditingContext'; import { DataGridContext } from '../DataGridContext'; import { DataGridSelectionContext } from '../DataGridSelection/DataGridSelectionContext'; +import { TableDataContext } from '../TableDataContext'; export const CellRenderer: React.FC> = observer(function CellRenderer(props) { - const context = useContext(DataGridContext); + const dataGridContext = useContext(DataGridContext); + const tableDataContext = useContext(TableDataContext); const selectionContext = useContext(DataGridSelectionContext); const editingContext = useContext(EditingContext); - const editor = context?.model.source.getEditor(context.resultIndex); + const editor = dataGridContext?.model.source.getEditor(dataGridContext.resultIndex); const classes: string[] = []; const { rowIdx, column } = props; @@ -64,10 +68,16 @@ export const CellRenderer: React.FC> = observer(function if (!column.editable) { return; } + const format = dataGridContext?.model.source.getAction(dataGridContext.resultIndex, ResultSetFormatAction); + const columnIndex = tableDataContext?.getDataColumnIndexFromKey(column.key); - const cellValue = props.row[column.key]; - - if (cellValue !== null && typeof cellValue === 'object') { + if ( + !columnIndex + || format?.isReadOnly({ + row: rowIdx, + column: columnIndex, + }) + ) { return; } diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridSelection/useGridSelectionContext.tsx b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridSelection/useGridSelectionContext.tsx index 3c6de3799c..48a3a5af70 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridSelection/useGridSelectionContext.tsx +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridSelection/useGridSelectionContext.tsx @@ -10,7 +10,7 @@ import { action, observable } from 'mobx'; import { useState } from 'react'; import { useObjectRef } from '@cloudbeaver/core-blocks'; -import type { IResultSetSelectKey, ResultSetSelectAction } from '@cloudbeaver/plugin-data-viewer'; +import type { IResultSetElementKey, ResultSetSelectAction } from '@cloudbeaver/plugin-data-viewer'; import type { ITableData } from '../TableDataContext'; import type { IDraggingPosition } from '../useGridDragging'; @@ -172,7 +172,7 @@ export function useGridSelectionContext( const { selectionAction } = props; temporarySelection.clear(); - const key: IResultSetSelectKey = { + const key: IResultSetElementKey = { column: columnIdx, row: 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 668072587e..3d71b68b01 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx @@ -51,9 +51,9 @@ 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 modelResultData = model.getResult(resultIndex); - const tableData = useTableData(modelResultData); + const tableData = useTableData(model, resultIndex); const gridSortingContext = useGridSortingContext(model); const gridSelectionContext = useGridSelectionContext(tableData, selectionAction); diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableDataContext.ts b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableDataContext.ts index f530280e70..f826fd4312 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableDataContext.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableDataContext.ts @@ -19,6 +19,7 @@ export interface ITableData { getCellValue: (rowIndex: number, key: string | number) => any; getColumnInfo: (key: string | number) => SqlResultColumn | undefined; getColumnsInRange: (startIndex: number, endIndex: number) => Array>; + getDataColumnIndexFromKey: (columnKey: string | number) => number | null; getColumnIndexFromKey: (columnKey: string | number) => number | null; isIndexColumn: (columnKey: string | number) => boolean; isIndexColumnInRange: (columnsRange: Array>) => boolean; diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/useTableData.ts b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/useTableData.ts index ed9e03d5d9..6d6da61b4d 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/useTableData.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/useTableData.ts @@ -13,7 +13,7 @@ import type { Column } from 'react-data-grid'; import { useObjectRef } from '@cloudbeaver/core-blocks'; import type { SqlResultColumn } from '@cloudbeaver/core-sdk'; import { TextTools } from '@cloudbeaver/core-utils'; -import type { IDatabaseResultSet } from '@cloudbeaver/plugin-data-viewer'; +import { IDatabaseDataModel, IDatabaseResultSet, ResultSetFormatAction } from '@cloudbeaver/plugin-data-viewer'; import { ResultSetTools } from '../ResultSetTools'; import { IndexFormatter } from './Formatters/IndexFormatter'; @@ -32,13 +32,17 @@ export const indexColumn: Column = { formatter: IndexFormatter, }; -export function useTableData(modelResultData: IDatabaseResultSet | null): ITableData { - const props = useObjectRef({ modelResultData }, undefined, true); +export function useTableData(model: IDatabaseDataModel, resultIndex: number): ITableData { + const modelResultData = model.getResult(resultIndex); + const props = useObjectRef({ modelResultData, model, resultIndex }, undefined, true); const [state] = useState(() => computed(() => { if (!props.modelResultData?.data) { return { columns: [], rows: [] }; } + const format = model.source.getAction(resultIndex, ResultSetFormatAction); + + // TODO: seems it must be moved to ResultSetFormatAction const columnNames = ResultSetTools.getHeaders(props.modelResultData.data); const rowStrings = ResultSetTools.getLongestCells(props.modelResultData.data); @@ -60,7 +64,7 @@ export function useTableData(modelResultData: IDatabaseResultSet | null): ITable const columns = props.modelResultData.data?.columns!.map>((col, columnIndex) => ({ key: columnIndex + '', name: col.label!, - editable: !col.readOnly, + editable: !format.isReadOnly({ column: columnIndex }), width: Math.min(300, measuredCells[columnIndex]), headerRenderer: TableColumnHeader, })) || []; @@ -84,7 +88,16 @@ export function useTableData(modelResultData: IDatabaseResultSet | null): ITable return this.rows[rowIndex][key as number]; }, getColumnInfo(key: string | number): SqlResultColumn | undefined { - return props.modelResultData?.data?.columns?.[Number(key)]; + return this.dataColumns[Number(key)]; + }, + getDataColumnIndexFromKey(key: string | number) { + const info = this.getColumnInfo(key); + + if (!info) { + return null; + } + + return Number(key); }, getColumnIndexFromKey(key: string | number) { const index = this.columns.findIndex((column: any) => column.key === String(key)); diff --git a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/IDatabaseDataFormatAction.ts b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/IDatabaseDataFormatAction.ts index 48024e1d3c..d7cefe4b45 100644 --- a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/IDatabaseDataFormatAction.ts +++ b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/IDatabaseDataFormatAction.ts @@ -9,8 +9,9 @@ import type { IDatabaseDataAction } from '../IDatabaseDataAction'; import type { IDatabaseDataResult } from '../IDatabaseDataResult'; -export interface IDatabaseDataFormatAction +export interface IDatabaseDataFormatAction extends IDatabaseDataAction { + isReadOnly: (key: TKey) => boolean; get: (value: any) => any; toString: (value: any) => string; } diff --git a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/IResultSetElementKey.ts b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/IResultSetElementKey.ts new file mode 100644 index 0000000000..4526c2be4b --- /dev/null +++ b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/IResultSetElementKey.ts @@ -0,0 +1,12 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2021 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +export interface IResultSetElementKey { + readonly row?: number; + readonly column?: number; +} diff --git a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSetFormatAction.ts b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetFormatAction.ts similarity index 58% rename from webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSetFormatAction.ts rename to webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetFormatAction.ts index 46df0c8638..528a2323a7 100644 --- a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSetFormatAction.ts +++ b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetFormatAction.ts @@ -8,12 +8,13 @@ import { ResultDataFormat } from '@cloudbeaver/core-sdk'; -import type { IDatabaseResultSet } from '../IDatabaseResultSet'; -import { databaseDataAction } from './DatabaseDataActionDecorator'; -import type { IDatabaseDataFormatAction } from './IDatabaseDataFormatAction'; +import type { IDatabaseResultSet } from '../../IDatabaseResultSet'; +import { databaseDataAction } from '../DatabaseDataActionDecorator'; +import type { IDatabaseDataFormatAction } from '../IDatabaseDataFormatAction'; +import type { IResultSetElementKey } from './IResultSetElementKey'; @databaseDataAction() -export class ResultSetFormatAction implements IDatabaseDataFormatAction { +export class ResultSetFormatAction implements IDatabaseDataFormatAction { static dataFormat = ResultDataFormat.Resultset; result: IDatabaseResultSet; @@ -21,6 +22,26 @@ export class ResultSetFormatAction implements IDatabaseDataFormatAction { +export class ResultSetSelectAction implements IDatabaseDataSelectAction { static dataFormat = ResultDataFormat.Resultset; result: IDatabaseResultSet; - readonly actions: IExecutor>; + readonly actions: IExecutor>; readonly selectedElements: Map; - private focusedElement: IResultSetSelectKey | null; + private focusedElement: IResultSetElementKey | null; constructor(result: IDatabaseResultSet) { this.result = result; @@ -51,7 +47,7 @@ export class ResultSetSelectAction implements IDatabaseDataSelectAction 0; } - isElementSelected(key: IResultSetSelectKey): boolean { + isElementSelected(key: IResultSetElementKey): boolean { if (key.row === undefined) { const rows = this.result.data?.rows?.length || 0; for (let row = 0; row < rows; row++) { @@ -76,12 +72,12 @@ export class ResultSetSelectAction implements IDatabaseDataSelectAction> { - const selectedKeys: Array> = []; + getSelectedElements(): Array> { + const selectedKeys: Array> = []; for (const [row, value] of this.selectedElements) { selectedKeys.push(...value.map(column => ({ row, column }))); @@ -94,7 +90,7 @@ export class ResultSetSelectAction implements IDatabaseDataSelectAction | undefined; + let firstSelectedCell: Required | undefined; + let readonly = true; if (result?.data?.rows && selectedCells.length > 0) { + const format = model.source.getAction(resultIndex, ResultSetFormatAction); + firstSelectedCell = selectedCells[0]; value = model.source .getEditor(resultIndex) .getCell(firstSelectedCell.row, firstSelectedCell.column); - stringValue = model.source - .getAction(resultIndex, ResultSetFormatAction) - .get(value); + stringValue = format.get(value); + readonly = format.isReadOnly(firstSelectedCell); } const handleChange = (value: string) => { @@ -62,7 +65,7 @@ export const TextValuePresentation: TabContainerPanelComponent diff --git a/webapp/packages/plugin-data-viewer/src/index.ts b/webapp/packages/plugin-data-viewer/src/index.ts index 10631d9967..f2fa42de8c 100644 --- a/webapp/packages/plugin-data-viewer/src/index.ts +++ b/webapp/packages/plugin-data-viewer/src/index.ts @@ -2,11 +2,12 @@ import { manifest } from './manifest'; export default manifest; +export * from './DatabaseDataModel/Actions/ResultSet/IResultSetElementKey'; +export * from './DatabaseDataModel/Actions/ResultSet/ResultSetFormatAction'; +export * from './DatabaseDataModel/Actions/ResultSet/ResultSetSelectAction'; export * from './DatabaseDataModel/Actions/DatabaseDataActionDecorator'; export * from './DatabaseDataModel/Actions/IDatabaseDataFormatAction'; export * from './DatabaseDataModel/Actions/IDatabaseDataSelectAction'; -export * from './DatabaseDataModel/Actions/ResultSetFormatAction'; -export * from './DatabaseDataModel/Actions/ResultSetSelectAction'; export * from './DatabaseDataModel/DatabaseDataActions'; export * from './DatabaseDataModel/DatabaseDataEditor'; export * from './DatabaseDataModel/DatabaseDataFormat';