@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.
Files changed (115) hide show
  1. package/dist/GridMenus.svelte +184 -6
  2. package/dist/SvAutoComplete.svelte +4 -0
  3. package/dist/SvComboBox.svelte +5 -1
  4. package/dist/SvDateRangeInput.svelte +25 -3
  5. package/dist/SvDropDownList.svelte +4 -0
  6. package/dist/SvForm.svelte +8 -8
  7. package/dist/SvGrid.controller.svelte.d.ts +48 -1
  8. package/dist/SvGrid.controller.svelte.js +214 -52
  9. package/dist/SvGrid.css +221 -0
  10. package/dist/SvGrid.helpers.d.ts +55 -0
  11. package/dist/SvGrid.helpers.js +83 -0
  12. package/dist/SvGrid.svelte +145 -0
  13. package/dist/SvGrid.types.d.ts +105 -0
  14. package/dist/SvGridCellEditor.svelte +99 -17
  15. package/dist/SvGridSelect.svelte +6 -2
  16. package/dist/SvGridSelect.svelte.d.ts +1 -1
  17. package/dist/SvListBox.svelte +8 -0
  18. package/dist/SvMultiSelect.svelte +6 -2
  19. package/dist/SvMultiSelect.svelte.d.ts +1 -1
  20. package/dist/SvNumberInput.svelte +4 -0
  21. package/dist/SvRichCell.svelte +125 -0
  22. package/dist/SvRichCell.svelte.d.ts +15 -0
  23. package/dist/SvTextArea.svelte +5 -1
  24. package/dist/SvTreeSelect.svelte +6 -2
  25. package/dist/SvTreeSelect.svelte.d.ts +1 -1
  26. package/dist/builtin-editors.d.ts +2 -2
  27. package/dist/builtin-editors.js +19 -3
  28. package/dist/cdn/GridMenus-BzWXQjv3.js +600 -0
  29. package/dist/cdn/GridMenus-xr_rHx8F.js +601 -0
  30. package/dist/cdn/SvDateRangeInput-CaOuMs8O.js +198 -0
  31. package/dist/cdn/SvDateRangeInput-DMLKmGEc.js +199 -0
  32. package/dist/cdn/SvDateTimePicker-CCbDZNZB.js +816 -0
  33. package/dist/cdn/SvDateTimePicker-sonaH0oh.js +812 -0
  34. package/dist/cdn/SvGridCellEditor-BAPSC7EL.js +550 -0
  35. package/dist/cdn/SvGridCellEditor-BwzmUWtL.js +549 -0
  36. package/dist/cdn/date-format-BNii4zeD.js +1393 -0
  37. package/dist/cdn/date-format-BnnHlqGw.js +1395 -0
  38. package/dist/cdn/{editor-registry-CiI0xlKg.js → editor-registry-BhgE3S84.js} +51 -25
  39. package/dist/cdn/{src-D1lXwq1l.js → src-DGo7IHug.js} +5730 -5198
  40. package/dist/cdn/{src-C9Hihx1W.js → src-skQN5eqh.js} +7216 -6684
  41. package/dist/cdn/svgrid.js +14 -12
  42. package/dist/cdn/svgrid.svelte-external.js +14 -12
  43. package/dist/cell-values.d.ts +16 -0
  44. package/dist/cell-values.js +28 -0
  45. package/dist/clipboard.d.ts +22 -0
  46. package/dist/clipboard.js +405 -27
  47. package/dist/editing.js +12 -7
  48. package/dist/editors/cell-editors.d.ts +11 -0
  49. package/dist/editors/cell-editors.js +18 -1
  50. package/dist/grid-messages.d.ts +15 -0
  51. package/dist/grid-messages.js +15 -0
  52. package/dist/index.d.ts +4 -0
  53. package/dist/index.js +4 -0
  54. package/dist/list-option.d.ts +6 -0
  55. package/dist/menus.js +40 -7
  56. package/dist/sanitize-html.d.ts +37 -0
  57. package/dist/sanitize-html.js +234 -0
  58. package/dist/selection-bar-view.svelte.d.ts +25 -0
  59. package/dist/selection-bar-view.svelte.js +13 -0
  60. package/dist/selection.d.ts +2 -1
  61. package/dist/selection.js +129 -13
  62. package/package.json +1 -1
  63. package/src/GridMenus.svelte +184 -6
  64. package/src/SvAutoComplete.svelte +4 -0
  65. package/src/SvComboBox.svelte +5 -1
  66. package/src/SvDateRangeInput.svelte +25 -3
  67. package/src/SvDropDownList.svelte +4 -0
  68. package/src/SvForm.svelte +8 -8
  69. package/src/SvGrid.controller.svelte.ts +251 -61
  70. package/src/SvGrid.css +221 -0
  71. package/src/SvGrid.helpers.ts +87 -0
  72. package/src/SvGrid.svelte +145 -0
  73. package/src/SvGrid.types.ts +112 -0
  74. package/src/SvGridCellEditor.svelte +99 -17
  75. package/src/SvGridSelect.svelte +6 -2
  76. package/src/SvListBox.svelte +8 -0
  77. package/src/SvMultiSelect.svelte +6 -2
  78. package/src/SvNumberInput.svelte +4 -0
  79. package/src/SvRichCell.svelte +125 -0
  80. package/src/SvTextArea.svelte +5 -1
  81. package/src/SvTreeSelect.svelte +6 -2
  82. package/src/builtin-editors.test.ts +18 -1
  83. package/src/builtin-editors.ts +19 -3
  84. package/src/cell-values.ts +30 -0
  85. package/src/clipboard.test.ts +93 -20
  86. package/src/clipboard.ts +433 -33
  87. package/src/date-string-column.test.ts +87 -0
  88. package/src/editing.test.ts +25 -0
  89. package/src/editing.ts +12 -9
  90. package/src/editor-block-prop.test.ts +132 -0
  91. package/src/editors/cell-editors.ts +27 -1
  92. package/src/grid-messages.ts +34 -0
  93. package/src/index.ts +13 -0
  94. package/src/list-option.ts +6 -0
  95. package/src/menus.test.ts +43 -0
  96. package/src/menus.ts +37 -7
  97. package/src/move-cells.test.ts +850 -0
  98. package/src/number-editor-literal.test.ts +84 -0
  99. package/src/number-editor.grid.test.ts +160 -0
  100. package/src/sanitize-html.test.ts +182 -0
  101. package/src/sanitize-html.ts +235 -0
  102. package/src/selection-bar-view.svelte.ts +36 -0
  103. package/src/selection.test.ts +157 -0
  104. package/src/selection.ts +131 -13
  105. package/src/svgrid.filter-menu-listbox.svelte.test.ts +56 -10
  106. package/src/svgrid.in-suggest-snapshot.svelte.test.ts +91 -0
  107. package/src/svgrid.menu-scroll-close.test.ts +204 -0
  108. package/src/svgrid.selection-bar-seam.test.ts +185 -0
  109. package/src/svgrid.upsell-license.test.ts +117 -0
  110. package/dist/cdn/GridMenus-BuoBPqxx.js +0 -493
  111. package/dist/cdn/GridMenus-n4llxoOI.js +0 -494
  112. package/dist/cdn/SvDateTimePicker-CHnUkWcH.js +0 -2206
  113. package/dist/cdn/SvDateTimePicker-CRQH_U7E.js +0 -2201
  114. package/dist/cdn/SvGridCellEditor-BGUCzuPB.js +0 -523
  115. 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
