This commit is contained in:
Jan Prochazka
2021-02-22 21:09:06 +01:00
parent 1bb5f4974d
commit 390447c948
10 changed files with 741 additions and 39 deletions

View File

@@ -0,0 +1,40 @@
<script lang="ts">
import ColumnLabel from './ColumnLabel.svelte';
export let conid = undefined;
export let database = undefined;
export let column;
</script>
<div class="header">
<div class="label">
<ColumnLabel {...column} />
</div>
</div>
<style>
.header {
display: flex;
flex-wrap: nowrap;
}
.label {
flex: 1;
min-width: 10px;
padding: 2px;
margin: auto;
white-space: nowrap;
}
.icon {
margin-left: 3px;
}
.resizer {
background-color: var(--theme-border);
width: 2px;
cursor: col-resize;
z-index: 1;
}
.grouping {
color: green;
white-space: nowrap;
}
</style>

View File

@@ -0,0 +1,46 @@
<script context="module" lang="ts">
export function getColumnIcon(column, forceIcon = false) {
if (column.autoIncrement) return 'img autoincrement';
if (column.foreignKey) return 'img foreign-key';
if (forceIcon) return 'img column';
return null;
}
</script>
<script lang="ts">
import FontIcon from '../icons/FontIcon.svelte';
export let notNull = false;
export let forceIcon = false;
export let headerText = '';
export let columnName = '';
export let extInfo = null;
$: icon = getColumnIcon($$props, forceIcon);
</script>
<span class="label" class:notNull>
{#if icon}
<FontIcon {icon} />
{/if}
{headerText || columnName}
{#if extInfo}
<span class="extinfo">{extInfo}</span>
{/if}
</span>
<style>
.label {
white-space: nowrap;
}
.label.notNull {
font-weight: bold;
}
.extinfo {
font-weight: normal;
margin-left: 5px;
color: var(--theme-font-3);
}
</style>

View File

@@ -1,15 +1,6 @@
<script lang="ts">
export let config;
export let gridCoreComponent;
export let loadNextData = undefined;
export let grider = undefined;
$: firstVisibleRowScrollIndex = 0;
$: visibleRowCountUpperBound = 25;
if (loadNextData && firstVisibleRowScrollIndex + visibleRowCountUpperBound >= grider.rowCount) {
loadNextData();
}
</script>
<svelte:component this={gridCoreComponent} {...$$props} />

View File

@@ -1,9 +1,64 @@
<div class="container">
<script lang="ts">
import { GridDisplay } from 'dbgate-datalib';
import _ from 'lodash';
import ColumnHeaderControl from './ColumnHeaderControl.svelte';
import { countColumnSizes, countVisibleRealColumns } from './gridutil';
export let loadNextData = undefined;
export let grider = undefined;
export let display: GridDisplay = undefined;
export let conid = undefined;
export let database = undefined;
let containerHeight = 0;
let containerWidth = 0;
let rowHeight = 0;
let firstVisibleRowScrollIndex = 0;
let firstVisibleColumnScrollIndex = 0;
// $: firstVisibleRowScrollIndex = 0;
$: visibleRowCountUpperBound = 25;
// $: console.log('grider', grider);
$: columns = display.allColumns;
$: columnSizes = countColumnSizes(grider, columns, containerWidth, display);
$: headerColWidth = 40;
$: gridScrollAreaHeight = containerHeight - 2 * rowHeight;
$: gridScrollAreaWidth = containerWidth - columnSizes.frozenSize - headerColWidth - 32;
$: visibleRealColumns = countVisibleRealColumns(
columnSizes,
firstVisibleColumnScrollIndex,
gridScrollAreaWidth,
columns
);
$: console.log('visibleRealColumns', visibleRealColumns);
$: realColumnUniqueNames = _.range(columnSizes.realCount).map(
realIndex => (columns[columnSizes.realToModel(realIndex)] || {}).uniqueName
);
$: {
if (loadNextData && firstVisibleRowScrollIndex + visibleRowCountUpperBound >= grider.rowCount) {
loadNextData();
}
}
</script>
<div class="container" bind:clientWidth={containerWidth} bind:clientHeight={containerHeight}>
<input type="text" class="focus-field" />
<table class="table">
<thead>
<tr>
<td class="header=cell" data-row="header" data-col="header" />
<td class="header-cell" data-row="header" data-col="header" />
{#each visibleRealColumns as col (col.uniqueName)}
<td class="header-cell" data-row="header" data-col={col.colIndex}>
<ColumnHeaderControl column={col} {conid} {database} />
</td>
{/each}
</tr>
</thead>
<tbody />

View File

@@ -65,4 +65,4 @@
};
</script>
<DataGridCore />
<DataGridCore {...$$props} loadNextData={handleLoadNextData} {grider} />

View File

@@ -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);
}
}

View File

@@ -1,8 +1,10 @@
<script lang="ts">
import { TableFormViewDisplay } from 'dbgate-datalib';
import { findEngineDriver } from 'dbgate-tools';
import { createGridCache, TableFormViewDisplay, TableGridDisplay } from 'dbgate-datalib';
import { findEngineDriver } from 'dbgate-tools';
import { writable } from 'svelte/store';
import { extensions } from '../stores';
import { useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders';
import { useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders';
import DataGrid from './DataGrid.svelte';
import SqlDataGridCore from './SqlDataGridCore.svelte';
@@ -16,20 +18,30 @@ import { useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders';
$: connection = useConnectionInfo({ conid });
$: dbinfo = useDatabaseInfo({ conid, database });
// $: display = connection
// ? new TableFormViewDisplay(
// { schemaName, pureName },
// findEngineDriver(connection, extensions),
// config,
// setConfig,
// cache || myCache,
// setCache || setMyCache,
// dbinfo
// )
// : null;;
const cache = writable(createGridCache());
// $: console.log('display', display);
$: display = connection
? new TableGridDisplay(
{ schemaName, pureName },
findEngineDriver($connection, $extensions),
$config,
config.update,
$cache,
cache.update,
$dbinfo
)
: // ? new TableFormViewDisplay(
// { schemaName, pureName },
// findEngineDriver(connection, $extensions),
// $config,
// config.update,
// $cache,
// cache.update,
// $dbinfo
// )
null;
</script>
<!-- <DataGrid {...$$props} gridCoreComponent={SqlDataGridCore} /> -->
XXX
<DataGrid {...$$props} gridCoreComponent={SqlDataGridCore} {display} />

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
}