@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
|
@@ -62,11 +62,11 @@ describe('isBucketableColumn', () => {
|
|
|
62
62
|
const cols = makeColumns()
|
|
63
63
|
|
|
64
64
|
it('returns { isDate: false } for a number column', () => {
|
|
65
|
-
expect(isBucketableColumn(cols.num)).toEqual({ isDate: false })
|
|
65
|
+
expect(isBucketableColumn(cols.num!)).toEqual({ isDate: false })
|
|
66
66
|
})
|
|
67
67
|
|
|
68
68
|
it('returns { isDate: true } for a date column', () => {
|
|
69
|
-
expect(isBucketableColumn(cols.when)).toEqual({ isDate: true })
|
|
69
|
+
expect(isBucketableColumn(cols.when!)).toEqual({ isDate: true })
|
|
70
70
|
})
|
|
71
71
|
|
|
72
72
|
it('returns { isDate: true } for a datetime column', () => {
|
|
@@ -81,15 +81,15 @@ describe('isBucketableColumn', () => {
|
|
|
81
81
|
})
|
|
82
82
|
|
|
83
83
|
it('returns null for a text column', () => {
|
|
84
|
-
expect(isBucketableColumn(cols.id)).toBeNull()
|
|
84
|
+
expect(isBucketableColumn(cols.id!)).toBeNull()
|
|
85
85
|
})
|
|
86
86
|
|
|
87
87
|
it('returns null for a checkbox column', () => {
|
|
88
|
-
expect(isBucketableColumn(cols.flag)).toBeNull()
|
|
88
|
+
expect(isBucketableColumn(cols.flag!)).toBeNull()
|
|
89
89
|
})
|
|
90
90
|
|
|
91
91
|
it('returns null for a column with no editorType', () => {
|
|
92
|
-
expect(isBucketableColumn(cols.name)).toBeNull()
|
|
92
|
+
expect(isBucketableColumn(cols.name!)).toBeNull()
|
|
93
93
|
})
|
|
94
94
|
})
|
|
95
95
|
|
|
@@ -105,7 +105,7 @@ describe('buildBuckets - numeric', () => {
|
|
|
105
105
|
name: '',
|
|
106
106
|
flag: false,
|
|
107
107
|
}))
|
|
108
|
-
expect(buildBuckets(cols.num
|
|
108
|
+
expect(buildBuckets(cols.num!, false, data, accessor)).toBeNull()
|
|
109
109
|
})
|
|
110
110
|
|
|
111
111
|
it('builds 10 buckets when distinct values exceed the threshold', () => {
|
|
@@ -117,7 +117,7 @@ describe('buildBuckets - numeric', () => {
|
|
|
117
117
|
name: '',
|
|
118
118
|
flag: false,
|
|
119
119
|
}))
|
|
120
|
-
const buckets = buildBuckets(cols.num
|
|
120
|
+
const buckets = buildBuckets(cols.num!, false, data, accessor)
|
|
121
121
|
expect(buckets).not.toBeNull()
|
|
122
122
|
expect(buckets!).toHaveLength(10)
|
|
123
123
|
})
|
|
@@ -130,12 +130,12 @@ describe('buildBuckets - numeric', () => {
|
|
|
130
130
|
name: '',
|
|
131
131
|
flag: false,
|
|
132
132
|
}))
|
|
133
|
-
const buckets = buildBuckets(cols.num
|
|
134
|
-
expect(buckets[0]
|
|
135
|
-
expect(buckets[9]
|
|
133
|
+
const buckets = buildBuckets(cols.num!, false, data, accessor)!
|
|
134
|
+
expect(buckets[0]!.numericMin).toBe(0)
|
|
135
|
+
expect(buckets[9]!.numericMax).toBe(30)
|
|
136
136
|
// bucket i's max equals bucket i+1's min (contiguous, no gaps)
|
|
137
137
|
for (let i = 0; i < buckets.length - 1; i += 1) {
|
|
138
|
-
expect(buckets[i]
|
|
138
|
+
expect(buckets[i]!.numericMax).toBeCloseTo(buckets[i + 1]!.numericMin, 10)
|
|
139
139
|
}
|
|
140
140
|
})
|
|
141
141
|
|
|
@@ -147,10 +147,10 @@ describe('buildBuckets - numeric', () => {
|
|
|
147
147
|
name: '',
|
|
148
148
|
flag: false,
|
|
149
149
|
}))
|
|
150
|
-
const buckets = buildBuckets(cols.num
|
|
150
|
+
const buckets = buildBuckets(cols.num!, false, data, accessor)!
|
|
151
151
|
expect(buckets.filter((b) => b.isLast)).toHaveLength(1)
|
|
152
|
-
expect(buckets[9]
|
|
153
|
-
expect(buckets[0]
|
|
152
|
+
expect(buckets[9]!.isLast).toBe(true)
|
|
153
|
+
expect(buckets[0]!.isLast).toBe(false)
|
|
154
154
|
expect(buckets.every((b) => b.isDate === false)).toBe(true)
|
|
155
155
|
})
|
|
156
156
|
|
|
@@ -162,8 +162,8 @@ describe('buildBuckets - numeric', () => {
|
|
|
162
162
|
name: '',
|
|
163
163
|
flag: false,
|
|
164
164
|
}))
|
|
165
|
-
const buckets = buildBuckets(cols.num
|
|
166
|
-
expect(buckets[0]
|
|
165
|
+
const buckets = buildBuckets(cols.num!, false, data, accessor)!
|
|
166
|
+
expect(buckets[0]!.label).toContain('–')
|
|
167
167
|
})
|
|
168
168
|
|
|
169
169
|
it('ignores non-finite / blank values when computing min/max and distinct', () => {
|
|
@@ -180,10 +180,10 @@ describe('buildBuckets - numeric', () => {
|
|
|
180
180
|
{ id: 'b', num: undefined as unknown as number, when: '', name: '', flag: false },
|
|
181
181
|
{ id: 'c', num: 'not-a-number' as unknown as number, when: '', name: '', flag: false },
|
|
182
182
|
]
|
|
183
|
-
const buckets = buildBuckets(cols.num
|
|
183
|
+
const buckets = buildBuckets(cols.num!, false, data, accessor)!
|
|
184
184
|
// range is still 0..30 - the junk rows were skipped
|
|
185
|
-
expect(buckets[0]
|
|
186
|
-
expect(buckets[9]
|
|
185
|
+
expect(buckets[0]!.numericMin).toBe(0)
|
|
186
|
+
expect(buckets[9]!.numericMax).toBe(30)
|
|
187
187
|
})
|
|
188
188
|
|
|
189
189
|
it('returns null when all finite values are identical (min === max)', () => {
|
|
@@ -196,7 +196,7 @@ describe('buildBuckets - numeric', () => {
|
|
|
196
196
|
name: '',
|
|
197
197
|
flag: false,
|
|
198
198
|
}))
|
|
199
|
-
expect(buildBuckets(cols.num
|
|
199
|
+
expect(buildBuckets(cols.num!, false, data, accessor)).toBeNull()
|
|
200
200
|
})
|
|
201
201
|
|
|
202
202
|
it('returns null when there is no finite data at all', () => {
|
|
@@ -207,11 +207,11 @@ describe('buildBuckets - numeric', () => {
|
|
|
207
207
|
name: '',
|
|
208
208
|
flag: false,
|
|
209
209
|
}))
|
|
210
|
-
expect(buildBuckets(cols.num
|
|
210
|
+
expect(buildBuckets(cols.num!, false, data, accessor)).toBeNull()
|
|
211
211
|
})
|
|
212
212
|
|
|
213
213
|
it('returns null for an empty dataset', () => {
|
|
214
|
-
expect(buildBuckets(cols.num
|
|
214
|
+
expect(buildBuckets(cols.num!, false, [], accessor)).toBeNull()
|
|
215
215
|
})
|
|
216
216
|
|
|
217
217
|
it('handles negative ranges', () => {
|
|
@@ -222,9 +222,9 @@ describe('buildBuckets - numeric', () => {
|
|
|
222
222
|
name: '',
|
|
223
223
|
flag: false,
|
|
224
224
|
}))
|
|
225
|
-
const buckets = buildBuckets(cols.num
|
|
226
|
-
expect(buckets[0]
|
|
227
|
-
expect(buckets[9]
|
|
225
|
+
const buckets = buildBuckets(cols.num!, false, data, accessor)!
|
|
226
|
+
expect(buckets[0]!.numericMin).toBe(-20)
|
|
227
|
+
expect(buckets[9]!.numericMax).toBe(20)
|
|
228
228
|
})
|
|
229
229
|
})
|
|
230
230
|
|
|
@@ -241,11 +241,11 @@ describe('buildBuckets - date', () => {
|
|
|
241
241
|
name: '',
|
|
242
242
|
flag: false,
|
|
243
243
|
}))
|
|
244
|
-
const buckets = buildBuckets(cols.when
|
|
244
|
+
const buckets = buildBuckets(cols.when!, true, data, accessor)!
|
|
245
245
|
expect(buckets).toHaveLength(10)
|
|
246
246
|
expect(buckets.every((b) => b.isDate === true)).toBe(true)
|
|
247
247
|
// labels should look like dates (contain a 4-digit year)
|
|
248
|
-
expect(buckets[0]
|
|
248
|
+
expect(buckets[0]!.label).toMatch(/\d{4}/)
|
|
249
249
|
})
|
|
250
250
|
|
|
251
251
|
it('accepts Date instances directly', () => {
|
|
@@ -257,9 +257,9 @@ describe('buildBuckets - date', () => {
|
|
|
257
257
|
)
|
|
258
258
|
// accessor reads row[column.id]; column id is 'when'
|
|
259
259
|
const rows = data.map((d) => ({ when: d.when })) as unknown as Row[]
|
|
260
|
-
const buckets = buildBuckets(cols.when
|
|
260
|
+
const buckets = buildBuckets(cols.when!, true, rows, accessor)!
|
|
261
261
|
expect(buckets).toHaveLength(10)
|
|
262
|
-
expect(buckets[0]
|
|
262
|
+
expect(buckets[0]!.numericMin).toBe(start)
|
|
263
263
|
})
|
|
264
264
|
|
|
265
265
|
it('returns null when date values are too few', () => {
|
|
@@ -270,7 +270,7 @@ describe('buildBuckets - date', () => {
|
|
|
270
270
|
name: '',
|
|
271
271
|
flag: false,
|
|
272
272
|
}))
|
|
273
|
-
expect(buildBuckets(cols.when
|
|
273
|
+
expect(buildBuckets(cols.when!, true, data, accessor)).toBeNull()
|
|
274
274
|
})
|
|
275
275
|
|
|
276
276
|
it('skips invalid date strings', () => {
|
|
@@ -286,9 +286,9 @@ describe('buildBuckets - date', () => {
|
|
|
286
286
|
})),
|
|
287
287
|
{ id: 'bad', num: 0, when: 'not-a-date', name: '', flag: false },
|
|
288
288
|
]
|
|
289
|
-
const buckets = buildBuckets(cols.when
|
|
289
|
+
const buckets = buildBuckets(cols.when!, true, data, accessor)!
|
|
290
290
|
expect(buckets).toHaveLength(10)
|
|
291
|
-
expect(buckets[0]
|
|
291
|
+
expect(buckets[0]!.numericMin).toBe(start)
|
|
292
292
|
})
|
|
293
293
|
})
|
|
294
294
|
|
|
@@ -344,7 +344,7 @@ describe('isInBucket', () => {
|
|
|
344
344
|
name: '',
|
|
345
345
|
flag: false,
|
|
346
346
|
}))
|
|
347
|
-
const buckets = buildBuckets(cols.num
|
|
347
|
+
const buckets = buildBuckets(cols.num!, false, data, accessor)!
|
|
348
348
|
for (const v of [0, 1, 15, 50, 99, 100]) {
|
|
349
349
|
const hits = buckets.filter((b) => isInBucket(v, b))
|
|
350
350
|
expect(hits).toHaveLength(1)
|
package/src/features.ts
CHANGED
|
@@ -2,104 +2,14 @@
|
|
|
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
|
-
createSvelteVirtualizer,
|
|
13
|
-
createSortedRowModel,
|
|
14
|
-
createSvGrid,
|
|
15
|
-
getGridCellDomId,
|
|
16
|
-
sortFns,
|
|
17
5
|
tableFeatures,
|
|
18
6
|
rowSortingFeature,
|
|
19
7
|
columnFilteringFeature,
|
|
20
8
|
columnGroupingFeature,
|
|
21
|
-
type CellEditorOption,
|
|
22
|
-
type Column,
|
|
23
|
-
type ColumnDef,
|
|
24
|
-
type Row,
|
|
25
9
|
type RowData,
|
|
26
10
|
type TableFeatures,
|
|
27
11
|
} from "./index";
|
|
28
12
|
import "./sv-grid-scrollbar";
|
|
29
|
-
import {
|
|
30
|
-
computeColumnStat,
|
|
31
|
-
formatsNeedingStats,
|
|
32
|
-
type ColumnStat,
|
|
33
|
-
} from "./conditional-formatting";
|
|
34
|
-
import SvGridDropdown from "./SvGridDropdown.svelte";
|
|
35
|
-
import type {
|
|
36
|
-
Props,
|
|
37
|
-
SelectionRange,
|
|
38
|
-
CellEditState,
|
|
39
|
-
FilterOperator,
|
|
40
|
-
MenuPosition,
|
|
41
|
-
} from "./SvGrid.types";
|
|
42
|
-
import {
|
|
43
|
-
rawToNumber,
|
|
44
|
-
} from "./SvGrid.helpers";
|
|
45
|
-
import {
|
|
46
|
-
createScrollSync,
|
|
47
|
-
} from "./scroll-sync";
|
|
48
|
-
import {
|
|
49
|
-
createKeyboard,
|
|
50
|
-
} from "./keyboard-handlers";
|
|
51
|
-
import {
|
|
52
|
-
createSummaries,
|
|
53
|
-
} from "./summaries";
|
|
54
|
-
import {
|
|
55
|
-
createMenus,
|
|
56
|
-
} from "./menus";
|
|
57
|
-
import {
|
|
58
|
-
createCellRender,
|
|
59
|
-
} from "./cell-render";
|
|
60
|
-
import {
|
|
61
|
-
createEditing,
|
|
62
|
-
} from "./editing";
|
|
63
|
-
import {
|
|
64
|
-
createSelection,
|
|
65
|
-
} from "./selection";
|
|
66
|
-
import {
|
|
67
|
-
createColumns,
|
|
68
|
-
} from "./columns";
|
|
69
|
-
import {
|
|
70
|
-
createGridApi,
|
|
71
|
-
} from "./build-api";
|
|
72
|
-
import {
|
|
73
|
-
createClipboard,
|
|
74
|
-
} from "./clipboard";
|
|
75
|
-
import {
|
|
76
|
-
filterOperatorOptions,
|
|
77
|
-
fallbackOperatorOption,
|
|
78
|
-
TEXT_OPERATORS,
|
|
79
|
-
NUMBER_OPERATORS,
|
|
80
|
-
DATE_OPERATORS,
|
|
81
|
-
CHECKBOX_OPERATORS,
|
|
82
|
-
operatorOption,
|
|
83
|
-
operatorsForColumn,
|
|
84
|
-
defaultOperatorFor,
|
|
85
|
-
operatorLabelFor,
|
|
86
|
-
} from "./filter-operators";
|
|
87
|
-
import {
|
|
88
|
-
type FacetBucket,
|
|
89
|
-
isBucketableColumn,
|
|
90
|
-
buildBuckets,
|
|
91
|
-
isInBucket,
|
|
92
|
-
} from "./facet-buckets";
|
|
93
|
-
import {
|
|
94
|
-
getColumnBaseValue,
|
|
95
|
-
isGroupRow,
|
|
96
|
-
toolPanelHeaderLabel,
|
|
97
|
-
formatSummaryNumeric,
|
|
98
|
-
getColumnAlign,
|
|
99
|
-
getPinnedCellValue,
|
|
100
|
-
getColumnAccessorValue,
|
|
101
|
-
columnDefMatchesId,
|
|
102
|
-
} from "./cell-values";
|
|
103
13
|
|
|
104
14
|
export function createFeatures<
|
|
105
15
|
TFeatures extends TableFeatures = TableFeatures,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { buildAutoGroupColumns, insertGroupFooters } from './group-display'
|
|
2
|
+
import { buildAutoGroupColumns, insertGroupFooters, paginateGroupedRows } from './group-display'
|
|
3
3
|
|
|
4
4
|
type R = { id: string; depth: number; group?: boolean; total?: number; footer?: boolean }
|
|
5
5
|
|
|
@@ -75,3 +75,93 @@ describe('buildAutoGroupColumns', () => {
|
|
|
75
75
|
expect(r.autoColumns.map((c) => c.field)).toEqual(['region', 'category'])
|
|
76
76
|
})
|
|
77
77
|
})
|
|
78
|
+
|
|
79
|
+
describe('paginateGroupedRows (#73)', () => {
|
|
80
|
+
// Ten groups of two rows each - the shape from the issue report, where
|
|
81
|
+
// slicing the flat list by pageSize leaves almost no data on a page.
|
|
82
|
+
const many: R[] = []
|
|
83
|
+
for (let g = 0; g < 10; g += 1) {
|
|
84
|
+
many.push({ id: `g${g}`, depth: 0, group: true })
|
|
85
|
+
many.push({ id: `g${g}r0`, depth: 1 })
|
|
86
|
+
many.push({ id: `g${g}r1`, depth: 1 })
|
|
87
|
+
}
|
|
88
|
+
const base = { getDepth: (r: R) => r.depth, isGroup: (r: R) => !!r.group }
|
|
89
|
+
|
|
90
|
+
it('counts only data rows against pageSize', () => {
|
|
91
|
+
const { rows, dataRowCount } = paginateGroupedRows(many, { ...base, pageIndex: 0, pageSize: 10 })
|
|
92
|
+
expect(dataRowCount).toBe(20)
|
|
93
|
+
// A naive slice would have yielded 10 rows TOTAL, only ~3 of them data.
|
|
94
|
+
expect(rows.filter((r) => !r.group)).toHaveLength(10)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('reprints the banner each page a group appears on', () => {
|
|
98
|
+
const page0 = paginateGroupedRows(many, { ...base, pageIndex: 0, pageSize: 3 }).rows
|
|
99
|
+
expect(page0.map((r) => r.id)).toEqual(['g0', 'g0r0', 'g0r1', 'g1', 'g1r0'])
|
|
100
|
+
// g1 is reprinted because its second row leads the next page.
|
|
101
|
+
const page1 = paginateGroupedRows(many, { ...base, pageIndex: 1, pageSize: 3 }).rows
|
|
102
|
+
expect(page1.map((r) => r.id)).toEqual(['g1', 'g1r1', 'g2', 'g2r0', 'g2r1'])
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('emits a banner once for a group spanning many rows on one page', () => {
|
|
106
|
+
const wide: R[] = [{ id: 'g', depth: 0, group: true }]
|
|
107
|
+
for (let i = 0; i < 5; i += 1) wide.push({ id: `r${i}`, depth: 1 })
|
|
108
|
+
const { rows } = paginateGroupedRows(wide, { ...base, pageIndex: 0, pageSize: 5 })
|
|
109
|
+
expect(rows.filter((r) => r.group)).toHaveLength(1)
|
|
110
|
+
expect(rows).toHaveLength(6)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('reprints the whole ancestor chain for nested groups', () => {
|
|
114
|
+
const nested: R[] = [
|
|
115
|
+
{ id: 'A', depth: 0, group: true },
|
|
116
|
+
{ id: 'A1', depth: 1, group: true },
|
|
117
|
+
{ id: 'x', depth: 2 },
|
|
118
|
+
{ id: 'y', depth: 2 },
|
|
119
|
+
]
|
|
120
|
+
const page1 = paginateGroupedRows(nested, { ...base, pageIndex: 1, pageSize: 1 }).rows
|
|
121
|
+
expect(page1.map((r) => r.id)).toEqual(['A', 'A1', 'y'])
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('gives a COLLAPSED group its own page slot', () => {
|
|
125
|
+
// All groups collapsed: the flat model is banners only. Treating those as
|
|
126
|
+
// zero-cost headers left every page empty - a real bug caught by the DOM test.
|
|
127
|
+
const collapsed: R[] = []
|
|
128
|
+
for (let g = 0; g < 10; g += 1) collapsed.push({ id: `c${g}`, depth: 0, group: true })
|
|
129
|
+
const { rows, dataRowCount } = paginateGroupedRows(collapsed, {
|
|
130
|
+
...base,
|
|
131
|
+
isExpanded: () => false,
|
|
132
|
+
pageIndex: 0,
|
|
133
|
+
pageSize: 4,
|
|
134
|
+
})
|
|
135
|
+
expect(dataRowCount).toBe(10)
|
|
136
|
+
expect(rows.map((r) => r.id)).toEqual(['c0', 'c1', 'c2', 'c3'])
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('mixes an expanded group with a collapsed sibling', () => {
|
|
140
|
+
const mixed: R[] = [
|
|
141
|
+
{ id: 'open', depth: 0, group: true },
|
|
142
|
+
{ id: 'o1', depth: 1 },
|
|
143
|
+
{ id: 'o2', depth: 1 },
|
|
144
|
+
{ id: 'shut', depth: 0, group: true },
|
|
145
|
+
]
|
|
146
|
+
const { rows, dataRowCount } = paginateGroupedRows(mixed, {
|
|
147
|
+
...base,
|
|
148
|
+
isExpanded: (r: R) => r.id === 'open',
|
|
149
|
+
pageIndex: 0,
|
|
150
|
+
pageSize: 5,
|
|
151
|
+
})
|
|
152
|
+
// 2 data rows + 1 collapsed group = 3 units; the open banner is free.
|
|
153
|
+
expect(dataRowCount).toBe(3)
|
|
154
|
+
expect(rows.map((r) => r.id)).toEqual(['open', 'o1', 'o2', 'shut'])
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('handles an out-of-range page', () => {
|
|
158
|
+
expect(paginateGroupedRows(many, { ...base, pageIndex: 99, pageSize: 10 }).rows).toEqual([])
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('leaves ungrouped data untouched', () => {
|
|
162
|
+
const flat: R[] = [{ id: 'a', depth: 0 }, { id: 'b', depth: 0 }, { id: 'c', depth: 0 }]
|
|
163
|
+
const { rows, dataRowCount } = paginateGroupedRows(flat, { ...base, pageIndex: 1, pageSize: 2 })
|
|
164
|
+
expect(dataRowCount).toBe(3)
|
|
165
|
+
expect(rows.map((r) => r.id)).toEqual(['c'])
|
|
166
|
+
})
|
|
167
|
+
})
|
package/src/group-display.ts
CHANGED
|
@@ -70,6 +70,89 @@ export function insertGroupFooters<R>(rows: ReadonlyArray<R>, opts: GroupFooterO
|
|
|
70
70
|
return out
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
export type GroupPaginationOptions<R> = {
|
|
74
|
+
/** Depth of a row (group rows and their descendants). */
|
|
75
|
+
getDepth: (row: R) => number
|
|
76
|
+
/** True when a row is a group (banner) row. */
|
|
77
|
+
isGroup: (row: R) => boolean
|
|
78
|
+
/**
|
|
79
|
+
* True when a group row is expanded. Defaults to treating every group as
|
|
80
|
+
* expanded. This matters: an EXPANDED group is a header, reprinted above its
|
|
81
|
+
* children and costing no page budget, while a COLLAPSED group is the visible
|
|
82
|
+
* unit itself and must consume a slot - otherwise a fully collapsed grid has
|
|
83
|
+
* no budget-consuming rows at all and every page comes back empty.
|
|
84
|
+
*/
|
|
85
|
+
isExpanded?: (row: R) => boolean
|
|
86
|
+
pageIndex: number
|
|
87
|
+
pageSize: number
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type GroupPaginationResult<R> = {
|
|
91
|
+
/** The rows to render for this page: the page's budget-consuming rows, each
|
|
92
|
+
* preceded by whichever ancestor banners have not been printed yet. */
|
|
93
|
+
rows: R[]
|
|
94
|
+
/** Budget-consuming rows across every page - what the pager divides by.
|
|
95
|
+
* Data rows plus collapsed group rows; expanded banners do not count. */
|
|
96
|
+
dataRowCount: number
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Paginate a grouped row list by DATA rows, repeating each page's ancestor
|
|
101
|
+
* group headers.
|
|
102
|
+
*
|
|
103
|
+
* Slicing the flat grouped list directly counts group banners against
|
|
104
|
+
* `pageSize`, so `pageSize: 10` over ten groups of two shows almost no data
|
|
105
|
+
* (#73). Here only non-group rows consume the page budget, and a page reprints
|
|
106
|
+
* the group chain its first rows sit under - the same thing a spreadsheet does
|
|
107
|
+
* when a grouped table breaks across pages.
|
|
108
|
+
*
|
|
109
|
+
* Ancestors are tracked with a depth stack rather than parent pointers, so this
|
|
110
|
+
* works on the already-flattened expanded model and needs nothing from the row
|
|
111
|
+
* objects but depth and group-ness.
|
|
112
|
+
*/
|
|
113
|
+
export function paginateGroupedRows<R>(
|
|
114
|
+
rows: ReadonlyArray<R>,
|
|
115
|
+
opts: GroupPaginationOptions<R>,
|
|
116
|
+
): GroupPaginationResult<R> {
|
|
117
|
+
const { getDepth, isGroup, isExpanded, pageIndex, pageSize } = opts
|
|
118
|
+
const expanded = isExpanded ?? (() => true)
|
|
119
|
+
const start = pageIndex * pageSize
|
|
120
|
+
const end = start + pageSize
|
|
121
|
+
|
|
122
|
+
const out: R[] = []
|
|
123
|
+
// Expanded banners currently open, outermost first. `printed` marks the ones
|
|
124
|
+
// this page has already emitted, so a group spanning rows 3-8 prints its
|
|
125
|
+
// banner once, not once per row.
|
|
126
|
+
const openGroups: Array<{ row: R; printed: boolean }> = []
|
|
127
|
+
let unitsSeen = 0
|
|
128
|
+
|
|
129
|
+
for (const row of rows) {
|
|
130
|
+
const depth = getDepth(row)
|
|
131
|
+
// A row at depth <= an open banner's depth closes it.
|
|
132
|
+
while (openGroups.length > 0 && getDepth(openGroups[openGroups.length - 1]!.row) >= depth) {
|
|
133
|
+
openGroups.pop()
|
|
134
|
+
}
|
|
135
|
+
if (isGroup(row) && expanded(row)) {
|
|
136
|
+
// A header for rows that follow: reprinted per page, costs no budget.
|
|
137
|
+
openGroups.push({ row, printed: false })
|
|
138
|
+
continue
|
|
139
|
+
}
|
|
140
|
+
// Everything else is a visible unit: a data row, or a collapsed group
|
|
141
|
+
// standing in for its whole subtree.
|
|
142
|
+
const index = unitsSeen
|
|
143
|
+
unitsSeen += 1
|
|
144
|
+
if (index < start || index >= end) continue
|
|
145
|
+
for (const g of openGroups) {
|
|
146
|
+
if (g.printed) continue
|
|
147
|
+
g.printed = true
|
|
148
|
+
out.push(g.row)
|
|
149
|
+
}
|
|
150
|
+
out.push(row)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return { rows: out, dataRowCount: unitsSeen }
|
|
154
|
+
}
|
|
155
|
+
|
|
73
156
|
export type AutoGroupColumnSpec = {
|
|
74
157
|
/** Synthetic column id (e.g. `__autoGroup` or `__group_<field>`). */
|
|
75
158
|
id: string
|
package/src/index.ts
CHANGED
|
@@ -32,6 +32,8 @@ export {
|
|
|
32
32
|
createGroupedRowModel,
|
|
33
33
|
createPaginatedRowModel,
|
|
34
34
|
createSortedRowModel,
|
|
35
|
+
createTreeRowModel,
|
|
36
|
+
flattenTreeData,
|
|
35
37
|
applyGroupAggregate,
|
|
36
38
|
filterFns,
|
|
37
39
|
rowExpandingFeature,
|
|
@@ -52,6 +54,8 @@ export {
|
|
|
52
54
|
type Column,
|
|
53
55
|
type ColumnDef,
|
|
54
56
|
type ColumnDefTemplate,
|
|
57
|
+
type GridColumnDef,
|
|
58
|
+
type GridColumns,
|
|
55
59
|
type GroupAggregator,
|
|
56
60
|
type Header,
|
|
57
61
|
type HeaderContext,
|
|
@@ -62,6 +66,8 @@ export {
|
|
|
62
66
|
type SvGrid as SvGridInstance,
|
|
63
67
|
type SvGridOptions,
|
|
64
68
|
type TableFeatures,
|
|
69
|
+
type TreeRowModelOptions,
|
|
70
|
+
type FlattenTreeOptions,
|
|
65
71
|
type Updater,
|
|
66
72
|
} from './core'
|
|
67
73
|
|
|
@@ -671,10 +677,13 @@ export {
|
|
|
671
677
|
export {
|
|
672
678
|
insertGroupFooters,
|
|
673
679
|
buildAutoGroupColumns,
|
|
680
|
+
paginateGroupedRows,
|
|
674
681
|
type GroupDisplayType,
|
|
675
682
|
type GroupFooterOptions,
|
|
676
683
|
type AutoGroupColumnSpec,
|
|
677
684
|
type AutoGroupResult,
|
|
685
|
+
type GroupPaginationOptions,
|
|
686
|
+
type GroupPaginationResult,
|
|
678
687
|
} from './group-display'
|
|
679
688
|
export { getKeyboardIntent, getNextActiveCell, type GridKeyboardIntent } from './keyboard'
|
|
680
689
|
export { createVirtualizer } from './virtualization/virtualizer'
|
package/src/keyboard-handlers.ts
CHANGED
|
@@ -2,130 +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 {
|
|
45
|
-
formatNumericWithConfig,
|
|
46
|
-
getDateFormatter,
|
|
47
|
-
resolveDatePattern,
|
|
48
|
-
} from "./cell-formatting";
|
|
49
|
-
import {
|
|
50
|
-
RenderSnippetConfig,
|
|
51
|
-
RenderComponentConfig,
|
|
52
|
-
} from "./render-component";
|
|
53
|
-
import { buildFillPattern } from "./fill-patterns";
|
|
54
|
-
import { buildSparkline, toSparklineValues } from "./sparkline";
|
|
55
|
-
import {
|
|
56
|
-
resolveCellFormat,
|
|
57
|
-
computeColumnStat,
|
|
58
|
-
formatsNeedingStats,
|
|
59
|
-
type ColumnStat,
|
|
60
|
-
type ResolvedCellFormat,
|
|
61
|
-
} from "./conditional-formatting";
|
|
62
|
-
import SvGridDropdown from "./SvGridDropdown.svelte";
|
|
63
|
-
import type {
|
|
64
|
-
Props,
|
|
65
|
-
SelectionPoint,
|
|
66
|
-
SelectionRange,
|
|
67
|
-
CellEditState,
|
|
68
|
-
FilterOperator,
|
|
69
|
-
FilterOption,
|
|
70
|
-
MenuPosition,
|
|
71
|
-
} from "./SvGrid.types";
|
|
72
|
-
import {
|
|
73
|
-
cfTextStyle,
|
|
74
|
-
fmtStat,
|
|
75
|
-
getCellKey,
|
|
76
|
-
resolveClassList,
|
|
77
|
-
toDateInputValue,
|
|
78
|
-
toDateTimeLocalInputValue,
|
|
79
|
-
getEditableInputValue,
|
|
80
|
-
getEditorInputType,
|
|
81
|
-
toValueArray,
|
|
82
|
-
getOptionLabel,
|
|
83
|
-
getOptionColor,
|
|
84
|
-
colorfulChipStyle,
|
|
85
|
-
getEditorClass,
|
|
86
|
-
asDate,
|
|
87
|
-
clampMenuX,
|
|
88
|
-
cssEscape,
|
|
89
|
-
rawToNumber,
|
|
90
|
-
formatFacetNumber,
|
|
91
|
-
formatFacetDate,
|
|
92
|
-
} from "./SvGrid.helpers";
|
|
93
|
-
import { createSummaries } from "./summaries";
|
|
94
|
-
import { createMenus } from "./menus";
|
|
95
|
-
import { createCellRender } from "./cell-render";
|
|
96
|
-
import { createEditing } from "./editing";
|
|
97
|
-
import { createSelection } from "./selection";
|
|
98
|
-
import { createColumns } from "./columns";
|
|
99
|
-
import { createGridApi } from "./build-api";
|
|
100
|
-
import { createClipboard } from "./clipboard";
|
|
101
|
-
import {
|
|
102
|
-
filterOperatorOptions,
|
|
103
|
-
fallbackOperatorOption,
|
|
104
|
-
TEXT_OPERATORS,
|
|
105
|
-
NUMBER_OPERATORS,
|
|
106
|
-
DATE_OPERATORS,
|
|
107
|
-
CHECKBOX_OPERATORS,
|
|
108
|
-
operatorOption,
|
|
109
|
-
operatorsForColumn,
|
|
110
|
-
defaultOperatorFor,
|
|
111
|
-
operatorLabelFor,
|
|
112
|
-
} from "./filter-operators";
|
|
113
|
-
import {
|
|
114
|
-
type FacetBucket,
|
|
115
|
-
isBucketableColumn,
|
|
116
|
-
buildBuckets,
|
|
117
|
-
isInBucket,
|
|
118
|
-
} from "./facet-buckets";
|
|
119
|
-
import {
|
|
120
|
-
getColumnBaseValue,
|
|
121
|
-
isGroupRow,
|
|
122
|
-
toolPanelHeaderLabel,
|
|
123
|
-
formatSummaryNumeric,
|
|
124
|
-
getColumnAlign,
|
|
125
|
-
getPinnedCellValue,
|
|
126
|
-
getColumnAccessorValue,
|
|
127
|
-
columnDefMatchesId,
|
|
128
|
-
} from "./cell-values";
|
|
129
9
|
|
|
130
10
|
import { getKeyboardIntent, getNextActiveCell } from "./keyboard";
|
|
131
11
|
|
|
@@ -249,6 +129,23 @@ export function createKeyboard<
|
|
|
249
129
|
// expands a collapsed group row, ArrowLeft collapses an expanded one,
|
|
250
130
|
// instead of moving the active cell (treegrid-style). Leaves fall through
|
|
251
131
|
// to normal navigation.
|
|
132
|
+
// Client tree data gets the same ArrowRight/ArrowLeft contract, driven off
|
|
133
|
+
// the row model rather than consumer callbacks.
|
|
134
|
+
if (ctx.props.treeData && (intent === "moveRight" || intent === "moveLeft")) {
|
|
135
|
+
const row = ctx.allRows[current.rowIndex];
|
|
136
|
+
if (row?.getCanExpand?.()) {
|
|
137
|
+
const expanded = row.getIsExpanded?.() ?? false;
|
|
138
|
+
if (intent === "moveRight" && !expanded) {
|
|
139
|
+
row.toggleExpanded?.();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (intent === "moveLeft" && expanded) {
|
|
143
|
+
row.toggleExpanded?.();
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
252
149
|
const serverGroup = ctx.props.serverGroup;
|
|
253
150
|
if (serverGroup && (intent === "moveRight" || intent === "moveLeft")) {
|
|
254
151
|
const data = ctx.allRows[current.rowIndex]?.original;
|