@svgrid/grid 2.6.20 → 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 (110) 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 +14 -8
  5. package/dist/SvGrid.controller.svelte.js +214 -74
  6. package/dist/SvGrid.css +1 -1
  7. package/dist/SvGrid.svelte +112 -57
  8. package/dist/SvGrid.types.d.ts +191 -1
  9. package/dist/ai.d.ts +28 -0
  10. package/dist/ai.js +6 -0
  11. package/dist/cdn/{GridMenus-B0F9iBrG.js → GridMenus-BuoBPqxx.js} +137 -132
  12. package/dist/cdn/GridMenus-n4llxoOI.js +494 -0
  13. package/dist/cdn/column-resize-DsfNXMom.js +102 -0
  14. package/dist/cdn/row-resize-BRcimkUT.js +95 -0
  15. package/dist/cdn/{src-Cd0tearp.js → src-C9Hihx1W.js} +3497 -3487
  16. package/dist/cdn/{src-B1TdiyS8.js → src-D1lXwq1l.js} +8258 -8248
  17. package/dist/cdn/svgrid.js +10 -8
  18. package/dist/cdn/svgrid.svelte-external.js +10 -8
  19. package/dist/cdn/validate-_CDJzgIo.js +75 -0
  20. package/dist/cell-formatting.d.ts +2 -0
  21. package/dist/cell-formatting.js +2 -0
  22. package/dist/chart-export.d.ts +1 -0
  23. package/dist/chart.d.ts +31 -5
  24. package/dist/chart.js +9 -3
  25. package/dist/column-groups.js +1 -1
  26. package/dist/column-resize.d.ts +46 -0
  27. package/dist/column-resize.js +205 -0
  28. package/dist/columns.d.ts +0 -3
  29. package/dist/columns.js +0 -57
  30. package/dist/core.d.ts +216 -4
  31. package/dist/core.js +532 -119
  32. package/dist/createTree.svelte.d.ts +3 -0
  33. package/dist/createTree.svelte.js +1 -0
  34. package/dist/datetime/date-core.d.ts +2 -0
  35. package/dist/datetime/date-restrict.d.ts +1 -0
  36. package/dist/datetime/timezone.d.ts +1 -0
  37. package/dist/dock-manager-model.d.ts +3 -0
  38. package/dist/dock-manager-model.js +1 -0
  39. package/dist/dock-model.d.ts +6 -0
  40. package/dist/dock-model.js +3 -0
  41. package/dist/editor-contract.d.ts +1 -0
  42. package/dist/filtering/excel-filters.js +28 -0
  43. package/dist/group-display.d.ts +1 -1
  44. package/dist/index.d.ts +2 -1
  45. package/dist/index.js +6 -0
  46. package/dist/list-option.d.ts +1 -0
  47. package/dist/menus.js +1 -1
  48. package/dist/positioning.d.ts +2 -0
  49. package/dist/row-resize.d.ts +11 -0
  50. package/dist/row-resize.js +7 -1
  51. package/dist/scheduler-ical.d.ts +1 -0
  52. package/dist/scheduler-model.d.ts +1 -0
  53. package/dist/selection.js +9 -0
  54. package/dist/spreadsheet.d.ts +1 -1
  55. package/dist/spreadsheet.js +1 -1
  56. package/dist/summaries.js +22 -0
  57. package/dist/svgrid-wrapper.types.d.ts +5 -0
  58. package/dist/toast-store.svelte.d.ts +4 -0
  59. package/dist/validate.d.ts +50 -0
  60. package/dist/validate.js +187 -0
  61. package/package.json +4 -1
  62. package/src/GridMenus.svelte +17 -12
  63. package/src/SvGrid.controller.svelte.ts +223 -76
  64. package/src/SvGrid.css +1 -1
  65. package/src/SvGrid.svelte +112 -57
  66. package/src/SvGrid.types.ts +191 -1
  67. package/src/ai.ts +28 -0
  68. package/src/cell-formatting.ts +2 -0
  69. package/src/chart-export.ts +1 -0
  70. package/src/chart.ts +31 -5
  71. package/src/column-groups.ts +1 -1
  72. package/src/column-resize.test.ts +381 -0
  73. package/src/column-resize.ts +227 -0
  74. package/src/columns.test.ts +0 -103
  75. package/src/columns.ts +0 -58
  76. package/src/core.aggregate.test.ts +134 -0
  77. package/src/core.filter.test.ts +156 -0
  78. package/src/core.grouping.test.ts +146 -0
  79. package/src/core.row-shape.test.ts +119 -0
  80. package/src/core.rowmodel-cache.test.ts +121 -0
  81. package/src/core.sort.test.ts +293 -0
  82. package/src/core.ts +723 -119
  83. package/src/createTree.svelte.ts +3 -0
  84. package/src/datetime/date-core.ts +2 -0
  85. package/src/datetime/date-restrict.ts +1 -0
  86. package/src/datetime/timezone.ts +1 -0
  87. package/src/dock-manager-model.ts +3 -0
  88. package/src/dock-model.ts +6 -0
  89. package/src/editor-contract.ts +1 -0
  90. package/src/filtering/excel-filters.ts +30 -0
  91. package/src/filtering/normalize-fast-path.test.ts +104 -0
  92. package/src/group-display.ts +1 -1
  93. package/src/index.ts +12 -1
  94. package/src/list-option.ts +1 -0
  95. package/src/menus.ts +1 -1
  96. package/src/positioning.ts +2 -0
  97. package/src/resize-props.test.ts +361 -0
  98. package/src/row-resize.test.ts +31 -0
  99. package/src/row-resize.ts +21 -3
  100. package/src/scheduler-ical.ts +1 -0
  101. package/src/scheduler-model.ts +1 -0
  102. package/src/selection.ts +9 -0
  103. package/src/spreadsheet.ts +1 -1
  104. package/src/summaries.ts +21 -0
  105. package/src/svgrid-wrapper.types.ts +5 -0
  106. package/src/svgrid.summaries.test.ts +217 -0
  107. package/src/toast-store.svelte.ts +4 -0
  108. package/src/validate.test.ts +207 -0
  109. package/src/validate.ts +269 -0
  110. package/dist/cdn/GridMenus-IHK_l7m6.js +0 -489
