@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
|
@@ -30,6 +30,7 @@ export type TreeNode = {
|
|
|
30
30
|
* even with no children yet; the renderer loads them on first expand). */
|
|
31
31
|
lazy?: boolean;
|
|
32
32
|
};
|
|
33
|
+
/** A node's checkbox state. `'indeterminate'` means some but not all descendants are checked. */
|
|
33
34
|
export type CheckState = 'checked' | 'indeterminate' | 'unchecked';
|
|
34
35
|
/** A single visible (flattened) tree row. */
|
|
35
36
|
export type TreeRow = {
|
|
@@ -73,6 +74,7 @@ export type TreeConfig = {
|
|
|
73
74
|
/** Filter query: show only matching nodes + their ancestors, auto-expanded. */
|
|
74
75
|
filter?: () => string | undefined;
|
|
75
76
|
};
|
|
77
|
+
/** Build the headless tree model: expansion, selection and checkbox cascading, with no markup. */
|
|
76
78
|
export declare function createTree(config: TreeConfig): {
|
|
77
79
|
/** Internally-managed set of expanded node ids. */
|
|
78
80
|
readonly expanded: Set<string>;
|
|
@@ -113,4 +115,5 @@ export declare function createTree(config: TreeConfig): {
|
|
|
113
115
|
onkeydown: (e: KeyboardEvent) => void;
|
|
114
116
|
};
|
|
115
117
|
};
|
|
118
|
+
/** The headless tree instance returned by {@link createTree}. */
|
|
116
119
|
export type Tree = ReturnType<typeof createTree>;
|
|
@@ -87,6 +87,7 @@ export function sortTreeNodes(nodes, compare) {
|
|
|
87
87
|
.sort(compare)
|
|
88
88
|
.map((n) => (n.children ? { ...n, children: sortTreeNodes(n.children, compare) } : n));
|
|
89
89
|
}
|
|
90
|
+
/** Build the headless tree model: expansion, selection and checkbox cascading, with no markup. */
|
|
90
91
|
export function createTree(config) {
|
|
91
92
|
const nodes = () => config.nodes();
|
|
92
93
|
const checkable = () => config.checkable?.() ?? false;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* - `firstDayOfWeek` is 0-6 (0 = Sunday), matching `Date.getDay()` and Smart's
|
|
10
10
|
* `firstDayOfWeek`.
|
|
11
11
|
*/
|
|
12
|
+
/** Anything the date helpers accept: a `Date`, epoch milliseconds, or a parseable string. */
|
|
12
13
|
export type DateLike = Date | number | string;
|
|
13
14
|
/** Coerce a Date | epoch-ms | parseable string to a Date, or null if invalid. */
|
|
14
15
|
export declare function toDate(value: DateLike | null | undefined): Date | null;
|
|
@@ -63,6 +64,7 @@ export declare function centuryRange(year: number): {
|
|
|
63
64
|
start: number;
|
|
64
65
|
end: number;
|
|
65
66
|
};
|
|
67
|
+
/** One cell of a month grid, including the leading and trailing days from adjacent months. */
|
|
66
68
|
export type MonthMatrixCell = {
|
|
67
69
|
date: Date;
|
|
68
70
|
/** In the displayed month (vs. leading/trailing days of adjacent months). */
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* still selectable). Mirrors Smart's calendar constraint model. Framework-free.
|
|
5
5
|
*/
|
|
6
6
|
import { type DateLike } from './date-core.js';
|
|
7
|
+
/** Which dates a picker allows: bounds, an explicit disabled set, and disabled weekdays. */
|
|
7
8
|
export type RestrictOptions = {
|
|
8
9
|
min?: DateLike | null;
|
|
9
10
|
max?: DateLike | null;
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* These functions are DST-aware and take no ambient "now" (safe for the model /
|
|
12
12
|
* headless contexts that forbid `Date.now()`).
|
|
13
13
|
*/
|
|
14
|
+
/** A wall-clock time broken into fields, as it reads in a specific time zone. */
|
|
14
15
|
export type ZoneParts = {
|
|
15
16
|
year: number;
|
|
16
17
|
month: number;
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* painting; all the surgery lives here and is unit-tested without a browser.
|
|
17
17
|
*/
|
|
18
18
|
import { type DockNode, type DockTabs, type DockPane, type DockZone, type IdGen } from './dock-model.js';
|
|
19
|
+
/** Which edge a pane docks against when dropped. */
|
|
19
20
|
export type DockSide = Exclude<DockZone, 'center'>;
|
|
20
21
|
/** A floating window: one tabs leaf shown in a movable/resizable frame. */
|
|
21
22
|
export type FloatWindow = {
|
|
@@ -40,6 +41,7 @@ export type AutoHideEntry = {
|
|
|
40
41
|
/** Fly-out panel size in px along the reveal axis. */
|
|
41
42
|
size: number;
|
|
42
43
|
};
|
|
44
|
+
/** The whole dock manager: its layout tree plus floating and pinned panes. */
|
|
43
45
|
export type DockManagerState = {
|
|
44
46
|
main: DockNode | null;
|
|
45
47
|
floating: FloatWindow[];
|
|
@@ -57,6 +59,7 @@ export type PaneLocation = {
|
|
|
57
59
|
kind: 'autoHide';
|
|
58
60
|
entryId: string;
|
|
59
61
|
};
|
|
62
|
+
/** Find a pane in the layout tree by id, returning it with its parent for mutation. */
|
|
60
63
|
export declare function locatePane(state: DockManagerState, paneId: string): PaneLocation | null;
|
|
61
64
|
/** Which surface holds a tabs leaf: 'main', a window id, or null. */
|
|
62
65
|
export declare function surfaceOfTabs(state: DockManagerState, tabsId: string): 'main' | string | null;
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* painting; all the surgery lives here and is unit-tested without a browser.
|
|
17
17
|
*/
|
|
18
18
|
import { tabs, dockInto, removePane as removePaneFromTree, removeLeaf, dockLeafToEdge, reorderPane, findTabsWithPane, allPaneIds, } from './dock-model.js';
|
|
19
|
+
/** Find a pane in the layout tree by id, returning it with its parent for mutation. */
|
|
19
20
|
export function locatePane(state, paneId) {
|
|
20
21
|
if (state.main && findTabsWithPane(state.main, paneId))
|
|
21
22
|
return { kind: 'main' };
|
package/dist/dock-model.d.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* the drag gestures; all the tree surgery lives here so it is unit-testable
|
|
13
13
|
* without a browser. Modelled on Smart's `smart-layout` group/item structure.
|
|
14
14
|
*/
|
|
15
|
+
/** One dockable pane: its id, title, and the state a tab needs to render. */
|
|
15
16
|
export type DockPane = {
|
|
16
17
|
id: string;
|
|
17
18
|
title: string;
|
|
@@ -36,14 +37,19 @@ export type DockGroup = {
|
|
|
36
37
|
/** Size weight per child; parallel to `children`, normalized to sum ~1. */
|
|
37
38
|
sizes: number[];
|
|
38
39
|
};
|
|
40
|
+
/** A node in the layout tree - either a split group or a tabbed leaf. */
|
|
39
41
|
export type DockNode = DockGroup | DockTabs;
|
|
40
42
|
/** Where a dragged pane lands relative to a target leaf. */
|
|
41
43
|
export type DockZone = 'left' | 'right' | 'top' | 'bottom' | 'center';
|
|
44
|
+
/** Supplies ids for newly created nodes, so layouts stay deterministic in tests. */
|
|
42
45
|
export type IdGen = () => string;
|
|
46
|
+
/** Build a {@link DockPane}. */
|
|
43
47
|
export declare function pane(id: string, title: string, closable?: boolean): DockPane;
|
|
44
48
|
/** Largest per-pane `minSize` in a leaf (0 if none). Used by splitter clamping. */
|
|
45
49
|
export declare function leafMinSize(node: DockTabs): number;
|
|
50
|
+
/** Build a tabbed leaf holding the given panes. */
|
|
46
51
|
export declare function tabs(genId: IdGen, panes: DockPane[], active?: number): DockTabs;
|
|
52
|
+
/** Build a split group: panes or nested groups laid out in a row or column. */
|
|
47
53
|
export declare function group(genId: IdGen, direction: 'row' | 'column', children: DockNode[], sizes?: number[]): DockGroup;
|
|
48
54
|
/** Return sizes summing to 1, padded/trimmed to `count`. Even split when absent. */
|
|
49
55
|
export declare function normalizeSizes(sizes: number[] | undefined, count: number): number[];
|
package/dist/dock-model.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* without a browser. Modelled on Smart's `smart-layout` group/item structure.
|
|
14
14
|
*/
|
|
15
15
|
// ---- construction helpers -------------------------------------------------
|
|
16
|
+
/** Build a {@link DockPane}. */
|
|
16
17
|
export function pane(id, title, closable = true) {
|
|
17
18
|
return { id, title, closable };
|
|
18
19
|
}
|
|
@@ -20,9 +21,11 @@ export function pane(id, title, closable = true) {
|
|
|
20
21
|
export function leafMinSize(node) {
|
|
21
22
|
return node.panes.reduce((m, p) => Math.max(m, p.minSize ?? 0), 0);
|
|
22
23
|
}
|
|
24
|
+
/** Build a tabbed leaf holding the given panes. */
|
|
23
25
|
export function tabs(genId, panes, active = 0) {
|
|
24
26
|
return { type: 'tabs', id: genId(), panes, active: clampIndex(active, panes.length) };
|
|
25
27
|
}
|
|
28
|
+
/** Build a split group: panes or nested groups laid out in a row or column. */
|
|
26
29
|
export function group(genId, direction, children, sizes) {
|
|
27
30
|
return { type: 'group', id: genId(), direction, children, sizes: normalizeSizes(sizes, children.length) };
|
|
28
31
|
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Framework-free (no Svelte, no DOM) so it can be unit-tested and reused.
|
|
11
11
|
*/
|
|
12
|
+
/** How much room a cell editor asks for, so the grid can size its overlay. */
|
|
12
13
|
export type EditorSize = 'sm' | 'md' | 'lg';
|
|
13
14
|
/**
|
|
14
15
|
* A small action button rendered inside a framed field (via `SvField`'s
|
|
@@ -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.
|
package/dist/group-display.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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/list-option.d.ts
CHANGED
|
@@ -48,6 +48,7 @@ export type VirtualListRow = {
|
|
|
48
48
|
opt: IndexedOption;
|
|
49
49
|
size: number;
|
|
50
50
|
};
|
|
51
|
+
/** A flattened option list plus its measurements, for virtualizing long dropdowns. */
|
|
51
52
|
export type FlatVirtualModel = {
|
|
52
53
|
/** Group headings + options in render order, each with its px height. */
|
|
53
54
|
entries: VirtualListRow[];
|
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
|
|
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");
|
package/dist/positioning.d.ts
CHANGED
|
@@ -37,6 +37,7 @@ export type Viewport = {
|
|
|
37
37
|
width: number;
|
|
38
38
|
height: number;
|
|
39
39
|
};
|
|
40
|
+
/** Where to put a floating element: its preferred side, offsets, and collision behaviour. */
|
|
40
41
|
export type ComputePositionOptions = {
|
|
41
42
|
/** Preferred placement. Default `'bottom-start'`. */
|
|
42
43
|
placement?: Placement;
|
|
@@ -61,6 +62,7 @@ export type ComputePositionOptions = {
|
|
|
61
62
|
/** Floor for the available main-axis size returned by `size`. Default 0. */
|
|
62
63
|
minMainAxis?: number;
|
|
63
64
|
};
|
|
65
|
+
/** The resolved position, including the side actually used after collision handling. */
|
|
64
66
|
export type ComputePositionResult = {
|
|
65
67
|
/** Floating left, in viewport (fixed-position) coordinates. */
|
|
66
68
|
x: number;
|
package/dist/row-resize.d.ts
CHANGED
|
@@ -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;
|
package/dist/row-resize.js
CHANGED
|
@@ -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/scheduler-ical.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* LOCATION. No `Date.now()` in the core - `toICS` accepts a stamp.
|
|
7
7
|
*/
|
|
8
8
|
import type { RecurrenceRule } from './recurrence.js';
|
|
9
|
+
/** One VEVENT: the fields SvGrid reads from and writes to an iCalendar feed. */
|
|
9
10
|
export type ICalEvent = {
|
|
10
11
|
uid?: string;
|
|
11
12
|
title: string;
|
|
@@ -171,6 +171,7 @@ export type DayLayout<TData = unknown> = {
|
|
|
171
171
|
events: PositionedEvent<TData>[];
|
|
172
172
|
overflows: OverflowMarker<TData>[];
|
|
173
173
|
};
|
|
174
|
+
/** Tuning for event layout: how overlapping events share horizontal space. */
|
|
174
175
|
export type LayoutOptions = {
|
|
175
176
|
dayStartHour?: number;
|
|
176
177
|
dayEndHour?: number;
|
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 ||
|
package/dist/spreadsheet.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
*
|
package/dist/spreadsheet.js
CHANGED
|
@@ -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
|
-
*
|
|
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/dist/summaries.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// summaries handlers extracted from the controller. Imperative event handlers
|
|
2
|
+
// reading/writing controller state via the `ctx` handle; the reactive core
|
|
3
|
+
// ($state/$derived/$effect) stays in the controller.
|
|
4
|
+
import { applyGroupAggregate, } from "./index.js";
|
|
1
5
|
import "./sv-grid-scrollbar.js";
|
|
2
6
|
import { getCellKey, } from "./SvGrid.helpers.js";
|
|
3
7
|
import { formatSummaryNumeric, } from "./cell-values.js";
|
|
@@ -25,6 +29,24 @@ export function createSummaries(ctx) {
|
|
|
25
29
|
const fieldFn = def.fieldFn;
|
|
26
30
|
const field = def.field;
|
|
27
31
|
const columnId = column.id;
|
|
32
|
+
// A column that declares its own `summary` opts out of the default
|
|
33
|
+
// sum/count below. Only that column pays for the aggregator dispatch, so
|
|
34
|
+
// a grid that declares none keeps the original hot loop exactly as it was.
|
|
35
|
+
const declared = def.summary;
|
|
36
|
+
if (declared !== undefined) {
|
|
37
|
+
if (declared === false) {
|
|
38
|
+
summary[columnId] = "";
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const value = applyGroupAggregate(declared, columnId, rows);
|
|
42
|
+
summary[columnId] =
|
|
43
|
+
typeof value === "number" && Number.isFinite(value)
|
|
44
|
+
? formatSummaryNumeric(column, value)
|
|
45
|
+
: value == null
|
|
46
|
+
? ""
|
|
47
|
+
: String(value);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
28
50
|
let numericSum = 0;
|
|
29
51
|
let numericCount = 0;
|
|
30
52
|
for (let i = 0; i < rowCount; i += 1) {
|
|
@@ -3,6 +3,7 @@ import type { FilterOperator, Props } from './SvGrid.types.js';
|
|
|
3
3
|
import type { GridExportOptions, GridClipboardOptions } from './export-format.js';
|
|
4
4
|
import type { ChartSpec, ChartType } from './chart.js';
|
|
5
5
|
import type { GridPredicateExpr } from './filtering/predicate-expr.js';
|
|
6
|
+
/** The comparisons a column filter can use, as shown in the filter menu. */
|
|
6
7
|
export type SvGridFilterOperator = FilterOperator;
|
|
7
8
|
/**
|
|
8
9
|
* A serializable snapshot of everything that makes up the current "view":
|
|
@@ -80,6 +81,7 @@ export type SvGridTransaction<TData> = {
|
|
|
80
81
|
update?: ReadonlyArray<TData>;
|
|
81
82
|
remove?: ReadonlyArray<TData | string>;
|
|
82
83
|
};
|
|
84
|
+
/** What a batched `applyTransaction` did: how many rows were added, updated and removed. */
|
|
83
85
|
export type SvGridTransactionResult = {
|
|
84
86
|
added: number;
|
|
85
87
|
updated: number;
|
|
@@ -458,6 +460,7 @@ export type SvGridApi<TFeatures extends TableFeatures, TData extends RowData> =
|
|
|
458
460
|
/** Force a recompute of the row pipeline + a re-render. */
|
|
459
461
|
refresh(): void;
|
|
460
462
|
};
|
|
463
|
+
/** The props `<SvGrid>` accepts. See the SvGrid reference for the full list with defaults. */
|
|
461
464
|
export type SvGridWrapperProps<TFeatures extends TableFeatures, TData extends RowData> = {
|
|
462
465
|
data: ReadonlyArray<TData>;
|
|
463
466
|
columns: Array<ColumnDef<TFeatures, TData>>;
|
|
@@ -505,6 +508,8 @@ export type SvGridWrapperProps<TFeatures extends TableFeatures, TData extends Ro
|
|
|
505
508
|
enableCellSelection?: boolean;
|
|
506
509
|
enableInlineEditing?: boolean;
|
|
507
510
|
enableRowSummaries?: boolean;
|
|
511
|
+
/** Shortcut alias for `enableRowSummaries`; wins when both are set. */
|
|
512
|
+
summary?: boolean;
|
|
508
513
|
/** Receives the imperative grid API when the component is ready. */
|
|
509
514
|
onApiReady?: (api: SvGridApi<TFeatures, TData>) => void;
|
|
510
515
|
};
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* Mount a single <SvToaster /> once near the app root to render the queue.
|
|
15
15
|
*/
|
|
16
16
|
import type { Snippet } from 'svelte';
|
|
17
|
+
/** A toast's severity, which selects its colour and icon. */
|
|
17
18
|
export type ToastVariant = 'info' | 'success' | 'warning' | 'error';
|
|
18
19
|
/** A button rendered inside a toast (a primary `action` or a secondary `cancel`). */
|
|
19
20
|
export type ToastAction = {
|
|
@@ -23,6 +24,7 @@ export type ToastAction = {
|
|
|
23
24
|
/** Keep the toast open after the click (default: dismiss it). */
|
|
24
25
|
keepOpen?: boolean;
|
|
25
26
|
};
|
|
27
|
+
/** Options for one toast: its variant, how long it stays, and any action button. */
|
|
26
28
|
export type ToastOptions = {
|
|
27
29
|
variant?: ToastVariant;
|
|
28
30
|
/** Auto-dismiss after N ms. `0` = sticky (dismiss manually). Default 4000. */
|
|
@@ -38,6 +40,7 @@ export type ToastOptions = {
|
|
|
38
40
|
/** Render a fully custom toast body instead of the icon + title + message. */
|
|
39
41
|
render?: Snippet<[Toast]>;
|
|
40
42
|
};
|
|
43
|
+
/** A live toast: its options plus the id needed to dismiss it. */
|
|
41
44
|
export type Toast = {
|
|
42
45
|
id: number;
|
|
43
46
|
message: string;
|
|
@@ -58,6 +61,7 @@ export type PromiseMessages<T> = {
|
|
|
58
61
|
error: string | ((error: unknown) => string);
|
|
59
62
|
};
|
|
60
63
|
type VariantOptions = Omit<ToastOptions, 'variant'>;
|
|
64
|
+
/** The callable toast API - `toast(msg)` plus `.success` / `.error` / friends. */
|
|
61
65
|
export type ToastFn = {
|
|
62
66
|
(message: string, options?: ToastOptions): number;
|
|
63
67
|
info: (message: string, options?: VariantOptions) => number;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Development-time configuration checks.
|
|
3
|
+
*
|
|
4
|
+
* The grid used to fail silently on the most common mistakes. A misspelled
|
|
5
|
+
* `field` rendered a column of empty cells with nothing on the console; a
|
|
6
|
+
* `pageSize` with no pagination was quietly ignored; a column marked
|
|
7
|
+
* `sortable` with no sorting enabled just did not sort. Each of those costs
|
|
8
|
+
* someone an afternoon, and none of them were detectable from the outside.
|
|
9
|
+
*
|
|
10
|
+
* This module is the check. It is pure and returns messages rather than
|
|
11
|
+
* printing them, so it can be unit-tested; the controller runs it in an effect
|
|
12
|
+
* and prints each message once, in dev builds only.
|
|
13
|
+
*
|
|
14
|
+
* Rules for anything added here:
|
|
15
|
+
* - Never fire on valid config. A false positive teaches people to ignore
|
|
16
|
+
* warnings, which is worse than staying silent.
|
|
17
|
+
* - Say what happened, what it means, and how to fix it - the house style set
|
|
18
|
+
* by the one pre-existing warning in server-data-source.ts.
|
|
19
|
+
* - Stay O(columns), or O(columns x a fixed row sample). This runs on data
|
|
20
|
+
* changes, and must not scale with row count.
|
|
21
|
+
*/
|
|
22
|
+
import type { ColumnDef, RowData, TableFeatures } from './core.js';
|
|
23
|
+
export type ValidateInput<TFeatures extends TableFeatures, TData extends RowData> = {
|
|
24
|
+
data: ReadonlyArray<TData>;
|
|
25
|
+
columns: ReadonlyArray<ColumnDef<TFeatures, TData>>;
|
|
26
|
+
/** The resolved feature set, after the boolean shortcuts have injected theirs. */
|
|
27
|
+
features?: Record<string, unknown>;
|
|
28
|
+
sortable?: boolean;
|
|
29
|
+
pageable?: boolean;
|
|
30
|
+
showPagination?: boolean;
|
|
31
|
+
pageSize?: number;
|
|
32
|
+
groupBy?: ReadonlyArray<string>;
|
|
33
|
+
treeData?: {
|
|
34
|
+
parentField?: string;
|
|
35
|
+
idField?: string;
|
|
36
|
+
column?: string;
|
|
37
|
+
};
|
|
38
|
+
initialColumnPinning?: {
|
|
39
|
+
left?: ReadonlyArray<string>;
|
|
40
|
+
right?: ReadonlyArray<string>;
|
|
41
|
+
};
|
|
42
|
+
columnVirtualization?: boolean;
|
|
43
|
+
externalPagination?: boolean;
|
|
44
|
+
rowCount?: number;
|
|
45
|
+
externalSort?: boolean;
|
|
46
|
+
onSortingChange?: unknown;
|
|
47
|
+
externalFilter?: boolean;
|
|
48
|
+
onFiltersChange?: unknown;
|
|
49
|
+
};
|
|
50
|
+
export declare function validateGridConfig<TFeatures extends TableFeatures, TData extends RowData>(input: ValidateInput<TFeatures, TData>): string[];
|