@svgrid/grid 2.2.31 → 2.2.33
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 +1 -1
- package/dist/DockNodeView.svelte +1 -1
- package/dist/GridFooter.svelte +0 -40
- package/dist/GridMenus.svelte +0 -77
- package/dist/SvAvatar.svelte +1 -1
- package/dist/SvCheckBox.svelte +2 -1
- package/dist/SvCircularProgress.svelte +1 -1
- package/dist/SvDateRangeInput.svelte +1 -1
- package/dist/SvGrid.controller.svelte.d.ts +20 -0
- package/dist/SvGrid.controller.svelte.js +358 -18
- package/dist/SvGrid.css +103 -3
- package/dist/SvGrid.svelte +164 -35
- package/dist/SvGrid.types.d.ts +81 -0
- package/dist/SvGridChart.svelte +2 -10
- package/dist/SvGridDropdown.svelte +38 -6
- package/dist/SvGridDropdown.svelte.d.ts +4 -0
- package/dist/SvGridSelect.svelte +12 -5
- package/dist/SvGridSelect.svelte.d.ts +6 -0
- package/dist/SvGroupCell.svelte +9 -2
- package/dist/SvKbd.svelte +1 -1
- package/dist/SvList.svelte +1 -1
- package/dist/SvMenubar.svelte +1 -1
- package/dist/SvMultiSelect.svelte +12 -4
- package/dist/SvMultiSelect.svelte.d.ts +6 -0
- package/dist/SvOtpInput.svelte +1 -1
- package/dist/SvPasswordInput.svelte +1 -1
- package/dist/SvProgress.svelte +1 -1
- package/dist/SvRating.svelte +1 -1
- package/dist/SvResult.svelte +1 -1
- package/dist/SvRichText.svelte +35 -16
- package/dist/SvRichText.svelte.d.ts +9 -0
- package/dist/SvSwitchButton.svelte +2 -1
- package/dist/SvToaster.svelte +1 -1
- package/dist/SvTour.svelte +4 -0
- package/dist/SvTree.svelte +9 -2
- package/dist/SvTree.svelte.d.ts +5 -0
- package/dist/SvTreeSelect.svelte +6 -4
- package/dist/build-api.js +16 -0
- package/dist/cdn/{GridMenus-Ds3OpzZT.js → GridMenus-B7XhhqdN.js} +34 -36
- package/dist/cdn/{GridMenus-Da_p7SKS.js → GridMenus-BxmIvCJh.js} +35 -37
- package/dist/cdn/{SvDateTimePicker-DB0aIEk8.js → SvDateTimePicker-ClqtVIj3.js} +2 -1
- package/dist/cdn/{SvDateTimePicker-B8GopjhC.js → SvDateTimePicker-o94wfSUa.js} +3 -2
- package/dist/cdn/{SvGridChart-BcLCYvw9.js → SvGridChart-Cr_90pPD.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-DvjwxLvK.js → SvGridChartPanel-ICbxiUvK.js} +4 -4
- package/dist/cdn/{SvGridChartView-PwqI4s02.js → SvGridChartView-Bx42PMvf.js} +2 -2
- package/dist/cdn/{SvGridDropdown-hpnHvOGt.js → SvGridDropdown-CUOdm7YA.js} +93 -88
- package/dist/cdn/{SvGridDropdown-DjALc_4f.js → SvGridDropdown-D0VdjeR8.js} +129 -123
- package/dist/cdn/{disclose-version-DoD9AFD_.js → disclose-version-DljhZohK.js} +45 -45
- package/dist/cdn/{src-DYDCiC0D.js → src-erjBRYfX.js} +5952 -5615
- package/dist/cdn/{src-BLJfdyL0.js → src-ktbQVOk-.js} +4190 -3851
- package/dist/cdn/svgrid.js +9 -9
- package/dist/cdn/svgrid.svelte-external.js +8 -8
- package/dist/cell-render.d.ts +1 -0
- package/dist/cell-render.js +115 -1
- package/dist/cell-values.js +5 -0
- package/dist/chart.d.ts +2 -0
- package/dist/chart.js +1 -4
- package/dist/core.d.ts +80 -9
- package/dist/core.js +89 -0
- package/dist/createMenu.svelte.js +0 -1
- package/dist/createPopoverSelect.svelte.d.ts +2 -2
- package/dist/editing.js +1 -1
- package/dist/editor-contract.d.ts +12 -5
- package/dist/editor-registry.d.ts +18 -7
- package/dist/editor-registry.js +10 -1
- package/dist/export-format.js +3 -0
- package/dist/group-display.d.ts +39 -0
- package/dist/group-display.js +52 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/keyboard-handlers.js +16 -0
- package/dist/recurrence.js +3 -2
- package/dist/selection.js +0 -1
- package/dist/svgrid-wrapper.types.d.ts +8 -2
- package/dist/themes/index.d.ts +10 -0
- package/dist/themes/index.js +26 -0
- package/package.json +3 -2
- package/src/DockNodeView.svelte +1 -1
- package/src/GridFooter.svelte +0 -40
- package/src/GridMenus.svelte +0 -77
- package/src/SvAccordion.test.ts +5 -5
- package/src/SvAvatar.svelte +1 -1
- package/src/SvButtonGroup.test.ts +6 -6
- package/src/SvCalendar.test.ts +1 -1
- package/src/SvCheckBox.svelte +2 -1
- package/src/SvCircularProgress.svelte +1 -1
- package/src/SvDateRangeInput.svelte +1 -1
- package/src/SvFileUpload.test.ts +1 -1
- package/src/SvGrid.controller.svelte.ts +367 -22
- package/src/SvGrid.css +103 -3
- package/src/SvGrid.svelte +164 -35
- package/src/SvGrid.types.ts +81 -1
- package/src/SvGridChart.svelte +2 -10
- package/src/SvGridDropdown.svelte +38 -6
- package/src/SvGridSelect.svelte +12 -5
- package/src/SvGroupCell.svelte +9 -2
- package/src/SvKbd.svelte +1 -1
- package/src/SvList.svelte +1 -1
- package/src/SvMenubar.svelte +1 -1
- package/src/SvMultiSelect.svelte +12 -4
- package/src/SvOtpInput.svelte +1 -1
- package/src/SvPasswordInput.svelte +1 -1
- package/src/SvProgress.svelte +1 -1
- package/src/SvRating.svelte +1 -1
- package/src/SvResult.svelte +1 -1
- package/src/SvRichText.svelte +35 -16
- package/src/SvSwitchButton.svelte +2 -1
- package/src/SvToaster.svelte +1 -1
- package/src/SvTour.svelte +4 -0
- package/src/SvTree.svelte +9 -2
- package/src/SvTreeSelect.svelte +6 -4
- package/src/ai.test.ts +1 -1
- package/src/build-api.ts +15 -105
- package/src/builtin-editors.test.ts +3 -1
- package/src/cell-formatting.test.ts +1 -1
- package/src/cell-render.test.ts +27 -3
- package/src/cell-render.ts +122 -95
- package/src/cell-values.ts +4 -0
- package/src/chart-export.test.ts +2 -2
- package/src/chart.ts +1 -2
- package/src/clipboard.test.ts +0 -2
- package/src/clipboard.ts +0 -102
- package/src/columns.ts +0 -115
- package/src/core.ts +157 -8
- package/src/createMenu.svelte.ts +0 -1
- package/src/editing.test.ts +1 -1
- package/src/editing.ts +2 -105
- package/src/editor-contract.ts +12 -5
- package/src/editor-registry.grid.test.ts +48 -2
- package/src/editor-registry.test.ts +3 -1
- package/src/editor-registry.ts +23 -7
- package/src/export-format.ts +3 -0
- package/src/facet-buckets.test.ts +33 -33
- package/src/features.ts +0 -90
- package/src/group-display.test.ts +91 -1
- package/src/group-display.ts +83 -0
- package/src/index.ts +9 -0
- package/src/keyboard-handlers.coverage.test.ts +1 -1
- package/src/keyboard-handlers.ts +17 -120
- package/src/list-power.test.ts +2 -2
- package/src/menus.test.ts +4 -5
- package/src/menus.ts +0 -112
- package/src/named-views.coverage.test.ts +3 -3
- package/src/new-features.test.ts +3 -1
- package/src/recurrence.ts +3 -2
- package/src/row-drag.test.ts +1 -1
- package/src/row-resize.test.ts +41 -42
- package/src/scheduler-model.ts +1 -1
- package/src/scroll-sync.test.ts +4 -7
- package/src/scroll-sync.ts +0 -122
- package/src/selection.ts +0 -109
- package/src/server-data-source.coverage.test.ts +1 -1
- package/src/server-group-model.test.ts +8 -8
- package/src/spreadsheet.test.ts +0 -1
- package/src/summaries.ts +0 -112
- package/src/svgrid-wrapper.types.ts +11 -9
- package/src/svgrid.async-editor-options.test.ts +271 -0
- package/src/svgrid.auto-row-height.test.ts +204 -0
- package/src/svgrid.behavior.test.ts +0 -1
- package/src/svgrid.charting.test.ts +4 -4
- package/src/svgrid.column-reorder.test.ts +0 -8
- package/src/svgrid.comments-autocomplete.test.ts +8 -2
- package/src/svgrid.context-menu.test.ts +25 -9
- package/src/svgrid.filter-depth.test.ts +163 -0
- package/src/svgrid.filter-menu-listbox.svelte.test.ts +16 -2
- package/src/svgrid.filter-menu-scroll.test.ts +10 -2
- package/src/svgrid.grand-total.test.ts +188 -0
- package/src/svgrid.group-display-mode.test.ts +171 -0
- package/src/svgrid.group-footers.test.ts +121 -0
- package/src/svgrid.group-pagination.test.ts +153 -0
- package/src/svgrid.tree-data.test.ts +186 -0
- package/src/svgriddropdown.async-panel.svelte.test.ts +195 -0
- package/src/themes/index.ts +42 -0
- package/src/tree-edit.test.ts +4 -4
- package/src/tree-row-model.test.ts +168 -0
- package/src/ui-localization.test.ts +113 -0
- package/themes/ag-alpine.css +22 -0
- package/themes/antd.css +22 -0
- package/themes/atlassian.css +22 -0
- package/themes/bootstrap.css +22 -0
- package/themes/carbon.css +22 -0
- package/themes/catppuccin.css +22 -0
- package/themes/dracula.css +22 -0
- package/themes/ember.css +22 -0
- package/themes/excel.css +22 -0
- package/themes/fluent.css +22 -0
- package/themes/github.css +22 -0
- package/themes/linear.css +22 -0
- package/themes/material.css +22 -0
- package/themes/nord.css +22 -0
- package/themes/notion.css +22 -0
- package/themes/salesforce.css +22 -0
- package/themes/sap.css +22 -0
- package/themes/shadcn.css +22 -0
- package/themes/tailwind.css +22 -0
- package/themes/vercel.css +22 -0
package/src/cell-render.test.ts
CHANGED
|
@@ -9,16 +9,35 @@ import { createCellRender } from './cell-render'
|
|
|
9
9
|
// shape that mirrors the @svgrid/grid runtime closely enough to exercise
|
|
10
10
|
// every branch.
|
|
11
11
|
|
|
12
|
+
// Only `id` / `original` / `getCellValueByColumnId` are read by the functions
|
|
13
|
+
// under test, but the stub carries the full `Row` shape so it is assignable
|
|
14
|
+
// without a cast at every call site.
|
|
12
15
|
type AnyRow = {
|
|
13
16
|
id: string
|
|
17
|
+
index: number
|
|
14
18
|
original: Record<string, unknown>
|
|
19
|
+
depth: number
|
|
20
|
+
getCanExpand: () => boolean
|
|
21
|
+
getIsExpanded: () => boolean
|
|
22
|
+
toggleExpanded: () => void
|
|
23
|
+
getIsSelected: () => boolean
|
|
24
|
+
toggleSelected: () => void
|
|
25
|
+
getAllCells: () => never[]
|
|
15
26
|
getCellValueByColumnId: (colId: string) => unknown
|
|
16
27
|
}
|
|
17
28
|
|
|
18
29
|
function makeRow(id: string, data: Record<string, unknown>): AnyRow {
|
|
19
30
|
return {
|
|
20
31
|
id,
|
|
32
|
+
index: 0,
|
|
21
33
|
original: data,
|
|
34
|
+
depth: 0,
|
|
35
|
+
getCanExpand: () => false,
|
|
36
|
+
getIsExpanded: () => false,
|
|
37
|
+
toggleExpanded: () => {},
|
|
38
|
+
getIsSelected: () => false,
|
|
39
|
+
toggleSelected: () => {},
|
|
40
|
+
getAllCells: () => [],
|
|
22
41
|
getCellValueByColumnId: (colId: string) => data[colId],
|
|
23
42
|
}
|
|
24
43
|
}
|
|
@@ -34,6 +53,11 @@ function makeCtx(overrides: Partial<any> = {}): any {
|
|
|
34
53
|
conditionalColumnStats: new Map(),
|
|
35
54
|
noteOverrides: {},
|
|
36
55
|
editorOptionsCache: {},
|
|
56
|
+
// Async `editorOptions` bookkeeping: resolved lists (reactive in the real
|
|
57
|
+
// controller) plus two plain Sets used as dedupe guards.
|
|
58
|
+
asyncEditorOptions: {} as Record<string, unknown>,
|
|
59
|
+
asyncEditorOptionsPending: new Set<string>(),
|
|
60
|
+
asyncEditorColumns: new Set<string>(),
|
|
37
61
|
...overrides,
|
|
38
62
|
}
|
|
39
63
|
}
|
|
@@ -172,7 +196,7 @@ describe('createCellRender / computeCellClass', () => {
|
|
|
172
196
|
const row = makeRow('r', { score: 9 })
|
|
173
197
|
expect(cr.computeCellClass(row, makeColumn('score', { cellClass }))).toBe('big')
|
|
174
198
|
expect(cellClass).toHaveBeenCalledTimes(1)
|
|
175
|
-
const passed = cellClass.mock.calls[0][0]
|
|
199
|
+
const passed = cellClass.mock.calls[0]![0]
|
|
176
200
|
expect(passed.row).toBe(row)
|
|
177
201
|
expect(passed.getValue()).toBe(9)
|
|
178
202
|
})
|
|
@@ -423,7 +447,7 @@ describe('createCellRender / formatPinnedValue', () => {
|
|
|
423
447
|
const formatter = vi.fn(({ value, row }: any) => `${value}:${row}`)
|
|
424
448
|
const col = makeColumn('a', { formatter })
|
|
425
449
|
expect(cr.formatPinnedValue(col, 5)).toBe('5:null')
|
|
426
|
-
expect(formatter.mock.calls[0][0].row).toBeNull()
|
|
450
|
+
expect(formatter.mock.calls[0]![0].row).toBeNull()
|
|
427
451
|
})
|
|
428
452
|
|
|
429
453
|
it('masks password pinned values', () => {
|
|
@@ -478,7 +502,7 @@ describe('createCellRender / computePinnedCellClass', () => {
|
|
|
478
502
|
field: 'a',
|
|
479
503
|
})
|
|
480
504
|
expect(cr.computePinnedCellClass({ a: 3 }, col)).toBe('pos')
|
|
481
|
-
expect(cellClass.mock.calls[0][0].row).toBeNull()
|
|
505
|
+
expect(cellClass.mock.calls[0]![0].row).toBeNull()
|
|
482
506
|
})
|
|
483
507
|
|
|
484
508
|
it('coerces a falsy cellClass result to empty string', () => {
|
package/src/cell-render.ts
CHANGED
|
@@ -2,127 +2,33 @@
|
|
|
2
2
|
// reading/writing controller state via the `ctx` handle; the reactive core
|
|
3
3
|
// ($state/$derived/$effect) stays in the controller.
|
|
4
4
|
import {
|
|
5
|
-
applyExcelFilter,
|
|
6
|
-
normalizeForFilter,
|
|
7
|
-
createColumnVirtualizer,
|
|
8
|
-
createCoreRowModel,
|
|
9
|
-
createExpandedRowModel,
|
|
10
|
-
createFilteredRowModel,
|
|
11
|
-
createGroupedRowModel,
|
|
12
|
-
createPaginatedRowModel,
|
|
13
|
-
createSvelteVirtualizer,
|
|
14
|
-
createSortedRowModel,
|
|
15
|
-
createSvGrid,
|
|
16
|
-
filterFns,
|
|
17
|
-
getGridCellA11yProps,
|
|
18
|
-
getGridCellDomId,
|
|
19
|
-
getGridHeaderA11yProps,
|
|
20
|
-
getGridRootA11yProps,
|
|
21
|
-
getGridRowA11yProps,
|
|
22
|
-
parseEditorValue,
|
|
23
5
|
normalizeEditorOptions,
|
|
24
|
-
sortFns,
|
|
25
|
-
tableFeatures,
|
|
26
|
-
rowSortingFeature,
|
|
27
|
-
columnFilteringFeature,
|
|
28
|
-
columnGroupingFeature,
|
|
29
|
-
type CellContext,
|
|
30
|
-
type EditorContext,
|
|
31
6
|
type CellEditorOption,
|
|
32
|
-
type CellEditorType,
|
|
33
7
|
type CellFormatter,
|
|
34
8
|
type CellFormatConfig,
|
|
35
9
|
type Column,
|
|
36
|
-
type ColumnDef,
|
|
37
10
|
type Row,
|
|
38
11
|
type RowData,
|
|
39
|
-
type SvGridApi,
|
|
40
12
|
type TableFeatures,
|
|
41
13
|
} from "./index";
|
|
42
14
|
import "./sv-grid-scrollbar";
|
|
43
|
-
import type { Snippet } from "svelte";
|
|
44
|
-
import { getKeyboardIntent, getNextActiveCell } from "./keyboard";
|
|
45
15
|
import {
|
|
46
16
|
formatNumericWithConfig,
|
|
47
17
|
getDateFormatter,
|
|
48
18
|
resolveDatePattern,
|
|
49
19
|
} from "./cell-formatting";
|
|
50
|
-
import {
|
|
51
|
-
RenderSnippetConfig,
|
|
52
|
-
RenderComponentConfig,
|
|
53
|
-
} from "./render-component";
|
|
54
|
-
import { buildFillPattern } from "./fill-patterns";
|
|
55
|
-
import { buildSparkline, toSparklineValues } from "./sparkline";
|
|
56
20
|
import {
|
|
57
21
|
resolveCellFormat,
|
|
58
|
-
computeColumnStat,
|
|
59
|
-
formatsNeedingStats,
|
|
60
|
-
type ColumnStat,
|
|
61
22
|
type ResolvedCellFormat,
|
|
62
23
|
} from "./conditional-formatting";
|
|
63
|
-
import SvGridDropdown from "./SvGridDropdown.svelte";
|
|
64
|
-
import type {
|
|
65
|
-
Props,
|
|
66
|
-
SelectionPoint,
|
|
67
|
-
SelectionRange,
|
|
68
|
-
CellEditState,
|
|
69
|
-
FilterOperator,
|
|
70
|
-
FilterOption,
|
|
71
|
-
MenuPosition,
|
|
72
|
-
} from "./SvGrid.types";
|
|
73
24
|
import {
|
|
74
|
-
cfTextStyle,
|
|
75
|
-
fmtStat,
|
|
76
25
|
getCellKey,
|
|
77
26
|
resolveClassList,
|
|
78
|
-
toDateInputValue,
|
|
79
|
-
toDateTimeLocalInputValue,
|
|
80
|
-
getEditableInputValue,
|
|
81
|
-
getEditorInputType,
|
|
82
|
-
toValueArray,
|
|
83
27
|
getOptionLabel,
|
|
84
|
-
getOptionColor,
|
|
85
|
-
colorfulChipStyle,
|
|
86
|
-
getEditorClass,
|
|
87
28
|
asDate,
|
|
88
|
-
clampMenuX,
|
|
89
|
-
cssEscape,
|
|
90
|
-
rawToNumber,
|
|
91
|
-
formatFacetNumber,
|
|
92
|
-
formatFacetDate,
|
|
93
29
|
} from "./SvGrid.helpers";
|
|
94
|
-
import { createEditing } from "./editing";
|
|
95
|
-
import { createSelection } from "./selection";
|
|
96
|
-
import { createColumns } from "./columns";
|
|
97
|
-
import { createGridApi } from "./build-api";
|
|
98
|
-
import { createClipboard } from "./clipboard";
|
|
99
|
-
import {
|
|
100
|
-
filterOperatorOptions,
|
|
101
|
-
fallbackOperatorOption,
|
|
102
|
-
TEXT_OPERATORS,
|
|
103
|
-
NUMBER_OPERATORS,
|
|
104
|
-
DATE_OPERATORS,
|
|
105
|
-
CHECKBOX_OPERATORS,
|
|
106
|
-
operatorOption,
|
|
107
|
-
operatorsForColumn,
|
|
108
|
-
defaultOperatorFor,
|
|
109
|
-
operatorLabelFor,
|
|
110
|
-
} from "./filter-operators";
|
|
111
30
|
import {
|
|
112
|
-
type FacetBucket,
|
|
113
|
-
isBucketableColumn,
|
|
114
|
-
buildBuckets,
|
|
115
|
-
isInBucket,
|
|
116
|
-
} from "./facet-buckets";
|
|
117
|
-
import {
|
|
118
|
-
getColumnBaseValue,
|
|
119
|
-
isGroupRow,
|
|
120
|
-
toolPanelHeaderLabel,
|
|
121
|
-
formatSummaryNumeric,
|
|
122
|
-
getColumnAlign,
|
|
123
31
|
getPinnedCellValue,
|
|
124
|
-
getColumnAccessorValue,
|
|
125
|
-
columnDefMatchesId,
|
|
126
32
|
} from "./cell-values";
|
|
127
33
|
|
|
128
34
|
export function createCellRender<
|
|
@@ -263,6 +169,77 @@ export function createCellRender<
|
|
|
263
169
|
return v && v.trim() ? v : null
|
|
264
170
|
}
|
|
265
171
|
|
|
172
|
+
const isThenable = (v: unknown): v is Promise<unknown> =>
|
|
173
|
+
typeof (v as { then?: unknown } | null)?.then === "function";
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Resolve a promised option list once per key, then keep it.
|
|
177
|
+
*
|
|
178
|
+
* Render paths call `getColumnEditorOptions` synchronously and often, so an
|
|
179
|
+
* async source cannot block: the first call starts the fetch and returns an
|
|
180
|
+
* empty list, and the resolved value lands in reactive state which re-renders
|
|
181
|
+
* the editor. `asyncEditorOptionsPending` tracks in-flight keys so repeated
|
|
182
|
+
* renders do not fire the request again.
|
|
183
|
+
*/
|
|
184
|
+
function resolveAsyncOptions(
|
|
185
|
+
key: string,
|
|
186
|
+
promise: Promise<unknown>,
|
|
187
|
+
/** Drop other cached entries starting with this prefix when the result
|
|
188
|
+
* lands - used to evict a cascade's superseded signatures. */
|
|
189
|
+
prunePrefix?: string,
|
|
190
|
+
): CellEditorOption[] {
|
|
191
|
+
const done = ctx.asyncEditorOptions[key];
|
|
192
|
+
if (done) return done;
|
|
193
|
+
const pending: Set<string> = ctx.asyncEditorOptionsPending;
|
|
194
|
+
if (!pending.has(key)) {
|
|
195
|
+
// Plain Set, not reactive state: this runs inside the template, and a
|
|
196
|
+
// $state write during render is a Svelte 5 error.
|
|
197
|
+
pending.add(key);
|
|
198
|
+
const store = (value: CellEditorOption[]) => {
|
|
199
|
+
const next: Record<string, CellEditorOption[]> = {};
|
|
200
|
+
for (const [k, v] of Object.entries(ctx.asyncEditorOptions as Record<string, CellEditorOption[]>)) {
|
|
201
|
+
// Keep everything except this cell's stale signatures.
|
|
202
|
+
if (prunePrefix && k !== key && k.startsWith(prunePrefix)) continue;
|
|
203
|
+
next[k] = v;
|
|
204
|
+
}
|
|
205
|
+
next[key] = value;
|
|
206
|
+
ctx.asyncEditorOptions = next;
|
|
207
|
+
};
|
|
208
|
+
promise
|
|
209
|
+
.then((value) => store(normalizeEditorOptions(value as never)))
|
|
210
|
+
// A failed lookup settles on "no options" rather than leaving the
|
|
211
|
+
// editor spinning forever.
|
|
212
|
+
.catch(() => store([]))
|
|
213
|
+
.finally(() => {
|
|
214
|
+
pending.delete(key);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Cache key for a per-row async source.
|
|
222
|
+
*
|
|
223
|
+
* A cascade depends on the row's OTHER cells - City is a function of Country -
|
|
224
|
+
* so keying by row id alone made the first result permanent: changing the
|
|
225
|
+
* country left the old city list cached forever. The row's own values are
|
|
226
|
+
* folded into the key, so editing the row naturally supersedes the entry.
|
|
227
|
+
*
|
|
228
|
+
* Only reached for columns already known to be async, so the stringify cost
|
|
229
|
+
* is bounded by the rows actually rendering such a column.
|
|
230
|
+
*/
|
|
231
|
+
function asyncRowKey(column: Column<TData>, row: Row<TData>): string {
|
|
232
|
+
let signature: string;
|
|
233
|
+
try {
|
|
234
|
+
signature = JSON.stringify(row.original);
|
|
235
|
+
} catch {
|
|
236
|
+
// Circular / non-serializable row: fall back to identity, which at least
|
|
237
|
+
// invalidates when the consumer replaces the object immutably.
|
|
238
|
+
signature = String(row.index);
|
|
239
|
+
}
|
|
240
|
+
return `${column.id}::${row.id}::${signature}`;
|
|
241
|
+
}
|
|
242
|
+
|
|
266
243
|
function getColumnEditorOptions(
|
|
267
244
|
column: Column<TData>,
|
|
268
245
|
row?: Row<TData> | null,
|
|
@@ -272,8 +249,36 @@ export function createCellRender<
|
|
|
272
249
|
// Dynamic per-row: must be re-evaluated because the row's other
|
|
273
250
|
// cells may have just changed (cascade).
|
|
274
251
|
if (!row?.original) return [];
|
|
275
|
-
|
|
252
|
+
// Once a column is known to hand back promises, answer from the cache
|
|
253
|
+
// WITHOUT calling the source again. Render reads options constantly, so
|
|
254
|
+
// calling an async source every time fires a real request per render -
|
|
255
|
+
// and any state the source touches is being mutated mid-render, which
|
|
256
|
+
// Svelte rejects outright. The key folds in the row's values, so editing
|
|
257
|
+
// the row supersedes the entry and the cascade refetches.
|
|
258
|
+
const known = (ctx.asyncEditorColumns as Set<string>).has(column.id);
|
|
259
|
+
if (known) {
|
|
260
|
+
const key = asyncRowKey(column, row);
|
|
261
|
+
const done = ctx.asyncEditorOptions[key];
|
|
262
|
+
if (done) return done;
|
|
263
|
+
if ((ctx.asyncEditorOptionsPending as Set<string>).has(key)) return [];
|
|
264
|
+
const produced = def(row.original as TData);
|
|
265
|
+
if (isThenable(produced)) {
|
|
266
|
+
return resolveAsyncOptions(key, produced, `${column.id}::${row.id}::`);
|
|
267
|
+
}
|
|
268
|
+
return normalizeEditorOptions(produced);
|
|
269
|
+
}
|
|
270
|
+
const produced = def(row.original as TData);
|
|
271
|
+
if (isThenable(produced)) {
|
|
272
|
+
(ctx.asyncEditorColumns as Set<string>).add(column.id);
|
|
273
|
+
return resolveAsyncOptions(
|
|
274
|
+
asyncRowKey(column, row),
|
|
275
|
+
produced,
|
|
276
|
+
`${column.id}::${row.id}::`,
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
return normalizeEditorOptions(produced);
|
|
276
280
|
}
|
|
281
|
+
if (isThenable(def)) return resolveAsyncOptions(column.id, def);
|
|
277
282
|
const id = column.id;
|
|
278
283
|
if (
|
|
279
284
|
!ctx.editorOptionsCache[id] ||
|
|
@@ -285,6 +290,27 @@ export function createCellRender<
|
|
|
285
290
|
return ctx.editorOptionsCache[id];
|
|
286
291
|
}
|
|
287
292
|
|
|
293
|
+
/**
|
|
294
|
+
* True while a column's (or cell's) async option list is still loading.
|
|
295
|
+
* Derived from "the source is async and no result has landed" rather than
|
|
296
|
+
* from the pending Set, so it stays reactive - `asyncEditorOptions` is the
|
|
297
|
+
* reactive half, the Set is only a dedupe guard.
|
|
298
|
+
*/
|
|
299
|
+
function areEditorOptionsLoading(
|
|
300
|
+
column: Column<TData>,
|
|
301
|
+
row?: Row<TData> | null,
|
|
302
|
+
): boolean {
|
|
303
|
+
const def = column.columnDef.editorOptions;
|
|
304
|
+
if (typeof def === "function") {
|
|
305
|
+
if (!row?.original) return false;
|
|
306
|
+
// Never call the source here - that would fire another request just to
|
|
307
|
+
// answer "are we loading?". The resolver records which columns are async.
|
|
308
|
+
if (!(ctx.asyncEditorColumns as Set<string>).has(column.id)) return false;
|
|
309
|
+
return !ctx.asyncEditorOptions[asyncRowKey(column, row)];
|
|
310
|
+
}
|
|
311
|
+
return isThenable(def) && !ctx.asyncEditorOptions[column.id];
|
|
312
|
+
}
|
|
313
|
+
|
|
288
314
|
/** Joined display string for list/chips cells. */
|
|
289
315
|
function formatListCellValue(
|
|
290
316
|
column: Column<TData>,
|
|
@@ -461,6 +487,7 @@ export function createCellRender<
|
|
|
461
487
|
computeCellValidity,
|
|
462
488
|
computeCellNote,
|
|
463
489
|
getColumnEditorOptions,
|
|
490
|
+
areEditorOptionsLoading,
|
|
464
491
|
formatListCellValue,
|
|
465
492
|
formatCellValue,
|
|
466
493
|
formatPinnedValue,
|
package/src/cell-values.ts
CHANGED
|
@@ -19,6 +19,10 @@ export function getColumnBaseValue<TData extends RowData>(row: Row<TData>, colum
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function isGroupRow<TData extends RowData>(row: Row<TData>) {
|
|
22
|
+
// Tree rows are expandable too, but they are real data rows - rendering one as
|
|
23
|
+
// a full-width banner would swallow its cells. Only grouping's synthetic rows
|
|
24
|
+
// are banners.
|
|
25
|
+
if ((row as { __treeRow?: boolean }).__treeRow) return false;
|
|
22
26
|
return typeof row.getCanExpand === "function" && row.getCanExpand();
|
|
23
27
|
}
|
|
24
28
|
|
package/src/chart-export.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi, type Mock } from 'vitest'
|
|
2
2
|
import {
|
|
3
3
|
chartToSvgString,
|
|
4
4
|
chartToPngBlob,
|
|
@@ -125,7 +125,7 @@ describe('chartToSvgString', () => {
|
|
|
125
125
|
describe('downloadChartSvg', () => {
|
|
126
126
|
let createSpy: ReturnType<typeof vi.spyOn>
|
|
127
127
|
let revokeSpy: ReturnType<typeof vi.spyOn>
|
|
128
|
-
let clickSpy:
|
|
128
|
+
let clickSpy: Mock<() => void>
|
|
129
129
|
|
|
130
130
|
beforeEach(() => {
|
|
131
131
|
vi.useFakeTimers()
|
package/src/chart.ts
CHANGED
|
@@ -38,7 +38,6 @@ export type SeriesOverlay = 'linear' | `sma:${number}` | `ema:${number}`
|
|
|
38
38
|
export type SeriesPattern = 'solid' | 'stripe' | 'crosshatch' | 'dots' | 'diagonal'
|
|
39
39
|
/** Cycle used when `ChartSpec.patternFallback` is true and a series has no
|
|
40
40
|
* explicit `pattern` set. Skips `'solid'` so every series gets a texture. */
|
|
41
|
-
const PATTERN_CYCLE: SeriesPattern[] = ['stripe', 'crosshatch', 'dots', 'diagonal']
|
|
42
41
|
|
|
43
42
|
export type ChartSeries = {
|
|
44
43
|
label: string
|
|
@@ -2254,7 +2253,7 @@ export function rowsToChartSpec<T extends Record<string, unknown>>(
|
|
|
2254
2253
|
entries.reduce((sum, e) => sum + (Number.isFinite(e.values[i]!) ? e.values[i]! : 0), 0),
|
|
2255
2254
|
)
|
|
2256
2255
|
const sort = opts.sort ?? (opts.topN ? 'value-desc' : 'none')
|
|
2257
|
-
|
|
2256
|
+
const order = categories.map((_, i) => i)
|
|
2258
2257
|
if (sort === 'value-desc') order.sort((a, b) => totals[b]! - totals[a]!)
|
|
2259
2258
|
else if (sort === 'value-asc') order.sort((a, b) => totals[a]! - totals[b]!)
|
|
2260
2259
|
else if (sort === 'category') order.sort((a, b) => categories[a]!.localeCompare(categories[b]!))
|
package/src/clipboard.test.ts
CHANGED
|
@@ -614,7 +614,6 @@ describe('copySelectionToClipboard', () => {
|
|
|
614
614
|
configurable: true,
|
|
615
615
|
})
|
|
616
616
|
const execCommand = vi.fn().mockReturnValue(true)
|
|
617
|
-
// @ts-expect-error - jsdom lacks execCommand by default
|
|
618
617
|
document.execCommand = execCommand
|
|
619
618
|
const ctx = makeCtx()
|
|
620
619
|
ctx.selectionRange = {
|
|
@@ -631,7 +630,6 @@ describe('copySelectionToClipboard', () => {
|
|
|
631
630
|
value: undefined,
|
|
632
631
|
configurable: true,
|
|
633
632
|
})
|
|
634
|
-
// @ts-expect-error - override execCommand to throw
|
|
635
633
|
document.execCommand = () => {
|
|
636
634
|
throw new Error('blocked')
|
|
637
635
|
}
|
package/src/clipboard.ts
CHANGED
|
@@ -2,122 +2,20 @@
|
|
|
2
2
|
// that read/write controller state via the `ctx` handle; the reactive core
|
|
3
3
|
// ($state/$derived/$effect) stays in the controller.
|
|
4
4
|
import {
|
|
5
|
-
applyExcelFilter,
|
|
6
|
-
normalizeForFilter,
|
|
7
|
-
createColumnVirtualizer,
|
|
8
|
-
createCoreRowModel,
|
|
9
|
-
createExpandedRowModel,
|
|
10
|
-
createFilteredRowModel,
|
|
11
|
-
createGroupedRowModel,
|
|
12
|
-
createPaginatedRowModel,
|
|
13
|
-
createSvelteVirtualizer,
|
|
14
|
-
createSortedRowModel,
|
|
15
|
-
createSvGrid,
|
|
16
|
-
filterFns,
|
|
17
|
-
getGridCellA11yProps,
|
|
18
|
-
getGridCellDomId,
|
|
19
|
-
getGridHeaderA11yProps,
|
|
20
|
-
getGridRootA11yProps,
|
|
21
|
-
getGridRowA11yProps,
|
|
22
5
|
parseEditorValue,
|
|
23
|
-
normalizeEditorOptions,
|
|
24
|
-
sortFns,
|
|
25
|
-
tableFeatures,
|
|
26
|
-
rowSortingFeature,
|
|
27
|
-
columnFilteringFeature,
|
|
28
|
-
columnGroupingFeature,
|
|
29
|
-
type CellContext,
|
|
30
|
-
type EditorContext,
|
|
31
|
-
type CellEditorOption,
|
|
32
6
|
type CellEditorType,
|
|
33
|
-
type CellFormatter,
|
|
34
|
-
type CellFormatConfig,
|
|
35
|
-
type Column,
|
|
36
|
-
type ColumnDef,
|
|
37
|
-
type Row,
|
|
38
7
|
type RowData,
|
|
39
|
-
type SvGridApi,
|
|
40
8
|
type TableFeatures,
|
|
41
9
|
} from "./index";
|
|
42
10
|
import "./sv-grid-scrollbar";
|
|
43
|
-
import type { Snippet } from "svelte";
|
|
44
|
-
import { getKeyboardIntent, getNextActiveCell } from "./keyboard";
|
|
45
|
-
import {
|
|
46
|
-
formatNumericWithConfig,
|
|
47
|
-
getDateFormatter,
|
|
48
|
-
resolveDatePattern,
|
|
49
|
-
} from "./cell-formatting";
|
|
50
|
-
import {
|
|
51
|
-
RenderSnippetConfig,
|
|
52
|
-
RenderComponentConfig,
|
|
53
|
-
} from "./render-component";
|
|
54
11
|
import { buildFillPattern } from "./fill-patterns";
|
|
55
|
-
import { buildSparkline, toSparklineValues } from "./sparkline";
|
|
56
|
-
import {
|
|
57
|
-
resolveCellFormat,
|
|
58
|
-
computeColumnStat,
|
|
59
|
-
formatsNeedingStats,
|
|
60
|
-
type ColumnStat,
|
|
61
|
-
type ResolvedCellFormat,
|
|
62
|
-
} from "./conditional-formatting";
|
|
63
|
-
import SvGridDropdown from "./SvGridDropdown.svelte";
|
|
64
|
-
import type {
|
|
65
|
-
Props,
|
|
66
|
-
SelectionPoint,
|
|
67
|
-
SelectionRange,
|
|
68
|
-
CellEditState,
|
|
69
|
-
FilterOperator,
|
|
70
|
-
FilterOption,
|
|
71
|
-
MenuPosition,
|
|
72
|
-
} from "./SvGrid.types";
|
|
73
12
|
import {
|
|
74
|
-
cfTextStyle,
|
|
75
|
-
fmtStat,
|
|
76
13
|
getCellKey,
|
|
77
|
-
resolveClassList,
|
|
78
|
-
toDateInputValue,
|
|
79
|
-
toDateTimeLocalInputValue,
|
|
80
|
-
getEditableInputValue,
|
|
81
|
-
getEditorInputType,
|
|
82
|
-
toValueArray,
|
|
83
|
-
getOptionLabel,
|
|
84
|
-
getOptionColor,
|
|
85
|
-
colorfulChipStyle,
|
|
86
|
-
getEditorClass,
|
|
87
|
-
asDate,
|
|
88
|
-
clampMenuX,
|
|
89
|
-
cssEscape,
|
|
90
|
-
rawToNumber,
|
|
91
|
-
formatFacetNumber,
|
|
92
|
-
formatFacetDate,
|
|
93
14
|
} from "./SvGrid.helpers";
|
|
94
|
-
import {
|
|
95
|
-
filterOperatorOptions,
|
|
96
|
-
fallbackOperatorOption,
|
|
97
|
-
TEXT_OPERATORS,
|
|
98
|
-
NUMBER_OPERATORS,
|
|
99
|
-
DATE_OPERATORS,
|
|
100
|
-
CHECKBOX_OPERATORS,
|
|
101
|
-
operatorOption,
|
|
102
|
-
operatorsForColumn,
|
|
103
|
-
defaultOperatorFor,
|
|
104
|
-
operatorLabelFor,
|
|
105
|
-
} from "./filter-operators";
|
|
106
|
-
import {
|
|
107
|
-
type FacetBucket,
|
|
108
|
-
isBucketableColumn,
|
|
109
|
-
buildBuckets,
|
|
110
|
-
isInBucket,
|
|
111
|
-
} from "./facet-buckets";
|
|
112
15
|
import {
|
|
113
16
|
getColumnBaseValue,
|
|
114
17
|
isGroupRow,
|
|
115
18
|
toolPanelHeaderLabel,
|
|
116
|
-
formatSummaryNumeric,
|
|
117
|
-
getColumnAlign,
|
|
118
|
-
getPinnedCellValue,
|
|
119
|
-
getColumnAccessorValue,
|
|
120
|
-
columnDefMatchesId,
|
|
121
19
|
} from "./cell-values";
|
|
122
20
|
|
|
123
21
|
export function createClipboard<
|
package/src/columns.ts
CHANGED
|
@@ -2,125 +2,10 @@
|
|
|
2
2
|
// reading/writing controller state via the `ctx` handle; the reactive core
|
|
3
3
|
// ($state/$derived/$effect) stays in the controller.
|
|
4
4
|
import {
|
|
5
|
-
applyExcelFilter,
|
|
6
|
-
normalizeForFilter,
|
|
7
|
-
createColumnVirtualizer,
|
|
8
|
-
createCoreRowModel,
|
|
9
|
-
createExpandedRowModel,
|
|
10
|
-
createFilteredRowModel,
|
|
11
|
-
createGroupedRowModel,
|
|
12
|
-
createPaginatedRowModel,
|
|
13
|
-
createSvelteVirtualizer,
|
|
14
|
-
createSortedRowModel,
|
|
15
|
-
createSvGrid,
|
|
16
|
-
filterFns,
|
|
17
|
-
getGridCellA11yProps,
|
|
18
|
-
getGridCellDomId,
|
|
19
|
-
getGridHeaderA11yProps,
|
|
20
|
-
getGridRootA11yProps,
|
|
21
|
-
getGridRowA11yProps,
|
|
22
|
-
parseEditorValue,
|
|
23
|
-
normalizeEditorOptions,
|
|
24
|
-
sortFns,
|
|
25
|
-
tableFeatures,
|
|
26
|
-
rowSortingFeature,
|
|
27
|
-
columnFilteringFeature,
|
|
28
|
-
columnGroupingFeature,
|
|
29
|
-
type CellContext,
|
|
30
|
-
type EditorContext,
|
|
31
|
-
type CellEditorOption,
|
|
32
|
-
type CellEditorType,
|
|
33
|
-
type CellFormatter,
|
|
34
|
-
type CellFormatConfig,
|
|
35
|
-
type Column,
|
|
36
|
-
type ColumnDef,
|
|
37
|
-
type Row,
|
|
38
5
|
type RowData,
|
|
39
|
-
type SvGridApi,
|
|
40
6
|
type TableFeatures,
|
|
41
7
|
} from "./index";
|
|
42
8
|
import "./sv-grid-scrollbar";
|
|
43
|
-
import type { Snippet } from "svelte";
|
|
44
|
-
import { getKeyboardIntent, getNextActiveCell } from "./keyboard";
|
|
45
|
-
import {
|
|
46
|
-
formatNumericWithConfig,
|
|
47
|
-
getDateFormatter,
|
|
48
|
-
resolveDatePattern,
|
|
49
|
-
} from "./cell-formatting";
|
|
50
|
-
import {
|
|
51
|
-
RenderSnippetConfig,
|
|
52
|
-
RenderComponentConfig,
|
|
53
|
-
} from "./render-component";
|
|
54
|
-
import { buildFillPattern } from "./fill-patterns";
|
|
55
|
-
import { buildSparkline, toSparklineValues } from "./sparkline";
|
|
56
|
-
import {
|
|
57
|
-
resolveCellFormat,
|
|
58
|
-
computeColumnStat,
|
|
59
|
-
formatsNeedingStats,
|
|
60
|
-
type ColumnStat,
|
|
61
|
-
type ResolvedCellFormat,
|
|
62
|
-
} from "./conditional-formatting";
|
|
63
|
-
import SvGridDropdown from "./SvGridDropdown.svelte";
|
|
64
|
-
import type {
|
|
65
|
-
Props,
|
|
66
|
-
SelectionPoint,
|
|
67
|
-
SelectionRange,
|
|
68
|
-
CellEditState,
|
|
69
|
-
FilterOperator,
|
|
70
|
-
FilterOption,
|
|
71
|
-
MenuPosition,
|
|
72
|
-
} from "./SvGrid.types";
|
|
73
|
-
import {
|
|
74
|
-
cfTextStyle,
|
|
75
|
-
fmtStat,
|
|
76
|
-
getCellKey,
|
|
77
|
-
resolveClassList,
|
|
78
|
-
toDateInputValue,
|
|
79
|
-
toDateTimeLocalInputValue,
|
|
80
|
-
getEditableInputValue,
|
|
81
|
-
getEditorInputType,
|
|
82
|
-
toValueArray,
|
|
83
|
-
getOptionLabel,
|
|
84
|
-
getOptionColor,
|
|
85
|
-
colorfulChipStyle,
|
|
86
|
-
getEditorClass,
|
|
87
|
-
asDate,
|
|
88
|
-
clampMenuX,
|
|
89
|
-
cssEscape,
|
|
90
|
-
rawToNumber,
|
|
91
|
-
formatFacetNumber,
|
|
92
|
-
formatFacetDate,
|
|
93
|
-
} from "./SvGrid.helpers";
|
|
94
|
-
import { createGridApi } from "./build-api";
|
|
95
|
-
import { createClipboard } from "./clipboard";
|
|
96
|
-
import {
|
|
97
|
-
filterOperatorOptions,
|
|
98
|
-
fallbackOperatorOption,
|
|
99
|
-
TEXT_OPERATORS,
|
|
100
|
-
NUMBER_OPERATORS,
|
|
101
|
-
DATE_OPERATORS,
|
|
102
|
-
CHECKBOX_OPERATORS,
|
|
103
|
-
operatorOption,
|
|
104
|
-
operatorsForColumn,
|
|
105
|
-
defaultOperatorFor,
|
|
106
|
-
operatorLabelFor,
|
|
107
|
-
} from "./filter-operators";
|
|
108
|
-
import {
|
|
109
|
-
type FacetBucket,
|
|
110
|
-
isBucketableColumn,
|
|
111
|
-
buildBuckets,
|
|
112
|
-
isInBucket,
|
|
113
|
-
} from "./facet-buckets";
|
|
114
|
-
import {
|
|
115
|
-
getColumnBaseValue,
|
|
116
|
-
isGroupRow,
|
|
117
|
-
toolPanelHeaderLabel,
|
|
118
|
-
formatSummaryNumeric,
|
|
119
|
-
getColumnAlign,
|
|
120
|
-
getPinnedCellValue,
|
|
121
|
-
getColumnAccessorValue,
|
|
122
|
-
columnDefMatchesId,
|
|
123
|
-
} from "./cell-values";
|
|
124
9
|
|
|
125
10
|
export function createColumns<
|
|
126
11
|
TFeatures extends TableFeatures = TableFeatures,
|