@svgrid/grid 2.6.21 → 2.6.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +22 -0
  3. package/dist/GridMenus.svelte +17 -12
  4. package/dist/SvGrid.controller.svelte.d.ts +13 -8
  5. package/dist/SvGrid.controller.svelte.js +150 -72
  6. package/dist/SvGrid.css +1 -1
  7. package/dist/SvGrid.svelte +110 -56
  8. package/dist/SvGrid.types.d.ts +41 -1
  9. package/dist/cdn/{GridMenus-BfTAKn84.js → GridMenus-BuoBPqxx.js} +137 -132
  10. package/dist/cdn/GridMenus-n4llxoOI.js +494 -0
  11. package/dist/cdn/column-resize-DsfNXMom.js +102 -0
  12. package/dist/cdn/row-resize-BRcimkUT.js +95 -0
  13. package/dist/cdn/{src-BYq-qyrp.js → src-C9Hihx1W.js} +3456 -3459
  14. package/dist/cdn/{src-DBel9wRZ.js → src-D1lXwq1l.js} +8283 -8286
  15. package/dist/cdn/svgrid.js +10 -8
  16. package/dist/cdn/svgrid.svelte-external.js +10 -8
  17. package/dist/column-groups.js +1 -1
  18. package/dist/column-resize.d.ts +46 -0
  19. package/dist/column-resize.js +205 -0
  20. package/dist/columns.d.ts +0 -3
  21. package/dist/columns.js +0 -57
  22. package/dist/core.d.ts +19 -4
  23. package/dist/core.js +460 -119
  24. package/dist/filtering/excel-filters.js +28 -0
  25. package/dist/group-display.d.ts +1 -1
  26. package/dist/index.d.ts +2 -1
  27. package/dist/index.js +6 -0
  28. package/dist/menus.js +1 -1
  29. package/dist/row-resize.d.ts +11 -0
  30. package/dist/row-resize.js +7 -1
  31. package/dist/selection.js +9 -0
  32. package/dist/spreadsheet.d.ts +1 -1
  33. package/dist/spreadsheet.js +1 -1
  34. package/package.json +1 -1
  35. package/src/GridMenus.svelte +17 -12
  36. package/src/SvGrid.controller.svelte.ts +155 -74
  37. package/src/SvGrid.css +1 -1
  38. package/src/SvGrid.svelte +110 -56
  39. package/src/SvGrid.types.ts +41 -1
  40. package/src/column-groups.ts +1 -1
  41. package/src/column-resize.test.ts +381 -0
  42. package/src/column-resize.ts +227 -0
  43. package/src/columns.test.ts +0 -103
  44. package/src/columns.ts +0 -58
  45. package/src/core.aggregate.test.ts +134 -0
  46. package/src/core.filter.test.ts +156 -0
  47. package/src/core.grouping.test.ts +146 -0
  48. package/src/core.row-shape.test.ts +119 -0
  49. package/src/core.rowmodel-cache.test.ts +121 -0
  50. package/src/core.sort.test.ts +293 -0
  51. package/src/core.ts +516 -119
  52. package/src/filtering/excel-filters.ts +30 -0
  53. package/src/filtering/normalize-fast-path.test.ts +104 -0
  54. package/src/group-display.ts +1 -1
  55. package/src/index.ts +12 -1
  56. package/src/menus.ts +1 -1
  57. package/src/resize-props.test.ts +361 -0
  58. package/src/row-resize.test.ts +31 -0
  59. package/src/row-resize.ts +21 -3
  60. package/src/selection.ts +9 -0
  61. package/src/spreadsheet.ts +1 -1
  62. package/dist/cdn/GridMenus-C3bJd7w8.js +0 -489
