@svgrid/grid 3.0.4 → 3.0.5
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/README.md +4 -4
- package/dist/SvGrid.controller.svelte.d.ts +12 -2
- package/dist/SvGrid.controller.svelte.js +192 -8
- package/dist/SvGrid.css +73 -0
- package/dist/SvGrid.svelte +128 -3
- package/dist/SvGrid.types.d.ts +356 -3
- package/dist/build-api.js +11 -6
- package/dist/cdn/{GridMenus-BL7ZgQvU.js → GridMenus-CID62rlI.js} +11 -11
- package/dist/cdn/{GridMenus-7kbpnnBW.js → GridMenus-DLL-qZeu.js} +11 -11
- package/dist/cdn/server-block-cache-CGoWz-87.js +237 -0
- package/dist/cdn/{src-BKhZ6eXd.js → src-Bklofsad.js} +8818 -8798
- package/dist/cdn/{src-V1uu8iE9.js → src-jcFMkBnN.js} +4282 -4262
- package/dist/cdn/svgrid.js +27 -26
- package/dist/cdn/svgrid.svelte-external.js +27 -26
- package/dist/cdn/validate-C7Rn_Bns.js +76 -0
- package/dist/core.js +36 -13
- package/dist/editing.js +10 -0
- package/dist/gantt-view.svelte.d.ts +24 -0
- package/dist/gantt-view.svelte.js +13 -0
- package/dist/grid-icons.d.ts +2 -2
- package/dist/grid-icons.js +2 -2
- package/dist/grid-messages.d.ts +6 -0
- package/dist/grid-messages.js +5 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +3 -3
- package/dist/row-model.d.ts +133 -0
- package/dist/row-model.js +16 -0
- package/dist/selection.d.ts +2 -1
- package/dist/selection.js +30 -2
- package/dist/server-block-cache.d.ts +214 -0
- package/dist/server-block-cache.js +531 -0
- package/dist/server-data-source.d.ts +240 -6
- package/dist/server-data-source.js +220 -20
- package/dist/server.d.ts +13 -0
- package/dist/server.js +13 -0
- package/dist/validate.d.ts +4 -0
- package/dist/validate.js +21 -3
- package/package.json +6 -1
- package/src/SvGrid.controller.svelte.ts +191 -8
- package/src/SvGrid.css +73 -0
- package/src/SvGrid.svelte +128 -3
- package/src/SvGrid.types.ts +376 -3
- package/src/build-api.ts +11 -4
- package/src/core.rowmodel-cache.test.ts +51 -0
- package/src/core.ts +33 -12
- package/src/editing.ts +8 -0
- package/src/gantt-stub.test.svelte +38 -0
- package/src/gantt-view.svelte.ts +35 -0
- package/src/grid-icons.ts +2 -2
- package/src/grid-messages.ts +12 -0
- package/src/icon-seam.test.ts +2 -4
- package/src/index.ts +36 -10
- package/src/row-model.ts +146 -0
- package/src/selection.test.ts +3 -0
- package/src/selection.ts +32 -2
- package/src/server-block-cache.test.ts +645 -0
- package/src/server-block-cache.ts +677 -0
- package/src/server-data-source.infinite.test.ts +343 -0
- package/src/server-data-source.ts +450 -27
- package/src/server.ts +47 -0
- package/src/svgrid.gantt-seam.test.ts +199 -0
- package/src/svgrid.row-model-prop.svelte.test.ts +352 -0
- package/src/svgrid.row-model-seam.svelte.test.ts +289 -0
- package/src/svgrid.upsell-license.test.ts +7 -4
- package/src/validate.test.ts +29 -0
- package/src/validate.ts +28 -3
- package/dist/SvGroupCell.svelte +0 -141
- package/dist/SvGroupCell.svelte.d.ts +0 -49
- package/dist/SvRowGroupPanel.svelte +0 -186
- package/dist/SvRowGroupPanel.svelte.d.ts +0 -25
- package/dist/cdn/validate-_CDJzgIo.js +0 -75
- package/dist/server-group-model.d.ts +0 -98
- package/dist/server-group-model.js +0 -263
- package/src/SvGroupCell.svelte +0 -141
- package/src/SvRowGroupPanel.svelte +0 -186
- package/src/server-group-model.test.ts +0 -294
- package/src/server-group-model.ts +0 -370
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gantt-view registry - the injection seam for the *Gantt view of the grid*.
|
|
3
|
+
* Setting the `gantt` prop on `<SvGrid>` switches the table for a task table
|
|
4
|
+
* beside a time chart (bars by start / end, a work-breakdown tree, dependency
|
|
5
|
+
* arrows), but the renderer itself ships in `@svgrid/enterprise` (a paid
|
|
6
|
+
* feature). This tiny reactive holder lets enterprise register that renderer at
|
|
7
|
+
* import time; the grid looks it up and mounts it, or shows an upsell
|
|
8
|
+
* placeholder when it is absent. Mirrors the `scheduler-view` / `board-view`
|
|
9
|
+
* pattern.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* // in @svgrid/enterprise, at module load:
|
|
13
|
+
* import { registerGanttView } from '@svgrid/grid'
|
|
14
|
+
* import SvGridGantt from './gantt/SvGridGantt.svelte'
|
|
15
|
+
* registerGanttView(SvGridGantt)
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import type { Component } from 'svelte';
|
|
19
|
+
/** Register the component that renders `gantt` mode. Enterprise calls this. */
|
|
20
|
+
export declare function registerGanttView(component: Component<any>): void;
|
|
21
|
+
/** The registered Gantt renderer, or null when enterprise is not installed. */
|
|
22
|
+
export declare function getGanttView(): Component<any> | null;
|
|
23
|
+
/** Whether a Gantt renderer has been registered. */
|
|
24
|
+
export declare function hasGanttView(): boolean;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
let renderer = $state(null);
|
|
2
|
+
/** Register the component that renders `gantt` mode. Enterprise calls this. */
|
|
3
|
+
export function registerGanttView(component) {
|
|
4
|
+
renderer = component;
|
|
5
|
+
}
|
|
6
|
+
/** The registered Gantt renderer, or null when enterprise is not installed. */
|
|
7
|
+
export function getGanttView() {
|
|
8
|
+
return renderer;
|
|
9
|
+
}
|
|
10
|
+
/** Whether a Gantt renderer has been registered. */
|
|
11
|
+
export function hasGanttView() {
|
|
12
|
+
return renderer != null;
|
|
13
|
+
}
|
package/dist/grid-icons.d.ts
CHANGED
|
@@ -35,8 +35,8 @@ export type GridIconName = 'sort' | 'sort-asc' | 'sort-desc' | 'filter' | 'menu'
|
|
|
35
35
|
export type GridIcons = Partial<Record<GridIconName, Snippet>>;
|
|
36
36
|
/**
|
|
37
37
|
* Icons whose built-in form is a character rather than an SVG path. Kept as
|
|
38
|
-
* data so `<SvGrid>`, its footer and the
|
|
39
|
-
* the same defaults instead of three copies drifting apart.
|
|
38
|
+
* data so `<SvGrid>`, its footer and the row-group panel in @svgrid/enterprise
|
|
39
|
+
* render the same defaults instead of three copies drifting apart.
|
|
40
40
|
*
|
|
41
41
|
* Each is the exact character that shipped inline before, so a grid that sets
|
|
42
42
|
* no `icons` renders byte-for-byte what it always did.
|
package/dist/grid-icons.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Icons whose built-in form is a character rather than an SVG path. Kept as
|
|
3
|
-
* data so `<SvGrid>`, its footer and the
|
|
4
|
-
* the same defaults instead of three copies drifting apart.
|
|
3
|
+
* data so `<SvGrid>`, its footer and the row-group panel in @svgrid/enterprise
|
|
4
|
+
* render the same defaults instead of three copies drifting apart.
|
|
5
5
|
*
|
|
6
6
|
* Each is the exact character that shipped inline before, so a grid that sets
|
|
7
7
|
* no `icons` renders byte-for-byte what it always did.
|
package/dist/grid-messages.d.ts
CHANGED
|
@@ -74,8 +74,14 @@ export type GridChromeMessages = {
|
|
|
74
74
|
bulkEditCancel: string;
|
|
75
75
|
bulkEditNoFields: string;
|
|
76
76
|
chartRange: string;
|
|
77
|
+
rowLoadFailed: string;
|
|
78
|
+
rowLoadRetry: string;
|
|
77
79
|
pivotUpsellTitle: string;
|
|
78
80
|
pivotUpsellBody: string;
|
|
81
|
+
ganttUpsellTitle: string;
|
|
82
|
+
ganttUpsellBody: string;
|
|
83
|
+
/** Placeholder in the Gantt view's search box. */
|
|
84
|
+
ganttSearchPlaceholder: string;
|
|
79
85
|
/** Shared licensing line under every Enterprise upsell note. */
|
|
80
86
|
upsellLicense: string;
|
|
81
87
|
upsellLicenseLink: string;
|
package/dist/grid-messages.js
CHANGED
|
@@ -84,8 +84,13 @@ export const defaultGridMessages = {
|
|
|
84
84
|
opBefore: 'Before',
|
|
85
85
|
opAfter: 'After',
|
|
86
86
|
chartRange: 'Chart selected range',
|
|
87
|
+
rowLoadFailed: 'Could not load these rows.',
|
|
88
|
+
rowLoadRetry: 'Retry',
|
|
87
89
|
pivotUpsellTitle: 'Pivot mode',
|
|
88
90
|
pivotUpsellBody: 'Pivot mode is an Enterprise feature. Install @svgrid/enterprise and call enablePivot() to use it.',
|
|
91
|
+
ganttUpsellTitle: 'Gantt view',
|
|
92
|
+
ganttUpsellBody: 'The Gantt view is an Enterprise feature. Install @svgrid/enterprise and call enableGanttView() to render it.',
|
|
93
|
+
ganttSearchPlaceholder: 'Search tasks...',
|
|
89
94
|
upsellLicense: 'A license key is required for production use. Without one the feature still works, but the grid renders an unlicensed watermark.',
|
|
90
95
|
upsellLicenseLink: 'View licensing',
|
|
91
96
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export * from './ai.js';
|
|
|
12
12
|
export { registerExportProvider, getExportProvider, type GridExportProvider, type GridExportRunOptions, } from './export-provider.js';
|
|
13
13
|
export type { SchedulerConfig, SchedulerDrawerConfig, SchedulerEventMoveEvent, SchedulerEventResizeEvent, SchedulerEventCommitEvent, SchedulerRangeSelection, SchedulerView, SchedulerResource, SchedulerCollisionMode, SchedulerException, SchedulerEditScope, SchedulerOccurrenceChangeEvent, } from './SvGrid.types.js';
|
|
14
14
|
export { registerSchedulerView, getSchedulerView, hasSchedulerView } from './scheduler-view.svelte.js';
|
|
15
|
+
export type { GanttConfig, GanttDrawerConfig, GanttTaskMoveEvent, GanttTaskResizeEvent, GanttProgressChangeEvent, GanttTaskCommitEvent, GanttDependency, GanttDependencyType, GanttZoom, } from './SvGrid.types.js';
|
|
16
|
+
export { registerGanttView, getGanttView, hasGanttView } from './gantt-view.svelte.js';
|
|
15
17
|
export { registerSelectionBarView, getSelectionBarView, hasSelectionBarView } from './selection-bar-view.svelte.js';
|
|
16
18
|
export type { ChartViewConfig } from './SvGrid.types.js';
|
|
17
19
|
export { registerChartView, getChartView, hasChartView } from './chart-view.svelte.js';
|
|
@@ -211,10 +213,9 @@ export { resolveColumnTypes, inferCellDataType, type CellDataType } from './colu
|
|
|
211
213
|
export { computeColumnGroupMeta, hiddenLeavesForCollapse, type ColumnGroupMeta, type ColumnGroupShow, } from './column-groups.js';
|
|
212
214
|
export { createHyperFormulaSheet, type HyperFormulaSheet, type HyperFormulaSheetConfig, type HyperFormulaInstance, } from './hyperformula-adapter.js';
|
|
213
215
|
export { createCollaboration, broadcastChannelTransport, type CollabUser, type CollabCell, type CollabPresence, type CollabMessage, type CollabTransport, type Collaboration, } from './collaboration.js';
|
|
214
|
-
export { createServerDataSource, type ServerDataSource, type ServerRequest, type ServerResult, type ServerController, type ServerState, type ServerSortModel, type ServerFilterModel, type ServerAggregation, type ServerGroupRow, type ServerLeafRow, type ServerMoreRow, type ServerFooterRow, type ServerSkeletonRow, type ServerDisplayRow, } from './server-data-source.js';
|
|
215
|
-
export {
|
|
216
|
-
export {
|
|
217
|
-
export { default as SvRowGroupPanel } from './SvRowGroupPanel.svelte';
|
|
216
|
+
export { createServerDataSource, type ServerDataSource, type ServerSelectionRule, type ServerRequest, type ServerResult, type ServerController, type ServerControllerOptions, type ServerState, type ServerSortModel, type ServerFilterModel, type ServerAggregation, type ServerGroupRow, type ServerLeafRow, type ServerMoreRow, type ServerFooterRow, type ServerSkeletonRow, type ServerGrandTotalRow, type ServerPlaceholderRow, type ServerDisplayRow, } from './server-data-source.js';
|
|
217
|
+
export { createBlockCache, createRowPlaceholder, rowPlaceholderState, type BlockCache, type BlockCacheOptions, type BlockCacheState, type BlockFetchResult, type BlockState, } from './server-block-cache.js';
|
|
218
|
+
export { toServerFilterColumns, type GridRowModel, type GridFilterState, type GridRowModelSort, } from './row-model.js';
|
|
218
219
|
export { createNamedViews, memoryViews, localStorageViews, attachAutoSavedView, type SavedView, type ViewStorage, type NamedViews, type AutoSavedViewOptions, } from './named-views.js';
|
|
219
220
|
export { resolveCellFormat, computeColumnStat, lerpColor, rgba, contrastText, formatNeedsStats, formatsNeedingStats, type ConditionalFormat, type ConditionalFormatSpec, type ColorScaleFormat, type ColorScaleStop, type ScaleBounds, type DataBarFormat, type IconSetFormat, type RuleFormat, type ResolvedCellFormat, } from './conditional-formatting.js';
|
|
220
221
|
export { defaultGridMessages, resolveGridMessages, type GridMessages, type GridChromeMessages, } from './grid-messages.js';
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ export { registerBoardView, getBoardView, hasBoardView } from './board-view.svel
|
|
|
15
15
|
export * from './ai.js';
|
|
16
16
|
export { registerExportProvider, getExportProvider, } from './export-provider.js';
|
|
17
17
|
export { registerSchedulerView, getSchedulerView, hasSchedulerView } from './scheduler-view.svelte.js';
|
|
18
|
+
export { registerGanttView, getGanttView, hasGanttView } from './gantt-view.svelte.js';
|
|
18
19
|
// Bulk-action bar over the row selection. Prop + types free, renderer Pro.
|
|
19
20
|
export { registerSelectionBarView, getSelectionBarView, hasSelectionBarView } from './selection-bar-view.svelte.js';
|
|
20
21
|
export { registerChartView, getChartView, hasChartView } from './chart-view.svelte.js';
|
|
@@ -253,9 +254,8 @@ export { computeColumnGroupMeta, hiddenLeavesForCollapse, } from './column-group
|
|
|
253
254
|
export { createHyperFormulaSheet, } from './hyperformula-adapter.js';
|
|
254
255
|
export { createCollaboration, broadcastChannelTransport, } from './collaboration.js';
|
|
255
256
|
export { createServerDataSource, } from './server-data-source.js';
|
|
256
|
-
export {
|
|
257
|
-
export {
|
|
258
|
-
export { default as SvRowGroupPanel } from './SvRowGroupPanel.svelte';
|
|
257
|
+
export { createBlockCache, createRowPlaceholder, rowPlaceholderState, } from './server-block-cache.js';
|
|
258
|
+
export { toServerFilterColumns, } from './row-model.js';
|
|
259
259
|
export { createNamedViews, memoryViews, localStorageViews, attachAutoSavedView, } from './named-views.js';
|
|
260
260
|
export { resolveCellFormat, computeColumnStat, lerpColor, rgba, contrastText, formatNeedsStats, formatsNeedingStats, } from './conditional-formatting.js';
|
|
261
261
|
export { defaultGridMessages, resolveGridMessages, } from './grid-messages.js';
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GridRowModel` - one prop instead of eleven.
|
|
3
|
+
*
|
|
4
|
+
* A grid backed by a server needs the same wiring every time: hand it the
|
|
5
|
+
* rows, tell it not to sort or filter locally, forward the sort and the filter
|
|
6
|
+
* to the backend, report which rows are on screen, say which rows have not
|
|
7
|
+
* loaded, and delegate selection. Written out by hand that is a dozen props
|
|
8
|
+
* and a filter-shape conversion that every app gets slightly differently:
|
|
9
|
+
*
|
|
10
|
+
* ```svelte
|
|
11
|
+
* <SvGrid data={s.rows} externalSort externalFilter loading={s.loading}
|
|
12
|
+
* onSortingChange={(x) => ctl.setSort(x)}
|
|
13
|
+
* onFiltersChange={(f) => ctl.setFilter({ global: f.global,
|
|
14
|
+
* columns: Object.fromEntries(f.columns.map((c) => [c.id, { ... }])) })}
|
|
15
|
+
* onVisibleRangeChange={(r) => ctl.setViewport(r.startIndex, r.endIndex)}
|
|
16
|
+
* ... />
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* An object that implements this interface carries all of it, so the same grid
|
|
20
|
+
* becomes:
|
|
21
|
+
*
|
|
22
|
+
* ```svelte
|
|
23
|
+
* <SvGrid rowModel={ctl} {columns} />
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* `createServerDataSource` returns one, and so does the Enterprise server row
|
|
27
|
+
* model - which is the point of the interface rather than a concrete type: the
|
|
28
|
+
* grid depends on the SHAPE, never on either implementation, and an app with
|
|
29
|
+
* its own fetching can satisfy it too.
|
|
30
|
+
*
|
|
31
|
+
* Every member except `subscribe` / `getRows` / `isLoading` is optional, and a
|
|
32
|
+
* prop written explicitly on `<SvGrid>` always wins over the model's answer -
|
|
33
|
+
* so adopting this is never all-or-nothing.
|
|
34
|
+
*/
|
|
35
|
+
import type { FilterOperator } from './SvGrid.types.js';
|
|
36
|
+
/** The filter state as the grid reports it, straight from `onFiltersChange`. */
|
|
37
|
+
export type GridFilterState = {
|
|
38
|
+
global: string;
|
|
39
|
+
columns: Array<{
|
|
40
|
+
id: string;
|
|
41
|
+
operator: FilterOperator;
|
|
42
|
+
value: string;
|
|
43
|
+
valueTo?: string;
|
|
44
|
+
selectedValues?: Array<string>;
|
|
45
|
+
}>;
|
|
46
|
+
};
|
|
47
|
+
/** The sort the grid hands a row model: column id and direction, in priority order. */
|
|
48
|
+
export type GridRowModelSort = Array<{
|
|
49
|
+
id: string;
|
|
50
|
+
desc: boolean;
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* What `<SvGrid rowModel>` consumes: rows, a loading flag, a change
|
|
54
|
+
* subscription, and whichever optional parts the model implements - sort,
|
|
55
|
+
* filter, viewport, placeholders, group accessors, selection, filter
|
|
56
|
+
* values, paging, pinned rows and pivot columns. `createServerDataSource`
|
|
57
|
+
* and the Enterprise `createServerRowModel` both satisfy it.
|
|
58
|
+
*/
|
|
59
|
+
export type GridRowModel<TData> = {
|
|
60
|
+
/**
|
|
61
|
+
* Register a callback for "something changed, re-read me". Returns an
|
|
62
|
+
* unsubscribe function, which the grid calls on unmount.
|
|
63
|
+
*/
|
|
64
|
+
subscribe(onChange: () => void): () => void;
|
|
65
|
+
/** The rows to render, including any placeholders for unloaded ones. */
|
|
66
|
+
getRows(): ReadonlyArray<TData>;
|
|
67
|
+
/** True while a fetch is in flight, for the loading overlay. */
|
|
68
|
+
isLoading(): boolean;
|
|
69
|
+
/** Stable row id. Required once selection or editing is involved. */
|
|
70
|
+
getRowId?(row: TData, index: number): string;
|
|
71
|
+
/** Sort on the server. Present means "do not sort locally". */
|
|
72
|
+
setSort?(sort: GridRowModelSort): void;
|
|
73
|
+
/** Filter on the server. Present means "do not filter locally". */
|
|
74
|
+
setFilter?(filters: GridFilterState): void;
|
|
75
|
+
/** Which rows are on screen, so a block-loading model knows what to fetch. */
|
|
76
|
+
setViewport?(startIndex: number, endIndex: number): void;
|
|
77
|
+
/** Why a row has no data yet, or null when it does. */
|
|
78
|
+
rowPlaceholder?(row: TData, rowIndex: number): 'loading' | 'failed' | null;
|
|
79
|
+
/** Retry the failed block a placeholder row belongs to. */
|
|
80
|
+
retryRow?(row: TData, rowIndex: number): void;
|
|
81
|
+
/** Group / tree accessors, for the grid's treegrid keyboard and ARIA. */
|
|
82
|
+
group?: {
|
|
83
|
+
isGroup: (row: TData) => boolean;
|
|
84
|
+
level: (row: TData) => number;
|
|
85
|
+
expanded?: (row: TData) => boolean;
|
|
86
|
+
onToggle: (row: TData) => void;
|
|
87
|
+
};
|
|
88
|
+
/** Selection the grid does not own - see the `rowSelectionModel` prop. */
|
|
89
|
+
selection?: {
|
|
90
|
+
isSelected: (rowId: string, row: TData) => boolean;
|
|
91
|
+
headerState: () => 'none' | 'some' | 'all';
|
|
92
|
+
toggle: (rowId: string, row: TData, next: boolean) => void;
|
|
93
|
+
toggleAll: (next: boolean) => void;
|
|
94
|
+
selectedCount?: () => number | null;
|
|
95
|
+
bulkUpdate?: (patch: Record<string, unknown>) => Promise<number>;
|
|
96
|
+
};
|
|
97
|
+
/** Distinct values for a column's set filter, fetched from the server. */
|
|
98
|
+
filterValues?(columnId: string): Promise<Array<string>>;
|
|
99
|
+
/** Server-side paging. Omit it for infinite scrolling. */
|
|
100
|
+
pagination?: {
|
|
101
|
+
pageIndex: number;
|
|
102
|
+
pageSize: number;
|
|
103
|
+
rowCount: number;
|
|
104
|
+
setPage(pageIndex: number): void;
|
|
105
|
+
setPageSize(pageSize: number): void;
|
|
106
|
+
/** Choices for the footer's page-size selector. */
|
|
107
|
+
pageSizes?: number[];
|
|
108
|
+
/**
|
|
109
|
+
* Ask the grid for as many rows as its body fits: it measures and calls
|
|
110
|
+
* `setPageSize` whenever the fit changes.
|
|
111
|
+
*/
|
|
112
|
+
autoPageSize?: boolean;
|
|
113
|
+
};
|
|
114
|
+
/** Rows to pin above / below the scrolling body - a grand total, say. */
|
|
115
|
+
readonly pinnedTopRows?: ReadonlyArray<TData>;
|
|
116
|
+
readonly pinnedBottomRows?: ReadonlyArray<TData>;
|
|
117
|
+
/**
|
|
118
|
+
* Columns that replace the grid's `columns` while the model is in pivot
|
|
119
|
+
* mode, or null. Read after every change notification.
|
|
120
|
+
*/
|
|
121
|
+
readonly pivotResultColumns?: ReadonlyArray<unknown> | null;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Turn the grid's filter payload into the column-keyed map a `ServerRequest`
|
|
125
|
+
* carries. Exported because a hand-written `GridRowModel` needs exactly this
|
|
126
|
+
* conversion, and copying it is how the two shapes drift apart.
|
|
127
|
+
*/
|
|
128
|
+
export declare function toServerFilterColumns(filters: GridFilterState): Record<string, {
|
|
129
|
+
operator: string;
|
|
130
|
+
value: string;
|
|
131
|
+
valueTo?: string;
|
|
132
|
+
selectedValues?: Array<string>;
|
|
133
|
+
}>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn the grid's filter payload into the column-keyed map a `ServerRequest`
|
|
3
|
+
* carries. Exported because a hand-written `GridRowModel` needs exactly this
|
|
4
|
+
* conversion, and copying it is how the two shapes drift apart.
|
|
5
|
+
*/
|
|
6
|
+
export function toServerFilterColumns(filters) {
|
|
7
|
+
return Object.fromEntries(filters.columns.map((c) => [
|
|
8
|
+
c.id,
|
|
9
|
+
{
|
|
10
|
+
operator: c.operator,
|
|
11
|
+
value: c.value,
|
|
12
|
+
...(c.valueTo !== undefined ? { valueTo: c.valueTo } : {}),
|
|
13
|
+
...(c.selectedValues !== undefined ? { selectedValues: c.selectedValues } : {}),
|
|
14
|
+
},
|
|
15
|
+
]));
|
|
16
|
+
}
|
package/dist/selection.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { type RowData, type TableFeatures } from "./index.js";
|
|
2
2
|
import "./sv-grid-scrollbar.js";
|
|
3
3
|
export declare function createSelection<TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData>(ctx: any): {
|
|
4
|
-
isRowSelected: (rowId: string) =>
|
|
4
|
+
isRowSelected: (rowId: string) => any;
|
|
5
5
|
toggleRowSelectionById: (rowId: string) => void;
|
|
6
6
|
toggleSelectAllRows: () => void;
|
|
7
|
+
setSelectAllRows: (select: boolean) => void;
|
|
7
8
|
setActiveCell: (rowIndex: number, colIndex: number) => void;
|
|
8
9
|
scrollActiveCellIntoView: (rowIndex: number, colIndex: number) => void;
|
|
9
10
|
setSelection: (rowIndex: number, colIndex: number, additive?: boolean) => void;
|
package/dist/selection.js
CHANGED
|
@@ -7,14 +7,41 @@ import { getColumnBaseValue, isGroupRow, } from "./cell-values.js";
|
|
|
7
7
|
import { expandRectToMerges, originOf, endOf } from "./merges.js";
|
|
8
8
|
export function createSelection(ctx) {
|
|
9
9
|
function isRowSelected(rowId) {
|
|
10
|
+
const model = ctx.props.rowSelectionModel;
|
|
11
|
+
if (model) {
|
|
12
|
+
const row = ctx.allRows.find((r) => r.id === rowId);
|
|
13
|
+
return row ? model.isSelected(rowId, row.original) : false;
|
|
14
|
+
}
|
|
10
15
|
return Boolean(ctx.rowSelectionState[rowId]);
|
|
11
16
|
}
|
|
12
17
|
function toggleRowSelectionById(rowId) {
|
|
18
|
+
const model = ctx.props.rowSelectionModel;
|
|
19
|
+
if (model) {
|
|
20
|
+
const row = ctx.allRows.find((r) => r.id === rowId);
|
|
21
|
+
if (!row)
|
|
22
|
+
return;
|
|
23
|
+
model.toggle(rowId, row.original, !model.isSelected(rowId, row.original));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
13
26
|
ctx.grid.setRowSelection((prev) => ({ ...prev, [rowId]: !prev[rowId] }));
|
|
14
27
|
}
|
|
15
28
|
function toggleSelectAllRows() {
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
setSelectAllRows(ctx.headerSelectionState !== "all");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Select or clear every row, for the header checkbox and for
|
|
33
|
+
* `api.selectAllRows()`.
|
|
34
|
+
*/
|
|
35
|
+
function setSelectAllRows(select) {
|
|
36
|
+
// An external model decides what "all" means - possibly a million rows
|
|
37
|
+
// the grid has never seen - so it gets the intent, not a row list.
|
|
38
|
+
if (ctx.props.rowSelectionModel) {
|
|
39
|
+
ctx.props.rowSelectionModel.toggleAll(select);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Placeholder rows are excluded: there is no row there to select yet,
|
|
43
|
+
// and ticking one would leave a selected id that never resolves.
|
|
44
|
+
const selectable = ctx.allRows.filter((row) => !isGroupRow(row) && !ctx.placeholderStateOf(row));
|
|
18
45
|
ctx.grid.setRowSelection((prev) => {
|
|
19
46
|
const next = { ...prev };
|
|
20
47
|
for (const row of selectable) {
|
|
@@ -646,6 +673,7 @@ export function createSelection(ctx) {
|
|
|
646
673
|
isRowSelected,
|
|
647
674
|
toggleRowSelectionById,
|
|
648
675
|
toggleSelectAllRows,
|
|
676
|
+
setSelectAllRows,
|
|
649
677
|
setActiveCell,
|
|
650
678
|
scrollActiveCellIntoView,
|
|
651
679
|
setSelection,
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Block cache for server-backed rows - the engine behind infinite scrolling.
|
|
3
|
+
*
|
|
4
|
+
* Paging asks for "page 3". Infinite scrolling asks a different question: the
|
|
5
|
+
* user is looking at rows 4,000 to 4,020 of a table with a million rows, so
|
|
6
|
+
* fetch the block that covers them, keep a bounded number of recently-seen
|
|
7
|
+
* blocks, and render something sensible for every row nobody has fetched yet.
|
|
8
|
+
* That is all this module does, in plain TypeScript with no Svelte and no DOM.
|
|
9
|
+
*
|
|
10
|
+
* The shape of the problem (and most of the vocabulary) is the same one every
|
|
11
|
+
* grid with a server row model solves, so the options here are deliberately
|
|
12
|
+
* recognisable: `blockSize`, `maxBlocksInCache`, `maxConcurrentRequests`,
|
|
13
|
+
* `blockLoadDebounceMs`, `initialRowCount`, `overflowRows`.
|
|
14
|
+
*
|
|
15
|
+
* Three things it takes seriously:
|
|
16
|
+
*
|
|
17
|
+
* - **Unloaded rows still have to render.** `rows()` always returns a dense
|
|
18
|
+
* array, with a shared sentinel object in every slot that has no data. The
|
|
19
|
+
* grid recognises those (see {@link rowPlaceholderState}) and draws a
|
|
20
|
+
* skeleton or a retry affordance instead of an empty row, so the scrollbar
|
|
21
|
+
* never lies and scrolling never leaves a hole.
|
|
22
|
+
* - **The row count may be unknown.** A backend that cannot cheaply count
|
|
23
|
+
* says so (omit `rowCount`, or send `-1`) and the list grows by a block at
|
|
24
|
+
* a time until a short block proves where the end is.
|
|
25
|
+
* - **Requests are not free.** In-flight blocks are deduplicated, at most
|
|
26
|
+
* `maxConcurrentRequests` are open at once, a fast scroll debounces rather
|
|
27
|
+
* than firing a request per frame, and blocks abandoned by eviction or
|
|
28
|
+
* purge are aborted.
|
|
29
|
+
*
|
|
30
|
+
* Enterprise's server row model runs one of these per group level; the free
|
|
31
|
+
* flat controller runs exactly one.
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* The mark that says "this row is not data yet".
|
|
35
|
+
*
|
|
36
|
+
* A symbol, for two reasons. It cannot collide with a field in anyone's row
|
|
37
|
+
* data, which a string key like `__svPlaceholder` eventually would. And
|
|
38
|
+
* `Symbol.for` puts it in the global registry, so a placeholder still reads as
|
|
39
|
+
* one after it has crossed a module boundary - two copies of this file loaded
|
|
40
|
+
* through different specifiers would otherwise mint different symbols.
|
|
41
|
+
*/
|
|
42
|
+
declare const PLACEHOLDER: unique symbol;
|
|
43
|
+
/** Rows the cache has not loaded. One shared object per state, not per row. */
|
|
44
|
+
declare const LOADING_ROW: Readonly<{
|
|
45
|
+
[PLACEHOLDER]: "loading";
|
|
46
|
+
}>;
|
|
47
|
+
declare const FAILED_ROW: Readonly<{
|
|
48
|
+
[PLACEHOLDER]: "failed";
|
|
49
|
+
}>;
|
|
50
|
+
/**
|
|
51
|
+
* Why a row is not real data, or `null` when it is.
|
|
52
|
+
*
|
|
53
|
+
* Deliberately NOT an identity check. The sentinels are shared objects, so
|
|
54
|
+
* `row === LOADING_ROW` looks like the obvious test - but rows routinely
|
|
55
|
+
* arrive here through something that wrapped them. Svelte is the everyday
|
|
56
|
+
* case: assigning the controller state into `$state` makes the row array
|
|
57
|
+
* deeply reactive, and every row read back out is a proxy of the original.
|
|
58
|
+
* Identity fails, every placeholder reads as real data, and the grid renders
|
|
59
|
+
* a screen of blank rows instead of skeletons. Reading the mark works through
|
|
60
|
+
* any wrapper that forwards property access, which is all of them.
|
|
61
|
+
*/
|
|
62
|
+
export declare function rowPlaceholderState(row: unknown): 'loading' | 'failed' | null;
|
|
63
|
+
/**
|
|
64
|
+
* Mint a frozen placeholder the grid will recognise, carrying whatever
|
|
65
|
+
* other fields the caller wants on it. The row model uses this to give its
|
|
66
|
+
* placeholders a `kind`, so code reading display rows and code reading grid
|
|
67
|
+
* rows agree on what they are looking at.
|
|
68
|
+
*/
|
|
69
|
+
export declare function createRowPlaceholder<T extends object>(state: 'loading' | 'failed', fields: T): Readonly<T & {
|
|
70
|
+
readonly [PLACEHOLDER]: 'loading' | 'failed';
|
|
71
|
+
}>;
|
|
72
|
+
/** What a block fetch has to answer with. `rowCount` is optional: see {@link BlockCacheOptions}. */
|
|
73
|
+
export type BlockFetchResult<TData> = {
|
|
74
|
+
rows: ReadonlyArray<TData>;
|
|
75
|
+
/**
|
|
76
|
+
* Total rows after filtering. Omit it (or send `-1`) when the backend cannot
|
|
77
|
+
* count cheaply - the cache then discovers the end from the first short
|
|
78
|
+
* block, and `lastRowKnown()` stays false until it does.
|
|
79
|
+
*/
|
|
80
|
+
rowCount?: number;
|
|
81
|
+
};
|
|
82
|
+
/** What a block cache is built from: block size, caps, debounce and the fetch. */
|
|
83
|
+
export type BlockCacheOptions<TData> = {
|
|
84
|
+
/**
|
|
85
|
+
* Rows per request. Bigger blocks mean fewer round trips and more wasted
|
|
86
|
+
* rows when the user scrolls past; 100 is the usual compromise.
|
|
87
|
+
*/
|
|
88
|
+
blockSize?: number;
|
|
89
|
+
/**
|
|
90
|
+
* Keep at most this many loaded blocks. Blocks outside the viewport are
|
|
91
|
+
* evicted least-recently-seen first, and scrolling back re-fetches them.
|
|
92
|
+
* Unlimited by default, which is right until the dataset is big enough that
|
|
93
|
+
* holding every visited block matters.
|
|
94
|
+
*/
|
|
95
|
+
maxBlocksInCache?: number;
|
|
96
|
+
/** Requests open at once. Default 2. */
|
|
97
|
+
maxConcurrentRequests?: number;
|
|
98
|
+
/**
|
|
99
|
+
* Wait this long after the viewport settles before fetching. Non-zero values
|
|
100
|
+
* stop a drag of the scrollbar from requesting every block it flies past.
|
|
101
|
+
*/
|
|
102
|
+
blockLoadDebounceMs?: number;
|
|
103
|
+
/**
|
|
104
|
+
* Rows to claim before anything is loaded, so the grid has a scrollbar (and
|
|
105
|
+
* `scrollToRow` has somewhere to land) on the first paint. Default 1.
|
|
106
|
+
*/
|
|
107
|
+
initialRowCount?: number;
|
|
108
|
+
/**
|
|
109
|
+
* While the total is unknown, how many unloaded rows to keep past the last
|
|
110
|
+
* loaded one. Scrolling into them is what asks for the next block. Default 1.
|
|
111
|
+
*/
|
|
112
|
+
overflowRows?: number;
|
|
113
|
+
/** Fetch one block. Reject, or throw, to mark it failed. */
|
|
114
|
+
fetch: (startRow: number, endRow: number, signal: AbortSignal) => Promise<BlockFetchResult<TData>>;
|
|
115
|
+
/** Called after any change to the rows, the count, or a block's state. */
|
|
116
|
+
onChange: (cache: BlockCacheState) => void;
|
|
117
|
+
/**
|
|
118
|
+
* How to coalesce `onChange`. Defaults to a microtask, so a burst of block
|
|
119
|
+
* arrivals rebuilds the row array once. Pass `(fn) => fn()` in tests to make
|
|
120
|
+
* every change synchronous.
|
|
121
|
+
*/
|
|
122
|
+
schedule?: (flush: () => void) => void;
|
|
123
|
+
};
|
|
124
|
+
/** One block's place in the world, for diagnostics and for tests. */
|
|
125
|
+
export type BlockState = {
|
|
126
|
+
blockIndex: number;
|
|
127
|
+
startRow: number;
|
|
128
|
+
endRow: number;
|
|
129
|
+
status: 'loading' | 'loaded' | 'failed';
|
|
130
|
+
/** Monotonic counter of when the viewport last covered this block. */
|
|
131
|
+
lastTouched: number;
|
|
132
|
+
};
|
|
133
|
+
/** One block as `getCacheState()` reports it. */
|
|
134
|
+
export type BlockCacheState = {
|
|
135
|
+
/** Rows after filtering, or `null` while the backend has not said. */
|
|
136
|
+
rowCount: number | null;
|
|
137
|
+
/** False while the end of the data is still being discovered. */
|
|
138
|
+
lastRowKnown: boolean;
|
|
139
|
+
/** True while at least one block is in flight. */
|
|
140
|
+
loading: boolean;
|
|
141
|
+
/** Blocks whose fetch rejected. Empty unless something went wrong. */
|
|
142
|
+
failedBlocks: number[];
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* A block cache: rows arrive in blocks as the viewport reaches them,
|
|
146
|
+
* placeholders stand in until then, and blocks far from the viewport are
|
|
147
|
+
* evicted past the cap.
|
|
148
|
+
*/
|
|
149
|
+
export type BlockCache<TData> = {
|
|
150
|
+
/**
|
|
151
|
+
* Tell the cache which rows are on screen. Fetches what is missing (after
|
|
152
|
+
* `blockLoadDebounceMs`) and marks those blocks as recently seen so eviction
|
|
153
|
+
* spares them. Safe to call on every scroll frame.
|
|
154
|
+
*/
|
|
155
|
+
setViewport(startIndex: number, endIndex: number): void;
|
|
156
|
+
/** The dense row array to hand the grid. Placeholders fill unloaded slots. */
|
|
157
|
+
rows(): ReadonlyArray<TData>;
|
|
158
|
+
/** One row, without building the array. Returns a placeholder when unloaded. */
|
|
159
|
+
getRow(index: number): TData | typeof LOADING_ROW | typeof FAILED_ROW;
|
|
160
|
+
rowCount(): number | null;
|
|
161
|
+
lastRowKnown(): boolean;
|
|
162
|
+
/** Re-fetch the blocks that failed. */
|
|
163
|
+
retryFailed(): void;
|
|
164
|
+
/**
|
|
165
|
+
* Re-fetch the loaded blocks in place, keeping the row count and scroll
|
|
166
|
+
* position. What you want after a mutation lands on the server.
|
|
167
|
+
*/
|
|
168
|
+
refresh(): void;
|
|
169
|
+
/** Drop everything and start over from the current viewport. */
|
|
170
|
+
purge(): void;
|
|
171
|
+
/** Override the total, e.g. from a count endpoint that answered separately. */
|
|
172
|
+
setRowCount(count: number | null, known?: boolean): void;
|
|
173
|
+
/**
|
|
174
|
+
* Write rows straight into the cache from `startRow`, as if a fetch had
|
|
175
|
+
* returned them, bypassing the datasource, the debounce and the
|
|
176
|
+
* concurrency cap. `startRow` must sit on a block boundary. An explicit
|
|
177
|
+
* `rowCount` settles the total; without one a short final slice does.
|
|
178
|
+
*/
|
|
179
|
+
applyRows(startRow: number, rows: ReadonlyArray<TData>, rowCount?: number): void;
|
|
180
|
+
/** Replace one loaded row. No-op when its block is not loaded. */
|
|
181
|
+
patch(index: number, row: TData): boolean;
|
|
182
|
+
/**
|
|
183
|
+
* The index of the first LOADED row matching `predicate`, or -1. Rows
|
|
184
|
+
* that are not loaded are not visited - a transaction addressed by id
|
|
185
|
+
* can only touch what is in the cache.
|
|
186
|
+
*/
|
|
187
|
+
findIndex(predicate: (row: TData, index: number) => boolean): number;
|
|
188
|
+
/**
|
|
189
|
+
* Insert rows at an index and grow the count. Works within whichever
|
|
190
|
+
* run of loaded blocks contains `index`; loaded blocks AFTER that run
|
|
191
|
+
* are dropped, because their rows have shifted and will be re-fetched at
|
|
192
|
+
* their new offsets. An index in an unloaded region grows the count
|
|
193
|
+
* only (the rows exist; they arrive when scrolled to). Returns whether
|
|
194
|
+
* the rows were placed in the cache.
|
|
195
|
+
*/
|
|
196
|
+
insert(index: number, rows: ReadonlyArray<TData>): boolean;
|
|
197
|
+
/**
|
|
198
|
+
* Remove `count` rows at an index and shrink the count, with the same
|
|
199
|
+
* run semantics as `insert`. Returns how many rows were actually
|
|
200
|
+
* removed from the cache.
|
|
201
|
+
*/
|
|
202
|
+
remove(index: number, count?: number): number;
|
|
203
|
+
/** Every block the cache is holding, for `debug` output and tests. */
|
|
204
|
+
getCacheState(): BlockState[];
|
|
205
|
+
/** Abort what is in flight and stop emitting. Call on unmount. */
|
|
206
|
+
dispose(): void;
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Build a block cache over a `fetch(startRow, endRow, signal)`. The free
|
|
210
|
+
* infinite row model and the Enterprise server-side row model both run on
|
|
211
|
+
* it, one per level.
|
|
212
|
+
*/
|
|
213
|
+
export declare function createBlockCache<TData>(options: BlockCacheOptions<TData>): BlockCache<TData>;
|
|
214
|
+
export {};
|