@svgrid/grid 3.0.3 → 4.0.0
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 +5 -4
- package/dist/GridMenus.svelte +44 -2
- package/dist/SvChartMenu.svelte +69 -0
- package/dist/SvChartMenu.svelte.d.ts +11 -0
- package/dist/SvChartPanes.svelte +113 -0
- package/dist/SvChartPanes.svelte.d.ts +20 -0
- package/dist/SvGrid.controller.svelte.d.ts +122 -41
- package/dist/SvGrid.controller.svelte.js +845 -207
- package/dist/SvGrid.css +178 -2
- package/dist/SvGrid.svelte +467 -516
- package/dist/SvGrid.types.d.ts +315 -12
- package/dist/SvGridCellEditor.svelte +17 -0
- package/dist/SvGridChart.svelte +2257 -329
- package/dist/SvGridChart.svelte.d.ts +6 -106
- package/dist/SvGridChart.types.d.ts +338 -0
- package/dist/SvGridChart.types.js +1 -0
- package/dist/SvGridChartBuilder.svelte +323 -0
- package/dist/SvGridChartBuilder.svelte.d.ts +34 -0
- package/dist/SvGridChartPanel.svelte +221 -275
- package/dist/SvGridChartPickers.svelte +417 -0
- package/dist/SvGridChartPickers.svelte.d.ts +70 -0
- package/dist/SvGridChartView.svelte +13 -14
- package/dist/SvModal.svelte +7 -1
- package/dist/SvModal.svelte.d.ts +5 -0
- package/dist/ai.d.ts +40 -8
- package/dist/ai.js +135 -24
- package/dist/aria.d.ts +11 -0
- package/dist/build-api.js +119 -28
- package/dist/cdn/GridMenus-CLI93jke.js +635 -0
- package/dist/cdn/{GridMenus-DhdPF700.js → GridMenus-CwooL3QA.js} +240 -207
- package/dist/cdn/SvChartMenu-Bl6PBbkT.js +58 -0
- package/dist/cdn/SvChartMenu-FBSMINA6.js +59 -0
- package/dist/cdn/{SvDateRangeInput-DMLKmGEc.js → SvDateRangeInput-BeU_TkbQ.js} +15 -14
- package/dist/cdn/{SvDateRangeInput-CaOuMs8O.js → SvDateRangeInput-yX8vzleW.js} +38 -37
- package/dist/cdn/{SvDateTimePicker-sonaH0oh.js → SvDateTimePicker-DQwt4UAs.js} +139 -180
- package/dist/cdn/SvDateTimePicker-vNU6bZ-q.js +775 -0
- package/dist/cdn/{SvGridCellEditor-CjEJMJKc.js → SvGridCellEditor-B1p-vCK5.js} +188 -178
- package/dist/cdn/{SvGridCellEditor-BWnTi2N7.js → SvGridCellEditor-D_0q4xAu.js} +98 -88
- package/dist/cdn/SvGridChart-C3EWAZaM.js +3574 -0
- package/dist/cdn/SvGridChart-DzLkSwCH.js +3572 -0
- package/dist/cdn/SvGridChartBuilder-BE2T1ykB.js +783 -0
- package/dist/cdn/SvGridChartBuilder-CfII62sZ.js +784 -0
- package/dist/cdn/SvGridChartPanel-CCX5_Wgd.js +675 -0
- package/dist/cdn/SvGridChartPanel-xf4sWVTo.js +699 -0
- package/dist/cdn/SvGridChartView-CfuXmY5I.js +48 -0
- package/dist/cdn/SvGridChartView-DYwabQWj.js +47 -0
- package/dist/cdn/SvMenuList-CLN8OuIK.js +452 -0
- package/dist/cdn/SvMenuList-DkGhNKjf.js +451 -0
- package/dist/cdn/SvModal-BAE-pjZX.js +396 -0
- package/dist/cdn/SvModal-CIZWCcad.js +397 -0
- package/dist/cdn/chart-Dz7SqMXH.js +4341 -0
- package/dist/cdn/chart-export-BXqueeWJ.js +135 -0
- package/dist/cdn/chart-export-pdf-DONFUnNY.js +110 -0
- package/dist/cdn/chart-panel-messages-CUbf2R4i.js +1052 -0
- package/dist/cdn/chart-panel-messages-CmNrMdsr.js +1053 -0
- package/dist/cdn/chart-summary-BJW_tg_X.js +138 -0
- package/dist/cdn/chart-trend-CaN9mDEV.js +50 -0
- package/dist/cdn/chart-validate-HcOVFAfJ.js +188 -0
- package/dist/cdn/{column-resize-DsfNXMom.js → column-resize-DpmOLfRp.js} +3 -3
- package/dist/cdn/{date-format-BnnHlqGw.js → date-format-CtquV-p3.js} +583 -603
- package/dist/cdn/{date-format-BNii4zeD.js → date-format-D6KFzU_W.js} +347 -367
- package/dist/cdn/dismissable-DAHetSNk.js +44 -0
- package/dist/cdn/editor-contract-LXgQJAgd.js +22 -0
- package/dist/cdn/focus-trap-BBOQYBma.js +65 -0
- package/dist/cdn/{row-resize-BRcimkUT.js → row-resize-niQCp040.js} +44 -36
- package/dist/cdn/server-block-cache-CGoWz-87.js +237 -0
- package/dist/cdn/{src-D6XvA1Ij.js → src-B6HXdfHY.js} +10448 -10049
- package/dist/cdn/{src-Bugf5XlI.js → src-C6Iowdvs.js} +6908 -6509
- package/dist/cdn/svgrid.js +27 -15
- package/dist/cdn/svgrid.svelte-external.js +27 -15
- package/dist/cdn/validate-CchXzyrX.js +76 -0
- package/dist/chart-axes.d.ts +53 -0
- package/dist/chart-axes.js +351 -0
- package/dist/chart-cartesian.d.ts +88 -0
- package/dist/chart-cartesian.js +1862 -0
- package/dist/chart-decimate.d.ts +51 -0
- package/dist/chart-decimate.js +199 -0
- package/dist/chart-export-pdf.d.ts +47 -0
- package/dist/chart-export-pdf.js +187 -0
- package/dist/chart-export.d.ts +2 -1
- package/dist/chart-export.js +18 -3
- package/dist/chart-financial.d.ts +91 -0
- package/dist/chart-financial.js +175 -0
- package/dist/chart-flow.d.ts +10 -0
- package/dist/chart-flow.js +298 -0
- package/dist/chart-format.d.ts +18 -0
- package/dist/chart-format.js +65 -0
- package/dist/chart-grid.d.ts +8 -0
- package/dist/chart-grid.js +190 -0
- package/dist/chart-hierarchy.d.ts +17 -0
- package/dist/chart-hierarchy.js +146 -0
- package/dist/chart-indicators.d.ts +115 -0
- package/dist/chart-indicators.js +389 -0
- package/dist/chart-messages.d.ts +95 -0
- package/dist/chart-messages.js +92 -0
- package/dist/chart-motion.d.ts +16 -0
- package/dist/chart-motion.js +141 -0
- package/dist/chart-panel-messages.d.ts +265 -0
- package/dist/chart-panel-messages.js +285 -0
- package/dist/chart-pivot.d.ts +87 -0
- package/dist/chart-pivot.js +156 -0
- package/dist/chart-polar.d.ts +25 -0
- package/dist/chart-polar.js +700 -0
- package/dist/chart-samples.d.ts +16 -0
- package/dist/chart-samples.js +153 -0
- package/dist/chart-scale.d.ts +113 -0
- package/dist/chart-scale.js +405 -0
- package/dist/chart-stats.d.ts +111 -0
- package/dist/chart-stats.js +419 -0
- package/dist/chart-stream.d.ts +45 -0
- package/dist/chart-stream.js +91 -0
- package/dist/chart-summary.d.ts +27 -0
- package/dist/chart-summary.js +205 -0
- package/dist/chart-sync.svelte.d.ts +32 -0
- package/dist/chart-sync.svelte.js +23 -0
- package/dist/chart-table.d.ts +35 -0
- package/dist/chart-table.js +165 -0
- package/dist/chart-trend.d.ts +19 -0
- package/dist/chart-trend.js +61 -0
- package/dist/chart-types.d.ts +1456 -0
- package/dist/chart-types.js +6 -0
- package/dist/chart-validate.d.ts +43 -0
- package/dist/chart-validate.js +272 -0
- package/dist/chart-zoom.d.ts +40 -0
- package/dist/chart-zoom.js +145 -0
- package/dist/chart.d.ts +186 -865
- package/dist/chart.js +460 -2204
- package/dist/clipboard.d.ts +1 -0
- package/dist/clipboard.js +137 -18
- package/dist/column-resize.d.ts +3 -0
- package/dist/column-resize.js +4 -1
- package/dist/columns.js +3 -0
- package/dist/command-context.d.ts +19 -0
- package/dist/command-context.js +116 -0
- package/dist/core.d.ts +7 -0
- package/dist/core.js +36 -13
- package/dist/createPopoverSelect.svelte.d.ts +2 -2
- package/dist/editing.d.ts +2 -8
- package/dist/editing.js +272 -33
- package/dist/fill-patterns.d.ts +0 -5
- package/dist/fill-patterns.js +56 -2
- package/dist/grid-icons.d.ts +2 -2
- package/dist/grid-icons.js +2 -2
- package/dist/grid-messages.d.ts +12 -3
- package/dist/grid-messages.js +7 -0
- package/dist/history.d.ts +83 -0
- package/dist/history.js +133 -0
- package/dist/index.d.ts +27 -10
- package/dist/index.js +22 -5
- package/dist/keyboard-handlers.js +88 -31
- package/dist/keyboard.d.ts +55 -0
- package/dist/keyboard.js +132 -0
- package/dist/menus.d.ts +3 -1
- package/dist/menus.js +7 -2
- package/dist/merges.d.ts +97 -0
- package/dist/merges.js +147 -0
- package/dist/row-model.d.ts +133 -0
- package/dist/row-model.js +16 -0
- package/dist/row-resize.d.ts +3 -0
- package/dist/row-resize.js +14 -0
- package/dist/scroll-sync.d.ts +1 -0
- package/dist/scroll-sync.js +24 -0
- package/dist/selection.d.ts +18 -1
- package/dist/selection.js +138 -18
- 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/shortcut-registry.d.ts +145 -0
- package/dist/shortcut-registry.js +44 -0
- package/dist/svgrid-wrapper.types.d.ts +213 -22
- package/dist/validate.d.ts +2 -0
- package/dist/validate.js +8 -3
- package/dist/virtualization/virtualizer.js +38 -17
- package/package.json +11 -1
- package/src/GridMenus.svelte +44 -2
- package/src/SvChartMenu.svelte +69 -0
- package/src/SvChartPanes.svelte +113 -0
- package/src/SvChartPanes.test.ts +66 -0
- package/src/SvGrid.controller.svelte.ts +818 -179
- package/src/SvGrid.css +178 -2
- package/src/SvGrid.svelte +467 -516
- package/src/SvGrid.types.ts +293 -11
- package/src/SvGridCellEditor.svelte +17 -0
- package/src/SvGridChart.svelte +2257 -329
- package/src/SvGridChart.test.ts +1419 -1
- package/src/SvGridChart.types.ts +336 -0
- package/src/SvGridChartBuilder.svelte +323 -0
- package/src/SvGridChartPanel.svelte +221 -275
- package/src/SvGridChartPickers.svelte +417 -0
- package/src/SvGridChartView.svelte +13 -14
- package/src/SvModal.svelte +7 -1
- package/src/SvModal.test.ts +9 -0
- package/src/ai.test.ts +57 -2
- package/src/ai.ts +166 -22
- package/src/aria.d.ts +11 -0
- package/src/build-api.coverage.test.ts +184 -0
- package/src/build-api.ts +118 -22
- package/src/chart-axes.test.ts +573 -0
- package/src/chart-axes.ts +342 -0
- package/src/chart-cartesian.ts +1863 -0
- package/src/chart-decimate.test.ts +249 -0
- package/src/chart-decimate.ts +186 -0
- package/src/chart-export-pdf.test.ts +91 -0
- package/src/chart-export-pdf.ts +207 -0
- package/src/chart-export.test.ts +14 -0
- package/src/chart-export.ts +13 -3
- package/src/chart-financial.ts +208 -0
- package/src/chart-flow.ts +286 -0
- package/src/chart-format.ts +67 -0
- package/src/chart-grid.ts +179 -0
- package/src/chart-hierarchy.ts +135 -0
- package/src/chart-indicators.test.ts +443 -0
- package/src/chart-indicators.ts +384 -0
- package/src/chart-messages.test.ts +21 -0
- package/src/chart-messages.ts +184 -0
- package/src/chart-motion.test.ts +71 -0
- package/src/chart-motion.ts +148 -0
- package/src/chart-panel-messages.test.ts +29 -0
- package/src/chart-panel-messages.ts +550 -0
- package/src/chart-pivot.test.ts +54 -0
- package/src/chart-pivot.ts +225 -0
- package/src/chart-polar.ts +674 -0
- package/src/chart-samples.ts +106 -0
- package/src/chart-scale.ts +394 -0
- package/src/chart-series-types.test.ts +714 -0
- package/src/chart-stats.ts +409 -0
- package/src/chart-stream.test.ts +75 -0
- package/src/chart-stream.ts +114 -0
- package/src/chart-summary.test.ts +90 -0
- package/src/chart-summary.ts +197 -0
- package/src/chart-sync.svelte.ts +41 -0
- package/src/chart-table.test.ts +76 -0
- package/src/chart-table.ts +178 -0
- package/src/chart-trend.ts +47 -0
- package/src/chart-types.ts +1309 -0
- package/src/chart-validate.test.ts +100 -0
- package/src/chart-validate.ts +247 -0
- package/src/chart-zoom.test.ts +137 -0
- package/src/chart-zoom.ts +124 -0
- package/src/chart.coverage.test.ts +81 -0
- package/src/chart.test.ts +56 -3
- package/src/chart.ts +557 -2784
- package/src/clipboard.test.ts +151 -0
- package/src/clipboard.ts +145 -16
- package/src/column-resize.ts +7 -1
- package/src/columns.ts +2 -0
- package/src/command-context.test.ts +200 -0
- package/src/command-context.ts +137 -0
- package/src/core.rowmodel-cache.test.ts +51 -0
- package/src/core.ts +40 -12
- package/src/editing.test.ts +281 -3
- package/src/editing.ts +264 -38
- package/src/fill-patterns.test.ts +35 -0
- package/src/fill-patterns.ts +56 -2
- package/src/grid-icons.ts +2 -2
- package/src/grid-messages.ts +21 -3
- package/src/history.test.ts +196 -0
- package/src/history.ts +162 -0
- package/src/icon-seam.test.ts +5 -6
- package/src/index.ts +169 -12
- package/src/keyboard-handlers.coverage.test.ts +126 -6
- package/src/keyboard-handlers.ts +91 -30
- package/src/keyboard-shortcuts.seam.test.ts +200 -0
- package/src/keyboard.test.ts +124 -1
- package/src/keyboard.ts +142 -0
- package/src/menus.test.ts +26 -0
- package/src/menus.ts +10 -3
- package/src/merges.test.ts +112 -0
- package/src/merges.ts +168 -0
- package/src/pivot.grid.test.ts +40 -5
- package/src/row-model.ts +146 -0
- package/src/row-resize.test.ts +44 -0
- package/src/row-resize.ts +17 -0
- package/src/scroll-sync.test.ts +42 -1
- package/src/scroll-sync.ts +25 -0
- package/src/selection.multi-range.test.ts +34 -0
- package/src/selection.test.ts +33 -2
- package/src/selection.ts +143 -21
- 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/shortcut-registry.test.ts +97 -0
- package/src/shortcut-registry.ts +181 -0
- package/src/svgrid-wrapper.types.ts +184 -13
- package/src/svgrid.behavior.test.ts +135 -0
- package/src/svgrid.charting.test.ts +513 -3
- package/src/svgrid.context-menu.test.ts +31 -0
- package/src/svgrid.new-features.wrapper.test.ts +10 -3
- package/src/svgrid.row-model-prop.svelte.test.ts +352 -0
- package/src/svgrid.row-model-seam.svelte.test.ts +289 -0
- package/src/validate.test.ts +10 -0
- package/src/validate.ts +10 -3
- package/src/virtualization/virtualizer.ts +39 -17
- 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/GridMenus-CHOMnW-c.js +0 -602
- package/dist/cdn/SvDateTimePicker-CCbDZNZB.js +0 -816
- package/dist/cdn/SvGridChart-1PMSp2LZ.js +0 -1481
- package/dist/cdn/SvGridChart-D02avhyr.js +0 -1480
- package/dist/cdn/SvGridChartPanel-DJnViOv4.js +0 -834
- package/dist/cdn/SvGridChartPanel-vPTNerFZ.js +0 -810
- package/dist/cdn/SvGridChartView-C--HhdUo.js +0 -56
- package/dist/cdn/SvGridChartView-KyyFG-wY.js +0 -55
- package/dist/cdn/chart-DLg3_zTQ.js +0 -1652
- 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
package/src/row-model.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
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'
|
|
36
|
+
|
|
37
|
+
/** The filter state as the grid reports it, straight from `onFiltersChange`. */
|
|
38
|
+
export type GridFilterState = {
|
|
39
|
+
global: string
|
|
40
|
+
columns: Array<{
|
|
41
|
+
id: string
|
|
42
|
+
operator: FilterOperator
|
|
43
|
+
value: string
|
|
44
|
+
valueTo?: string
|
|
45
|
+
selectedValues?: Array<string>
|
|
46
|
+
}>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** The sort the grid hands a row model: column id and direction, in priority order. */
|
|
50
|
+
export type GridRowModelSort = Array<{ id: string; 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
|
+
/**
|
|
125
|
+
* Turn the grid's filter payload into the column-keyed map a `ServerRequest`
|
|
126
|
+
* carries. Exported because a hand-written `GridRowModel` needs exactly this
|
|
127
|
+
* conversion, and copying it is how the two shapes drift apart.
|
|
128
|
+
*/
|
|
129
|
+
export function toServerFilterColumns(
|
|
130
|
+
filters: GridFilterState,
|
|
131
|
+
): Record<
|
|
132
|
+
string,
|
|
133
|
+
{ operator: string; value: string; valueTo?: string; selectedValues?: Array<string> }
|
|
134
|
+
> {
|
|
135
|
+
return Object.fromEntries(
|
|
136
|
+
filters.columns.map((c) => [
|
|
137
|
+
c.id,
|
|
138
|
+
{
|
|
139
|
+
operator: c.operator,
|
|
140
|
+
value: c.value,
|
|
141
|
+
...(c.valueTo !== undefined ? { valueTo: c.valueTo } : {}),
|
|
142
|
+
...(c.selectedValues !== undefined ? { selectedValues: c.selectedValues } : {}),
|
|
143
|
+
},
|
|
144
|
+
]),
|
|
145
|
+
)
|
|
146
|
+
}
|
package/src/row-resize.test.ts
CHANGED
|
@@ -209,6 +209,50 @@ describe('rowResize - strip injection', () => {
|
|
|
209
209
|
// Drag lifecycle
|
|
210
210
|
// ---------------------------------------------------------------------------
|
|
211
211
|
|
|
212
|
+
describe('rowResize - double-click', () => {
|
|
213
|
+
it('asks for an autosize of the row and keeps the click from the row itself', () => {
|
|
214
|
+
const { host, rows } = buildGrid([{ rowIndex: 0 }, { rowIndex: 1 }])
|
|
215
|
+
track(host)
|
|
216
|
+
const onAutosize = vi.fn()
|
|
217
|
+
const rowSaw = vi.fn()
|
|
218
|
+
rows[1]!.addEventListener('dblclick', rowSaw)
|
|
219
|
+
const onResize = vi.fn()
|
|
220
|
+
const action = rowResize(host, { onResize, onAutosize })
|
|
221
|
+
const strip = stripOf(rows[1]!)!
|
|
222
|
+
// Two presses on one strip inside 400ms are a double-click.
|
|
223
|
+
strip.dispatchEvent(pointer('pointerdown', { clientY: 100 }))
|
|
224
|
+
window.dispatchEvent(pointer('pointerup', { clientY: 100 }))
|
|
225
|
+
strip.dispatchEvent(pointer('pointerdown', { clientY: 100 }))
|
|
226
|
+
expect(onAutosize).toHaveBeenCalledWith(1)
|
|
227
|
+
// No drag started, so nothing to release.
|
|
228
|
+
expect(strip.classList.contains('is-resizing')).toBe(false)
|
|
229
|
+
window.dispatchEvent(pointer('pointerup', { clientY: 100 }))
|
|
230
|
+
// The first press's release reported the unchanged height; the second
|
|
231
|
+
// press started no drag.
|
|
232
|
+
expect(onResize).toHaveBeenCalledTimes(1)
|
|
233
|
+
expect(rowSaw).not.toHaveBeenCalled()
|
|
234
|
+
action.destroy()
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('does nothing without an onAutosize handler', () => {
|
|
238
|
+
const { host, rows } = buildGrid([{ rowIndex: 0 }])
|
|
239
|
+
track(host)
|
|
240
|
+
const rowSaw = vi.fn()
|
|
241
|
+
rows[0]!.addEventListener('dblclick', rowSaw)
|
|
242
|
+
const onResize = vi.fn()
|
|
243
|
+
const action = rowResize(host, { onResize })
|
|
244
|
+
const strip = stripOf(rows[0]!)!
|
|
245
|
+
strip.dispatchEvent(pointer('pointerdown', { clientY: 100 }))
|
|
246
|
+
window.dispatchEvent(pointer('pointerup', { clientY: 100 }))
|
|
247
|
+
strip.dispatchEvent(pointer('pointerdown', { clientY: 100 }))
|
|
248
|
+
// Without a handler the second press is an ordinary drag start.
|
|
249
|
+
expect(strip.classList.contains('is-resizing')).toBe(true)
|
|
250
|
+
window.dispatchEvent(pointer('pointerup', { clientY: 100 }))
|
|
251
|
+
expect(rowSaw).not.toHaveBeenCalled()
|
|
252
|
+
action.destroy()
|
|
253
|
+
})
|
|
254
|
+
})
|
|
255
|
+
|
|
212
256
|
describe('rowResize - drag', () => {
|
|
213
257
|
it('reports the new height on pointerup and fires move callbacks', () => {
|
|
214
258
|
const { host, rows } = buildGrid([{ rowIndex: 0 }])
|
package/src/row-resize.ts
CHANGED
|
@@ -33,6 +33,9 @@ export type RowResizeOptions = {
|
|
|
33
33
|
onResize: (rowIndex: number, height: number) => void
|
|
34
34
|
/** Optional callback fired during the drag (every pointermove). */
|
|
35
35
|
onResizeMove?: (rowIndex: number, height: number) => void
|
|
36
|
+
/** Double-click on the strip: size the row to its content, the
|
|
37
|
+
* spreadsheet gesture. Without it a double-click does nothing. */
|
|
38
|
+
onAutosize?: (rowIndex: number) => void
|
|
36
39
|
/** Min row height in px. Default 20. */
|
|
37
40
|
min?: number
|
|
38
41
|
/** Max row height in px. Default 320. */
|
|
@@ -63,6 +66,8 @@ type ActiveDrag = {
|
|
|
63
66
|
export function rowResize(node: HTMLElement, opts: RowResizeOptions) {
|
|
64
67
|
let current = opts
|
|
65
68
|
let drag: ActiveDrag | null = null
|
|
69
|
+
/** The last press on a strip, for telling a double-click from two drags. */
|
|
70
|
+
let lastPress: { rowIndex: number; at: number } | null = null
|
|
66
71
|
|
|
67
72
|
function rowIndexOf(tr: HTMLTableRowElement): number {
|
|
68
73
|
const dataRow = tr.querySelector<HTMLElement>('[data-svgrid-row]')
|
|
@@ -133,6 +138,18 @@ export function rowResize(node: HTMLElement, opts: RowResizeOptions) {
|
|
|
133
138
|
if (!Number.isFinite(rowIndex)) return
|
|
134
139
|
e.preventDefault()
|
|
135
140
|
e.stopPropagation()
|
|
141
|
+
// The second press of a double-click: size the row to its content
|
|
142
|
+
// instead of starting another drag. Timed here rather than read from
|
|
143
|
+
// a dblclick event, whose target is the row once the first press's
|
|
144
|
+
// resize has re-rendered the strip; pointerdown carries no count.
|
|
145
|
+
const now = e.timeStamp
|
|
146
|
+
const again = lastPress !== null && lastPress.rowIndex === rowIndex && now - lastPress.at < 400
|
|
147
|
+
lastPress = { rowIndex, at: now }
|
|
148
|
+
if (again && current.onAutosize) {
|
|
149
|
+
lastPress = null
|
|
150
|
+
current.onAutosize(rowIndex)
|
|
151
|
+
return
|
|
152
|
+
}
|
|
136
153
|
t.classList.add('is-resizing')
|
|
137
154
|
drag = {
|
|
138
155
|
rowIndex,
|
package/src/scroll-sync.test.ts
CHANGED
|
@@ -203,6 +203,47 @@ describe('scheduleScrollSync / flushScheduledScrollSync', () => {
|
|
|
203
203
|
})
|
|
204
204
|
})
|
|
205
205
|
|
|
206
|
+
describe('syncScrollNow', () => {
|
|
207
|
+
let cancelled: number[] = []
|
|
208
|
+
beforeEach(() => {
|
|
209
|
+
cancelled = []
|
|
210
|
+
vi.stubGlobal('requestAnimationFrame', () => 42)
|
|
211
|
+
vi.stubGlobal('cancelAnimationFrame', (h: number) => {
|
|
212
|
+
cancelled.push(h)
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
afterEach(() => {
|
|
216
|
+
vi.unstubAllGlobals()
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('flushes the virtualizers synchronously, without waiting for a frame', () => {
|
|
220
|
+
const { ctx, ss } = syncFor({
|
|
221
|
+
rowVirtualizationEnabled: true,
|
|
222
|
+
columnVirtualizationEnabled: true,
|
|
223
|
+
domToLogicalRowOffset: (n: number) => n * 2,
|
|
224
|
+
})
|
|
225
|
+
ss.syncScrollNow(30, 88)
|
|
226
|
+
expect(ctx.pendingScrollTop).toBe(30)
|
|
227
|
+
expect(ctx.pendingScrollLeft).toBe(88)
|
|
228
|
+
expect(ctx.scrollVersion).toBe(1)
|
|
229
|
+
expect(ctx.virtualizer.setScrollOffset).toHaveBeenCalledWith(60)
|
|
230
|
+
expect(ctx.columnVirtualizer.setHorizontalOffset).toHaveBeenCalledWith(88)
|
|
231
|
+
expect(ctx.scrollSyncRaf).toBeNull()
|
|
232
|
+
expect(cancelled).toEqual([])
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
it('cancels a frame that a scroll event already scheduled, so the position is not applied twice', () => {
|
|
236
|
+
const { ctx, ss } = syncFor({ rowVirtualizationEnabled: true })
|
|
237
|
+
ss.scheduleScrollSync(10, 0)
|
|
238
|
+
expect(ctx.scrollSyncRaf).toBe(42)
|
|
239
|
+
ss.syncScrollNow(64, 0)
|
|
240
|
+
expect(cancelled).toEqual([42])
|
|
241
|
+
expect(ctx.scrollSyncRaf).toBeNull()
|
|
242
|
+
expect(ctx.virtualizer.setScrollOffset).toHaveBeenCalledTimes(1)
|
|
243
|
+
expect(ctx.virtualizer.setScrollOffset).toHaveBeenCalledWith(64)
|
|
244
|
+
})
|
|
245
|
+
})
|
|
246
|
+
|
|
206
247
|
describe('onBodyScroll', () => {
|
|
207
248
|
beforeEach(() => {
|
|
208
249
|
// These tests assert what happens BEFORE the frame runs, so the stub just
|
|
@@ -321,7 +362,7 @@ describe('createScrollSync wiring', () => {
|
|
|
321
362
|
it('exposes the documented handler surface', () => {
|
|
322
363
|
const { ss } = syncFor()
|
|
323
364
|
expect(Object.keys(ss).sort()).toEqual(
|
|
324
|
-
['flushScheduledScrollSync', 'hideTooltip', 'onBodyScroll', 'scheduleScrollSync', 'showTooltipFor'],
|
|
365
|
+
['flushScheduledScrollSync', 'hideTooltip', 'onBodyScroll', 'scheduleScrollSync', 'showTooltipFor', 'syncScrollNow'],
|
|
325
366
|
)
|
|
326
367
|
})
|
|
327
368
|
})
|
package/src/scroll-sync.ts
CHANGED
|
@@ -66,6 +66,30 @@ export function createScrollSync<
|
|
|
66
66
|
ctx.scrollSyncRaf = requestAnimationFrame(flushScheduledScrollSync);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Sync the virtualizers to a scroll position NOW instead of on the next
|
|
71
|
+
* animation frame.
|
|
72
|
+
*
|
|
73
|
+
* For a scroll the grid made itself - keyboard navigation bringing the
|
|
74
|
+
* active cell into view - the position is already known, and going through
|
|
75
|
+
* the browser's `scroll` event and then the rAF above put the row-window
|
|
76
|
+
* update one frame behind the key press: the keydown's own flush painted
|
|
77
|
+
* the active cell on the OLD rows, and the rows only caught up on the next
|
|
78
|
+
* frame. Two painted frames per arrow key. Syncing inline folds the window
|
|
79
|
+
* update into the same flush as the active-cell change, so the key press
|
|
80
|
+
* paints once. The `scroll` event still fires afterwards and re-syncs to
|
|
81
|
+
* the same offset, which the virtualizers treat as a no-op.
|
|
82
|
+
*/
|
|
83
|
+
function syncScrollNow(scrollTop: number, scrollLeft: number) {
|
|
84
|
+
if (ctx.scrollSyncRaf !== null) {
|
|
85
|
+
cancelAnimationFrame(ctx.scrollSyncRaf);
|
|
86
|
+
ctx.scrollSyncRaf = null;
|
|
87
|
+
}
|
|
88
|
+
ctx.pendingScrollTop = scrollTop;
|
|
89
|
+
ctx.pendingScrollLeft = scrollLeft;
|
|
90
|
+
flushScheduledScrollSync();
|
|
91
|
+
}
|
|
92
|
+
|
|
69
93
|
function onBodyScroll(event: Event) {
|
|
70
94
|
const container = event.currentTarget as HTMLDivElement | null;
|
|
71
95
|
if (!container) return;
|
|
@@ -91,6 +115,7 @@ export function createScrollSync<
|
|
|
91
115
|
hideTooltip,
|
|
92
116
|
flushScheduledScrollSync,
|
|
93
117
|
scheduleScrollSync,
|
|
118
|
+
syncScrollNow,
|
|
94
119
|
onBodyScroll,
|
|
95
120
|
};
|
|
96
121
|
}
|
|
@@ -58,4 +58,38 @@ describe('multiple range selection', () => {
|
|
|
58
58
|
// outside every range
|
|
59
59
|
expect(s.getCellRangeEdges(2, 2)).toBeNull()
|
|
60
60
|
})
|
|
61
|
+
|
|
62
|
+
it('cellSelectionState folds active cell, range edges and fill handle into one value', () => {
|
|
63
|
+
const ctx = makeCtx()
|
|
64
|
+
ctx.activeCell = { rowIndex: 1, colIndex: 1, cellId: null }
|
|
65
|
+
ctx.fillHandleCell = { rowIndex: 1, colIndex: 1 }
|
|
66
|
+
const s = createSelection(ctx)
|
|
67
|
+
s.setSelection(0, 0)
|
|
68
|
+
s.extendSelection(1, 1)
|
|
69
|
+
|
|
70
|
+
// The focus corner: active, on the bottom-right edges, carries the handle.
|
|
71
|
+
expect(s.cellSelectionState(1, 1)).toEqual({
|
|
72
|
+
active: true,
|
|
73
|
+
edges: { top: false, bottom: true, left: false, right: true },
|
|
74
|
+
fillHandle: true,
|
|
75
|
+
})
|
|
76
|
+
// Another cell of the range: in the range, not active, no handle.
|
|
77
|
+
expect(s.cellSelectionState(0, 1)).toEqual({
|
|
78
|
+
active: false,
|
|
79
|
+
edges: { top: true, bottom: false, left: false, right: true },
|
|
80
|
+
fillHandle: false,
|
|
81
|
+
})
|
|
82
|
+
// A plain cell is `null`, not an all-false object, so the view can tell
|
|
83
|
+
// "nothing changed here" by identity.
|
|
84
|
+
expect(s.cellSelectionState(5, 5)).toBeNull()
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('cellSelectionState reports the active cell even with no range anchored', () => {
|
|
88
|
+
const ctx = makeCtx()
|
|
89
|
+
ctx.activeCell = { rowIndex: 2, colIndex: 3, cellId: null }
|
|
90
|
+
ctx.fillHandleCell = null
|
|
91
|
+
const s = createSelection(ctx)
|
|
92
|
+
expect(s.cellSelectionState(2, 3)).toEqual({ active: true, edges: null, fillHandle: false })
|
|
93
|
+
expect(s.cellSelectionState(2, 4)).toBeNull()
|
|
94
|
+
})
|
|
61
95
|
})
|
package/src/selection.test.ts
CHANGED
|
@@ -39,6 +39,9 @@ function makeCtx(overrides: any = {}) {
|
|
|
39
39
|
allRows: [],
|
|
40
40
|
allColumns: [],
|
|
41
41
|
headerSelectionState: 'none',
|
|
42
|
+
// The real controller always supplies this; a fake that omits it would
|
|
43
|
+
// make every select-all path throw rather than assert.
|
|
44
|
+
placeholderStateOf: () => null,
|
|
42
45
|
userHasActivatedCell: false,
|
|
43
46
|
enableCellSelectionEffective: true,
|
|
44
47
|
selectionRange: { anchor: null, focus: null },
|
|
@@ -75,6 +78,8 @@ function makeCtx(overrides: any = {}) {
|
|
|
75
78
|
// for plain drag-select. Its own behaviour is covered in move-cells.test.ts.
|
|
76
79
|
trackEdgeScroll: vi.fn(),
|
|
77
80
|
stopEdgeScroll: vi.fn(),
|
|
81
|
+
// Inline virtualizer sync after a programmatic scroll (scroll-sync.ts).
|
|
82
|
+
syncScrollNow: vi.fn(),
|
|
78
83
|
grid: {
|
|
79
84
|
setRowSelection,
|
|
80
85
|
setActiveCell,
|
|
@@ -367,6 +372,19 @@ describe('createSelection - onCellPointerDown', () => {
|
|
|
367
372
|
expect(ctx.selectionRange.anchor).toEqual({ rowIndex: 0, colIndex: 0 })
|
|
368
373
|
expect(ctx.selectionRange.focus).toEqual({ rowIndex: 1, colIndex: 1 })
|
|
369
374
|
expect(ctx.activeAtPointerDown).toEqual({ rowIndex: 0, colIndex: 0 })
|
|
375
|
+
// The active cell stays at the anchor, as in Excel.
|
|
376
|
+
expect(ctx.grid.setActiveCell).not.toHaveBeenCalled()
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
it('Shift+click on a grid never clicked makes the cell active', () => {
|
|
380
|
+
const ctx = makeCtx({
|
|
381
|
+
allRows: [makeDataRow('r0'), makeDataRow('r1')],
|
|
382
|
+
allColumns: [makeColumn('c0'), makeColumn('c1')],
|
|
383
|
+
activeCell: null,
|
|
384
|
+
})
|
|
385
|
+
const sel = createSelection(ctx)
|
|
386
|
+
sel.onCellPointerDown(1, 1, { button: 0, shiftKey: true } as any)
|
|
387
|
+
expect(ctx.grid.setActiveCell).toHaveBeenCalled()
|
|
370
388
|
})
|
|
371
389
|
})
|
|
372
390
|
|
|
@@ -378,7 +396,7 @@ describe('createSelection - onCellPointerEnter / drag', () => {
|
|
|
378
396
|
expect(ctx.grid.setActiveCell).not.toHaveBeenCalled()
|
|
379
397
|
})
|
|
380
398
|
|
|
381
|
-
it('extends selection and
|
|
399
|
+
it('extends the selection while dragging and leaves the active cell where the drag began', () => {
|
|
382
400
|
const ctx = makeCtx({
|
|
383
401
|
isDraggingSelection: true,
|
|
384
402
|
selectionRange: { anchor: { rowIndex: 0, colIndex: 0 }, focus: { rowIndex: 0, colIndex: 0 } },
|
|
@@ -388,7 +406,7 @@ describe('createSelection - onCellPointerEnter / drag', () => {
|
|
|
388
406
|
const sel = createSelection(ctx)
|
|
389
407
|
sel.onCellPointerEnter(1, 1)
|
|
390
408
|
expect(ctx.selectionRange.focus).toEqual({ rowIndex: 1, colIndex: 1 })
|
|
391
|
-
expect(ctx.grid.setActiveCell).toHaveBeenCalled()
|
|
409
|
+
expect(ctx.grid.setActiveCell).not.toHaveBeenCalled()
|
|
392
410
|
})
|
|
393
411
|
|
|
394
412
|
it('ignores group rows while dragging', () => {
|
|
@@ -649,6 +667,19 @@ describe('createSelection - onCellClick', () => {
|
|
|
649
667
|
expect(ctx.onCellDoubleClick).toHaveBeenCalledWith(0, 0)
|
|
650
668
|
})
|
|
651
669
|
|
|
670
|
+
it('leaves the cell alone on a second click when editOnSecondClick is off', () => {
|
|
671
|
+
// A sheet: clicking the active cell again is a click, not an edit.
|
|
672
|
+
const ctx = clickCtx({
|
|
673
|
+
editingEnabled: true,
|
|
674
|
+
activeAtPointerDown: { rowIndex: 0, colIndex: 0 },
|
|
675
|
+
props: { editOnSecondClick: false },
|
|
676
|
+
})
|
|
677
|
+
const sel = createSelection(ctx)
|
|
678
|
+
sel.onCellClick(0, 0)
|
|
679
|
+
expect(ctx.onCellDoubleClick).not.toHaveBeenCalled()
|
|
680
|
+
expect(ctx.editingCell).toBeNull()
|
|
681
|
+
})
|
|
682
|
+
|
|
652
683
|
it('clears editing when not re-clicking the active cell', () => {
|
|
653
684
|
const ctx = clickCtx({
|
|
654
685
|
editingEnabled: true,
|