@svgrid/grid 1.1.0 → 1.1.2
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/dist/FlexRender.svelte +96 -96
- package/dist/GridMenus.svelte +205 -127
- package/dist/SvGrid.controller.svelte.d.ts +44 -1
- package/dist/SvGrid.controller.svelte.js +240 -44
- package/dist/SvGrid.css +325 -22
- package/dist/SvGrid.helpers.test.d.ts +1 -0
- package/dist/SvGrid.helpers.test.js +298 -0
- package/dist/SvGrid.svelte +729 -203
- package/dist/SvGrid.types.d.ts +91 -4
- package/dist/aligned-grids.d.ts +6 -0
- package/dist/aligned-grids.js +84 -0
- package/dist/aligned-grids.test.d.ts +1 -0
- package/dist/aligned-grids.test.js +75 -0
- package/dist/build-api.coverage.test.d.ts +20 -0
- package/dist/build-api.coverage.test.js +505 -0
- package/dist/build-api.js +61 -31
- package/dist/cell-render.test.d.ts +1 -0
- package/dist/cell-render.test.js +338 -0
- package/dist/cell-values.d.ts +1 -1
- package/dist/cell-values.js +7 -7
- package/dist/chart-export.test.d.ts +1 -0
- package/dist/chart-export.test.js +302 -0
- package/dist/chart.coverage.test.d.ts +1 -0
- package/dist/chart.coverage.test.js +748 -0
- package/dist/clipboard.js +88 -24
- package/dist/clipboard.test.d.ts +1 -0
- package/dist/clipboard.test.js +700 -0
- package/dist/collaboration.coverage.test.d.ts +1 -0
- package/dist/collaboration.coverage.test.js +200 -0
- package/dist/column-groups.d.ts +19 -0
- package/dist/column-groups.js +62 -0
- package/dist/column-groups.test.d.ts +1 -0
- package/dist/column-groups.test.js +56 -0
- package/dist/column-types.d.ts +10 -0
- package/dist/column-types.js +63 -0
- package/dist/column-types.test.d.ts +1 -0
- package/dist/column-types.test.js +62 -0
- package/dist/columns.test.d.ts +1 -0
- package/dist/columns.test.js +625 -0
- package/dist/core.d.ts +86 -1
- package/dist/core.js +2 -2
- package/dist/editing.d.ts +7 -0
- package/dist/editing.js +191 -5
- package/dist/editing.test.d.ts +1 -0
- package/dist/editing.test.js +732 -0
- package/dist/editors/cell-editors.coverage.test.d.ts +1 -0
- package/dist/editors/cell-editors.coverage.test.js +139 -0
- package/dist/facet-buckets.test.d.ts +1 -0
- package/dist/facet-buckets.test.js +296 -0
- package/dist/filter-operators.test.d.ts +1 -0
- package/dist/filter-operators.test.js +135 -0
- package/dist/hyperformula-adapter.test.d.ts +1 -0
- package/dist/hyperformula-adapter.test.js +205 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -1
- package/dist/keyboard-handlers.coverage.test.d.ts +1 -0
- package/dist/keyboard-handlers.coverage.test.js +495 -0
- package/dist/keyboard-handlers.js +7 -2
- package/dist/menus.js +1 -0
- package/dist/menus.test.d.ts +1 -0
- package/dist/menus.test.js +560 -0
- package/dist/named-views.coverage.test.d.ts +1 -0
- package/dist/named-views.coverage.test.js +180 -0
- package/dist/row-drag.d.ts +49 -0
- package/dist/row-drag.js +221 -0
- package/dist/row-drag.test.d.ts +1 -0
- package/dist/row-drag.test.js +142 -0
- package/dist/row-resize.test.d.ts +1 -0
- package/dist/row-resize.test.js +329 -0
- package/dist/scroll-sync.js +3 -0
- package/dist/scroll-sync.test.d.ts +1 -0
- package/dist/scroll-sync.test.js +290 -0
- package/dist/selection.d.ts +7 -1
- package/dist/selection.js +76 -36
- package/dist/selection.multi-range.test.d.ts +1 -0
- package/dist/selection.multi-range.test.js +55 -0
- package/dist/selection.test.d.ts +1 -0
- package/dist/selection.test.js +647 -0
- package/dist/server-data-source.coverage.test.d.ts +1 -0
- package/dist/server-data-source.coverage.test.js +154 -0
- package/dist/spreadsheet.d.ts +30 -0
- package/dist/spreadsheet.js +48 -0
- package/dist/spreadsheet.test.d.ts +1 -0
- package/dist/spreadsheet.test.js +446 -0
- package/dist/summaries.js +4 -4
- package/dist/sv-grid-scrollbar.js +13 -1
- package/dist/svgrid-wrapper.types.d.ts +19 -0
- package/dist/svgrid.behavior.test.js +20 -0
- package/dist/svgrid.interaction.test.js +31 -0
- package/dist/svgrid.new-features.wrapper.test.js +34 -2
- package/dist/test-setup.js +9 -3
- package/dist/virtualization/scroll-scaling.d.ts +17 -0
- package/dist/virtualization/scroll-scaling.js +35 -0
- package/dist/virtualization/scroll-scaling.test.js +42 -1
- package/package.json +2 -1
- package/src/FlexRender.svelte +96 -96
- package/src/GridMenus.svelte +205 -127
- package/src/SvGrid.controller.svelte.ts +204 -36
- package/src/SvGrid.css +277 -12
- package/src/SvGrid.svelte +727 -205
- package/src/SvGrid.types.ts +85 -4
- package/src/a11y.contract.test.ts +49 -49
- package/src/a11y.test.ts +59 -59
- package/src/a11y.ts +59 -59
- package/src/aligned-grids.test.ts +80 -0
- package/src/aligned-grids.ts +87 -0
- package/src/build-api.ts +43 -23
- package/src/cell-formatting.ts +169 -169
- package/src/cell-values.ts +4 -4
- package/src/clipboard.test.ts +49 -0
- package/src/clipboard.ts +51 -23
- package/src/column-groups.test.ts +59 -0
- package/src/column-groups.ts +80 -0
- package/src/column-types.test.ts +68 -0
- package/src/column-types.ts +82 -0
- package/src/core.performance.test.ts +30 -30
- package/src/core.ts +1077 -999
- package/src/createGrid.svelte.ts +42 -42
- package/src/createGrid.test.ts +10 -10
- package/src/createGridState.svelte.ts +17 -17
- package/src/editing.ts +161 -5
- package/src/flex-render.ts +3 -3
- package/src/index.ts +208 -196
- package/src/keyboard.test.ts +59 -59
- package/src/keyboard.ts +97 -97
- package/src/menus.ts +1 -0
- package/src/merge-objects.ts +48 -48
- package/src/render-component.ts +28 -28
- package/src/row-drag.test.ts +168 -0
- package/src/row-drag.ts +255 -0
- package/src/scroll-sync.ts +2 -0
- package/src/selection.multi-range.test.ts +61 -0
- package/src/selection.ts +71 -37
- package/src/spreadsheet.test.ts +46 -2
- package/src/spreadsheet.ts +58 -0
- package/src/static-functions.ts +11 -11
- package/src/subscribe.ts +38 -38
- package/src/summaries.ts +4 -4
- package/src/svgrid-wrapper.types.ts +412 -393
- package/src/svgrid.features.test.ts +157 -157
- package/src/svgrid.new-features.wrapper.test.ts +2 -2
- package/src/svgrid.wrapper.test.ts +40 -40
- package/src/virtualization/column-virtualizer.test.ts +27 -27
- package/src/virtualization/column-virtualizer.ts +30 -30
- package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
- package/src/virtualization/types.ts +30 -30
- package/src/virtualization/virtualizer.test.ts +47 -47
- package/src/virtualization/virtualizer.ts +296 -296
|
@@ -25,6 +25,10 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
25
25
|
operator: FilterOperator;
|
|
26
26
|
value: string;
|
|
27
27
|
valueTo?: string;
|
|
28
|
+
operator2?: FilterOperator;
|
|
29
|
+
value2?: string;
|
|
30
|
+
valueTo2?: string;
|
|
31
|
+
join?: "AND" | "OR";
|
|
28
32
|
}>;
|
|
29
33
|
verticalScrollbarEl: HTMLElement | null;
|
|
30
34
|
horizontalScrollbarEl: HTMLElement | null;
|
|
@@ -35,6 +39,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
35
39
|
pendingScrollLeft: number;
|
|
36
40
|
scrollSyncRaf: number | null;
|
|
37
41
|
selectionRange: SelectionRange;
|
|
42
|
+
selectionRanges: SelectionRange[];
|
|
38
43
|
isDraggingSelection: boolean;
|
|
39
44
|
fillDrag: {
|
|
40
45
|
sourceMinRow: number;
|
|
@@ -49,6 +54,10 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
49
54
|
colIndex: number;
|
|
50
55
|
} | null;
|
|
51
56
|
editingCell: CellEditState;
|
|
57
|
+
fullRowEdit: {
|
|
58
|
+
rowId: string;
|
|
59
|
+
draft: Record<string, unknown>;
|
|
60
|
+
} | null;
|
|
52
61
|
editedCellValues: Record<string, unknown>;
|
|
53
62
|
readonly UNDO_LIMIT: number;
|
|
54
63
|
history: {
|
|
@@ -100,6 +109,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
100
109
|
readonly DATE_OPERATORS: FilterOperator[];
|
|
101
110
|
readonly CHECKBOX_OPERATORS: FilterOperator[];
|
|
102
111
|
columnMenuFor: string | null;
|
|
112
|
+
columnMenuTab: "filter" | "columns" | "general";
|
|
103
113
|
columnMenuPos: MenuPosition;
|
|
104
114
|
columnMenuSearch: string;
|
|
105
115
|
filterMenuFor: string | null;
|
|
@@ -140,6 +150,8 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
140
150
|
internalData: readonly TData[];
|
|
141
151
|
internalColumns: ColumnDef<TFeatures, TData>[];
|
|
142
152
|
hiddenColumns: Record<string, boolean>;
|
|
153
|
+
readonly toggleColumnGroup: (groupId: string) => void;
|
|
154
|
+
readonly isColumnGroupCollapsed: (groupId: string) => boolean;
|
|
143
155
|
readonly externalSortEnabled: boolean;
|
|
144
156
|
readonly externalFilterEnabled: boolean;
|
|
145
157
|
readonly passthroughSortedRowModel: ({ rows }: {
|
|
@@ -165,6 +177,10 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
165
177
|
* multi-level value tree). They render as empty cells so the
|
|
166
178
|
* layout stays aligned without showing duplicate labels. */
|
|
167
179
|
isPlaceholder: boolean;
|
|
180
|
+
/** Set when this group cell has a collapse toggle. */
|
|
181
|
+
groupId?: string;
|
|
182
|
+
collapsible: boolean;
|
|
183
|
+
collapsed: boolean;
|
|
168
184
|
}[];
|
|
169
185
|
}[];
|
|
170
186
|
readonly pinnedOffsets: {
|
|
@@ -176,6 +192,17 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
176
192
|
colDragId: string | null;
|
|
177
193
|
colDropOnId: string | null;
|
|
178
194
|
colDropSide: "before" | "after" | null;
|
|
195
|
+
rowDragActive: boolean;
|
|
196
|
+
rowDropIndex: number | null;
|
|
197
|
+
rowDropSide: "before" | "after" | null;
|
|
198
|
+
readonly onRowDragStart: (e: DragEvent, rowIndex: number) => void;
|
|
199
|
+
readonly onRowDragOver: (e: DragEvent, rowIndex: number) => void;
|
|
200
|
+
readonly onRowDragLeave: (rowIndex: number) => void;
|
|
201
|
+
readonly onRowDrop: (e: DragEvent, rowIndex: number) => void;
|
|
202
|
+
readonly onRowsContainerDragOver: (e: DragEvent) => void;
|
|
203
|
+
readonly onRowsContainerDrop: (e: DragEvent) => void;
|
|
204
|
+
readonly onRowDragEnd: () => void;
|
|
205
|
+
readonly broadcastAlignedScroll: (left: number) => void;
|
|
179
206
|
readonly getCurrentColumnOrder: () => string[];
|
|
180
207
|
readonly emitColumnOrder: () => void;
|
|
181
208
|
readonly setColumnOrderInternal: (ids: ReadonlyArray<string>) => void;
|
|
@@ -215,6 +242,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
215
242
|
max: number;
|
|
216
243
|
} | null;
|
|
217
244
|
toolPanelOpen: boolean;
|
|
245
|
+
toolPanelTab: "columns" | "filters";
|
|
218
246
|
readonly toolPanelEnabled: boolean;
|
|
219
247
|
readonly toolPanelColumns: Column<TData>[];
|
|
220
248
|
readonly toolPanelHeaderLabel: typeof toolPanelHeaderLabel;
|
|
@@ -324,7 +352,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
324
352
|
readonly startColumnResize: (event: PointerEvent, columnId: string) => void;
|
|
325
353
|
readonly onColumnResizeMove: (event: PointerEvent) => void;
|
|
326
354
|
readonly endColumnResize: () => void;
|
|
327
|
-
readonly setSelection: (rowIndex: number, colIndex: number) => void;
|
|
355
|
+
readonly setSelection: (rowIndex: number, colIndex: number, additive?: boolean) => void;
|
|
328
356
|
readonly extendSelection: (rowIndex: number, colIndex: number) => void;
|
|
329
357
|
readonly isCellInSelectedRange: (rowIndex: number, colIndex: number) => boolean;
|
|
330
358
|
readonly getCellRangeEdges: (rowIndex: number, colIndex: number) => {
|
|
@@ -333,6 +361,12 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
333
361
|
left: boolean;
|
|
334
362
|
right: boolean;
|
|
335
363
|
} | null;
|
|
364
|
+
readonly getSelectionRects: () => {
|
|
365
|
+
minRow: number;
|
|
366
|
+
maxRow: number;
|
|
367
|
+
minCol: number;
|
|
368
|
+
maxCol: number;
|
|
369
|
+
}[];
|
|
336
370
|
readonly fillHandleCell: {
|
|
337
371
|
rowIndex: any;
|
|
338
372
|
colIndex: any;
|
|
@@ -354,9 +388,18 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
354
388
|
readonly onCellClick: (rowIndex: number, colIndex: number) => void;
|
|
355
389
|
readonly emitCellDoubleClick: (rowIndex: number, colIndex: number) => void;
|
|
356
390
|
readonly copySelectionToClipboard: () => void;
|
|
391
|
+
readonly cutSelectionToClipboard: () => Promise<void>;
|
|
392
|
+
readonly pasteFromClipboard: () => Promise<void>;
|
|
393
|
+
readonly onGridPaste: (event: ClipboardEvent) => void;
|
|
357
394
|
readonly clearSelectedCells: () => boolean;
|
|
358
395
|
readonly onCellDoubleClick: (rowIndex: number, colIndex: number) => void;
|
|
359
396
|
readonly startEditingWithChar: (rowIndex: number, colIndex: number, char: string) => boolean;
|
|
397
|
+
readonly startEditing: (rowIndex: number, columnId: string) => boolean;
|
|
398
|
+
readonly stopEditing: (cancel?: boolean) => boolean;
|
|
399
|
+
readonly startFullRowEdit: (rowIndex: number) => boolean;
|
|
400
|
+
readonly setFullRowDraft: (columnId: string, value: unknown) => void;
|
|
401
|
+
readonly commitFullRowEdit: () => void;
|
|
402
|
+
readonly cancelFullRowEdit: () => void;
|
|
360
403
|
readonly saveEditingCell: () => void;
|
|
361
404
|
readonly applyHistoryStep: (step: {
|
|
362
405
|
rowId: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { applyExcelFilter, normalizeForFilter, createColumnVirtualizer, createCoreRowModel, createExpandedRowModel, createFilteredRowModel, createGroupedRowModel, createSvelteVirtualizer, createSortedRowModel, createSvGrid, getGridCellDomId, sortFns, } from "./index";
|
|
2
|
-
import { createRowScrollScaling } from "./virtualization/scroll-scaling";
|
|
2
|
+
import { createRowScrollScaling, resolveMaxDomHeight, } from "./virtualization/scroll-scaling";
|
|
3
3
|
import "./sv-grid-scrollbar";
|
|
4
4
|
import { computeColumnStat, formatsNeedingStats, } from "./conditional-formatting";
|
|
5
5
|
import { rawToNumber, } from "./SvGrid.helpers";
|
|
@@ -12,6 +12,10 @@ import { createCellRender, } from "./cell-render";
|
|
|
12
12
|
import { createEditing, } from "./editing";
|
|
13
13
|
import { createSelection, } from "./selection";
|
|
14
14
|
import { createColumns, } from "./columns";
|
|
15
|
+
import { createRowDrag, } from "./row-drag";
|
|
16
|
+
import { createAlignedGrids, } from "./aligned-grids";
|
|
17
|
+
import { resolveColumnTypes, } from "./column-types";
|
|
18
|
+
import { computeColumnGroupMeta, hiddenLeavesForCollapse, } from "./column-groups";
|
|
15
19
|
import { createGridApi, } from "./build-api";
|
|
16
20
|
import { createClipboard, } from "./clipboard";
|
|
17
21
|
import { filterOperatorOptions, fallbackOperatorOption, TEXT_OPERATORS, NUMBER_OPERATORS, DATE_OPERATORS, CHECKBOX_OPERATORS, operatorOption, operatorsForColumn, defaultOperatorFor, operatorLabelFor, } from "./filter-operators";
|
|
@@ -24,35 +28,81 @@ import { getColumnBaseValue, isGroupRow, toolPanelHeaderLabel, formatSummaryNume
|
|
|
24
28
|
*/
|
|
25
29
|
const MAX_DOM_SCROLL_HEIGHT_FALLBACK = 8000000;
|
|
26
30
|
/**
|
|
27
|
-
* The browser's actual maximum element height in CSS px. Browsers
|
|
28
|
-
* tall a single element may be
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
31
|
+
* The browser's actual maximum *scrollable* element height in CSS px. Browsers
|
|
32
|
+
* clamp how tall a single element may be, and the cap is lower on mobile /
|
|
33
|
+
* high-DPR devices (the physical limit is in device px, so a 3x-DPR phone has
|
|
34
|
+
* ~1/3 the CSS-px cap of a 1x desktop). Past that cap a scroll container
|
|
35
|
+
* silently clamps its `scrollHeight` and the tail rows of a huge virtualized
|
|
36
|
+
* grid become unreachable.
|
|
37
|
+
*
|
|
38
|
+
* We measure two signals from one offscreen probe and keep the smaller (see
|
|
39
|
+
* `resolveMaxDomHeight`): the probe's clamped `offsetHeight`, AND the
|
|
40
|
+
* `scrollHeight` a real `overflow:auto` container exposes for it. The second
|
|
41
|
+
* matters because mobile WebKit/Blink can report a generous `offsetHeight` yet
|
|
42
|
+
* expose a smaller scrollable range - trusting the layout height alone is what
|
|
43
|
+
* stranded the last rows on phones. Using a real scroll container also folds in
|
|
44
|
+
* DPR clamping for free. Cached for the page lifetime; constant per browser.
|
|
32
45
|
*/
|
|
33
46
|
let detectedMaxDomHeight = null;
|
|
47
|
+
/**
|
|
48
|
+
* Seed the `hiddenColumns` map from any column def marked `visible: false`.
|
|
49
|
+
* Walks groups so a hidden group hides all of its leaf columns. Keyed by the
|
|
50
|
+
* same id `setColumnVisible` uses (`id ?? field`), so user toggles afterward
|
|
51
|
+
* stay consistent. Run once at mount; prop changes don't re-apply it.
|
|
52
|
+
*/
|
|
53
|
+
function initialHiddenColumns(defs) {
|
|
54
|
+
const hidden = {};
|
|
55
|
+
const walk = (cols, inheritedHidden) => {
|
|
56
|
+
for (const def of cols) {
|
|
57
|
+
const hide = inheritedHidden || def.visible === false;
|
|
58
|
+
if (def.columns?.length) {
|
|
59
|
+
walk(def.columns, hide);
|
|
60
|
+
}
|
|
61
|
+
else if (hide) {
|
|
62
|
+
const id = def.id ?? def.field;
|
|
63
|
+
if (id)
|
|
64
|
+
hidden[id] = true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
walk(defs, false);
|
|
69
|
+
return hidden;
|
|
70
|
+
}
|
|
34
71
|
function getMaxDomScrollHeight() {
|
|
72
|
+
// Escape hatch: a page may pin the cap via `window.__svgridMaxDomHeight`.
|
|
73
|
+
// Checked before the cache so it always wins. Two uses: reproducing a
|
|
74
|
+
// phone's lower element-height limit on desktop (and our e2e coverage of
|
|
75
|
+
// the huge-list path), and overriding detection on a device where it reads
|
|
76
|
+
// wrong. A non-positive / non-finite value is ignored.
|
|
77
|
+
if (typeof window !== "undefined") {
|
|
78
|
+
const forced = window
|
|
79
|
+
.__svgridMaxDomHeight;
|
|
80
|
+
if (typeof forced === "number" && Number.isFinite(forced) && forced > 0) {
|
|
81
|
+
return forced;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
35
84
|
if (detectedMaxDomHeight != null)
|
|
36
85
|
return detectedMaxDomHeight;
|
|
37
86
|
if (typeof document === "undefined" || !document.body) {
|
|
38
87
|
return MAX_DOM_SCROLL_HEIGHT_FALLBACK;
|
|
39
88
|
}
|
|
40
89
|
try {
|
|
90
|
+
// The wrapper is itself an `overflow:auto` scroll container (kept tiny and
|
|
91
|
+
// offscreen so it never affects page layout or scroll), so we can read the
|
|
92
|
+
// height it actually exposes as scrollable - not just the probe's layout
|
|
93
|
+
// height. On high-DPR mobile the two diverge and the scrollable one is the
|
|
94
|
+
// limit that matters.
|
|
41
95
|
const wrap = document.createElement("div");
|
|
42
96
|
wrap.style.cssText =
|
|
43
|
-
"position:fixed;top:0;left:-9999px;width:1px;height:
|
|
97
|
+
"position:fixed;top:0;left:-9999px;width:1px;height:100px;overflow:auto;visibility:hidden;pointer-events:none;";
|
|
44
98
|
const probe = document.createElement("div");
|
|
45
99
|
probe.style.cssText = "width:1px;height:1000000000px;";
|
|
46
100
|
wrap.appendChild(probe);
|
|
47
101
|
document.body.appendChild(wrap);
|
|
48
|
-
const
|
|
102
|
+
const layoutCap = probe.offsetHeight;
|
|
103
|
+
const scrollCap = wrap.scrollHeight;
|
|
49
104
|
document.body.removeChild(wrap);
|
|
50
|
-
|
|
51
|
-
// If the browser did not clamp (returned ~1e9) or returned junk, fall back.
|
|
52
|
-
detectedMaxDomHeight =
|
|
53
|
-
measured > 100000 && measured < 900000000
|
|
54
|
-
? measured
|
|
55
|
-
: MAX_DOM_SCROLL_HEIGHT_FALLBACK;
|
|
105
|
+
detectedMaxDomHeight = resolveMaxDomHeight(layoutCap, scrollCap, MAX_DOM_SCROLL_HEIGHT_FALLBACK);
|
|
56
106
|
}
|
|
57
107
|
catch {
|
|
58
108
|
detectedMaxDomHeight = MAX_DOM_SCROLL_HEIGHT_FALLBACK;
|
|
@@ -91,6 +141,10 @@ export function createSvGridController(props) {
|
|
|
91
141
|
let pendingScrollLeft = 0;
|
|
92
142
|
let scrollSyncRaf = null;
|
|
93
143
|
let selectionRange = $state({ anchor: null, focus: null });
|
|
144
|
+
// Extra committed ranges for multi-range (Ctrl+drag) selection. The
|
|
145
|
+
// `selectionRange` above is always the ACTIVE range being manipulated; these
|
|
146
|
+
// are the finished ones. Full selection = these + the active range.
|
|
147
|
+
let selectionRanges = $state.raw([]);
|
|
94
148
|
let isDraggingSelection = $state(false);
|
|
95
149
|
/** Excel-style fill handle drag state. While non-null we paint a "fill
|
|
96
150
|
* preview" overlay on cells between the source range and the pointer
|
|
@@ -98,6 +152,9 @@ export function createSvGridController(props) {
|
|
|
98
152
|
let fillDrag = $state(null);
|
|
99
153
|
let activeAtPointerDown = null;
|
|
100
154
|
let editingCell = $state(null);
|
|
155
|
+
// Full-row editing: the row currently in whole-row edit + its per-column
|
|
156
|
+
// draft (keyed by column id). Null when not in full-row mode.
|
|
157
|
+
let fullRowEdit = $state(null);
|
|
101
158
|
let editedCellValues = $state({});
|
|
102
159
|
const UNDO_LIMIT = 200;
|
|
103
160
|
let history = $state([]);
|
|
@@ -160,6 +217,7 @@ export function createSvGridController(props) {
|
|
|
160
217
|
const rowNumberColumnWidth = $derived(props.rowNumberWidth ?? 56);
|
|
161
218
|
const showRowNumbersEffective = $derived(props.showRowNumbers ?? false);
|
|
162
219
|
let columnMenuFor = $state(null);
|
|
220
|
+
let columnMenuTab = $state("general");
|
|
163
221
|
let columnMenuPos = $state({ x: 0, y: 0 });
|
|
164
222
|
let columnMenuSearch = $state("");
|
|
165
223
|
let filterMenuFor = $state(null);
|
|
@@ -205,14 +263,25 @@ export function createSvGridController(props) {
|
|
|
205
263
|
};
|
|
206
264
|
});
|
|
207
265
|
/** Vertical overflow from the virtualizer's authoritative total size,
|
|
208
|
-
* NOT from `scrollMetrics.scrollHeight
|
|
266
|
+
* NOT from `scrollMetrics.scrollHeight` alone. Reading DOM dimensions
|
|
209
267
|
* during a Svelte derived runs BEFORE the browser paints - the table
|
|
210
268
|
* hasn't laid out the new rows yet, so `scrollHeight` is briefly 0
|
|
211
269
|
* even after data loads. That made the overflow flag return false,
|
|
212
|
-
* hid the scrollbar, and broke dragging.
|
|
270
|
+
* hid the scrollbar, and broke dragging.
|
|
271
|
+
*
|
|
272
|
+
* However, virtualizer.getTotalSize() uses rowHeight * numRows which
|
|
273
|
+
* underestimates when variable-height rows are present (e.g. master-detail
|
|
274
|
+
* expanded rows). We therefore take the MAX of the two sources:
|
|
275
|
+
* - virtualizer.getTotalSize(): correct at initial load (before first paint)
|
|
276
|
+
* - scrollMetrics.scrollHeight: correct after detail rows expand (DOM is live,
|
|
277
|
+
* ResizeObserver on gridRootEl already bumps scrollVersion at that point) */
|
|
213
278
|
const hasVerticalOverflow = $derived.by(() => {
|
|
214
279
|
virtualizer.version;
|
|
215
|
-
|
|
280
|
+
const virtualizerSize = virtualizer.getTotalSize();
|
|
281
|
+
// scrollMetrics.scrollHeight is 0 before initial paint; once the table
|
|
282
|
+
// is in the DOM it reflects the true content height including expanded rows.
|
|
283
|
+
const domSize = scrollMetrics.scrollHeight;
|
|
284
|
+
return Math.max(virtualizerSize, domSize) > viewportHeight + 1;
|
|
216
285
|
});
|
|
217
286
|
// Effective filter-UI flags. Each show* prop wins when explicitly set;
|
|
218
287
|
// otherwise the `filterMode` prop (default 'menu') picks exactly one surface.
|
|
@@ -236,9 +305,41 @@ export function createSvGridController(props) {
|
|
|
236
305
|
// mutates these so add/remove operations don't need a callback round-trip.
|
|
237
306
|
// svelte-ignore state_referenced_locally
|
|
238
307
|
let internalData = $state.raw(props.data);
|
|
308
|
+
// Resolve `cellDataType` / `inferColumnTypes` into concrete editorType +
|
|
309
|
+
// format defaults once, up front, so every downstream reader sees a normal
|
|
310
|
+
// column. Explicit fields on the ColumnDef always win.
|
|
311
|
+
// svelte-ignore state_referenced_locally
|
|
312
|
+
const resolveCols = (cols) => resolveColumnTypes(cols, props.data?.[0], props.inferColumnTypes === true);
|
|
239
313
|
// svelte-ignore state_referenced_locally
|
|
240
|
-
let internalColumns = $state.raw(props.columns);
|
|
241
|
-
|
|
314
|
+
let internalColumns = $state.raw(resolveCols(props.columns));
|
|
315
|
+
// svelte-ignore state_referenced_locally
|
|
316
|
+
let hiddenColumns = $state(initialHiddenColumns(props.columns));
|
|
317
|
+
// Collapsible column groups (columnGroupShow). Meta is derived from the tree;
|
|
318
|
+
// `collapsedColumnGroups` is the live set of collapsed group ids, seeded once
|
|
319
|
+
// from each collapsible group's `openByDefault` (default: collapsed).
|
|
320
|
+
const columnGroupMeta = $derived(computeColumnGroupMeta(props.columns));
|
|
321
|
+
// svelte-ignore state_referenced_locally
|
|
322
|
+
let collapsedColumnGroups = $state((() => {
|
|
323
|
+
const meta = computeColumnGroupMeta(props.columns);
|
|
324
|
+
const s = new Set();
|
|
325
|
+
for (const id of meta.collapsibleGroupIds)
|
|
326
|
+
if (!meta.defaultOpen.get(id))
|
|
327
|
+
s.add(id);
|
|
328
|
+
return s;
|
|
329
|
+
})());
|
|
330
|
+
// Leaf ids hidden right now because their group is collapsed/expanded.
|
|
331
|
+
const hiddenByGroupCollapse = $derived(hiddenLeavesForCollapse(columnGroupMeta, collapsedColumnGroups));
|
|
332
|
+
function toggleColumnGroup(groupId) {
|
|
333
|
+
const next = new Set(collapsedColumnGroups);
|
|
334
|
+
if (next.has(groupId))
|
|
335
|
+
next.delete(groupId);
|
|
336
|
+
else
|
|
337
|
+
next.add(groupId);
|
|
338
|
+
collapsedColumnGroups = next;
|
|
339
|
+
}
|
|
340
|
+
function isColumnGroupCollapsed(groupId) {
|
|
341
|
+
return collapsedColumnGroups.has(groupId);
|
|
342
|
+
}
|
|
242
343
|
$effect(() => {
|
|
243
344
|
// When the consumer replaces `data` (e.g. a "Reset" button), drop any
|
|
244
345
|
// accumulated cell-edit overrides - otherwise `getCellDisplayValue`
|
|
@@ -248,7 +349,7 @@ export function createSvGridController(props) {
|
|
|
248
349
|
editedCellValues = {};
|
|
249
350
|
});
|
|
250
351
|
$effect(() => {
|
|
251
|
-
internalColumns = props.columns;
|
|
352
|
+
internalColumns = resolveCols(props.columns);
|
|
252
353
|
});
|
|
253
354
|
// Captured ONCE at mount: `externalSort` is a structural choice (tree vs
|
|
254
355
|
// flat data) so toggling it after mount is not supported. Reading it here
|
|
@@ -334,7 +435,7 @@ export function createSvGridController(props) {
|
|
|
334
435
|
const allColumns = $derived.by(() => {
|
|
335
436
|
let raw = grid
|
|
336
437
|
.getAllColumns()
|
|
337
|
-
.filter((column) => !hiddenColumns[column.id]);
|
|
438
|
+
.filter((column) => !hiddenColumns[column.id] && !hiddenByGroupCollapse[column.id]);
|
|
338
439
|
// Apply user reorder (if any). Unknown ids in userColumnOrder are
|
|
339
440
|
// skipped; columns not in userColumnOrder keep their original
|
|
340
441
|
// relative order after the user-ordered ones.
|
|
@@ -403,13 +504,17 @@ export function createSvGridController(props) {
|
|
|
403
504
|
function buildId(def, parentId, fallbackIx) {
|
|
404
505
|
return def.id ?? def.field ?? `${parentId ?? 'col'}_d_${fallbackIx}`;
|
|
405
506
|
}
|
|
507
|
+
// Leaves hidden by a collapsed/expanded group are skipped everywhere here,
|
|
508
|
+
// so group colSpan + widthPx exclude them and stay aligned with the leaves
|
|
509
|
+
// the body actually renders.
|
|
510
|
+
const hiddenLeaf = hiddenByGroupCollapse;
|
|
406
511
|
function collectLeaves(defs, parentId, depthHere) {
|
|
407
512
|
defs.forEach((def, ix) => {
|
|
408
513
|
const id = buildId(def, parentId, ix);
|
|
409
514
|
if (def.columns?.length) {
|
|
410
515
|
collectLeaves(def.columns, id, depthHere + 1);
|
|
411
516
|
}
|
|
412
|
-
else {
|
|
517
|
+
else if (!hiddenLeaf[id]) {
|
|
413
518
|
leafEntries.push({ id, widthPx: getColumnWidth(id) });
|
|
414
519
|
}
|
|
415
520
|
});
|
|
@@ -419,6 +524,10 @@ export function createSvGridController(props) {
|
|
|
419
524
|
const nodes = [];
|
|
420
525
|
for (const def of defs) {
|
|
421
526
|
const id = buildId(def, parentId, nodes.length);
|
|
527
|
+
// Skip leaves the collapse state hides, so leaf indices/colSpans match
|
|
528
|
+
// `leafEntries` (and the body's rendered columns) exactly.
|
|
529
|
+
if (!def.columns?.length && hiddenLeaf[id])
|
|
530
|
+
continue;
|
|
422
531
|
const leafStart = cursor.leaf;
|
|
423
532
|
if (def.columns?.length) {
|
|
424
533
|
indexTree(def.columns, id, cursor);
|
|
@@ -464,6 +573,7 @@ export function createSvGridController(props) {
|
|
|
464
573
|
const cells = at.map((n) => {
|
|
465
574
|
const isLeafEarly = !n.def.columns?.length;
|
|
466
575
|
const headerText = typeof n.def.header === 'string' ? n.def.header : '';
|
|
576
|
+
const collapsible = columnGroupMeta.collapsibleGroupIds.has(n.id);
|
|
467
577
|
return {
|
|
468
578
|
key: `${n.id}_d${d}`,
|
|
469
579
|
label: isLeafEarly ? '' : headerText,
|
|
@@ -471,6 +581,9 @@ export function createSvGridController(props) {
|
|
|
471
581
|
widthPx: sumLeafWidths(n.leafStart, n.leafEnd),
|
|
472
582
|
firstLeafIndex: n.leafStart,
|
|
473
583
|
isPlaceholder: isLeafEarly,
|
|
584
|
+
groupId: collapsible ? n.id : undefined,
|
|
585
|
+
collapsible,
|
|
586
|
+
collapsed: collapsible && collapsedColumnGroups.has(n.id),
|
|
474
587
|
};
|
|
475
588
|
});
|
|
476
589
|
rows.push({ id: `gh_${d}`, cells });
|
|
@@ -504,6 +617,12 @@ export function createSvGridController(props) {
|
|
|
504
617
|
let colDragId = $state(null);
|
|
505
618
|
let colDropOnId = $state(null);
|
|
506
619
|
let colDropSide = $state(null);
|
|
620
|
+
// Live drag state for managed row dragging. Only meaningful while a row is
|
|
621
|
+
// being dragged (`props.rowDragManaged`). `rowDropIndex` is the visible row
|
|
622
|
+
// index currently hovered; `rowDropSide` says which edge the drop line paints.
|
|
623
|
+
let rowDragActive = $state(false);
|
|
624
|
+
let rowDropIndex = $state(null);
|
|
625
|
+
let rowDropSide = $state(null);
|
|
507
626
|
// ---- Conditional formatting --------------------------------------------
|
|
508
627
|
// True when the feature is in use; gates the per-cell positioning context
|
|
509
628
|
// (cells are otherwise non-relative for scroll performance).
|
|
@@ -521,12 +640,12 @@ export function createSvGridController(props) {
|
|
|
521
640
|
if (!needs)
|
|
522
641
|
continue;
|
|
523
642
|
const def = column.columnDef;
|
|
524
|
-
const
|
|
643
|
+
const fieldFn = def.fieldFn;
|
|
525
644
|
const field = def.field;
|
|
526
645
|
const stat = computeColumnStat((function* () {
|
|
527
646
|
for (const row of allRows) {
|
|
528
|
-
yield
|
|
529
|
-
?
|
|
647
|
+
yield fieldFn
|
|
648
|
+
? fieldFn(row.original)
|
|
530
649
|
: field
|
|
531
650
|
? row.original[field]
|
|
532
651
|
: row.getCellValueByColumnId(column.id);
|
|
@@ -577,23 +696,36 @@ export function createSvGridController(props) {
|
|
|
577
696
|
.some((cell) => normalizeForFilter(String(cell.getValue() ?? ""), props.filterLocale)
|
|
578
697
|
.includes(needle)));
|
|
579
698
|
}
|
|
580
|
-
|
|
581
|
-
|
|
699
|
+
// A single condition is "active" if it has the value(s) it needs.
|
|
700
|
+
const condActive = (op, value, valueTo) => {
|
|
701
|
+
if (op === "isBlank")
|
|
582
702
|
return true;
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
703
|
+
if (op === "between")
|
|
704
|
+
return value.trim().length > 0 && (valueTo ?? "").trim().length > 0;
|
|
705
|
+
return value.trim().length > 0;
|
|
706
|
+
};
|
|
707
|
+
const evalCond = (cellValue, columnId, op, value, valueTo) => applyExcelFilter(cellValue, { id: columnId, operator: op, value, valueTo: op === "between" ? valueTo : undefined }, { locale: props.filterLocale });
|
|
708
|
+
// A column filter is active if either of its (up to two) conditions is.
|
|
709
|
+
const menuFilters = Object.entries(filterMenuValues).filter(([_, f]) => {
|
|
710
|
+
const a = condActive(f.operator, f.value, f.valueTo);
|
|
711
|
+
const b = !!f.operator2 && condActive(f.operator2, f.value2 ?? "", f.valueTo2);
|
|
712
|
+
return a || b;
|
|
589
713
|
});
|
|
590
714
|
if (menuFilters.length) {
|
|
591
|
-
rows = rows.filter((row) => menuFilters.every(([columnId,
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
715
|
+
rows = rows.filter((row) => menuFilters.every(([columnId, f]) => {
|
|
716
|
+
const cellValue = getRowColumnValue(row, columnId);
|
|
717
|
+
const aActive = condActive(f.operator, f.value, f.valueTo);
|
|
718
|
+
const bActive = !!f.operator2 && condActive(f.operator2, f.value2 ?? "", f.valueTo2);
|
|
719
|
+
const ra = aActive ? evalCond(cellValue, columnId, f.operator, f.value, f.valueTo) : null;
|
|
720
|
+
const rb = bActive
|
|
721
|
+
? evalCond(cellValue, columnId, f.operator2, f.value2 ?? "", f.valueTo2)
|
|
722
|
+
: null;
|
|
723
|
+
if (ra === null)
|
|
724
|
+
return rb ?? true;
|
|
725
|
+
if (rb === null)
|
|
726
|
+
return ra;
|
|
727
|
+
return f.join === "OR" ? ra || rb : ra && rb;
|
|
728
|
+
}));
|
|
597
729
|
}
|
|
598
730
|
const valueFilterEntries = Object.entries(valueFilters);
|
|
599
731
|
if (valueFilterEntries.length) {
|
|
@@ -739,7 +871,10 @@ export function createSvGridController(props) {
|
|
|
739
871
|
};
|
|
740
872
|
});
|
|
741
873
|
// ---- Tool panel (docked columns sidebar) -------------------------------
|
|
742
|
-
|
|
874
|
+
// svelte-ignore state_referenced_locally
|
|
875
|
+
let toolPanelOpen = $state(props.toolPanelDefaultOpen === true);
|
|
876
|
+
// svelte-ignore state_referenced_locally
|
|
877
|
+
let toolPanelTab = $state(props.toolPanelDefaultTab ?? "columns");
|
|
743
878
|
const toolPanelEnabled = $derived(props.toolPanel === true);
|
|
744
879
|
// Every column (including hidden ones) in the user's current order, so the
|
|
745
880
|
// panel can toggle/reorder anything. Group columns are flagged live.
|
|
@@ -1053,6 +1188,7 @@ export function createSvGridController(props) {
|
|
|
1053
1188
|
if (isFirstRender)
|
|
1054
1189
|
return;
|
|
1055
1190
|
selectionRange = { anchor: null, focus: null };
|
|
1191
|
+
selectionRanges = [];
|
|
1056
1192
|
editingCell = null;
|
|
1057
1193
|
if (scrollContainer) {
|
|
1058
1194
|
scrollContainer.scrollTop = 0;
|
|
@@ -1218,7 +1354,16 @@ export function createSvGridController(props) {
|
|
|
1218
1354
|
return null;
|
|
1219
1355
|
const rowNumberWidth = showRowNumbersEffective ? rowNumberColumnWidth : 0;
|
|
1220
1356
|
const selectionWidth = showRowSelectionEffective ? selectionColumnWidth : 0;
|
|
1221
|
-
|
|
1357
|
+
// Reserve the custom vertical scrollbar's width when it's visible. It
|
|
1358
|
+
// overlays the right 16px of the viewport (absolute, z-index 40) and
|
|
1359
|
+
// does NOT shrink clientWidth, so without this the last fitted column
|
|
1360
|
+
// slides under it and its right-aligned content (e.g. a number column)
|
|
1361
|
+
// is hidden behind the opaque scrollbar.
|
|
1362
|
+
const scrollbarWidth = hasVerticalOverflow ? 16 : 0;
|
|
1363
|
+
const target = (scrollContainer?.clientWidth ?? 0) -
|
|
1364
|
+
rowNumberWidth -
|
|
1365
|
+
selectionWidth -
|
|
1366
|
+
scrollbarWidth;
|
|
1222
1367
|
if (target <= 0)
|
|
1223
1368
|
return null;
|
|
1224
1369
|
// Split base widths into pinned (user-resized) and scalable.
|
|
@@ -1322,7 +1467,9 @@ export function createSvGridController(props) {
|
|
|
1322
1467
|
return map;
|
|
1323
1468
|
});
|
|
1324
1469
|
const columnMenuFacetValues = $derived.by(() => {
|
|
1325
|
-
|
|
1470
|
+
// The funnel popover drives via `filterMenuFor`; the column menu's Filter
|
|
1471
|
+
// tab drives via `columnMenuFor`. Support whichever is open.
|
|
1472
|
+
const columnId = filterMenuFor ?? columnMenuFor;
|
|
1326
1473
|
if (!columnId)
|
|
1327
1474
|
return [];
|
|
1328
1475
|
const column = allColumns.find((entry) => entry.id === columnId);
|
|
@@ -1393,6 +1540,8 @@ export function createSvGridController(props) {
|
|
|
1393
1540
|
set scrollSyncRaf(v) { scrollSyncRaf = v; },
|
|
1394
1541
|
get selectionRange() { return selectionRange; },
|
|
1395
1542
|
set selectionRange(v) { selectionRange = v; },
|
|
1543
|
+
get selectionRanges() { return selectionRanges; },
|
|
1544
|
+
set selectionRanges(v) { selectionRanges = v; },
|
|
1396
1545
|
get isDraggingSelection() { return isDraggingSelection; },
|
|
1397
1546
|
set isDraggingSelection(v) { isDraggingSelection = v; },
|
|
1398
1547
|
get fillDrag() { return fillDrag; },
|
|
@@ -1401,6 +1550,8 @@ export function createSvGridController(props) {
|
|
|
1401
1550
|
set activeAtPointerDown(v) { activeAtPointerDown = v; },
|
|
1402
1551
|
get editingCell() { return editingCell; },
|
|
1403
1552
|
set editingCell(v) { editingCell = v; },
|
|
1553
|
+
get fullRowEdit() { return fullRowEdit; },
|
|
1554
|
+
set fullRowEdit(v) { fullRowEdit = v; },
|
|
1404
1555
|
get editedCellValues() { return editedCellValues; },
|
|
1405
1556
|
set editedCellValues(v) { editedCellValues = v; },
|
|
1406
1557
|
get UNDO_LIMIT() { return UNDO_LIMIT; },
|
|
@@ -1453,6 +1604,8 @@ export function createSvGridController(props) {
|
|
|
1453
1604
|
get DATE_OPERATORS() { return DATE_OPERATORS; },
|
|
1454
1605
|
get CHECKBOX_OPERATORS() { return CHECKBOX_OPERATORS; },
|
|
1455
1606
|
get columnMenuFor() { return columnMenuFor; },
|
|
1607
|
+
get columnMenuTab() { return columnMenuTab; },
|
|
1608
|
+
set columnMenuTab(v) { columnMenuTab = v; },
|
|
1456
1609
|
set columnMenuFor(v) { columnMenuFor = v; },
|
|
1457
1610
|
get columnMenuPos() { return columnMenuPos; },
|
|
1458
1611
|
set columnMenuPos(v) { columnMenuPos = v; },
|
|
@@ -1498,6 +1651,8 @@ export function createSvGridController(props) {
|
|
|
1498
1651
|
set internalColumns(v) { internalColumns = v; },
|
|
1499
1652
|
get hiddenColumns() { return hiddenColumns; },
|
|
1500
1653
|
set hiddenColumns(v) { hiddenColumns = v; },
|
|
1654
|
+
get toggleColumnGroup() { return toggleColumnGroup; },
|
|
1655
|
+
get isColumnGroupCollapsed() { return isColumnGroupCollapsed; },
|
|
1501
1656
|
get externalSortEnabled() { return externalSortEnabled; },
|
|
1502
1657
|
get externalFilterEnabled() { return externalFilterEnabled; },
|
|
1503
1658
|
get passthroughSortedRowModel() { return passthroughSortedRowModel; },
|
|
@@ -1519,6 +1674,20 @@ export function createSvGridController(props) {
|
|
|
1519
1674
|
set colDropOnId(v) { colDropOnId = v; },
|
|
1520
1675
|
get colDropSide() { return colDropSide; },
|
|
1521
1676
|
set colDropSide(v) { colDropSide = v; },
|
|
1677
|
+
get rowDragActive() { return rowDragActive; },
|
|
1678
|
+
set rowDragActive(v) { rowDragActive = v; },
|
|
1679
|
+
get rowDropIndex() { return rowDropIndex; },
|
|
1680
|
+
set rowDropIndex(v) { rowDropIndex = v; },
|
|
1681
|
+
get rowDropSide() { return rowDropSide; },
|
|
1682
|
+
set rowDropSide(v) { rowDropSide = v; },
|
|
1683
|
+
get onRowDragStart() { return onRowDragStart; },
|
|
1684
|
+
get onRowDragOver() { return onRowDragOver; },
|
|
1685
|
+
get onRowDragLeave() { return onRowDragLeave; },
|
|
1686
|
+
get onRowDrop() { return onRowDrop; },
|
|
1687
|
+
get onRowsContainerDragOver() { return onRowsContainerDragOver; },
|
|
1688
|
+
get onRowsContainerDrop() { return onRowsContainerDrop; },
|
|
1689
|
+
get onRowDragEnd() { return onRowDragEnd; },
|
|
1690
|
+
get broadcastAlignedScroll() { return broadcastAlignedScroll; },
|
|
1522
1691
|
get getCurrentColumnOrder() { return getCurrentColumnOrder; },
|
|
1523
1692
|
get emitColumnOrder() { return emitColumnOrder; },
|
|
1524
1693
|
get setColumnOrderInternal() { return setColumnOrderInternal; },
|
|
@@ -1554,6 +1723,8 @@ export function createSvGridController(props) {
|
|
|
1554
1723
|
get statusBarStats() { return statusBarStats; },
|
|
1555
1724
|
get toolPanelOpen() { return toolPanelOpen; },
|
|
1556
1725
|
set toolPanelOpen(v) { toolPanelOpen = v; },
|
|
1726
|
+
get toolPanelTab() { return toolPanelTab; },
|
|
1727
|
+
set toolPanelTab(v) { toolPanelTab = v; },
|
|
1557
1728
|
get toolPanelEnabled() { return toolPanelEnabled; },
|
|
1558
1729
|
get toolPanelColumns() { return toolPanelColumns; },
|
|
1559
1730
|
get toolPanelHeaderLabel() { return toolPanelHeaderLabel; },
|
|
@@ -1636,6 +1807,7 @@ export function createSvGridController(props) {
|
|
|
1636
1807
|
get extendSelection() { return extendSelection; },
|
|
1637
1808
|
get isCellInSelectedRange() { return isCellInSelectedRange; },
|
|
1638
1809
|
get getCellRangeEdges() { return getCellRangeEdges; },
|
|
1810
|
+
get getSelectionRects() { return getSelectionRects; },
|
|
1639
1811
|
get fillHandleCell() { return fillHandleCell; },
|
|
1640
1812
|
get isInFillPreview() { return isInFillPreview; },
|
|
1641
1813
|
get findColumnById() { return findColumnById; },
|
|
@@ -1654,9 +1826,18 @@ export function createSvGridController(props) {
|
|
|
1654
1826
|
get onCellClick() { return onCellClick; },
|
|
1655
1827
|
get emitCellDoubleClick() { return emitCellDoubleClick; },
|
|
1656
1828
|
get copySelectionToClipboard() { return copySelectionToClipboard; },
|
|
1829
|
+
get cutSelectionToClipboard() { return cutSelectionToClipboard; },
|
|
1830
|
+
get pasteFromClipboard() { return pasteFromClipboard; },
|
|
1831
|
+
get onGridPaste() { return onGridPaste; },
|
|
1657
1832
|
get clearSelectedCells() { return clearSelectedCells; },
|
|
1658
1833
|
get onCellDoubleClick() { return onCellDoubleClick; },
|
|
1659
1834
|
get startEditingWithChar() { return startEditingWithChar; },
|
|
1835
|
+
get startEditing() { return startEditing; },
|
|
1836
|
+
get stopEditing() { return stopEditing; },
|
|
1837
|
+
get startFullRowEdit() { return startFullRowEdit; },
|
|
1838
|
+
get setFullRowDraft() { return setFullRowDraft; },
|
|
1839
|
+
get commitFullRowEdit() { return commitFullRowEdit; },
|
|
1840
|
+
get cancelFullRowEdit() { return cancelFullRowEdit; },
|
|
1660
1841
|
get saveEditingCell() { return saveEditingCell; },
|
|
1661
1842
|
get applyHistoryStep() { return applyHistoryStep; },
|
|
1662
1843
|
get updateEditingCellValue() { return updateEditingCellValue; },
|
|
@@ -1723,10 +1904,25 @@ export function createSvGridController(props) {
|
|
|
1723
1904
|
const { computeSummaries, hasRenderedColumn } = createSummaries(ctx);
|
|
1724
1905
|
const { updateFilterRow, updateFilterOperator, updateFilterMenuValue, updateFilterMenuValueTo, toggleCheckboxWithKeyboard, isColumnFiltered, closeMenus, openChooseColumns, openColumnMenu, openFilterMenu, openOperatorMenu, sortColumnFromMenu, clearColumnSort, groupByColumnFromMenu, clearGroupingFromMenu, isFacetChecked, toggleFacetValue, isAllFacetsChecked, toggleAllFacets, clearColumnFilter, changePage, goToPage, setPageSize, openContextMenu, closeContextMenu, contextMenuItems, saveComment, removeComment, closeCommentEditor } = createMenus(ctx);
|
|
1725
1906
|
const { cellConditionalFormat, computeRowClass, computeCellClass, computeCellTooltip, computeCellNote, getColumnEditorOptions, formatListCellValue, formatCellValue, formatPinnedValue, computePinnedCellClass } = createCellRender(ctx);
|
|
1726
|
-
const { isCellEditable, isCellEditableAt, getRowColumnValue, getCellDisplayValue, startEditingWithChar, saveEditingCell, applyHistoryStep, updateEditingCellValue, onEditorKeyDown, focusOnMount, onCellDoubleClick, pasteFromClipboard } = createEditing(ctx);
|
|
1727
|
-
const { isRowSelected, toggleRowSelectionById, toggleSelectAllRows, setActiveCell, scrollActiveCellIntoView, setSelection, extendSelection, isCellInSelectedRange, getCellRangeEdges, isInFillPreview, findColumnById, onCellPointerDown, onCellPointerEnter, endDragSelection, onWindowPointerMove, onCellClick, emitCellDoubleClick } = createSelection(ctx);
|
|
1907
|
+
const { isCellEditable, isCellEditableAt, getRowColumnValue, getCellDisplayValue, startEditingWithChar, startEditing, stopEditing, startFullRowEdit, setFullRowDraft, commitFullRowEdit, cancelFullRowEdit, saveEditingCell, applyHistoryStep, updateEditingCellValue, onEditorKeyDown, focusOnMount, onCellDoubleClick, pasteFromClipboard, onGridPaste } = createEditing(ctx);
|
|
1908
|
+
const { isRowSelected, toggleRowSelectionById, toggleSelectAllRows, setActiveCell, scrollActiveCellIntoView, setSelection, extendSelection, isCellInSelectedRange, getCellRangeEdges, getSelectionRects, isInFillPreview, findColumnById, onCellPointerDown, onCellPointerEnter, endDragSelection, onWindowPointerMove, onCellClick, emitCellDoubleClick } = createSelection(ctx);
|
|
1728
1909
|
const { cellPinStyle, isColumnPinned, getCurrentColumnOrder, emitColumnOrder, setColumnOrderInternal, applyColumnDrop, onColumnHeaderDragStart, onColumnHeaderDragOver, onColumnHeaderDragLeave, onColumnHeaderDrop, onColumnHeaderDragEnd, pinColumnLeft, pinColumnRight, unpinColumn, toggleColumnVisibleInPanel, moveColumnInPanel, toggleGroupInPanel, getColumnBaseWidth, getColumnWidth, startColumnResize, onColumnResizeMove, endColumnResize, measureText, autosizeColumn, autosizeAllColumns, resetColumns } = createColumns(ctx);
|
|
1910
|
+
const { onRowDragStart, onRowDragOver, onRowDragLeave, onRowDrop, onRowsContainerDragOver, onRowsContainerDrop, onRowDragEnd } = createRowDrag(ctx);
|
|
1911
|
+
const { register: registerAlignedGrid, broadcastScroll: broadcastAlignedScroll, broadcastWidths: broadcastAlignedWidths } = createAlignedGrids(ctx);
|
|
1729
1912
|
const { buildApi } = createGridApi(ctx);
|
|
1730
1913
|
const { readCellRaw, writeCellRaw, applyFillPattern, clearSelectedCellValues, startFillDrag, onFillPointerMove, onFillPointerUp, toggleBooleanCell, copySelectionToClipboard, clearSelectedCells, cutSelectionToClipboard } = createClipboard(ctx);
|
|
1914
|
+
// Aligned grids: register in the shared group on mount, and mirror column
|
|
1915
|
+
// resizes to peers whenever columnWidths changes. Horizontal-scroll mirroring
|
|
1916
|
+
// is driven from onBodyScroll (via ctx.broadcastAlignedScroll).
|
|
1917
|
+
$effect(() => {
|
|
1918
|
+
if (props.alignedGridGroup == null)
|
|
1919
|
+
return;
|
|
1920
|
+
return registerAlignedGrid();
|
|
1921
|
+
});
|
|
1922
|
+
$effect(() => {
|
|
1923
|
+
// Track columnWidths reactively, then broadcast to aligned peers.
|
|
1924
|
+
void columnWidths;
|
|
1925
|
+
broadcastAlignedWidths();
|
|
1926
|
+
});
|
|
1731
1927
|
return ctx;
|
|
1732
1928
|
}
|