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 a2c7cba1e2..14112045bc 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 @@ -10,32 +10,28 @@ import { observer } from 'mobx-react-lite'; import { useCallback, useContext } from 'react'; import type { CellRendererProps } from 'react-data-grid'; import { Cell } from 'react-data-grid'; -import styled, { css, use } from 'reshadow'; import { DataGridSelectionContext } from '../DataGridSelection/DataGridSelectionContext'; -const styles = css` - Cell[|selected] { - background-color: rgba(0, 145, 234, 0.3); - } -`; - export const CellRenderer: React.FC> = observer(function CellRenderer(props) { const gridSelectionContext = useContext(DataGridSelectionContext); if (!gridSelectionContext) { throw new Error('Grid selection context must be provided'); } + let classes = ''; const { rowIdx, column } = props; const { isSelected, select } = gridSelectionContext; - const selected = isSelected(column.key, rowIdx); + if (isSelected(column.key, rowIdx)) { + classes += 'rdg-cell-custom-selected'; + } const onClickHandler = useCallback((event: React.MouseEvent) => { select(column.key, rowIdx, event.ctrlKey, event.shiftKey); }, [column, rowIdx, select]); - return styled(styles)( - + return ( + ); }); 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 a6443d1dca..a5601f7542 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx @@ -6,15 +6,12 @@ * you may not use this file except in compliance with the License. */ -import 'react-data-grid/dist/react-data-grid.css'; - import { computed } from 'mobx'; import { observer } from 'mobx-react-lite'; import { useCallback, useEffect, useMemo } from 'react'; import DataGrid from 'react-data-grid'; import type { Column } from 'react-data-grid'; import styled from 'reshadow'; -import { css } from 'reshadow'; import type { SqlResultSet } from '@cloudbeaver/core-sdk'; import { useStyles } from '@cloudbeaver/core-theming'; @@ -22,22 +19,19 @@ import { TextTools } from '@cloudbeaver/core-utils'; import type { IDatabaseDataModel } from '@cloudbeaver/plugin-data-viewer'; import { ResultSetTools } from '../ResultSetTools'; -import { CellFormatter } from './CellFormatter/CellFormatter'; +import baseStyles from '../styles/base.scss'; +import { reactGridStyles } from '../styles/styles'; import { DataGridContext } from './DataGridContext'; import { DataGridSelectionContext } from './DataGridSelection/DataGridSelectionContext'; import { useGridSelectionContext } from './DataGridSelection/useGridSelectionContext'; import { DataGridSortingContext } from './DataGridSorting/DataGridSortingContext'; import { useGridSortingContext } from './DataGridSorting/useGridSortingContext'; import { DataGridTableContainer } from './DataGridTableContainer'; +import { CellFormatter } from './Formatters/CellFormatter'; +import { IndexFormatter } from './Formatters/IndexFormatter'; import { RowRenderer } from './RowRenderer/RowRenderer'; import { TableColumnHeaderNew } from './TableColumnHeader/TableColumnHeader-new'; -const baseStyles = css` - DataGrid { - flex: 1; - } -`; - interface Props { model: IDatabaseDataModel; resultIndex: number; @@ -51,15 +45,16 @@ function isAtBottom(event: React.UIEvent): boolean { const indexColumn: Column = { key: Number.MAX_SAFE_INTEGER + '', - name: '#', - width: 100, + name:
#
, + minWidth: 60, + width: 60, resizable: false, frozen: true, - formatter: ({ rowIdx }: any) => {rowIdx + 1}, + formatter: IndexFormatter, }; -export const DataGridTable: React.FC = observer(function DataGridTable({ model, resultIndex }) { - const styles = useStyles(baseStyles); +export const DataGridTable: React.FC = observer(function DataGridTable({ model, resultIndex, className }) { + const styles = useStyles(reactGridStyles, baseStyles); const modelResultData = model?.getResult(resultIndex); @@ -108,7 +103,7 @@ export const DataGridTable: React.FC = observer(function DataGridTable({ } return rowStrings[i]; }), - }).map(v => v + 16 + 32 + 20 + 24); + }).map(v => v + 16 + 32 + 20); // TODO: we need some result type specified formatter to common actions with data const rows = (modelResultData.data as SqlResultSet).rows || []; @@ -117,6 +112,7 @@ export const DataGridTable: React.FC = observer(function DataGridTable({ key: i + '', name: col.name!, width: Math.min(300, measuredCells[i]), + minWidth: 40, resizable: true, headerRenderer: TableColumnHeaderNew, formatter: CellFormatter, @@ -130,10 +126,11 @@ export const DataGridTable: React.FC = observer(function DataGridTable({ - + & KeyboardEvent; -export const DataGridTableContainer: React.FC = function DataGridTableContainer({ modelResultData, children }) { +export const DataGridTableContainer: React.FC = function DataGridTableContainer({ + modelResultData, children, className, +}) { const selectionContext = useContext(DataGridSelectionContext); if (!selectionContext) { @@ -88,7 +91,7 @@ export const DataGridTableContainer: React.FC = function DataGridTableCon }, [modelResultData, selectionContext.selectedCells]); return styled(styles)( - + {children} ); diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/Formatters/CellFormatter.tsx b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/Formatters/CellFormatter.tsx new file mode 100644 index 0000000000..14ed8b22b2 --- /dev/null +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/Formatters/CellFormatter.tsx @@ -0,0 +1,51 @@ +/* + * cloudbeaver - Cloud Database Manager + * Copyright (C) 2020 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +import type { FormatterProps } from 'react-data-grid'; + +function valueGetter(rawValue: any) { + if (rawValue !== null && typeof rawValue === 'object') { + return JSON.stringify(rawValue); + } + + return rawValue; +} + +function formatValue(rawValue: any) { + const value = valueGetter(rawValue); + + if (typeof value === 'string' && value.length > 1000) { + return value.split('').map(v => (v.charCodeAt(0) < 32 ? ' ' : v)).join(''); + } + + if (value === null) { + return '[null]'; + } + + return String(value); +} + +function getClasses(rawValue: any) { + const classes = []; + if (rawValue === null) { + classes.push('cell-null'); + } + return classes.join(' '); +} + +export const CellFormatter: React.FC = function CellFormatter({ row, column }) { + const rawValue = row[column.key]; + const classes = getClasses(rawValue); + const value = formatValue(rawValue); + + return ( + + {value} + + ); +}; diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/CellFormatter/CellFormatter.tsx b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/Formatters/IndexFormatter.tsx similarity index 63% rename from webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/CellFormatter/CellFormatter.tsx rename to webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/Formatters/IndexFormatter.tsx index 31e5148f5e..0067831005 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/CellFormatter/CellFormatter.tsx +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/Formatters/IndexFormatter.tsx @@ -8,10 +8,10 @@ import type { FormatterProps } from 'react-data-grid'; -export const CellFormatter: React.FC = function CellFormatter({ row, column }) { +export const IndexFormatter: React.FC = function IndexFormatter({ rowIdx }) { return ( -
- {JSON.stringify(row[column.key])} -
+ + {rowIdx + 1} + ); }; diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableColumnHeader/TableColumnHeader-new.tsx b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableColumnHeader/TableColumnHeader-new.tsx index e4fcc133b0..de06e5c8ce 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableColumnHeader/TableColumnHeader-new.tsx +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/TableColumnHeader/TableColumnHeader-new.tsx @@ -33,10 +33,10 @@ const headerStyles = css` overflow: hidden; } icon { - height: 16px; + display: flex; } StaticImage { - height: 100%; + height: 16px; } name { margin-left: 8px; diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/styles/base.scss b/webapp/packages/plugin-data-spreadsheet-new/src/styles/base.scss new file mode 100644 index 0000000000..c1b92be378 --- /dev/null +++ b/webapp/packages/plugin-data-spreadsheet-new/src/styles/base.scss @@ -0,0 +1,29 @@ +/* + * cloudbeaver - Cloud Database Manager + * Copyright (C) 2020 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +@import "react-data-grid/dist/react-data-grid.css"; + +.cb-react-grid-theme { + font-family: inherit; + outline: 0; + .rdg { + --font-size: 12px; + height: 100%; + background-color: inherit; + border: none; + } + + .rdg-header-row, + .rdg-row { + background-color: transparent; + } + + .rdg-cell { + border-color: transparent; + } +} diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/styles/styles.ts b/webapp/packages/plugin-data-spreadsheet-new/src/styles/styles.ts new file mode 100644 index 0000000000..8114aa9b7a --- /dev/null +++ b/webapp/packages/plugin-data-spreadsheet-new/src/styles/styles.ts @@ -0,0 +1,24 @@ +/* + * cloudbeaver - Cloud Database Manager + * Copyright (C) 2020 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +import type { ThemeSelector } from '@cloudbeaver/core-theming'; + +export const reactGridStyles: ThemeSelector = async theme => { + let styles: any; + + switch (theme) { + case 'dark': + styles = await import('./themes/dark.module.scss'); + break; + default: + styles = await import('./themes/light.module.scss'); + break; + } + + return [styles.default]; +}; 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 new file mode 100644 index 0000000000..e9f4556586 --- /dev/null +++ b/webapp/packages/plugin-data-spreadsheet-new/src/styles/themes/_base-react-grid.scss @@ -0,0 +1,72 @@ +/* + * cloudbeaver - Cloud Database Manager + * Copyright (C) 2020 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +@import "@cloudbeaver/core-theming/src/styles/branding"; + +@mixin base-react-grid() { + :global { + .cb-react-grid-theme { + &:focus-within .rdg-cell-custom-selected { + background-color: rgba(0, 145, 234, 0.3); + } + .rdg { + @include mdc-theme-prop(color, on-surface, false); + } + + .rdg-header-row { + @include mdc-theme-prop(background-color, surface, false); + } + + .rdg-row { + @include mdc-theme-prop(border-color, background, false); + } + + .rdg-row:hover, + .rdg-row:hover .rdg-cell-frozen { + @include stripes-background($mdc-theme-secondary); + border-bottom: 1px solid; + border-bottom-color: $color-positive; + } + + .rdg-cell { + @include mdc-theme-prop(border-color, background, false); + &:focus { + outline: 0; + } + } + + .rdg-cell-frozen { + @include mdc-theme-prop(background-color, surface, false); + } + + .rdg-cell-selected { + box-shadow: inset 0 0 0 1px #0091ea; + } + + .rdg-cell-custom-selected { + background-color: rgba(0, 145, 234, 0.2); + box-shadow: inset 0 0 0 1px #0091ea; + } + + .cell-formatter { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + .cell-index-column-name { + margin-left: 8px; + } + + .cell-null { + text-transform: uppercase; + opacity: 0.65; + } + } + } +} diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/styles/themes/dark.module.scss b/webapp/packages/plugin-data-spreadsheet-new/src/styles/themes/dark.module.scss new file mode 100644 index 0000000000..ca1ae2bb9e --- /dev/null +++ b/webapp/packages/plugin-data-spreadsheet-new/src/styles/themes/dark.module.scss @@ -0,0 +1,14 @@ +/* + * cloudbeaver - Cloud Database Manager + * Copyright (C) 2020 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + + @import "@cloudbeaver/core-theming/src/styles/theme-dark"; + @import "base-react-grid"; + + :global .#{$theme-class} { + @include base-react-grid; + } \ No newline at end of file diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/styles/themes/light.module.scss b/webapp/packages/plugin-data-spreadsheet-new/src/styles/themes/light.module.scss new file mode 100644 index 0000000000..3368bbb19c --- /dev/null +++ b/webapp/packages/plugin-data-spreadsheet-new/src/styles/themes/light.module.scss @@ -0,0 +1,14 @@ +/* + * cloudbeaver - Cloud Database Manager + * Copyright (C) 2020 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +@import "@cloudbeaver/core-theming/src/styles/theme-light"; +@import "base-react-grid"; + +:global .#{$theme-class} { + @include base-react-grid; +}