diff --git a/packages/web/src/datagrid/ColumnHeaderControl.svelte b/packages/web/src/datagrid/ColumnHeaderControl.svelte new file mode 100644 index 000000000..69af6b3c3 --- /dev/null +++ b/packages/web/src/datagrid/ColumnHeaderControl.svelte @@ -0,0 +1,40 @@ + + +
+
+ +
+
+ + diff --git a/packages/web/src/datagrid/ColumnLabel.svelte b/packages/web/src/datagrid/ColumnLabel.svelte new file mode 100644 index 000000000..931f88cab --- /dev/null +++ b/packages/web/src/datagrid/ColumnLabel.svelte @@ -0,0 +1,46 @@ + + + + + + {#if icon} + + {/if} + {headerText || columnName} + {#if extInfo} + {extInfo} + {/if} + + + diff --git a/packages/web/src/datagrid/DataGrid.svelte b/packages/web/src/datagrid/DataGrid.svelte index cab71cf0c..2d28f7558 100644 --- a/packages/web/src/datagrid/DataGrid.svelte +++ b/packages/web/src/datagrid/DataGrid.svelte @@ -1,15 +1,6 @@ diff --git a/packages/web/src/datagrid/DataGridCore.svelte b/packages/web/src/datagrid/DataGridCore.svelte index 58a98ee65..99526f444 100644 --- a/packages/web/src/datagrid/DataGridCore.svelte +++ b/packages/web/src/datagrid/DataGridCore.svelte @@ -1,9 +1,64 @@ -
+ + +
- + {/each} diff --git a/packages/web/src/datagrid/LoadingDataGridCore.svelte b/packages/web/src/datagrid/LoadingDataGridCore.svelte index d1258c4bb..b02094499 100644 --- a/packages/web/src/datagrid/LoadingDataGridCore.svelte +++ b/packages/web/src/datagrid/LoadingDataGridCore.svelte @@ -65,4 +65,4 @@ }; - + diff --git a/packages/web/src/datagrid/SeriesSizes.ts b/packages/web/src/datagrid/SeriesSizes.ts new file mode 100644 index 000000000..f221a4c56 --- /dev/null +++ b/packages/web/src/datagrid/SeriesSizes.ts @@ -0,0 +1,341 @@ +import _ from 'lodash'; + +export class SeriesSizeItem { + public scrollIndex: number = -1; + public modelIndex: number; + public frozenIndex: number = -1; + public size: number; + public position: number; + public get endPosition(): number { + return this.position + this.size; + } +} + +export class SeriesSizes { + private sizeOverridesByModelIndex: { [id: number]: number } = {}; + public count: number = 0; + public defaultSize: number = 50; + public maxSize: number = 1000; + private hiddenAndFrozenModelIndexes: number[] = []; + private frozenModelIndexes: number[] = []; + private hiddenModelIndexes: number[] = []; + private scrollItems: SeriesSizeItem[] = []; + private positions: number[] = []; + private scrollIndexes: number[] = []; + private frozenItems: SeriesSizeItem[] = []; + + public get scrollCount(): number { + return this.count - (this.hiddenAndFrozenModelIndexes != null ? this.hiddenAndFrozenModelIndexes.length : 0); + } + public get frozenCount(): number { + return this.frozenModelIndexes != null ? this.frozenModelIndexes.length : 0; + } + public get frozenSize(): number { + return _.sumBy(this.frozenItems, x => x.size); + } + public get realCount(): number { + return this.frozenCount + this.scrollCount; + } + + // public clear(): void { + // this.scrollItems = []; + // this.sizeOverridesByModelIndex = {}; + // this.positions = []; + // this.scrollIndexes = []; + // this.frozenItems = []; + // this.hiddenAndFrozenModelIndexes = null; + // this.frozenModelIndexes = null; + // } + public putSizeOverride(modelIndex: number, size: number, sizeByUser = false): void { + if (this.maxSize && size > this.maxSize && !sizeByUser) { + size = this.maxSize; + } + + let currentSize = this.sizeOverridesByModelIndex[modelIndex]; + if (sizeByUser || !currentSize || size > currentSize) { + this.sizeOverridesByModelIndex[modelIndex] = size; + } + // if (!_.has(this.sizeOverridesByModelIndex, modelIndex)) + // this.sizeOverridesByModelIndex[modelIndex] = size; + // if (size > this.sizeOverridesByModelIndex[modelIndex]) + // this.sizeOverridesByModelIndex[modelIndex] = size; + } + public buildIndex(): void { + this.scrollItems = []; + this.scrollIndexes = _.filter( + _.map(_.range(this.count), x => this.modelToReal(x) - this.frozenCount), + // _.map(this.intKeys(_.keys(this.sizeOverridesByModelIndex)), (x) => this.modelToReal(x) - this.frozenCount), + x => x >= 0 + ); + this.scrollIndexes.sort(); + let lastScrollIndex: number = -1; + let lastEndPosition: number = 0; + this.scrollIndexes.forEach(scrollIndex => { + let modelIndex: number = this.realToModel(scrollIndex + this.frozenCount); + let size: number = this.sizeOverridesByModelIndex[modelIndex]; + let item = new SeriesSizeItem(); + item.scrollIndex = scrollIndex; + item.modelIndex = modelIndex; + item.size = size; + item.position = lastEndPosition + (scrollIndex - lastScrollIndex - 1) * this.defaultSize; + this.scrollItems.push(item); + lastScrollIndex = scrollIndex; + lastEndPosition = item.endPosition; + }); + this.positions = _.map(this.scrollItems, x => x.position); + this.frozenItems = []; + let lastpos: number = 0; + for (let i: number = 0; i < this.frozenCount; i++) { + let modelIndex: number = this.frozenModelIndexes[i]; + let size: number = this.getSizeByModelIndex(modelIndex); + let item = new SeriesSizeItem(); + item.frozenIndex = i; + item.modelIndex = modelIndex; + item.size = size; + item.position = lastpos; + this.frozenItems.push(item); + lastpos += size; + } + } + + public getScrollIndexOnPosition(position: number): number { + let itemOrder: number = _.sortedIndex(this.positions, position); + if (this.positions[itemOrder] == position) return itemOrder; + if (itemOrder == 0) return Math.floor(position / this.defaultSize); + if (position <= this.scrollItems[itemOrder - 1].endPosition) return this.scrollItems[itemOrder - 1].scrollIndex; + return ( + Math.floor((position - this.scrollItems[itemOrder - 1].position) / this.defaultSize) + + this.scrollItems[itemOrder - 1].scrollIndex + ); + } + public getFrozenIndexOnPosition(position: number): number { + this.frozenItems.forEach(function (item) { + if (position >= item.position && position <= item.endPosition) return item.frozenIndex; + }); + return -1; + } + // public getSizeSum(startScrollIndex: number, endScrollIndex: number): number { + // let order1: number = _.sortedIndexOf(this.scrollIndexes, startScrollIndex); + // let order2: number = _.sortedIndexOf(this.scrollIndexes, endScrollIndex); + // let count: number = endScrollIndex - startScrollIndex; + // if (order1 < 0) + // order1 = ~order1; + // if (order2 < 0) + // order2 = ~order2; + // let result: number = 0; + // for (let i: number = order1; i <= order2; i++) { + // if (i < 0) + // continue; + // if (i >= this.scrollItems.length) + // continue; + // let item = this.scrollItems[i]; + // if (item.scrollIndex < startScrollIndex) + // continue; + // if (item.scrollIndex >= endScrollIndex) + // continue; + // result += item.size; + // count--; + // } + // result += count * this.defaultSize; + // return result; + // } + public getSizeByModelIndex(modelIndex: number): number { + if (_.has(this.sizeOverridesByModelIndex, modelIndex)) return this.sizeOverridesByModelIndex[modelIndex]; + return this.defaultSize; + } + public getSizeByScrollIndex(scrollIndex: number): number { + return this.getSizeByRealIndex(scrollIndex + this.frozenCount); + } + public getSizeByRealIndex(realIndex: number): number { + let modelIndex: number = this.realToModel(realIndex); + return this.getSizeByModelIndex(modelIndex); + } + public removeSizeOverride(realIndex: number): void { + let modelIndex: number = this.realToModel(realIndex); + delete this.sizeOverridesByModelIndex[modelIndex]; + } + public getScroll(sourceScrollIndex: number, targetScrollIndex: number): number { + if (sourceScrollIndex < targetScrollIndex) { + return -_.sum( + _.map(_.range(sourceScrollIndex, targetScrollIndex - sourceScrollIndex), x => this.getSizeByScrollIndex(x)) + ); + } else { + return _.sum( + _.map(_.range(targetScrollIndex, sourceScrollIndex - targetScrollIndex), x => this.getSizeByScrollIndex(x)) + ); + } + } + public modelIndexIsInScrollArea(modelIndex: number): boolean { + let realIndex = this.modelToReal(modelIndex); + return realIndex >= this.frozenCount; + } + public getTotalScrollSizeSum(): number { + let scrollSizeOverrides = _.map( + _.filter(this.intKeys(this.sizeOverridesByModelIndex), x => this.modelIndexIsInScrollArea(x)), + x => this.sizeOverridesByModelIndex[x] + ); + return _.sum(scrollSizeOverrides) + (this.count - scrollSizeOverrides.length) * this.defaultSize; + } + public getVisibleScrollSizeSum(): number { + let scrollSizeOverrides = _.map( + _.filter(this.intKeys(this.sizeOverridesByModelIndex), x => !_.includes(this.hiddenAndFrozenModelIndexes, x)), + x => this.sizeOverridesByModelIndex[x] + ); + return ( + _.sum(scrollSizeOverrides) + + (this.count - this.hiddenModelIndexes.length - scrollSizeOverrides.length) * this.defaultSize + ); + } + private intKeys(value): number[] { + return _.keys(value).map(x => _.parseInt(x)); + } + public getPositionByRealIndex(realIndex: number): number { + if (realIndex < 0) return 0; + if (realIndex < this.frozenCount) return this.frozenItems[realIndex].position; + return this.getPositionByScrollIndex(realIndex - this.frozenCount); + } + public getPositionByScrollIndex(scrollIndex: number): number { + let order: number = _.sortedIndex(this.scrollIndexes, scrollIndex); + if (this.scrollIndexes[order] == scrollIndex) return this.scrollItems[order].position; + order--; + if (order < 0) return scrollIndex * this.defaultSize; + return ( + this.scrollItems[order].endPosition + (scrollIndex - this.scrollItems[order].scrollIndex - 1) * this.defaultSize + ); + } + public getVisibleScrollCount(firstVisibleIndex: number, viewportSize: number): number { + let res: number = 0; + let index: number = firstVisibleIndex; + let count: number = 0; + while (res < viewportSize && index <= this.scrollCount) { + res += this.getSizeByScrollIndex(index); + index++; + count++; + } + return count; + } + public getVisibleScrollCountReversed(lastVisibleIndex: number, viewportSize: number): number { + let res: number = 0; + let index: number = lastVisibleIndex; + let count: number = 0; + while (res < viewportSize && index >= 0) { + res += this.getSizeByScrollIndex(index); + index--; + count++; + } + return count; + } + public invalidateAfterScroll( + oldFirstVisible: number, + newFirstVisible: number, + invalidate: (_: number) => void, + viewportSize: number + ): void { + if (newFirstVisible > oldFirstVisible) { + let oldVisibleCount: number = this.getVisibleScrollCount(oldFirstVisible, viewportSize); + let newVisibleCount: number = this.getVisibleScrollCount(newFirstVisible, viewportSize); + for (let i: number = oldFirstVisible + oldVisibleCount - 1; i <= newFirstVisible + newVisibleCount; i++) { + invalidate(i + this.frozenCount); + } + } else { + for (let i: number = newFirstVisible; i <= oldFirstVisible; i++) { + invalidate(i + this.frozenCount); + } + } + } + public isWholeInView(firstVisibleIndex: number, index: number, viewportSize: number): boolean { + let res: number = 0; + let testedIndex: number = firstVisibleIndex; + while (res < viewportSize && testedIndex < this.count) { + res += this.getSizeByScrollIndex(testedIndex); + if (testedIndex == index) return res <= viewportSize; + testedIndex++; + } + return false; + } + public scrollInView(firstVisibleIndex: number, scrollIndex: number, viewportSize: number): number { + if (this.isWholeInView(firstVisibleIndex, scrollIndex, viewportSize)) { + return firstVisibleIndex; + } + if (scrollIndex < firstVisibleIndex) { + return scrollIndex; + } + let testedIndex = firstVisibleIndex + 1; + while (testedIndex < this.scrollCount) { + if (this.isWholeInView(testedIndex, scrollIndex, viewportSize)) { + return testedIndex; + } + testedIndex++; + } + return this.scrollCount - 1; + + // let res: number = 0; + // let testedIndex: number = scrollIndex; + // while (res < viewportSize && testedIndex >= 0) { + // let size: number = this.getSizeByScrollIndex(testedIndex); + // if (res + size > viewportSize) return testedIndex + 1; + // testedIndex--; + // res += size; + // } + // if (res >= viewportSize && testedIndex < scrollIndex) return testedIndex + 1; + // return firstVisibleIndex; + } + public resize(realIndex: number, newSize: number): void { + if (realIndex < 0) return; + let modelIndex: number = this.realToModel(realIndex); + if (modelIndex < 0) return; + this.sizeOverridesByModelIndex[modelIndex] = newSize; + this.buildIndex(); + } + public setExtraordinaryIndexes(hidden: number[], frozen: number[]): void { + //this._hiddenAndFrozenModelIndexes = _.clone(hidden); + hidden = hidden.filter(x => x >= 0); + frozen = frozen.filter(x => x >= 0); + + hidden.sort((a, b) => a - b); + frozen.sort((a, b) => a - b); + this.frozenModelIndexes = _.filter(frozen, x => !_.includes(hidden, x)); + this.hiddenModelIndexes = _.filter(hidden, x => !_.includes(frozen, x)); + this.hiddenAndFrozenModelIndexes = _.concat(hidden, this.frozenModelIndexes); + this.frozenModelIndexes.sort((a, b) => a - b); + if (this.hiddenAndFrozenModelIndexes.length == 0) this.hiddenAndFrozenModelIndexes = null; + if (this.frozenModelIndexes.length == 0) this.frozenModelIndexes = null; + this.buildIndex(); + } + public realToModel(realIndex: number): number { + if (this.hiddenAndFrozenModelIndexes == null && this.frozenModelIndexes == null) return realIndex; + if (realIndex < 0) return -1; + if (realIndex < this.frozenCount && this.frozenModelIndexes != null) return this.frozenModelIndexes[realIndex]; + if (this.hiddenAndFrozenModelIndexes == null) return realIndex; + realIndex -= this.frozenCount; + for (let hidItem of this.hiddenAndFrozenModelIndexes) { + if (realIndex < hidItem) return realIndex; + realIndex++; + } + return realIndex; + } + public modelToReal(modelIndex: number): number { + if (this.hiddenAndFrozenModelIndexes == null && this.frozenModelIndexes == null) return modelIndex; + if (modelIndex < 0) return -1; + let frozenIndex: number = this.frozenModelIndexes != null ? _.indexOf(this.frozenModelIndexes, modelIndex) : -1; + if (frozenIndex >= 0) return frozenIndex; + if (this.hiddenAndFrozenModelIndexes == null) return modelIndex; + let hiddenIndex: number = _.sortedIndex(this.hiddenAndFrozenModelIndexes, modelIndex); + if (this.hiddenAndFrozenModelIndexes[hiddenIndex] == modelIndex) return -1; + if (hiddenIndex >= 0) return modelIndex - hiddenIndex + this.frozenCount; + return modelIndex; + } + public getFrozenPosition(frozenIndex: number): number { + return this.frozenItems[frozenIndex].position; + } + public hasSizeOverride(modelIndex: number): boolean { + return _.has(this.sizeOverridesByModelIndex, modelIndex); + } + public isVisible(testedRealIndex: number, firstVisibleScrollIndex: number, viewportSize: number): boolean { + if (testedRealIndex < 0) return false; + if (testedRealIndex >= 0 && testedRealIndex < this.frozenCount) return true; + let scrollIndex: number = testedRealIndex - this.frozenCount; + let onPageIndex: number = scrollIndex - firstVisibleScrollIndex; + return onPageIndex >= 0 && onPageIndex < this.getVisibleScrollCount(firstVisibleScrollIndex, viewportSize); + } +} diff --git a/packages/web/src/datagrid/TableDataGrid.svelte b/packages/web/src/datagrid/TableDataGrid.svelte index 2f5d18194..0089a6496 100644 --- a/packages/web/src/datagrid/TableDataGrid.svelte +++ b/packages/web/src/datagrid/TableDataGrid.svelte @@ -1,8 +1,10 @@ - - - XXX + diff --git a/packages/web/src/datagrid/gridutil.ts b/packages/web/src/datagrid/gridutil.ts new file mode 100644 index 000000000..bf64c2a94 --- /dev/null +++ b/packages/web/src/datagrid/gridutil.ts @@ -0,0 +1,144 @@ +import _ from 'lodash'; +import { SeriesSizes } from './SeriesSizes'; +import { CellAddress } from './selection'; +import { GridDisplay } from 'dbgate-datalib'; +import Grider from './Grider'; + +export function countColumnSizes(grider: Grider, columns, containerWidth, display: GridDisplay) { + const columnSizes = new SeriesSizes(); + if (!grider || !columns) return columnSizes; + + let canvas = document.createElement('canvas'); + let context = canvas.getContext('2d'); + + //return this.context.measureText(txt).width; + + // 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 (display.config.columnWidths[column.uniqueName]) { + columnSizes.putSizeOverride(colIndex, display.config.columnWidths[column.uniqueName]); + continue; + } + + // if (column.columnClientObject != null && column.columnClientObject.notNull) context.font = "bold 14px Helvetica"; + // else context.font = "14px Helvetica"; + context.font = 'bold 14px Helvetica'; + + const text = column.headerText; + const headerWidth = context.measureText(text).width + 64; + + // 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 rowIndex = 0; rowIndex < Math.min(grider.rowCount, 20); rowIndex += 1) { + const row = grider.getRowData(rowIndex); + for (let colIndex = 0; colIndex < columns.length; colIndex++) { + const uqName = columns[colIndex].uniqueName; + + if (display.config.columnWidths[uqName]) { + continue; + } + + const text = row[uqName]; + const 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; +} + +export function countVisibleRealColumns(columnSizes, firstVisibleColumnScrollIndex, gridScrollAreaWidth, columns) { + const visibleColumnCount = columnSizes.getVisibleScrollCount(firstVisibleColumnScrollIndex, gridScrollAreaWidth); + // console.log('visibleColumnCount', visibleColumnCount); + // console.log('gridScrollAreaWidth', gridScrollAreaWidth); + + const visibleRealColumnIndexes = []; + const modelIndexes = {}; + /** @type {(import('dbgate-datalib').DisplayColumn & {widthPx: string; colIndex: number})[]} */ + 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, + colIndex, + widthNumber, + widthPx: `${widthNumber}px`, + }); + } + return realColumns; +} + +export function filterCellForRow(cell, row: number): CellAddress | null { + return cell && (cell[0] == row || _.isString(cell[0])) ? cell : null; +} + +export function filterCellsForRow(cells, row: number): CellAddress[] | null { + const res = (cells || []).filter(x => x[0] == row || _.isString(x[0])); + return res.length > 0 ? res : null; +} + +export function cellIsSelected(row, col, selectedCells) { + if (!selectedCells) return false; + for (const [selectedRow, selectedCol] of selectedCells) { + if (row == selectedRow && col == selectedCol) return true; + if (selectedRow == 'header' && col == selectedCol) return true; + if (row == selectedRow && selectedCol == 'header') return true; + if (selectedRow == 'header' && selectedCol == 'header') return true; + } + return false; +} diff --git a/packages/web/src/datagrid/selection.ts b/packages/web/src/datagrid/selection.ts new file mode 100644 index 000000000..28ff9384a --- /dev/null +++ b/packages/web/src/datagrid/selection.ts @@ -0,0 +1,69 @@ +import _ from 'lodash'; +export type CellAddress = [number | 'header' | 'filter' | undefined, number | 'header' | undefined]; +export type RegularCellAddress = [number, number]; + +export const topLeftCell: CellAddress = [0, 0]; +export const undefinedCell: CellAddress = [undefined, undefined]; +export const nullCell: CellAddress = null; +export const emptyCellArray: CellAddress[] = []; + +export function isRegularCell(cell: CellAddress): cell is RegularCellAddress { + if (!cell) return false; + const [row, col] = cell; + return _.isNumber(row) && _.isNumber(col); +} + +export function getCellRange(a: CellAddress, b: CellAddress): CellAddress[] { + const [rowA, colA] = a; + const [rowB, colB] = b; + + if (_.isNumber(rowA) && _.isNumber(colA) && _.isNumber(rowB) && _.isNumber(colB)) { + const rowMin = Math.min(rowA, rowB); + const rowMax = Math.max(rowA, rowB); + const colMin = Math.min(colA, colB); + const colMax = Math.max(colA, colB); + const res = []; + for (let row = rowMin; row <= rowMax; row++) { + for (let col = colMin; col <= colMax; col++) { + res.push([row, col]); + } + } + return res; + } + if (rowA == 'header' && rowB == 'header' && _.isNumber(colA) && _.isNumber(colB)) { + const colMin = Math.min(colA, colB); + const colMax = Math.max(colA, colB); + const res = []; + for (let col = colMin; col <= colMax; col++) { + res.push(['header', col]); + } + return res; + } + if (colA == 'header' && colB == 'header' && _.isNumber(rowA) && _.isNumber(rowB)) { + const rowMin = Math.min(rowA, rowB); + const rowMax = Math.max(rowA, rowB); + const res = []; + for (let row = rowMin; row <= rowMax; row++) { + res.push([row, 'header']); + } + return res; + } + if (colA == 'header' && colB == 'header' && rowA == 'header' && rowB == 'header') { + return [['header', 'header']]; + } + return []; +} + +export function convertCellAddress(row, col): CellAddress { + const rowNumber = parseInt(row); + const colNumber = parseInt(col); + return [_.isNaN(rowNumber) ? row : rowNumber, _.isNaN(colNumber) ? col : colNumber]; +} + +export function cellFromEvent(event): CellAddress { + const cell = event.target.closest('td'); + if (!cell) return undefinedCell; + const col = cell.getAttribute('data-col'); + const row = cell.getAttribute('data-row'); + return convertCellAddress(row, col); +} diff --git a/packages/web/src/utility/useGridConfig.ts b/packages/web/src/utility/useGridConfig.ts index ee1e25e7d..af453a42c 100644 --- a/packages/web/src/utility/useGridConfig.ts +++ b/packages/web/src/utility/useGridConfig.ts @@ -2,20 +2,24 @@ import { createGridConfig } from 'dbgate-datalib'; import { writable } from 'svelte/store'; import { onDestroy } from 'svelte'; -const loadGridConfigFunc = tabid => () => { - const existing = localStorage.getItem(`tabdata_grid_${tabid}`); - if (existing) { - return { - ...createGridConfig(), - ...JSON.parse(existing), - }; +function doLoadGridConfigFunc(tabid) { + try { + const existing = localStorage.getItem(`tabdata_grid_${tabid}`); + if (existing) { + return { + ...createGridConfig(), + ...JSON.parse(existing), + }; + } + } catch (err) { + console.warn('Error loading grid config:', err.message); } return createGridConfig(); -}; +} export default function useGridConfig(tabid) { - const config = writable(loadGridConfigFunc(tabid)); + const config = writable(doLoadGridConfigFunc(tabid)); const unsubscribe = config.subscribe(value => localStorage.setItem(`tabdata_grid_${tabid}`, JSON.stringify(value))); - onDestroy(unsubscribe) + onDestroy(unsubscribe); return config; }
+ + {#each visibleRealColumns as col (col.uniqueName)} + + +