@svgrid/grid 3.0.0 → 3.0.2
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 +190 -181
- package/dist/GridFooter.svelte.d.ts +8 -0
- package/dist/GridMenus.svelte +18 -10
- package/dist/GridMenus.svelte.d.ts +2 -1
- 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/SvGrid.controller.svelte.d.ts +17 -0
- package/dist/SvGrid.controller.svelte.js +128 -14
- package/dist/SvGrid.css +79 -10
- package/dist/SvGrid.svelte +191 -186
- package/dist/SvGrid.types.d.ts +42 -1
- package/dist/SvGridCellEditor.svelte +748 -744
- package/dist/SvGridChart.svelte +2246 -1716
- package/dist/SvGridChart.svelte.d.ts +63 -1
- package/dist/SvGridChartPanel.svelte +633 -485
- package/dist/SvGridChartView.svelte +79 -70
- package/dist/SvGridChartView.svelte.d.ts +7 -0
- package/dist/SvGridDropdown.svelte +728 -728
- package/dist/SvGridSelect.svelte +270 -270
- package/dist/SvGroupCell.svelte +141 -116
- package/dist/SvGroupCell.svelte.d.ts +8 -0
- 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 +186 -170
- package/dist/SvRowGroupPanel.svelte.d.ts +8 -0
- 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/ai.d.ts +11 -2
- package/dist/ai.js +51 -7
- package/dist/cdn/GridMenus-DyFA-QxA.js +602 -0
- package/dist/cdn/GridMenus-M2XP9t0_.js +603 -0
- 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-DAb9RbRs.js +1480 -0
- package/dist/cdn/SvGridChart-Yl-IXZFO.js +1481 -0
- package/dist/cdn/SvGridChartPanel-DayQpDsR.js +834 -0
- package/dist/cdn/SvGridChartPanel-aqf8n-vJ.js +810 -0
- package/dist/cdn/{SvGridChartView-eSPuJE6g.js → SvGridChartView-CxfakxR0.js} +5 -3
- package/dist/cdn/{SvGridChartView-SmPW10dI.js → SvGridChartView-Dm_YEx9L.js} +5 -3
- 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/cell-formatting-C2Hf5gqd.js +86 -0
- package/dist/cdn/chart-CKJRCPOk.js +1652 -0
- 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/{export-format-Cv1Dll6k.js → export-format-CUDLy2yn.js} +84 -168
- package/dist/cdn/{src-B_YS5AOc.js → src-BJ2opd0j.js} +3642 -3450
- package/dist/cdn/{src-DYXXpuSk.js → src-DNbz94Ct.js} +8649 -8457
- package/dist/cdn/svgrid.js +16 -15
- package/dist/cdn/svgrid.svelte-external.js +16 -15
- package/dist/cell-formatting.d.ts +7 -0
- package/dist/cell-formatting.js +7 -1
- package/dist/chart-export.d.ts +23 -2
- package/dist/chart-export.js +88 -14
- package/dist/chart.d.ts +410 -18
- package/dist/chart.js +824 -24
- package/dist/editing.js +22 -13
- package/dist/grid-icons.d.ts +49 -0
- package/dist/grid-icons.js +86 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/package.json +1 -1
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +190 -181
- package/src/GridMenus.svelte +18 -10
- 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.controller.svelte.ts +146 -13
- package/src/SvGrid.css +79 -10
- package/src/SvGrid.svelte +191 -186
- package/src/SvGrid.types.ts +2133 -2092
- package/src/SvGridCellEditor.svelte +748 -744
- package/src/SvGridChart.svelte +2246 -1716
- package/src/SvGridChart.test.ts +243 -1
- package/src/SvGridChartPanel.svelte +633 -485
- package/src/SvGridChartView.svelte +79 -70
- package/src/SvGridDropdown.svelte +728 -728
- package/src/SvGridSelect.svelte +270 -270
- package/src/SvGroupCell.svelte +141 -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 +186 -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 +561 -502
- package/src/ai.ts +1472 -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 +177 -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.test.ts +112 -0
- package/src/chart-export.ts +270 -202
- package/src/chart-view.svelte.ts +36 -36
- package/src/chart.test.ts +830 -1
- package/src/chart.ts +3342 -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/grid-icons.test.ts +46 -0
- package/src/grid-icons.ts +184 -0
- package/src/group-display.test.ts +167 -167
- package/src/group-display.ts +200 -200
- package/src/headless.ts +87 -87
- package/src/icon-overrides.grid.test.ts +188 -0
- package/src/icon-seam.test.ts +100 -0
- package/src/index.ts +19 -0
- 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 +654 -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/dist/cdn/GridMenus-DMihUtma.js +0 -600
- package/dist/cdn/GridMenus-nmNDj1a3.js +0 -601
- package/dist/cdn/SvGridChart-Bs2GIR2Q.js +0 -1202
- package/dist/cdn/SvGridChart-CwhFz7GV.js +0 -1203
- package/dist/cdn/SvGridChartPanel-1WXuStzM.js +0 -561
- package/dist/cdn/SvGridChartPanel-e5_nqNP9.js +0 -585
- package/dist/cdn/chart-i4XcXyHJ.js +0 -1214
|
@@ -2028,6 +2028,18 @@ export function createSvGridController<
|
|
|
2028
2028
|
dimensionId: string | null;
|
|
2029
2029
|
measureId: string | null;
|
|
2030
2030
|
seriesId: string | null | undefined;
|
|
2031
|
+
/** Scatter only: the Y measure. The X measure reuses `measureId`. */
|
|
2032
|
+
measure2Id: string | null;
|
|
2033
|
+
// These four were config-only until the panel grew controls for them, so
|
|
2034
|
+
// an author could set them and a user could not. Null inherits the config.
|
|
2035
|
+
/** Bar orientation. */
|
|
2036
|
+
orientation: "vertical" | "horizontal" | null;
|
|
2037
|
+
/** Normalise each category to 100 percent. Implies stacked. */
|
|
2038
|
+
stacked100: boolean | null;
|
|
2039
|
+
/** Pie only: draw as a donut. */
|
|
2040
|
+
donut: boolean | null;
|
|
2041
|
+
/** Series colours chosen in the panel. */
|
|
2042
|
+
palette: string[] | null;
|
|
2031
2043
|
stacked: boolean | null;
|
|
2032
2044
|
dataLabels: boolean | null;
|
|
2033
2045
|
logScale: boolean | null;
|
|
@@ -2045,6 +2057,11 @@ export function createSvGridController<
|
|
|
2045
2057
|
dimensionId: null,
|
|
2046
2058
|
measureId: null,
|
|
2047
2059
|
seriesId: undefined,
|
|
2060
|
+
measure2Id: null,
|
|
2061
|
+
orientation: null,
|
|
2062
|
+
stacked100: null,
|
|
2063
|
+
donut: null,
|
|
2064
|
+
palette: null,
|
|
2048
2065
|
stacked: null,
|
|
2049
2066
|
dataLabels: null,
|
|
2050
2067
|
logScale: null,
|
|
@@ -2088,14 +2105,22 @@ export function createSvGridController<
|
|
|
2088
2105
|
const chartableColumns = $derived.by(function chartableColumns_d() {
|
|
2089
2106
|
const dims: Array<{ id: string; field: string; label: string }> = [];
|
|
2090
2107
|
const measures: Array<{ id: string; field: string; label: string }> = [];
|
|
2108
|
+
// Dates are a subset of dims, tracked separately so the panel can tell
|
|
2109
|
+
// whether a calendar chart is possible at all before the user picks
|
|
2110
|
+
// anything. Offering a type that can only render blank is worse than not
|
|
2111
|
+
// offering it.
|
|
2112
|
+
const dates: Array<{ id: string; field: string; label: string }> = [];
|
|
2091
2113
|
for (const col of allColumns) {
|
|
2092
2114
|
const field = col.columnDef.field;
|
|
2093
2115
|
if (!field) continue;
|
|
2094
2116
|
const entry = { id: col.id, field, label: columnLabel(col) };
|
|
2095
2117
|
if (isNumericColumn(col)) measures.push(entry);
|
|
2096
|
-
else
|
|
2118
|
+
else {
|
|
2119
|
+
dims.push(entry);
|
|
2120
|
+
if (columnIsDate(col)) dates.push(entry);
|
|
2121
|
+
}
|
|
2097
2122
|
}
|
|
2098
|
-
return { dims, measures };
|
|
2123
|
+
return { dims, measures, dates };
|
|
2099
2124
|
});
|
|
2100
2125
|
const chartColumnDefaults = $derived.by(function chartColumnDefaults_d() {
|
|
2101
2126
|
const { dims, measures } = chartableColumns;
|
|
@@ -2139,6 +2164,21 @@ export function createSvGridController<
|
|
|
2139
2164
|
if (configured.length) return configured;
|
|
2140
2165
|
return chartColumnDefaults.measureIds;
|
|
2141
2166
|
});
|
|
2167
|
+
/** Scatter's Y measure. Defaults to the second numeric column, so picking
|
|
2168
|
+
* scatter plots something immediately rather than nothing. */
|
|
2169
|
+
const effectiveChartMeasure2Id = $derived(
|
|
2170
|
+
activeChart.measure2Id ??
|
|
2171
|
+
chartableColumns.measures.find((m) => m.id !== effectiveChartMeasureId)?.id ??
|
|
2172
|
+
null,
|
|
2173
|
+
);
|
|
2174
|
+
const effectiveChartOrientation = $derived(
|
|
2175
|
+
activeChart.orientation ?? chartCfg?.orientation ?? "vertical",
|
|
2176
|
+
);
|
|
2177
|
+
const effectiveChartStacked100 = $derived(
|
|
2178
|
+
activeChart.stacked100 ?? chartCfg?.stacked100 === true,
|
|
2179
|
+
);
|
|
2180
|
+
const effectiveChartDonut = $derived(activeChart.donut ?? !!chartCfg?.donut);
|
|
2181
|
+
const effectiveChartPalette = $derived(activeChart.palette ?? chartCfg?.palette ?? null);
|
|
2142
2182
|
const effectiveChartStacked = $derived(chartStacked ?? chartCfg?.stacked ?? false);
|
|
2143
2183
|
const effectiveChartDataLabels = $derived(chartDataLabels ?? chartCfg?.dataLabels ?? false);
|
|
2144
2184
|
const effectiveChartLogScale = $derived(chartLogScale ?? chartCfg?.yScale === "log");
|
|
@@ -2162,8 +2202,13 @@ export function createSvGridController<
|
|
|
2162
2202
|
? "Count"
|
|
2163
2203
|
: `${chartReduce === "avg" ? "Average" : "Sum"} of ${label}`;
|
|
2164
2204
|
});
|
|
2165
|
-
|
|
2166
|
-
|
|
2205
|
+
/**
|
|
2206
|
+
* Does this column hold dates? Declared type first, then a probe of the
|
|
2207
|
+
* first non-empty value. Lifted out of `chartDimensionIsDate` so the chart
|
|
2208
|
+
* panel can also ask it of columns the user has NOT picked yet, which is
|
|
2209
|
+
* what decides whether a calendar chart is offerable at all.
|
|
2210
|
+
*/
|
|
2211
|
+
function columnIsDate(col: Column<TData> | undefined): boolean {
|
|
2167
2212
|
if (!col) return false;
|
|
2168
2213
|
if (col.columnDef.cellDataType === "date") return true;
|
|
2169
2214
|
if (col.columnDef.cellDataType) return false;
|
|
@@ -2174,7 +2219,10 @@ export function createSvGridController<
|
|
|
2174
2219
|
return typeof v === "string" && /^\d{4}-\d{2}(-\d{2})?/.test(v);
|
|
2175
2220
|
}
|
|
2176
2221
|
return false;
|
|
2177
|
-
}
|
|
2222
|
+
}
|
|
2223
|
+
const chartDimensionIsDate = $derived.by<boolean>(() =>
|
|
2224
|
+
columnIsDate(allColumns.find((c) => c.id === effectiveChartDimensionId)),
|
|
2225
|
+
);
|
|
2178
2226
|
const chartRows = $derived.by<TData[]>(() => {
|
|
2179
2227
|
const rects = getSelectionRects();
|
|
2180
2228
|
const pushLeaf = (out: TData[], row: Row<TData> | undefined) => {
|
|
@@ -2260,12 +2308,27 @@ export function createSvGridController<
|
|
|
2260
2308
|
// null until it resolves, a tick after the panel itself starts loading (which it
|
|
2261
2309
|
// already does). The `getAggregate` server path uses the local bucketsToSpec and
|
|
2262
2310
|
// does not need the engine.
|
|
2263
|
-
|
|
2311
|
+
// The whole module, not just `rowsToChartSpec`: the panel can now reach
|
|
2312
|
+
// every chart type, and the ones that do not read a categories-by-series
|
|
2313
|
+
// grid (scatter, gauge, treemap, calendar, sankey) need their own builders
|
|
2314
|
+
// from the same lazy chunk. Same `import()`, same boundary.
|
|
2315
|
+
let chartEngine = $state<typeof import("./chart") | null>(null);
|
|
2264
2316
|
$effect(() => {
|
|
2265
2317
|
if (chartingEnabled && !chartEngine)
|
|
2266
|
-
import("./chart").then((m) => (chartEngine = m
|
|
2318
|
+
import("./chart").then((m) => (chartEngine = m));
|
|
2267
2319
|
});
|
|
2268
2320
|
|
|
2321
|
+
/** Put the chart's numbers in the same locale as the grid's. A grid that set
|
|
2322
|
+
* `localization.locale` has already said which locale its data is in, and a
|
|
2323
|
+
* chart of that data reading in a different one is a bug nobody would think
|
|
2324
|
+
* to look for; `charting.locale` overrides when they really should differ.
|
|
2325
|
+
* Currency has no such default - only the app knows what the numbers are. */
|
|
2326
|
+
const applyChartLocale = (spec: ChartSpec) => {
|
|
2327
|
+
const locale = chartCfg?.locale ?? props.localization?.locale;
|
|
2328
|
+
if (locale) spec.locale = locale;
|
|
2329
|
+
if (chartCfg?.currency) spec.currency = chartCfg.currency;
|
|
2330
|
+
};
|
|
2331
|
+
|
|
2269
2332
|
const chartSpec = $derived.by<ChartSpec | null>(() => {
|
|
2270
2333
|
if (!chartingEnabled || !chartCfg) return null;
|
|
2271
2334
|
if (chartCfg.getAggregate) {
|
|
@@ -2277,6 +2340,7 @@ export function createSvGridController<
|
|
|
2277
2340
|
if (effectiveChartTimeAxis) spec.xType = "time";
|
|
2278
2341
|
if (effectiveChartLogScale) spec.yScale = "log";
|
|
2279
2342
|
if (effectiveChartValueFormat) spec.valueFormat = effectiveChartValueFormat;
|
|
2343
|
+
applyChartLocale(spec);
|
|
2280
2344
|
return spec;
|
|
2281
2345
|
}
|
|
2282
2346
|
if (chartCfg.buildSpec) return chartCfg.buildSpec(chartRows) ?? null;
|
|
@@ -2287,31 +2351,65 @@ export function createSvGridController<
|
|
|
2287
2351
|
// Engine not loaded yet (lazy): render nothing until it resolves.
|
|
2288
2352
|
if (!chartEngine) return null;
|
|
2289
2353
|
const seriesField = fieldOf(effectiveChartSeriesId);
|
|
2354
|
+
const rowsForChart = chartRows as Array<Record<string, unknown>>;
|
|
2355
|
+
|
|
2356
|
+
// Some types read the rows directly rather than a grouped grid: a scatter
|
|
2357
|
+
// point is one row, a gauge has no category axis at all, and a box plot
|
|
2358
|
+
// needs the whole sample per group rather than one reduced number. The
|
|
2359
|
+
// engine owns that dispatch - naming each type here would put them in the
|
|
2360
|
+
// base bundle, which every grid pays for whether or not it ever charts.
|
|
2361
|
+
// Everything else falls through to the aggregation below and is reshaped
|
|
2362
|
+
// afterwards, so reduce / sort / topN / "Other" keep working for all of it.
|
|
2363
|
+
const direct = chartEngine.rowsToDirectSpec(chartType, rowsForChart, {
|
|
2364
|
+
category: category as string,
|
|
2365
|
+
...(values[0] ? { value: values[0] } : {}),
|
|
2366
|
+
...(fieldOf(effectiveChartMeasure2Id) ? { value2: fieldOf(effectiveChartMeasure2Id)! } : {}),
|
|
2367
|
+
...(seriesField ? { series: seriesField } : {}),
|
|
2368
|
+
reduce: chartReduce,
|
|
2369
|
+
...(effectiveChartPalette ? { palette: effectiveChartPalette } : {}),
|
|
2370
|
+
});
|
|
2371
|
+
if (direct) return direct;
|
|
2372
|
+
// A direct type that could not build (scatter with no Y picked yet, for
|
|
2373
|
+
// instance) must not fall through to the bar-chart path and draw the wrong
|
|
2374
|
+
// chart - it renders the panel's empty state instead.
|
|
2375
|
+
if (chartType === "scatter" || chartType === "gauge" || chartType === "boxplot") return null;
|
|
2290
2376
|
|
|
2291
|
-
const spec = chartEngine<Record<string, unknown>>(
|
|
2377
|
+
const spec = chartEngine.rowsToChartSpec<Record<string, unknown>>(rowsForChart, {
|
|
2292
2378
|
type: chartType,
|
|
2293
2379
|
category: category as string,
|
|
2294
2380
|
value: values.length === 1 ? values[0]! : values,
|
|
2295
2381
|
...(seriesField ? { series: seriesField } : {}),
|
|
2296
2382
|
reduce: chartReduce,
|
|
2297
|
-
stacked: effectiveChartStacked ||
|
|
2298
|
-
stacked100:
|
|
2299
|
-
...(
|
|
2383
|
+
stacked: effectiveChartStacked || effectiveChartStacked100,
|
|
2384
|
+
stacked100: effectiveChartStacked100,
|
|
2385
|
+
...(effectiveChartPalette ? { palette: effectiveChartPalette } : {}),
|
|
2300
2386
|
...(chartCfg.topN ? { topN: chartCfg.topN } : {}),
|
|
2301
2387
|
...(chartCfg.otherLabel ? { otherLabel: chartCfg.otherLabel } : {}),
|
|
2302
2388
|
...(chartCfg.sort ? { sort: chartCfg.sort } : {}),
|
|
2303
2389
|
});
|
|
2304
2390
|
|
|
2305
|
-
|
|
2391
|
+
// Reshape the aggregated grid into whatever this type actually reads.
|
|
2392
|
+
// These run AFTER the aggregation so grouping, reduce, sort, topN and the
|
|
2393
|
+
// "Other" bucket apply to them exactly as they do to a bar chart.
|
|
2394
|
+
if (chartType === "treemap") spec.treemap = chartEngine.specToTreemap(spec);
|
|
2395
|
+
else if (chartType === "calendar") spec.calendarValues = chartEngine.specToCalendar(spec);
|
|
2396
|
+
else if (chartType === "sankey") {
|
|
2397
|
+
const flow = chartEngine.specToSankey(spec);
|
|
2398
|
+
spec.sankeyNodes = flow.nodes;
|
|
2399
|
+
spec.sankeyLinks = flow.links;
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
if (effectiveChartOrientation === "horizontal") spec.orientation = "horizontal";
|
|
2306
2403
|
if (effectiveChartTimeAxis) spec.xType = "time";
|
|
2307
2404
|
if (effectiveChartLogScale) spec.yScale = "log";
|
|
2308
2405
|
if (effectiveChartValueFormat) spec.valueFormat = effectiveChartValueFormat;
|
|
2406
|
+
applyChartLocale(spec);
|
|
2309
2407
|
if (chartType !== "pie" && spec.orientation !== "horizontal" && !spec.yAxisTitle && chartAutoYAxisTitle) {
|
|
2310
2408
|
spec.yAxisTitle = chartAutoYAxisTitle;
|
|
2311
2409
|
}
|
|
2312
2410
|
if (effectiveChartValueFormat && activeChart.valueFormat === null) { /* inherited default */ }
|
|
2313
2411
|
if (chartType === "pie") {
|
|
2314
|
-
if (
|
|
2412
|
+
if (effectiveChartDonut) spec.innerRadius = typeof chartCfg.donut === "number" ? chartCfg.donut : 0.6;
|
|
2315
2413
|
}
|
|
2316
2414
|
if (chartCfg.annotations) spec.annotations = chartCfg.annotations;
|
|
2317
2415
|
if (chartCfg.patternFallback) spec.patternFallback = true;
|
|
@@ -2331,7 +2429,20 @@ export function createSvGridController<
|
|
|
2331
2429
|
return spec;
|
|
2332
2430
|
});
|
|
2333
2431
|
|
|
2432
|
+
/**
|
|
2433
|
+
* Can a click on this chart be turned back into a grid filter?
|
|
2434
|
+
*
|
|
2435
|
+
* Only where a clicked mark maps to exactly one value of the charted
|
|
2436
|
+
* dimension. It does not for a sankey (clicking a TARGET would filter the
|
|
2437
|
+
* SOURCE column by a target value and empty the grid), a gauge (no category
|
|
2438
|
+
* at all), or a scatter (the mark carries a point label, not a group).
|
|
2439
|
+
*/
|
|
2440
|
+
const chartCrossFilterable = $derived(
|
|
2441
|
+
chartType !== "sankey" && chartType !== "gauge" && chartType !== "scatter",
|
|
2442
|
+
);
|
|
2443
|
+
|
|
2334
2444
|
function applyChartCrossFilter(category: string) {
|
|
2445
|
+
if (!chartCrossFilterable) return;
|
|
2335
2446
|
const dimId = effectiveChartDimensionId;
|
|
2336
2447
|
if (!dimId) return;
|
|
2337
2448
|
const existing = valueFilters[dimId];
|
|
@@ -3937,6 +4048,11 @@ export function createSvGridController<
|
|
|
3937
4048
|
dimension: c.dimensionId,
|
|
3938
4049
|
series: c.seriesId ?? null,
|
|
3939
4050
|
measure: c.measureId,
|
|
4051
|
+
measure2: c.measure2Id,
|
|
4052
|
+
orientation: c.orientation,
|
|
4053
|
+
stacked100: c.stacked100,
|
|
4054
|
+
donut: c.donut,
|
|
4055
|
+
palette: c.palette,
|
|
3940
4056
|
reduce: c.reduce,
|
|
3941
4057
|
stacked: c.stacked,
|
|
3942
4058
|
dataLabels: c.dataLabels,
|
|
@@ -3954,6 +4070,11 @@ export function createSvGridController<
|
|
|
3954
4070
|
dimensionId: (c.dimension as string | null) ?? null,
|
|
3955
4071
|
measureId: (c.measure as string | null) ?? null,
|
|
3956
4072
|
seriesId: c.series as string | null | undefined,
|
|
4073
|
+
measure2Id: (c.measure2 as string | null) ?? null,
|
|
4074
|
+
orientation: (c.orientation as "vertical" | "horizontal" | null) ?? null,
|
|
4075
|
+
stacked100: (c.stacked100 as boolean | null) ?? null,
|
|
4076
|
+
donut: (c.donut as boolean | null) ?? null,
|
|
4077
|
+
palette: (c.palette as string[] | null) ?? null,
|
|
3957
4078
|
stacked: (c.stacked as boolean | null) ?? null,
|
|
3958
4079
|
dataLabels: (c.dataLabels as boolean | null) ?? null,
|
|
3959
4080
|
logScale: (c.logScale as boolean | null) ?? null,
|
|
@@ -3963,6 +4084,18 @@ export function createSvGridController<
|
|
|
3963
4084
|
charts = restored.length ? restored : [makeChart("Chart 1")];
|
|
3964
4085
|
activeChartIndex = Math.max(0, Math.min(charts.length - 1, active ?? 0));
|
|
3965
4086
|
},
|
|
4087
|
+
get chartCrossFilterable() { return chartCrossFilterable; },
|
|
4088
|
+
get chartOrientation() { return effectiveChartOrientation; },
|
|
4089
|
+
set chartOrientation(v) { activeChart.orientation = v as never; },
|
|
4090
|
+
get chartStacked100() { return effectiveChartStacked100; },
|
|
4091
|
+
set chartStacked100(v) { activeChart.stacked100 = v as never; },
|
|
4092
|
+
get chartDonut() { return effectiveChartDonut; },
|
|
4093
|
+
set chartDonut(v) { activeChart.donut = v as never; },
|
|
4094
|
+
get chartPalette() { return effectiveChartPalette; },
|
|
4095
|
+
set chartPalette(v) { activeChart.palette = v as never; },
|
|
4096
|
+
get chartMeasure2Id() { return activeChart.measure2Id; },
|
|
4097
|
+
set chartMeasure2Id(v) { activeChart.measure2Id = v as never; },
|
|
4098
|
+
get effectiveChartMeasure2Id() { return effectiveChartMeasure2Id; },
|
|
3966
4099
|
get chartAiHandler() { return chartAiHandler; },
|
|
3967
4100
|
set chartAiHandler(v) { chartAiHandler = v as never; },
|
|
3968
4101
|
applyChartConfig(config: {
|
package/src/SvGrid.css
CHANGED
|
@@ -153,6 +153,10 @@
|
|
|
153
153
|
/* Advanced-filter indicator. Reads as state rather than as another action, so
|
|
154
154
|
it uses the accent fill the toolbar buttons reserve for `is-active` instead
|
|
155
155
|
of the neutral button shell. */
|
|
156
|
+
.sv-grid-advf-chip > .sv-grid-icon {
|
|
157
|
+
width: 13px;
|
|
158
|
+
height: 13px;
|
|
159
|
+
}
|
|
156
160
|
.sv-grid-advf-chip {
|
|
157
161
|
display: inline-flex;
|
|
158
162
|
align-items: center;
|
|
@@ -261,6 +265,12 @@
|
|
|
261
265
|
flex-shrink: 0;
|
|
262
266
|
}
|
|
263
267
|
.sv-grid-tool-panel-btn {
|
|
268
|
+
/* Centred as flex because these draw an icon now, not a bare character: a
|
|
269
|
+
glyph default still centres, and an SVG override no longer sits on the
|
|
270
|
+
text baseline. */
|
|
271
|
+
display: inline-flex;
|
|
272
|
+
align-items: center;
|
|
273
|
+
justify-content: center;
|
|
264
274
|
width: 22px;
|
|
265
275
|
height: 22px;
|
|
266
276
|
border: 0;
|
|
@@ -807,9 +817,18 @@
|
|
|
807
817
|
color: var(--sg-accent, #2563eb);
|
|
808
818
|
}
|
|
809
819
|
.sv-grid-group-caret {
|
|
820
|
+
display: inline-flex;
|
|
821
|
+
align-items: center;
|
|
822
|
+
justify-content: center;
|
|
823
|
+
width: 10px;
|
|
824
|
+
height: 10px;
|
|
810
825
|
flex-shrink: 0;
|
|
811
826
|
transition: transform 140ms ease;
|
|
812
827
|
}
|
|
828
|
+
.sv-grid-group-caret > .sv-grid-icon {
|
|
829
|
+
width: 100%;
|
|
830
|
+
height: 100%;
|
|
831
|
+
}
|
|
813
832
|
/* Collapsed: point the caret right (like a closed disclosure). */
|
|
814
833
|
.sv-grid-group-toggle.is-collapsed .sv-grid-group-caret {
|
|
815
834
|
transform: rotate(-90deg);
|
|
@@ -1855,6 +1874,23 @@ select.sv-grid-fr-editor {
|
|
|
1855
1874
|
display: block;
|
|
1856
1875
|
flex: none;
|
|
1857
1876
|
}
|
|
1877
|
+
/* A consumer icon from the `icons` prop. SvGrid wraps the override in this box
|
|
1878
|
+
rather than asking the consumer to add `.sv-grid-icon` themselves, so it
|
|
1879
|
+
inherits every per-site size rule below and the expander's rotate-on-open
|
|
1880
|
+
without needing to know those rules exist. Must stay AFTER .sv-grid-icon:
|
|
1881
|
+
equal specificity, and this display has to win. */
|
|
1882
|
+
.sv-grid-icon-custom {
|
|
1883
|
+
display: inline-flex;
|
|
1884
|
+
align-items: center;
|
|
1885
|
+
justify-content: center;
|
|
1886
|
+
line-height: 0;
|
|
1887
|
+
}
|
|
1888
|
+
.sv-grid-icon-custom > svg,
|
|
1889
|
+
.sv-grid-icon-custom > img {
|
|
1890
|
+
width: 100%;
|
|
1891
|
+
height: 100%;
|
|
1892
|
+
display: block;
|
|
1893
|
+
}
|
|
1858
1894
|
|
|
1859
1895
|
.sv-grid-header-cell {
|
|
1860
1896
|
display: flex;
|
|
@@ -2453,14 +2489,19 @@ select.sv-grid-fr-editor {
|
|
|
2453
2489
|
group banners and auto-group columns, SvGroupCell and SvTree all draw the
|
|
2454
2490
|
same `chevron-right` and rotate it open, rather than swapping between two
|
|
2455
2491
|
different glyphs. */
|
|
2456
|
-
.
|
|
2457
|
-
|
|
2492
|
+
/* Direct child, not descendant. The column-group header button also carries
|
|
2493
|
+
`sv-grid-group-toggle` + `aria-expanded`, and its caret is wrapped one level
|
|
2494
|
+
deeper - a descendant selector would rotate that caret 90deg too, so an
|
|
2495
|
+
expanded down-caret would point left. The three row/tree expanders render
|
|
2496
|
+
their icon as an immediate child, so `>` changes nothing for them. */
|
|
2497
|
+
.sv-grid-tree-toggle > .sv-grid-icon,
|
|
2498
|
+
.sv-grid-group-toggle > .sv-grid-icon {
|
|
2458
2499
|
width: 12px;
|
|
2459
2500
|
height: 12px;
|
|
2460
2501
|
transition: transform 0.15s ease;
|
|
2461
2502
|
}
|
|
2462
|
-
.sv-grid-tree-toggle[aria-expanded="true"] .sv-grid-icon,
|
|
2463
|
-
.sv-grid-group-toggle[aria-expanded="true"] .sv-grid-icon {
|
|
2503
|
+
.sv-grid-tree-toggle[aria-expanded="true"] > .sv-grid-icon,
|
|
2504
|
+
.sv-grid-group-toggle[aria-expanded="true"] > .sv-grid-icon {
|
|
2464
2505
|
transform: rotate(90deg);
|
|
2465
2506
|
}
|
|
2466
2507
|
.sv-grid-tree-toggle:focus-visible {
|
|
@@ -2624,7 +2665,7 @@ select.sv-grid-fr-editor {
|
|
|
2624
2665
|
box-shadow: 0 10px 25px rgba(15, 23, 42, 0.18);
|
|
2625
2666
|
min-width: 280px;
|
|
2626
2667
|
}
|
|
2627
|
-
.sv-grid-find-icon {
|
|
2668
|
+
.sv-grid-find > .sv-grid-icon {
|
|
2628
2669
|
width: 14px; height: 14px;
|
|
2629
2670
|
color: var(--sg-muted, #94a3b8);
|
|
2630
2671
|
flex: none;
|
|
@@ -2868,13 +2909,26 @@ select.sv-grid-fr-editor {
|
|
|
2868
2909
|
opacity: 1;
|
|
2869
2910
|
}
|
|
2870
2911
|
|
|
2871
|
-
/* Magnifier prefix on the menu's search input.
|
|
2912
|
+
/* Magnifier prefix on the menu's search input. Real markup rather than a
|
|
2913
|
+
background-image, so it follows the theme and the `icons` prop can replace
|
|
2914
|
+
it; the input keeps its own class because focusFilterPanel() and a test both
|
|
2915
|
+
resolve `.sv-grid-menu-search`. */
|
|
2872
2916
|
.sv-grid-menu-search {
|
|
2873
2917
|
padding-left: 28px;
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2918
|
+
}
|
|
2919
|
+
.sv-grid-menu-search-wrap {
|
|
2920
|
+
position: relative;
|
|
2921
|
+
display: block;
|
|
2922
|
+
}
|
|
2923
|
+
.sv-grid-menu-search-wrap > .sv-grid-icon {
|
|
2924
|
+
position: absolute;
|
|
2925
|
+
left: 8px;
|
|
2926
|
+
top: 50%;
|
|
2927
|
+
transform: translateY(-50%);
|
|
2928
|
+
width: 14px;
|
|
2929
|
+
height: 14px;
|
|
2930
|
+
color: var(--sg-muted, #94a3b8);
|
|
2931
|
+
pointer-events: none;
|
|
2878
2932
|
}
|
|
2879
2933
|
|
|
2880
2934
|
/* Filter menu vertical rhythm. */
|
|
@@ -2910,6 +2964,11 @@ select.sv-grid-fr-editor {
|
|
|
2910
2964
|
opacity: 1;
|
|
2911
2965
|
}
|
|
2912
2966
|
|
|
2967
|
+
.sv-grid-board-search > .sv-grid-icon {
|
|
2968
|
+
width: 14px;
|
|
2969
|
+
height: 14px;
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2913
2972
|
/* Kanban board mode search box (board.searchable) */
|
|
2914
2973
|
.sv-grid-board-search {
|
|
2915
2974
|
display: flex;
|
|
@@ -3048,6 +3107,16 @@ select.sv-grid-fr-editor {
|
|
|
3048
3107
|
.sv-grid-chart-actions { display: inline-flex; align-items: center; gap: 6px; }
|
|
3049
3108
|
.sv-grid-chart-controls { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; }
|
|
3050
3109
|
.sv-grid-chart-controls:empty { display: none; }
|
|
3110
|
+
/* Says why the chosen chart type cannot draw yet - a missing Split by, too few
|
|
3111
|
+
groups. The picker deliberately does NOT hide such a type: options that come
|
|
3112
|
+
and go as the user filters are worse than one sentence of explanation. */
|
|
3113
|
+
.sv-grid-chart-hint {
|
|
3114
|
+
flex-basis: 100%;
|
|
3115
|
+
margin: 2px 0 0;
|
|
3116
|
+
font-size: 11.5px;
|
|
3117
|
+
font-weight: 400;
|
|
3118
|
+
color: var(--sg-muted, #64748b);
|
|
3119
|
+
}
|
|
3051
3120
|
.sv-grid-chart-ctl { display: flex; flex-direction: column; gap: 2px; font-size: 11px; font-weight: 600; color: var(--sg-muted, #64748b); }
|
|
3052
3121
|
.sv-grid-chart-ctl select {
|
|
3053
3122
|
font: inherit; font-size: 12.5px; font-weight: 400; color: var(--sg-fg, #0f172a);
|