package/dist/chart.d.ts CHANGED
@@ -34,8 +34,7 @@ export type SeriesOverlay = 'linear' | `sma:${number}` | `ema:${number}`;
34
34
  /** A texture fill applied in addition to (and on top of) the series color.
35
35
  * Helps colorblind readers distinguish series at a glance. */
36
36
  export type SeriesPattern = 'solid' | 'stripe' | 'crosshatch' | 'dots' | 'diagonal';
37
- /** Cycle used when `ChartSpec.patternFallback` is true and a series has no
38
- * explicit `pattern` set. Skips `'solid'` so every series gets a texture. */
37
+ /** One plotted series: its label, its values (one per category), and how to draw it. */
39
38
  export type ChartSeries = {
40
39
  label: string;
41
40
  values: number[];
@@ -93,6 +92,12 @@ export type ChartReferenceLine = {
93
92
  color?: string;
94
93
  dashed?: boolean;
95
94
  };
95
+ /**
96
+ * What to plot - the input you build and hand to a chart. Categories are the
97
+ * x-axis labels and every series supplies one value per category.
98
+ *
99
+ * {@link buildChart} turns this into a {@link ChartGeometry} for rendering.
100
+ */
96
101
  export type ChartSpec = {
97
102
  /** Default type for series that don't set their own `type`. */
98
103
  type: ChartType;
@@ -362,6 +367,7 @@ export type ChartHeatmapCell = {
362
367
  rowLabel: string;
363
368
  colLabel: string;
364
369
  };
370
+ /** A computed bar rectangle in SVG coordinates. Output of {@link buildChart}, not an input. */
365
371
  export type ChartBar = {
366
372
  x: number;
367
373
  y: number;
@@ -374,6 +380,7 @@ export type ChartBar = {
374
380
  series: string;
375
381
  value: number;
376
382
  };
383
+ /** One computed point on a line, with whether the series has a value there. */
377
384
  export type ChartLinePoint = {
378
385
  x: number;
379
386
  y: number;
@@ -382,6 +389,7 @@ export type ChartLinePoint = {
382
389
  /** False for null / NaN values - the line breaks (gap), no dot is drawn. */
383
390
  defined: boolean;
384
391
  };
392
+ /** A computed line series: its points and the path drawn through them. */
385
393
  export type ChartLine = {
386
394
  path: string;
387
395
  areaPath: string;
@@ -392,6 +400,7 @@ export type ChartLine = {
392
400
  * series, when both arrays are supplied. Empty otherwise. */
393
401
  bandPath?: string;
394
402
  };
403
+ /** A computed pie slice, as an SVG arc plus its label placement. */
395
404
  export type ChartPieSlice = {
396
405
  path: string;
397
406
  color: string;
@@ -402,19 +411,23 @@ export type ChartPieSlice = {
402
411
  cx: number;
403
412
  cy: number;
404
413
  };
414
+ /** A value-axis tick: the number, where it sits vertically, and its label. */
405
415
  export type ChartAxisTick = {
406
416
  value: number;
407
417
  y: number;
408
418
  label: string;
409
419
  };
420
+ /** A category-axis tick: the label and its horizontal position. */
410
421
  export type ChartCategoryTick = {
411
422
  label: string;
412
423
  x: number;
413
424
  };
425
+ /** One legend entry, paired with the series colour it stands for. */
414
426
  export type ChartLegendItem = {
415
427
  label: string;
416
428
  color: string;
417
429
  };
430
+ /** A computed reference line (target, average, threshold) at its plotted height. */
418
431
  export type ChartRefLineGeo = {
419
432
  y: number;
420
433
  label: string;
@@ -428,6 +441,7 @@ export type ChartRefLineGeoV = {
428
441
  color: string;
429
442
  dashed: boolean;
430
443
  };
444
+ /** A computed scatter point in SVG coordinates. */
431
445
  export type ChartScatterDot = {
432
446
  cx: number;
433
447
  cy: number;
@@ -438,6 +452,11 @@ export type ChartScatterDot = {
438
452
  x: number;
439
453
  y: number;
440
454
  };
455
+ /**
456
+ * Everything needed to render a chart: the plot rectangle plus every mark
457
+ * already positioned in SVG coordinates. Produced by {@link buildChart} from a
458
+ * {@link ChartSpec}, so a renderer does no maths of its own.
459
+ */
441
460
  export type ChartGeometry = {
442
461
  type: ChartType;
443
462
  width: number;
@@ -542,7 +561,9 @@ export type ChartGeometry = {
542
561
  /** Sankey links (type === 'sankey'). */
543
562
  sankeyLinks: ChartSankeyLink[];
544
563
  };
564
+ /** Series colours used when a {@link ChartSeries} sets none, in order. */
545
565
  export declare const DEFAULT_PALETTE: string[];
566
+ /** An axis range rounded to human-friendly bounds and tick spacing. */
546
567
  export type NiceScale = {
547
568
  min: number;
548
569
  max: number;
@@ -559,9 +580,6 @@ export declare function pickContrastText(bgHex: string): string;
559
580
  * the smallest that covers the top, then enumerate decade boundaries. Used
560
581
  * by log-scale axes (yScale: 'log'). */
561
582
  export declare function niceLogScale(min: number, max: number): NiceScale;
562
- /** Ordinary least-squares regression on (i, values[i]) pairs (i = x index).
563
- * Returns the fitted value at each x index, or NaN where the source value
564
- * was non-finite. */
565
583
  /** Build an SVG path from a list of (x,y) pairs, optionally smoothed via
566
584
  * monotone cubic interpolation (preserves local extrema - no overshoots).
567
585
  * Breaks the path at `defined === false` gaps. */
@@ -570,6 +588,9 @@ export declare function buildLinePath(pts: Array<{
570
588
  y: number;
571
589
  defined: boolean;
572
590
  }>, smooth: boolean): string;
591
+ /** Ordinary least-squares regression on (i, values[i]) pairs (i = x index).
592
+ * Returns the fitted value at each x index, or NaN where the source value
593
+ * was non-finite. */
573
594
  export declare function linearTrend(values: number[]): number[];
574
595
  /** Simple moving average over a window of `period` values. Window centres
575
596
  * trail to the right (typical for time-series). NaN for points before the
@@ -590,6 +611,11 @@ export type ChartValueFormat = 'number' | 'currency' | 'percent' | 'compact';
590
611
  * `'compact'` = the plain compact form.
591
612
  */
592
613
  export declare function formatChartValue(n: number, format?: ChartValueFormat): string;
614
+ /**
615
+ * Lay out a {@link ChartSpec} into renderable {@link ChartGeometry} - scales,
616
+ * ticks, and the position of every bar, line, slice and dot. Pure: no DOM, so
617
+ * it runs during SSR and can be unit-tested directly.
618
+ */
593
619
  export declare function buildChart(spec: ChartSpec, theme?: 'light' | 'dark'): ChartGeometry;
594
620
  /**
595
621
  * Aggregate flat rows into a chart spec. Group by a category field, reduce a
package/dist/chart.js CHANGED
@@ -1,3 +1,4 @@
1
+ /** Series colours used when a {@link ChartSeries} sets none, in order. */
1
2
  export const DEFAULT_PALETTE = [
2
3
  '#2563eb',
3
4
  '#16a34a',
@@ -112,9 +113,6 @@ function project(value, min, max, isLog) {
112
113
  return (value - min) / (max - min);
113
114
  }
114
115
  // ---- Overlay math: trendline + moving averages -----------------------
115
- /** Ordinary least-squares regression on (i, values[i]) pairs (i = x index).
116
- * Returns the fitted value at each x index, or NaN where the source value
117
- * was non-finite. */
118
116
  /** Build an SVG path from a list of (x,y) pairs, optionally smoothed via
119
117
  * monotone cubic interpolation (preserves local extrema - no overshoots).
120
118
  * Breaks the path at `defined === false` gaps. */
@@ -205,6 +203,9 @@ function monotoneCubicPath(pts) {
205
203
  }
206
204
  return path;
207
205
  }
206
+ /** Ordinary least-squares regression on (i, values[i]) pairs (i = x index).
207
+ * Returns the fitted value at each x index, or NaN where the source value
208
+ * was non-finite. */
208
209
  export function linearTrend(values) {
209
210
  let n = 0, sumX = 0, sumY = 0, sumXX = 0, sumXY = 0;
210
211
  for (let i = 0; i < values.length; i += 1) {
@@ -403,6 +404,11 @@ function axisDomain(list, categories, stacked, extra = [], isLog = false) {
403
404
  }
404
405
  return isLog ? niceLogScale(dMin, dMax) : niceScale(dMin, dMax);
405
406
  }
407
+ /**
408
+ * Lay out a {@link ChartSpec} into renderable {@link ChartGeometry} - scales,
409
+ * ticks, and the position of every bar, line, slice and dot. Pure: no DOM, so
410
+ * it runs during SSR and can be unit-tested directly.
411
+ */
406
412
  export function buildChart(spec, theme = 'light') {
407
413
  const width = spec.width ?? 520;
408
414
  const height = spec.height ?? 300;
@@ -1,4 +1,4 @@
1
- // Collapsible column groups (AG-Grid `columnGroupShow`). A column group can
1
+ // Collapsible column groups (a per-column `columnGroupShow` flag). A column group can
2
2
  // carry a collapse toggle: its child columns tagged `columnGroupShow: 'open'`
3
3
  // show only when the group is expanded, `'closed'` show only when collapsed,
4
4
  // and untagged children always show.
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Column-resize Svelte action - drag a header's right edge to widen / narrow
3
+ * the column. Counterpart of `rowResize`, and built the same way for the same
4
+ * reason: `<SvGrid columnResize>` is opt-in, so the code behind it must not sit
5
+ * in the base bundle of every grid that leaves it off. The grid pulls this
6
+ * module through `import()` the first time a consumer turns the prop on.
7
+ *
8
+ * Handles are injected rather than rendered, so nothing about them compiles
9
+ * into `SvGrid.svelte`. A header cell is keyed by `data-svgrid-header-col` -
10
+ * body cells use `data-col-id`, which is a different attribute on purpose, and
11
+ * matching the wrong one finds nothing. One handle goes into every such `th`
12
+ * under `node`, and a MutationObserver keeps up with virtualized columns
13
+ * scrolling in and out.
14
+ *
15
+ * <div use:columnResize={{ getWidth, onResize }}>
16
+ * <SvGrid ... />
17
+ * </div>
18
+ *
19
+ * Visual: a 5px strip on the header's right edge with an accent centre pill,
20
+ * from SvGrid.css. Cursor is `col-resize`.
21
+ */
22
+ export type ColumnResizeOptions = {
23
+ /** Current width of a column, in px. Read when a drag starts. */
24
+ getWidth: (columnId: string) => number;
25
+ /** Called on every animation frame of the drag, and once at the end. */
26
+ onResize: (columnId: string, width: number) => void;
27
+ /** Accessible name for the handle. Defaults to the column id. */
28
+ label?: (columnId: string) => string;
29
+ /**
30
+ * Per-column opt-out, backing `ColumnDef.resizable`. Returning false means no
31
+ * handle is created for that column at all - which is what makes the drag,
32
+ * the arrow keys and the double-click autosize go away together, rather than
33
+ * each needing its own guard.
34
+ */
35
+ canResize?: (columnId: string) => boolean;
36
+ /** Size the column to its content. Bound to a double-click on the handle. */
37
+ onAutosize?: (columnId: string) => void;
38
+ /** Minimum width in px. Default 40. */
39
+ min?: number;
40
+ /** When true, the action removes its handles and ignores events. */
41
+ disabled?: boolean;
42
+ };
43
+ export declare function columnResize(node: HTMLElement, opts: ColumnResizeOptions): {
44
+ update(next: ColumnResizeOptions): void;
45
+ destroy(): void;
46
+ };
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Column-resize Svelte action - drag a header's right edge to widen / narrow
3
+ * the column. Counterpart of `rowResize`, and built the same way for the same
4
+ * reason: `<SvGrid columnResize>` is opt-in, so the code behind it must not sit
5
+ * in the base bundle of every grid that leaves it off. The grid pulls this
6
+ * module through `import()` the first time a consumer turns the prop on.
7
+ *
8
+ * Handles are injected rather than rendered, so nothing about them compiles
9
+ * into `SvGrid.svelte`. A header cell is keyed by `data-svgrid-header-col` -
10
+ * body cells use `data-col-id`, which is a different attribute on purpose, and
11
+ * matching the wrong one finds nothing. One handle goes into every such `th`
12
+ * under `node`, and a MutationObserver keeps up with virtualized columns
13
+ * scrolling in and out.
14
+ *
15
+ * <div use:columnResize={{ getWidth, onResize }}>
16
+ * <SvGrid ... />
17
+ * </div>
18
+ *
19
+ * Visual: a 5px strip on the header's right edge with an accent centre pill,
20
+ * from SvGrid.css. Cursor is `col-resize`.
21
+ */
22
+ const HANDLE_CLASS = 'sv-grid-resize-handle';
23
+ export function columnResize(node, opts) {
24
+ let current = opts;
25
+ let drag = null;
26
+ // Coalesce onto the animation frame: pointermove fires many times per frame
27
+ // and each width write triggers a full recompute of the column-layout
28
+ // pipeline. Without this the grid stutters for the whole drag.
29
+ let raf = null;
30
+ let pendingWidth = 0;
31
+ const minWidth = () => current.min ?? 40;
32
+ function columnIdOf(el) {
33
+ return el.closest('[data-svgrid-header-col]')?.dataset.svgridHeaderCol ?? null;
34
+ }
35
+ function commit() {
36
+ if (!drag)
37
+ return;
38
+ current.onResize(drag.columnId, pendingWidth);
39
+ }
40
+ function onPointerMove(e) {
41
+ if (!drag)
42
+ return;
43
+ pendingWidth = Math.max(minWidth(), drag.startWidth + (e.clientX - drag.startX));
44
+ if (raf !== null)
45
+ return;
46
+ raf = requestAnimationFrame(() => {
47
+ raf = null;
48
+ commit();
49
+ });
50
+ }
51
+ function onPointerUp(e) {
52
+ if (!drag)
53
+ return;
54
+ if (raf !== null) {
55
+ cancelAnimationFrame(raf);
56
+ raf = null;
57
+ }
58
+ // Commit the final width even if the last frame was cancelled mid-flight.
59
+ pendingWidth = Math.max(minWidth(), drag.startWidth + (e.clientX - drag.startX));
60
+ commit();
61
+ drag.handle.classList.remove('is-resizing');
62
+ syncAria(drag.handle, drag.columnId);
63
+ try {
64
+ drag.handle.releasePointerCapture(e.pointerId);
65
+ }
66
+ catch {
67
+ /* release is best-effort */
68
+ }
69
+ drag = null;
70
+ window.removeEventListener('pointermove', onPointerMove);
71
+ window.removeEventListener('pointerup', onPointerUp);
72
+ window.removeEventListener('pointercancel', onPointerUp);
73
+ document.body.style.cursor = '';
74
+ }
75
+ function onPointerDown(e) {
76
+ if (current.disabled)
77
+ return;
78
+ const t = e.target;
79
+ if (!t?.classList.contains(HANDLE_CLASS))
80
+ return;
81
+ const columnId = columnIdOf(t);
82
+ if (!columnId || current.canResize?.(columnId) === false)
83
+ return;
84
+ e.preventDefault();
85
+ e.stopPropagation();
86
+ t.classList.add('is-resizing');
87
+ pendingWidth = current.getWidth(columnId);
88
+ drag = { columnId, startX: e.clientX, startWidth: pendingWidth, handle: t };
89
+ try {
90
+ t.setPointerCapture(e.pointerId);
91
+ }
92
+ catch {
93
+ /* capture is best-effort */
94
+ }
95
+ document.body.style.cursor = 'col-resize';
96
+ window.addEventListener('pointermove', onPointerMove);
97
+ window.addEventListener('pointerup', onPointerUp);
98
+ window.addEventListener('pointercancel', onPointerUp);
99
+ }
100
+ /**
101
+ * Keyboard resize (#79). The handle is a focusable `role="separator"` acting
102
+ * as a splitter, so it has to answer the arrow keys. Left/Right shrink/grow,
103
+ * Shift gives a 1px step - the same contract `rowResize` uses vertically.
104
+ */
105
+ function onKeyDown(e) {
106
+ if (current.disabled)
107
+ return;
108
+ const t = e.target;
109
+ if (!t?.classList.contains(HANDLE_CLASS))
110
+ return;
111
+ const step = e.shiftKey ? 1 : 10;
112
+ let delta = 0;
113
+ if (e.key === 'ArrowLeft')
114
+ delta = -step;
115
+ else if (e.key === 'ArrowRight')
116
+ delta = step;
117
+ else
118
+ return;
119
+ const columnId = columnIdOf(t);
120
+ if (!columnId || current.canResize?.(columnId) === false)
121
+ return;
122
+ e.preventDefault();
123
+ e.stopPropagation();
124
+ const next = Math.max(minWidth(), Math.round(current.getWidth(columnId) + delta));
125
+ current.onResize(columnId, next);
126
+ syncAria(t, columnId);
127
+ }
128
+ /** A focusable separator is a widget, so ARIA wants a current value. */
129
+ function syncAria(handle, columnId) {
130
+ const w = Math.round(current.getWidth(columnId));
131
+ handle.setAttribute('aria-valuenow', String(w));
132
+ handle.setAttribute('aria-valuemin', String(minWidth()));
133
+ handle.setAttribute('aria-valuetext', `${w} pixels`);
134
+ }
135
+ /** One handle per header cell. Skips group headers and spacer columns,
136
+ * which have no id to resize. */
137
+ function decorate() {
138
+ if (current.disabled) {
139
+ removeAll();
140
+ return;
141
+ }
142
+ const heads = node.querySelectorAll('th.sv-grid-column[data-svgrid-header-col]');
143
+ for (const th of heads) {
144
+ if (th.classList.contains('sv-grid-column-spacer'))
145
+ continue;
146
+ const columnId = th.dataset.svgridHeaderCol;
147
+ if (!columnId)
148
+ continue;
149
+ if (current.canResize?.(columnId) === false) {
150
+ // The column may have been resizable a moment ago - a re-decorate has
151
+ // to take the handle away, not just stop adding one.
152
+ th.querySelector(`:scope > .${HANDLE_CLASS}`)?.remove();
153
+ continue;
154
+ }
155
+ if (th.querySelector(`:scope > .${HANDLE_CLASS}`))
156
+ continue;
157
+ const handle = document.createElement('div');
158
+ handle.className = HANDLE_CLASS;
159
+ handle.setAttribute('role', 'separator');
160
+ handle.setAttribute('aria-orientation', 'vertical');
161
+ handle.setAttribute('aria-label', `Resize ${current.label?.(columnId) ?? columnId}`);
162
+ handle.tabIndex = 0;
163
+ syncAria(handle, columnId);
164
+ // Double-click the handle to size the column to its content - the
165
+ // spreadsheet gesture. Stop it there so it does not also reach the
166
+ // header, where a double-click would toggle the sort twice.
167
+ handle.addEventListener('dblclick', (ev) => {
168
+ ev.stopPropagation();
169
+ ev.preventDefault();
170
+ if (current.disabled)
171
+ return;
172
+ current.onAutosize?.(columnId);
173
+ syncAria(handle, columnId);
174
+ });
175
+ th.appendChild(handle);
176
+ }
177
+ }
178
+ function removeAll() {
179
+ node.querySelectorAll(`.${HANDLE_CLASS}`).forEach((el) => el.remove());
180
+ }
181
+ node.addEventListener('pointerdown', onPointerDown, { capture: true });
182
+ node.addEventListener('keydown', onKeyDown, { capture: true });
183
+ const observer = new MutationObserver(() => decorate());
184
+ observer.observe(node, { childList: true, subtree: true });
185
+ decorate();
186
+ return {
187
+ update(next) {
188
+ const wasDisabled = current.disabled;
189
+ current = next;
190
+ if (wasDisabled !== current.disabled)
191
+ decorate();
192
+ },
193
+ destroy() {
194
+ node.removeEventListener('pointerdown', onPointerDown, { capture: true });
195
+ node.removeEventListener('keydown', onKeyDown, { capture: true });
196
+ window.removeEventListener('pointermove', onPointerMove);
197
+ window.removeEventListener('pointerup', onPointerUp);
198
+ window.removeEventListener('pointercancel', onPointerUp);
199
+ if (raf !== null)
200
+ cancelAnimationFrame(raf);
201
+ observer.disconnect();
202
+ removeAll();
203
+ },
204
+ };
205
+ }
package/dist/columns.d.ts CHANGED
@@ -20,9 +20,6 @@ export declare function createColumns<TFeatures extends TableFeatures = TableFea
20
20
  toggleGroupInPanel: (columnId: string) => void;
21
21
  getColumnBaseWidth: (columnId: string) => any;
22
22
  getColumnWidth: (columnId: string) => any;
23
- startColumnResize: (event: PointerEvent, columnId: string) => void;
24
- onColumnResizeMove: (event: PointerEvent) => void;
25
- endColumnResize: () => void;
26
23
  measureText: (text: string, font: string) => number;
27
24
  autosizeColumn: (columnId: string) => void;
28
25
  autosizeAllColumns: () => void;
package/dist/columns.js CHANGED
@@ -156,60 +156,6 @@ export function createColumns(ctx) {
156
156
  return fitted;
157
157
  return getColumnBaseWidth(columnId);
158
158
  }
159
- function startColumnResize(event, columnId) {
160
- event.stopPropagation();
161
- event.preventDefault();
162
- ctx.resizingColumnId = columnId;
163
- ctx.resizeStartX = event.clientX;
164
- ctx.resizeStartWidth = getColumnWidth(columnId);
165
- // Capture the pointer so the drag keeps tracking when it leaves the 4px
166
- // handle - without this, touch (and fast mouse) drags drop mid-resize (#59).
167
- try {
168
- event.currentTarget?.setPointerCapture?.(event.pointerId);
169
- }
170
- catch { /* capture is best-effort */ }
171
- document.addEventListener("pointermove", onColumnResizeMove);
172
- document.addEventListener("pointerup", endColumnResize);
173
- document.addEventListener("pointercancel", endColumnResize);
174
- }
175
- function onColumnResizeMove(event) {
176
- if (!ctx.resizingColumnId)
177
- return;
178
- // Coalesce updates onto the animation frame: pointermove can fire many
179
- // times per frame, and each $state mutation triggers a full reactive
180
- // recompute of the column-layout pipeline. Without rAF coalescing the
181
- // grid stutters during the drag.
182
- ctx.resizePendingWidth = Math.max(ctx.MIN_COLUMN_WIDTH, ctx.resizeStartWidth + (event.clientX - ctx.resizeStartX));
183
- if (ctx.resizeRaf !== null)
184
- return;
185
- ctx.resizeRaf = requestAnimationFrame(() => {
186
- ctx.resizeRaf = null;
187
- if (!ctx.resizingColumnId)
188
- return;
189
- ctx.columnWidths = {
190
- ...ctx.columnWidths,
191
- [ctx.resizingColumnId]: ctx.resizePendingWidth,
192
- };
193
- });
194
- }
195
- function endColumnResize() {
196
- if (ctx.resizeRaf !== null) {
197
- cancelAnimationFrame(ctx.resizeRaf);
198
- ctx.resizeRaf = null;
199
- }
200
- if (ctx.resizingColumnId && ctx.resizePendingWidth) {
201
- // Make sure the final width is committed even if the last rAF was
202
- // canceled mid-flight.
203
- ctx.columnWidths = {
204
- ...ctx.columnWidths,
205
- [ctx.resizingColumnId]: ctx.resizePendingWidth,
206
- };
207
- }
208
- ctx.resizingColumnId = null;
209
- document.removeEventListener("pointermove", onColumnResizeMove);
210
- document.removeEventListener("pointerup", endColumnResize);
211
- document.removeEventListener("pointercancel", endColumnResize);
212
- }
213
159
  function measureText(text, font) {
214
160
  if (!text)
215
161
  return 0;
@@ -280,9 +226,6 @@ export function createColumns(ctx) {
280
226
  toggleGroupInPanel,
281
227
  getColumnBaseWidth,
282
228
  getColumnWidth,
283
- startColumnResize,
284
- onColumnResizeMove,
285
- endColumnResize,
286
229
  measureText,
287
230
  autosizeColumn,
288
231
  autosizeAllColumns,