@svgrid/grid 2.6.21 → 2.7.0
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 +150 -88
- package/README.md +221 -199
- package/dist/FlexRender.svelte +96 -96
- package/dist/GridFooter.svelte +181 -181
- package/dist/GridMenus.svelte +201 -18
- package/dist/SvAutoComplete.svelte +169 -165
- package/dist/SvAvatar.svelte +75 -75
- package/dist/SvCalendar.svelte +503 -503
- package/dist/SvCarousel.svelte +141 -141
- package/dist/SvCheckBox.svelte +102 -102
- package/dist/SvCircularProgress.svelte +109 -109
- package/dist/SvColorInput.svelte +181 -181
- package/dist/SvComboBox.svelte +279 -275
- package/dist/SvContextMenu.svelte +116 -116
- package/dist/SvCountryInput.svelte +163 -163
- package/dist/SvDateRangeInput.svelte +25 -3
- package/dist/SvDrawer.svelte +254 -254
- package/dist/SvDropDownList.svelte +378 -374
- package/dist/SvDurationInput.svelte +126 -126
- package/dist/SvField.svelte +293 -293
- package/dist/SvForm.svelte +437 -437
- package/dist/SvGrid.controller.svelte.d.ts +61 -9
- package/dist/SvGrid.controller.svelte.js +363 -123
- package/dist/SvGrid.css +222 -1
- package/dist/SvGrid.helpers.d.ts +55 -0
- package/dist/SvGrid.helpers.js +83 -0
- package/dist/SvGrid.svelte +255 -56
- package/dist/SvGrid.types.d.ts +146 -1
- package/dist/SvGridCellEditor.svelte +744 -662
- package/dist/SvGridChart.svelte +1716 -1716
- package/dist/SvGridChartPanel.svelte +485 -485
- package/dist/SvGridChartView.svelte +70 -70
- package/dist/SvGridDropdown.svelte +728 -728
- package/dist/SvGridSelect.svelte +270 -266
- package/dist/SvGridSelect.svelte.d.ts +1 -1
- package/dist/SvGroupCell.svelte +116 -116
- package/dist/SvListBox.svelte +334 -326
- package/dist/SvMaskedInput.svelte +122 -122
- package/dist/SvMenu.svelte +124 -124
- package/dist/SvMenuList.svelte +146 -146
- package/dist/SvMultiSelect.svelte +293 -289
- package/dist/SvMultiSelect.svelte.d.ts +1 -1
- package/dist/SvNumberInput.svelte +172 -168
- package/dist/SvOtpInput.svelte +158 -158
- package/dist/SvPasswordInput.svelte +151 -151
- package/dist/SvPhoneInput.svelte +133 -133
- package/dist/SvPopover.svelte +197 -197
- package/dist/SvProgress.svelte +116 -116
- package/dist/SvRadioGroup.svelte +107 -107
- package/dist/SvRating.svelte +112 -112
- package/dist/SvResult.svelte +73 -73
- package/dist/SvRichCell.svelte +125 -0
- package/dist/SvRichCell.svelte.d.ts +15 -0
- package/dist/SvRichText.svelte +211 -211
- package/dist/SvRowGroupPanel.svelte +170 -170
- package/dist/SvScrollArea.svelte +61 -61
- package/dist/SvSlider.svelte +203 -203
- package/dist/SvSwitchButton.svelte +108 -108
- package/dist/SvTagsInput.svelte +115 -115
- package/dist/SvTextArea.svelte +5 -1
- package/dist/SvTextInput.svelte +147 -147
- package/dist/SvTimePicker.svelte +245 -245
- package/dist/SvToaster.svelte +159 -159
- package/dist/SvToggleButton.svelte +85 -85
- package/dist/SvTooltip.svelte +161 -161
- package/dist/SvTour.svelte +208 -208
- package/dist/SvTree.svelte +444 -444
- package/dist/SvTreeSelect.svelte +239 -235
- package/dist/SvTreeSelect.svelte.d.ts +1 -1
- package/dist/builtin-editors.d.ts +2 -2
- package/dist/builtin-editors.js +19 -3
- package/dist/cdn/GridMenus-C4oLsJyj.js +600 -0
- package/dist/cdn/GridMenus-yEivjNwl.js +601 -0
- package/dist/cdn/SvDateRangeInput-Bh0A0JkF.js +199 -0
- package/dist/cdn/SvDateRangeInput-DflbiP7N.js +198 -0
- package/dist/cdn/SvDateTimePicker-BWwpfB_o.js +812 -0
- package/dist/cdn/SvDateTimePicker-Bivn8dAP.js +816 -0
- package/dist/cdn/SvGridCellEditor-AXL8cHGO.js +550 -0
- package/dist/cdn/SvGridCellEditor-Ba7rY3eu.js +549 -0
- package/dist/cdn/{SvGridChart-BwVos976.js → SvGridChart-Bs2GIR2Q.js} +1 -1
- package/dist/cdn/{SvGridChart-BEJmNNx9.js → SvGridChart-CwhFz7GV.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-BOknOkrP.js → SvGridChartPanel-1WXuStzM.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-D2PjII4O.js → SvGridChartPanel-e5_nqNP9.js} +1 -1
- package/dist/cdn/{SvGridChartView-00LPUHL1.js → SvGridChartView-SmPW10dI.js} +1 -1
- package/dist/cdn/{SvGridChartView-BhUEJ5ki.js → SvGridChartView-eSPuJE6g.js} +1 -1
- package/dist/cdn/{SvGridDropdown-D0VdjeR8.js → SvGridDropdown-B1ZcLpgs.js} +1 -1
- package/dist/cdn/{SvGridDropdown-D13MtJ2j.js → SvGridDropdown-CtdbHcIS.js} +1 -1
- package/dist/cdn/column-resize-DsfNXMom.js +102 -0
- package/dist/cdn/date-format-CcP1tafP.js +1395 -0
- package/dist/cdn/date-format-DZT7T1wf.js +1393 -0
- package/dist/cdn/{editor-registry-CiI0xlKg.js → editor-registry-BhgE3S84.js} +51 -25
- package/dist/cdn/row-resize-BRcimkUT.js +95 -0
- package/dist/cdn/{src-BYq-qyrp.js → src-COC0vnpg.js} +7846 -7317
- package/dist/cdn/{src-DBel9wRZ.js → src-pYGV2rMR.js} +10033 -9504
- package/dist/cdn/svgrid.js +15 -11
- package/dist/cdn/svgrid.svelte-external.js +15 -11
- package/dist/cell-values.d.ts +16 -0
- package/dist/cell-values.js +28 -0
- package/dist/chart-export.js +8 -8
- package/dist/clipboard.d.ts +22 -0
- package/dist/clipboard.js +405 -27
- 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 +19 -4
- package/dist/core.js +460 -119
- package/dist/editing.js +12 -7
- package/dist/editors/cell-editors.d.ts +11 -0
- package/dist/editors/cell-editors.js +18 -1
- package/dist/filtering/excel-filters.js +28 -0
- package/dist/grid-messages.d.ts +15 -0
- package/dist/grid-messages.js +15 -0
- package/dist/group-display.d.ts +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +10 -0
- package/dist/list-option.d.ts +6 -0
- package/dist/menus.js +41 -8
- package/dist/row-resize.d.ts +11 -0
- package/dist/row-resize.js +7 -1
- package/dist/sanitize-html.d.ts +37 -0
- package/dist/sanitize-html.js +234 -0
- package/dist/selection-bar-view.svelte.d.ts +25 -0
- package/dist/selection-bar-view.svelte.js +13 -0
- package/dist/selection.d.ts +2 -1
- package/dist/selection.js +133 -8
- package/dist/spreadsheet.d.ts +1 -1
- package/dist/spreadsheet.js +1 -1
- package/package.json +11 -11
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +181 -181
- package/src/GridMenus.svelte +201 -18
- package/src/SvAutoComplete.svelte +169 -165
- package/src/SvAvatar.svelte +75 -75
- package/src/SvCalendar.svelte +503 -503
- package/src/SvCalendar.test.ts +226 -226
- package/src/SvCarousel.svelte +141 -141
- package/src/SvCheckBox.svelte +102 -102
- package/src/SvCircularProgress.svelte +109 -109
- package/src/SvColorInput.svelte +181 -181
- package/src/SvComboBox.svelte +279 -275
- package/src/SvContextMenu.svelte +116 -116
- package/src/SvCountryInput.svelte +163 -163
- package/src/SvDateRangeInput.svelte +25 -3
- package/src/SvDrawer.svelte +254 -254
- package/src/SvDropDownList.svelte +378 -374
- package/src/SvDurationInput.svelte +126 -126
- package/src/SvField.svelte +293 -293
- package/src/SvForm.svelte +437 -437
- package/src/SvForm.test.ts +411 -411
- package/src/SvGrid.controller.svelte.ts +404 -133
- package/src/SvGrid.css +222 -1
- package/src/SvGrid.helpers.ts +87 -0
- package/src/SvGrid.svelte +255 -56
- package/src/SvGrid.types.ts +2092 -1940
- package/src/SvGridCellEditor.svelte +744 -662
- package/src/SvGridChart.svelte +1716 -1716
- package/src/SvGridChartPanel.svelte +485 -485
- package/src/SvGridChartView.svelte +70 -70
- package/src/SvGridDropdown.svelte +728 -728
- package/src/SvGridSelect.svelte +270 -266
- package/src/SvGroupCell.svelte +116 -116
- package/src/SvListBox.svelte +334 -326
- package/src/SvMaskedInput.svelte +122 -122
- package/src/SvMenu.svelte +124 -124
- package/src/SvMenu.test.ts +97 -97
- package/src/SvMenuList.svelte +146 -146
- package/src/SvMultiSelect.svelte +293 -289
- package/src/SvNumberInput.svelte +172 -168
- package/src/SvOtpInput.svelte +158 -158
- package/src/SvPasswordInput.svelte +151 -151
- package/src/SvPhoneInput.svelte +133 -133
- package/src/SvPopover.svelte +197 -197
- package/src/SvProgress.svelte +116 -116
- package/src/SvRadioGroup.svelte +107 -107
- package/src/SvRating.svelte +112 -112
- package/src/SvResult.svelte +73 -73
- package/src/SvRichCell.svelte +125 -0
- package/src/SvRichText.svelte +211 -211
- package/src/SvRowGroupPanel.svelte +170 -170
- package/src/SvScrollArea.svelte +61 -61
- package/src/SvSlider.svelte +203 -203
- package/src/SvSwitchButton.svelte +108 -108
- package/src/SvTagsInput.svelte +115 -115
- package/src/SvTextArea.svelte +5 -1
- package/src/SvTextInput.svelte +147 -147
- package/src/SvTimePicker.svelte +245 -245
- package/src/SvToaster.svelte +159 -159
- package/src/SvToaster.test.ts +95 -95
- package/src/SvToggleButton.svelte +85 -85
- package/src/SvTooltip.svelte +161 -161
- package/src/SvTour.svelte +208 -208
- package/src/SvTree.svelte +444 -444
- package/src/SvTreeSelect.svelte +239 -235
- package/src/a11y/dismissable.test.ts +119 -119
- package/src/a11y/dismissable.ts +114 -114
- package/src/a11y.contract.test.ts +49 -49
- package/src/a11y.test.ts +59 -59
- package/src/a11y.ts +61 -61
- package/src/ai.test.ts +502 -502
- package/src/ai.ts +1419 -1419
- package/src/build-api.coverage.test.ts +633 -633
- package/src/build-api.ts +846 -846
- package/src/builtin-editors.grid.test.ts +83 -83
- package/src/builtin-editors.test.ts +18 -1
- package/src/builtin-editors.ts +19 -3
- package/src/cell-formatting.ts +171 -171
- package/src/cell-render.test.ts +513 -513
- package/src/cell-render.ts +496 -496
- package/src/cell-values.ts +148 -118
- package/src/chart-export.ts +202 -202
- package/src/chart-view.svelte.ts +36 -36
- package/src/chart.ts +2321 -2321
- package/src/clipboard.test.ts +93 -20
- package/src/clipboard.ts +433 -33
- package/src/collaboration.test.ts +104 -104
- package/src/collaboration.ts +167 -167
- package/src/column-groups.ts +78 -78
- 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.performance.test.ts +30 -30
- 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 +1865 -1468
- package/src/createAutocomplete.svelte.ts +132 -132
- package/src/createCombobox.svelte.ts +191 -191
- package/src/createCountryInput.svelte.ts +157 -157
- package/src/createDropdownList.svelte.ts +168 -168
- package/src/createForm.svelte.ts +386 -386
- package/src/createGrid.svelte.ts +42 -42
- package/src/createGrid.test.ts +10 -10
- package/src/createGridState.svelte.ts +17 -17
- package/src/createListbox.svelte.ts +250 -250
- package/src/createMenu.svelte.ts +224 -224
- package/src/createPopoverSelect.svelte.ts +213 -213
- package/src/createSlider.svelte.ts +191 -191
- package/src/createTooltip.svelte.ts +144 -144
- package/src/createTree.svelte.ts +322 -322
- package/src/date-string-column.test.ts +87 -0
- package/src/datetime/date-core.ts +206 -206
- package/src/datetime/date-restrict.ts +61 -61
- package/src/datetime/timezone.ts +135 -135
- package/src/dock-manager-model.ts +596 -596
- package/src/dock-model.ts +374 -374
- package/src/editing.test.ts +974 -949
- package/src/editing.ts +609 -606
- package/src/editor-block-prop.test.ts +132 -0
- package/src/editor-contract.ts +171 -171
- package/src/editor-registry.grid.test.ts +144 -144
- package/src/editor-registry.ts +122 -122
- package/src/editors/cell-editors.ts +27 -1
- package/src/export-data-api.test.ts +126 -126
- package/src/export-format.test.ts +107 -107
- package/src/export-format.ts +601 -601
- package/src/filter-operators.ts +160 -160
- package/src/filtering/excel-filters.ts +325 -295
- package/src/filtering/normalize-fast-path.test.ts +104 -0
- package/src/flex-render.ts +3 -3
- package/src/form-field.ts +127 -127
- package/src/grid-messages.ts +34 -0
- package/src/group-display.test.ts +167 -167
- package/src/group-display.ts +200 -200
- package/src/headless.ts +87 -87
- package/src/index.ts +25 -1
- package/src/js-scroller.svelte.ts +173 -173
- package/src/keyboard-handlers.ts +270 -270
- package/src/keyboard.test.ts +59 -59
- package/src/keyboard.ts +97 -97
- package/src/list-nav.test.ts +49 -49
- package/src/list-nav.ts +29 -29
- package/src/list-option.test.ts +56 -56
- package/src/list-option.ts +179 -173
- package/src/menus.test.ts +43 -0
- package/src/menus.ts +597 -567
- package/src/merge-objects.ts +48 -48
- package/src/move-cells.test.ts +850 -0
- package/src/number-editor-literal.test.ts +84 -0
- package/src/number-editor.grid.test.ts +160 -0
- package/src/overlays.test.ts +90 -90
- package/src/positioning.ts +268 -268
- package/src/render-component.ts +28 -28
- package/src/resize-props.test.ts +361 -0
- package/src/row-drag-touch.ts +211 -211
- package/src/row-drag.test.ts +353 -353
- package/src/row-drag.ts +415 -415
- package/src/row-resize.test.ts +524 -493
- package/src/row-resize.ts +228 -210
- package/src/sanitize-html.test.ts +182 -0
- package/src/sanitize-html.ts +235 -0
- package/src/scheduler-ical.ts +181 -181
- package/src/scheduler-model.test.ts +562 -562
- package/src/scheduler-model.ts +873 -873
- package/src/selection-bar-view.svelte.ts +36 -0
- package/src/selection.test.ts +900 -743
- package/src/selection.ts +135 -8
- package/src/server-data-source.test.ts +383 -383
- package/src/server-data-source.ts +469 -469
- package/src/sparkline.test.ts +68 -68
- package/src/sparkline.ts +169 -169
- package/src/spreadsheet.test.ts +488 -488
- package/src/spreadsheet.ts +312 -312
- package/src/static-functions.ts +11 -11
- package/src/subscribe.ts +38 -38
- package/src/summaries.ts +113 -113
- package/src/svgrid-wrapper.types.ts +563 -563
- package/src/svgrid.async-editor-options.test.ts +273 -273
- package/src/svgrid.auto-row-height.test.ts +204 -204
- package/src/svgrid.behavior.test.ts +910 -910
- package/src/svgrid.charting.test.ts +534 -534
- package/src/svgrid.comments-autocomplete.test.ts +127 -127
- package/src/svgrid.context-menu.test.ts +147 -147
- package/src/svgrid.features.test.ts +157 -157
- package/src/svgrid.filter-depth.test.ts +163 -163
- package/src/svgrid.filter-menu-listbox.svelte.test.ts +377 -331
- package/src/svgrid.filter-menu-scroll.test.ts +112 -112
- package/src/svgrid.grand-total.test.ts +188 -188
- package/src/svgrid.group-display-mode.test.ts +171 -171
- package/src/svgrid.group-footers.test.ts +121 -121
- package/src/svgrid.group-pagination.test.ts +153 -153
- package/src/svgrid.in-suggest-snapshot.svelte.test.ts +91 -0
- package/src/svgrid.menu-scroll-close.test.ts +204 -0
- package/src/svgrid.new-features.wrapper.test.ts +251 -251
- package/src/svgrid.selection-bar-seam.test.ts +185 -0
- package/src/svgrid.tree-data.test.ts +186 -186
- package/src/svgrid.upsell-license.test.ts +117 -0
- package/src/svgrid.wrapper.test.ts +63 -63
- package/src/svgriddropdown.async-panel.svelte.test.ts +195 -195
- package/src/test-setup.ts +62 -62
- package/src/themes/index.ts +288 -288
- package/src/toast-store.svelte.ts +250 -250
- package/src/toast-store.test.ts +147 -147
- package/src/tree-row-model.test.ts +168 -168
- package/src/ui-buttons.test.ts +144 -144
- package/src/ui-inputs.test.ts +118 -118
- package/src/ui-localization.test.ts +113 -113
- package/src/ui-range.test.ts +70 -70
- package/src/ui-selection.test.ts +155 -155
- package/src/ui-tier1.test.ts +142 -142
- package/src/virtual.test.ts +88 -88
- 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 +322 -322
- package/dist/cdn/GridMenus-BfTAKn84.js +0 -488
- package/dist/cdn/GridMenus-C3bJd7w8.js +0 -489
- package/dist/cdn/SvDateTimePicker-CHnUkWcH.js +0 -2206
- package/dist/cdn/SvDateTimePicker-CRQH_U7E.js +0 -2201
- package/dist/cdn/SvGridCellEditor-BGUCzuPB.js +0 -523
- package/dist/cdn/SvGridCellEditor-G8IMWIws.js +0 -522
|
@@ -225,6 +225,19 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
225
225
|
* preview" overlay on cells between the source range and the pointer
|
|
226
226
|
* cell; on pointerup we extrapolate the source pattern into them. */
|
|
227
227
|
let fillDrag = $state(null);
|
|
228
|
+
/** An in-flight Excel-style range MOVE (drag the selection's border to
|
|
229
|
+
* relocate its values). Same source rectangle as `fillDrag`, plus where
|
|
230
|
+
* inside that rectangle the pointer grabbed it - the drop offset is
|
|
231
|
+
* `target - grab`, which is what lets a drag started on the range's LEFT
|
|
232
|
+
* edge land the range where the pointer is rather than jumping it.
|
|
233
|
+
* `copy` mirrors the Ctrl / Cmd key and is re-read on every move, so the
|
|
234
|
+
* user can change their mind mid-drag exactly as in a spreadsheet. */
|
|
235
|
+
let moveDrag = $state(null);
|
|
236
|
+
/** True while the pointer hovers the grab strip on a selection border, so
|
|
237
|
+
* the root can switch to a move cursor. Kept as its own flag rather than
|
|
238
|
+
* recomputed per cell: it flips at most twice per border crossing and only
|
|
239
|
+
* one attribute on one element reads it. */
|
|
240
|
+
let moveGrabHover = $state(false);
|
|
228
241
|
let activeAtPointerDown = null;
|
|
229
242
|
let editingCell = $state(null);
|
|
230
243
|
// Full-row editing: the row currently in whole-row edit + its per-column
|
|
@@ -275,9 +288,6 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
275
288
|
let editorSelectAll = true;
|
|
276
289
|
/** Per-column width overrides set by the resize handles. */
|
|
277
290
|
let columnWidths = $state({});
|
|
278
|
-
let resizingColumnId = $state(null);
|
|
279
|
-
let resizeStartX = 0;
|
|
280
|
-
let resizeStartWidth = 0;
|
|
281
291
|
const MIN_COLUMN_WIDTH = 40;
|
|
282
292
|
/** Columns pinned to the left or right edge of the grid (sticky positioning).
|
|
283
293
|
* Seeded from `props.initialColumnPinning` so demos / tests can show the
|
|
@@ -294,7 +304,26 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
294
304
|
let dataStateVersion = $state(0);
|
|
295
305
|
const selectionColumnWidth = 44;
|
|
296
306
|
const rowNumberColumnWidth = $derived(props.rowNumberWidth ?? 56);
|
|
297
|
-
|
|
307
|
+
// A per-row `rowHeight` function already supplies heights, so auto-measuring
|
|
308
|
+
// would fight it. Fixed numbers are fine - they become the pre-measure estimate.
|
|
309
|
+
const autoRowHeightOn = $derived(props.autoRowHeight === true && typeof props.rowHeight !== "function");
|
|
310
|
+
// Auto height measures the row from its content, so a dragged height would be
|
|
311
|
+
// overwritten on the next measurement pass. Let auto height win rather than
|
|
312
|
+
// having the two fight.
|
|
313
|
+
const rowResizeOn = $derived(props.rowResize === true && !autoRowHeightOn);
|
|
314
|
+
/**
|
|
315
|
+
* The row-number gutter. Explicit `showRowNumbers` always wins; otherwise
|
|
316
|
+
* `rowResize` brings it in, because a resizable row needs a row header to
|
|
317
|
+
* grab - that is where the drag strip lives, and it is where a spreadsheet
|
|
318
|
+
* puts it. Dragging the edge of a data cell instead works but reads as an
|
|
319
|
+
* accident.
|
|
320
|
+
*
|
|
321
|
+
* Both derived above are declared here rather than beside the rest of the
|
|
322
|
+
* row-resize state further down: a `$derived` referenced before its
|
|
323
|
+
* declaration in a .svelte.ts silently compiles to an empty getter, so this
|
|
324
|
+
* gate would have read `undefined` and the gutter would never appear.
|
|
325
|
+
*/
|
|
326
|
+
const showRowNumbersEffective = $derived(props.showRowNumbers ?? rowResizeOn);
|
|
298
327
|
let columnMenuFor = $state(null);
|
|
299
328
|
let columnMenuTab = $state("general");
|
|
300
329
|
let columnMenuPos = $state({ x: 0, y: 0 });
|
|
@@ -318,11 +347,11 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
318
347
|
let commentEditFor = $state(null);
|
|
319
348
|
let commentDraft = $state("");
|
|
320
349
|
let valueFilters = $state({});
|
|
321
|
-
const viewportWidth = $derived.by(()
|
|
350
|
+
const viewportWidth = $derived.by(function viewportWidth_d() {
|
|
322
351
|
viewportVersion;
|
|
323
352
|
return scrollContainer ? scrollContainer.clientWidth : 0;
|
|
324
353
|
});
|
|
325
|
-
const viewportHeight = $derived.by(()
|
|
354
|
+
const viewportHeight = $derived.by(function viewportHeight_d() {
|
|
326
355
|
viewportVersion;
|
|
327
356
|
return scrollContainer ? scrollContainer.clientHeight : 0;
|
|
328
357
|
});
|
|
@@ -344,7 +373,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
344
373
|
const hb = column.columnDef?.hideBelow;
|
|
345
374
|
return hb != null && viewportWidth > 0 && viewportWidth < hb;
|
|
346
375
|
}
|
|
347
|
-
const scrollMetrics = $derived.by(()
|
|
376
|
+
const scrollMetrics = $derived.by(function scrollMetrics_d() {
|
|
348
377
|
scrollVersion;
|
|
349
378
|
viewportVersion;
|
|
350
379
|
// Track the virtualizers' versions too so when data loads or row /
|
|
@@ -378,7 +407,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
378
407
|
* - virtualizer.getTotalSize(): correct at initial load (before first paint)
|
|
379
408
|
* - scrollMetrics.scrollHeight: correct after detail rows expand (DOM is live,
|
|
380
409
|
* ResizeObserver on gridRootEl already bumps scrollVersion at that point) */
|
|
381
|
-
const hasVerticalOverflow = $derived.by(()
|
|
410
|
+
const hasVerticalOverflow = $derived.by(function hasVerticalOverflow_d() {
|
|
382
411
|
virtualizer.version;
|
|
383
412
|
const virtualizerSize = virtualizer.getTotalSize();
|
|
384
413
|
// scrollMetrics.scrollHeight is 0 before initial paint; once the table
|
|
@@ -408,6 +437,11 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
408
437
|
props.enableCellSelection ??
|
|
409
438
|
((props.selectionMode ?? "both") === "cell" ||
|
|
410
439
|
(props.selectionMode ?? "both") === "both"));
|
|
440
|
+
// Range drag-and-drop rides on cell selection: there is no range to grab
|
|
441
|
+
// without it, so it can never be on alone. Default ON, matching the fill
|
|
442
|
+
// handle - both are spreadsheet affordances users arrive expecting, and
|
|
443
|
+
// neither is reachable until something is actually selected.
|
|
444
|
+
const moveCellsEffective = $derived((props.moveCells ?? true) && enableCellSelectionEffective);
|
|
411
445
|
// The aggregate footer row. `summary` is the shortcut alias, so it wins over
|
|
412
446
|
// the fine-grained prop - the same precedence `selectable` uses.
|
|
413
447
|
//
|
|
@@ -636,7 +670,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
636
670
|
// Declared here rather than beside the other pagination/state derivations:
|
|
637
671
|
// the auto-group column pipeline below reads it, and a `const` used above its
|
|
638
672
|
// declaration is a type error even though `$derived` is lazy enough at runtime.
|
|
639
|
-
const groupingColumns = $derived.by(()
|
|
673
|
+
const groupingColumns = $derived.by(function groupingColumns_d() {
|
|
640
674
|
gridStateVersion;
|
|
641
675
|
return grid.getState().grouping ?? [];
|
|
642
676
|
});
|
|
@@ -657,7 +691,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
657
691
|
* any other column. They carry no `field` - their cell content is resolved
|
|
658
692
|
* from the row's group state at render time.
|
|
659
693
|
*/
|
|
660
|
-
const autoGroupColumns = $derived.by(()
|
|
694
|
+
const autoGroupColumns = $derived.by(function autoGroupColumns_d() {
|
|
661
695
|
if (!groupColumnMode)
|
|
662
696
|
return [];
|
|
663
697
|
const sourceById = new Map(grid.getAllColumns().map((c) => [c.id, c]));
|
|
@@ -723,7 +757,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
723
757
|
depth: columnId === "__autoGroup" ? depth : 0,
|
|
724
758
|
};
|
|
725
759
|
}
|
|
726
|
-
const allColumns = $derived.by(()
|
|
760
|
+
const allColumns = $derived.by(function allColumns_d() {
|
|
727
761
|
let raw = grid
|
|
728
762
|
.getAllColumns()
|
|
729
763
|
.filter((column) => !hiddenColumns[column.id] &&
|
|
@@ -771,7 +805,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
771
805
|
return [...left, ...unpinned, ...right];
|
|
772
806
|
});
|
|
773
807
|
/** Header groups reordered to match {@link allColumns}. */
|
|
774
|
-
const headerGroups = $derived.by(()
|
|
808
|
+
const headerGroups = $derived.by(function headerGroups_d() {
|
|
775
809
|
const base = grid.getHeaderGroups();
|
|
776
810
|
if (!base.length)
|
|
777
811
|
return base;
|
|
@@ -797,7 +831,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
797
831
|
}
|
|
798
832
|
return [{ id: base[0].id, headers }];
|
|
799
833
|
});
|
|
800
|
-
const groupHeaderRows = $derived.by(()
|
|
834
|
+
const groupHeaderRows = $derived.by(function groupHeaderRows_d() {
|
|
801
835
|
const userCols = props.columns ?? [];
|
|
802
836
|
// 1. Find max depth in the user-provided column tree.
|
|
803
837
|
function maxDepth(defs) {
|
|
@@ -905,7 +939,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
905
939
|
return rows;
|
|
906
940
|
});
|
|
907
941
|
/** Cumulative pixel offsets for left- and right-pinned columns. */
|
|
908
|
-
const pinnedOffsets = $derived.by(()
|
|
942
|
+
const pinnedOffsets = $derived.by(function pinnedOffsets_d() {
|
|
909
943
|
const rowNumberWidth = showRowNumbersEffective ? rowNumberColumnWidth : 0;
|
|
910
944
|
const selectionWidth = showRowSelectionEffective ? selectionColumnWidth : 0;
|
|
911
945
|
const left = {};
|
|
@@ -950,7 +984,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
950
984
|
const hasConditionalFormats = $derived((props.conditionalFormats?.length ?? 0) > 0);
|
|
951
985
|
// Per-column numeric min/max, needed only by colorScale / dataBar formats.
|
|
952
986
|
// Lazy: this derived never runs unless `conditionalFormats` is set.
|
|
953
|
-
const conditionalColumnStats = $derived.by(()
|
|
987
|
+
const conditionalColumnStats = $derived.by(function conditionalColumnStats_d() {
|
|
954
988
|
const map = new Map();
|
|
955
989
|
const formats = props.conditionalFormats;
|
|
956
990
|
if (!formats?.length || !formatsNeedingStats(formats))
|
|
@@ -990,14 +1024,14 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
990
1024
|
}
|
|
991
1025
|
return map;
|
|
992
1026
|
});
|
|
993
|
-
const sortDirectionByColumn = $derived.by(()
|
|
1027
|
+
const sortDirectionByColumn = $derived.by(function sortDirectionByColumn_d() {
|
|
994
1028
|
gridStateVersion;
|
|
995
1029
|
const directions = {};
|
|
996
1030
|
for (const column of allColumns)
|
|
997
1031
|
directions[column.id] = column.getIsSorted();
|
|
998
1032
|
return directions;
|
|
999
1033
|
});
|
|
1000
|
-
const paginationState = $derived.by(()
|
|
1034
|
+
const paginationState = $derived.by(function paginationState_d() {
|
|
1001
1035
|
gridStateVersion;
|
|
1002
1036
|
return grid.getState().pagination ?? { pageIndex: 0, pageSize: 10 };
|
|
1003
1037
|
});
|
|
@@ -1006,30 +1040,38 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1006
1040
|
* compute the correct "X to Y of Z" range and total page count when
|
|
1007
1041
|
* filters reduce the dataset.
|
|
1008
1042
|
*/
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1043
|
+
/**
|
|
1044
|
+
* The per-column filter stages: the menu's (up to two) conditions, then the
|
|
1045
|
+
* set-filter checklists. Split out of `allRowsBeforePagination` so the facet
|
|
1046
|
+
* list can run the SAME pipeline with its own column left out - a checklist
|
|
1047
|
+
* that offers values excluded by the other columns' filters sends you to an
|
|
1048
|
+
* empty grid. Re-implementing the stages beside the original would have let
|
|
1049
|
+
* the two drift.
|
|
1050
|
+
*
|
|
1051
|
+
* `excludeColumnId` omits that column's own filters, which is what "every
|
|
1052
|
+
* other filter" means for the column whose menu is open.
|
|
1053
|
+
*/
|
|
1054
|
+
function applyColumnFilters(inputRows, excludeColumnId) {
|
|
1055
|
+
let rows = inputRows;
|
|
1056
|
+
/**
|
|
1057
|
+
* Resolve a column ONCE and return a reader for it.
|
|
1058
|
+
*
|
|
1059
|
+
* `getRowColumnValue` finds the column with a linear scan over every
|
|
1060
|
+
* column, which is fine for a one-off read and wrong inside a loop over
|
|
1061
|
+
* 100,000 rows - it made each filtered column cost O(rows x columns).
|
|
1062
|
+
* The id is fixed for the life of a filter, so the scan is hoisted here and
|
|
1063
|
+
* the row loop gets a closure that only reads.
|
|
1064
|
+
*
|
|
1065
|
+
* Falls back to the row's own accessor when the id names no column, which
|
|
1066
|
+
* is also what `getRowColumnValue` does - group rows override
|
|
1067
|
+
* `getCellValueByColumnId` to return their aggregate.
|
|
1068
|
+
*/
|
|
1069
|
+
const makeColumnReader = (columnId) => {
|
|
1070
|
+
const column = allColumns.find((entry) => entry.id === columnId);
|
|
1071
|
+
return column
|
|
1072
|
+
? (row) => getColumnBaseValue(row, column)
|
|
1073
|
+
: (row) => row.getCellValueByColumnId(columnId);
|
|
1074
|
+
};
|
|
1033
1075
|
// A single condition is "active" if it has the value(s) it needs.
|
|
1034
1076
|
const condActive = (op, value, valueTo) => {
|
|
1035
1077
|
if (op === "isBlank" || op === "isNotBlank")
|
|
@@ -1044,7 +1086,9 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1044
1086
|
// redundant passes.
|
|
1045
1087
|
const compileCond = (columnId, op, value, valueTo) => compileExcelFilter({ id: columnId, operator: op, value, valueTo: op === "between" ? valueTo : undefined }, { locale: (props.filterLocale ?? props.localization?.locale) });
|
|
1046
1088
|
// A column filter is active if either of its (up to two) conditions is.
|
|
1047
|
-
const menuFilters = Object.entries(filterMenuValues).filter(([
|
|
1089
|
+
const menuFilters = Object.entries(filterMenuValues).filter(([id, f]) => {
|
|
1090
|
+
if (id === excludeColumnId)
|
|
1091
|
+
return false;
|
|
1048
1092
|
const a = condActive(f.operator, f.value, f.valueTo);
|
|
1049
1093
|
const b = !!f.operator2 && condActive(f.operator2, f.value2 ?? "", f.valueTo2);
|
|
1050
1094
|
return a || b;
|
|
@@ -1054,6 +1098,11 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1054
1098
|
// become compiled predicates before a single row is tested.
|
|
1055
1099
|
const compiledMenuFilters = menuFilters.map(([columnId, f]) => ({
|
|
1056
1100
|
columnId,
|
|
1101
|
+
// The column, resolved once. `getRowColumnValue` looks it up with a
|
|
1102
|
+
// linear scan of every column, and the id is fixed per filter, so
|
|
1103
|
+
// leaving that inside the row loop made it O(rows x columns) per
|
|
1104
|
+
// filtered column for no reason.
|
|
1105
|
+
readValue: makeColumnReader(columnId),
|
|
1057
1106
|
join: f.join,
|
|
1058
1107
|
a: condActive(f.operator, f.value, f.valueTo)
|
|
1059
1108
|
? compileCond(columnId, f.operator, f.value, f.valueTo)
|
|
@@ -1062,8 +1111,8 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1062
1111
|
? compileCond(columnId, f.operator2, f.value2 ?? "", f.valueTo2)
|
|
1063
1112
|
: null,
|
|
1064
1113
|
}));
|
|
1065
|
-
rows = rows.filter((row) => compiledMenuFilters.every(({
|
|
1066
|
-
const cellValue =
|
|
1114
|
+
rows = rows.filter((row) => compiledMenuFilters.every(({ readValue, join, a, b }) => {
|
|
1115
|
+
const cellValue = readValue(row);
|
|
1067
1116
|
const ra = a ? a(cellValue) : null;
|
|
1068
1117
|
const rb = b ? b(cellValue) : null;
|
|
1069
1118
|
if (ra === null)
|
|
@@ -1073,7 +1122,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1073
1122
|
return join === "OR" ? ra || rb : ra && rb;
|
|
1074
1123
|
}));
|
|
1075
1124
|
}
|
|
1076
|
-
const valueFilterEntries = Object.entries(valueFilters);
|
|
1125
|
+
const valueFilterEntries = Object.entries(valueFilters).filter(([id]) => id !== excludeColumnId);
|
|
1077
1126
|
if (valueFilterEntries.length) {
|
|
1078
1127
|
// Resolve bucket defs up front so we don't re-hit the derived map
|
|
1079
1128
|
// for every row × column combination. Columns without bucketing
|
|
@@ -1081,10 +1130,11 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1081
1130
|
const bucketEntries = valueFilterEntries.map(([columnId, allowed]) => ({
|
|
1082
1131
|
columnId,
|
|
1083
1132
|
allowed,
|
|
1133
|
+
readValue: makeColumnReader(columnId),
|
|
1084
1134
|
buckets: facetBucketsByColumn.get(columnId) ?? null,
|
|
1085
1135
|
}));
|
|
1086
|
-
rows = rows.filter((row) => bucketEntries.every(({
|
|
1087
|
-
const raw =
|
|
1136
|
+
rows = rows.filter((row) => bucketEntries.every(({ allowed, buckets, readValue }) => {
|
|
1137
|
+
const raw = readValue(row);
|
|
1088
1138
|
if (buckets) {
|
|
1089
1139
|
// Range-bucketed filter: find which bucket this row's value
|
|
1090
1140
|
// falls into and check whether that bucket's label is allowed.
|
|
@@ -1101,6 +1151,33 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1101
1151
|
return allowed.has(String(raw ?? ""));
|
|
1102
1152
|
}));
|
|
1103
1153
|
}
|
|
1154
|
+
return rows;
|
|
1155
|
+
}
|
|
1156
|
+
const allRowsBeforePagination = $derived.by(function allRowsBeforePagination_d() {
|
|
1157
|
+
// Depend on dataStateVersion (row-model-affecting store changes) NOT
|
|
1158
|
+
// gridStateVersion - so moving the active cell / selection does not force
|
|
1159
|
+
// this O(rows) pipeline to re-run. Filter-input state (globalFilter etc.)
|
|
1160
|
+
// and internalData are read below and tracked as their own dependencies.
|
|
1161
|
+
dataStateVersion;
|
|
1162
|
+
// Touch internalData + internalColumns so the row model re-derives when
|
|
1163
|
+
// the consumer replaces the data array (e.g. via a "Reset" button).
|
|
1164
|
+
void internalData;
|
|
1165
|
+
void internalColumns;
|
|
1166
|
+
const rawRows = grid.getRowModel().rows;
|
|
1167
|
+
// External-filter mode: the consumer fetched / pre-filtered the rows
|
|
1168
|
+
// themselves (server-side data sources). Skip every local filter pass
|
|
1169
|
+
// so the data isn't double-filtered against the visible page.
|
|
1170
|
+
if (externalFilterEnabled)
|
|
1171
|
+
return rawRows;
|
|
1172
|
+
let rows = rawRows;
|
|
1173
|
+
if (globalFilter.trim()) {
|
|
1174
|
+
const needle = normalizeForFilter(globalFilter, (props.filterLocale ?? props.localization?.locale));
|
|
1175
|
+
rows = rows.filter((row) => row
|
|
1176
|
+
.getAllCells()
|
|
1177
|
+
.some((cell) => normalizeForFilter(String(cell.getValue() ?? ""), (props.filterLocale ?? props.localization?.locale))
|
|
1178
|
+
.includes(needle)));
|
|
1179
|
+
}
|
|
1180
|
+
rows = applyColumnFilters(rows);
|
|
1104
1181
|
// --- Advanced filter (Pro) ---------------------------------------------
|
|
1105
1182
|
// Runs LAST for two reasons: it evaluates over the smallest row set, and
|
|
1106
1183
|
// its aggregates (`SUM(amount) > 1000`) fold over what the user is
|
|
@@ -1142,7 +1219,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1142
1219
|
* is active we page by DATA rows and reprint each page's ancestor banners.
|
|
1143
1220
|
* Null when grouping is off, so the flat path stays a cheap slice.
|
|
1144
1221
|
*/
|
|
1145
|
-
const groupedPage = $derived.by(()
|
|
1222
|
+
const groupedPage = $derived.by(function groupedPage_d() {
|
|
1146
1223
|
if (!paginationEnabled || externalPaginationEnabled)
|
|
1147
1224
|
return null;
|
|
1148
1225
|
if (!groupingColumns.length)
|
|
@@ -1211,7 +1288,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1211
1288
|
* the full dataset rather than the current page (see the comment above
|
|
1212
1289
|
* `_rowModels`).
|
|
1213
1290
|
*/
|
|
1214
|
-
const allRows = $derived.by(()
|
|
1291
|
+
const allRows = $derived.by(function allRows_d() {
|
|
1215
1292
|
const paged = (() => {
|
|
1216
1293
|
const rows = allRowsBeforePagination;
|
|
1217
1294
|
// External pagination: `data` already IS the current page - never slice.
|
|
@@ -1278,10 +1355,49 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1278
1355
|
const paginationTotalRows = $derived(externalPaginationEnabled ? (props.rowCount ?? 0) : paginatedRowTotal);
|
|
1279
1356
|
const paginationPageIndex = $derived(externalPaginationEnabled ? (props.pageIndex ?? 0) : paginationState.pageIndex);
|
|
1280
1357
|
const paginationPageSize = $derived(externalPaginationEnabled ? (props.pageSize ?? 10) : paginationState.pageSize);
|
|
1281
|
-
const rowSelectionState = $derived.by(()
|
|
1358
|
+
const rowSelectionState = $derived.by(function rowSelectionState_d() {
|
|
1282
1359
|
gridStateVersion;
|
|
1283
1360
|
return grid.getState().rowSelection ?? {};
|
|
1284
1361
|
});
|
|
1362
|
+
// ---- Selection bar ----------------------------------------------------
|
|
1363
|
+
// The floating bulk-action bar. Off unless asked for: it overlaps a row, and
|
|
1364
|
+
// what belongs on it is app-specific. The RENDERER is Enterprise and arrives
|
|
1365
|
+
// through the selection-bar-view seam; everything here is free.
|
|
1366
|
+
const selectionBarOn = $derived(props.selectionBar !== undefined && props.selectionBar !== false);
|
|
1367
|
+
/** The prop in its three shapes - `true`, an action array, or a full config -
|
|
1368
|
+
* normalised to the config object the renderer reads. */
|
|
1369
|
+
const selectionBarConfig = $derived.by(function selectionBarConfig_d() {
|
|
1370
|
+
const raw = props.selectionBar;
|
|
1371
|
+
if (Array.isArray(raw))
|
|
1372
|
+
return { actions: raw };
|
|
1373
|
+
if (raw && typeof raw === "object")
|
|
1374
|
+
return raw;
|
|
1375
|
+
return {};
|
|
1376
|
+
});
|
|
1377
|
+
const selectionBarActions = $derived(selectionBarConfig.actions ?? []);
|
|
1378
|
+
const selectionBarPosition = $derived(selectionBarConfig.position === "top" ? "top" : "bottom");
|
|
1379
|
+
const selectionBarMaxVisible = $derived(typeof selectionBarConfig.maxVisible === "number" && selectionBarConfig.maxVisible > 0
|
|
1380
|
+
? selectionBarConfig.maxVisible
|
|
1381
|
+
: 6);
|
|
1382
|
+
const selectionBarHideClear = $derived(selectionBarConfig.hideClear === true);
|
|
1383
|
+
/** Selected rows in DISPLAY order, plus their ids - what every action on the
|
|
1384
|
+
* bar receives, and what its count shows. Built from `allRows` rather than
|
|
1385
|
+
* the selection map so the order matches what the user is looking at. */
|
|
1386
|
+
const selectionBarTarget = $derived.by(function selectionBarTarget_d() {
|
|
1387
|
+
const rows = [];
|
|
1388
|
+
const ids = [];
|
|
1389
|
+
if (!selectionBarOn)
|
|
1390
|
+
return { rows, ids };
|
|
1391
|
+
for (const row of allRows) {
|
|
1392
|
+
if (isGroupRow(row))
|
|
1393
|
+
continue;
|
|
1394
|
+
if (rowSelectionState[row.id]) {
|
|
1395
|
+
rows.push(row.original);
|
|
1396
|
+
ids.push(row.id);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
return { rows, ids };
|
|
1400
|
+
});
|
|
1285
1401
|
// Forward selection changes to the consumer. Skips the very first invocation
|
|
1286
1402
|
// (the initial empty state) so consumers don't get a spurious callback on mount.
|
|
1287
1403
|
let lastSelectionSerialized = "";
|
|
@@ -1331,7 +1447,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1331
1447
|
const statusBarAggregates = $derived(typeof props.statusBar === "object" && props.statusBar.aggregates
|
|
1332
1448
|
? props.statusBar.aggregates
|
|
1333
1449
|
: ["count", "sum", "avg", "min", "max"]);
|
|
1334
|
-
const statusBarStats = $derived.by(()
|
|
1450
|
+
const statusBarStats = $derived.by(function statusBarStats_d() {
|
|
1335
1451
|
if (!statusBarEnabled)
|
|
1336
1452
|
return null;
|
|
1337
1453
|
const a = selectionRange.anchor;
|
|
@@ -1390,7 +1506,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1390
1506
|
const toolPanelEnabled = $derived(props.toolPanel === true);
|
|
1391
1507
|
// Every column (including hidden ones) in the user's current order, so the
|
|
1392
1508
|
// panel can toggle/reorder anything. Group columns are flagged live.
|
|
1393
|
-
const toolPanelColumns = $derived.by(()
|
|
1509
|
+
const toolPanelColumns = $derived.by(function toolPanelColumns_d() {
|
|
1394
1510
|
gridStateVersion;
|
|
1395
1511
|
const all = grid.getAllColumns();
|
|
1396
1512
|
if (!userColumnOrder.length)
|
|
@@ -1428,7 +1544,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1428
1544
|
// reveals. `a11y/grid-announcements.ts` documents what is deliberately left
|
|
1429
1545
|
// unsaid, and why saying it would make the grid talk over itself.
|
|
1430
1546
|
/** Data rows before any local filtering - the denominator in "12 of 250". */
|
|
1431
|
-
const unfilteredRowTotal = $derived.by(()
|
|
1547
|
+
const unfilteredRowTotal = $derived.by(function unfilteredRowTotal_d() {
|
|
1432
1548
|
dataStateVersion;
|
|
1433
1549
|
void internalData;
|
|
1434
1550
|
return grid.getRowModel().rows.length;
|
|
@@ -1506,7 +1622,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1506
1622
|
props.onPivotModeChange?.(next);
|
|
1507
1623
|
}
|
|
1508
1624
|
const pivotActive = $derived(!!pivotConfig && pivotModeOn && hasPivotEngine());
|
|
1509
|
-
const pivotResult = $derived.by(()
|
|
1625
|
+
const pivotResult = $derived.by(function pivotResult_d() {
|
|
1510
1626
|
if (!pivotActive || !pivotConfig)
|
|
1511
1627
|
return null;
|
|
1512
1628
|
const engine = getPivotEngine();
|
|
@@ -1584,7 +1700,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1584
1700
|
const h = col.columnDef.header;
|
|
1585
1701
|
return typeof h === "string" && h ? h : (col.columnDef.field ?? col.id);
|
|
1586
1702
|
};
|
|
1587
|
-
const chartableColumns = $derived.by(()
|
|
1703
|
+
const chartableColumns = $derived.by(function chartableColumns_d() {
|
|
1588
1704
|
const dims = [];
|
|
1589
1705
|
const measures = [];
|
|
1590
1706
|
for (const col of allColumns) {
|
|
@@ -1599,7 +1715,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1599
1715
|
}
|
|
1600
1716
|
return { dims, measures };
|
|
1601
1717
|
});
|
|
1602
|
-
const chartColumnDefaults = $derived.by(()
|
|
1718
|
+
const chartColumnDefaults = $derived.by(function chartColumnDefaults_d() {
|
|
1603
1719
|
const { dims, measures } = chartableColumns;
|
|
1604
1720
|
let dimId = dims[0]?.id ?? null;
|
|
1605
1721
|
let seriesId = null;
|
|
@@ -1972,7 +2088,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1972
2088
|
// the virtualizer's own (real) numbers take over.
|
|
1973
2089
|
const preMeasureRowHeight = $derived(typeof props.rowHeight === "number" ? props.rowHeight : 30);
|
|
1974
2090
|
const preMeasureViewport = $derived(typeof props.containerHeight === "number" ? props.containerHeight : 520);
|
|
1975
|
-
const virtualRows = $derived.by(()
|
|
2091
|
+
const virtualRows = $derived.by(function virtualRows_d() {
|
|
1976
2092
|
virtualizer.version;
|
|
1977
2093
|
const items = virtualizer.getVirtualItems();
|
|
1978
2094
|
if (items.length || allRows.length === 0)
|
|
@@ -1983,7 +2099,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1983
2099
|
// only, and it matches what the server produced, so hydration is clean.
|
|
1984
2100
|
return buildPreMeasureItems(allRows.length, preMeasureRowHeight, preMeasureViewport, props.overscan ?? 8);
|
|
1985
2101
|
});
|
|
1986
|
-
const virtualRowTotalSize = $derived.by(()
|
|
2102
|
+
const virtualRowTotalSize = $derived.by(function virtualRowTotalSize_d() {
|
|
1987
2103
|
virtualizer.version;
|
|
1988
2104
|
const size = virtualizer.getTotalSize();
|
|
1989
2105
|
// Keep the scroll height honest during the pre-measurement render, so the
|
|
@@ -2031,7 +2147,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2031
2147
|
// OWN committed scroll offset - not the live DOM scrollTop - so the spacer
|
|
2032
2148
|
// shift and the rendered window are always computed from the same state and
|
|
2033
2149
|
// can never skew by a frame (which would jitter at extreme scale).
|
|
2034
|
-
const rowOffsetAdjustment = $derived.by(()
|
|
2150
|
+
const rowOffsetAdjustment = $derived.by(function rowOffsetAdjustment_d() {
|
|
2035
2151
|
if (!rowScrollScalingActive)
|
|
2036
2152
|
return 0;
|
|
2037
2153
|
virtualizer.version;
|
|
@@ -2042,11 +2158,11 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2042
2158
|
// virtualRowStart / virtualRowBottomSpacer.
|
|
2043
2159
|
const rowTopSpacer = $derived(Math.max(virtualRowStart - rowOffsetAdjustment, 0));
|
|
2044
2160
|
const rowBottomSpacer = $derived(Math.max(rowDomTotalSize - (virtualRowEnd - rowOffsetAdjustment), 0));
|
|
2045
|
-
const virtualColumns = $derived.by(()
|
|
2161
|
+
const virtualColumns = $derived.by(function virtualColumns_d() {
|
|
2046
2162
|
columnVirtualizerVersion;
|
|
2047
2163
|
return columnVirtualizer.getVirtualItems();
|
|
2048
2164
|
});
|
|
2049
|
-
const virtualColumnTotalSize = $derived.by(()
|
|
2165
|
+
const virtualColumnTotalSize = $derived.by(function virtualColumnTotalSize_d() {
|
|
2050
2166
|
columnVirtualizerVersion;
|
|
2051
2167
|
return columnVirtualizer.getTotalSize();
|
|
2052
2168
|
});
|
|
@@ -2061,7 +2177,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2061
2177
|
return item;
|
|
2062
2178
|
});
|
|
2063
2179
|
};
|
|
2064
|
-
const renderedColumnItems = $derived.by(()
|
|
2180
|
+
const renderedColumnItems = $derived.by(function renderedColumnItems_d() {
|
|
2065
2181
|
if (!columnVirtualizationEnabled)
|
|
2066
2182
|
return allColumnItems();
|
|
2067
2183
|
// Column virtualization is ON by default and its virtualizer, like the row
|
|
@@ -2107,7 +2223,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2107
2223
|
const renderedColumns = $derived.by(() => renderedColumnItems
|
|
2108
2224
|
.map((item) => ({ item, column: allColumns[item.index] }))
|
|
2109
2225
|
.filter(hasRenderedColumn));
|
|
2110
|
-
const totalColumnWidth = $derived.by(()
|
|
2226
|
+
const totalColumnWidth = $derived.by(function totalColumnWidth_d() {
|
|
2111
2227
|
if (columnVirtualizationEnabled)
|
|
2112
2228
|
return virtualColumnTotalSize;
|
|
2113
2229
|
let total = 0;
|
|
@@ -2124,7 +2240,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2124
2240
|
* column instead is reactive to both `columnWidths` and
|
|
2125
2241
|
* `fittedColumnWidths`, so the overflow decision settles in the same
|
|
2126
2242
|
* render where fit-scaling lands - no race, no scrollbar flash. */
|
|
2127
|
-
const hasHorizontalOverflow = $derived.by(()
|
|
2243
|
+
const hasHorizontalOverflow = $derived.by(function hasHorizontalOverflow_d() {
|
|
2128
2244
|
const fixedCols = (showRowNumbersEffective ? rowNumberColumnWidth : 0) +
|
|
2129
2245
|
(showRowSelectionEffective ? selectionColumnWidth : 0);
|
|
2130
2246
|
let total = fixedCols;
|
|
@@ -2150,7 +2266,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2150
2266
|
* everything stays pixel-aligned. When virtualization is off this is a
|
|
2151
2267
|
* pass-through.
|
|
2152
2268
|
*/
|
|
2153
|
-
const groupHeaderRowsWindowed = $derived.by(()
|
|
2269
|
+
const groupHeaderRowsWindowed = $derived.by(function groupHeaderRowsWindowed_d() {
|
|
2154
2270
|
const base = groupHeaderRows;
|
|
2155
2271
|
if (!columnVirtualizationEnabled || base.length === 0)
|
|
2156
2272
|
return base;
|
|
@@ -2176,11 +2292,11 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2176
2292
|
return { ...row, cells };
|
|
2177
2293
|
});
|
|
2178
2294
|
});
|
|
2179
|
-
const activeCell = $derived.by(()
|
|
2295
|
+
const activeCell = $derived.by(function activeCell_d() {
|
|
2180
2296
|
gridStateVersion;
|
|
2181
2297
|
return (grid.getState().activeCell ?? { rowIndex: 0, colIndex: 0, cellId: null });
|
|
2182
2298
|
});
|
|
2183
|
-
const activeDescendantId = $derived.by(()
|
|
2299
|
+
const activeDescendantId = $derived.by(function activeDescendantId_d() {
|
|
2184
2300
|
const active = activeCell;
|
|
2185
2301
|
const inRows = active.rowIndex >= 0 && active.rowIndex < allRows.length;
|
|
2186
2302
|
const inCols = active.colIndex >= 0 && active.colIndex < allColumns.length;
|
|
@@ -2222,7 +2338,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2222
2338
|
* `null` above the cell limit - that case stays on the rAF-deferred effect,
|
|
2223
2339
|
* which keeps a huge grid painting before it totals.
|
|
2224
2340
|
*/
|
|
2225
|
-
const eagerSummaries = $derived.by(()
|
|
2341
|
+
const eagerSummaries = $derived.by(function eagerSummaries_d() {
|
|
2226
2342
|
if (!summarize)
|
|
2227
2343
|
return null;
|
|
2228
2344
|
if (!rowSummariesEnabled)
|
|
@@ -2375,10 +2491,37 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2375
2491
|
// is the reactive signal instead, bumped only when a height actually changes.
|
|
2376
2492
|
const measuredRowHeights = new Map();
|
|
2377
2493
|
let autoRowHeightVersion = $state(0);
|
|
2378
|
-
// A per-row `rowHeight` function already supplies heights, so auto-measuring
|
|
2379
|
-
// would fight it. Fixed numbers are fine - they become the pre-measure estimate.
|
|
2380
|
-
const autoRowHeightOn = $derived(props.autoRowHeight === true && typeof props.rowHeight !== "function");
|
|
2381
2494
|
const autoRowHeightFallback = $derived(typeof props.rowHeight === "number" ? props.rowHeight : 30);
|
|
2495
|
+
// ----- Interactive row resize (`rowResize` prop) -----
|
|
2496
|
+
// Heights the user has dragged, by row index. A plain Map plus a version
|
|
2497
|
+
// counter, exactly like `measuredRowHeights` above and for the same reason:
|
|
2498
|
+
// the reset effect below has to read this collection to know whether there is
|
|
2499
|
+
// anything to clear, and if the collection were reactive that read would make
|
|
2500
|
+
// every resize re-trigger the reset and wipe the height it just stored.
|
|
2501
|
+
const rowResizeHeights = new Map();
|
|
2502
|
+
let rowResizeVersion = $state(0);
|
|
2503
|
+
function setRowResizeHeight(index, height) {
|
|
2504
|
+
if (!rowResizeOn || !Number.isFinite(index) || height <= 0)
|
|
2505
|
+
return;
|
|
2506
|
+
rowResizeHeights.set(index, Math.round(height));
|
|
2507
|
+
rowResizeVersion += 1;
|
|
2508
|
+
}
|
|
2509
|
+
/** A user-dragged height for this row, or undefined when it has not been
|
|
2510
|
+
* resized. Read by the view AND by the virtualizer's `estimateSize`. */
|
|
2511
|
+
function rowResizeHeightPx(index) {
|
|
2512
|
+
return rowResizeOn ? rowResizeHeights.get(index) : undefined;
|
|
2513
|
+
}
|
|
2514
|
+
// A row index means a different row once the data changes, so keeping the
|
|
2515
|
+
// heights would size new rows by the old ones - the same reasoning as the
|
|
2516
|
+
// measured-height reset below.
|
|
2517
|
+
$effect(() => {
|
|
2518
|
+
void allRows.length;
|
|
2519
|
+
void internalData;
|
|
2520
|
+
if (rowResizeHeights.size === 0)
|
|
2521
|
+
return;
|
|
2522
|
+
rowResizeHeights.clear();
|
|
2523
|
+
rowResizeVersion += 1;
|
|
2524
|
+
});
|
|
2382
2525
|
/** Report a row's measured height. Called by the view's measuring action.
|
|
2383
2526
|
* Sub-pixel churn is ignored so a fractional layout can't loop. */
|
|
2384
2527
|
function reportRowHeight(index, height) {
|
|
@@ -2450,11 +2593,19 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2450
2593
|
// the estimate for rows that have not rendered yet. Touch the version so a
|
|
2451
2594
|
// new measurement re-runs this effect (the Map itself is not reactive).
|
|
2452
2595
|
autoRowHeightVersion;
|
|
2453
|
-
|
|
2596
|
+
// Touch the version so a resize re-runs this effect: the virtualizer has to
|
|
2597
|
+
// know the new size or every row below the resized one is positioned
|
|
2598
|
+
// against a stale offset.
|
|
2599
|
+
rowResizeVersion;
|
|
2600
|
+
const base = autoRowHeightOn
|
|
2454
2601
|
? (index) => measuredRowHeights.get(index) ?? autoRowHeightFallback
|
|
2455
2602
|
: typeof rh === "function"
|
|
2456
2603
|
? rh
|
|
2457
2604
|
: (rh ?? 30);
|
|
2605
|
+
const estimateSize = rowResizeOn
|
|
2606
|
+
? (index) => rowResizeHeights.get(index) ??
|
|
2607
|
+
(typeof base === "function" ? base(index) : base)
|
|
2608
|
+
: base;
|
|
2458
2609
|
virtualizer.setOptions({
|
|
2459
2610
|
count: allRows.length,
|
|
2460
2611
|
estimateSize,
|
|
@@ -2552,7 +2703,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2552
2703
|
// Lets the resolver answer from cache without re-invoking the source, so an
|
|
2553
2704
|
// async source fires one request per row instead of one per render.
|
|
2554
2705
|
const asyncEditorColumns = new Set();
|
|
2555
|
-
const headerSelectionState = $derived.by(()
|
|
2706
|
+
const headerSelectionState = $derived.by(function headerSelectionState_d() {
|
|
2556
2707
|
gridStateVersion;
|
|
2557
2708
|
const selectable = allRows.filter((row) => !isGroupRow(row));
|
|
2558
2709
|
if (!selectable.length)
|
|
@@ -2585,7 +2736,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2585
2736
|
* Returns `null` when fit scaling is not in effect (off, no room, total
|
|
2586
2737
|
* already >= target). Callers then fall back to the base width.
|
|
2587
2738
|
*/
|
|
2588
|
-
const fittedColumnWidths = $derived.by(()
|
|
2739
|
+
const fittedColumnWidths = $derived.by(function fittedColumnWidths_d() {
|
|
2589
2740
|
// Track viewport size (not scrollVersion) so we don't recompute on
|
|
2590
2741
|
// every scroll - only when the container actually resizes.
|
|
2591
2742
|
viewportVersion;
|
|
@@ -2650,12 +2801,10 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2650
2801
|
widths[lastId] = Math.max(MIN_COLUMN_WIDTH, scalableTarget - runningSum);
|
|
2651
2802
|
return widths;
|
|
2652
2803
|
});
|
|
2653
|
-
let resizePendingWidth = 0;
|
|
2654
|
-
let resizeRaf = null;
|
|
2655
2804
|
/** Where the fill handle should render: the bottom-right cell of the
|
|
2656
2805
|
* selection range (or the active cell if there's no range). Returns
|
|
2657
2806
|
* null when cell selection is off or there is no anchored selection. */
|
|
2658
|
-
const fillHandleCell = $derived.by(()
|
|
2807
|
+
const fillHandleCell = $derived.by(function fillHandleCell_d() {
|
|
2659
2808
|
if (!(props.enableCellSelection ?? false))
|
|
2660
2809
|
return null;
|
|
2661
2810
|
const anchor = selectionRange.anchor;
|
|
@@ -2699,7 +2848,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2699
2848
|
* reused by both the facet UI and the row filter. Computing them lazily
|
|
2700
2849
|
* in a $derived means columns with no filter menu open and no active
|
|
2701
2850
|
* filter never pay the iteration cost. */
|
|
2702
|
-
const facetBucketsByColumn = $derived.by(()
|
|
2851
|
+
const facetBucketsByColumn = $derived.by(function facetBucketsByColumn_d() {
|
|
2703
2852
|
const map = new Map();
|
|
2704
2853
|
for (const column of allColumns) {
|
|
2705
2854
|
const meta = isBucketableColumn(column);
|
|
@@ -2747,24 +2896,58 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2747
2896
|
* column id (not just the open menu) so the filter row can drive it too.
|
|
2748
2897
|
*/
|
|
2749
2898
|
function facetValuesForColumn(columnId) {
|
|
2899
|
+
return facetEntriesForColumn(columnId).map((entry) => entry.value);
|
|
2900
|
+
}
|
|
2901
|
+
/**
|
|
2902
|
+
* The values a column's checklist offers, with the row count behind each.
|
|
2903
|
+
*
|
|
2904
|
+
* Scoped to the rows passing every OTHER column's filters, not to the whole
|
|
2905
|
+
* dataset. Offering the full set means a checklist full of values that
|
|
2906
|
+
* cannot appear: filter Country to Germany and the City list still showed
|
|
2907
|
+
* every city on file, most of them a one-click route to an empty grid.
|
|
2908
|
+
* The column's OWN filter is excluded, so unticking a value never makes the
|
|
2909
|
+
* rest of the list vanish underneath the pointer.
|
|
2910
|
+
*/
|
|
2911
|
+
function facetEntriesForColumn(columnId) {
|
|
2750
2912
|
if (!columnId)
|
|
2751
2913
|
return [];
|
|
2752
|
-
// Server-provided distinct values win (fetched + cached above).
|
|
2914
|
+
// Server-provided distinct values win (fetched + cached above). The grid
|
|
2915
|
+
// has not seen the rows behind them, so there is nothing to count.
|
|
2753
2916
|
if (props.serverFilterValues)
|
|
2754
|
-
return serverFacetValues[columnId] ?? []
|
|
2917
|
+
return (serverFacetValues[columnId] ?? []).map((value) => ({
|
|
2918
|
+
value,
|
|
2919
|
+
count: null,
|
|
2920
|
+
}));
|
|
2755
2921
|
const column = allColumns.find((entry) => entry.id === columnId);
|
|
2756
2922
|
if (!column)
|
|
2757
2923
|
return [];
|
|
2758
|
-
|
|
2924
|
+
const scope = applyColumnFilters(grid.getRowModel().rows, columnId);
|
|
2759
2925
|
const buckets = facetBucketsByColumn.get(columnId);
|
|
2760
|
-
if (buckets)
|
|
2761
|
-
|
|
2926
|
+
if (buckets) {
|
|
2927
|
+
// Range-bucketed facets for numeric / date columns with many values.
|
|
2928
|
+
const counts = new Map(buckets.map((b) => [b.label, 0]));
|
|
2929
|
+
for (const row of scope) {
|
|
2930
|
+
const num = rawToNumber(getColumnBaseValue(row, column), buckets[0].isDate);
|
|
2931
|
+
if (!Number.isFinite(num))
|
|
2932
|
+
continue;
|
|
2933
|
+
for (const bucket of buckets) {
|
|
2934
|
+
if (isInBucket(num, bucket)) {
|
|
2935
|
+
counts.set(bucket.label, (counts.get(bucket.label) ?? 0) + 1);
|
|
2936
|
+
break;
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
return buckets.map((b) => ({ value: b.label, count: counts.get(b.label) ?? 0 }));
|
|
2941
|
+
}
|
|
2762
2942
|
// Default: distinct-value facets.
|
|
2763
|
-
const
|
|
2764
|
-
for (const
|
|
2765
|
-
|
|
2943
|
+
const counts = new Map();
|
|
2944
|
+
for (const row of scope) {
|
|
2945
|
+
const value = String(getColumnBaseValue(row, column) ?? "");
|
|
2946
|
+
counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
2766
2947
|
}
|
|
2767
|
-
return Array.from(
|
|
2948
|
+
return Array.from(counts.keys())
|
|
2949
|
+
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))
|
|
2950
|
+
.map((value) => ({ value, count: counts.get(value) ?? 0 }));
|
|
2768
2951
|
}
|
|
2769
2952
|
/**
|
|
2770
2953
|
* Facet values for the open menu, SNAPSHOTTED when it opens.
|
|
@@ -2785,8 +2968,9 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2785
2968
|
// out a fresh empty array/set on every read.
|
|
2786
2969
|
const EMPTY_FACETS = [];
|
|
2787
2970
|
const EMPTY_FACET_SET = new Set();
|
|
2971
|
+
const EMPTY_FACET_ENTRIES = [];
|
|
2788
2972
|
let facetCache = null;
|
|
2789
|
-
const columnMenuFacetValues = $derived.by(()
|
|
2973
|
+
const columnMenuFacetValues = $derived.by(function columnMenuFacetValues_d() {
|
|
2790
2974
|
// The funnel popover drives via `filterMenuFor`; the column menu's Filter
|
|
2791
2975
|
// tab drives via `columnMenuFor`. Support whichever is open.
|
|
2792
2976
|
const columnId = filterMenuFor ?? columnMenuFor;
|
|
@@ -2801,10 +2985,12 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2801
2985
|
if (facetCache?.columnId === columnId && facetCache.source === source) {
|
|
2802
2986
|
return facetCache.values;
|
|
2803
2987
|
}
|
|
2988
|
+
const entries = untrack(() => facetEntriesForColumn(columnId));
|
|
2804
2989
|
facetCache = {
|
|
2805
2990
|
columnId,
|
|
2806
2991
|
source,
|
|
2807
|
-
values:
|
|
2992
|
+
values: entries.map((e) => e.value),
|
|
2993
|
+
counts: new Map(entries.flatMap((e) => (e.count == null ? [] : [[e.value, e.count]]))),
|
|
2808
2994
|
};
|
|
2809
2995
|
return facetCache.values;
|
|
2810
2996
|
});
|
|
@@ -2816,46 +3002,76 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2816
3002
|
if (!(filterMenuFor ?? columnMenuFor))
|
|
2817
3003
|
facetCache = null;
|
|
2818
3004
|
});
|
|
2819
|
-
const columnMenuVisibleFacets = $derived.by(()
|
|
3005
|
+
const columnMenuVisibleFacets = $derived.by(function columnMenuVisibleFacets_d() {
|
|
2820
3006
|
const query = columnMenuSearch.trim().toLowerCase();
|
|
2821
3007
|
if (!query)
|
|
2822
3008
|
return columnMenuFacetValues;
|
|
2823
3009
|
return columnMenuFacetValues.filter((value) => value.toLowerCase().includes(query));
|
|
2824
3010
|
});
|
|
2825
3011
|
/** Visible facets as listbox options ((Blanks) label for the empty value). */
|
|
2826
|
-
const columnMenuFacetOptions = $derived
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
3012
|
+
const columnMenuFacetOptions = $derived.by(function columnMenuFacetOptions_d() {
|
|
3013
|
+
// Reading the values first materializes the snapshot (and its counts).
|
|
3014
|
+
const values = columnMenuVisibleFacets;
|
|
3015
|
+
const counts = facetCache?.counts;
|
|
3016
|
+
return values.map((value) => {
|
|
3017
|
+
const count = counts?.get(value);
|
|
3018
|
+
return {
|
|
3019
|
+
value,
|
|
3020
|
+
label: value === "" ? "(Blanks)" : value,
|
|
3021
|
+
// How many rows this value would keep, given the other columns'
|
|
3022
|
+
// filters. Absent in server-values mode, where the grid never sees
|
|
3023
|
+
// the rows behind the list.
|
|
3024
|
+
hint: count == null ? undefined : String(count),
|
|
3025
|
+
};
|
|
3026
|
+
});
|
|
3027
|
+
});
|
|
2830
3028
|
/**
|
|
2831
3029
|
* Checked facets for the open menu. An ABSENT `valueFilters` entry means
|
|
2832
3030
|
* "everything checked", so that case materializes the set once here rather
|
|
2833
3031
|
* than every consumer re-deriving it.
|
|
2834
3032
|
*/
|
|
2835
|
-
const columnMenuSelectedFacets = $derived.by(()
|
|
3033
|
+
const columnMenuSelectedFacets = $derived.by(function columnMenuSelectedFacets_d() {
|
|
2836
3034
|
const columnId = filterMenuFor ?? columnMenuFor;
|
|
2837
3035
|
if (!columnId)
|
|
2838
3036
|
return EMPTY_FACET_SET;
|
|
2839
3037
|
return valueFilters[columnId] ?? new Set(columnMenuFacetValues);
|
|
2840
3038
|
});
|
|
3039
|
+
/**
|
|
3040
|
+
* The offered values, in their own derived so the QUERY does not re-run the
|
|
3041
|
+
* scan. It used to sit in `inSuggestValues` beside the `inSuggestQuery`
|
|
3042
|
+
* read, so every keystroke in a chip input rebuilt the distinct-value set -
|
|
3043
|
+
* and once the scan started narrowing itself against the other columns'
|
|
3044
|
+
* filters, every keystroke ran the filter pipeline too. Split out, it
|
|
3045
|
+
* re-runs only when the dropdown moves to another column.
|
|
3046
|
+
*/
|
|
3047
|
+
const inSuggestEntries = $derived.by(function inSuggestEntries_d() {
|
|
3048
|
+
const columnId = inSuggestFor;
|
|
3049
|
+
if (!columnId)
|
|
3050
|
+
return EMPTY_FACET_ENTRIES;
|
|
3051
|
+
return untrack(() => facetEntriesForColumn(columnId));
|
|
3052
|
+
});
|
|
2841
3053
|
// Distinct values offered in the `in` / `notIn` suggestions dropdown for the
|
|
2842
3054
|
// active chip input, narrowed by whatever the user has typed. Uncapped - the
|
|
2843
3055
|
// dropdown windows its rows, so a high-cardinality column costs a list of
|
|
2844
3056
|
// strings, not thousands of nodes.
|
|
2845
|
-
const inSuggestValues = $derived.by(()
|
|
2846
|
-
if (!inSuggestFor)
|
|
2847
|
-
return EMPTY_FACETS;
|
|
3057
|
+
const inSuggestValues = $derived.by(function inSuggestValues_d() {
|
|
2848
3058
|
const query = inSuggestQuery.trim().toLowerCase();
|
|
2849
|
-
const all =
|
|
2850
|
-
return query
|
|
2851
|
-
? all.filter((value) => value.toLowerCase().includes(query))
|
|
2852
|
-
: all;
|
|
3059
|
+
const all = inSuggestEntries;
|
|
3060
|
+
return (query ? all.filter((e) => e.value.toLowerCase().includes(query)) : all).map((e) => e.value);
|
|
2853
3061
|
});
|
|
2854
3062
|
/** `in` / `notIn` suggestions as listbox options. */
|
|
2855
|
-
const inSuggestOptions = $derived
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
3063
|
+
const inSuggestOptions = $derived.by(function inSuggestOptions_d() {
|
|
3064
|
+
const query = inSuggestQuery.trim().toLowerCase();
|
|
3065
|
+
const shown = query
|
|
3066
|
+
? inSuggestEntries.filter((e) => e.value.toLowerCase().includes(query))
|
|
3067
|
+
: inSuggestEntries;
|
|
3068
|
+
return shown.map((e) => ({
|
|
3069
|
+
value: e.value,
|
|
3070
|
+
label: e.value === "" ? "(Blanks)" : e.value,
|
|
3071
|
+
// Same match count the menu's checklist shows.
|
|
3072
|
+
hint: e.count == null ? undefined : String(e.count),
|
|
3073
|
+
}));
|
|
3074
|
+
});
|
|
2859
3075
|
// Fire onApiReady exactly once when the grid is first ready. Wrapping in
|
|
2860
3076
|
// an effect that tracks `props.onApiReady` was racy - every parent render
|
|
2861
3077
|
// creates a new inline arrow, the effect re-fired, and any synchronous
|
|
@@ -2914,6 +3130,10 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2914
3130
|
set isDraggingSelection(v) { isDraggingSelection = v; },
|
|
2915
3131
|
get fillDrag() { return fillDrag; },
|
|
2916
3132
|
set fillDrag(v) { fillDrag = v; },
|
|
3133
|
+
get moveDrag() { return moveDrag; },
|
|
3134
|
+
set moveDrag(v) { moveDrag = v; },
|
|
3135
|
+
get moveGrabHover() { return moveGrabHover; },
|
|
3136
|
+
set moveGrabHover(v) { moveGrabHover = v; },
|
|
2917
3137
|
get activeAtPointerDown() { return activeAtPointerDown; },
|
|
2918
3138
|
set activeAtPointerDown(v) { activeAtPointerDown = v; },
|
|
2919
3139
|
get editingCell() { return editingCell; },
|
|
@@ -2950,12 +3170,6 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2950
3170
|
set editorSelectAll(v) { editorSelectAll = v; },
|
|
2951
3171
|
get columnWidths() { return columnWidths; },
|
|
2952
3172
|
set columnWidths(v) { columnWidths = v; },
|
|
2953
|
-
get resizingColumnId() { return resizingColumnId; },
|
|
2954
|
-
set resizingColumnId(v) { resizingColumnId = v; },
|
|
2955
|
-
get resizeStartX() { return resizeStartX; },
|
|
2956
|
-
set resizeStartX(v) { resizeStartX = v; },
|
|
2957
|
-
get resizeStartWidth() { return resizeStartWidth; },
|
|
2958
|
-
set resizeStartWidth(v) { resizeStartWidth = v; },
|
|
2959
3173
|
get MIN_COLUMN_WIDTH() { return MIN_COLUMN_WIDTH; },
|
|
2960
3174
|
get columnPinning() { return columnPinning; },
|
|
2961
3175
|
set columnPinning(v) { columnPinning = v; },
|
|
@@ -3023,6 +3237,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
3023
3237
|
get showRowSelectionEffective() { return showRowSelectionEffective; },
|
|
3024
3238
|
get rowSummariesEnabled() { return rowSummariesEnabled; },
|
|
3025
3239
|
get enableCellSelectionEffective() { return enableCellSelectionEffective; },
|
|
3240
|
+
get moveCellsEffective() { return moveCellsEffective; },
|
|
3026
3241
|
get flushScheduledScrollSync() { return flushScheduledScrollSync; },
|
|
3027
3242
|
get scheduleScrollSync() { return scheduleScrollSync; },
|
|
3028
3243
|
get internalData() { return internalData; },
|
|
@@ -3104,6 +3319,12 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
3104
3319
|
get allRowsBeforePagination() { return allRowsBeforePagination; },
|
|
3105
3320
|
get allRows() { return allRows; },
|
|
3106
3321
|
get rowSelectionState() { return rowSelectionState; },
|
|
3322
|
+
get selectionBarOn() { return selectionBarOn; },
|
|
3323
|
+
get selectionBarActions() { return selectionBarActions; },
|
|
3324
|
+
get selectionBarPosition() { return selectionBarPosition; },
|
|
3325
|
+
get selectionBarMaxVisible() { return selectionBarMaxVisible; },
|
|
3326
|
+
get selectionBarHideClear() { return selectionBarHideClear; },
|
|
3327
|
+
get selectionBarTarget() { return selectionBarTarget; },
|
|
3107
3328
|
get lastSelectionSerialized() { return lastSelectionSerialized; },
|
|
3108
3329
|
set lastSelectionSerialized(v) { lastSelectionSerialized = v; },
|
|
3109
3330
|
get lastCellRangeSerialized() { return lastCellRangeSerialized; },
|
|
@@ -3278,6 +3499,15 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
3278
3499
|
autoRowHeightVersion;
|
|
3279
3500
|
return (index) => measuredRowHeights.get(index);
|
|
3280
3501
|
},
|
|
3502
|
+
/** True when the `rowResize` prop is on and nothing overrides it. */
|
|
3503
|
+
get rowResizeOn() { return rowResizeOn; },
|
|
3504
|
+
/** Record the height the user dragged a row to. */
|
|
3505
|
+
get setRowResizeHeight() { return setRowResizeHeight; },
|
|
3506
|
+
/** A row's dragged height, or undefined when it has not been resized. */
|
|
3507
|
+
get rowResizeHeightPx() {
|
|
3508
|
+
rowResizeVersion;
|
|
3509
|
+
return rowResizeHeightPx;
|
|
3510
|
+
},
|
|
3281
3511
|
get virtualRowTotalSize() { return virtualRowTotalSize; },
|
|
3282
3512
|
get virtualRowStart() { return virtualRowStart; },
|
|
3283
3513
|
get virtualRowEnd() { return virtualRowEnd; },
|
|
@@ -3340,13 +3570,6 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
3340
3570
|
get getColumnBaseWidth() { return getColumnBaseWidth; },
|
|
3341
3571
|
get fittedColumnWidths() { return fittedColumnWidths; },
|
|
3342
3572
|
get getColumnWidth() { return getColumnWidth; },
|
|
3343
|
-
get resizePendingWidth() { return resizePendingWidth; },
|
|
3344
|
-
set resizePendingWidth(v) { resizePendingWidth = v; },
|
|
3345
|
-
get resizeRaf() { return resizeRaf; },
|
|
3346
|
-
set resizeRaf(v) { resizeRaf = v; },
|
|
3347
|
-
get startColumnResize() { return startColumnResize; },
|
|
3348
|
-
get onColumnResizeMove() { return onColumnResizeMove; },
|
|
3349
|
-
get endColumnResize() { return endColumnResize; },
|
|
3350
3573
|
get setSelection() { return setSelection; },
|
|
3351
3574
|
get extendSelection() { return extendSelection; },
|
|
3352
3575
|
get isCellInSelectedRange() { return isCellInSelectedRange; },
|
|
@@ -3354,8 +3577,18 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
3354
3577
|
get getSelectionRects() { return getSelectionRects; },
|
|
3355
3578
|
get fillHandleCell() { return fillHandleCell; },
|
|
3356
3579
|
get isInFillPreview() { return isInFillPreview; },
|
|
3580
|
+
get dragPreviewRect() { return dragPreviewRect; },
|
|
3357
3581
|
get fillMarqueeEdges() { return fillMarqueeEdges; },
|
|
3358
3582
|
get findColumnById() { return findColumnById; },
|
|
3583
|
+
/**
|
|
3584
|
+
* Whether this column may be resized by the user, from its
|
|
3585
|
+
* `ColumnDef.resizable`. Defaults to true, so a column only opts out by
|
|
3586
|
+
* saying so; the grid-wide `columnResize` prop gates all of them above
|
|
3587
|
+
* this. Used by the resize action and by the column menu's Autosize item.
|
|
3588
|
+
*/
|
|
3589
|
+
columnResizable(columnId) {
|
|
3590
|
+
return findColumnById(columnId)?.columnDef?.resizable !== false;
|
|
3591
|
+
},
|
|
3359
3592
|
get readCellRaw() { return readCellRaw; },
|
|
3360
3593
|
get writeCellRaw() { return writeCellRaw; },
|
|
3361
3594
|
get applyFillPattern() { return applyFillPattern; },
|
|
@@ -3363,6 +3596,14 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
3363
3596
|
get startFillDrag() { return startFillDrag; },
|
|
3364
3597
|
get onFillPointerMove() { return onFillPointerMove; },
|
|
3365
3598
|
get onFillPointerUp() { return onFillPointerUp; },
|
|
3599
|
+
get isOnMoveGrabStrip() { return isOnMoveGrabStrip; },
|
|
3600
|
+
get startMoveDrag() { return startMoveDrag; },
|
|
3601
|
+
get onMovePointerMove() { return onMovePointerMove; },
|
|
3602
|
+
get onMovePointerUp() { return onMovePointerUp; },
|
|
3603
|
+
get trackEdgeScroll() { return trackEdgeScroll; },
|
|
3604
|
+
get stopEdgeScroll() { return stopEdgeScroll; },
|
|
3605
|
+
get applyMoveRange() { return applyMoveRange; },
|
|
3606
|
+
get moveDestRect() { return moveDestRect; },
|
|
3366
3607
|
get toggleBooleanCell() { return toggleBooleanCell; },
|
|
3367
3608
|
get onCellPointerDown() { return onCellPointerDown; },
|
|
3368
3609
|
get onCellPointerEnter() { return onCellPointerEnter; },
|
|
@@ -3464,12 +3705,12 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
3464
3705
|
const { updateFilterRow, updateFilterOperator, updateFilterMenuValue, updateFilterMenuValueTo, addFilterToken, removeFilterToken, toggleFilterToken, toggleCheckboxWithKeyboard, isColumnFiltered, closeMenus, openInSuggest, closeInSuggest, openChooseColumns, openColumnMenu, openFilterMenu, openOperatorMenu, sortColumnFromMenu, clearColumnSort, groupByColumnFromMenu, clearGroupingFromMenu, isFacetChecked, toggleFacetValue, setFacetSelection, setFilterTokens, isAllFacetsChecked, toggleAllFacets, clearColumnFilter, changePage, goToPage, setPageSize, openContextMenu, closeContextMenu, contextMenuItems, saveComment, removeComment, closeCommentEditor } = createMenus(ctx);
|
|
3465
3706
|
const { cellConditionalFormat, computeRowClass, computeCellClass, computeCellTooltip, computeCellValidity, computeCellNote, getColumnEditorOptions, areEditorOptionsLoading, formatListCellValue, formatCellValue, formatPinnedValue, computePinnedCellClass } = createCellRender(ctx);
|
|
3466
3707
|
const { isCellEditable, isCellEditableAt, getRowColumnValue, getCellDisplayValue, startEditingWithChar, startEditing, stopEditing, startFullRowEdit, setFullRowDraft, commitFullRowEdit, cancelFullRowEdit, saveEditingCell, applyHistoryStep, updateEditingCellValue, onEditorKeyDown, commitAndMoveByTab, focusOnMount, onCellDoubleClick, pasteFromClipboard, onGridPaste } = createEditing(ctx);
|
|
3467
|
-
const { isRowSelected, toggleRowSelectionById, toggleSelectAllRows, setActiveCell, scrollActiveCellIntoView, setSelection, extendSelection, isCellInSelectedRange, getCellRangeEdges, getSelectionRects, isInFillPreview, fillMarqueeEdges, findColumnById, onCellPointerDown, onCellPointerEnter, endDragSelection, onWindowPointerMove, onCellClick, emitCellDoubleClick } = createSelection(ctx);
|
|
3468
|
-
const { cellPinStyle, isColumnPinned, getCurrentColumnOrder, emitColumnOrder, setColumnOrderInternal, applyColumnDrop, onColumnHeaderDragStart, onColumnHeaderDragOver, onColumnHeaderDragLeave, onColumnHeaderDrop, onColumnHeaderDragEnd, pinColumnLeft, pinColumnRight, unpinColumn, toggleColumnVisibleInPanel, moveColumnInPanel, toggleGroupInPanel, getColumnBaseWidth, getColumnWidth,
|
|
3708
|
+
const { isRowSelected, toggleRowSelectionById, toggleSelectAllRows, setActiveCell, scrollActiveCellIntoView, setSelection, extendSelection, isCellInSelectedRange, getCellRangeEdges, getSelectionRects, isInFillPreview, dragPreviewRect, fillMarqueeEdges, findColumnById, onCellPointerDown, onCellPointerEnter, endDragSelection, onWindowPointerMove, onCellClick, emitCellDoubleClick } = createSelection(ctx);
|
|
3709
|
+
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(ctx);
|
|
3469
3710
|
const { onRowDragStart, onRowDragOver, onRowDragLeave, onRowDrop, onRowsContainerDragOver, onRowsContainerDrop, onRowDragEnd, onRowPointerDown, destroyRowDrag } = createRowDrag(ctx);
|
|
3470
3711
|
const { register: registerAlignedGrid, broadcastScroll: broadcastAlignedScroll, broadcastWidths: broadcastAlignedWidths } = createAlignedGrids(ctx);
|
|
3471
3712
|
const { buildApi } = createGridApi(ctx);
|
|
3472
|
-
const { readCellRaw, writeCellRaw, applyFillPattern, clearSelectedCellValues, startFillDrag, onFillPointerMove, onFillPointerUp, toggleBooleanCell, copySelectionToClipboard, clearSelectedCells, cutSelectionToClipboard } = createClipboard(ctx);
|
|
3713
|
+
const { readCellRaw, writeCellRaw, applyFillPattern, clearSelectedCellValues, startFillDrag, onFillPointerMove, onFillPointerUp, isOnMoveGrabStrip, startMoveDrag, onMovePointerMove, onMovePointerUp, applyMoveRange, moveDestRect, trackEdgeScroll, stopEdgeScroll, toggleBooleanCell, copySelectionToClipboard, clearSelectedCells, cutSelectionToClipboard } = createClipboard(ctx);
|
|
3473
3714
|
// Dev-time configuration checks. Silent misconfiguration was the grid's
|
|
3474
3715
|
// biggest usability gap - a misspelled `field` rendered a column of blank
|
|
3475
3716
|
// cells and printed nothing, and an inert `pageSize` was simply ignored.
|
|
@@ -3541,7 +3782,6 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
3541
3782
|
// this grid still owns (#68). Each of these is a no-op when idle.
|
|
3542
3783
|
$effect(() => {
|
|
3543
3784
|
return () => {
|
|
3544
|
-
endColumnResize();
|
|
3545
3785
|
hideTooltip();
|
|
3546
3786
|
destroyRowDrag();
|
|
3547
3787
|
};
|