@@ -8,11 +8,39 @@ const DIACRITIC_RE = /[̀-ͯ]/g;
8
8
  export function normalizeForFilter(s, locale) {
9
9
  if (!s)
10
10
  return '';
11
+ // Fast path for pure-ASCII input, which is the overwhelming majority of what
12
+ // a grid filters. Both of the expensive steps are provably no-ops there:
13
+ // ASCII is already NFD-normalised, and the combining-marks block this strips
14
+ // (U+0300..U+036F) contains no ASCII. So lowercasing alone is the same
15
+ // answer, and `normalize-fast-path.test.ts` checks that exhaustively over
16
+ // every ASCII code point rather than taking the argument on trust.
17
+ //
18
+ // Worth the branch: this runs once per cell per filter - 100,000 times for a
19
+ // one-column filter over a 100k grid - and a CPU profile of a browser filter
20
+ // put more time in this function than in any other.
21
+ //
22
+ // Note the locale still routes through toLocaleLowerCase even on the fast
23
+ // path: 'I'.toLocaleLowerCase('tr') is 'ı', so ASCII does NOT imply the
24
+ // locale can be ignored.
25
+ if (isAsciiOnly(s)) {
26
+ return locale ? s.toLocaleLowerCase(locale) : s.toLowerCase();
27
+ }
11
28
  const stripped = s.normalize('NFD').replace(DIACRITIC_RE, '');
12
29
  return locale
13
30
  ? stripped.toLocaleLowerCase(locale)
14
31
  : stripped.toLowerCase();
15
32
  }
33
+ /**
34
+ * True when every code unit is ASCII. A plain char-code scan, which is far
35
+ * cheaper than the `normalize()` + regex pass it lets us skip.
36
+ */
37
+ function isAsciiOnly(s) {
38
+ for (let i = 0; i < s.length; i++) {
39
+ if (s.charCodeAt(i) > 127)
40
+ return false;
41
+ }
42
+ return true;
43
+ }
16
44
  /**
17
45
  * Delimiter used to serialise the `in` / `notIn` value list into the single
18
46
  * `value` string that the filter model carries.
@@ -13,7 +13,7 @@
13
13
  * Both are generic over the row / column shape (via small accessor callbacks) so
14
14
  * they are trivially unit-testable and carry no dependency on the grid runtime.
15
15
  */
16
- /** AG-Grid-style grouping display variants. */
16
+ /** Grouping display variants. */
17
17
  export type GroupDisplayType = 'groupRows' | 'singleColumn' | 'multipleColumns';