- const allRowsBeforePagination = $derived.by(function allRowsBeforePagination_d() {
1026
- // Depend on dataStateVersion (row-model-affecting store changes) NOT
1027
- // gridStateVersion - so moving the active cell / selection does not force
1028
- // this O(rows) pipeline to re-run. Filter-input state (globalFilter etc.)
1029
- // and internalData are read below and tracked as their own dependencies.
1030
- dataStateVersion;
1031
- // Touch internalData + internalColumns so the row model re-derives when
1032
- // the consumer replaces the data array (e.g. via a "Reset" button).
1033
- void internalData;
1034
- void internalColumns;
1035
- const rawRows = grid.getRowModel().rows;
1036
- // External-filter mode: the consumer fetched / pre-filtered the rows
1037
- // themselves (server-side data sources). Skip every local filter pass
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(([_, f]) => {
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
- // Range-bucketed facets for numeric / date columns with many values.
2924
+ const scope = applyColumnFilters(grid.getRowModel().rows, columnId);
2833
2925
  const buckets = facetBucketsByColumn.get(columnId);
2834
- if (buckets)
2835
- return buckets.map((b) => b.label);
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 seen = new Set();
2838
- for (const rowData of props.data) {
2839
- seen.add(String(getColumnAccessorValue(rowData, column) ?? ""));
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(seen).sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
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: untrack(() => facetValuesForColumn(columnId)),
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(columnMenuVisibleFacets.map((value) => ({
2901
- value,
2902
- label: value === "" ? "(Blanks)" : value,
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 = facetValuesForColumn(inSuggestFor);
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(inSuggestValues.map((value) => ({
2930
- value,
2931
- label: value === "" ? "(Blanks)" : value,
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.