@svgrid/grid 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +116 -88
- package/README.md +199 -199
- package/dist/FlexRender.svelte +96 -96
- package/dist/GridFooter.svelte +181 -181
- package/dist/SvAutoComplete.svelte +169 -169
- package/dist/SvAvatar.svelte +75 -75
- package/dist/SvCalendar.svelte +503 -503
- package/dist/SvCarousel.svelte +141 -141
- package/dist/SvCheckBox.svelte +102 -102
- package/dist/SvCircularProgress.svelte +109 -109
- package/dist/SvColorInput.svelte +181 -181
- package/dist/SvComboBox.svelte +279 -279
- package/dist/SvContextMenu.svelte +116 -116
- package/dist/SvCountryInput.svelte +163 -163
- package/dist/SvDrawer.svelte +254 -254
- package/dist/SvDropDownList.svelte +378 -378
- package/dist/SvDurationInput.svelte +126 -126
- package/dist/SvField.svelte +293 -293
- package/dist/SvForm.svelte +437 -437
- package/dist/SvGridCellEditor.svelte +748 -744
- package/dist/SvGridChart.svelte +1716 -1716
- package/dist/SvGridChartPanel.svelte +485 -485
- package/dist/SvGridChartView.svelte +70 -70
- package/dist/SvGridDropdown.svelte +728 -728
- package/dist/SvGridSelect.svelte +270 -270
- package/dist/SvGroupCell.svelte +116 -116
- package/dist/SvListBox.svelte +334 -334
- package/dist/SvMaskedInput.svelte +122 -122
- package/dist/SvMenu.svelte +124 -124
- package/dist/SvMenuList.svelte +146 -146
- package/dist/SvMultiSelect.svelte +293 -293
- package/dist/SvNumberInput.svelte +172 -172
- package/dist/SvOtpInput.svelte +158 -158
- package/dist/SvPasswordInput.svelte +151 -151
- package/dist/SvPhoneInput.svelte +133 -133
- package/dist/SvPopover.svelte +197 -197
- package/dist/SvProgress.svelte +116 -116
- package/dist/SvRadioGroup.svelte +107 -107
- package/dist/SvRating.svelte +112 -112
- package/dist/SvResult.svelte +73 -73
- package/dist/SvRichText.svelte +211 -211
- package/dist/SvRowGroupPanel.svelte +170 -170
- package/dist/SvScrollArea.svelte +61 -61
- package/dist/SvSlider.svelte +203 -203
- package/dist/SvSwitchButton.svelte +108 -108
- package/dist/SvTagsInput.svelte +115 -115
- package/dist/SvTextInput.svelte +147 -147
- package/dist/SvTimePicker.svelte +245 -245
- package/dist/SvToaster.svelte +159 -159
- package/dist/SvToggleButton.svelte +85 -85
- package/dist/SvTooltip.svelte +161 -161
- package/dist/SvTour.svelte +208 -208
- package/dist/SvTree.svelte +444 -444
- package/dist/SvTreeSelect.svelte +239 -239
- package/dist/cdn/{GridMenus-DMihUtma.js → GridMenus-CYzOvmNQ.js} +3 -3
- package/dist/cdn/{GridMenus-nmNDj1a3.js → GridMenus-DFB6iwF8.js} +3 -3
- package/dist/cdn/{SvDateRangeInput-DflbiP7N.js → SvDateRangeInput-CaOuMs8O.js} +1 -1
- package/dist/cdn/{SvDateRangeInput-Bh0A0JkF.js → SvDateRangeInput-DMLKmGEc.js} +1 -1
- package/dist/cdn/{SvDateTimePicker-Bivn8dAP.js → SvDateTimePicker-CCbDZNZB.js} +1 -1
- package/dist/cdn/{SvDateTimePicker-BWwpfB_o.js → SvDateTimePicker-sonaH0oh.js} +1 -1
- package/dist/cdn/{SvGridCellEditor-Ba7rY3eu.js → SvGridCellEditor-BWnTi2N7.js} +2 -2
- package/dist/cdn/{SvGridCellEditor-AXL8cHGO.js → SvGridCellEditor-CjEJMJKc.js} +2 -2
- package/dist/cdn/{SvGridChart-CwhFz7GV.js → SvGridChart-BEJmNNx9.js} +1 -1
- package/dist/cdn/{SvGridChart-Bs2GIR2Q.js → SvGridChart-BwVos976.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-1WXuStzM.js → SvGridChartPanel-BOknOkrP.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-e5_nqNP9.js → SvGridChartPanel-D2PjII4O.js} +1 -1
- package/dist/cdn/{SvGridChartView-SmPW10dI.js → SvGridChartView-00LPUHL1.js} +1 -1
- package/dist/cdn/{SvGridChartView-eSPuJE6g.js → SvGridChartView-BhUEJ5ki.js} +1 -1
- package/dist/cdn/{SvGridDropdown-B1ZcLpgs.js → SvGridDropdown-D0VdjeR8.js} +1 -1
- package/dist/cdn/{SvGridDropdown-CtdbHcIS.js → SvGridDropdown-D13MtJ2j.js} +1 -1
- package/dist/cdn/{date-format-DZT7T1wf.js → date-format-BNii4zeD.js} +2 -2
- package/dist/cdn/{date-format-CcP1tafP.js → date-format-BnnHlqGw.js} +2 -2
- package/dist/cdn/{src-B_YS5AOc.js → src-C8QjSaq-.js} +42 -44
- package/dist/cdn/{src-DYXXpuSk.js → src-D0hO2eYN.js} +41 -43
- package/dist/cdn/svgrid.js +7 -7
- package/dist/cdn/svgrid.svelte-external.js +7 -7
- package/dist/chart-export.js +8 -8
- package/dist/editing.js +22 -13
- package/package.json +11 -11
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +181 -181
- package/src/SvAutoComplete.svelte +169 -169
- package/src/SvAvatar.svelte +75 -75
- package/src/SvCalendar.svelte +503 -503
- package/src/SvCalendar.test.ts +226 -226
- package/src/SvCarousel.svelte +141 -141
- package/src/SvCheckBox.svelte +102 -102
- package/src/SvCircularProgress.svelte +109 -109
- package/src/SvColorInput.svelte +181 -181
- package/src/SvComboBox.svelte +279 -279
- package/src/SvContextMenu.svelte +116 -116
- package/src/SvCountryInput.svelte +163 -163
- package/src/SvDrawer.svelte +254 -254
- package/src/SvDropDownList.svelte +378 -378
- package/src/SvDurationInput.svelte +126 -126
- package/src/SvField.svelte +293 -293
- package/src/SvForm.svelte +437 -437
- package/src/SvForm.test.ts +411 -411
- package/src/SvGrid.types.ts +2092 -2092
- package/src/SvGridCellEditor.svelte +748 -744
- package/src/SvGridChart.svelte +1716 -1716
- package/src/SvGridChartPanel.svelte +485 -485
- package/src/SvGridChartView.svelte +70 -70
- package/src/SvGridDropdown.svelte +728 -728
- package/src/SvGridSelect.svelte +270 -270
- package/src/SvGroupCell.svelte +116 -116
- package/src/SvListBox.svelte +334 -334
- package/src/SvMaskedInput.svelte +122 -122
- package/src/SvMenu.svelte +124 -124
- package/src/SvMenu.test.ts +97 -97
- package/src/SvMenuList.svelte +146 -146
- package/src/SvMultiSelect.svelte +293 -293
- package/src/SvNumberInput.svelte +172 -172
- package/src/SvOtpInput.svelte +158 -158
- package/src/SvPasswordInput.svelte +151 -151
- package/src/SvPhoneInput.svelte +133 -133
- package/src/SvPopover.svelte +197 -197
- package/src/SvProgress.svelte +116 -116
- package/src/SvRadioGroup.svelte +107 -107
- package/src/SvRating.svelte +112 -112
- package/src/SvResult.svelte +73 -73
- package/src/SvRichText.svelte +211 -211
- package/src/SvRowGroupPanel.svelte +170 -170
- package/src/SvScrollArea.svelte +61 -61
- package/src/SvSlider.svelte +203 -203
- package/src/SvSwitchButton.svelte +108 -108
- package/src/SvTagsInput.svelte +115 -115
- package/src/SvTextInput.svelte +147 -147
- package/src/SvTimePicker.svelte +245 -245
- package/src/SvToaster.svelte +159 -159
- package/src/SvToaster.test.ts +95 -95
- package/src/SvToggleButton.svelte +85 -85
- package/src/SvTooltip.svelte +161 -161
- package/src/SvTour.svelte +208 -208
- package/src/SvTree.svelte +444 -444
- package/src/SvTreeSelect.svelte +239 -239
- package/src/a11y/dismissable.test.ts +119 -119
- package/src/a11y/dismissable.ts +114 -114
- package/src/a11y.contract.test.ts +49 -49
- package/src/a11y.test.ts +59 -59
- package/src/a11y.ts +61 -61
- package/src/ai.test.ts +502 -502
- package/src/ai.ts +1419 -1419
- package/src/build-api.coverage.test.ts +633 -633
- package/src/build-api.ts +846 -846
- package/src/builtin-editors.grid.test.ts +83 -83
- package/src/cell-formatting.ts +171 -171
- package/src/cell-render.test.ts +513 -513
- package/src/cell-render.ts +496 -496
- package/src/cell-values.ts +148 -148
- package/src/chart-export.ts +202 -202
- package/src/chart-view.svelte.ts +36 -36
- package/src/chart.ts +2321 -2321
- package/src/collaboration.test.ts +104 -104
- package/src/collaboration.ts +167 -167
- package/src/column-groups.ts +78 -78
- package/src/core.performance.test.ts +30 -30
- package/src/core.ts +1865 -1865
- package/src/createAutocomplete.svelte.ts +132 -132
- package/src/createCombobox.svelte.ts +191 -191
- package/src/createCountryInput.svelte.ts +157 -157
- package/src/createDropdownList.svelte.ts +168 -168
- package/src/createForm.svelte.ts +386 -386
- package/src/createGrid.svelte.ts +42 -42
- package/src/createGrid.test.ts +10 -10
- package/src/createGridState.svelte.ts +17 -17
- package/src/createListbox.svelte.ts +250 -250
- package/src/createMenu.svelte.ts +224 -224
- package/src/createPopoverSelect.svelte.ts +213 -213
- package/src/createSlider.svelte.ts +191 -191
- package/src/createTooltip.svelte.ts +144 -144
- package/src/createTree.svelte.ts +322 -322
- package/src/datetime/date-core.ts +206 -206
- package/src/datetime/date-restrict.ts +61 -61
- package/src/datetime/timezone.ts +135 -135
- package/src/dock-manager-model.ts +596 -596
- package/src/dock-model.ts +374 -374
- package/src/editing.test.ts +974 -974
- package/src/editing.ts +618 -609
- package/src/editor-contract.ts +171 -171
- package/src/editor-registry.grid.test.ts +144 -144
- package/src/editor-registry.ts +122 -122
- package/src/export-data-api.test.ts +126 -126
- package/src/export-format.test.ts +107 -107
- package/src/export-format.ts +601 -601
- package/src/filter-operators.ts +160 -160
- package/src/filtering/excel-filters.ts +325 -325
- package/src/flex-render.ts +3 -3
- package/src/form-field.ts +127 -127
- package/src/group-display.test.ts +167 -167
- package/src/group-display.ts +200 -200
- package/src/headless.ts +87 -87
- package/src/js-scroller.svelte.ts +173 -173
- package/src/keyboard-handlers.ts +270 -270
- package/src/keyboard.test.ts +59 -59
- package/src/keyboard.ts +97 -97
- package/src/list-nav.test.ts +49 -49
- package/src/list-nav.ts +29 -29
- package/src/list-option.test.ts +56 -56
- package/src/list-option.ts +179 -179
- package/src/menus.ts +597 -597
- package/src/merge-objects.ts +48 -48
- package/src/overlays.test.ts +90 -90
- package/src/positioning.ts +268 -268
- package/src/render-component.ts +28 -28
- package/src/row-drag.test.ts +401 -401
- package/src/row-drag.ts +419 -419
- package/src/row-resize.test.ts +524 -524
- package/src/row-resize.ts +228 -228
- package/src/scheduler-ical.ts +181 -181
- package/src/scheduler-model.test.ts +562 -562
- package/src/scheduler-model.ts +873 -873
- package/src/selection.test.ts +885 -885
- package/src/server-data-source.test.ts +383 -383
- package/src/server-data-source.ts +469 -469
- package/src/sparkline.test.ts +68 -68
- package/src/sparkline.ts +169 -169
- package/src/spreadsheet.test.ts +488 -488
- package/src/spreadsheet.ts +312 -312
- package/src/static-functions.ts +11 -11
- package/src/subscribe.ts +38 -38
- package/src/summaries.ts +113 -113
- package/src/svgrid-wrapper.types.ts +563 -563
- package/src/svgrid.async-editor-options.test.ts +273 -273
- package/src/svgrid.auto-row-height.test.ts +204 -204
- package/src/svgrid.behavior.test.ts +910 -910
- package/src/svgrid.charting.test.ts +534 -534
- package/src/svgrid.comments-autocomplete.test.ts +127 -127
- package/src/svgrid.context-menu.test.ts +147 -147
- package/src/svgrid.features.test.ts +157 -157
- package/src/svgrid.filter-depth.test.ts +163 -163
- package/src/svgrid.filter-menu-listbox.svelte.test.ts +382 -377
- package/src/svgrid.filter-menu-scroll.test.ts +112 -112
- package/src/svgrid.grand-total.test.ts +188 -188
- package/src/svgrid.group-display-mode.test.ts +171 -171
- package/src/svgrid.group-footers.test.ts +121 -121
- package/src/svgrid.group-pagination.test.ts +153 -153
- package/src/svgrid.new-features.wrapper.test.ts +251 -251
- package/src/svgrid.tree-data.test.ts +186 -186
- package/src/svgrid.wrapper.test.ts +63 -63
- package/src/svgriddropdown.async-panel.svelte.test.ts +195 -195
- package/src/test-setup.ts +62 -62
- package/src/themes/index.ts +288 -288
- package/src/toast-store.svelte.ts +250 -250
- package/src/toast-store.test.ts +147 -147
- package/src/tree-row-model.test.ts +168 -168
- package/src/ui-buttons.test.ts +144 -144
- package/src/ui-inputs.test.ts +118 -118
- package/src/ui-localization.test.ts +113 -113
- package/src/ui-range.test.ts +70 -70
- package/src/ui-selection.test.ts +155 -155
- package/src/ui-tier1.test.ts +142 -142
- package/src/virtual.test.ts +88 -88
- package/src/virtualization/column-virtualizer.test.ts +27 -27
- package/src/virtualization/column-virtualizer.ts +30 -30
- package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
- package/src/virtualization/types.ts +30 -30
- package/src/virtualization/virtualizer.test.ts +47 -47
- package/src/virtualization/virtualizer.ts +322 -322
package/src/subscribe.ts
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
import type { RowData, SvGrid, TableFeatures } from './core'
|
|
2
|
-
|
|
3
|
-
const shallowCompare = (a: unknown, b: unknown) => {
|
|
4
|
-
if (Object.is(a, b)) return true
|
|
5
|
-
if (!a || !b || typeof a !== 'object' || typeof b !== 'object') return false
|
|
6
|
-
const aObj = a as Record<string, unknown>
|
|
7
|
-
const bObj = b as Record<string, unknown>
|
|
8
|
-
const aKeys = Object.keys(aObj)
|
|
9
|
-
const bKeys = Object.keys(bObj)
|
|
10
|
-
if (aKeys.length !== bKeys.length) return false
|
|
11
|
-
return aKeys.every((key) => Object.is(aObj[key], bObj[key]))
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Fine-grained subscription to `table.store` using `useSelector` with shallow
|
|
16
|
-
* comparison. Call from `<script>` so the selector is contextually typed.
|
|
17
|
-
*/
|
|
18
|
-
export function subscribeGrid<
|
|
19
|
-
TFeatures extends TableFeatures,
|
|
20
|
-
TData extends RowData,
|
|
21
|
-
TSelected,
|
|
22
|
-
>(
|
|
23
|
-
grid: SvGrid<TData>,
|
|
24
|
-
selector: (state: Record<string, any>) => TSelected,
|
|
25
|
-
) {
|
|
26
|
-
let current = selector(grid.store.state)
|
|
27
|
-
grid.store.subscribe(() => {
|
|
28
|
-
const next = selector(grid.store.state)
|
|
29
|
-
if (!shallowCompare(current, next)) current = next
|
|
30
|
-
})
|
|
31
|
-
return {
|
|
32
|
-
get current() {
|
|
33
|
-
return current
|
|
34
|
-
},
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export const subscribeSvGrid = subscribeGrid
|
|
1
|
+
import type { RowData, SvGrid, TableFeatures } from './core'
|
|
2
|
+
|
|
3
|
+
const shallowCompare = (a: unknown, b: unknown) => {
|
|
4
|
+
if (Object.is(a, b)) return true
|
|
5
|
+
if (!a || !b || typeof a !== 'object' || typeof b !== 'object') return false
|
|
6
|
+
const aObj = a as Record<string, unknown>
|
|
7
|
+
const bObj = b as Record<string, unknown>
|
|
8
|
+
const aKeys = Object.keys(aObj)
|
|
9
|
+
const bKeys = Object.keys(bObj)
|
|
10
|
+
if (aKeys.length !== bKeys.length) return false
|
|
11
|
+
return aKeys.every((key) => Object.is(aObj[key], bObj[key]))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Fine-grained subscription to `table.store` using `useSelector` with shallow
|
|
16
|
+
* comparison. Call from `<script>` so the selector is contextually typed.
|
|
17
|
+
*/
|
|
18
|
+
export function subscribeGrid<
|
|
19
|
+
TFeatures extends TableFeatures,
|
|
20
|
+
TData extends RowData,
|
|
21
|
+
TSelected,
|
|
22
|
+
>(
|
|
23
|
+
grid: SvGrid<TData>,
|
|
24
|
+
selector: (state: Record<string, any>) => TSelected,
|
|
25
|
+
) {
|
|
26
|
+
let current = selector(grid.store.state)
|
|
27
|
+
grid.store.subscribe(() => {
|
|
28
|
+
const next = selector(grid.store.state)
|
|
29
|
+
if (!shallowCompare(current, next)) current = next
|
|
30
|
+
})
|
|
31
|
+
return {
|
|
32
|
+
get current() {
|
|
33
|
+
return current
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const subscribeSvGrid = subscribeGrid
|
package/src/summaries.ts
CHANGED
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
// summaries handlers extracted from the controller. Imperative event handlers
|
|
2
|
-
// reading/writing controller state via the `ctx` handle; the reactive core
|
|
3
|
-
// ($state/$derived/$effect) stays in the controller.
|
|
4
|
-
import {
|
|
5
|
-
applyGroupAggregate,
|
|
6
|
-
type Column,
|
|
7
|
-
type Row,
|
|
8
|
-
type RowData,
|
|
9
|
-
type TableFeatures,
|
|
10
|
-
} from "./index";
|
|
11
|
-
import "./sv-grid-scrollbar";
|
|
12
|
-
import {
|
|
13
|
-
getCellKey,
|
|
14
|
-
} from "./SvGrid.helpers";
|
|
15
|
-
import {
|
|
16
|
-
formatSummaryNumeric,
|
|
17
|
-
} from "./cell-values";
|
|
18
|
-
|
|
19
|
-
export function createSummaries<
|
|
20
|
-
TFeatures extends TableFeatures = TableFeatures,
|
|
21
|
-
TData extends RowData = RowData,
|
|
22
|
-
>(ctx: any) {
|
|
23
|
-
/**
|
|
24
|
-
* Aggregate every row into a per-column footer summary (sum for numeric
|
|
25
|
-
* columns, `Count: N` otherwise). This loop is the hottest path on a
|
|
26
|
-
* large grid - it is rows x columns iterations - so two things keep the
|
|
27
|
-
* constant factor down:
|
|
28
|
-
*
|
|
29
|
-
* 1. The edited-cell overlay is only consulted when an edit actually
|
|
30
|
-
* exists. `key in editedCellValues` hits a reactive-proxy `has`
|
|
31
|
-
* trap for every cell otherwise - 5M no-op trap calls on a
|
|
32
|
-
* 100k x 50 grid. Skipping it when the map is empty is the single
|
|
33
|
-
* biggest win here.
|
|
34
|
-
* 2. The column's accessor (`fieldFn` / `field`) is resolved once
|
|
35
|
-
* per column, not re-read off `columnDef` for every cell.
|
|
36
|
-
*/
|
|
37
|
-
function computeSummaries(
|
|
38
|
-
rows: ReadonlyArray<Row<TData>>,
|
|
39
|
-
columns: ReadonlyArray<Column<TData>>,
|
|
40
|
-
): Record<string, string> {
|
|
41
|
-
const summary: Record<string, string> = {};
|
|
42
|
-
const rowCount = rows.length;
|
|
43
|
-
const hasEdits = Object.keys(ctx.editedCellValues).length > 0;
|
|
44
|
-
for (const column of columns) {
|
|
45
|
-
const def = column.columnDef;
|
|
46
|
-
const fieldFn = def.fieldFn;
|
|
47
|
-
const field = def.field;
|
|
48
|
-
const columnId = column.id;
|
|
49
|
-
|
|
50
|
-
// A column that declares its own `summary` opts out of the default
|
|
51
|
-
// sum/count below. Only that column pays for the aggregator dispatch, so
|
|
52
|
-
// a grid that declares none keeps the original hot loop exactly as it was.
|
|
53
|
-
const declared = def.summary;
|
|
54
|
-
if (declared !== undefined) {
|
|
55
|
-
if (declared === false) {
|
|
56
|
-
summary[columnId] = "";
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
const value = applyGroupAggregate(declared, columnId, rows);
|
|
60
|
-
summary[columnId] =
|
|
61
|
-
typeof value === "number" && Number.isFinite(value)
|
|
62
|
-
? formatSummaryNumeric(column, value)
|
|
63
|
-
: value == null
|
|
64
|
-
? ""
|
|
65
|
-
: String(value);
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
let numericSum = 0;
|
|
70
|
-
let numericCount = 0;
|
|
71
|
-
for (let i = 0; i < rowCount; i += 1) {
|
|
72
|
-
const row = rows[i]!;
|
|
73
|
-
let value: unknown;
|
|
74
|
-
const base = fieldFn
|
|
75
|
-
? fieldFn(row.original)
|
|
76
|
-
: field
|
|
77
|
-
? (row.original as Record<string, unknown>)[field]
|
|
78
|
-
: row.getCellValueByColumnId(columnId);
|
|
79
|
-
if (hasEdits) {
|
|
80
|
-
const key = getCellKey(row.id, columnId);
|
|
81
|
-
value = key in ctx.editedCellValues ? ctx.editedCellValues[key] : base;
|
|
82
|
-
} else {
|
|
83
|
-
value = base;
|
|
84
|
-
}
|
|
85
|
-
const asNumber = Number(value);
|
|
86
|
-
if (Number.isFinite(asNumber)) {
|
|
87
|
-
numericSum += asNumber;
|
|
88
|
-
numericCount += 1;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
summary[columnId] =
|
|
92
|
-
numericCount > 0
|
|
93
|
-
? formatSummaryNumeric(column, numericSum)
|
|
94
|
-
: `Count: ${rowCount}`;
|
|
95
|
-
}
|
|
96
|
-
return summary;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function hasRenderedColumn(entry: {
|
|
100
|
-
item: (typeof ctx.renderedColumnItems)[number];
|
|
101
|
-
column: (typeof ctx.allColumns)[number] | undefined;
|
|
102
|
-
}): entry is {
|
|
103
|
-
item: (typeof ctx.renderedColumnItems)[number];
|
|
104
|
-
column: (typeof ctx.allColumns)[number];
|
|
105
|
-
} {
|
|
106
|
-
return entry.column !== undefined;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
computeSummaries,
|
|
111
|
-
hasRenderedColumn,
|
|
112
|
-
};
|
|
113
|
-
}
|
|
1
|
+
// summaries handlers extracted from the controller. Imperative event handlers
|
|
2
|
+
// reading/writing controller state via the `ctx` handle; the reactive core
|
|
3
|
+
// ($state/$derived/$effect) stays in the controller.
|
|
4
|
+
import {
|
|
5
|
+
applyGroupAggregate,
|
|
6
|
+
type Column,
|
|
7
|
+
type Row,
|
|
8
|
+
type RowData,
|
|
9
|
+
type TableFeatures,
|
|
10
|
+
} from "./index";
|
|
11
|
+
import "./sv-grid-scrollbar";
|
|
12
|
+
import {
|
|
13
|
+
getCellKey,
|
|
14
|
+
} from "./SvGrid.helpers";
|
|
15
|
+
import {
|
|
16
|
+
formatSummaryNumeric,
|
|
17
|
+
} from "./cell-values";
|
|
18
|
+
|
|
19
|
+
export function createSummaries<
|
|
20
|
+
TFeatures extends TableFeatures = TableFeatures,
|
|
21
|
+
TData extends RowData = RowData,
|
|
22
|
+
>(ctx: any) {
|
|
23
|
+
/**
|
|
24
|
+
* Aggregate every row into a per-column footer summary (sum for numeric
|
|
25
|
+
* columns, `Count: N` otherwise). This loop is the hottest path on a
|
|
26
|
+
* large grid - it is rows x columns iterations - so two things keep the
|
|
27
|
+
* constant factor down:
|
|
28
|
+
*
|
|
29
|
+
* 1. The edited-cell overlay is only consulted when an edit actually
|
|
30
|
+
* exists. `key in editedCellValues` hits a reactive-proxy `has`
|
|
31
|
+
* trap for every cell otherwise - 5M no-op trap calls on a
|
|
32
|
+
* 100k x 50 grid. Skipping it when the map is empty is the single
|
|
33
|
+
* biggest win here.
|
|
34
|
+
* 2. The column's accessor (`fieldFn` / `field`) is resolved once
|
|
35
|
+
* per column, not re-read off `columnDef` for every cell.
|
|
36
|
+
*/
|
|
37
|
+
function computeSummaries(
|
|
38
|
+
rows: ReadonlyArray<Row<TData>>,
|
|
39
|
+
columns: ReadonlyArray<Column<TData>>,
|
|
40
|
+
): Record<string, string> {
|
|
41
|
+
const summary: Record<string, string> = {};
|
|
42
|
+
const rowCount = rows.length;
|
|
43
|
+
const hasEdits = Object.keys(ctx.editedCellValues).length > 0;
|
|
44
|
+
for (const column of columns) {
|
|
45
|
+
const def = column.columnDef;
|
|
46
|
+
const fieldFn = def.fieldFn;
|
|
47
|
+
const field = def.field;
|
|
48
|
+
const columnId = column.id;
|
|
49
|
+
|
|
50
|
+
// A column that declares its own `summary` opts out of the default
|
|
51
|
+
// sum/count below. Only that column pays for the aggregator dispatch, so
|
|
52
|
+
// a grid that declares none keeps the original hot loop exactly as it was.
|
|
53
|
+
const declared = def.summary;
|
|
54
|
+
if (declared !== undefined) {
|
|
55
|
+
if (declared === false) {
|
|
56
|
+
summary[columnId] = "";
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const value = applyGroupAggregate(declared, columnId, rows);
|
|
60
|
+
summary[columnId] =
|
|
61
|
+
typeof value === "number" && Number.isFinite(value)
|
|
62
|
+
? formatSummaryNumeric(column, value)
|
|
63
|
+
: value == null
|
|
64
|
+
? ""
|
|
65
|
+
: String(value);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let numericSum = 0;
|
|
70
|
+
let numericCount = 0;
|
|
71
|
+
for (let i = 0; i < rowCount; i += 1) {
|
|
72
|
+
const row = rows[i]!;
|
|
73
|
+
let value: unknown;
|
|
74
|
+
const base = fieldFn
|
|
75
|
+
? fieldFn(row.original)
|
|
76
|
+
: field
|
|
77
|
+
? (row.original as Record<string, unknown>)[field]
|
|
78
|
+
: row.getCellValueByColumnId(columnId);
|
|
79
|
+
if (hasEdits) {
|
|
80
|
+
const key = getCellKey(row.id, columnId);
|
|
81
|
+
value = key in ctx.editedCellValues ? ctx.editedCellValues[key] : base;
|
|
82
|
+
} else {
|
|
83
|
+
value = base;
|
|
84
|
+
}
|
|
85
|
+
const asNumber = Number(value);
|
|
86
|
+
if (Number.isFinite(asNumber)) {
|
|
87
|
+
numericSum += asNumber;
|
|
88
|
+
numericCount += 1;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
summary[columnId] =
|
|
92
|
+
numericCount > 0
|
|
93
|
+
? formatSummaryNumeric(column, numericSum)
|
|
94
|
+
: `Count: ${rowCount}`;
|
|
95
|
+
}
|
|
96
|
+
return summary;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function hasRenderedColumn(entry: {
|
|
100
|
+
item: (typeof ctx.renderedColumnItems)[number];
|
|
101
|
+
column: (typeof ctx.allColumns)[number] | undefined;
|
|
102
|
+
}): entry is {
|
|
103
|
+
item: (typeof ctx.renderedColumnItems)[number];
|
|
104
|
+
column: (typeof ctx.allColumns)[number];
|
|
105
|
+
} {
|
|
106
|
+
return entry.column !== undefined;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
computeSummaries,
|
|
111
|
+
hasRenderedColumn,
|
|
112
|
+
};
|
|
113
|
+
}
|