18
18
  export type GroupFooterOptions<R> = {
19
19
  /** Depth of a row (group rows and their descendants). */
package/dist/index.d.ts CHANGED
@@ -194,6 +194,7 @@ export { chartToSvgString, downloadChartSvg, chartToPngBlob, downloadChartPng, c
194
194
  export { buildSparkline, toSparklineValues, type SparklineConfig, type SparklineType, type SparklineGeometry, } from './sparkline.js';
195
195
  export { spreadsheetLayout, spansToMerges, type SpanColumn, type SpreadsheetActionOptions, type MergeSpec, type CellBorderSpec, type BorderSpec, } from './spreadsheet.js';
196
196
  export { rowResize, type RowResizeOptions } from './row-resize.js';
197
+ export { columnResize, type ColumnResizeOptions } from './column-resize.js';
197
198
  export { rowDropZone, type RowDragEndEvent, type RowDropZoneOptions } from './row-drag.js';
198
199
  export { resolveColumnTypes, inferCellDataType, type CellDataType } from './column-types.js';
199
200
  export { computeColumnGroupMeta, hiddenLeavesForCollapse, type ColumnGroupMeta, type ColumnGroupShow, } from './column-groups.js';
@@ -212,7 +213,7 @@ export { createVirtualizer } from './virtualization/virtualizer.js';
212
213
  export { createSvelteVirtualizer } from './virtualization/svelte-virtualizer.svelte.js';
213
214
  export { createColumnVirtualizer } from './virtualization/column-virtualizer.js';
214
215
  export type { VirtualItem, VirtualizerOptions, VirtualizerState } from './virtualization/types.js';
215
- export type { SvGridApi, SvGridFilterOperator, SvGridWrapperProps } from './svgrid-wrapper.types.js';
216
+ export type { SvGridApi, SvGridFilterOperator, SvGridViewState, SvGridWrapperProps, } from './svgrid-wrapper.types.js';
216
217
  export { parseEditorValue, normalizeEditorOptions, type CellEditorType, type CellEditorOption, } from './editors/cell-editors.js';
217
218
  export { applyExcelFilter, compileExcelFilter, normalizeForFilter, splitInTokens, joinInTokens, trailingInToken, type CompiledExcelFilter, type ExcelFilter, type ExcelFilterOperator, type ExcelFilterOptions, } from './filtering/excel-filters.js';
218
219
  export { ALL_FILTER_OPERATORS, SET_OPERATOR_IDS, VALUELESS_OPERATOR_IDS, RANGE_OPERATOR_IDS, isSetOperator, isValuelessOperator, isRangeOperator, type FilterValueType, } from './filtering/filter-operator-catalogue.js';
package/dist/index.js CHANGED
@@ -230,7 +230,13 @@ export { buildChart, formatChartValue, rowsToChartSpec, niceScale, niceLogScale,
230
230
  export { chartToSvgString, downloadChartSvg, chartToPngBlob, downloadChartPng, chartSpecToCsv, downloadChartCsv, } from './chart-export.js';
231
231
  export { buildSparkline, toSparklineValues, } from './sparkline.js';
232
232
  export { spreadsheetLayout, spansToMerges, } from './spreadsheet.js';
233
+ // The two resize actions, exported as a matched pair. `<SvGrid>` drives both
234
+ // through the `rowResize` / `columnResize` props - both default OFF, both are
235
+ // turned on by setting them true, and the grid `import()`s the module itself so
236
+ // a grid that leaves them off carries neither. Import them directly only for a
237
+ // headless table, which renders its own rows and headers.
233
238
  export { rowResize } from './row-resize.js';
239
+ export { columnResize } from './column-resize.js';
234
240
  export { rowDropZone } from './row-drag.js';
235
241
  export { resolveColumnTypes, inferCellDataType } from './column-types.js';
236
242
  export { computeColumnGroupMeta, hiddenLeavesForCollapse, } from './column-groups.js';
package/dist/menus.js CHANGED
@@ -118,7 +118,7 @@ export function createMenus(ctx) {
118
118
  function openChooseColumns(event) {
119
119
  event.stopPropagation();
120
120
  // Submenu behavior: keep the parent operations menu open and float the
121
- // Choose Columns panel out to the right of it (AG-Grid style). Falls
121
+ // Choose Columns panel out to the right of it, submenu-style. Falls
122
122
  // back to below the item if the popover would clip the viewport.
123
123
  const trigger = event.currentTarget;
124
124
  const menuEl = trigger.closest(".sv-grid-menu");
@@ -36,6 +36,17 @@ export type RowResizeOptions = {
36
36
  max?: number;
37
37
  /** When true, the action removes its strips and ignores events. */
38
38
  disabled?: boolean;
39
+ /**
40
+ * Where the drag strip is anchored.
41
+ * - `'gutter'` (default) - only rows that have a row-header gutter cell get
42
+ * a strip: the built-in row-number column (`showRowNumbers`) or a column
43
+ * tagged `cellClass: 'sv-row-gutter'`. Rows without one are skipped, so on
44
+ * a plain grid the action does nothing.
45
+ * - `'row'` - use the gutter when there is one, otherwise fall back to the
46
+ * row's first body cell. This is what `<SvGrid rowResize>` passes, so the
47
+ * prop works without also asking for a gutter column.
48
+ */
49
+ anchor?: 'gutter' | 'row';
39
50
  };
40
51
  export declare function rowResize(node: HTMLElement, opts: RowResizeOptions): {
41
52
  update(next: RowResizeOptions): void;
@@ -141,7 +141,13 @@ export function rowResize(node, opts) {
141
141
  // A row-header gutter cell: either a user column tagged
142
142
  // `sv-row-gutter`, or the grid's built-in row-number gutter
143
143
  // (`showRowNumbers`), whose cell is `.sv-grid-row-number-cell`.
144
- const gutter = tr.querySelector('.sv-grid-cell.sv-row-gutter, .sv-grid-cell.sv-grid-row-number-cell');
144
+ const gutter = tr.querySelector('.sv-grid-cell.sv-row-gutter, .sv-grid-cell.sv-grid-row-number-cell') ??
145
+ // `anchor: 'row'` - no gutter on this grid, so hang the strip off the
146
+ // first body cell instead. Without this a grid with no row-number
147
+ // column would silently not resize.
148
+ (current.anchor === 'row'
149
+ ? tr.querySelector('.sv-grid-cell')
150
+ : null);
145
151
  if (!gutter)
146
152
  continue;
147
153
  if (gutter.querySelector(`:scope > .${STRIP_CLASS}`))
package/dist/selection.js CHANGED
@@ -225,6 +225,15 @@ export function createSelection(ctx) {
225
225
  * outline wins on overlap). Returns null when the cell is in no range.
226
226
  */
227
227
  function getCellRangeEdges(rowIndex, colIndex) {
228
+ // Bail before `getSelectionRects()`, which allocates an array and resolves
229
+ // every range. This runs once per rendered CELL on every render - about 250
230
+ // times per frame on a default grid - and with nothing selected it did all
231
+ // that work to return null every time. An anchored active range or a
232
+ // committed one is the only way any rect can exist.
233
+ const active = ctx.selectionRange;
234
+ const committed = ctx.selectionRanges;
235
+ if (!committed?.length && !(active?.anchor && active?.focus))
236
+ return null;
228
237
  for (const rect of getSelectionRects()) {
229
238
  if (rowIndex < rect.minRow ||
230
239
  rowIndex > rect.maxRow ||
@@ -63,7 +63,7 @@ export type SpanColumn<TData = Record<string, unknown>> = {
63
63
  /**
64
64
  * Turn declarative per-column `colSpan` / `rowSpan` callbacks into a
65
65
  * `MergeSpec[]` you can hand to `spreadsheetLayout` - so value-driven,
66
- * AG-Grid-style spanning runs on the SAME real colspan/rowspan merge engine
66
+ * Declarative spanning runs on the SAME real colspan/rowspan merge engine
67
67
  * instead of a second code path. Recompute after sort/filter (indexes are
68
68
  * display-row indexes). A common pattern is "merge runs of equal values":
69
69
  *
@@ -22,7 +22,7 @@
22
22
  /**
23
23
  * Turn declarative per-column `colSpan` / `rowSpan` callbacks into a
24
24
  * `MergeSpec[]` you can hand to `spreadsheetLayout` - so value-driven,
25
- * AG-Grid-style spanning runs on the SAME real colspan/rowspan merge engine
25
+ * Declarative spanning runs on the SAME real colspan/rowspan merge engine
26
26
  * instead of a second code path. Recompute after sort/filter (indexes are
27
27
  * display-row indexes). A common pattern is "merge runs of equal values":
28
28
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svgrid/grid",
3
- "version": "2.6.21",
3
+ "version": "2.6.22",
4
4
  "description": "Svelte 5-native data grid and data table. Headless-first engine with virtual scrolling for 100k+ rows, Excel-style filtering, inline editing, grouping, tree data, pivot, and server-side data. Drop-in <SvGrid> component. TypeScript, SSR-ready.",
5
5
  "author": "jQWidgets <boikom@jqwidgets.com>",
6
6
  "license": "MIT",
@@ -450,18 +450,23 @@
450
450
  </button>
451
451
  {/if}
452
452
  <div class="sv-grid-menu-sep"></div>
453
- <button
454
- type="button"
455
- class="sv-grid-menu-item"
456
- role="menuitem"
457
- onclick={() => {
458
- autosizeColumn(menuColumnId);
459
- closeMenus();
460
- }}
461
- >
462
- <span class="sv-grid-header-icon">{@render icon("autosize")}</span> Autosize
463
- this column
464
- </button>
453
+ <!-- `resizable: false` means the width is part of the layout, so the
454
+ menu must not offer to change it either - otherwise removing the
455
+ drag handle just moves the same action one click away. -->
456
+ {#if ctrl.columnResizable(menuColumnId)}
457
+ <button
458
+ type="button"
459
+ class="sv-grid-menu-item"
460
+ role="menuitem"
461
+ onclick={() => {
462
+ autosizeColumn(menuColumnId);
463
+ closeMenus();
464
+ }}
465
+ >
466
+ <span class="sv-grid-header-icon">{@render icon("autosize")}</span> Autosize
467
+ this column
468
+ </button>
469
+ {/if}
465
470
  <button
466
471
  type="button"
467
472
  class="sv-grid-menu-item"