@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.
- package/CHANGELOG.md +62 -0
- package/README.md +22 -0
- package/dist/GridMenus.svelte +17 -12
- package/dist/SvGrid.controller.svelte.d.ts +14 -8
- package/dist/SvGrid.controller.svelte.js +214 -74
- package/dist/SvGrid.css +1 -1
- package/dist/SvGrid.svelte +112 -57
- package/dist/SvGrid.types.d.ts +191 -1
- package/dist/ai.d.ts +28 -0
- package/dist/ai.js +6 -0
- package/dist/cdn/{GridMenus-B0F9iBrG.js → GridMenus-BuoBPqxx.js} +137 -132
- package/dist/cdn/GridMenus-n4llxoOI.js +494 -0
- package/dist/cdn/column-resize-DsfNXMom.js +102 -0
- package/dist/cdn/row-resize-BRcimkUT.js +95 -0
- package/dist/cdn/{src-Cd0tearp.js → src-C9Hihx1W.js} +3497 -3487
- package/dist/cdn/{src-B1TdiyS8.js → src-D1lXwq1l.js} +8258 -8248
- package/dist/cdn/svgrid.js +10 -8
- package/dist/cdn/svgrid.svelte-external.js +10 -8
- package/dist/cdn/validate-_CDJzgIo.js +75 -0
- package/dist/cell-formatting.d.ts +2 -0
- package/dist/cell-formatting.js +2 -0
- package/dist/chart-export.d.ts +1 -0
- package/dist/chart.d.ts +31 -5
- package/dist/chart.js +9 -3
- package/dist/column-groups.js +1 -1
- package/dist/column-resize.d.ts +46 -0
- package/dist/column-resize.js +205 -0
- package/dist/columns.d.ts +0 -3
- package/dist/columns.js +0 -57
- package/dist/core.d.ts +216 -4
- package/dist/core.js +532 -119
- package/dist/createTree.svelte.d.ts +3 -0
- package/dist/createTree.svelte.js +1 -0
- package/dist/datetime/date-core.d.ts +2 -0
- package/dist/datetime/date-restrict.d.ts +1 -0
- package/dist/datetime/timezone.d.ts +1 -0
- package/dist/dock-manager-model.d.ts +3 -0
- package/dist/dock-manager-model.js +1 -0
- package/dist/dock-model.d.ts +6 -0
- package/dist/dock-model.js +3 -0
- package/dist/editor-contract.d.ts +1 -0
- package/dist/filtering/excel-filters.js +28 -0
- package/dist/group-display.d.ts +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +6 -0
- package/dist/list-option.d.ts +1 -0
- package/dist/menus.js +1 -1
- package/dist/positioning.d.ts +2 -0
- package/dist/row-resize.d.ts +11 -0
- package/dist/row-resize.js +7 -1
- package/dist/scheduler-ical.d.ts +1 -0
- package/dist/scheduler-model.d.ts +1 -0
- package/dist/selection.js +9 -0
- package/dist/spreadsheet.d.ts +1 -1
- package/dist/spreadsheet.js +1 -1
- package/dist/summaries.js +22 -0
- package/dist/svgrid-wrapper.types.d.ts +5 -0
- package/dist/toast-store.svelte.d.ts +4 -0
- package/dist/validate.d.ts +50 -0
- package/dist/validate.js +187 -0
- package/package.json +4 -1
- package/src/GridMenus.svelte +17 -12
- package/src/SvGrid.controller.svelte.ts +223 -76
- package/src/SvGrid.css +1 -1
- package/src/SvGrid.svelte +112 -57
- package/src/SvGrid.types.ts +191 -1
- package/src/ai.ts +28 -0
- package/src/cell-formatting.ts +2 -0
- package/src/chart-export.ts +1 -0
- package/src/chart.ts +31 -5
- package/src/column-groups.ts +1 -1
- package/src/column-resize.test.ts +381 -0
- package/src/column-resize.ts +227 -0
- package/src/columns.test.ts +0 -103
- package/src/columns.ts +0 -58
- package/src/core.aggregate.test.ts +134 -0
- package/src/core.filter.test.ts +156 -0
- package/src/core.grouping.test.ts +146 -0
- package/src/core.row-shape.test.ts +119 -0
- package/src/core.rowmodel-cache.test.ts +121 -0
- package/src/core.sort.test.ts +293 -0
- package/src/core.ts +723 -119
- package/src/createTree.svelte.ts +3 -0
- package/src/datetime/date-core.ts +2 -0
- package/src/datetime/date-restrict.ts +1 -0
- package/src/datetime/timezone.ts +1 -0
- package/src/dock-manager-model.ts +3 -0
- package/src/dock-model.ts +6 -0
- package/src/editor-contract.ts +1 -0
- package/src/filtering/excel-filters.ts +30 -0
- package/src/filtering/normalize-fast-path.test.ts +104 -0
- package/src/group-display.ts +1 -1
- package/src/index.ts +12 -1
- package/src/list-option.ts +1 -0
- package/src/menus.ts +1 -1
- package/src/positioning.ts +2 -0
- package/src/resize-props.test.ts +361 -0
- package/src/row-resize.test.ts +31 -0
- package/src/row-resize.ts +21 -3
- package/src/scheduler-ical.ts +1 -0
- package/src/scheduler-model.ts +1 -0
- package/src/selection.ts +9 -0
- package/src/spreadsheet.ts +1 -1
- package/src/summaries.ts +21 -0
- package/src/svgrid-wrapper.types.ts +5 -0
- package/src/svgrid.summaries.test.ts +217 -0
- package/src/toast-store.svelte.ts +4 -0
- package/src/validate.test.ts +207 -0
- package/src/validate.ts +269 -0
- 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
|
-
/**
|
|
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;
|
package/dist/column-groups.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Collapsible column groups (
|
|
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,
|