diff --git a/package.json b/package.json index e7152eeba..cb6d3247f 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,11 @@ "start:api": "yarn workspace @dbgate/api start", "start:web": "yarn workspace @dbgate/web start", "start:sqltree": "yarn workspace @dbgate/sqltree start", + "start:datalib": "yarn workspace @dbgate/datalib start", "start": "concurrently --kill-others-on-fail \"yarn start:api\" \"yarn start:web\"", + "lib": "concurrently --kill-others-on-fail \"yarn start:sqltree\" \"yarn start:datalib\"", + "ts:api": "yarn workspace @dbgate/api ts", "ts:web": "yarn workspace @dbgate/web ts", "ts": "yarn ts:api && yarn ts:web" diff --git a/packages/datalib/src/GridConfig.ts b/packages/datalib/src/GridConfig.ts index dc569cd49..fe311711f 100644 --- a/packages/datalib/src/GridConfig.ts +++ b/packages/datalib/src/GridConfig.ts @@ -1,4 +1,4 @@ export default interface GridConfig { - hiddenColumns: number[]; + hiddenColumns: string[]; } diff --git a/packages/datalib/src/GridDisplay.ts b/packages/datalib/src/GridDisplay.ts index f96832626..1f7203af1 100644 --- a/packages/datalib/src/GridDisplay.ts +++ b/packages/datalib/src/GridDisplay.ts @@ -1,14 +1,38 @@ -import GridConfig from "./GridConfig"; +import _ from 'lodash'; +import GridConfig from './GridConfig'; +import { ForeignKeyInfo } from '@dbgate/types'; export interface DisplayColumn { columnName: string; + headerText: string; + uniqueName: string; + uniquePath: string[]; + notNull: boolean; + autoIncrement: boolean; + isPrimaryKey: boolean; + foreignKey: ForeignKeyInfo; + isChecked: boolean; } export default abstract class GridDisplay { - constructor( - public config: GridConfig, - protected setConfig: (configh: GridConfig) => void - ) {} + constructor(public config: GridConfig, protected setConfig: (config: GridConfig) => void) {} abstract getPageQuery(offset: number, count: number): string; columns: DisplayColumn[]; + setColumnVisibility(uniqueName, isVisible) { + if (isVisible) { + this.setConfig({ + ...this.config, + hiddenColumns: (this.config.hiddenColumns || []).filter(x => x != uniqueName), + }); + } else { + this.setConfig({ + ...this.config, + hiddenColumns: [...(this.config.hiddenColumns || []), uniqueName], + }); + } + } + + get hiddenColumnIndexes() { + return (this.config.hiddenColumns || []).map(x => _.findIndex(this.columns, y => y.uniqueName == x)); + } } diff --git a/packages/datalib/src/TableGridDisplay.ts b/packages/datalib/src/TableGridDisplay.ts index 840566b9f..6aa58586f 100644 --- a/packages/datalib/src/TableGridDisplay.ts +++ b/packages/datalib/src/TableGridDisplay.ts @@ -8,10 +8,19 @@ export default class TableGridDisplay extends GridDisplay { public table: TableInfo, public driver: EngineDriver, config: GridConfig, - setConfig: (configh: GridConfig) => void + setConfig: (config: GridConfig) => void ) { super(config, setConfig); - this.columns = table.columns; + this.columns = table.columns.map(col => ({ + ...col, + headerText: col.columnName, + uniqueName: col.columnName, + uniquePath: [col.columnName], + isPrimaryKey: table.primaryKey && !!table.primaryKey.columns.find(x => x.columnName == col.columnName), + foreignKey: + table.foreignKeys && table.foreignKeys.find(fk => fk.columns.find(x => x.columnName == col.columnName)), + isChecked: !(config.hiddenColumns && config.hiddenColumns.includes(col.columnName)), + })); } createSelect() { @@ -19,7 +28,7 @@ export default class TableGridDisplay extends GridDisplay { const select: Select = { commandType: 'select', from: { name: this.table }, - columns: this.table.columns.map(col => ({ exprType: 'column', ...col })), + columns: this.table.columns.map(col => ({ exprType: 'column', alias: col.columnName, ...col })), orderBy: [ { exprType: 'column', diff --git a/packages/datalib/tsconfig.json b/packages/datalib/tsconfig.json index 4dfd0a5f7..0d17d2171 100644 --- a/packages/datalib/tsconfig.json +++ b/packages/datalib/tsconfig.json @@ -4,7 +4,9 @@ "module": "commonjs", "declaration": true, "skipLibCheck": true, - "outDir": "lib" + "outDir": "lib", + "esModuleInterop": true, + "preserveWatchOutput": true }, "include": [ "src/**/*" diff --git a/packages/sqltree/src/dumpSqlCommand.ts b/packages/sqltree/src/dumpSqlCommand.ts index ae8d19e03..d1a3b2a37 100644 --- a/packages/sqltree/src/dumpSqlCommand.ts +++ b/packages/sqltree/src/dumpSqlCommand.ts @@ -19,7 +19,7 @@ export function dumpSqlSelect(dmp: SqlDumper, select: Select) { dmp.put('&>&n'); dmp.putCollection(',&n', select.columns, fld => { dumpSqlExpression(dmp, fld); - if (fld.alias) dmp.put(' %i', fld.alias); + if (fld.alias) dmp.put(' ^as %i', fld.alias); }); dmp.put('&n&<'); } diff --git a/packages/sqltree/tsconfig.json b/packages/sqltree/tsconfig.json index 4dfd0a5f7..9792007a6 100644 --- a/packages/sqltree/tsconfig.json +++ b/packages/sqltree/tsconfig.json @@ -4,7 +4,8 @@ "module": "commonjs", "declaration": true, "skipLibCheck": true, - "outDir": "lib" + "outDir": "lib", + "preserveWatchOutput": true }, "include": [ "src/**/*" diff --git a/packages/web/src/datagrid/ColumnLabel.js b/packages/web/src/datagrid/ColumnLabel.js new file mode 100644 index 000000000..baf6d81ee --- /dev/null +++ b/packages/web/src/datagrid/ColumnLabel.js @@ -0,0 +1,20 @@ +//@ts-nocheck + +import React from 'react'; +import styled from 'styled-components'; +import { SequenceIcon } from '../icons'; + +const Label = styled.span` + font-weight: ${props => (props.notNull ? 'bold' : 'normal')}; +`; + +/** @param column {import('@dbgate/datalib').DisplayColumn|import('@dbgate/types').ColumnInfo} */ +export default function ColumnLabel(column) { + let Icon = null; + if (column.autoIncrement) Icon = SequenceIcon; + return ( + + ); +} diff --git a/packages/web/src/datagrid/ColumnManager.js b/packages/web/src/datagrid/ColumnManager.js new file mode 100644 index 000000000..4dc1e521a --- /dev/null +++ b/packages/web/src/datagrid/ColumnManager.js @@ -0,0 +1,31 @@ +import React from 'react'; +import styled from 'styled-components'; +import ColumnLabel from './ColumnLabel'; + +const Row = styled.div` + margin-left: 5px; + margin-right: 5px; + cursor: pointer; + &:hover { + background-color: lightblue; + } +`; + +/** @param props {import('./types').DataGridProps} */ +export default function ColumnManager(props) { + const { display } = props; + return ( +
+ {display.columns.map(item => ( + + display.setColumnVisibility(item.uniqueName, !item.isChecked)} + > + + + ))} +
+ ); +} diff --git a/packages/web/src/datagrid/DataGrid.js b/packages/web/src/datagrid/DataGrid.js index 4b5d8dd4e..e3cfe73a2 100644 --- a/packages/web/src/datagrid/DataGrid.js +++ b/packages/web/src/datagrid/DataGrid.js @@ -1,327 +1,37 @@ import React from 'react'; -import useFetch from '../utility/useFetch'; import styled from 'styled-components'; -import theme from '../theme'; -import { HorizontalScrollBar, VerticalScrollBar } from './ScrollBars'; -import useDimensions from '../utility/useDimensions'; -import { SeriesSizes } from './SeriesSizes'; -import axios from '../utility/axios'; +import DataGridCore from './DataGridCore'; +import ColumnManager from './ColumnManager'; -const GridContainer = styled.div` +const MainContainer = styled.div` position: absolute; left: 0; top: 0; right: 0; bottom: 0; + display: flex; `; -const Table = styled.table` - position: absolute; - left: 0; - top: 0; - bottom: 20px; - right: 20px; - overflow: scroll; - border-collapse: collapse; -`; -const TableHead = styled.thead` - // display: block; - // width: 300px; -`; -const TableBody = styled.tbody` - // display: block; - // overflow: auto; - // height: 100px; -`; -const TableHeaderRow = styled.tr` - // height: 35px; -`; -const TableBodyRow = styled.tr` - // height: 35px; - background-color: #ffffff; - &:nth-child(6n + 4) { - background-color: #ebebeb; - } - &:nth-child(6n + 7) { - background-color: #ebf5ff; - } -`; -const TableHeaderCell = styled.td` - font-weight: bold; - border: 1px solid #c0c0c0; - // border-collapse: collapse; - text-align: left; - padding: 2px; - background-color: #f6f7f9; - overflow: hidden; -`; -const TableBodyCell = styled.td` - font-weight: normal; - border: 1px solid #c0c0c0; - // border-collapse: collapse; - padding: 2px; - white-space: nowrap; - overflow: hidden; +const ColumnManagerContainer = styled.div` + background-color: white; + padding-top: 5px; `; -/** - * @param {object} props - * @param {number} props.conid - * @param {string} props.database - * @param {import('@dbgate/datalib').GridDisplay} props.display - */ +const DataGridContainer = styled.div` + position: relative; + flex-grow: 1; +`; + +/** @param props {import('./types').DataGridProps} */ export default function DataGrid(props) { - const { conid, database, display } = props; - const columns = display.columns; - - // console.log(`GRID, conid=${conid}, database=${database}, sql=${sql}`); - const [loadProps, setLoadProps] = React.useState({ - isLoading: false, - loadedRows: [], - isLoadedAll: false, - loadedTime: new Date().getTime(), - }); - const { isLoading, loadedRows, isLoadedAll, loadedTime } = loadProps; - - const loadedTimeRef = React.useRef(0); - - const loadNextData = async () => { - if (isLoading) return; - setLoadProps({ - ...loadProps, - isLoading: true, - }); - const loadStart = new Date().getTime(); - loadedTimeRef.current = loadStart; - - const sql = display.getPageQuery(loadedRows.length, 100); - - let response = await axios.request({ - url: 'database-connections/query-data', - method: 'post', - params: { - conid, - database, - }, - data: { sql }, - }); - if (loadedTimeRef.current !== loadStart) { - // new load was dispatched - return; - } - // if (!_.isArray(nextRows)) { - // console.log('Error loading data from server', nextRows); - // nextRows = []; - // } - const { rows: nextRows } = response.data; - console.log('nextRows',nextRows) - const loadedInfo = { - loadedRows: [...loadedRows, ...nextRows], - loadedTime, - isLoadedAll: nextRows.length === 0, - }; - setLoadProps({ - ...loadProps, - isLoading: false, - ...loadedInfo, - }); - }; - - // const data = useFetch({ - // url: 'database-connections/query-data', - // method: 'post', - // params: { - // conid, - // database, - // }, - // data: { sql }, - // }); - // const { rows, columns } = data || {}; - const [firstVisibleRowScrollIndex, setFirstVisibleRowScrollIndex] = React.useState(0); - const [firstVisibleColumnScrollIndex, setFirstVisibleColumnScrollIndex] = React.useState(0); - - const [headerRowRef, { height: rowHeight }] = useDimensions(); - const [tableBodyRef] = useDimensions(); - const [containerRef, { height: containerHeight, width: containerWidth }] = useDimensions(); - - const columnSizes = React.useMemo(() => countColumnSizes(), [loadedRows, containerWidth]); - - console.log('containerWidth', containerWidth); - - const gridScrollAreaHeight = containerHeight - 2 * rowHeight; - const gridScrollAreaWidth = containerWidth - columnSizes.frozenSize; - - const visibleRowCountUpperBound = Math.ceil(gridScrollAreaHeight / Math.floor(rowHeight)); - const visibleRowCountLowerBound = Math.floor(gridScrollAreaHeight / Math.ceil(rowHeight)); - // const visibleRowCountUpperBound = 20; - // const visibleRowCountLowerBound = 20; - - React.useEffect(() => { - if (!isLoadedAll && firstVisibleRowScrollIndex + visibleRowCountUpperBound >= loadedRows.length) { - loadNextData(); - } - }); - - if (!loadedRows || !columns) return null; - const rowCountNewIncluded = loadedRows.length; - - const handleRowScroll = value => { - setFirstVisibleRowScrollIndex(value); - }; - - const handleColumnScroll = value => { - setFirstVisibleColumnScrollIndex(value); - }; - - function countColumnSizes() { - let canvas = document.createElement('canvas'); - let context = canvas.getContext('2d'); - - //return this.context.measureText(txt).width; - const columnSizes = new SeriesSizes(); - if (!loadedRows || !columns) return columnSizes; - - console.log('countColumnSizes', loadedRows.length, containerWidth); - - columnSizes.maxSize = (containerWidth * 2) / 3; - columnSizes.count = columns.length; - - // columnSizes.setExtraordinaryIndexes(this.getHiddenColumnIndexes(), this.getFrozenColumnIndexes()); - columnSizes.setExtraordinaryIndexes([], []); - - for (let colIndex = 0; colIndex < columns.length; colIndex++) { - //this.columnSizes.PutSizeOverride(col, this.columns[col].Name.length * 8); - let column = columns[colIndex]; - - // if (column.columnClientObject != null && column.columnClientObject.notNull) context.font = "bold 14px Helvetica"; - // else context.font = "14px Helvetica"; - context.font = 'bold 14px Helvetica'; - - let text = column.columnName; - let headerWidth = context.measureText(text).width + 32; - - // if (column.columnClientObject != null && column.columnClientObject.icon != null) headerWidth += 16; - // if (this.getFilterOnColumn(column.uniquePath)) headerWidth += 16; - // if (this.getSortOrder(column.uniquePath)) headerWidth += 16; - - columnSizes.putSizeOverride(colIndex, headerWidth); - } - - // let headerWidth = this.rowHeaderWidthDefault; - // if (this.rowCount) headerWidth = context.measureText(this.rowCount.toString()).width + 8; - // this.rowHeaderWidth = this.rowHeaderWidthDefault; - // if (headerWidth > this.rowHeaderWidth) this.rowHeaderWidth = headerWidth; - - context.font = '14px Helvetica'; - for (let row of loadedRows) { - for (let colIndex = 0; colIndex < columns.length; colIndex++) { - let colName = columns[colIndex].columnName; - let text = row[colName]; - let width = context.measureText(text).width + 8; - // console.log('colName', colName, text, width); - columnSizes.putSizeOverride(colIndex, width); - // let colName = this.columns[colIndex].uniquePath; - // let text: string = row[colName].gridText; - // let width = context.measureText(text).width + 8; - // if (row[colName].dataPrefix) width += context.measureText(row[colName].dataPrefix).width + 3; - // this.columnSizes.putSizeOverride(colIndex, width); - } - } - - // for (let modelIndex = 0; modelIndex < this.columns.length; modelIndex++) { - // let width = getHashValue(this.widthHashPrefix + this.columns[modelIndex].uniquePath); - // if (width) this.columnSizes.putSizeOverride(modelIndex, _.toNumber(width), true); - // } - - columnSizes.buildIndex(); - return columnSizes; - } - - // console.log('visibleRowCountUpperBound', visibleRowCountUpperBound); - // console.log('gridScrollAreaHeight', gridScrollAreaHeight); - // console.log('containerHeight', containerHeight); - - const visibleColumnCount = columnSizes.getVisibleScrollCount(firstVisibleColumnScrollIndex, gridScrollAreaWidth); - console.log('visibleColumnCount', visibleColumnCount); - - const visibleRealColumnIndexes = []; - const modelIndexes = {}; - /** @type {(import('@dbgate/datalib').DisplayColumn & {widthPx: string})[]} */ - const realColumns = []; - - // frozen columns - for (let colIndex = 0; colIndex < columnSizes.frozenCount; colIndex++) { - visibleRealColumnIndexes.push(colIndex); - } - // scroll columns - for ( - let colIndex = firstVisibleColumnScrollIndex; - colIndex < firstVisibleColumnScrollIndex + visibleColumnCount; - colIndex++ - ) { - visibleRealColumnIndexes.push(colIndex + columnSizes.frozenCount); - } - - // real columns - for (let colIndex of visibleRealColumnIndexes) { - let modelColumnIndex = columnSizes.realToModel(colIndex); - modelIndexes[colIndex] = modelColumnIndex; - - let col = columns[modelColumnIndex]; - if (!col) continue; - const widthNumber = columnSizes.getSizeByRealIndex(colIndex); - realColumns.push({ - ...col, - widthPx: `${widthNumber}px`, - }); - } - - console.log('visibleRealColumnIndexes', visibleRealColumnIndexes); - return ( - - - - - {realColumns.map(col => ( - - {col.columnName} - - ))} - - - - {loadedRows - .slice(firstVisibleRowScrollIndex, firstVisibleRowScrollIndex + visibleRowCountUpperBound) - .map((row, index) => ( - - {realColumns.map(col => ( - - {row[col.columnName]} - - ))} - - ))} - -
- - -
+ + + + + + + + ); } diff --git a/packages/web/src/datagrid/DataGridCore.js b/packages/web/src/datagrid/DataGridCore.js new file mode 100644 index 000000000..471074bb5 --- /dev/null +++ b/packages/web/src/datagrid/DataGridCore.js @@ -0,0 +1,325 @@ +import React from 'react'; +import useFetch from '../utility/useFetch'; +import styled from 'styled-components'; +import theme from '../theme'; +import { HorizontalScrollBar, VerticalScrollBar } from './ScrollBars'; +import useDimensions from '../utility/useDimensions'; +import { SeriesSizes } from './SeriesSizes'; +import axios from '../utility/axios'; +import ColumnLabel from './ColumnLabel'; + +const GridContainer = styled.div` + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; +`; + +const Table = styled.table` + position: absolute; + left: 0; + top: 0; + bottom: 20px; + right: 20px; + overflow: scroll; + border-collapse: collapse; +`; +const TableHead = styled.thead` + // display: block; + // width: 300px; +`; +const TableBody = styled.tbody` + // display: block; + // overflow: auto; + // height: 100px; +`; +const TableHeaderRow = styled.tr` + // height: 35px; +`; +const TableBodyRow = styled.tr` + // height: 35px; + background-color: #ffffff; + &:nth-child(6n + 4) { + background-color: #ebebeb; + } + &:nth-child(6n + 7) { + background-color: #ebf5ff; + } +`; +const TableHeaderCell = styled.td` + // font-weight: bold; + border: 1px solid #c0c0c0; + // border-collapse: collapse; + text-align: left; + padding: 2px; + background-color: #f6f7f9; + overflow: hidden; +`; +const TableBodyCell = styled.td` + font-weight: normal; + border: 1px solid #c0c0c0; + // border-collapse: collapse; + padding: 2px; + white-space: nowrap; + overflow: hidden; +`; + +/** @param props {import('./types').DataGridProps} */ +export default function DataGridCore(props) { + const { conid, database, display } = props; + const columns = display.columns; + + // console.log(`GRID, conid=${conid}, database=${database}, sql=${sql}`); + const [loadProps, setLoadProps] = React.useState({ + isLoading: false, + loadedRows: [], + isLoadedAll: false, + loadedTime: new Date().getTime(), + }); + const { isLoading, loadedRows, isLoadedAll, loadedTime } = loadProps; + + const loadedTimeRef = React.useRef(0); + + const loadNextData = async () => { + if (isLoading) return; + setLoadProps({ + ...loadProps, + isLoading: true, + }); + const loadStart = new Date().getTime(); + loadedTimeRef.current = loadStart; + + const sql = display.getPageQuery(loadedRows.length, 100); + + let response = await axios.request({ + url: 'database-connections/query-data', + method: 'post', + params: { + conid, + database, + }, + data: { sql }, + }); + if (loadedTimeRef.current !== loadStart) { + // new load was dispatched + return; + } + // if (!_.isArray(nextRows)) { + // console.log('Error loading data from server', nextRows); + // nextRows = []; + // } + const { rows: nextRows } = response.data; + console.log('nextRows', nextRows); + const loadedInfo = { + loadedRows: [...loadedRows, ...nextRows], + loadedTime, + isLoadedAll: nextRows.length === 0, + }; + setLoadProps({ + ...loadProps, + isLoading: false, + ...loadedInfo, + }); + }; + + // const data = useFetch({ + // url: 'database-connections/query-data', + // method: 'post', + // params: { + // conid, + // database, + // }, + // data: { sql }, + // }); + // const { rows, columns } = data || {}; + const [firstVisibleRowScrollIndex, setFirstVisibleRowScrollIndex] = React.useState(0); + const [firstVisibleColumnScrollIndex, setFirstVisibleColumnScrollIndex] = React.useState(0); + + const [headerRowRef, { height: rowHeight }] = useDimensions(); + const [tableBodyRef] = useDimensions(); + const [containerRef, { height: containerHeight, width: containerWidth }] = useDimensions(); + + const columnSizes = React.useMemo(() => countColumnSizes(), [loadedRows, containerWidth, display]); + + console.log('containerWidth', containerWidth); + + const gridScrollAreaHeight = containerHeight - 2 * rowHeight; + const gridScrollAreaWidth = containerWidth - columnSizes.frozenSize; + + const visibleRowCountUpperBound = Math.ceil(gridScrollAreaHeight / Math.floor(rowHeight)); + const visibleRowCountLowerBound = Math.floor(gridScrollAreaHeight / Math.ceil(rowHeight)); + // const visibleRowCountUpperBound = 20; + // const visibleRowCountLowerBound = 20; + + React.useEffect(() => { + if (!isLoadedAll && firstVisibleRowScrollIndex + visibleRowCountUpperBound >= loadedRows.length) { + loadNextData(); + } + }); + + if (!loadedRows || !columns) return null; + const rowCountNewIncluded = loadedRows.length; + + const handleRowScroll = value => { + setFirstVisibleRowScrollIndex(value); + }; + + const handleColumnScroll = value => { + setFirstVisibleColumnScrollIndex(value); + }; + + function countColumnSizes() { + let canvas = document.createElement('canvas'); + let context = canvas.getContext('2d'); + + //return this.context.measureText(txt).width; + const columnSizes = new SeriesSizes(); + if (!loadedRows || !columns) return columnSizes; + + console.log('countColumnSizes', loadedRows.length, containerWidth); + + columnSizes.maxSize = (containerWidth * 2) / 3; + columnSizes.count = columns.length; + + // columnSizes.setExtraordinaryIndexes(this.getHiddenColumnIndexes(), this.getFrozenColumnIndexes()); + console.log('display.hiddenColumnIndexes', display.hiddenColumnIndexes) + + columnSizes.setExtraordinaryIndexes(display.hiddenColumnIndexes, []); + + for (let colIndex = 0; colIndex < columns.length; colIndex++) { + //this.columnSizes.PutSizeOverride(col, this.columns[col].Name.length * 8); + const column = columns[colIndex]; + + // if (column.columnClientObject != null && column.columnClientObject.notNull) context.font = "bold 14px Helvetica"; + // else context.font = "14px Helvetica"; + context.font = 'bold 14px Helvetica'; + + let text = column.headerText; + let headerWidth = context.measureText(text).width + 32; + + // if (column.columnClientObject != null && column.columnClientObject.icon != null) headerWidth += 16; + // if (this.getFilterOnColumn(column.uniquePath)) headerWidth += 16; + // if (this.getSortOrder(column.uniquePath)) headerWidth += 16; + + columnSizes.putSizeOverride(colIndex, headerWidth); + } + + // let headerWidth = this.rowHeaderWidthDefault; + // if (this.rowCount) headerWidth = context.measureText(this.rowCount.toString()).width + 8; + // this.rowHeaderWidth = this.rowHeaderWidthDefault; + // if (headerWidth > this.rowHeaderWidth) this.rowHeaderWidth = headerWidth; + + context.font = '14px Helvetica'; + for (let row of loadedRows) { + for (let colIndex = 0; colIndex < columns.length; colIndex++) { + let uqName = columns[colIndex].uniqueName; + let text = row[uqName]; + let width = context.measureText(text).width + 8; + // console.log('colName', colName, text, width); + columnSizes.putSizeOverride(colIndex, width); + // let colName = this.columns[colIndex].uniquePath; + // let text: string = row[colName].gridText; + // let width = context.measureText(text).width + 8; + // if (row[colName].dataPrefix) width += context.measureText(row[colName].dataPrefix).width + 3; + // this.columnSizes.putSizeOverride(colIndex, width); + } + } + + // for (let modelIndex = 0; modelIndex < this.columns.length; modelIndex++) { + // let width = getHashValue(this.widthHashPrefix + this.columns[modelIndex].uniquePath); + // if (width) this.columnSizes.putSizeOverride(modelIndex, _.toNumber(width), true); + // } + + columnSizes.buildIndex(); + return columnSizes; + } + + // console.log('visibleRowCountUpperBound', visibleRowCountUpperBound); + // console.log('gridScrollAreaHeight', gridScrollAreaHeight); + // console.log('containerHeight', containerHeight); + + const visibleColumnCount = columnSizes.getVisibleScrollCount(firstVisibleColumnScrollIndex, gridScrollAreaWidth); + console.log('visibleColumnCount', visibleColumnCount); + + const visibleRealColumnIndexes = []; + const modelIndexes = {}; + /** @type {(import('@dbgate/datalib').DisplayColumn & {widthPx: string})[]} */ + const realColumns = []; + + // frozen columns + for (let colIndex = 0; colIndex < columnSizes.frozenCount; colIndex++) { + visibleRealColumnIndexes.push(colIndex); + } + // scroll columns + for ( + let colIndex = firstVisibleColumnScrollIndex; + colIndex < firstVisibleColumnScrollIndex + visibleColumnCount; + colIndex++ + ) { + visibleRealColumnIndexes.push(colIndex + columnSizes.frozenCount); + } + + // real columns + for (let colIndex of visibleRealColumnIndexes) { + let modelColumnIndex = columnSizes.realToModel(colIndex); + modelIndexes[colIndex] = modelColumnIndex; + + let col = columns[modelColumnIndex]; + if (!col) continue; + const widthNumber = columnSizes.getSizeByRealIndex(colIndex); + realColumns.push({ + ...col, + widthPx: `${widthNumber}px`, + }); + } + + console.log('visibleRealColumnIndexes', visibleRealColumnIndexes); + + return ( + + + + + {realColumns.map(col => ( + + + + ))} + + + + {loadedRows + .slice(firstVisibleRowScrollIndex, firstVisibleRowScrollIndex + visibleRowCountUpperBound) + .map((row, index) => ( + + {realColumns.map(col => ( + + {row[col.columnName]} + + ))} + + ))} + +
+ + +
+ ); +} diff --git a/packages/web/src/datagrid/SeriesSizes.js b/packages/web/src/datagrid/SeriesSizes.js index df96d5e38..5cc319161 100644 --- a/packages/web/src/datagrid/SeriesSizes.js +++ b/packages/web/src/datagrid/SeriesSizes.js @@ -219,12 +219,12 @@ export class SeriesSizes { let index = firstVisibleIndex; let count = 0; while (res < viewportSize && index <= this.scrollCount) { - console.log('this.getSizeByScrollIndex(index)', this.getSizeByScrollIndex(index)); + // console.log('this.getSizeByScrollIndex(index)', this.getSizeByScrollIndex(index)); res += this.getSizeByScrollIndex(index); index++; count++; } - console.log('getVisibleScrollCount', firstVisibleIndex, viewportSize, count); + // console.log('getVisibleScrollCount', firstVisibleIndex, viewportSize, count); return count; } getVisibleScrollCountReversed(lastVisibleIndex, viewportSize) { diff --git a/packages/web/src/datagrid/types.ts b/packages/web/src/datagrid/types.ts new file mode 100644 index 000000000..8b22c2514 --- /dev/null +++ b/packages/web/src/datagrid/types.ts @@ -0,0 +1,7 @@ +import { GridDisplay } from '@dbgate/datalib'; + +export interface DataGridProps { + conid: number; + database: string; + display: GridDisplay; +}