@svgrid/grid 2.6.22 → 2.6.23
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/GridMenus.svelte +184 -6
- package/dist/SvAutoComplete.svelte +4 -0
- package/dist/SvComboBox.svelte +5 -1
- package/dist/SvDateRangeInput.svelte +25 -3
- package/dist/SvDropDownList.svelte +4 -0
- package/dist/SvForm.svelte +8 -8
- package/dist/SvGrid.controller.svelte.d.ts +48 -1
- package/dist/SvGrid.controller.svelte.js +214 -52
- package/dist/SvGrid.css +221 -0
- package/dist/SvGrid.helpers.d.ts +55 -0
- package/dist/SvGrid.helpers.js +83 -0
- package/dist/SvGrid.svelte +145 -0
- package/dist/SvGrid.types.d.ts +105 -0
- package/dist/SvGridCellEditor.svelte +99 -17
- package/dist/SvGridSelect.svelte +6 -2
- package/dist/SvGridSelect.svelte.d.ts +1 -1
- package/dist/SvListBox.svelte +8 -0
- package/dist/SvMultiSelect.svelte +6 -2
- package/dist/SvMultiSelect.svelte.d.ts +1 -1
- package/dist/SvNumberInput.svelte +4 -0
- package/dist/SvRichCell.svelte +125 -0
- package/dist/SvRichCell.svelte.d.ts +15 -0
- package/dist/SvTextArea.svelte +5 -1
- package/dist/SvTreeSelect.svelte +6 -2
- 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-BzWXQjv3.js +600 -0
- package/dist/cdn/GridMenus-xr_rHx8F.js +601 -0
- package/dist/cdn/SvDateRangeInput-CaOuMs8O.js +198 -0
- package/dist/cdn/SvDateRangeInput-DMLKmGEc.js +199 -0
- package/dist/cdn/SvDateTimePicker-CCbDZNZB.js +816 -0
- package/dist/cdn/SvDateTimePicker-sonaH0oh.js +812 -0
- package/dist/cdn/SvGridCellEditor-BAPSC7EL.js +550 -0
- package/dist/cdn/SvGridCellEditor-BwzmUWtL.js +549 -0
- package/dist/cdn/date-format-BNii4zeD.js +1393 -0
- package/dist/cdn/date-format-BnnHlqGw.js +1395 -0
- package/dist/cdn/{editor-registry-CiI0xlKg.js → editor-registry-BhgE3S84.js} +51 -25
- package/dist/cdn/{src-D1lXwq1l.js → src-DGo7IHug.js} +5730 -5198
- package/dist/cdn/{src-C9Hihx1W.js → src-skQN5eqh.js} +7216 -6684
- package/dist/cdn/svgrid.js +14 -12
- package/dist/cdn/svgrid.svelte-external.js +14 -12
- package/dist/cell-values.d.ts +16 -0
- package/dist/cell-values.js +28 -0
- package/dist/clipboard.d.ts +22 -0
- package/dist/clipboard.js +405 -27
- 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/grid-messages.d.ts +15 -0
- package/dist/grid-messages.js +15 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/list-option.d.ts +6 -0
- package/dist/menus.js +40 -7
- 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 +129 -13
- package/package.json +1 -1
- package/src/GridMenus.svelte +184 -6
- package/src/SvAutoComplete.svelte +4 -0
- package/src/SvComboBox.svelte +5 -1
- package/src/SvDateRangeInput.svelte +25 -3
- package/src/SvDropDownList.svelte +4 -0
- package/src/SvForm.svelte +8 -8
- package/src/SvGrid.controller.svelte.ts +251 -61
- package/src/SvGrid.css +221 -0
- package/src/SvGrid.helpers.ts +87 -0
- package/src/SvGrid.svelte +145 -0
- package/src/SvGrid.types.ts +112 -0
- package/src/SvGridCellEditor.svelte +99 -17
- package/src/SvGridSelect.svelte +6 -2
- package/src/SvListBox.svelte +8 -0
- package/src/SvMultiSelect.svelte +6 -2
- package/src/SvNumberInput.svelte +4 -0
- package/src/SvRichCell.svelte +125 -0
- package/src/SvTextArea.svelte +5 -1
- package/src/SvTreeSelect.svelte +6 -2
- package/src/builtin-editors.test.ts +18 -1
- package/src/builtin-editors.ts +19 -3
- package/src/cell-values.ts +30 -0
- package/src/clipboard.test.ts +93 -20
- package/src/clipboard.ts +433 -33
- package/src/date-string-column.test.ts +87 -0
- package/src/editing.test.ts +25 -0
- package/src/editing.ts +12 -9
- package/src/editor-block-prop.test.ts +132 -0
- package/src/editors/cell-editors.ts +27 -1
- package/src/grid-messages.ts +34 -0
- package/src/index.ts +13 -0
- package/src/list-option.ts +6 -0
- package/src/menus.test.ts +43 -0
- package/src/menus.ts +37 -7
- 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/sanitize-html.test.ts +182 -0
- package/src/sanitize-html.ts +235 -0
- package/src/selection-bar-view.svelte.ts +36 -0
- package/src/selection.test.ts +157 -0
- package/src/selection.ts +131 -13
- package/src/svgrid.filter-menu-listbox.svelte.test.ts +56 -10
- package/src/svgrid.in-suggest-snapshot.svelte.test.ts +91 -0
- package/src/svgrid.menu-scroll-close.test.ts +204 -0
- package/src/svgrid.selection-bar-seam.test.ts +185 -0
- package/src/svgrid.upsell-license.test.ts +117 -0
- package/dist/cdn/GridMenus-BuoBPqxx.js +0 -493
- package/dist/cdn/GridMenus-n4llxoOI.js +0 -494
- 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
|
|
@@ -424,6 +437,11 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
424
437
|
props.enableCellSelection ??
|
|
425
438
|
((props.selectionMode ?? "both") === "cell" ||
|
|
426
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);
|
|
427
445
|
// The aggregate footer row. `summary` is the shortcut alias, so it wins over
|
|
428
446
|
// the fine-grained prop - the same precedence `selectable` uses.
|
|
429
447
|
//
|
|
@@ -1022,30 +1040,19 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1022
1040
|
* compute the correct "X to Y of Z" range and total page count when
|
|
1023
1041
|
* filters reduce the dataset.
|
|
1024
1042
|
*/
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
// so the data isn't double-filtered against the visible page.
|
|
1039
|
-
if (externalFilterEnabled)
|
|
1040
|
-
return rawRows;
|
|
1041
|
-
let rows = rawRows;
|
|
1042
|
-
if (globalFilter.trim()) {
|
|
1043
|
-
const needle = normalizeForFilter(globalFilter, (props.filterLocale ?? props.localization?.locale));
|
|
1044
|
-
rows = rows.filter((row) => row
|
|
1045
|
-
.getAllCells()
|
|
1046
|
-
.some((cell) => normalizeForFilter(String(cell.getValue() ?? ""), (props.filterLocale ?? props.localization?.locale))
|
|
1047
|
-
.includes(needle)));
|
|
1048
|
-
}
|
|
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;
|
|
1049
1056
|
/**
|
|
1050
1057
|
* Resolve a column ONCE and return a reader for it.
|
|
1051
1058
|
*
|
|
@@ -1079,7 +1086,9 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1079
1086
|
// redundant passes.
|
|
1080
1087
|
const compileCond = (columnId, op, value, valueTo) => compileExcelFilter({ id: columnId, operator: op, value, valueTo: op === "between" ? valueTo : undefined }, { locale: (props.filterLocale ?? props.localization?.locale) });
|
|
1081
1088
|
// A column filter is active if either of its (up to two) conditions is.
|
|
1082
|
-
const menuFilters = Object.entries(filterMenuValues).filter(([
|
|
1089
|
+
const menuFilters = Object.entries(filterMenuValues).filter(([id, f]) => {
|
|
1090
|
+
if (id === excludeColumnId)
|
|
1091
|
+
return false;
|
|
1083
1092
|
const a = condActive(f.operator, f.value, f.valueTo);
|
|
1084
1093
|
const b = !!f.operator2 && condActive(f.operator2, f.value2 ?? "", f.valueTo2);
|
|
1085
1094
|
return a || b;
|
|
@@ -1113,7 +1122,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1113
1122
|
return join === "OR" ? ra || rb : ra && rb;
|
|
1114
1123
|
}));
|
|
1115
1124
|
}
|
|
1116
|
-
const valueFilterEntries = Object.entries(valueFilters);
|
|
1125
|
+
const valueFilterEntries = Object.entries(valueFilters).filter(([id]) => id !== excludeColumnId);
|
|
1117
1126
|
if (valueFilterEntries.length) {
|
|
1118
1127
|
// Resolve bucket defs up front so we don't re-hit the derived map
|
|
1119
1128
|
// for every row × column combination. Columns without bucketing
|
|
@@ -1142,6 +1151,33 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1142
1151
|
return allowed.has(String(raw ?? ""));
|
|
1143
1152
|
}));
|
|
1144
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);
|
|
1145
1181
|
// --- Advanced filter (Pro) ---------------------------------------------
|
|
1146
1182
|
// Runs LAST for two reasons: it evaluates over the smallest row set, and
|
|
1147
1183
|
// its aggregates (`SUM(amount) > 1000`) fold over what the user is
|
|
@@ -1323,6 +1359,45 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
1323
1359
|
gridStateVersion;
|
|
1324
1360
|
return grid.getState().rowSelection ?? {};
|
|
1325
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
|
+
});
|
|
1326
1401
|
// Forward selection changes to the consumer. Skips the very first invocation
|
|
1327
1402
|
// (the initial empty state) so consumers don't get a spurious callback on mount.
|
|
1328
1403
|
let lastSelectionSerialized = "";
|
|
@@ -2821,24 +2896,58 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2821
2896
|
* column id (not just the open menu) so the filter row can drive it too.
|
|
2822
2897
|
*/
|
|
2823
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) {
|
|
2824
2912
|
if (!columnId)
|
|
2825
2913
|
return [];
|
|
2826
|
-
// 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.
|
|
2827
2916
|
if (props.serverFilterValues)
|
|
2828
|
-
return serverFacetValues[columnId] ?? []
|
|
2917
|
+
return (serverFacetValues[columnId] ?? []).map((value) => ({
|
|
2918
|
+
value,
|
|
2919
|
+
count: null,
|
|
2920
|
+
}));
|
|
2829
2921
|
const column = allColumns.find((entry) => entry.id === columnId);
|
|
2830
2922
|
if (!column)
|
|
2831
2923
|
return [];
|
|
2832
|
-
|
|
2924
|
+
const scope = applyColumnFilters(grid.getRowModel().rows, columnId);
|
|
2833
2925
|
const buckets = facetBucketsByColumn.get(columnId);
|
|
2834
|
-
if (buckets)
|
|
2835
|
-
|
|
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
|
+
}
|
|
2836
2942
|
// Default: distinct-value facets.
|
|
2837
|
-
const
|
|
2838
|
-
for (const
|
|
2839
|
-
|
|
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);
|
|
2840
2947
|
}
|
|
2841
|
-
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 }));
|
|
2842
2951
|
}
|
|
2843
2952
|
/**
|
|
2844
2953
|
* Facet values for the open menu, SNAPSHOTTED when it opens.
|
|
@@ -2859,6 +2968,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2859
2968
|
// out a fresh empty array/set on every read.
|
|
2860
2969
|
const EMPTY_FACETS = [];
|
|
2861
2970
|
const EMPTY_FACET_SET = new Set();
|
|
2971
|
+
const EMPTY_FACET_ENTRIES = [];
|
|
2862
2972
|
let facetCache = null;
|
|
2863
2973
|
const columnMenuFacetValues = $derived.by(function columnMenuFacetValues_d() {
|
|
2864
2974
|
// The funnel popover drives via `filterMenuFor`; the column menu's Filter
|
|
@@ -2875,10 +2985,12 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2875
2985
|
if (facetCache?.columnId === columnId && facetCache.source === source) {
|
|
2876
2986
|
return facetCache.values;
|
|
2877
2987
|
}
|
|
2988
|
+
const entries = untrack(() => facetEntriesForColumn(columnId));
|
|
2878
2989
|
facetCache = {
|
|
2879
2990
|
columnId,
|
|
2880
2991
|
source,
|
|
2881
|
-
values:
|
|
2992
|
+
values: entries.map((e) => e.value),
|
|
2993
|
+
counts: new Map(entries.flatMap((e) => (e.count == null ? [] : [[e.value, e.count]]))),
|
|
2882
2994
|
};
|
|
2883
2995
|
return facetCache.values;
|
|
2884
2996
|
});
|
|
@@ -2897,10 +3009,22 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2897
3009
|
return columnMenuFacetValues.filter((value) => value.toLowerCase().includes(query));
|
|
2898
3010
|
});
|
|
2899
3011
|
/** Visible facets as listbox options ((Blanks) label for the empty value). */
|
|
2900
|
-
const columnMenuFacetOptions = $derived
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
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
|
+
});
|
|
2904
3028
|
/**
|
|
2905
3029
|
* Checked facets for the open menu. An ABSENT `valueFilters` entry means
|
|
2906
3030
|
* "everything checked", so that case materializes the set once here rather
|
|
@@ -2912,24 +3036,42 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2912
3036
|
return EMPTY_FACET_SET;
|
|
2913
3037
|
return valueFilters[columnId] ?? new Set(columnMenuFacetValues);
|
|
2914
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
|
+
});
|
|
2915
3053
|
// Distinct values offered in the `in` / `notIn` suggestions dropdown for the
|
|
2916
3054
|
// active chip input, narrowed by whatever the user has typed. Uncapped - the
|
|
2917
3055
|
// dropdown windows its rows, so a high-cardinality column costs a list of
|
|
2918
3056
|
// strings, not thousands of nodes.
|
|
2919
3057
|
const inSuggestValues = $derived.by(function inSuggestValues_d() {
|
|
2920
|
-
if (!inSuggestFor)
|
|
2921
|
-
return EMPTY_FACETS;
|
|
2922
3058
|
const query = inSuggestQuery.trim().toLowerCase();
|
|
2923
|
-
const all =
|
|
2924
|
-
return query
|
|
2925
|
-
? all.filter((value) => value.toLowerCase().includes(query))
|
|
2926
|
-
: all;
|
|
3059
|
+
const all = inSuggestEntries;
|
|
3060
|
+
return (query ? all.filter((e) => e.value.toLowerCase().includes(query)) : all).map((e) => e.value);
|
|
2927
3061
|
});
|
|
2928
3062
|
/** `in` / `notIn` suggestions as listbox options. */
|
|
2929
|
-
const inSuggestOptions = $derived
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
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
|
+
});
|
|
2933
3075
|
// Fire onApiReady exactly once when the grid is first ready. Wrapping in
|
|
2934
3076
|
// an effect that tracks `props.onApiReady` was racy - every parent render
|
|
2935
3077
|
// creates a new inline arrow, the effect re-fired, and any synchronous
|
|
@@ -2988,6 +3130,10 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
2988
3130
|
set isDraggingSelection(v) { isDraggingSelection = v; },
|
|
2989
3131
|
get fillDrag() { return fillDrag; },
|
|
2990
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; },
|
|
2991
3137
|
get activeAtPointerDown() { return activeAtPointerDown; },
|
|
2992
3138
|
set activeAtPointerDown(v) { activeAtPointerDown = v; },
|
|
2993
3139
|
get editingCell() { return editingCell; },
|
|
@@ -3091,6 +3237,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
3091
3237
|
get showRowSelectionEffective() { return showRowSelectionEffective; },
|
|
3092
3238
|
get rowSummariesEnabled() { return rowSummariesEnabled; },
|
|
3093
3239
|
get enableCellSelectionEffective() { return enableCellSelectionEffective; },
|
|
3240
|
+
get moveCellsEffective() { return moveCellsEffective; },
|
|
3094
3241
|
get flushScheduledScrollSync() { return flushScheduledScrollSync; },
|
|
3095
3242
|
get scheduleScrollSync() { return scheduleScrollSync; },
|
|
3096
3243
|
get internalData() { return internalData; },
|
|
@@ -3172,6 +3319,12 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
3172
3319
|
get allRowsBeforePagination() { return allRowsBeforePagination; },
|
|
3173
3320
|
get allRows() { return allRows; },
|
|
3174
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; },
|
|
3175
3328
|
get lastSelectionSerialized() { return lastSelectionSerialized; },
|
|
3176
3329
|
set lastSelectionSerialized(v) { lastSelectionSerialized = v; },
|
|
3177
3330
|
get lastCellRangeSerialized() { return lastCellRangeSerialized; },
|
|
@@ -3424,6 +3577,7 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
3424
3577
|
get getSelectionRects() { return getSelectionRects; },
|
|
3425
3578
|
get fillHandleCell() { return fillHandleCell; },
|
|
3426
3579
|
get isInFillPreview() { return isInFillPreview; },
|
|
3580
|
+
get dragPreviewRect() { return dragPreviewRect; },
|
|
3427
3581
|
get fillMarqueeEdges() { return fillMarqueeEdges; },
|
|
3428
3582
|
get findColumnById() { return findColumnById; },
|
|
3429
3583
|
/**
|
|
@@ -3442,6 +3596,14 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
3442
3596
|
get startFillDrag() { return startFillDrag; },
|
|
3443
3597
|
get onFillPointerMove() { return onFillPointerMove; },
|
|
3444
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; },
|
|
3445
3607
|
get toggleBooleanCell() { return toggleBooleanCell; },
|
|
3446
3608
|
get onCellPointerDown() { return onCellPointerDown; },
|
|
3447
3609
|
get onCellPointerEnter() { return onCellPointerEnter; },
|
|
@@ -3543,12 +3705,12 @@ export function createSvGridController(rawProps, domIdBase) {
|
|
|
3543
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);
|
|
3544
3706
|
const { cellConditionalFormat, computeRowClass, computeCellClass, computeCellTooltip, computeCellValidity, computeCellNote, getColumnEditorOptions, areEditorOptionsLoading, formatListCellValue, formatCellValue, formatPinnedValue, computePinnedCellClass } = createCellRender(ctx);
|
|
3545
3707
|
const { isCellEditable, isCellEditableAt, getRowColumnValue, getCellDisplayValue, startEditingWithChar, startEditing, stopEditing, startFullRowEdit, setFullRowDraft, commitFullRowEdit, cancelFullRowEdit, saveEditingCell, applyHistoryStep, updateEditingCellValue, onEditorKeyDown, commitAndMoveByTab, focusOnMount, onCellDoubleClick, pasteFromClipboard, onGridPaste } = createEditing(ctx);
|
|
3546
|
-
const { isRowSelected, toggleRowSelectionById, toggleSelectAllRows, setActiveCell, scrollActiveCellIntoView, setSelection, extendSelection, isCellInSelectedRange, getCellRangeEdges, getSelectionRects, isInFillPreview, fillMarqueeEdges, findColumnById, onCellPointerDown, onCellPointerEnter, endDragSelection, onWindowPointerMove, onCellClick, emitCellDoubleClick } = createSelection(ctx);
|
|
3708
|
+
const { isRowSelected, toggleRowSelectionById, toggleSelectAllRows, setActiveCell, scrollActiveCellIntoView, setSelection, extendSelection, isCellInSelectedRange, getCellRangeEdges, getSelectionRects, isInFillPreview, dragPreviewRect, fillMarqueeEdges, findColumnById, onCellPointerDown, onCellPointerEnter, endDragSelection, onWindowPointerMove, onCellClick, emitCellDoubleClick } = createSelection(ctx);
|
|
3547
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);
|
|
3548
3710
|
const { onRowDragStart, onRowDragOver, onRowDragLeave, onRowDrop, onRowsContainerDragOver, onRowsContainerDrop, onRowDragEnd, onRowPointerDown, destroyRowDrag } = createRowDrag(ctx);
|
|
3549
3711
|
const { register: registerAlignedGrid, broadcastScroll: broadcastAlignedScroll, broadcastWidths: broadcastAlignedWidths } = createAlignedGrids(ctx);
|
|
3550
3712
|
const { buildApi } = createGridApi(ctx);
|
|
3551
|
-
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);
|
|
3552
3714
|
// Dev-time configuration checks. Silent misconfiguration was the grid's
|
|
3553
3715
|
// biggest usability gap - a misspelled `field` rendered a column of blank
|
|
3554
3716
|
// cells and printed nothing, and an inert `pageSize` was simply ignored.
|