@svgrid/grid 3.0.2 → 3.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/GridMenus.svelte +44 -2
- package/dist/SvChartMenu.svelte +69 -0
- package/dist/SvChartMenu.svelte.d.ts +11 -0
- package/dist/SvChartPanes.svelte +113 -0
- package/dist/SvChartPanes.svelte.d.ts +20 -0
- package/dist/SvGrid.controller.svelte.d.ts +110 -39
- package/dist/SvGrid.controller.svelte.js +655 -200
- package/dist/SvGrid.css +105 -2
- package/dist/SvGrid.svelte +386 -514
- package/dist/SvGrid.types.d.ts +210 -9
- package/dist/SvGridCellEditor.svelte +17 -0
- package/dist/SvGridChart.svelte +2257 -329
- package/dist/SvGridChart.svelte.d.ts +6 -106
- package/dist/SvGridChart.types.d.ts +338 -0
- package/dist/SvGridChart.types.js +1 -0
- package/dist/SvGridChartBuilder.svelte +323 -0
- package/dist/SvGridChartBuilder.svelte.d.ts +34 -0
- package/dist/SvGridChartPanel.svelte +221 -275
- package/dist/SvGridChartPickers.svelte +417 -0
- package/dist/SvGridChartPickers.svelte.d.ts +70 -0
- package/dist/SvGridChartView.svelte +13 -14
- package/dist/SvModal.svelte +7 -1
- package/dist/SvModal.svelte.d.ts +5 -0
- package/dist/ai.d.ts +40 -8
- package/dist/ai.js +135 -24
- package/dist/aria.d.ts +11 -0
- package/dist/build-api.js +108 -22
- package/dist/cdn/GridMenus-7kbpnnBW.js +635 -0
- package/dist/cdn/GridMenus-BL7ZgQvU.js +636 -0
- package/dist/cdn/SvChartMenu-Bl6PBbkT.js +58 -0
- package/dist/cdn/SvChartMenu-FBSMINA6.js +59 -0
- package/dist/cdn/{SvDateRangeInput-DMLKmGEc.js → SvDateRangeInput-BeU_TkbQ.js} +15 -14
- package/dist/cdn/{SvDateRangeInput-CaOuMs8O.js → SvDateRangeInput-yX8vzleW.js} +38 -37
- package/dist/cdn/{SvDateTimePicker-sonaH0oh.js → SvDateTimePicker-DQwt4UAs.js} +139 -180
- package/dist/cdn/SvDateTimePicker-vNU6bZ-q.js +775 -0
- package/dist/cdn/{SvGridCellEditor-CjEJMJKc.js → SvGridCellEditor-B1p-vCK5.js} +188 -178
- package/dist/cdn/{SvGridCellEditor-BWnTi2N7.js → SvGridCellEditor-D_0q4xAu.js} +98 -88
- package/dist/cdn/SvGridChart-C3EWAZaM.js +3574 -0
- package/dist/cdn/SvGridChart-DzLkSwCH.js +3572 -0
- package/dist/cdn/SvGridChartBuilder-BE2T1ykB.js +783 -0
- package/dist/cdn/SvGridChartBuilder-CfII62sZ.js +784 -0
- package/dist/cdn/SvGridChartPanel-CCX5_Wgd.js +675 -0
- package/dist/cdn/SvGridChartPanel-xf4sWVTo.js +699 -0
- package/dist/cdn/SvGridChartView-CfuXmY5I.js +48 -0
- package/dist/cdn/SvGridChartView-DYwabQWj.js +47 -0
- package/dist/cdn/SvMenuList-CLN8OuIK.js +452 -0
- package/dist/cdn/SvMenuList-DkGhNKjf.js +451 -0
- package/dist/cdn/SvModal-BAE-pjZX.js +396 -0
- package/dist/cdn/SvModal-CIZWCcad.js +397 -0
- package/dist/cdn/chart-Dz7SqMXH.js +4341 -0
- package/dist/cdn/chart-export-BXqueeWJ.js +135 -0
- package/dist/cdn/chart-export-pdf-DONFUnNY.js +110 -0
- package/dist/cdn/chart-panel-messages-CUbf2R4i.js +1052 -0
- package/dist/cdn/chart-panel-messages-CmNrMdsr.js +1053 -0
- package/dist/cdn/chart-summary-BJW_tg_X.js +138 -0
- package/dist/cdn/chart-trend-CaN9mDEV.js +50 -0
- package/dist/cdn/chart-validate-HcOVFAfJ.js +188 -0
- package/dist/cdn/{column-resize-DsfNXMom.js → column-resize-DpmOLfRp.js} +3 -3
- package/dist/cdn/{date-format-BnnHlqGw.js → date-format-CtquV-p3.js} +583 -603
- package/dist/cdn/{date-format-BNii4zeD.js → date-format-D6KFzU_W.js} +347 -367
- package/dist/cdn/dismissable-DAHetSNk.js +44 -0
- package/dist/cdn/editor-contract-LXgQJAgd.js +22 -0
- package/dist/cdn/focus-trap-BBOQYBma.js +65 -0
- package/dist/cdn/{row-resize-BRcimkUT.js → row-resize-niQCp040.js} +44 -36
- package/dist/cdn/{src-DNbz94Ct.js → src-BKhZ6eXd.js} +10227 -9787
- package/dist/cdn/{src-BJ2opd0j.js → src-V1uu8iE9.js} +6568 -6128
- package/dist/cdn/svgrid.js +26 -15
- package/dist/cdn/svgrid.svelte-external.js +26 -15
- package/dist/chart-axes.d.ts +53 -0
- package/dist/chart-axes.js +351 -0
- package/dist/chart-cartesian.d.ts +88 -0
- package/dist/chart-cartesian.js +1862 -0
- package/dist/chart-decimate.d.ts +51 -0
- package/dist/chart-decimate.js +199 -0
- package/dist/chart-export-pdf.d.ts +47 -0
- package/dist/chart-export-pdf.js +187 -0
- package/dist/chart-export.d.ts +2 -1
- package/dist/chart-export.js +18 -3
- package/dist/chart-financial.d.ts +91 -0
- package/dist/chart-financial.js +175 -0
- package/dist/chart-flow.d.ts +10 -0
- package/dist/chart-flow.js +298 -0
- package/dist/chart-format.d.ts +18 -0
- package/dist/chart-format.js +65 -0
- package/dist/chart-grid.d.ts +8 -0
- package/dist/chart-grid.js +190 -0
- package/dist/chart-hierarchy.d.ts +17 -0
- package/dist/chart-hierarchy.js +146 -0
- package/dist/chart-indicators.d.ts +115 -0
- package/dist/chart-indicators.js +389 -0
- package/dist/chart-messages.d.ts +95 -0
- package/dist/chart-messages.js +92 -0
- package/dist/chart-motion.d.ts +16 -0
- package/dist/chart-motion.js +141 -0
- package/dist/chart-panel-messages.d.ts +265 -0
- package/dist/chart-panel-messages.js +285 -0
- package/dist/chart-pivot.d.ts +87 -0
- package/dist/chart-pivot.js +156 -0
- package/dist/chart-polar.d.ts +25 -0
- package/dist/chart-polar.js +700 -0
- package/dist/chart-samples.d.ts +16 -0
- package/dist/chart-samples.js +153 -0
- package/dist/chart-scale.d.ts +113 -0
- package/dist/chart-scale.js +405 -0
- package/dist/chart-stats.d.ts +111 -0
- package/dist/chart-stats.js +419 -0
- package/dist/chart-stream.d.ts +45 -0
- package/dist/chart-stream.js +91 -0
- package/dist/chart-summary.d.ts +27 -0
- package/dist/chart-summary.js +205 -0
- package/dist/chart-sync.svelte.d.ts +32 -0
- package/dist/chart-sync.svelte.js +23 -0
- package/dist/chart-table.d.ts +35 -0
- package/dist/chart-table.js +165 -0
- package/dist/chart-trend.d.ts +19 -0
- package/dist/chart-trend.js +61 -0
- package/dist/chart-types.d.ts +1456 -0
- package/dist/chart-types.js +6 -0
- package/dist/chart-validate.d.ts +43 -0
- package/dist/chart-validate.js +272 -0
- package/dist/chart-zoom.d.ts +40 -0
- package/dist/chart-zoom.js +145 -0
- package/dist/chart.d.ts +186 -865
- package/dist/chart.js +460 -2204
- package/dist/clipboard.d.ts +1 -0
- package/dist/clipboard.js +137 -18
- package/dist/column-resize.d.ts +3 -0
- package/dist/column-resize.js +4 -1
- package/dist/columns.js +3 -0
- package/dist/command-context.d.ts +19 -0
- package/dist/command-context.js +116 -0
- package/dist/core.d.ts +7 -0
- package/dist/createPopoverSelect.svelte.d.ts +2 -2
- package/dist/editing.d.ts +2 -8
- package/dist/editing.js +262 -33
- package/dist/fill-patterns.d.ts +0 -5
- package/dist/fill-patterns.js +56 -2
- package/dist/grid-messages.d.ts +10 -3
- package/dist/grid-messages.js +5 -0
- package/dist/history.d.ts +83 -0
- package/dist/history.js +133 -0
- package/dist/index.d.ts +24 -6
- package/dist/index.js +20 -2
- package/dist/keyboard-handlers.js +88 -31
- package/dist/keyboard.d.ts +55 -0
- package/dist/keyboard.js +132 -0
- package/dist/menus.d.ts +3 -1
- package/dist/menus.js +7 -2
- package/dist/merges.d.ts +97 -0
- package/dist/merges.js +147 -0
- package/dist/row-resize.d.ts +3 -0
- package/dist/row-resize.js +14 -0
- package/dist/scroll-sync.d.ts +1 -0
- package/dist/scroll-sync.js +24 -0
- package/dist/selection.d.ts +16 -0
- package/dist/selection.js +108 -16
- package/dist/shortcut-registry.d.ts +145 -0
- package/dist/shortcut-registry.js +44 -0
- package/dist/svgrid-wrapper.types.d.ts +213 -22
- package/dist/virtualization/virtualizer.js +38 -17
- package/package.json +6 -1
- package/src/GridMenus.svelte +44 -2
- package/src/SvChartMenu.svelte +69 -0
- package/src/SvChartPanes.svelte +113 -0
- package/src/SvChartPanes.test.ts +66 -0
- package/src/SvGrid.controller.svelte.ts +629 -172
- package/src/SvGrid.css +105 -2
- package/src/SvGrid.svelte +386 -514
- package/src/SvGrid.types.ts +188 -8
- package/src/SvGridCellEditor.svelte +17 -0
- package/src/SvGridChart.svelte +2257 -329
- package/src/SvGridChart.test.ts +1419 -1
- package/src/SvGridChart.types.ts +336 -0
- package/src/SvGridChartBuilder.svelte +323 -0
- package/src/SvGridChartPanel.svelte +221 -275
- package/src/SvGridChartPickers.svelte +417 -0
- package/src/SvGridChartView.svelte +13 -14
- package/src/SvModal.svelte +7 -1
- package/src/SvModal.test.ts +9 -0
- package/src/ai.test.ts +57 -2
- package/src/ai.ts +166 -22
- package/src/aria.d.ts +11 -0
- package/src/build-api.coverage.test.ts +184 -0
- package/src/build-api.ts +107 -18
- package/src/chart-axes.test.ts +573 -0
- package/src/chart-axes.ts +342 -0
- package/src/chart-cartesian.ts +1863 -0
- package/src/chart-decimate.test.ts +249 -0
- package/src/chart-decimate.ts +186 -0
- package/src/chart-export-pdf.test.ts +91 -0
- package/src/chart-export-pdf.ts +207 -0
- package/src/chart-export.test.ts +14 -0
- package/src/chart-export.ts +13 -3
- package/src/chart-financial.ts +208 -0
- package/src/chart-flow.ts +286 -0
- package/src/chart-format.ts +67 -0
- package/src/chart-grid.ts +179 -0
- package/src/chart-hierarchy.ts +135 -0
- package/src/chart-indicators.test.ts +443 -0
- package/src/chart-indicators.ts +384 -0
- package/src/chart-messages.test.ts +21 -0
- package/src/chart-messages.ts +184 -0
- package/src/chart-motion.test.ts +71 -0
- package/src/chart-motion.ts +148 -0
- package/src/chart-panel-messages.test.ts +29 -0
- package/src/chart-panel-messages.ts +550 -0
- package/src/chart-pivot.test.ts +54 -0
- package/src/chart-pivot.ts +225 -0
- package/src/chart-polar.ts +674 -0
- package/src/chart-samples.ts +106 -0
- package/src/chart-scale.ts +394 -0
- package/src/chart-series-types.test.ts +714 -0
- package/src/chart-stats.ts +409 -0
- package/src/chart-stream.test.ts +75 -0
- package/src/chart-stream.ts +114 -0
- package/src/chart-summary.test.ts +90 -0
- package/src/chart-summary.ts +197 -0
- package/src/chart-sync.svelte.ts +41 -0
- package/src/chart-table.test.ts +76 -0
- package/src/chart-table.ts +178 -0
- package/src/chart-trend.ts +47 -0
- package/src/chart-types.ts +1309 -0
- package/src/chart-validate.test.ts +100 -0
- package/src/chart-validate.ts +247 -0
- package/src/chart-zoom.test.ts +137 -0
- package/src/chart-zoom.ts +124 -0
- package/src/chart.coverage.test.ts +81 -0
- package/src/chart.test.ts +56 -3
- package/src/chart.ts +557 -2784
- package/src/clipboard.test.ts +151 -0
- package/src/clipboard.ts +145 -16
- package/src/column-resize.ts +7 -1
- package/src/columns.ts +2 -0
- package/src/command-context.test.ts +200 -0
- package/src/command-context.ts +137 -0
- package/src/core.ts +7 -0
- package/src/editing.test.ts +281 -3
- package/src/editing.ts +256 -38
- package/src/fill-patterns.test.ts +35 -0
- package/src/fill-patterns.ts +56 -2
- package/src/grid-messages.ts +16 -3
- package/src/history.test.ts +196 -0
- package/src/history.ts +162 -0
- package/src/icon-seam.test.ts +3 -2
- package/src/index.ts +150 -2
- package/src/keyboard-handlers.coverage.test.ts +126 -6
- package/src/keyboard-handlers.ts +91 -30
- package/src/keyboard-shortcuts.seam.test.ts +200 -0
- package/src/keyboard.test.ts +124 -1
- package/src/keyboard.ts +142 -0
- package/src/menus.test.ts +26 -0
- package/src/menus.ts +10 -3
- package/src/merges.test.ts +112 -0
- package/src/merges.ts +168 -0
- package/src/pivot.grid.test.ts +40 -5
- package/src/row-resize.test.ts +44 -0
- package/src/row-resize.ts +17 -0
- package/src/scroll-sync.test.ts +42 -1
- package/src/scroll-sync.ts +25 -0
- package/src/selection.multi-range.test.ts +34 -0
- package/src/selection.test.ts +30 -2
- package/src/selection.ts +111 -19
- package/src/shortcut-registry.test.ts +97 -0
- package/src/shortcut-registry.ts +181 -0
- package/src/svgrid-wrapper.types.ts +184 -13
- package/src/svgrid.behavior.test.ts +135 -0
- package/src/svgrid.charting.test.ts +513 -3
- package/src/svgrid.context-menu.test.ts +31 -0
- package/src/svgrid.new-features.wrapper.test.ts +10 -3
- package/src/virtualization/virtualizer.ts +39 -17
- package/dist/cdn/GridMenus-DyFA-QxA.js +0 -602
- package/dist/cdn/GridMenus-M2XP9t0_.js +0 -603
- package/dist/cdn/SvDateTimePicker-CCbDZNZB.js +0 -816
- package/dist/cdn/SvGridChart-DAb9RbRs.js +0 -1480
- package/dist/cdn/SvGridChart-Yl-IXZFO.js +0 -1481
- package/dist/cdn/SvGridChartPanel-DayQpDsR.js +0 -834
- package/dist/cdn/SvGridChartPanel-aqf8n-vJ.js +0 -810
- package/dist/cdn/SvGridChartView-CxfakxR0.js +0 -56
- package/dist/cdn/SvGridChartView-Dm_YEx9L.js +0 -55
- package/dist/cdn/chart-CKJRCPOk.js +0 -1652
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pivot result -> chart spec.
|
|
3
|
+
*
|
|
4
|
+
* Turns the `{ rows, columns }` a pivot engine produces (the enterprise
|
|
5
|
+
* engine, through the `pivot` prop or `SvPivotDesigner`) into a {@link
|
|
6
|
+
* ChartSpec}, so one pivot layout drives both the table and a chart. The
|
|
7
|
+
* grid's own Chart panel runs this in pivot mode; enterprise's
|
|
8
|
+
* `pivotToChartSpec` is this function with its own types.
|
|
9
|
+
*
|
|
10
|
+
* Mapping:
|
|
11
|
+
* - categories = the row-axis LEAF rows (subtotals and the grand-total row
|
|
12
|
+
* are skipped). With nested rows the parents become a second axis tier
|
|
13
|
+
* (`categoryGroups`). With no row dims, the single total row.
|
|
14
|
+
* - series = the column-axis leaf value columns (one per col-path x
|
|
15
|
+
* measure). The grand-total column is excluded unless `includeTotals`.
|
|
16
|
+
* - values = each leaf row's aggregated cell for that series column; an
|
|
17
|
+
* empty cell (an average over no rows is null) is a gap, not a zero.
|
|
18
|
+
*/
|
|
19
|
+
import type { CellFormatConfig } from './core'
|
|
20
|
+
import type { ChartSpec, ChartType } from './chart-types'
|
|
21
|
+
|
|
22
|
+
/** What {@link pivotResultToChartSpec} reads: the engine's rows and column tree, loosely typed so either package's result fits. */
|
|
23
|
+
export type PivotResultLike = {
|
|
24
|
+
rows: ReadonlyArray<Record<string, unknown>>
|
|
25
|
+
columns: ReadonlyArray<unknown>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Options for {@link pivotResultToChartSpec}. */
|
|
29
|
+
export type PivotResultChartOptions = {
|
|
30
|
+
/** Chart type. Default `'bar'`. A type that reads rows directly (scatter, gauge, box plot, histogram, ranges, bullet, candles) cannot be built from a pivot; see {@link pivotChartType}. */
|
|
31
|
+
type?: ChartType
|
|
32
|
+
/** Stack the series. Default false. */
|
|
33
|
+
stacked?: boolean
|
|
34
|
+
/** Stack to 100%. Implies `stacked`. */
|
|
35
|
+
stacked100?: boolean
|
|
36
|
+
/** Include the grand-total column / row as a series / category. Default false. */
|
|
37
|
+
includeTotals?: boolean
|
|
38
|
+
/** Cap the number of categories (row leaves) charted. */
|
|
39
|
+
maxCategories?: number
|
|
40
|
+
/**
|
|
41
|
+
* The measure's cell format, carried to the chart as `valueFormat` (with
|
|
42
|
+
* `currency` and `locale`), so a cell the table shows as "$469,662" charts
|
|
43
|
+
* as "$470k" on the axis and "$469,662" in the tooltip rather than "470k".
|
|
44
|
+
* With several measures in different formats, pass none.
|
|
45
|
+
*/
|
|
46
|
+
format?: CellFormatConfig
|
|
47
|
+
/**
|
|
48
|
+
* The layout's measures, each with its cell `format`, when the caller has
|
|
49
|
+
* them rather than one format: used as `format` when they agree, ignored
|
|
50
|
+
* when a layout mixes a currency and a count (a "$" on the count would be
|
|
51
|
+
* wrong).
|
|
52
|
+
*/
|
|
53
|
+
measures?: ReadonlyArray<{ format?: unknown }>
|
|
54
|
+
/** Name the value axis. Default: the measure's label when the layout has one measure, nothing otherwise; `null` for none. */
|
|
55
|
+
yAxisTitle?: string | null
|
|
56
|
+
/** Series colours, when the caller has a palette. */
|
|
57
|
+
palette?: string[]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Types that read rows one by one or need a shape a pivot does not have. */
|
|
61
|
+
const UNSHAPED: ReadonlySet<string> = new Set([
|
|
62
|
+
'scatter', 'gauge', 'boxplot', 'histogram', 'range-bar', 'range-area', 'dumbbell', 'bullet', 'candlestick', 'ohlc',
|
|
63
|
+
])
|
|
64
|
+
|
|
65
|
+
/** The type a pivot chart can draw: the one asked for, or a bar chart when that type cannot be built from a pivot. */
|
|
66
|
+
export function pivotChartType(type: ChartType | undefined): ChartType {
|
|
67
|
+
return !type || UNSHAPED.has(type) ? 'bar' : type
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The grid column a click on a pivot chart filters: the innermost row
|
|
72
|
+
* dimension, when the grid has a column for that field. A category of the
|
|
73
|
+
* chart is one value of it, so a value filter there narrows the source rows
|
|
74
|
+
* and the pivot with them.
|
|
75
|
+
*/
|
|
76
|
+
export function pivotFilterColumn(
|
|
77
|
+
rowFields: ReadonlyArray<string>,
|
|
78
|
+
columns: ReadonlyArray<{ id: string }>,
|
|
79
|
+
fieldOf: (id: string) => string | undefined,
|
|
80
|
+
): string | null {
|
|
81
|
+
const field = rowFields[rowFields.length - 1]
|
|
82
|
+
return (field && columns.find((c) => fieldOf(c.id) === field)?.id) || null
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Server aggregates as a spec: the grid panel's `charting.getAggregate` hands
|
|
87
|
+
* back `{ category, series?, value }` buckets and this lays them out as one
|
|
88
|
+
* series per distinct `series` (or a single one), categories in first-seen
|
|
89
|
+
* order, a missing cell as 0.
|
|
90
|
+
*/
|
|
91
|
+
export function bucketsToChartSpec(
|
|
92
|
+
buckets: ReadonlyArray<{ category: string; series?: string; value: number }>,
|
|
93
|
+
type: ChartType,
|
|
94
|
+
hasSeries: boolean,
|
|
95
|
+
): ChartSpec {
|
|
96
|
+
const cats: string[] = []
|
|
97
|
+
const catIdx = new Map<string, number>()
|
|
98
|
+
const seriesMap = new Map<string, number[]>()
|
|
99
|
+
for (const b of buckets) {
|
|
100
|
+
let ci = catIdx.get(b.category)
|
|
101
|
+
if (ci === undefined) {
|
|
102
|
+
ci = cats.length
|
|
103
|
+
catIdx.set(b.category, ci)
|
|
104
|
+
cats.push(b.category)
|
|
105
|
+
}
|
|
106
|
+
const key = hasSeries ? (b.series ?? '') : 'value'
|
|
107
|
+
let arr = seriesMap.get(key)
|
|
108
|
+
if (!arr) {
|
|
109
|
+
arr = []
|
|
110
|
+
seriesMap.set(key, arr)
|
|
111
|
+
}
|
|
112
|
+
arr[ci] = (arr[ci] ?? 0) + b.value
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
type,
|
|
116
|
+
categories: cats,
|
|
117
|
+
series: [...seriesMap.entries()].map(([label, values]) => ({ label, values: cats.map((_, i) => values[i] ?? 0) })),
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
type LooseColumn = { id?: string; header?: unknown; columns?: LooseColumn[] }
|
|
122
|
+
|
|
123
|
+
/** The chart fields a cell format maps to. Dates have no place on a value axis. */
|
|
124
|
+
function chartFormat(f: CellFormatConfig | undefined): Pick<ChartSpec, 'valueFormat' | 'currency' | 'locale' | 'yAxis'> {
|
|
125
|
+
if (!f) return {}
|
|
126
|
+
const locale = Array.isArray(f.locales) ? f.locales[0] : f.locales
|
|
127
|
+
const base = locale ? { locale } : {}
|
|
128
|
+
if (f.type === 'number') return { ...base, valueFormat: 'number' }
|
|
129
|
+
if (f.type === 'currency') return { ...base, valueFormat: 'currency', currency: f.currency ?? 'USD' }
|
|
130
|
+
if (f.type === 'percent') {
|
|
131
|
+
// The chart's percent format multiplies by 100 the way Intl does; cells
|
|
132
|
+
// that hold percentage points already read "42", so they take a
|
|
133
|
+
// formatter that only appends the sign.
|
|
134
|
+
if (f.valueIsPercentPoints) return { ...base, yAxis: { formatter: (v) => `${v}%` } }
|
|
135
|
+
return { ...base, valueFormat: 'percent' }
|
|
136
|
+
}
|
|
137
|
+
return {}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* A computed pivot as a chart spec. Row leaves become the categories (nested
|
|
142
|
+
* rows a grouped axis), column leaves the series, totals are left out unless
|
|
143
|
+
* asked for, the measure's format and name come along, and an empty cell is
|
|
144
|
+
* a gap.
|
|
145
|
+
*/
|
|
146
|
+
export function pivotResultToChartSpec(result: PivotResultLike, opts: PivotResultChartOptions = {}): ChartSpec {
|
|
147
|
+
const type = pivotChartType(opts.type)
|
|
148
|
+
|
|
149
|
+
// Category rows: the row-axis leaves. Fall back to the grand-total (single
|
|
150
|
+
// "All") row when no row dims are configured, then to every row.
|
|
151
|
+
let catRows = result.rows.filter((r) => r.__pivotKind === 'leaf')
|
|
152
|
+
if (!catRows.length) catRows = result.rows.filter((r) => r.__pivotKind === 'grandTotal')
|
|
153
|
+
if (!catRows.length) catRows = result.rows.slice()
|
|
154
|
+
if (opts.maxCategories && catRows.length > opts.maxCategories) catRows = catRows.slice(0, opts.maxCategories)
|
|
155
|
+
// Categories are the leaf's OWN label; when rows are nested, emit a parent
|
|
156
|
+
// tier (categoryGroups) so the axis reads "Americas | Canada, USA ..." as a
|
|
157
|
+
// grouped axis rather than colliding on a bare repeated leaf label.
|
|
158
|
+
const byId = new Map(result.rows.map((r) => [r.__pivotId as string, r]))
|
|
159
|
+
const ownLabel = (r: Record<string, unknown>) => String(r.__pivotLabel ?? '').trim() || '(blank)'
|
|
160
|
+
const categories = catRows.map(ownLabel)
|
|
161
|
+
const parentOf = (r: Record<string, unknown>) => (r.__pivotParentId ? byId.get(r.__pivotParentId as string) : undefined)
|
|
162
|
+
const nested = catRows.length > 0 && catRows.every((r) => parentOf(r) !== undefined)
|
|
163
|
+
let categoryGroups: Array<{ label: string; span: number }> | undefined
|
|
164
|
+
if (nested) {
|
|
165
|
+
categoryGroups = []
|
|
166
|
+
let prevId: unknown
|
|
167
|
+
for (const r of catRows) {
|
|
168
|
+
const pid = r.__pivotParentId
|
|
169
|
+
const last = categoryGroups[categoryGroups.length - 1]
|
|
170
|
+
if (last && pid === prevId) last.span += 1
|
|
171
|
+
else {
|
|
172
|
+
categoryGroups.push({ label: ownLabel(parentOf(r)!), span: 1 })
|
|
173
|
+
prevId = pid
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Collect the leaf value columns, carrying the accumulated column-group path
|
|
179
|
+
// so a series can be labelled by its column path ("Q1", or "Q1 · Revenue").
|
|
180
|
+
type Leaf = { id: string; path: string[]; header: string }
|
|
181
|
+
const leaves: Leaf[] = []
|
|
182
|
+
const walk = (cols: LooseColumn[] | undefined, path: string[]): void => {
|
|
183
|
+
for (const c of cols ?? []) {
|
|
184
|
+
if (!c || c.id === '__pivotRowHeader') continue
|
|
185
|
+
// Skip the grand-total column group (and its leaves) unless asked for.
|
|
186
|
+
if (!opts.includeTotals && typeof c.id === 'string' && c.id.startsWith('pv_group__grand')) continue
|
|
187
|
+
if (Array.isArray(c.columns)) walk(c.columns, [...path, String(c.header ?? '')])
|
|
188
|
+
else if (typeof c.id === 'string') leaves.push({ id: c.id, path, header: String(c.header ?? '') })
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
walk(result.columns as LooseColumn[], [])
|
|
192
|
+
|
|
193
|
+
const filtered = opts.includeTotals ? leaves : leaves.filter((l) => !l.id.includes('__total'))
|
|
194
|
+
const uniqueHeaders = new Set(filtered.map((l) => l.header))
|
|
195
|
+
const series = filtered.map((l) => ({
|
|
196
|
+
label: l.path.length === 0 ? l.header : uniqueHeaders.size > 1 ? [...l.path, l.header].join(' · ') : l.path.join(' · '),
|
|
197
|
+
values: catRows.map((r) => {
|
|
198
|
+
const v = r[l.id]
|
|
199
|
+
// An empty cell (an average over no rows is null) is a gap in the
|
|
200
|
+
// chart, not a zero bar; a sum over no rows is the 0 the engine wrote.
|
|
201
|
+
if (v == null || v === '') return Number.NaN
|
|
202
|
+
return typeof v === 'number' ? v : Number(v)
|
|
203
|
+
}),
|
|
204
|
+
}))
|
|
205
|
+
|
|
206
|
+
// With one measure the value axis can carry its name; the series are then
|
|
207
|
+
// the column-axis values and say nothing about what is measured.
|
|
208
|
+
const measureLabel = uniqueHeaders.size === 1 && filtered.length ? filtered[0]!.header : ''
|
|
209
|
+
const yAxisTitle = opts.yAxisTitle === undefined ? measureLabel : opts.yAxisTitle
|
|
210
|
+
const formats = (opts.measures ?? []).map((m) => m.format as CellFormatConfig | undefined)
|
|
211
|
+
const agreed = formats[0] && formats.every((f) => f && JSON.stringify(f) === JSON.stringify(formats[0])) ? formats[0] : undefined
|
|
212
|
+
const fmt = chartFormat(opts.format ?? agreed)
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
type,
|
|
216
|
+
categories,
|
|
217
|
+
series,
|
|
218
|
+
...(categoryGroups && categoryGroups.length > 1 ? { categoryGroups } : {}),
|
|
219
|
+
...(opts.stacked || opts.stacked100 ? { stacked: true } : {}),
|
|
220
|
+
...(opts.stacked100 ? { stacked100: true } : {}),
|
|
221
|
+
...(opts.palette ? { palette: opts.palette } : {}),
|
|
222
|
+
...fmt,
|
|
223
|
+
...(yAxisTitle ? { yAxis: { ...fmt.yAxis, title: yAxisTitle } } : {}),
|
|
224
|
+
}
|
|
225
|
+
}
|