@svgrid/grid 2.6.20 → 2.6.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/README.md +22 -0
- package/dist/GridMenus.svelte +17 -12
- package/dist/SvGrid.controller.svelte.d.ts +14 -8
- package/dist/SvGrid.controller.svelte.js +214 -74
- package/dist/SvGrid.css +1 -1
- package/dist/SvGrid.svelte +112 -57
- package/dist/SvGrid.types.d.ts +191 -1
- package/dist/ai.d.ts +28 -0
- package/dist/ai.js +6 -0
- package/dist/cdn/{GridMenus-B0F9iBrG.js → GridMenus-BuoBPqxx.js} +137 -132
- package/dist/cdn/GridMenus-n4llxoOI.js +494 -0
- package/dist/cdn/column-resize-DsfNXMom.js +102 -0
- package/dist/cdn/row-resize-BRcimkUT.js +95 -0
- package/dist/cdn/{src-Cd0tearp.js → src-C9Hihx1W.js} +3497 -3487
- package/dist/cdn/{src-B1TdiyS8.js → src-D1lXwq1l.js} +8258 -8248
- package/dist/cdn/svgrid.js +10 -8
- package/dist/cdn/svgrid.svelte-external.js +10 -8
- package/dist/cdn/validate-_CDJzgIo.js +75 -0
- package/dist/cell-formatting.d.ts +2 -0
- package/dist/cell-formatting.js +2 -0
- package/dist/chart-export.d.ts +1 -0
- package/dist/chart.d.ts +31 -5
- package/dist/chart.js +9 -3
- package/dist/column-groups.js +1 -1
- package/dist/column-resize.d.ts +46 -0
- package/dist/column-resize.js +205 -0
- package/dist/columns.d.ts +0 -3
- package/dist/columns.js +0 -57
- package/dist/core.d.ts +216 -4
- package/dist/core.js +532 -119
- package/dist/createTree.svelte.d.ts +3 -0
- package/dist/createTree.svelte.js +1 -0
- package/dist/datetime/date-core.d.ts +2 -0
- package/dist/datetime/date-restrict.d.ts +1 -0
- package/dist/datetime/timezone.d.ts +1 -0
- package/dist/dock-manager-model.d.ts +3 -0
- package/dist/dock-manager-model.js +1 -0
- package/dist/dock-model.d.ts +6 -0
- package/dist/dock-model.js +3 -0
- package/dist/editor-contract.d.ts +1 -0
- package/dist/filtering/excel-filters.js +28 -0
- package/dist/group-display.d.ts +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +6 -0
- package/dist/list-option.d.ts +1 -0
- package/dist/menus.js +1 -1
- package/dist/positioning.d.ts +2 -0
- package/dist/row-resize.d.ts +11 -0
- package/dist/row-resize.js +7 -1
- package/dist/scheduler-ical.d.ts +1 -0
- package/dist/scheduler-model.d.ts +1 -0
- package/dist/selection.js +9 -0
- package/dist/spreadsheet.d.ts +1 -1
- package/dist/spreadsheet.js +1 -1
- package/dist/summaries.js +22 -0
- package/dist/svgrid-wrapper.types.d.ts +5 -0
- package/dist/toast-store.svelte.d.ts +4 -0
- package/dist/validate.d.ts +50 -0
- package/dist/validate.js +187 -0
- package/package.json +4 -1
- package/src/GridMenus.svelte +17 -12
- package/src/SvGrid.controller.svelte.ts +223 -76
- package/src/SvGrid.css +1 -1
- package/src/SvGrid.svelte +112 -57
- package/src/SvGrid.types.ts +191 -1
- package/src/ai.ts +28 -0
- package/src/cell-formatting.ts +2 -0
- package/src/chart-export.ts +1 -0
- package/src/chart.ts +31 -5
- package/src/column-groups.ts +1 -1
- package/src/column-resize.test.ts +381 -0
- package/src/column-resize.ts +227 -0
- package/src/columns.test.ts +0 -103
- package/src/columns.ts +0 -58
- package/src/core.aggregate.test.ts +134 -0
- package/src/core.filter.test.ts +156 -0
- package/src/core.grouping.test.ts +146 -0
- package/src/core.row-shape.test.ts +119 -0
- package/src/core.rowmodel-cache.test.ts +121 -0
- package/src/core.sort.test.ts +293 -0
- package/src/core.ts +723 -119
- package/src/createTree.svelte.ts +3 -0
- package/src/datetime/date-core.ts +2 -0
- package/src/datetime/date-restrict.ts +1 -0
- package/src/datetime/timezone.ts +1 -0
- package/src/dock-manager-model.ts +3 -0
- package/src/dock-model.ts +6 -0
- package/src/editor-contract.ts +1 -0
- package/src/filtering/excel-filters.ts +30 -0
- package/src/filtering/normalize-fast-path.test.ts +104 -0
- package/src/group-display.ts +1 -1
- package/src/index.ts +12 -1
- package/src/list-option.ts +1 -0
- package/src/menus.ts +1 -1
- package/src/positioning.ts +2 -0
- package/src/resize-props.test.ts +361 -0
- package/src/row-resize.test.ts +31 -0
- package/src/row-resize.ts +21 -3
- package/src/scheduler-ical.ts +1 -0
- package/src/scheduler-model.ts +1 -0
- package/src/selection.ts +9 -0
- package/src/spreadsheet.ts +1 -1
- package/src/summaries.ts +21 -0
- package/src/svgrid-wrapper.types.ts +5 -0
- package/src/svgrid.summaries.test.ts +217 -0
- package/src/toast-store.svelte.ts +4 -0
- package/src/validate.test.ts +207 -0
- package/src/validate.ts +269 -0
- package/dist/cdn/GridMenus-IHK_l7m6.js +0 -489
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
resolveMaxDomHeight,
|
|
33
33
|
} from "./virtualization/scroll-scaling";
|
|
34
34
|
import { buildPreMeasureItems } from "./virtualization/virtualizer";
|
|
35
|
+
import { DEV } from "esm-env";
|
|
35
36
|
import "./sv-grid-scrollbar";
|
|
36
37
|
import {
|
|
37
38
|
computeColumnStat,
|
|
@@ -61,6 +62,7 @@ import {
|
|
|
61
62
|
import {
|
|
62
63
|
createSummaries,
|
|
63
64
|
} from "./summaries";
|
|
65
|
+
|
|
64
66
|
import {
|
|
65
67
|
createMenus,
|
|
66
68
|
} from "./menus";
|
|
@@ -454,9 +456,6 @@ export function createSvGridController<
|
|
|
454
456
|
let editorSelectAll = true;
|
|
455
457
|
/** Per-column width overrides set by the resize handles. */
|
|
456
458
|
let columnWidths = $state<Record<string, number>>({});
|
|
457
|
-
let resizingColumnId = $state<string | null>(null);
|
|
458
|
-
let resizeStartX = 0;
|
|
459
|
-
let resizeStartWidth = 0;
|
|
460
459
|
const MIN_COLUMN_WIDTH = 40;
|
|
461
460
|
/** Columns pinned to the left or right edge of the grid (sticky positioning).
|
|
462
461
|
* Seeded from `props.initialColumnPinning` so demos / tests can show the
|
|
@@ -473,7 +472,28 @@ export function createSvGridController<
|
|
|
473
472
|
let dataStateVersion = $state(0);
|
|
474
473
|
const selectionColumnWidth = 44;
|
|
475
474
|
const rowNumberColumnWidth = $derived(props.rowNumberWidth ?? 56);
|
|
476
|
-
|
|
475
|
+
// A per-row `rowHeight` function already supplies heights, so auto-measuring
|
|
476
|
+
// would fight it. Fixed numbers are fine - they become the pre-measure estimate.
|
|
477
|
+
const autoRowHeightOn = $derived(
|
|
478
|
+
props.autoRowHeight === true && typeof props.rowHeight !== "function",
|
|
479
|
+
);
|
|
480
|
+
// Auto height measures the row from its content, so a dragged height would be
|
|
481
|
+
// overwritten on the next measurement pass. Let auto height win rather than
|
|
482
|
+
// having the two fight.
|
|
483
|
+
const rowResizeOn = $derived(props.rowResize === true && !autoRowHeightOn);
|
|
484
|
+
/**
|
|
485
|
+
* The row-number gutter. Explicit `showRowNumbers` always wins; otherwise
|
|
486
|
+
* `rowResize` brings it in, because a resizable row needs a row header to
|
|
487
|
+
* grab - that is where the drag strip lives, and it is where a spreadsheet
|
|
488
|
+
* puts it. Dragging the edge of a data cell instead works but reads as an
|
|
489
|
+
* accident.
|
|
490
|
+
*
|
|
491
|
+
* Both derived above are declared here rather than beside the rest of the
|
|
492
|
+
* row-resize state further down: a `$derived` referenced before its
|
|
493
|
+
* declaration in a .svelte.ts silently compiles to an empty getter, so this
|
|
494
|
+
* gate would have read `undefined` and the gutter would never appear.
|
|
495
|
+
*/
|
|
496
|
+
const showRowNumbersEffective = $derived(props.showRowNumbers ?? rowResizeOn);
|
|
477
497
|
let columnMenuFor = $state<string | null>(null);
|
|
478
498
|
let columnMenuTab = $state<"general" | "filter" | "columns">("general");
|
|
479
499
|
let columnMenuPos = $state<MenuPosition>({ x: 0, y: 0 });
|
|
@@ -497,11 +517,11 @@ export function createSvGridController<
|
|
|
497
517
|
let commentEditFor = $state<{ rowId: string; columnId: string; x: number; y: number } | null>(null);
|
|
498
518
|
let commentDraft = $state("");
|
|
499
519
|
let valueFilters = $state<Record<string, Set<string>>>({});
|
|
500
|
-
const viewportWidth = $derived.by(()
|
|
520
|
+
const viewportWidth = $derived.by(function viewportWidth_d() {
|
|
501
521
|
viewportVersion;
|
|
502
522
|
return scrollContainer ? scrollContainer.clientWidth : 0;
|
|
503
523
|
});
|
|
504
|
-
const viewportHeight = $derived.by(()
|
|
524
|
+
const viewportHeight = $derived.by(function viewportHeight_d() {
|
|
505
525
|
viewportVersion;
|
|
506
526
|
return scrollContainer ? scrollContainer.clientHeight : 0;
|
|
507
527
|
});
|
|
@@ -527,7 +547,7 @@ export function createSvGridController<
|
|
|
527
547
|
const hb = column.columnDef?.hideBelow;
|
|
528
548
|
return hb != null && viewportWidth > 0 && viewportWidth < hb;
|
|
529
549
|
}
|
|
530
|
-
const scrollMetrics = $derived.by(()
|
|
550
|
+
const scrollMetrics = $derived.by(function scrollMetrics_d() {
|
|
531
551
|
scrollVersion;
|
|
532
552
|
viewportVersion;
|
|
533
553
|
// Track the virtualizers' versions too so when data loads or row /
|
|
@@ -561,7 +581,7 @@ export function createSvGridController<
|
|
|
561
581
|
* - virtualizer.getTotalSize(): correct at initial load (before first paint)
|
|
562
582
|
* - scrollMetrics.scrollHeight: correct after detail rows expand (DOM is live,
|
|
563
583
|
* ResizeObserver on gridRootEl already bumps scrollVersion at that point) */
|
|
564
|
-
const hasVerticalOverflow = $derived.by(()
|
|
584
|
+
const hasVerticalOverflow = $derived.by(function hasVerticalOverflow_d() {
|
|
565
585
|
virtualizer.version;
|
|
566
586
|
const virtualizerSize = virtualizer.getTotalSize();
|
|
567
587
|
// scrollMetrics.scrollHeight is 0 before initial paint; once the table
|
|
@@ -606,6 +626,17 @@ export function createSvGridController<
|
|
|
606
626
|
(props.selectionMode ?? "both") === "both"),
|
|
607
627
|
);
|
|
608
628
|
|
|
629
|
+
// The aggregate footer row. `summary` is the shortcut alias, so it wins over
|
|
630
|
+
// the fine-grained prop - the same precedence `selectable` uses.
|
|
631
|
+
//
|
|
632
|
+
// It starts OFF. It used to default ON, which meant a plain
|
|
633
|
+
// `<SvGrid {data} {columns} />` grew a totals row nobody asked for, and every
|
|
634
|
+
// caller that did not want one had to say so: 375 call sites in this repo
|
|
635
|
+
// passed `enableRowSummaries={false}` against 6 that opted in.
|
|
636
|
+
const rowSummariesEnabled = $derived(
|
|
637
|
+
props.summary ?? props.enableRowSummaries ?? false,
|
|
638
|
+
);
|
|
639
|
+
|
|
609
640
|
|
|
610
641
|
|
|
611
642
|
// Internal source-of-truth for data and column defs. Seeded from props and
|
|
@@ -843,7 +874,7 @@ export function createSvGridController<
|
|
|
843
874
|
// Declared here rather than beside the other pagination/state derivations:
|
|
844
875
|
// the auto-group column pipeline below reads it, and a `const` used above its
|
|
845
876
|
// declaration is a type error even though `$derived` is lazy enough at runtime.
|
|
846
|
-
const groupingColumns = $derived.by(()
|
|
877
|
+
const groupingColumns = $derived.by(function groupingColumns_d() {
|
|
847
878
|
gridStateVersion;
|
|
848
879
|
return grid.getState().grouping ?? [];
|
|
849
880
|
});
|
|
@@ -868,7 +899,7 @@ export function createSvGridController<
|
|
|
868
899
|
* any other column. They carry no `field` - their cell content is resolved
|
|
869
900
|
* from the row's group state at render time.
|
|
870
901
|
*/
|
|
871
|
-
const autoGroupColumns = $derived.by(()
|
|
902
|
+
const autoGroupColumns = $derived.by(function autoGroupColumns_d() {
|
|
872
903
|
if (!groupColumnMode) return [] as Column<TData>[];
|
|
873
904
|
const sourceById = new Map(grid.getAllColumns().map((c) => [c.id, c]));
|
|
874
905
|
return autoGroupSpec.autoColumns.map((spec) => {
|
|
@@ -937,7 +968,7 @@ export function createSvGridController<
|
|
|
937
968
|
};
|
|
938
969
|
}
|
|
939
970
|
|
|
940
|
-
const allColumns = $derived.by(()
|
|
971
|
+
const allColumns = $derived.by(function allColumns_d() {
|
|
941
972
|
let raw = grid
|
|
942
973
|
.getAllColumns()
|
|
943
974
|
.filter(
|
|
@@ -983,7 +1014,7 @@ export function createSvGridController<
|
|
|
983
1014
|
});
|
|
984
1015
|
|
|
985
1016
|
/** Header groups reordered to match {@link allColumns}. */
|
|
986
|
-
const headerGroups = $derived.by(()
|
|
1017
|
+
const headerGroups = $derived.by(function headerGroups_d() {
|
|
987
1018
|
const base = grid.getHeaderGroups();
|
|
988
1019
|
if (!base.length) return base;
|
|
989
1020
|
const byId = new Map(
|
|
@@ -1041,7 +1072,7 @@ export function createSvGridController<
|
|
|
1041
1072
|
collapsible: boolean;
|
|
1042
1073
|
collapsed: boolean;
|
|
1043
1074
|
};
|
|
1044
|
-
const groupHeaderRows = $derived.by(()
|
|
1075
|
+
const groupHeaderRows = $derived.by(function groupHeaderRows_d() {
|
|
1045
1076
|
const userCols: Array<ColumnDef<any, TData>> =
|
|
1046
1077
|
(props.columns as unknown as Array<ColumnDef<any, TData>>) ?? [];
|
|
1047
1078
|
|
|
@@ -1171,7 +1202,7 @@ export function createSvGridController<
|
|
|
1171
1202
|
});
|
|
1172
1203
|
|
|
1173
1204
|
/** Cumulative pixel offsets for left- and right-pinned columns. */
|
|
1174
|
-
const pinnedOffsets = $derived.by(()
|
|
1205
|
+
const pinnedOffsets = $derived.by(function pinnedOffsets_d() {
|
|
1175
1206
|
const rowNumberWidth = showRowNumbersEffective ? rowNumberColumnWidth : 0;
|
|
1176
1207
|
const selectionWidth = showRowSelectionEffective ? selectionColumnWidth : 0;
|
|
1177
1208
|
const left: Record<string, number> = {};
|
|
@@ -1228,7 +1259,7 @@ export function createSvGridController<
|
|
|
1228
1259
|
);
|
|
1229
1260
|
// Per-column numeric min/max, needed only by colorScale / dataBar formats.
|
|
1230
1261
|
// Lazy: this derived never runs unless `conditionalFormats` is set.
|
|
1231
|
-
const conditionalColumnStats = $derived.by(()
|
|
1262
|
+
const conditionalColumnStats = $derived.by(function conditionalColumnStats_d() {
|
|
1232
1263
|
const map = new Map<string, ColumnStat>();
|
|
1233
1264
|
const formats = props.conditionalFormats;
|
|
1234
1265
|
if (!formats?.length || !formatsNeedingStats(formats)) return map;
|
|
@@ -1275,7 +1306,7 @@ export function createSvGridController<
|
|
|
1275
1306
|
|
|
1276
1307
|
|
|
1277
1308
|
|
|
1278
|
-
const sortDirectionByColumn = $derived.by(()
|
|
1309
|
+
const sortDirectionByColumn = $derived.by(function sortDirectionByColumn_d() {
|
|
1279
1310
|
gridStateVersion;
|
|
1280
1311
|
const directions: Record<string, false | "asc" | "desc"> = {};
|
|
1281
1312
|
for (const column of allColumns)
|
|
@@ -1284,7 +1315,7 @@ export function createSvGridController<
|
|
|
1284
1315
|
});
|
|
1285
1316
|
|
|
1286
1317
|
|
|
1287
|
-
const paginationState = $derived.by(()
|
|
1318
|
+
const paginationState = $derived.by(function paginationState_d() {
|
|
1288
1319
|
gridStateVersion;
|
|
1289
1320
|
return grid.getState().pagination ?? { pageIndex: 0, pageSize: 10 };
|
|
1290
1321
|
});
|
|
@@ -1295,7 +1326,7 @@ export function createSvGridController<
|
|
|
1295
1326
|
* compute the correct "X to Y of Z" range and total page count when
|
|
1296
1327
|
* filters reduce the dataset.
|
|
1297
1328
|
*/
|
|
1298
|
-
const allRowsBeforePagination = $derived.by(()
|
|
1329
|
+
const allRowsBeforePagination = $derived.by(function allRowsBeforePagination_d() {
|
|
1299
1330
|
// Depend on dataStateVersion (row-model-affecting store changes) NOT
|
|
1300
1331
|
// gridStateVersion - so moving the active cell / selection does not force
|
|
1301
1332
|
// this O(rows) pipeline to re-run. Filter-input state (globalFilter etc.)
|
|
@@ -1324,6 +1355,26 @@ export function createSvGridController<
|
|
|
1324
1355
|
);
|
|
1325
1356
|
}
|
|
1326
1357
|
|
|
1358
|
+
/**
|
|
1359
|
+
* Resolve a column ONCE and return a reader for it.
|
|
1360
|
+
*
|
|
1361
|
+
* `getRowColumnValue` finds the column with a linear scan over every
|
|
1362
|
+
* column, which is fine for a one-off read and wrong inside a loop over
|
|
1363
|
+
* 100,000 rows - it made each filtered column cost O(rows x columns).
|
|
1364
|
+
* The id is fixed for the life of a filter, so the scan is hoisted here and
|
|
1365
|
+
* the row loop gets a closure that only reads.
|
|
1366
|
+
*
|
|
1367
|
+
* Falls back to the row's own accessor when the id names no column, which
|
|
1368
|
+
* is also what `getRowColumnValue` does - group rows override
|
|
1369
|
+
* `getCellValueByColumnId` to return their aggregate.
|
|
1370
|
+
*/
|
|
1371
|
+
const makeColumnReader = (columnId: string) => {
|
|
1372
|
+
const column = allColumns.find((entry: any) => entry.id === columnId);
|
|
1373
|
+
return column
|
|
1374
|
+
? (row: Row<TData>) => getColumnBaseValue(row, column)
|
|
1375
|
+
: (row: Row<TData>) => row.getCellValueByColumnId(columnId);
|
|
1376
|
+
};
|
|
1377
|
+
|
|
1327
1378
|
// A single condition is "active" if it has the value(s) it needs.
|
|
1328
1379
|
const condActive = (op: FilterOperator, value: string, valueTo?: string): boolean => {
|
|
1329
1380
|
if (op === "isBlank" || op === "isNotBlank") return true;
|
|
@@ -1355,6 +1406,11 @@ export function createSvGridController<
|
|
|
1355
1406
|
// become compiled predicates before a single row is tested.
|
|
1356
1407
|
const compiledMenuFilters = menuFilters.map(([columnId, f]) => ({
|
|
1357
1408
|
columnId,
|
|
1409
|
+
// The column, resolved once. `getRowColumnValue` looks it up with a
|
|
1410
|
+
// linear scan of every column, and the id is fixed per filter, so
|
|
1411
|
+
// leaving that inside the row loop made it O(rows x columns) per
|
|
1412
|
+
// filtered column for no reason.
|
|
1413
|
+
readValue: makeColumnReader(columnId),
|
|
1358
1414
|
join: f.join,
|
|
1359
1415
|
a: condActive(f.operator, f.value, f.valueTo)
|
|
1360
1416
|
? compileCond(columnId, f.operator, f.value, f.valueTo)
|
|
@@ -1364,8 +1420,8 @@ export function createSvGridController<
|
|
|
1364
1420
|
: null,
|
|
1365
1421
|
}));
|
|
1366
1422
|
rows = rows.filter((row) =>
|
|
1367
|
-
compiledMenuFilters.every(({
|
|
1368
|
-
const cellValue =
|
|
1423
|
+
compiledMenuFilters.every(({ readValue, join, a, b }) => {
|
|
1424
|
+
const cellValue = readValue(row);
|
|
1369
1425
|
const ra = a ? a(cellValue) : null;
|
|
1370
1426
|
const rb = b ? b(cellValue) : null;
|
|
1371
1427
|
if (ra === null) return rb ?? true;
|
|
@@ -1383,11 +1439,12 @@ export function createSvGridController<
|
|
|
1383
1439
|
const bucketEntries = valueFilterEntries.map(([columnId, allowed]) => ({
|
|
1384
1440
|
columnId,
|
|
1385
1441
|
allowed,
|
|
1442
|
+
readValue: makeColumnReader(columnId),
|
|
1386
1443
|
buckets: facetBucketsByColumn.get(columnId) ?? null,
|
|
1387
1444
|
}));
|
|
1388
1445
|
rows = rows.filter((row) =>
|
|
1389
|
-
bucketEntries.every(({
|
|
1390
|
-
const raw =
|
|
1446
|
+
bucketEntries.every(({ allowed, buckets, readValue }) => {
|
|
1447
|
+
const raw = readValue(row);
|
|
1391
1448
|
if (buckets) {
|
|
1392
1449
|
// Range-bucketed filter: find which bucket this row's value
|
|
1393
1450
|
// falls into and check whether that bucket's label is allowed.
|
|
@@ -1445,7 +1502,7 @@ export function createSvGridController<
|
|
|
1445
1502
|
* is active we page by DATA rows and reprint each page's ancestor banners.
|
|
1446
1503
|
* Null when grouping is off, so the flat path stays a cheap slice.
|
|
1447
1504
|
*/
|
|
1448
|
-
const groupedPage = $derived.by(()
|
|
1505
|
+
const groupedPage = $derived.by(function groupedPage_d() {
|
|
1449
1506
|
if (!paginationEnabled || externalPaginationEnabled) return null;
|
|
1450
1507
|
if (!groupingColumns.length) return null;
|
|
1451
1508
|
const { pageIndex, pageSize } = paginationState;
|
|
@@ -1512,7 +1569,7 @@ export function createSvGridController<
|
|
|
1512
1569
|
* the full dataset rather than the current page (see the comment above
|
|
1513
1570
|
* `_rowModels`).
|
|
1514
1571
|
*/
|
|
1515
|
-
const allRows = $derived.by(()
|
|
1572
|
+
const allRows = $derived.by(function allRows_d() {
|
|
1516
1573
|
const paged = (() => {
|
|
1517
1574
|
const rows = allRowsBeforePagination;
|
|
1518
1575
|
// External pagination: `data` already IS the current page - never slice.
|
|
@@ -1585,7 +1642,7 @@ export function createSvGridController<
|
|
|
1585
1642
|
const paginationPageSize = $derived(
|
|
1586
1643
|
externalPaginationEnabled ? (props.pageSize ?? 10) : paginationState.pageSize,
|
|
1587
1644
|
);
|
|
1588
|
-
const rowSelectionState = $derived.by(()
|
|
1645
|
+
const rowSelectionState = $derived.by(function rowSelectionState_d() {
|
|
1589
1646
|
gridStateVersion;
|
|
1590
1647
|
return grid.getState().rowSelection ?? {};
|
|
1591
1648
|
});
|
|
@@ -1641,7 +1698,7 @@ export function createSvGridController<
|
|
|
1641
1698
|
? props.statusBar.aggregates
|
|
1642
1699
|
: (["count", "sum", "avg", "min", "max"] as const),
|
|
1643
1700
|
);
|
|
1644
|
-
const statusBarStats = $derived.by(()
|
|
1701
|
+
const statusBarStats = $derived.by(function statusBarStats_d() {
|
|
1645
1702
|
if (!statusBarEnabled) return null;
|
|
1646
1703
|
const a = selectionRange.anchor;
|
|
1647
1704
|
const f = selectionRange.focus;
|
|
@@ -1693,7 +1750,7 @@ export function createSvGridController<
|
|
|
1693
1750
|
const toolPanelEnabled = $derived(props.toolPanel === true);
|
|
1694
1751
|
// Every column (including hidden ones) in the user's current order, so the
|
|
1695
1752
|
// panel can toggle/reorder anything. Group columns are flagged live.
|
|
1696
|
-
const toolPanelColumns = $derived.by(()
|
|
1753
|
+
const toolPanelColumns = $derived.by(function toolPanelColumns_d() {
|
|
1697
1754
|
gridStateVersion;
|
|
1698
1755
|
const all = grid.getAllColumns();
|
|
1699
1756
|
if (!userColumnOrder.length) return all;
|
|
@@ -1730,7 +1787,7 @@ export function createSvGridController<
|
|
|
1730
1787
|
// unsaid, and why saying it would make the grid talk over itself.
|
|
1731
1788
|
|
|
1732
1789
|
/** Data rows before any local filtering - the denominator in "12 of 250". */
|
|
1733
|
-
const unfilteredRowTotal = $derived.by(()
|
|
1790
|
+
const unfilteredRowTotal = $derived.by(function unfilteredRowTotal_d() {
|
|
1734
1791
|
dataStateVersion;
|
|
1735
1792
|
void internalData;
|
|
1736
1793
|
return grid.getRowModel().rows.length;
|
|
@@ -1827,7 +1884,7 @@ export function createSvGridController<
|
|
|
1827
1884
|
props.onPivotModeChange?.(next);
|
|
1828
1885
|
}
|
|
1829
1886
|
const pivotActive = $derived(!!pivotConfig && pivotModeOn && hasPivotEngine());
|
|
1830
|
-
const pivotResult = $derived.by(()
|
|
1887
|
+
const pivotResult = $derived.by(function pivotResult_d() {
|
|
1831
1888
|
if (!pivotActive || !pivotConfig) return null;
|
|
1832
1889
|
const engine = getPivotEngine();
|
|
1833
1890
|
if (!engine) return null;
|
|
@@ -1931,7 +1988,7 @@ export function createSvGridController<
|
|
|
1931
1988
|
const h = col.columnDef.header;
|
|
1932
1989
|
return typeof h === "string" && h ? h : (col.columnDef.field ?? col.id);
|
|
1933
1990
|
};
|
|
1934
|
-
const chartableColumns = $derived.by(()
|
|
1991
|
+
const chartableColumns = $derived.by(function chartableColumns_d() {
|
|
1935
1992
|
const dims: Array<{ id: string; field: string; label: string }> = [];
|
|
1936
1993
|
const measures: Array<{ id: string; field: string; label: string }> = [];
|
|
1937
1994
|
for (const col of allColumns) {
|
|
@@ -1943,7 +2000,7 @@ export function createSvGridController<
|
|
|
1943
2000
|
}
|
|
1944
2001
|
return { dims, measures };
|
|
1945
2002
|
});
|
|
1946
|
-
const chartColumnDefaults = $derived.by(()
|
|
2003
|
+
const chartColumnDefaults = $derived.by(function chartColumnDefaults_d() {
|
|
1947
2004
|
const { dims, measures } = chartableColumns;
|
|
1948
2005
|
let dimId = dims[0]?.id ?? null;
|
|
1949
2006
|
let seriesId: string | null = null;
|
|
@@ -2302,7 +2359,7 @@ export function createSvGridController<
|
|
|
2302
2359
|
typeof props.containerHeight === "number" ? props.containerHeight : 520,
|
|
2303
2360
|
);
|
|
2304
2361
|
|
|
2305
|
-
const virtualRows = $derived.by(()
|
|
2362
|
+
const virtualRows = $derived.by(function virtualRows_d() {
|
|
2306
2363
|
virtualizer.version;
|
|
2307
2364
|
const items = virtualizer.getVirtualItems();
|
|
2308
2365
|
if (items.length || allRows.length === 0) return items;
|
|
@@ -2317,7 +2374,7 @@ export function createSvGridController<
|
|
|
2317
2374
|
props.overscan ?? 8,
|
|
2318
2375
|
);
|
|
2319
2376
|
});
|
|
2320
|
-
const virtualRowTotalSize = $derived.by(()
|
|
2377
|
+
const virtualRowTotalSize = $derived.by(function virtualRowTotalSize_d() {
|
|
2321
2378
|
virtualizer.version;
|
|
2322
2379
|
const size = virtualizer.getTotalSize();
|
|
2323
2380
|
// Keep the scroll height honest during the pre-measurement render, so the
|
|
@@ -2376,7 +2433,7 @@ export function createSvGridController<
|
|
|
2376
2433
|
// OWN committed scroll offset - not the live DOM scrollTop - so the spacer
|
|
2377
2434
|
// shift and the rendered window are always computed from the same state and
|
|
2378
2435
|
// can never skew by a frame (which would jitter at extreme scale).
|
|
2379
|
-
const rowOffsetAdjustment = $derived.by(()
|
|
2436
|
+
const rowOffsetAdjustment = $derived.by(function rowOffsetAdjustment_d() {
|
|
2380
2437
|
if (!rowScrollScalingActive) return 0;
|
|
2381
2438
|
virtualizer.version;
|
|
2382
2439
|
const logical = virtualizer.getState().scrollOffset;
|
|
@@ -2388,11 +2445,11 @@ export function createSvGridController<
|
|
|
2388
2445
|
const rowBottomSpacer = $derived(
|
|
2389
2446
|
Math.max(rowDomTotalSize - (virtualRowEnd - rowOffsetAdjustment), 0),
|
|
2390
2447
|
);
|
|
2391
|
-
const virtualColumns = $derived.by(()
|
|
2448
|
+
const virtualColumns = $derived.by(function virtualColumns_d() {
|
|
2392
2449
|
columnVirtualizerVersion;
|
|
2393
2450
|
return columnVirtualizer.getVirtualItems();
|
|
2394
2451
|
});
|
|
2395
|
-
const virtualColumnTotalSize = $derived.by(()
|
|
2452
|
+
const virtualColumnTotalSize = $derived.by(function virtualColumnTotalSize_d() {
|
|
2396
2453
|
columnVirtualizerVersion;
|
|
2397
2454
|
return columnVirtualizer.getTotalSize();
|
|
2398
2455
|
});
|
|
@@ -2408,7 +2465,7 @@ export function createSvGridController<
|
|
|
2408
2465
|
});
|
|
2409
2466
|
};
|
|
2410
2467
|
|
|
2411
|
-
const renderedColumnItems = $derived.by(()
|
|
2468
|
+
const renderedColumnItems = $derived.by(function renderedColumnItems_d() {
|
|
2412
2469
|
if (!columnVirtualizationEnabled) return allColumnItems();
|
|
2413
2470
|
// Column virtualization is ON by default and its virtualizer, like the row
|
|
2414
2471
|
// one, only learns `count` from an effect - which never runs on a server.
|
|
@@ -2453,7 +2510,7 @@ export function createSvGridController<
|
|
|
2453
2510
|
.map((item) => ({ item, column: allColumns[item.index] }))
|
|
2454
2511
|
.filter(hasRenderedColumn),
|
|
2455
2512
|
);
|
|
2456
|
-
const totalColumnWidth = $derived.by(()
|
|
2513
|
+
const totalColumnWidth = $derived.by(function totalColumnWidth_d() {
|
|
2457
2514
|
if (columnVirtualizationEnabled) return virtualColumnTotalSize;
|
|
2458
2515
|
let total = 0;
|
|
2459
2516
|
for (const column of allColumns) total += getColumnWidth(column.id);
|
|
@@ -2468,7 +2525,7 @@ export function createSvGridController<
|
|
|
2468
2525
|
* column instead is reactive to both `columnWidths` and
|
|
2469
2526
|
* `fittedColumnWidths`, so the overflow decision settles in the same
|
|
2470
2527
|
* render where fit-scaling lands - no race, no scrollbar flash. */
|
|
2471
|
-
const hasHorizontalOverflow = $derived.by(()
|
|
2528
|
+
const hasHorizontalOverflow = $derived.by(function hasHorizontalOverflow_d() {
|
|
2472
2529
|
const fixedCols =
|
|
2473
2530
|
(showRowNumbersEffective ? rowNumberColumnWidth : 0) +
|
|
2474
2531
|
(showRowSelectionEffective ? selectionColumnWidth : 0);
|
|
@@ -2501,7 +2558,7 @@ export function createSvGridController<
|
|
|
2501
2558
|
* everything stays pixel-aligned. When virtualization is off this is a
|
|
2502
2559
|
* pass-through.
|
|
2503
2560
|
*/
|
|
2504
|
-
const groupHeaderRowsWindowed = $derived.by(()
|
|
2561
|
+
const groupHeaderRowsWindowed = $derived.by(function groupHeaderRowsWindowed_d() {
|
|
2505
2562
|
const base = groupHeaderRows;
|
|
2506
2563
|
if (!columnVirtualizationEnabled || base.length === 0) return base;
|
|
2507
2564
|
const items = renderedColumnItems;
|
|
@@ -2524,14 +2581,14 @@ export function createSvGridController<
|
|
|
2524
2581
|
});
|
|
2525
2582
|
});
|
|
2526
2583
|
|
|
2527
|
-
const activeCell = $derived.by(()
|
|
2584
|
+
const activeCell = $derived.by(function activeCell_d() {
|
|
2528
2585
|
gridStateVersion;
|
|
2529
2586
|
return (
|
|
2530
2587
|
grid.getState().activeCell ?? { rowIndex: 0, colIndex: 0, cellId: null }
|
|
2531
2588
|
);
|
|
2532
2589
|
});
|
|
2533
2590
|
|
|
2534
|
-
const activeDescendantId = $derived.by(()
|
|
2591
|
+
const activeDescendantId = $derived.by(function activeDescendantId_d() {
|
|
2535
2592
|
const active = activeCell;
|
|
2536
2593
|
const inRows = active.rowIndex >= 0 && active.rowIndex < allRows.length;
|
|
2537
2594
|
const inCols = active.colIndex >= 0 && active.colIndex < allColumns.length;
|
|
@@ -2581,9 +2638,9 @@ export function createSvGridController<
|
|
|
2581
2638
|
* `null` above the cell limit - that case stays on the rAF-deferred effect,
|
|
2582
2639
|
* which keeps a huge grid painting before it totals.
|
|
2583
2640
|
*/
|
|
2584
|
-
const eagerSummaries = $derived.by(()
|
|
2641
|
+
const eagerSummaries = $derived.by(function eagerSummaries_d() {
|
|
2585
2642
|
if (!summarize) return null;
|
|
2586
|
-
if (!
|
|
2643
|
+
if (!rowSummariesEnabled) return null;
|
|
2587
2644
|
const rows = allRows;
|
|
2588
2645
|
const columns = allColumns;
|
|
2589
2646
|
if (rows.length * columns.length > SUMMARY_DEFER_CELL_LIMIT) return null;
|
|
@@ -2607,7 +2664,7 @@ export function createSvGridController<
|
|
|
2607
2664
|
void editedCellValues;
|
|
2608
2665
|
const rows = allRows;
|
|
2609
2666
|
const columns = allColumns;
|
|
2610
|
-
if (!
|
|
2667
|
+
if (!rowSummariesEnabled) {
|
|
2611
2668
|
deferredSummaries = {};
|
|
2612
2669
|
return;
|
|
2613
2670
|
}
|
|
@@ -2733,15 +2790,41 @@ export function createSvGridController<
|
|
|
2733
2790
|
// is the reactive signal instead, bumped only when a height actually changes.
|
|
2734
2791
|
const measuredRowHeights = new Map<number, number>();
|
|
2735
2792
|
let autoRowHeightVersion = $state(0);
|
|
2736
|
-
// A per-row `rowHeight` function already supplies heights, so auto-measuring
|
|
2737
|
-
// would fight it. Fixed numbers are fine - they become the pre-measure estimate.
|
|
2738
|
-
const autoRowHeightOn = $derived(
|
|
2739
|
-
props.autoRowHeight === true && typeof props.rowHeight !== "function",
|
|
2740
|
-
);
|
|
2741
2793
|
const autoRowHeightFallback = $derived(
|
|
2742
2794
|
typeof props.rowHeight === "number" ? props.rowHeight : 30,
|
|
2743
2795
|
);
|
|
2744
2796
|
|
|
2797
|
+
// ----- Interactive row resize (`rowResize` prop) -----
|
|
2798
|
+
// Heights the user has dragged, by row index. A plain Map plus a version
|
|
2799
|
+
// counter, exactly like `measuredRowHeights` above and for the same reason:
|
|
2800
|
+
// the reset effect below has to read this collection to know whether there is
|
|
2801
|
+
// anything to clear, and if the collection were reactive that read would make
|
|
2802
|
+
// every resize re-trigger the reset and wipe the height it just stored.
|
|
2803
|
+
const rowResizeHeights = new Map<number, number>();
|
|
2804
|
+
let rowResizeVersion = $state(0);
|
|
2805
|
+
|
|
2806
|
+
function setRowResizeHeight(index: number, height: number): void {
|
|
2807
|
+
if (!rowResizeOn || !Number.isFinite(index) || height <= 0) return;
|
|
2808
|
+
rowResizeHeights.set(index, Math.round(height));
|
|
2809
|
+
rowResizeVersion += 1;
|
|
2810
|
+
}
|
|
2811
|
+
/** A user-dragged height for this row, or undefined when it has not been
|
|
2812
|
+
* resized. Read by the view AND by the virtualizer's `estimateSize`. */
|
|
2813
|
+
function rowResizeHeightPx(index: number): number | undefined {
|
|
2814
|
+
return rowResizeOn ? rowResizeHeights.get(index) : undefined;
|
|
2815
|
+
}
|
|
2816
|
+
|
|
2817
|
+
// A row index means a different row once the data changes, so keeping the
|
|
2818
|
+
// heights would size new rows by the old ones - the same reasoning as the
|
|
2819
|
+
// measured-height reset below.
|
|
2820
|
+
$effect(() => {
|
|
2821
|
+
void allRows.length;
|
|
2822
|
+
void internalData;
|
|
2823
|
+
if (rowResizeHeights.size === 0) return;
|
|
2824
|
+
rowResizeHeights.clear();
|
|
2825
|
+
rowResizeVersion += 1;
|
|
2826
|
+
});
|
|
2827
|
+
|
|
2745
2828
|
/** Report a row's measured height. Called by the view's measuring action.
|
|
2746
2829
|
* Sub-pixel churn is ignored so a fractional layout can't loop. */
|
|
2747
2830
|
function reportRowHeight(index: number, height: number): void {
|
|
@@ -2812,11 +2895,20 @@ export function createSvGridController<
|
|
|
2812
2895
|
// the estimate for rows that have not rendered yet. Touch the version so a
|
|
2813
2896
|
// new measurement re-runs this effect (the Map itself is not reactive).
|
|
2814
2897
|
autoRowHeightVersion;
|
|
2815
|
-
|
|
2898
|
+
// Touch the version so a resize re-runs this effect: the virtualizer has to
|
|
2899
|
+
// know the new size or every row below the resized one is positioned
|
|
2900
|
+
// against a stale offset.
|
|
2901
|
+
rowResizeVersion;
|
|
2902
|
+
const base = autoRowHeightOn
|
|
2816
2903
|
? (index: number) => measuredRowHeights.get(index) ?? autoRowHeightFallback
|
|
2817
2904
|
: typeof rh === "function"
|
|
2818
2905
|
? rh
|
|
2819
2906
|
: (rh ?? 30);
|
|
2907
|
+
const estimateSize = rowResizeOn
|
|
2908
|
+
? (index: number) =>
|
|
2909
|
+
rowResizeHeights.get(index) ??
|
|
2910
|
+
(typeof base === "function" ? base(index) : base)
|
|
2911
|
+
: base;
|
|
2820
2912
|
virtualizer.setOptions({
|
|
2821
2913
|
count: allRows.length,
|
|
2822
2914
|
estimateSize,
|
|
@@ -2943,7 +3035,7 @@ export function createSvGridController<
|
|
|
2943
3035
|
|
|
2944
3036
|
|
|
2945
3037
|
|
|
2946
|
-
const headerSelectionState = $derived.by(()
|
|
3038
|
+
const headerSelectionState = $derived.by(function headerSelectionState_d() {
|
|
2947
3039
|
gridStateVersion;
|
|
2948
3040
|
const selectable = allRows.filter((row) => !isGroupRow(row));
|
|
2949
3041
|
if (!selectable.length) return "none";
|
|
@@ -2978,7 +3070,7 @@ export function createSvGridController<
|
|
|
2978
3070
|
* Returns `null` when fit scaling is not in effect (off, no room, total
|
|
2979
3071
|
* already >= target). Callers then fall back to the base width.
|
|
2980
3072
|
*/
|
|
2981
|
-
const fittedColumnWidths = $derived.by(()
|
|
3073
|
+
const fittedColumnWidths = $derived.by(function fittedColumnWidths_d() {
|
|
2982
3074
|
// Track viewport size (not scrollVersion) so we don't recompute on
|
|
2983
3075
|
// every scroll - only when the container actually resizes.
|
|
2984
3076
|
viewportVersion;
|
|
@@ -3044,8 +3136,6 @@ export function createSvGridController<
|
|
|
3044
3136
|
});
|
|
3045
3137
|
|
|
3046
3138
|
|
|
3047
|
-
let resizePendingWidth = 0;
|
|
3048
|
-
let resizeRaf: number | null = null;
|
|
3049
3139
|
|
|
3050
3140
|
|
|
3051
3141
|
|
|
@@ -3057,7 +3147,7 @@ export function createSvGridController<
|
|
|
3057
3147
|
/** Where the fill handle should render: the bottom-right cell of the
|
|
3058
3148
|
* selection range (or the active cell if there's no range). Returns
|
|
3059
3149
|
* null when cell selection is off or there is no anchored selection. */
|
|
3060
|
-
const fillHandleCell = $derived.by(()
|
|
3150
|
+
const fillHandleCell = $derived.by(function fillHandleCell_d() {
|
|
3061
3151
|
if (!(props.enableCellSelection ?? false)) return null;
|
|
3062
3152
|
const anchor = selectionRange.anchor;
|
|
3063
3153
|
const focus = selectionRange.focus;
|
|
@@ -3162,7 +3252,7 @@ export function createSvGridController<
|
|
|
3162
3252
|
* reused by both the facet UI and the row filter. Computing them lazily
|
|
3163
3253
|
* in a $derived means columns with no filter menu open and no active
|
|
3164
3254
|
* filter never pay the iteration cost. */
|
|
3165
|
-
const facetBucketsByColumn = $derived.by(()
|
|
3255
|
+
const facetBucketsByColumn = $derived.by(function facetBucketsByColumn_d() {
|
|
3166
3256
|
const map = new Map<string, Array<FacetBucket>>();
|
|
3167
3257
|
for (const column of allColumns) {
|
|
3168
3258
|
const meta = isBucketableColumn(column);
|
|
@@ -3249,7 +3339,7 @@ export function createSvGridController<
|
|
|
3249
3339
|
source: Array<string> | null;
|
|
3250
3340
|
values: Array<string>;
|
|
3251
3341
|
} | null = null;
|
|
3252
|
-
const columnMenuFacetValues = $derived.by(()
|
|
3342
|
+
const columnMenuFacetValues = $derived.by(function columnMenuFacetValues_d() {
|
|
3253
3343
|
// The funnel popover drives via `filterMenuFor`; the column menu's Filter
|
|
3254
3344
|
// tab drives via `columnMenuFor`. Support whichever is open.
|
|
3255
3345
|
const columnId = filterMenuFor ?? columnMenuFor;
|
|
@@ -3279,7 +3369,7 @@ export function createSvGridController<
|
|
|
3279
3369
|
if (!(filterMenuFor ?? columnMenuFor)) facetCache = null;
|
|
3280
3370
|
});
|
|
3281
3371
|
|
|
3282
|
-
const columnMenuVisibleFacets = $derived.by(()
|
|
3372
|
+
const columnMenuVisibleFacets = $derived.by(function columnMenuVisibleFacets_d() {
|
|
3283
3373
|
const query = columnMenuSearch.trim().toLowerCase();
|
|
3284
3374
|
if (!query) return columnMenuFacetValues;
|
|
3285
3375
|
return columnMenuFacetValues.filter((value) =>
|
|
@@ -3300,7 +3390,7 @@ export function createSvGridController<
|
|
|
3300
3390
|
* "everything checked", so that case materializes the set once here rather
|
|
3301
3391
|
* than every consumer re-deriving it.
|
|
3302
3392
|
*/
|
|
3303
|
-
const columnMenuSelectedFacets = $derived.by(()
|
|
3393
|
+
const columnMenuSelectedFacets = $derived.by(function columnMenuSelectedFacets_d() {
|
|
3304
3394
|
const columnId = filterMenuFor ?? columnMenuFor;
|
|
3305
3395
|
if (!columnId) return EMPTY_FACET_SET;
|
|
3306
3396
|
return valueFilters[columnId] ?? new Set(columnMenuFacetValues);
|
|
@@ -3310,7 +3400,7 @@ export function createSvGridController<
|
|
|
3310
3400
|
// active chip input, narrowed by whatever the user has typed. Uncapped - the
|
|
3311
3401
|
// dropdown windows its rows, so a high-cardinality column costs a list of
|
|
3312
3402
|
// strings, not thousands of nodes.
|
|
3313
|
-
const inSuggestValues = $derived.by(()
|
|
3403
|
+
const inSuggestValues = $derived.by(function inSuggestValues_d() {
|
|
3314
3404
|
if (!inSuggestFor) return EMPTY_FACETS;
|
|
3315
3405
|
const query = inSuggestQuery.trim().toLowerCase();
|
|
3316
3406
|
const all = facetValuesForColumn(inSuggestFor);
|
|
@@ -3428,12 +3518,6 @@ export function createSvGridController<
|
|
|
3428
3518
|
set editorSelectAll(v) { editorSelectAll = v as never; },
|
|
3429
3519
|
get columnWidths() { return columnWidths; },
|
|
3430
3520
|
set columnWidths(v) { columnWidths = v as never; },
|
|
3431
|
-
get resizingColumnId() { return resizingColumnId; },
|
|
3432
|
-
set resizingColumnId(v) { resizingColumnId = v as never; },
|
|
3433
|
-
get resizeStartX() { return resizeStartX; },
|
|
3434
|
-
set resizeStartX(v) { resizeStartX = v as never; },
|
|
3435
|
-
get resizeStartWidth() { return resizeStartWidth; },
|
|
3436
|
-
set resizeStartWidth(v) { resizeStartWidth = v as never; },
|
|
3437
3521
|
get MIN_COLUMN_WIDTH() { return MIN_COLUMN_WIDTH; },
|
|
3438
3522
|
get columnPinning() { return columnPinning; },
|
|
3439
3523
|
set columnPinning(v) { columnPinning = v as never; },
|
|
@@ -3499,6 +3583,7 @@ export function createSvGridController<
|
|
|
3499
3583
|
get showColumnFiltersEffective() { return showColumnFiltersEffective; },
|
|
3500
3584
|
get showInlineColumnFilterEffective() { return showInlineColumnFilterEffective; },
|
|
3501
3585
|
get showRowSelectionEffective() { return showRowSelectionEffective; },
|
|
3586
|
+
get rowSummariesEnabled() { return rowSummariesEnabled; },
|
|
3502
3587
|
get enableCellSelectionEffective() { return enableCellSelectionEffective; },
|
|
3503
3588
|
get flushScheduledScrollSync() { return flushScheduledScrollSync; },
|
|
3504
3589
|
get scheduleScrollSync() { return scheduleScrollSync; },
|
|
@@ -3751,6 +3836,15 @@ export function createSvGridController<
|
|
|
3751
3836
|
autoRowHeightVersion;
|
|
3752
3837
|
return (index: number): number | undefined => measuredRowHeights.get(index);
|
|
3753
3838
|
},
|
|
3839
|
+
/** True when the `rowResize` prop is on and nothing overrides it. */
|
|
3840
|
+
get rowResizeOn() { return rowResizeOn; },
|
|
3841
|
+
/** Record the height the user dragged a row to. */
|
|
3842
|
+
get setRowResizeHeight() { return setRowResizeHeight; },
|
|
3843
|
+
/** A row's dragged height, or undefined when it has not been resized. */
|
|
3844
|
+
get rowResizeHeightPx() {
|
|
3845
|
+
rowResizeVersion;
|
|
3846
|
+
return rowResizeHeightPx;
|
|
3847
|
+
},
|
|
3754
3848
|
get virtualRowTotalSize() { return virtualRowTotalSize; },
|
|
3755
3849
|
get virtualRowStart() { return virtualRowStart; },
|
|
3756
3850
|
get virtualRowEnd() { return virtualRowEnd; },
|
|
@@ -3813,13 +3907,6 @@ export function createSvGridController<
|
|
|
3813
3907
|
get getColumnBaseWidth() { return getColumnBaseWidth; },
|
|
3814
3908
|
get fittedColumnWidths() { return fittedColumnWidths; },
|
|
3815
3909
|
get getColumnWidth() { return getColumnWidth; },
|
|
3816
|
-
get resizePendingWidth() { return resizePendingWidth; },
|
|
3817
|
-
set resizePendingWidth(v) { resizePendingWidth = v as never; },
|
|
3818
|
-
get resizeRaf() { return resizeRaf; },
|
|
3819
|
-
set resizeRaf(v) { resizeRaf = v as never; },
|
|
3820
|
-
get startColumnResize() { return startColumnResize; },
|
|
3821
|
-
get onColumnResizeMove() { return onColumnResizeMove; },
|
|
3822
|
-
get endColumnResize() { return endColumnResize; },
|
|
3823
3910
|
get setSelection() { return setSelection; },
|
|
3824
3911
|
get extendSelection() { return extendSelection; },
|
|
3825
3912
|
get isCellInSelectedRange() { return isCellInSelectedRange; },
|
|
@@ -3829,6 +3916,15 @@ export function createSvGridController<
|
|
|
3829
3916
|
get isInFillPreview() { return isInFillPreview; },
|
|
3830
3917
|
get fillMarqueeEdges() { return fillMarqueeEdges; },
|
|
3831
3918
|
get findColumnById() { return findColumnById; },
|
|
3919
|
+
/**
|
|
3920
|
+
* Whether this column may be resized by the user, from its
|
|
3921
|
+
* `ColumnDef.resizable`. Defaults to true, so a column only opts out by
|
|
3922
|
+
* saying so; the grid-wide `columnResize` prop gates all of them above
|
|
3923
|
+
* this. Used by the resize action and by the column menu's Autosize item.
|
|
3924
|
+
*/
|
|
3925
|
+
columnResizable(columnId: string): boolean {
|
|
3926
|
+
return findColumnById(columnId)?.columnDef?.resizable !== false;
|
|
3927
|
+
},
|
|
3832
3928
|
get readCellRaw() { return readCellRaw; },
|
|
3833
3929
|
get writeCellRaw() { return writeCellRaw; },
|
|
3834
3930
|
get applyFillPattern() { return applyFillPattern; },
|
|
@@ -3939,12 +4035,64 @@ export function createSvGridController<
|
|
|
3939
4035
|
const { cellConditionalFormat, computeRowClass, computeCellClass, computeCellTooltip, computeCellValidity, computeCellNote, getColumnEditorOptions, areEditorOptionsLoading, formatListCellValue, formatCellValue, formatPinnedValue, computePinnedCellClass } = createCellRender<TFeatures, TData>(ctx);
|
|
3940
4036
|
const { isCellEditable, isCellEditableAt, getRowColumnValue, getCellDisplayValue, startEditingWithChar, startEditing, stopEditing, startFullRowEdit, setFullRowDraft, commitFullRowEdit, cancelFullRowEdit, saveEditingCell, applyHistoryStep, updateEditingCellValue, onEditorKeyDown, commitAndMoveByTab, focusOnMount, onCellDoubleClick, pasteFromClipboard, onGridPaste } = createEditing<TFeatures, TData>(ctx);
|
|
3941
4037
|
const { isRowSelected, toggleRowSelectionById, toggleSelectAllRows, setActiveCell, scrollActiveCellIntoView, setSelection, extendSelection, isCellInSelectedRange, getCellRangeEdges, getSelectionRects, isInFillPreview, fillMarqueeEdges, findColumnById, onCellPointerDown, onCellPointerEnter, endDragSelection, onWindowPointerMove, onCellClick, emitCellDoubleClick } = createSelection<TFeatures, TData>(ctx);
|
|
3942
|
-
const { cellPinStyle, isColumnPinned, getCurrentColumnOrder, emitColumnOrder, setColumnOrderInternal, applyColumnDrop, onColumnHeaderDragStart, onColumnHeaderDragOver, onColumnHeaderDragLeave, onColumnHeaderDrop, onColumnHeaderDragEnd, pinColumnLeft, pinColumnRight, unpinColumn, toggleColumnVisibleInPanel, moveColumnInPanel, toggleGroupInPanel, getColumnBaseWidth, getColumnWidth,
|
|
4038
|
+
const { cellPinStyle, isColumnPinned, getCurrentColumnOrder, emitColumnOrder, setColumnOrderInternal, applyColumnDrop, onColumnHeaderDragStart, onColumnHeaderDragOver, onColumnHeaderDragLeave, onColumnHeaderDrop, onColumnHeaderDragEnd, pinColumnLeft, pinColumnRight, unpinColumn, toggleColumnVisibleInPanel, moveColumnInPanel, toggleGroupInPanel, getColumnBaseWidth, getColumnWidth, measureText, autosizeColumn, autosizeAllColumns, resetColumns } = createColumns<TFeatures, TData>(ctx);
|
|
3943
4039
|
const { onRowDragStart, onRowDragOver, onRowDragLeave, onRowDrop, onRowsContainerDragOver, onRowsContainerDrop, onRowDragEnd, onRowPointerDown, destroyRowDrag } = createRowDrag<TFeatures, TData>(ctx);
|
|
3944
4040
|
const { register: registerAlignedGrid, broadcastScroll: broadcastAlignedScroll, broadcastWidths: broadcastAlignedWidths } = createAlignedGrids<TFeatures, TData>(ctx);
|
|
3945
4041
|
const { buildApi } = createGridApi<TFeatures, TData>(ctx);
|
|
3946
4042
|
const { readCellRaw, writeCellRaw, applyFillPattern, clearSelectedCellValues, startFillDrag, onFillPointerMove, onFillPointerUp, toggleBooleanCell, copySelectionToClipboard, clearSelectedCells, cutSelectionToClipboard } = createClipboard(ctx);
|
|
3947
4043
|
|
|
4044
|
+
// Dev-time configuration checks. Silent misconfiguration was the grid's
|
|
4045
|
+
// biggest usability gap - a misspelled `field` rendered a column of blank
|
|
4046
|
+
// cells and printed nothing, and an inert `pageSize` was simply ignored.
|
|
4047
|
+
//
|
|
4048
|
+
// `validateGridConfig` is pure (see validate.ts) so it can be unit-tested;
|
|
4049
|
+
// this only decides when to run it, and prints each distinct message once.
|
|
4050
|
+
//
|
|
4051
|
+
// It is loaded with `import()` rather than a static import so the checks live
|
|
4052
|
+
// in a lazy chunk. The base bundle is size-budgeted and already sits at its
|
|
4053
|
+
// limit, so a static import cost ~1 KB gzip that a production app would carry
|
|
4054
|
+
// and never execute.
|
|
4055
|
+
//
|
|
4056
|
+
// `DEV` comes from `esm-env` because the alternatives do not work here:
|
|
4057
|
+
// `process.env.NODE_ENV` is undefined in Vite's browser runtime (the checks
|
|
4058
|
+
// silently never ran), and Vite's own flag makes svelte-package warn that it
|
|
4059
|
+
// only works for Vite consumers. esm-env resolves through export conditions,
|
|
4060
|
+
// so it is correct in dev and folds to `false` in a production build. It is
|
|
4061
|
+
// already a direct dependency of Svelte, so it adds nothing to an install.
|
|
4062
|
+
if (DEV) {
|
|
4063
|
+
const warnedConfig = new Set<string>();
|
|
4064
|
+
$effect(() => {
|
|
4065
|
+
// Read the reactive values synchronously so the effect tracks them; the
|
|
4066
|
+
// module load below is async and would otherwise register no dependencies.
|
|
4067
|
+
const snapshot = {
|
|
4068
|
+
data: internalData,
|
|
4069
|
+
columns: internalColumns,
|
|
4070
|
+
features: resolveEffectiveFeatures() as Record<string, unknown>,
|
|
4071
|
+
sortable: props.sortable,
|
|
4072
|
+
pageable: props.pageable,
|
|
4073
|
+
showPagination: props.showPagination,
|
|
4074
|
+
pageSize: props.pageSize,
|
|
4075
|
+
groupBy: props.groupBy,
|
|
4076
|
+
treeData: props.treeData,
|
|
4077
|
+
initialColumnPinning: props.initialColumnPinning,
|
|
4078
|
+
columnVirtualization: props.columnVirtualization,
|
|
4079
|
+
externalPagination: props.externalPagination,
|
|
4080
|
+
rowCount: props.rowCount,
|
|
4081
|
+
externalSort: props.externalSort,
|
|
4082
|
+
onSortingChange: props.onSortingChange,
|
|
4083
|
+
externalFilter: props.externalFilter,
|
|
4084
|
+
onFiltersChange: props.onFiltersChange,
|
|
4085
|
+
};
|
|
4086
|
+
void import("./validate").then(({ validateGridConfig }) => {
|
|
4087
|
+
for (const message of validateGridConfig(snapshot as never)) {
|
|
4088
|
+
if (warnedConfig.has(message)) continue;
|
|
4089
|
+
warnedConfig.add(message);
|
|
4090
|
+
console.warn(message);
|
|
4091
|
+
}
|
|
4092
|
+
});
|
|
4093
|
+
});
|
|
4094
|
+
}
|
|
4095
|
+
|
|
3948
4096
|
// Aligned grids: register in the shared group on mount, and mirror column
|
|
3949
4097
|
// resizes to peers whenever columnWidths changes. Horizontal-scroll mirroring
|
|
3950
4098
|
// is driven from onBodyScroll (via ctx.broadcastAlignedScroll).
|
|
@@ -3964,7 +4112,6 @@ export function createSvGridController<
|
|
|
3964
4112
|
// this grid still owns (#68). Each of these is a no-op when idle.
|
|
3965
4113
|
$effect(() => {
|
|
3966
4114
|
return () => {
|
|
3967
|
-
endColumnResize();
|
|
3968
4115
|
hideTooltip();
|
|
3969
4116
|
destroyRowDrag();
|
|
3970
4117
|
};
|