@svgrid/grid 3.0.1 → 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/dist/GridFooter.svelte +14 -5
- package/dist/GridFooter.svelte.d.ts +8 -0
- package/dist/GridMenus.svelte +18 -10
- package/dist/GridMenus.svelte.d.ts +2 -1
- 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/SvGridChart.svelte +571 -41
- package/dist/SvGridChart.svelte.d.ts +63 -1
- package/dist/SvGridChartPanel.svelte +169 -21
- package/dist/SvGridChartView.svelte +9 -0
- package/dist/SvGridChartView.svelte.d.ts +7 -0
- package/dist/SvGroupCell.svelte +28 -3
- package/dist/SvGroupCell.svelte.d.ts +8 -0
- package/dist/SvRowGroupPanel.svelte +20 -4
- package/dist/SvRowGroupPanel.svelte.d.ts +8 -0
- 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-DFB6iwF8.js → GridMenus-M2XP9t0_.js} +152 -150
- 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-BhUEJ5ki.js → SvGridChartView-CxfakxR0.js} +5 -3
- package/dist/cdn/{SvGridChartView-00LPUHL1.js → SvGridChartView-Dm_YEx9L.js} +5 -3
- package/dist/cdn/cell-formatting-C2Hf5gqd.js +86 -0
- package/dist/cdn/chart-CKJRCPOk.js +1652 -0
- package/dist/cdn/{export-format-Cv1Dll6k.js → export-format-CUDLy2yn.js} +84 -168
- package/dist/cdn/{src-C8QjSaq-.js → src-BJ2opd0j.js} +3608 -3414
- package/dist/cdn/{src-D0hO2eYN.js → src-DNbz94Ct.js} +8617 -8423
- 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 +80 -6
- package/dist/chart.d.ts +410 -18
- package/dist/chart.js +824 -24
- 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 +11 -11
- package/src/GridFooter.svelte +14 -5
- package/src/GridMenus.svelte +18 -10
- 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 +42 -1
- package/src/SvGridChart.svelte +571 -41
- package/src/SvGridChart.test.ts +243 -1
- package/src/SvGridChartPanel.svelte +169 -21
- package/src/SvGridChartView.svelte +9 -0
- package/src/SvGroupCell.svelte +28 -3
- package/src/SvRowGroupPanel.svelte +20 -4
- package/src/ai.test.ts +60 -1
- package/src/ai.ts +59 -6
- package/src/cell-formatting.ts +7 -1
- package/src/chart-export.test.ts +112 -0
- package/src/chart-export.ts +76 -8
- package/src/chart.test.ts +830 -1
- package/src/chart.ts +1053 -32
- package/src/grid-icons.test.ts +46 -0
- package/src/grid-icons.ts +184 -0
- 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/svgrid.charting.test.ts +120 -0
- package/dist/cdn/GridMenus-CYzOvmNQ.js +0 -600
- package/dist/cdn/SvGridChart-BEJmNNx9.js +0 -1203
- package/dist/cdn/SvGridChart-BwVos976.js +0 -1202
- package/dist/cdn/SvGridChartPanel-BOknOkrP.js +0 -561
- package/dist/cdn/SvGridChartPanel-D2PjII4O.js +0 -585
- package/dist/cdn/chart-i4XcXyHJ.js +0 -1214
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grid chrome icons. Every glyph `<SvGrid>` draws for its own UI - sort
|
|
3
|
+
* indicators, the filter funnel, expanders, the pager arrows, tool-panel
|
|
4
|
+
* buttons - has a name here, and the `icons` prop replaces any subset of them.
|
|
5
|
+
*
|
|
6
|
+
* This mirrors `grid-messages.ts`: a name-keyed map with built-in defaults,
|
|
7
|
+
* where an unset name keeps the default. A partial map is all a consumer ever
|
|
8
|
+
* needs, so `icons` is safe to pass without knowing the full catalogue. That
|
|
9
|
+
* matters more than it looks - a single catch-all snippet would make the
|
|
10
|
+
* consumer responsible for every name the grid can ask for, and missing one
|
|
11
|
+
* renders an empty box. The `op-between` comment in SvGrid.svelte records what
|
|
12
|
+
* that looks like when it ships.
|
|
13
|
+
*
|
|
14
|
+
* Deliberately NOT in here:
|
|
15
|
+
* - the scrollbar stepper arrows. `sv-grid-scrollbar.ts` is a custom element
|
|
16
|
+
* that builds its SVG with `createElementNS` inside a shadow root, on
|
|
17
|
+
* purpose, so it stays compatible with strict CSP / Trusted Types. A Svelte
|
|
18
|
+
* snippet cannot render there. Theme it with the `--sg-scrollbar-*` tokens.
|
|
19
|
+
* - the checkbox tick and its indeterminate dash. Both are `::after`
|
|
20
|
+
* pseudo-elements drawn from borders (SvGrid.css), a form-control
|
|
21
|
+
* affordance rather than an icon, and they render once per visible row.
|
|
22
|
+
* - conditional formatting's `iconSet`. Those are value-driven data glyphs a
|
|
23
|
+
* consumer already chooses; a different concept that shares the word.
|
|
24
|
+
*/
|
|
25
|
+
import type { Snippet } from 'svelte';
|
|
26
|
+
/**
|
|
27
|
+
* Every icon `<SvGrid>` draws for its own chrome. Pass any subset of these to
|
|
28
|
+
* the `icons` prop to swap the built-in glyph for your own markup.
|
|
29
|
+
*/
|
|
30
|
+
export type GridIconName = 'sort' | 'sort-asc' | 'sort-desc' | 'filter' | 'menu' | 'group' | 'x' | 'chevron-down' | 'chevron-right' | 'autosize' | 'columns' | 'reset' | 'op-contains' | 'op-notContains' | 'op-equals' | 'op-notEquals' | 'op-startsWith' | 'op-endsWith' | 'op-regex' | 'op-in' | 'op-notIn' | 'op-greaterThan' | 'op-lessThan' | 'op-between' | 'op-isBlank' | 'op-isNotBlank' | 'pin-left' | 'pin-right' | 'unpin' | 'search' | 'tool-panel' | 'chart' | 'advanced-filter' | 'column-group-caret' | 'close' | 'clear' | 'remove' | 'row-number' | 'pinned-row-top' | 'pinned-row-bottom' | 'find-prev' | 'find-next' | 'move-up' | 'move-down' | 'group-add' | 'page-first' | 'page-prev' | 'page-next' | 'page-last' | 'page-size-caret' | 'drag-handle' | 'breadcrumb-separator';
|
|
31
|
+
/**
|
|
32
|
+
* Consumer icon overrides, keyed by name. Any subset; names you leave out keep
|
|
33
|
+
* their built-in glyph, so `icons` never has to be exhaustive.
|
|
34
|
+
*/
|
|
35
|
+
export type GridIcons = Partial<Record<GridIconName, Snippet>>;
|
|
36
|
+
/**
|
|
37
|
+
* Icons whose built-in form is a character rather than an SVG path. Kept as
|
|
38
|
+
* data so `<SvGrid>`, its footer and the standalone `SvRowGroupPanel` render
|
|
39
|
+
* the same defaults instead of three copies drifting apart.
|
|
40
|
+
*
|
|
41
|
+
* Each is the exact character that shipped inline before, so a grid that sets
|
|
42
|
+
* no `icons` renders byte-for-byte what it always did.
|
|
43
|
+
*/
|
|
44
|
+
export declare const GRID_ICON_GLYPHS: Partial<Record<GridIconName, string>>;
|
|
45
|
+
/**
|
|
46
|
+
* Every icon name, in catalogue order. Exported for tests and docs generation;
|
|
47
|
+
* the grid itself never reads it, so it costs a consumer's bundle nothing.
|
|
48
|
+
*/
|
|
49
|
+
export declare const GRID_ICON_NAMES: ReadonlyArray<GridIconName>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icons whose built-in form is a character rather than an SVG path. Kept as
|
|
3
|
+
* data so `<SvGrid>`, its footer and the standalone `SvRowGroupPanel` render
|
|
4
|
+
* the same defaults instead of three copies drifting apart.
|
|
5
|
+
*
|
|
6
|
+
* Each is the exact character that shipped inline before, so a grid that sets
|
|
7
|
+
* no `icons` renders byte-for-byte what it always did.
|
|
8
|
+
*/
|
|
9
|
+
export const GRID_ICON_GLYPHS = {
|
|
10
|
+
close: '✕', // multiplication X
|
|
11
|
+
clear: '✕',
|
|
12
|
+
remove: '×', // multiplication sign
|
|
13
|
+
'row-number': '#',
|
|
14
|
+
'pinned-row-top': '↑', // upwards arrow
|
|
15
|
+
'pinned-row-bottom': '↓', // downwards arrow
|
|
16
|
+
'find-prev': '↑',
|
|
17
|
+
'find-next': '↓',
|
|
18
|
+
'move-up': '↑',
|
|
19
|
+
'move-down': '↓',
|
|
20
|
+
'group-add': '⊞', // squared plus
|
|
21
|
+
'page-first': '⇤', // leftwards arrow to bar
|
|
22
|
+
'page-prev': '‹', // single left-pointing angle quote
|
|
23
|
+
'page-next': '›', // single right-pointing angle quote
|
|
24
|
+
'page-last': '⇥', // rightwards arrow to bar
|
|
25
|
+
'page-size-caret': '▾', // black down-pointing small triangle
|
|
26
|
+
'drag-handle': '⠿', // braille pattern dots-123456, the grip
|
|
27
|
+
'breadcrumb-separator': '›',
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Every icon name, in catalogue order. Exported for tests and docs generation;
|
|
31
|
+
* the grid itself never reads it, so it costs a consumer's bundle nothing.
|
|
32
|
+
*/
|
|
33
|
+
export const GRID_ICON_NAMES = [
|
|
34
|
+
'sort',
|
|
35
|
+
'sort-asc',
|
|
36
|
+
'sort-desc',
|
|
37
|
+
'filter',
|
|
38
|
+
'menu',
|
|
39
|
+
'group',
|
|
40
|
+
'x',
|
|
41
|
+
'chevron-down',
|
|
42
|
+
'chevron-right',
|
|
43
|
+
'autosize',
|
|
44
|
+
'columns',
|
|
45
|
+
'reset',
|
|
46
|
+
'op-contains',
|
|
47
|
+
'op-notContains',
|
|
48
|
+
'op-equals',
|
|
49
|
+
'op-notEquals',
|
|
50
|
+
'op-startsWith',
|
|
51
|
+
'op-endsWith',
|
|
52
|
+
'op-regex',
|
|
53
|
+
'op-in',
|
|
54
|
+
'op-notIn',
|
|
55
|
+
'op-greaterThan',
|
|
56
|
+
'op-lessThan',
|
|
57
|
+
'op-between',
|
|
58
|
+
'op-isBlank',
|
|
59
|
+
'op-isNotBlank',
|
|
60
|
+
'pin-left',
|
|
61
|
+
'pin-right',
|
|
62
|
+
'unpin',
|
|
63
|
+
'search',
|
|
64
|
+
'tool-panel',
|
|
65
|
+
'chart',
|
|
66
|
+
'advanced-filter',
|
|
67
|
+
'column-group-caret',
|
|
68
|
+
'close',
|
|
69
|
+
'clear',
|
|
70
|
+
'remove',
|
|
71
|
+
'row-number',
|
|
72
|
+
'pinned-row-top',
|
|
73
|
+
'pinned-row-bottom',
|
|
74
|
+
'find-prev',
|
|
75
|
+
'find-next',
|
|
76
|
+
'move-up',
|
|
77
|
+
'move-down',
|
|
78
|
+
'group-add',
|
|
79
|
+
'page-first',
|
|
80
|
+
'page-prev',
|
|
81
|
+
'page-next',
|
|
82
|
+
'page-last',
|
|
83
|
+
'page-size-caret',
|
|
84
|
+
'drag-handle',
|
|
85
|
+
'breadcrumb-separator',
|
|
86
|
+
];
|
package/dist/index.d.ts
CHANGED
|
@@ -193,8 +193,8 @@ export { default as SvChart } from './SvGridChart.svelte';
|
|
|
193
193
|
export { default as SvChartPanel } from './SvGridChartPanel.svelte';
|
|
194
194
|
export type { ChartingConfig, ChartAggregateRequest, ChartAggregateBucket } from './SvGrid.types.js';
|
|
195
195
|
export type { GridLocalization } from './SvGrid.types.js';
|
|
196
|
-
export { buildChart, formatChartValue, rowsToChartSpec, niceScale, niceLogScale, linearTrend, simpleMovingAverage, exponentialMovingAverage, computeOverlay, buildLinePath, sampleGradient, pickContrastText, DEFAULT_PALETTE, type ChartType, type ChartSpec, type ChartSeries, type ChartGeometry, type ChartBar, type ChartLine, type ChartLinePoint, type ChartPieSlice, type ChartSelection, type ChartReferenceLine, type ChartRefLineGeo, type ChartRefLineGeoV, type ChartScatterDot, type ScatterPoint, type NiceScale, type SeriesOverlay, type SeriesPattern, type ChartAnnotation, type ChartHeatmapCell, type ChartFunnelSegment, type ChartRadarSeries, type ChartRadarAxis, type ChartTreemapCell, type ChartCalendarCell, type ChartGaugeLayout, type ChartSankeyNode, type ChartSankeyLink, type ChartValueFormat, type TreeNode, } from './chart.js';
|
|
197
|
-
export { chartToSvgString, downloadChartSvg, chartToPngBlob, downloadChartPng, chartSpecToCsv, downloadChartCsv, type ChartExportOptions, } from './chart-export.js';
|
|
196
|
+
export { buildChart, formatChartValue, rowsToChartSpec, rowsToBoxSpec, rowsToDirectSpec, boxStats, sliceChartWindow, chartScales, ordinalDateTicks, niceScale, niceLogScale, linearTrend, simpleMovingAverage, exponentialMovingAverage, computeOverlay, buildLinePath, sampleGradient, pickContrastText, DEFAULT_PALETTE, type ChartType, type ChartSpec, type ChartSeries, type ChartGeometry, type ChartScales, type ChartBar, type ChartCandle, type ChartBox, type ChartErrorBar, type BoxStats, type OhlcBar, type ChartLine, type ChartLinePoint, type ChartPieSlice, type ChartSelection, type ChartReferenceLine, type ChartRefLineGeo, type ChartRefLineGeoV, type ChartScatterDot, type ScatterPoint, type NiceScale, type SeriesOverlay, type SeriesPattern, type ChartAnnotation, type ChartHeatmapCell, type ChartFunnelSegment, type ChartRadarSeries, type ChartRadarAxis, type ChartTreemapCell, type ChartCalendarCell, type ChartGaugeLayout, type ChartSankeyNode, type ChartSankeyLink, type ChartValueFormat, type TreeNode, } from './chart.js';
|
|
197
|
+
export { chartToSvgString, downloadChartSvg, chartToPngBlob, downloadChartPng, chartCsvExportable, chartSpecToCsv, downloadChartCsv, type ChartExportOptions, } from './chart-export.js';
|
|
198
198
|
export { buildSparkline, toSparklineValues, type SparklineConfig, type SparklineType, type SparklineGeometry, } from './sparkline.js';
|
|
199
199
|
export { spreadsheetLayout, spansToMerges, type SpanColumn, type SpreadsheetActionOptions, type MergeSpec, type CellBorderSpec, type BorderSpec, } from './spreadsheet.js';
|
|
200
200
|
export { rowResize, type RowResizeOptions } from './row-resize.js';
|
|
@@ -211,6 +211,7 @@ export { default as SvRowGroupPanel } from './SvRowGroupPanel.svelte';
|
|
|
211
211
|
export { createNamedViews, memoryViews, localStorageViews, attachAutoSavedView, type SavedView, type ViewStorage, type NamedViews, type AutoSavedViewOptions, } from './named-views.js';
|
|
212
212
|
export { resolveCellFormat, computeColumnStat, lerpColor, rgba, contrastText, formatNeedsStats, formatsNeedingStats, type ConditionalFormat, type ConditionalFormatSpec, type ColorScaleFormat, type ColorScaleStop, type ScaleBounds, type DataBarFormat, type IconSetFormat, type RuleFormat, type ResolvedCellFormat, } from './conditional-formatting.js';
|
|
213
213
|
export { defaultGridMessages, resolveGridMessages, type GridMessages, } from './grid-messages.js';
|
|
214
|
+
export { GRID_ICON_GLYPHS, GRID_ICON_NAMES, type GridIconName, type GridIcons, } from './grid-icons.js';
|
|
214
215
|
export { insertGroupFooters, buildAutoGroupColumns, paginateGroupedRows, type GroupDisplayType, type GroupFooterOptions, type AutoGroupColumnSpec, type AutoGroupResult, type GroupPaginationOptions, type GroupPaginationResult, } from './group-display.js';
|
|
215
216
|
export { getKeyboardIntent, getNextActiveCell, type GridKeyboardIntent } from './keyboard.js';
|
|
216
217
|
export { createVirtualizer } from './virtualization/virtualizer.js';
|
package/dist/index.js
CHANGED
|
@@ -230,8 +230,8 @@ export { default as SvGridChartPanel } from './SvGridChartPanel.svelte';
|
|
|
230
230
|
// as aliases so existing imports keep working.
|
|
231
231
|
export { default as SvChart } from './SvGridChart.svelte';
|
|
232
232
|
export { default as SvChartPanel } from './SvGridChartPanel.svelte';
|
|
233
|
-
export { buildChart, formatChartValue, rowsToChartSpec, niceScale, niceLogScale, linearTrend, simpleMovingAverage, exponentialMovingAverage, computeOverlay, buildLinePath, sampleGradient, pickContrastText, DEFAULT_PALETTE, } from './chart.js';
|
|
234
|
-
export { chartToSvgString, downloadChartSvg, chartToPngBlob, downloadChartPng, chartSpecToCsv, downloadChartCsv, } from './chart-export.js';
|
|
233
|
+
export { buildChart, formatChartValue, rowsToChartSpec, rowsToBoxSpec, rowsToDirectSpec, boxStats, sliceChartWindow, chartScales, ordinalDateTicks, niceScale, niceLogScale, linearTrend, simpleMovingAverage, exponentialMovingAverage, computeOverlay, buildLinePath, sampleGradient, pickContrastText, DEFAULT_PALETTE, } from './chart.js';
|
|
234
|
+
export { chartToSvgString, downloadChartSvg, chartToPngBlob, downloadChartPng, chartCsvExportable, chartSpecToCsv, downloadChartCsv, } from './chart-export.js';
|
|
235
235
|
export { buildSparkline, toSparklineValues, } from './sparkline.js';
|
|
236
236
|
export { spreadsheetLayout, spansToMerges, } from './spreadsheet.js';
|
|
237
237
|
// The two resize actions, exported as a matched pair. `<SvGrid>` drives both
|
|
@@ -253,6 +253,7 @@ export { default as SvRowGroupPanel } from './SvRowGroupPanel.svelte';
|
|
|
253
253
|
export { createNamedViews, memoryViews, localStorageViews, attachAutoSavedView, } from './named-views.js';
|
|
254
254
|
export { resolveCellFormat, computeColumnStat, lerpColor, rgba, contrastText, formatNeedsStats, formatsNeedingStats, } from './conditional-formatting.js';
|
|
255
255
|
export { defaultGridMessages, resolveGridMessages, } from './grid-messages.js';
|
|
256
|
+
export { GRID_ICON_GLYPHS, GRID_ICON_NAMES, } from './grid-icons.js';
|
|
256
257
|
export { insertGroupFooters, buildAutoGroupColumns, paginateGroupedRows, } from './group-display.js';
|
|
257
258
|
export { getKeyboardIntent, getNextActiveCell } from './keyboard.js';
|
|
258
259
|
export { createVirtualizer } from './virtualization/virtualizer.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svgrid/grid",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "Svelte 5-native data grid and data table. Headless-first engine with virtual scrolling for 100k+ rows, Excel-style filtering, inline editing, grouping, tree data, pivot, and server-side data. Drop-in <SvGrid> component. TypeScript, SSR-ready.",
|
|
5
5
|
"author": "jQWidgets <boikom@jqwidgets.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -116,15 +116,6 @@
|
|
|
116
116
|
"themes",
|
|
117
117
|
"CHANGELOG.md"
|
|
118
118
|
],
|
|
119
|
-
"scripts": {
|
|
120
|
-
"clean": "rimraf ./build && rimraf ./dist",
|
|
121
|
-
"test:eslint": "eslint ./src",
|
|
122
|
-
"test:types": "svelte-check --tsconfig ./tsconfig.json",
|
|
123
|
-
"test:lib": "vitest run --coverage",
|
|
124
|
-
"test:build": "publint --strict",
|
|
125
|
-
"build": "svelte-package --input ./src --output ./dist && node ../../tools/strip-dist-tests.mjs ./dist && node ../../tools/add-dist-extensions.mjs ./dist && node ./scripts/build-cdn.mjs && node ./scripts/build-themes.mjs",
|
|
126
|
-
"lint": "eslint ./src"
|
|
127
|
-
},
|
|
128
119
|
"devDependencies": {
|
|
129
120
|
"@sveltejs/package": "^2.5.7",
|
|
130
121
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
|
@@ -141,5 +132,14 @@
|
|
|
141
132
|
},
|
|
142
133
|
"dependencies": {
|
|
143
134
|
"esm-env": "^1.2.2"
|
|
135
|
+
},
|
|
136
|
+
"scripts": {
|
|
137
|
+
"clean": "rimraf ./build && rimraf ./dist",
|
|
138
|
+
"test:eslint": "eslint ./src",
|
|
139
|
+
"test:types": "svelte-check --tsconfig ./tsconfig.json",
|
|
140
|
+
"test:lib": "vitest run --coverage",
|
|
141
|
+
"test:build": "publint --strict",
|
|
142
|
+
"build": "svelte-package --input ./src --output ./dist && node ../../tools/strip-dist-tests.mjs ./dist && node ../../tools/add-dist-extensions.mjs ./dist && node ./scripts/build-cdn.mjs && node ./scripts/build-themes.mjs",
|
|
143
|
+
"lint": "eslint ./src"
|
|
144
144
|
}
|
|
145
|
-
}
|
|
145
|
+
}
|
package/src/GridFooter.svelte
CHANGED
|
@@ -7,16 +7,25 @@
|
|
|
7
7
|
import {
|
|
8
8
|
fmtStat,
|
|
9
9
|
} from "./SvGrid.helpers";
|
|
10
|
+
import type { Snippet } from "svelte";
|
|
11
|
+
import type { GridIconName } from "./grid-icons";
|
|
10
12
|
import type { SvGridController } from "./SvGrid.controller.svelte";
|
|
11
13
|
|
|
12
14
|
let {
|
|
13
15
|
ctrl,
|
|
16
|
+
icon,
|
|
14
17
|
pager = true,
|
|
15
18
|
showStatus = true,
|
|
16
19
|
top = false,
|
|
17
20
|
pageSizeOptions,
|
|
18
21
|
}: {
|
|
19
22
|
ctrl: SvGridController<TFeatures, TData>;
|
|
23
|
+
/**
|
|
24
|
+
* The grid's icon resolver, so the pager arrows honour the `icons` prop.
|
|
25
|
+
* Required rather than optional: SvGrid is the only thing that mounts this
|
|
26
|
+
* component, so a missing icon would be a bug, not a supported call.
|
|
27
|
+
*/
|
|
28
|
+
icon: Snippet<[GridIconName]>;
|
|
20
29
|
/** Render the pagination controls in this instance. */
|
|
21
30
|
pager?: boolean;
|
|
22
31
|
/** Render the status bar in this instance. */
|
|
@@ -132,7 +141,7 @@
|
|
|
132
141
|
onclick={openPageSize}
|
|
133
142
|
>
|
|
134
143
|
<span class="sv-grid-dropdown-label">{pageSize}</span>
|
|
135
|
-
<span class="sv-grid-dropdown-caret" aria-hidden="true"
|
|
144
|
+
<span class="sv-grid-dropdown-caret" aria-hidden="true">{@render icon("page-size-caret")}</span>
|
|
136
145
|
</button>
|
|
137
146
|
</div>
|
|
138
147
|
{/if}
|
|
@@ -149,14 +158,14 @@
|
|
|
149
158
|
class="sv-grid-pagination-btn"
|
|
150
159
|
disabled={onFirst}
|
|
151
160
|
onclick={() => goToPage(0)}
|
|
152
|
-
aria-label={messages.firstPage}
|
|
161
|
+
aria-label={messages.firstPage}>{@render icon("page-first")}</button
|
|
153
162
|
>
|
|
154
163
|
<button
|
|
155
164
|
type="button"
|
|
156
165
|
class="sv-grid-pagination-btn"
|
|
157
166
|
disabled={onFirst}
|
|
158
167
|
onclick={() => changePage(-1)}
|
|
159
|
-
aria-label={messages.prevPage}
|
|
168
|
+
aria-label={messages.prevPage}>{@render icon("page-prev")}</button
|
|
160
169
|
>
|
|
161
170
|
<span class="sv-grid-pagination-label">
|
|
162
171
|
{messages.page} <strong>{currentPage.toLocaleString()}</strong> {messages.of}
|
|
@@ -167,14 +176,14 @@
|
|
|
167
176
|
class="sv-grid-pagination-btn"
|
|
168
177
|
disabled={onLast}
|
|
169
178
|
onclick={() => changePage(1)}
|
|
170
|
-
aria-label={messages.nextPage}
|
|
179
|
+
aria-label={messages.nextPage}>{@render icon("page-next")}</button
|
|
171
180
|
>
|
|
172
181
|
<button
|
|
173
182
|
type="button"
|
|
174
183
|
class="sv-grid-pagination-btn"
|
|
175
184
|
disabled={onLast}
|
|
176
185
|
onclick={() => goToPage(pageCount - 1)}
|
|
177
|
-
aria-label={messages.lastPage}
|
|
186
|
+
aria-label={messages.lastPage}>{@render icon("page-last")}</button
|
|
178
187
|
>
|
|
179
188
|
</div>
|
|
180
189
|
</div>
|
package/src/GridMenus.svelte
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import "./sv-grid-scrollbar";
|
|
11
11
|
import { onScrollOutside } from "./a11y/dismissable";
|
|
12
12
|
import type { Snippet } from "svelte";
|
|
13
|
+
import type { GridIconName } from "./grid-icons";
|
|
13
14
|
import SvListBox from "./SvListBox.svelte";
|
|
14
15
|
import type {
|
|
15
16
|
FilterOperator,
|
|
@@ -23,7 +24,7 @@
|
|
|
23
24
|
|
|
24
25
|
import type { SvGridController } from "./SvGrid.controller.svelte";
|
|
25
26
|
|
|
26
|
-
let { ctrl, icon }: { ctrl: SvGridController<TFeatures, TData>; icon: Snippet<[
|
|
27
|
+
let { ctrl, icon }: { ctrl: SvGridController<TFeatures, TData>; icon: Snippet<[GridIconName]> } = $props();
|
|
27
28
|
|
|
28
29
|
// View facade: re-bind the controller's reactive members so the markup
|
|
29
30
|
// (moved verbatim from SvGrid.svelte) stays identical.
|
|
@@ -403,7 +404,7 @@
|
|
|
403
404
|
onclick={() => patchFilter(colId, { join: "OR" })}>OR</button>
|
|
404
405
|
<button type="button" class="sv-grid-menu-join-x" title="Remove second condition"
|
|
405
406
|
aria-label="Remove second condition"
|
|
406
|
-
onclick={() => patchFilter(colId, { operator2: undefined, value2: undefined, valueTo2: undefined, join: undefined })}
|
|
407
|
+
onclick={() => patchFilter(colId, { operator2: undefined, value2: undefined, valueTo2: undefined, join: undefined })}>{@render icon("remove")}</button>
|
|
407
408
|
</div>
|
|
408
409
|
<select
|
|
409
410
|
class="sv-grid-menu-operator-select"
|
|
@@ -452,11 +453,18 @@
|
|
|
452
453
|
<span class="sv-grid-menu-head-note">{pickedCount} of {totalCount}</span>
|
|
453
454
|
{/if}
|
|
454
455
|
</div>
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
456
|
+
<!-- The magnifier used to be a background-image data URI in SvGrid.css
|
|
457
|
+
with a hard-coded stroke colour, so it ignored every theme and could
|
|
458
|
+
not be overridden. It is markup now, and the input keeps its own
|
|
459
|
+
class: focusFilterPanel() and a test both resolve `.sv-grid-menu-search`. -->
|
|
460
|
+
<span class="sv-grid-menu-search-wrap">
|
|
461
|
+
{@render icon("search")}
|
|
462
|
+
<input
|
|
463
|
+
class="sv-grid-menu-search"
|
|
464
|
+
placeholder="Search values..."
|
|
465
|
+
bind:value={ctrl.columnMenuSearch}
|
|
466
|
+
/>
|
|
467
|
+
</span>
|
|
460
468
|
<label class="sv-grid-facet sv-grid-facet-all">
|
|
461
469
|
<input type="checkbox" checked={isAllFacetsChecked(colId)} onchange={() => toggleAllFacets(colId)} />
|
|
462
470
|
<!-- Says what it will actually do: with a query typed it acts on the
|
|
@@ -593,7 +601,7 @@
|
|
|
593
601
|
onclick={() => pinColumnLeft(menuColumnId)}
|
|
594
602
|
>
|
|
595
603
|
<span class="sv-grid-header-icon"
|
|
596
|
-
>{@render icon("
|
|
604
|
+
>{@render icon("pin-left")}</span
|
|
597
605
|
> Pin to left
|
|
598
606
|
</button>
|
|
599
607
|
<button
|
|
@@ -604,7 +612,7 @@
|
|
|
604
612
|
onclick={() => pinColumnRight(menuColumnId)}
|
|
605
613
|
>
|
|
606
614
|
<span class="sv-grid-header-icon"
|
|
607
|
-
>{@render icon("
|
|
615
|
+
>{@render icon("pin-right")}</span
|
|
608
616
|
> Pin to right
|
|
609
617
|
</button>
|
|
610
618
|
<button
|
|
@@ -614,7 +622,7 @@
|
|
|
614
622
|
disabled={!menuPinSide}
|
|
615
623
|
onclick={() => unpinColumn(menuColumnId)}
|
|
616
624
|
>
|
|
617
|
-
<span class="sv-grid-header-icon">{@render icon("
|
|
625
|
+
<span class="sv-grid-header-icon">{@render icon("unpin")}</span> Unpin column
|
|
618
626
|
</button>
|
|
619
627
|
{/if}
|
|
620
628
|
<div class="sv-grid-menu-sep"></div>
|
|
@@ -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: {
|