@arcfusionz/arc-analytic-ui 0.0.0
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/_virtual/_rolldown/runtime.js +13 -0
- package/dist/components/AnomalyCard/AnomalyCard.chart.d.ts +64 -0
- package/dist/components/AnomalyCard/AnomalyCard.chart.js +341 -0
- package/dist/components/AnomalyCard/AnomalyCard.d.ts +169 -0
- package/dist/components/AnomalyCard/AnomalyCard.js +1019 -0
- package/dist/components/AnomalyCard/AnomalyCard.model.d.ts +508 -0
- package/dist/components/AnomalyCard/AnomalyCard.model.js +553 -0
- package/dist/components/AnomalyCard/index.d.ts +4 -0
- package/dist/components/AnomalyCard/index.js +4 -0
- package/dist/components/ChartCard/ChartCard.chart.d.ts +72 -0
- package/dist/components/ChartCard/ChartCard.chart.js +1333 -0
- package/dist/components/ChartCard/ChartCard.d.ts +206 -0
- package/dist/components/ChartCard/ChartCard.js +718 -0
- package/dist/components/ChartCard/ChartCard.model.d.ts +452 -0
- package/dist/components/ChartCard/ChartCard.model.js +1223 -0
- package/dist/components/ChartCard/index.d.ts +4 -0
- package/dist/components/ChartCard/index.js +4 -0
- package/dist/components/ComparisonCard/ComparisonCard.chart.d.ts +80 -0
- package/dist/components/ComparisonCard/ComparisonCard.chart.js +337 -0
- package/dist/components/ComparisonCard/ComparisonCard.d.ts +200 -0
- package/dist/components/ComparisonCard/ComparisonCard.js +1325 -0
- package/dist/components/ComparisonCard/ComparisonCard.model.d.ts +309 -0
- package/dist/components/ComparisonCard/ComparisonCard.model.js +263 -0
- package/dist/components/ComparisonCard/index.d.ts +4 -0
- package/dist/components/ComparisonCard/index.js +4 -0
- package/dist/components/ForecastCard/ForecastCard.chart.d.ts +82 -0
- package/dist/components/ForecastCard/ForecastCard.chart.js +416 -0
- package/dist/components/ForecastCard/ForecastCard.d.ts +148 -0
- package/dist/components/ForecastCard/ForecastCard.js +1029 -0
- package/dist/components/ForecastCard/ForecastCard.model.d.ts +497 -0
- package/dist/components/ForecastCard/ForecastCard.model.js +554 -0
- package/dist/components/ForecastCard/index.d.ts +4 -0
- package/dist/components/ForecastCard/index.js +4 -0
- package/dist/components/HighlightCard/HighlightCard.chart.d.ts +122 -0
- package/dist/components/HighlightCard/HighlightCard.chart.js +660 -0
- package/dist/components/HighlightCard/HighlightCard.d.ts +202 -0
- package/dist/components/HighlightCard/HighlightCard.js +1286 -0
- package/dist/components/HighlightCard/HighlightCard.model.d.ts +692 -0
- package/dist/components/HighlightCard/HighlightCard.model.js +875 -0
- package/dist/components/HighlightCard/index.d.ts +4 -0
- package/dist/components/HighlightCard/index.js +4 -0
- package/dist/components/InsightCard/InsightCard.chart.d.ts +91 -0
- package/dist/components/InsightCard/InsightCard.chart.js +471 -0
- package/dist/components/InsightCard/InsightCard.d.ts +161 -0
- package/dist/components/InsightCard/InsightCard.js +961 -0
- package/dist/components/InsightCard/InsightCard.model.d.ts +405 -0
- package/dist/components/InsightCard/InsightCard.model.js +421 -0
- package/dist/components/InsightCard/index.d.ts +4 -0
- package/dist/components/InsightCard/index.js +4 -0
- package/dist/components/MetricCard/MetricCard.chart.d.ts +79 -0
- package/dist/components/MetricCard/MetricCard.chart.js +435 -0
- package/dist/components/MetricCard/MetricCard.d.ts +227 -0
- package/dist/components/MetricCard/MetricCard.js +1293 -0
- package/dist/components/MetricCard/MetricCard.model.d.ts +521 -0
- package/dist/components/MetricCard/MetricCard.model.js +627 -0
- package/dist/components/MetricCard/index.d.ts +4 -0
- package/dist/components/MetricCard/index.js +4 -0
- package/dist/components/ProfileCard/ProfileCard.chart.d.ts +64 -0
- package/dist/components/ProfileCard/ProfileCard.chart.js +427 -0
- package/dist/components/ProfileCard/ProfileCard.d.ts +140 -0
- package/dist/components/ProfileCard/ProfileCard.js +735 -0
- package/dist/components/ProfileCard/ProfileCard.model.d.ts +279 -0
- package/dist/components/ProfileCard/ProfileCard.model.js +244 -0
- package/dist/components/ProfileCard/index.d.ts +4 -0
- package/dist/components/ProfileCard/index.js +4 -0
- package/dist/components/ProgressCard/ProgressCard.chart.d.ts +73 -0
- package/dist/components/ProgressCard/ProgressCard.chart.js +373 -0
- package/dist/components/ProgressCard/ProgressCard.d.ts +137 -0
- package/dist/components/ProgressCard/ProgressCard.js +908 -0
- package/dist/components/ProgressCard/ProgressCard.model.d.ts +385 -0
- package/dist/components/ProgressCard/ProgressCard.model.js +389 -0
- package/dist/components/ProgressCard/index.d.ts +4 -0
- package/dist/components/ProgressCard/index.js +4 -0
- package/dist/components/ReportCard/ReportCard.chart.d.ts +45 -0
- package/dist/components/ReportCard/ReportCard.chart.js +231 -0
- package/dist/components/ReportCard/ReportCard.d.ts +153 -0
- package/dist/components/ReportCard/ReportCard.js +1118 -0
- package/dist/components/ReportCard/ReportCard.model.d.ts +392 -0
- package/dist/components/ReportCard/ReportCard.model.js +399 -0
- package/dist/components/ReportCard/index.d.ts +4 -0
- package/dist/components/ReportCard/index.js +4 -0
- package/dist/components/SankeyCard/SankeyCard.chart.d.ts +75 -0
- package/dist/components/SankeyCard/SankeyCard.chart.js +249 -0
- package/dist/components/SankeyCard/SankeyCard.d.ts +185 -0
- package/dist/components/SankeyCard/SankeyCard.js +788 -0
- package/dist/components/SankeyCard/SankeyCard.model.d.ts +501 -0
- package/dist/components/SankeyCard/SankeyCard.model.js +789 -0
- package/dist/components/SankeyCard/index.d.ts +4 -0
- package/dist/components/SankeyCard/index.js +4 -0
- package/dist/components/StatusCard/StatusCard.chart.d.ts +60 -0
- package/dist/components/StatusCard/StatusCard.chart.js +188 -0
- package/dist/components/StatusCard/StatusCard.d.ts +176 -0
- package/dist/components/StatusCard/StatusCard.js +989 -0
- package/dist/components/StatusCard/StatusCard.model.d.ts +555 -0
- package/dist/components/StatusCard/StatusCard.model.js +776 -0
- package/dist/components/StatusCard/index.d.ts +4 -0
- package/dist/components/StatusCard/index.js +4 -0
- package/dist/components/TableCard/TableCard.chart.d.ts +53 -0
- package/dist/components/TableCard/TableCard.chart.js +192 -0
- package/dist/components/TableCard/TableCard.d.ts +140 -0
- package/dist/components/TableCard/TableCard.js +811 -0
- package/dist/components/TableCard/TableCard.model.d.ts +381 -0
- package/dist/components/TableCard/TableCard.model.js +475 -0
- package/dist/components/TableCard/index.d.ts +4 -0
- package/dist/components/TableCard/index.js +4 -0
- package/dist/components/TargetCard/TargetCard.chart.d.ts +52 -0
- package/dist/components/TargetCard/TargetCard.chart.js +273 -0
- package/dist/components/TargetCard/TargetCard.d.ts +143 -0
- package/dist/components/TargetCard/TargetCard.js +910 -0
- package/dist/components/TargetCard/TargetCard.model.d.ts +468 -0
- package/dist/components/TargetCard/TargetCard.model.js +581 -0
- package/dist/components/TargetCard/index.d.ts +4 -0
- package/dist/components/TargetCard/index.js +4 -0
- package/dist/components/TreeCard/TreeCard.chart.d.ts +80 -0
- package/dist/components/TreeCard/TreeCard.chart.js +178 -0
- package/dist/components/TreeCard/TreeCard.d.ts +182 -0
- package/dist/components/TreeCard/TreeCard.js +1187 -0
- package/dist/components/TreeCard/TreeCard.model.d.ts +330 -0
- package/dist/components/TreeCard/TreeCard.model.js +401 -0
- package/dist/components/TreeCard/index.d.ts +4 -0
- package/dist/components/TreeCard/index.js +4 -0
- package/dist/components/TrendCard/TrendCard.chart.d.ts +75 -0
- package/dist/components/TrendCard/TrendCard.chart.js +393 -0
- package/dist/components/TrendCard/TrendCard.d.ts +224 -0
- package/dist/components/TrendCard/TrendCard.js +1053 -0
- package/dist/components/TrendCard/TrendCard.model.d.ts +545 -0
- package/dist/components/TrendCard/TrendCard.model.js +623 -0
- package/dist/components/TrendCard/index.d.ts +4 -0
- package/dist/components/TrendCard/index.js +4 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +57 -0
- package/dist/lib/vega/ArcVegaChart.d.ts +24 -0
- package/dist/lib/vega/ArcVegaChart.js +42 -0
- package/dist/lib/vega/ArcVegaSurface.d.ts +13 -0
- package/dist/lib/vega/ArcVegaSurface.js +38 -0
- package/dist/lib/vega/axisTicks.js +156 -0
- package/dist/lib/vega/config.d.ts +29 -0
- package/dist/lib/vega/config.js +113 -0
- package/dist/lib/vega/embed.d.ts +29 -0
- package/dist/lib/vega/embed.js +29 -0
- package/dist/lib/vega/engine.d.ts +10 -0
- package/dist/lib/vega/engine.js +6 -0
- package/dist/lib/vega/formatters.d.ts +125 -0
- package/dist/lib/vega/formatters.js +277 -0
- package/dist/lib/vega/index.d.ts +11 -0
- package/dist/lib/vega/load.d.ts +13 -0
- package/dist/lib/vega/load.js +21 -0
- package/dist/lib/vega/time.d.ts +45 -0
- package/dist/lib/vega/time.js +105 -0
- package/dist/lib/vega/tokens.d.ts +73 -0
- package/dist/lib/vega/tokens.js +137 -0
- package/dist/lib/vega/tooltip.d.ts +45 -0
- package/dist/lib/vega/tooltip.js +124 -0
- package/dist/lib/vega/useArcVega.d.ts +43 -0
- package/dist/lib/vega/useArcVega.js +121 -0
- package/package.json +60 -0
|
@@ -0,0 +1,1223 @@
|
|
|
1
|
+
import { formatDate, formatNumber } from "../../lib/vega/formatters.js";
|
|
2
|
+
//#region src/components/ChartCard/ChartCard.model.ts
|
|
3
|
+
const NO_FIELDS = {
|
|
4
|
+
x: "none",
|
|
5
|
+
y: "none",
|
|
6
|
+
series: "none",
|
|
7
|
+
value: false,
|
|
8
|
+
size: false,
|
|
9
|
+
x2: false
|
|
10
|
+
};
|
|
11
|
+
const fields = (overrides) => ({
|
|
12
|
+
...NO_FIELDS,
|
|
13
|
+
...overrides
|
|
14
|
+
});
|
|
15
|
+
const XY = fields({
|
|
16
|
+
x: "required",
|
|
17
|
+
y: "required",
|
|
18
|
+
series: "optional"
|
|
19
|
+
});
|
|
20
|
+
/** Raw observations: a value, optionally grouped — Vega bins or aggregates them. */
|
|
21
|
+
const OBSERVATIONS = fields({
|
|
22
|
+
x: "optional",
|
|
23
|
+
y: "required",
|
|
24
|
+
series: "optional"
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* The registry every other part of the card reads: field-type inference, the
|
|
28
|
+
* legend, the description, the table twin and the spec builder all branch on
|
|
29
|
+
* this rather than on `type` directly, so adding a form is one entry plus one
|
|
30
|
+
* encoding.
|
|
31
|
+
*/
|
|
32
|
+
const CHART_TYPES = {
|
|
33
|
+
bar: {
|
|
34
|
+
intent: "magnitude",
|
|
35
|
+
mark: "bar",
|
|
36
|
+
fields: XY,
|
|
37
|
+
polar: false,
|
|
38
|
+
square: false,
|
|
39
|
+
horizontal: false,
|
|
40
|
+
stack: false,
|
|
41
|
+
color: "single",
|
|
42
|
+
legend: "series",
|
|
43
|
+
aggregating: false,
|
|
44
|
+
minRows: 1,
|
|
45
|
+
name: "bar chart"
|
|
46
|
+
},
|
|
47
|
+
"bar-horizontal": {
|
|
48
|
+
intent: "ranking",
|
|
49
|
+
mark: "bar",
|
|
50
|
+
fields: XY,
|
|
51
|
+
polar: false,
|
|
52
|
+
square: false,
|
|
53
|
+
horizontal: true,
|
|
54
|
+
stack: false,
|
|
55
|
+
color: "single",
|
|
56
|
+
legend: "series",
|
|
57
|
+
aggregating: false,
|
|
58
|
+
minRows: 1,
|
|
59
|
+
name: "horizontal bar chart"
|
|
60
|
+
},
|
|
61
|
+
"bar-grouped": {
|
|
62
|
+
intent: "magnitude",
|
|
63
|
+
mark: "bar",
|
|
64
|
+
fields: fields({
|
|
65
|
+
x: "required",
|
|
66
|
+
y: "required",
|
|
67
|
+
series: "required"
|
|
68
|
+
}),
|
|
69
|
+
polar: false,
|
|
70
|
+
square: false,
|
|
71
|
+
horizontal: false,
|
|
72
|
+
stack: false,
|
|
73
|
+
color: "identity",
|
|
74
|
+
legend: "series",
|
|
75
|
+
aggregating: false,
|
|
76
|
+
minRows: 2,
|
|
77
|
+
name: "grouped bar chart"
|
|
78
|
+
},
|
|
79
|
+
"bar-stacked": {
|
|
80
|
+
intent: "part-to-whole",
|
|
81
|
+
mark: "bar",
|
|
82
|
+
fields: fields({
|
|
83
|
+
x: "required",
|
|
84
|
+
y: "required",
|
|
85
|
+
series: "required"
|
|
86
|
+
}),
|
|
87
|
+
polar: false,
|
|
88
|
+
square: false,
|
|
89
|
+
horizontal: false,
|
|
90
|
+
stack: "zero",
|
|
91
|
+
color: "identity",
|
|
92
|
+
legend: "series",
|
|
93
|
+
aggregating: false,
|
|
94
|
+
minRows: 2,
|
|
95
|
+
name: "stacked bar chart"
|
|
96
|
+
},
|
|
97
|
+
"bar-normalized": {
|
|
98
|
+
intent: "part-to-whole",
|
|
99
|
+
mark: "bar",
|
|
100
|
+
fields: fields({
|
|
101
|
+
x: "required",
|
|
102
|
+
y: "required",
|
|
103
|
+
series: "required"
|
|
104
|
+
}),
|
|
105
|
+
polar: false,
|
|
106
|
+
square: false,
|
|
107
|
+
horizontal: false,
|
|
108
|
+
stack: "normalize",
|
|
109
|
+
color: "identity",
|
|
110
|
+
legend: "series",
|
|
111
|
+
aggregating: false,
|
|
112
|
+
minRows: 2,
|
|
113
|
+
name: "100% stacked bar chart"
|
|
114
|
+
},
|
|
115
|
+
"bar-diverging": {
|
|
116
|
+
intent: "deviation",
|
|
117
|
+
mark: "bar",
|
|
118
|
+
fields: XY,
|
|
119
|
+
polar: false,
|
|
120
|
+
square: false,
|
|
121
|
+
horizontal: true,
|
|
122
|
+
stack: false,
|
|
123
|
+
color: "single",
|
|
124
|
+
legend: "none",
|
|
125
|
+
aggregating: false,
|
|
126
|
+
minRows: 1,
|
|
127
|
+
name: "diverging bar chart"
|
|
128
|
+
},
|
|
129
|
+
"bar-range": {
|
|
130
|
+
intent: "flow",
|
|
131
|
+
mark: "bar",
|
|
132
|
+
fields: fields({
|
|
133
|
+
x: "required",
|
|
134
|
+
y: "optional",
|
|
135
|
+
series: "required",
|
|
136
|
+
x2: true
|
|
137
|
+
}),
|
|
138
|
+
polar: false,
|
|
139
|
+
square: false,
|
|
140
|
+
horizontal: true,
|
|
141
|
+
stack: false,
|
|
142
|
+
color: "identity",
|
|
143
|
+
legend: "series",
|
|
144
|
+
aggregating: false,
|
|
145
|
+
minRows: 1,
|
|
146
|
+
name: "range bar chart"
|
|
147
|
+
},
|
|
148
|
+
lollipop: {
|
|
149
|
+
intent: "ranking",
|
|
150
|
+
mark: "rule",
|
|
151
|
+
fields: XY,
|
|
152
|
+
polar: false,
|
|
153
|
+
square: false,
|
|
154
|
+
horizontal: true,
|
|
155
|
+
stack: false,
|
|
156
|
+
color: "single",
|
|
157
|
+
legend: "none",
|
|
158
|
+
aggregating: false,
|
|
159
|
+
minRows: 1,
|
|
160
|
+
name: "lollipop chart"
|
|
161
|
+
},
|
|
162
|
+
waterfall: {
|
|
163
|
+
intent: "flow",
|
|
164
|
+
mark: "bar",
|
|
165
|
+
fields: fields({
|
|
166
|
+
x: "required",
|
|
167
|
+
y: "required"
|
|
168
|
+
}),
|
|
169
|
+
polar: false,
|
|
170
|
+
square: false,
|
|
171
|
+
horizontal: false,
|
|
172
|
+
stack: false,
|
|
173
|
+
color: "single",
|
|
174
|
+
legend: "none",
|
|
175
|
+
aggregating: false,
|
|
176
|
+
minRows: 2,
|
|
177
|
+
name: "waterfall chart"
|
|
178
|
+
},
|
|
179
|
+
line: {
|
|
180
|
+
intent: "change-over-time",
|
|
181
|
+
mark: "line",
|
|
182
|
+
fields: XY,
|
|
183
|
+
polar: false,
|
|
184
|
+
square: false,
|
|
185
|
+
horizontal: false,
|
|
186
|
+
stack: false,
|
|
187
|
+
color: "identity",
|
|
188
|
+
legend: "series",
|
|
189
|
+
aggregating: false,
|
|
190
|
+
minRows: 2,
|
|
191
|
+
name: "line chart"
|
|
192
|
+
},
|
|
193
|
+
"line-points": {
|
|
194
|
+
intent: "change-over-time",
|
|
195
|
+
mark: "line",
|
|
196
|
+
fields: XY,
|
|
197
|
+
polar: false,
|
|
198
|
+
square: false,
|
|
199
|
+
horizontal: false,
|
|
200
|
+
stack: false,
|
|
201
|
+
color: "identity",
|
|
202
|
+
legend: "series",
|
|
203
|
+
aggregating: false,
|
|
204
|
+
minRows: 2,
|
|
205
|
+
name: "line chart with point markers"
|
|
206
|
+
},
|
|
207
|
+
step: {
|
|
208
|
+
intent: "change-over-time",
|
|
209
|
+
mark: "line",
|
|
210
|
+
fields: XY,
|
|
211
|
+
polar: false,
|
|
212
|
+
square: false,
|
|
213
|
+
horizontal: false,
|
|
214
|
+
stack: false,
|
|
215
|
+
color: "identity",
|
|
216
|
+
legend: "series",
|
|
217
|
+
aggregating: false,
|
|
218
|
+
minRows: 2,
|
|
219
|
+
name: "step chart"
|
|
220
|
+
},
|
|
221
|
+
trail: {
|
|
222
|
+
intent: "change-over-time",
|
|
223
|
+
mark: "line",
|
|
224
|
+
fields: fields({
|
|
225
|
+
x: "required",
|
|
226
|
+
y: "required",
|
|
227
|
+
series: "optional",
|
|
228
|
+
size: true
|
|
229
|
+
}),
|
|
230
|
+
polar: false,
|
|
231
|
+
square: false,
|
|
232
|
+
horizontal: false,
|
|
233
|
+
stack: false,
|
|
234
|
+
color: "identity",
|
|
235
|
+
legend: "series",
|
|
236
|
+
aggregating: false,
|
|
237
|
+
minRows: 2,
|
|
238
|
+
name: "trail chart"
|
|
239
|
+
},
|
|
240
|
+
area: {
|
|
241
|
+
intent: "change-over-time",
|
|
242
|
+
mark: "area",
|
|
243
|
+
fields: XY,
|
|
244
|
+
polar: false,
|
|
245
|
+
square: false,
|
|
246
|
+
horizontal: false,
|
|
247
|
+
stack: false,
|
|
248
|
+
color: "identity",
|
|
249
|
+
legend: "series",
|
|
250
|
+
aggregating: false,
|
|
251
|
+
minRows: 2,
|
|
252
|
+
name: "area chart"
|
|
253
|
+
},
|
|
254
|
+
"area-stacked": {
|
|
255
|
+
intent: "part-to-whole",
|
|
256
|
+
mark: "area",
|
|
257
|
+
fields: fields({
|
|
258
|
+
x: "required",
|
|
259
|
+
y: "required",
|
|
260
|
+
series: "required"
|
|
261
|
+
}),
|
|
262
|
+
polar: false,
|
|
263
|
+
square: false,
|
|
264
|
+
horizontal: false,
|
|
265
|
+
stack: "zero",
|
|
266
|
+
color: "identity",
|
|
267
|
+
legend: "series",
|
|
268
|
+
aggregating: false,
|
|
269
|
+
minRows: 2,
|
|
270
|
+
name: "stacked area chart"
|
|
271
|
+
},
|
|
272
|
+
"area-normalized": {
|
|
273
|
+
intent: "part-to-whole",
|
|
274
|
+
mark: "area",
|
|
275
|
+
fields: fields({
|
|
276
|
+
x: "required",
|
|
277
|
+
y: "required",
|
|
278
|
+
series: "required"
|
|
279
|
+
}),
|
|
280
|
+
polar: false,
|
|
281
|
+
square: false,
|
|
282
|
+
horizontal: false,
|
|
283
|
+
stack: "normalize",
|
|
284
|
+
color: "identity",
|
|
285
|
+
legend: "series",
|
|
286
|
+
aggregating: false,
|
|
287
|
+
minRows: 2,
|
|
288
|
+
name: "100% stacked area chart"
|
|
289
|
+
},
|
|
290
|
+
streamgraph: {
|
|
291
|
+
intent: "change-over-time",
|
|
292
|
+
mark: "area",
|
|
293
|
+
fields: fields({
|
|
294
|
+
x: "required",
|
|
295
|
+
y: "required",
|
|
296
|
+
series: "required"
|
|
297
|
+
}),
|
|
298
|
+
polar: false,
|
|
299
|
+
square: false,
|
|
300
|
+
horizontal: false,
|
|
301
|
+
stack: "center",
|
|
302
|
+
color: "identity",
|
|
303
|
+
legend: "series",
|
|
304
|
+
aggregating: false,
|
|
305
|
+
minRows: 2,
|
|
306
|
+
name: "streamgraph"
|
|
307
|
+
},
|
|
308
|
+
slope: {
|
|
309
|
+
intent: "change-over-time",
|
|
310
|
+
mark: "line",
|
|
311
|
+
fields: fields({
|
|
312
|
+
x: "required",
|
|
313
|
+
y: "required",
|
|
314
|
+
series: "required"
|
|
315
|
+
}),
|
|
316
|
+
polar: false,
|
|
317
|
+
square: false,
|
|
318
|
+
horizontal: false,
|
|
319
|
+
stack: false,
|
|
320
|
+
color: "identity",
|
|
321
|
+
legend: "series",
|
|
322
|
+
aggregating: false,
|
|
323
|
+
minRows: 2,
|
|
324
|
+
name: "slope graph"
|
|
325
|
+
},
|
|
326
|
+
pie: {
|
|
327
|
+
intent: "part-to-whole",
|
|
328
|
+
mark: "arc",
|
|
329
|
+
fields: fields({
|
|
330
|
+
series: "required",
|
|
331
|
+
value: true
|
|
332
|
+
}),
|
|
333
|
+
polar: true,
|
|
334
|
+
square: true,
|
|
335
|
+
horizontal: false,
|
|
336
|
+
stack: "zero",
|
|
337
|
+
color: "identity",
|
|
338
|
+
legend: "series",
|
|
339
|
+
aggregating: false,
|
|
340
|
+
minRows: 2,
|
|
341
|
+
name: "pie chart"
|
|
342
|
+
},
|
|
343
|
+
donut: {
|
|
344
|
+
intent: "part-to-whole",
|
|
345
|
+
mark: "arc",
|
|
346
|
+
fields: fields({
|
|
347
|
+
series: "required",
|
|
348
|
+
value: true
|
|
349
|
+
}),
|
|
350
|
+
polar: true,
|
|
351
|
+
square: true,
|
|
352
|
+
horizontal: false,
|
|
353
|
+
stack: "zero",
|
|
354
|
+
color: "identity",
|
|
355
|
+
legend: "series",
|
|
356
|
+
aggregating: false,
|
|
357
|
+
minRows: 2,
|
|
358
|
+
name: "donut chart"
|
|
359
|
+
},
|
|
360
|
+
radial: {
|
|
361
|
+
intent: "part-to-whole",
|
|
362
|
+
mark: "arc",
|
|
363
|
+
fields: fields({
|
|
364
|
+
series: "required",
|
|
365
|
+
value: true
|
|
366
|
+
}),
|
|
367
|
+
polar: true,
|
|
368
|
+
square: true,
|
|
369
|
+
horizontal: false,
|
|
370
|
+
stack: "zero",
|
|
371
|
+
color: "identity",
|
|
372
|
+
legend: "series",
|
|
373
|
+
aggregating: false,
|
|
374
|
+
minRows: 2,
|
|
375
|
+
name: "radial plot"
|
|
376
|
+
},
|
|
377
|
+
scatter: {
|
|
378
|
+
intent: "correlation",
|
|
379
|
+
mark: "point",
|
|
380
|
+
fields: XY,
|
|
381
|
+
polar: false,
|
|
382
|
+
square: false,
|
|
383
|
+
horizontal: false,
|
|
384
|
+
stack: false,
|
|
385
|
+
color: "identity",
|
|
386
|
+
legend: "series",
|
|
387
|
+
aggregating: false,
|
|
388
|
+
minRows: 2,
|
|
389
|
+
name: "scatter plot"
|
|
390
|
+
},
|
|
391
|
+
bubble: {
|
|
392
|
+
intent: "correlation",
|
|
393
|
+
mark: "point",
|
|
394
|
+
fields: fields({
|
|
395
|
+
x: "required",
|
|
396
|
+
y: "required",
|
|
397
|
+
series: "optional",
|
|
398
|
+
size: true
|
|
399
|
+
}),
|
|
400
|
+
polar: false,
|
|
401
|
+
square: false,
|
|
402
|
+
horizontal: false,
|
|
403
|
+
stack: false,
|
|
404
|
+
color: "identity",
|
|
405
|
+
legend: "series",
|
|
406
|
+
aggregating: false,
|
|
407
|
+
minRows: 2,
|
|
408
|
+
name: "bubble plot"
|
|
409
|
+
},
|
|
410
|
+
"connected-scatter": {
|
|
411
|
+
intent: "correlation",
|
|
412
|
+
mark: "line",
|
|
413
|
+
fields: XY,
|
|
414
|
+
polar: false,
|
|
415
|
+
square: false,
|
|
416
|
+
horizontal: false,
|
|
417
|
+
stack: false,
|
|
418
|
+
color: "identity",
|
|
419
|
+
legend: "series",
|
|
420
|
+
aggregating: false,
|
|
421
|
+
minRows: 2,
|
|
422
|
+
name: "connected scatter plot"
|
|
423
|
+
},
|
|
424
|
+
"heatmap-binned": {
|
|
425
|
+
intent: "correlation",
|
|
426
|
+
mark: "rect",
|
|
427
|
+
fields: fields({
|
|
428
|
+
x: "required",
|
|
429
|
+
y: "required"
|
|
430
|
+
}),
|
|
431
|
+
polar: false,
|
|
432
|
+
square: false,
|
|
433
|
+
horizontal: false,
|
|
434
|
+
stack: false,
|
|
435
|
+
color: "magnitude",
|
|
436
|
+
legend: "ramp",
|
|
437
|
+
aggregating: true,
|
|
438
|
+
minRows: 2,
|
|
439
|
+
name: "2D histogram heatmap"
|
|
440
|
+
},
|
|
441
|
+
histogram: {
|
|
442
|
+
intent: "distribution",
|
|
443
|
+
mark: "bar",
|
|
444
|
+
fields: fields({
|
|
445
|
+
y: "required",
|
|
446
|
+
series: "optional"
|
|
447
|
+
}),
|
|
448
|
+
polar: false,
|
|
449
|
+
square: false,
|
|
450
|
+
horizontal: false,
|
|
451
|
+
stack: "zero",
|
|
452
|
+
color: "identity",
|
|
453
|
+
legend: "series",
|
|
454
|
+
aggregating: true,
|
|
455
|
+
minRows: 2,
|
|
456
|
+
name: "histogram"
|
|
457
|
+
},
|
|
458
|
+
density: {
|
|
459
|
+
intent: "distribution",
|
|
460
|
+
mark: "area",
|
|
461
|
+
fields: fields({
|
|
462
|
+
y: "required",
|
|
463
|
+
series: "optional"
|
|
464
|
+
}),
|
|
465
|
+
polar: false,
|
|
466
|
+
square: false,
|
|
467
|
+
horizontal: false,
|
|
468
|
+
stack: false,
|
|
469
|
+
color: "identity",
|
|
470
|
+
legend: "series",
|
|
471
|
+
aggregating: true,
|
|
472
|
+
minRows: 3,
|
|
473
|
+
name: "density plot"
|
|
474
|
+
},
|
|
475
|
+
boxplot: {
|
|
476
|
+
intent: "distribution",
|
|
477
|
+
mark: "boxplot",
|
|
478
|
+
fields: OBSERVATIONS,
|
|
479
|
+
polar: false,
|
|
480
|
+
square: false,
|
|
481
|
+
horizontal: false,
|
|
482
|
+
stack: false,
|
|
483
|
+
color: "identity",
|
|
484
|
+
legend: "series",
|
|
485
|
+
aggregating: true,
|
|
486
|
+
minRows: 2,
|
|
487
|
+
name: "box plot"
|
|
488
|
+
},
|
|
489
|
+
violin: {
|
|
490
|
+
intent: "distribution",
|
|
491
|
+
mark: "area",
|
|
492
|
+
fields: fields({
|
|
493
|
+
x: "required",
|
|
494
|
+
y: "required",
|
|
495
|
+
series: "optional"
|
|
496
|
+
}),
|
|
497
|
+
polar: false,
|
|
498
|
+
square: false,
|
|
499
|
+
horizontal: false,
|
|
500
|
+
stack: "center",
|
|
501
|
+
color: "identity",
|
|
502
|
+
legend: "series",
|
|
503
|
+
aggregating: true,
|
|
504
|
+
minRows: 3,
|
|
505
|
+
name: "violin plot"
|
|
506
|
+
},
|
|
507
|
+
strip: {
|
|
508
|
+
intent: "distribution",
|
|
509
|
+
mark: "tick",
|
|
510
|
+
fields: OBSERVATIONS,
|
|
511
|
+
polar: false,
|
|
512
|
+
square: false,
|
|
513
|
+
horizontal: false,
|
|
514
|
+
stack: false,
|
|
515
|
+
color: "identity",
|
|
516
|
+
legend: "series",
|
|
517
|
+
aggregating: false,
|
|
518
|
+
minRows: 1,
|
|
519
|
+
name: "strip plot"
|
|
520
|
+
},
|
|
521
|
+
dot: {
|
|
522
|
+
intent: "distribution",
|
|
523
|
+
mark: "point",
|
|
524
|
+
fields: fields({
|
|
525
|
+
x: "required",
|
|
526
|
+
y: "required",
|
|
527
|
+
series: "optional"
|
|
528
|
+
}),
|
|
529
|
+
polar: false,
|
|
530
|
+
square: false,
|
|
531
|
+
horizontal: false,
|
|
532
|
+
stack: false,
|
|
533
|
+
color: "identity",
|
|
534
|
+
legend: "series",
|
|
535
|
+
aggregating: false,
|
|
536
|
+
minRows: 1,
|
|
537
|
+
name: "dot plot"
|
|
538
|
+
},
|
|
539
|
+
errorbar: {
|
|
540
|
+
intent: "distribution",
|
|
541
|
+
mark: "rule",
|
|
542
|
+
fields: OBSERVATIONS,
|
|
543
|
+
polar: false,
|
|
544
|
+
square: false,
|
|
545
|
+
horizontal: false,
|
|
546
|
+
stack: false,
|
|
547
|
+
color: "identity",
|
|
548
|
+
legend: "series",
|
|
549
|
+
aggregating: true,
|
|
550
|
+
minRows: 2,
|
|
551
|
+
name: "error bar chart"
|
|
552
|
+
},
|
|
553
|
+
errorband: {
|
|
554
|
+
intent: "distribution",
|
|
555
|
+
mark: "band",
|
|
556
|
+
fields: OBSERVATIONS,
|
|
557
|
+
polar: false,
|
|
558
|
+
square: false,
|
|
559
|
+
horizontal: false,
|
|
560
|
+
stack: false,
|
|
561
|
+
color: "identity",
|
|
562
|
+
legend: "series",
|
|
563
|
+
aggregating: true,
|
|
564
|
+
minRows: 2,
|
|
565
|
+
name: "line chart with a confidence band"
|
|
566
|
+
},
|
|
567
|
+
heatmap: {
|
|
568
|
+
intent: "magnitude",
|
|
569
|
+
mark: "rect",
|
|
570
|
+
fields: fields({
|
|
571
|
+
x: "required",
|
|
572
|
+
y: "required",
|
|
573
|
+
value: true
|
|
574
|
+
}),
|
|
575
|
+
polar: false,
|
|
576
|
+
square: false,
|
|
577
|
+
horizontal: false,
|
|
578
|
+
stack: false,
|
|
579
|
+
color: "magnitude",
|
|
580
|
+
legend: "ramp",
|
|
581
|
+
aggregating: false,
|
|
582
|
+
minRows: 1,
|
|
583
|
+
name: "heatmap"
|
|
584
|
+
},
|
|
585
|
+
punchcard: {
|
|
586
|
+
intent: "magnitude",
|
|
587
|
+
mark: "point",
|
|
588
|
+
fields: fields({
|
|
589
|
+
x: "required",
|
|
590
|
+
y: "required",
|
|
591
|
+
value: true
|
|
592
|
+
}),
|
|
593
|
+
polar: false,
|
|
594
|
+
square: false,
|
|
595
|
+
horizontal: false,
|
|
596
|
+
stack: false,
|
|
597
|
+
color: "magnitude",
|
|
598
|
+
legend: "ramp",
|
|
599
|
+
aggregating: false,
|
|
600
|
+
minRows: 1,
|
|
601
|
+
name: "punch card"
|
|
602
|
+
},
|
|
603
|
+
radar: {
|
|
604
|
+
intent: "correlation",
|
|
605
|
+
mark: "line",
|
|
606
|
+
fields: fields({
|
|
607
|
+
x: "required",
|
|
608
|
+
y: "required",
|
|
609
|
+
series: "optional"
|
|
610
|
+
}),
|
|
611
|
+
polar: true,
|
|
612
|
+
square: false,
|
|
613
|
+
horizontal: false,
|
|
614
|
+
stack: false,
|
|
615
|
+
color: "identity",
|
|
616
|
+
legend: "series",
|
|
617
|
+
aggregating: false,
|
|
618
|
+
minRows: 3,
|
|
619
|
+
name: "radar chart"
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
/** Every form, in the docs' own order — the docs table and the type control read this. */
|
|
623
|
+
const CHART_TYPE_ORDER = Object.keys(CHART_TYPES);
|
|
624
|
+
const DEFAULT_CHART_LABELS = {
|
|
625
|
+
noData: "No data",
|
|
626
|
+
noMatch: "No data matches the current filters",
|
|
627
|
+
clearFilters: "Clear filters",
|
|
628
|
+
error: "Could not load this chart",
|
|
629
|
+
retry: "Retry",
|
|
630
|
+
insufficient: "Not enough data to draw this chart",
|
|
631
|
+
stale: "Stale",
|
|
632
|
+
legendGroup: (name) => `Series shown in ${name}`,
|
|
633
|
+
showSeries: (label) => `Show ${label}`,
|
|
634
|
+
hideSeries: (label) => `Hide ${label}`,
|
|
635
|
+
other: "Other",
|
|
636
|
+
showTable: (name) => `Show the data behind ${name}`,
|
|
637
|
+
hideTable: (name) => `Hide the data behind ${name}`,
|
|
638
|
+
tableCaption: (name) => `Every value plotted in ${name}`,
|
|
639
|
+
tableSeries: "Series",
|
|
640
|
+
tableValue: "Value",
|
|
641
|
+
tableShare: "Share",
|
|
642
|
+
tableTruncated: (shown, total) => `First ${shown} of ${total} rows.`,
|
|
643
|
+
chartOf: (form, measure, dimension) => `${form} of ${measure} by ${dimension}`,
|
|
644
|
+
describeSeries: (count, names) => `${count} series: ${names}.`,
|
|
645
|
+
describeRange: (from, to) => `From ${from} to ${to}.`,
|
|
646
|
+
describeExtremes: (maxLabel, maxValue, minLabel, minValue) => `Highest ${maxLabel} at ${maxValue}; lowest ${minLabel} at ${minValue}.`,
|
|
647
|
+
describeTotal: (total) => `Total ${total}.`,
|
|
648
|
+
describeRamp: (low, high) => `Colour runs from ${low} to ${high}.`,
|
|
649
|
+
describeAggregate: (op, measure) => `${measure} aggregated by ${op}.`,
|
|
650
|
+
describeFolded: (count, label) => `${count} smaller series folded into ${label}.`,
|
|
651
|
+
describeHidden: (count) => `${count} series hidden.`,
|
|
652
|
+
describeStale: "This data is out of date.",
|
|
653
|
+
describeLoading: (name) => `${name} is loading.`,
|
|
654
|
+
describeEmpty: (name) => `${name} has no data.`,
|
|
655
|
+
describeError: (name) => `${name} could not be loaded.`
|
|
656
|
+
};
|
|
657
|
+
function resolveChartLabels(overrides) {
|
|
658
|
+
return overrides ? {
|
|
659
|
+
...DEFAULT_CHART_LABELS,
|
|
660
|
+
...overrides
|
|
661
|
+
} : DEFAULT_CHART_LABELS;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* The state machine every card in this package shares, plus `insufficient`:
|
|
665
|
+
* rows arrived but the form needs more of them than it got.
|
|
666
|
+
*/
|
|
667
|
+
function deriveChartState({ status, hasChart, hasData, filtered }) {
|
|
668
|
+
if (status === "error") return "error";
|
|
669
|
+
if (status === "loading") return hasData ? "refreshing" : "loading";
|
|
670
|
+
if (hasChart) return "ready";
|
|
671
|
+
if (hasData) return "insufficient";
|
|
672
|
+
return filtered ? "no-match" : "empty";
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Whether the form has enough to say something. Beyond the row floor in the
|
|
676
|
+
* registry, the forms Vega aggregates need several observations INSIDE a
|
|
677
|
+
* group: a box plot of one reading per category has no interquartile range to
|
|
678
|
+
* draw, and Vega then logs an empty-extent warning rather than a chart.
|
|
679
|
+
*/
|
|
680
|
+
function hasEnoughData(type, summary) {
|
|
681
|
+
if (!summary) return false;
|
|
682
|
+
const info = CHART_TYPES[type];
|
|
683
|
+
if ((summary.hasMeasure ? summary.count : summary.rows.length) < info.minRows) return false;
|
|
684
|
+
if (!info.aggregating) return true;
|
|
685
|
+
if (summary.count < 4) return false;
|
|
686
|
+
const perGroup = /* @__PURE__ */ new Map();
|
|
687
|
+
for (const row of summary.rows) {
|
|
688
|
+
if (row.v == null) continue;
|
|
689
|
+
const key = JSON.stringify([row.s ?? "", row.x ?? ""]);
|
|
690
|
+
perGroup.set(key, (perGroup.get(key) ?? 0) + 1);
|
|
691
|
+
}
|
|
692
|
+
for (const count of perGroup.values()) if (count >= 2) return true;
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* The field names a form needs but did not get. A card missing one of these
|
|
697
|
+
* cannot draw at all, so it shows the insufficient state and says which field
|
|
698
|
+
* is absent rather than rendering an empty frame.
|
|
699
|
+
*/
|
|
700
|
+
function missingChartFields(type, provided) {
|
|
701
|
+
const { fields } = CHART_TYPES[type];
|
|
702
|
+
const missing = [];
|
|
703
|
+
if (fields.x === "required" && !provided.x) missing.push("x");
|
|
704
|
+
if (fields.y === "required" && !provided.y) missing.push("y");
|
|
705
|
+
if (fields.series === "required" && !provided.series) missing.push("series");
|
|
706
|
+
if (fields.value && !provided.value && !provided.y) missing.push("value");
|
|
707
|
+
if (fields.x2 && !provided.x2) missing.push("x2");
|
|
708
|
+
return missing;
|
|
709
|
+
}
|
|
710
|
+
const ISO_DATE = /^\d{4}-\d{2}(-\d{2})?([T ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
711
|
+
function looksTemporal(value) {
|
|
712
|
+
if (value instanceof Date) return !Number.isNaN(value.getTime());
|
|
713
|
+
return typeof value === "string" && ISO_DATE.test(value);
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Infers a field's Vega-Lite type from the first rows that actually carry it:
|
|
717
|
+
* `Date` objects and ISO-8601 strings are temporal, numbers quantitative,
|
|
718
|
+
* everything else nominal. Returns `undefined` for a field no row defines, so
|
|
719
|
+
* a caller can tell "not present" from "nominal".
|
|
720
|
+
*/
|
|
721
|
+
function inferFieldType(rows, field) {
|
|
722
|
+
if (!rows || !field) return void 0;
|
|
723
|
+
let seen = 0;
|
|
724
|
+
let temporal = 0;
|
|
725
|
+
let numeric = 0;
|
|
726
|
+
for (const row of rows) {
|
|
727
|
+
const value = row[field];
|
|
728
|
+
if (value == null) continue;
|
|
729
|
+
seen += 1;
|
|
730
|
+
if (looksTemporal(value)) temporal += 1;
|
|
731
|
+
else if (typeof value === "number") numeric += 1;
|
|
732
|
+
if (seen >= 50) break;
|
|
733
|
+
}
|
|
734
|
+
if (seen === 0) return void 0;
|
|
735
|
+
if (temporal === seen) return "temporal";
|
|
736
|
+
if (numeric === seen) return "quantitative";
|
|
737
|
+
return "nominal";
|
|
738
|
+
}
|
|
739
|
+
/** The key the folded tail takes. Never collides with a consumer key, which is a field value. */
|
|
740
|
+
const CHART_OTHER_KEY = "__other__";
|
|
741
|
+
function toNumber(value) {
|
|
742
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
743
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
744
|
+
const parsed = Number(value);
|
|
745
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
746
|
+
}
|
|
747
|
+
return null;
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Coerces a position to what Vega can plot. A temporal field becomes epoch
|
|
751
|
+
* MILLISECONDS, because a spec's `format.parse` runs only for the data the
|
|
752
|
+
* spec itself carries — rows pushed through `view.data()` after the parse
|
|
753
|
+
* arrive verbatim, so an ISO string would reach a temporal scale as text and
|
|
754
|
+
* the marks would silently not draw. The sibling cards coerce the same way.
|
|
755
|
+
*/
|
|
756
|
+
function normalizePosition(value, type) {
|
|
757
|
+
if (value == null) return value;
|
|
758
|
+
if (type !== "temporal") return value;
|
|
759
|
+
const time = value instanceof Date ? value.getTime() : new Date(String(value)).getTime();
|
|
760
|
+
return Number.isNaN(time) ? null : time;
|
|
761
|
+
}
|
|
762
|
+
function xKey(value) {
|
|
763
|
+
if (value instanceof Date) return `d:${value.getTime()}`;
|
|
764
|
+
return `${typeof value}:${String(value)}`;
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Picks the paint for series `index` under the resolved colour job. Identity
|
|
768
|
+
* slots are assigned by the series' place in the consumer's order — never by
|
|
769
|
+
* rank — so hiding or re-sorting can never repaint the survivors. Past the
|
|
770
|
+
* theme's identity budget the ramp takes over: repeating a slot would make two
|
|
771
|
+
* series look like one, and a ramp at least stays ordered and separable.
|
|
772
|
+
*/
|
|
773
|
+
function chartSeriesColorAt(mode, index, total, tokens) {
|
|
774
|
+
if (mode === "single") return tokens.emphasis;
|
|
775
|
+
if (mode === "emphasis") return index === 0 ? tokens.emphasis : tokens.context;
|
|
776
|
+
if (mode === "categorical") {
|
|
777
|
+
const slots = tokens.category.length;
|
|
778
|
+
return tokens.category[index % slots];
|
|
779
|
+
}
|
|
780
|
+
const steps = tokens.sequential.length;
|
|
781
|
+
if (total <= 1) return tokens.emphasis;
|
|
782
|
+
const first = total >= steps ? 0 : Math.max(0, steps - total);
|
|
783
|
+
const span = steps - 1 - first;
|
|
784
|
+
const at = first + Math.round(index / (total - 1) * span);
|
|
785
|
+
return tokens.sequential[Math.min(steps - 1, Math.max(0, at))];
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Resolves the colour job from the prop, the series count and the theme's own
|
|
789
|
+
* identity budget (`chart-1…N`, three hues on the stock theme). A form whose
|
|
790
|
+
* colour encodes a MEASURE — a heatmap, a punch card — always resolves to
|
|
791
|
+
* `magnitude`, whatever the prop says: colour has one job per chart, and there
|
|
792
|
+
* it is already spoken for.
|
|
793
|
+
*/
|
|
794
|
+
function resolveChartColorMode(requested, type, seriesCount, slots) {
|
|
795
|
+
if (CHART_TYPES[type].color === "magnitude") return "magnitude";
|
|
796
|
+
if (seriesCount <= 1) return "single";
|
|
797
|
+
if (requested === "categorical") return seriesCount <= slots ? "categorical" : "sequential";
|
|
798
|
+
if (requested === "sequential") return "sequential";
|
|
799
|
+
if (requested === "emphasis") return "emphasis";
|
|
800
|
+
return seriesCount <= slots ? "categorical" : "sequential";
|
|
801
|
+
}
|
|
802
|
+
const DAY_MS = 864e5;
|
|
803
|
+
/** Infers the period of a temporal axis from the closest spacing its values show. */
|
|
804
|
+
function inferGranularity(values) {
|
|
805
|
+
const times = values.map((value) => value instanceof Date ? value.getTime() : typeof value === "number" ? value : Date.parse(String(value))).filter((time) => Number.isFinite(time)).sort((a, b) => a - b);
|
|
806
|
+
if (times.length < 2) return "day";
|
|
807
|
+
let smallest = Infinity;
|
|
808
|
+
for (let at = 1; at < times.length; at += 1) {
|
|
809
|
+
const gap = times[at] - times[at - 1];
|
|
810
|
+
if (gap > 0 && gap < smallest) smallest = gap;
|
|
811
|
+
}
|
|
812
|
+
if (!Number.isFinite(smallest)) return "day";
|
|
813
|
+
if (smallest < DAY_MS) return "datetime";
|
|
814
|
+
if (smallest >= DAY_MS * 300) return "year";
|
|
815
|
+
if (smallest >= DAY_MS * 27) return "month";
|
|
816
|
+
return "day";
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Turns the consumer's records into the long-form rows the plot draws: series
|
|
820
|
+
* keys and labels resolved (a series is one distinct value of the `series`
|
|
821
|
+
* field, in first-seen order), the measure coerced to a finite number or
|
|
822
|
+
* `null`, a paint baked into every row, the tail folded, the x categories
|
|
823
|
+
* optionally ranked, and the waterfall's running total computed here rather
|
|
824
|
+
* than in Vega so the table twin and the tooltip can show the same feet and
|
|
825
|
+
* heads the bars stand on.
|
|
826
|
+
*
|
|
827
|
+
* Returns `null` when there is nothing to normalise.
|
|
828
|
+
*/
|
|
829
|
+
function normalizeChartData(data, options) {
|
|
830
|
+
if (!data) return null;
|
|
831
|
+
const info = CHART_TYPES[options.type];
|
|
832
|
+
const { x: xField, y: yField, series: seriesField, value: valueField, size: sizeField, x2: x2Field, visible, maxSeries, otherLabel, sort = "none", colorMode, tokens } = options;
|
|
833
|
+
const measureField = info.fields.value ? valueField ?? yField : yField;
|
|
834
|
+
const xType = options.xType ?? inferFieldType(data, xField) ?? "nominal";
|
|
835
|
+
const yType = options.yType ?? inferFieldType(data, yField) ?? "quantitative";
|
|
836
|
+
const order = [];
|
|
837
|
+
const seen = /* @__PURE__ */ new Set();
|
|
838
|
+
if (seriesField) for (const record of data) {
|
|
839
|
+
const raw = record[seriesField];
|
|
840
|
+
if (raw == null) continue;
|
|
841
|
+
const key = String(raw);
|
|
842
|
+
if (seen.has(key)) continue;
|
|
843
|
+
seen.add(key);
|
|
844
|
+
order.push(key);
|
|
845
|
+
}
|
|
846
|
+
const singleSeries = order.length === 0;
|
|
847
|
+
const keys = singleSeries ? [""] : order;
|
|
848
|
+
const totals = new Map(keys.map((key) => [key, 0]));
|
|
849
|
+
const counts = new Map(keys.map((key) => [key, 0]));
|
|
850
|
+
for (const record of data) {
|
|
851
|
+
const key = singleSeries ? "" : String(record[seriesField] ?? "");
|
|
852
|
+
if (!totals.has(key)) continue;
|
|
853
|
+
const measure = toNumber(measureField ? record[measureField] : null);
|
|
854
|
+
if (measure == null) continue;
|
|
855
|
+
totals.set(key, (totals.get(key) ?? 0) + measure);
|
|
856
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
857
|
+
}
|
|
858
|
+
let kept = keys;
|
|
859
|
+
let foldedKeys = /* @__PURE__ */ new Set();
|
|
860
|
+
if (maxSeries != null && maxSeries > 0 && keys.length > maxSeries) {
|
|
861
|
+
const ranked = [...keys].sort((a, b) => (totals.get(b) ?? 0) - (totals.get(a) ?? 0));
|
|
862
|
+
const survivors = new Set(ranked.slice(0, Math.max(1, maxSeries - 1)));
|
|
863
|
+
foldedKeys = new Set(keys.filter((key) => !survivors.has(key)));
|
|
864
|
+
kept = keys.filter((key) => survivors.has(key));
|
|
865
|
+
if (foldedKeys.size > 0) kept = [...kept, CHART_OTHER_KEY];
|
|
866
|
+
}
|
|
867
|
+
const labelFor = (key) => key === "__other__" ? otherLabel : key === "" ? "" : key;
|
|
868
|
+
const infos = kept.map((key, index) => ({
|
|
869
|
+
key,
|
|
870
|
+
label: labelFor(key),
|
|
871
|
+
color: chartSeriesColorAt(colorMode, index, kept.length, tokens),
|
|
872
|
+
index,
|
|
873
|
+
total: key === "__other__" ? [...foldedKeys].reduce((sum, folded) => sum + (totals.get(folded) ?? 0), 0) : totals.get(key) ?? 0,
|
|
874
|
+
count: key === "__other__" ? [...foldedKeys].reduce((sum, folded) => sum + (counts.get(folded) ?? 0), 0) : counts.get(key) ?? 0
|
|
875
|
+
}));
|
|
876
|
+
const byKey = new Map(infos.map((entry) => [entry.key, entry]));
|
|
877
|
+
const known = new Set(infos.map((entry) => entry.key));
|
|
878
|
+
const requested = visible?.filter((key) => known.has(key));
|
|
879
|
+
const shown = requested && requested.length > 0 ? new Set(requested) : known;
|
|
880
|
+
const rows = [];
|
|
881
|
+
const xSeen = /* @__PURE__ */ new Map();
|
|
882
|
+
let total = 0;
|
|
883
|
+
let count = 0;
|
|
884
|
+
let min = null;
|
|
885
|
+
let max = null;
|
|
886
|
+
for (const record of data) {
|
|
887
|
+
const rawKey = singleSeries ? "" : String(record[seriesField] ?? "");
|
|
888
|
+
const key = foldedKeys.has(rawKey) ? CHART_OTHER_KEY : rawKey;
|
|
889
|
+
const entry = byKey.get(key);
|
|
890
|
+
if (!entry) continue;
|
|
891
|
+
const xValue = xField ? normalizePosition(record[xField], xType) : void 0;
|
|
892
|
+
if (xField && xValue != null) {
|
|
893
|
+
const seenKey = xKey(xValue);
|
|
894
|
+
if (!xSeen.has(seenKey)) xSeen.set(seenKey, xValue);
|
|
895
|
+
}
|
|
896
|
+
let measure = toNumber(measureField ? record[measureField] : null);
|
|
897
|
+
if (measure == null && info.fields.x2 && x2Field && xField) {
|
|
898
|
+
const from = toNumber(xValue);
|
|
899
|
+
const to = toNumber(normalizePosition(record[x2Field], xType));
|
|
900
|
+
if (from != null && to != null) measure = to - from;
|
|
901
|
+
}
|
|
902
|
+
const yRaw = yField ? record[yField] : void 0;
|
|
903
|
+
const yValue = yType === "quantitative" ? toNumber(yRaw) : normalizePosition(yRaw, yType);
|
|
904
|
+
if (!shown.has(key)) continue;
|
|
905
|
+
const row = {
|
|
906
|
+
...xField ? { x: xValue } : {},
|
|
907
|
+
...x2Field && info.fields.x2 ? { x2: normalizePosition(record[x2Field], xType) } : {},
|
|
908
|
+
...yField ? { y: yValue ?? null } : {},
|
|
909
|
+
v: measure,
|
|
910
|
+
...sizeField && info.fields.size ? { z: toNumber(record[sizeField]) } : {},
|
|
911
|
+
s: key,
|
|
912
|
+
n: entry.label,
|
|
913
|
+
c: entry.color,
|
|
914
|
+
i: entry.index,
|
|
915
|
+
o: rows.length
|
|
916
|
+
};
|
|
917
|
+
rows.push(row);
|
|
918
|
+
if (measure != null) {
|
|
919
|
+
total += measure;
|
|
920
|
+
count += 1;
|
|
921
|
+
const label = [entry.label, xField ? String(xValue ?? "") : ""].filter(Boolean).join(" ") || String(xValue ?? "");
|
|
922
|
+
if (!max || measure > max.value) max = {
|
|
923
|
+
label,
|
|
924
|
+
value: measure
|
|
925
|
+
};
|
|
926
|
+
if (!min || measure < min.value) min = {
|
|
927
|
+
label,
|
|
928
|
+
value: measure
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
if (rows.length === 0 && infos.every((entry) => entry.count === 0) && data.length === 0) return null;
|
|
933
|
+
let xValues = [...xSeen.values()];
|
|
934
|
+
if (sort !== "none" && xField && xType !== "quantitative") {
|
|
935
|
+
const byX = /* @__PURE__ */ new Map();
|
|
936
|
+
for (const row of rows) {
|
|
937
|
+
if (row.v == null) continue;
|
|
938
|
+
const seenKey = xKey(row.x);
|
|
939
|
+
byX.set(seenKey, (byX.get(seenKey) ?? 0) + row.v);
|
|
940
|
+
}
|
|
941
|
+
xValues = xValues.sort((a, b) => {
|
|
942
|
+
const left = byX.get(xKey(a)) ?? 0;
|
|
943
|
+
const right = byX.get(xKey(b)) ?? 0;
|
|
944
|
+
return sort === "desc" ? right - left : left - right;
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
if (options.type === "waterfall") {
|
|
948
|
+
let running = 0;
|
|
949
|
+
for (const row of rows) {
|
|
950
|
+
const step = row.v ?? 0;
|
|
951
|
+
row.lo = running;
|
|
952
|
+
running += step;
|
|
953
|
+
row.hi = running;
|
|
954
|
+
row.d = step > 0 ? "up" : step < 0 ? "down" : "total";
|
|
955
|
+
row.c = step < 0 ? tokens.divergingNegative : tokens.divergingPositive;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
if (options.type === "radar") {
|
|
959
|
+
const at = new Map(xValues.map((value, index) => [xKey(value), index]));
|
|
960
|
+
const outer = max && max.value > 0 ? max.value : 1;
|
|
961
|
+
for (const row of rows) {
|
|
962
|
+
const index = at.get(xKey(row.x)) ?? 0;
|
|
963
|
+
const angle = 2 * Math.PI * index / Math.max(1, xValues.length);
|
|
964
|
+
const unit = Math.max(0, row.v ?? 0) / outer;
|
|
965
|
+
row.px = Math.sin(angle) * unit;
|
|
966
|
+
row.py = Math.cos(angle) * unit;
|
|
967
|
+
row.ai = index;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
const domain = min && max ? [min.value, max.value] : null;
|
|
971
|
+
return {
|
|
972
|
+
rows,
|
|
973
|
+
series: infos,
|
|
974
|
+
count,
|
|
975
|
+
hasMeasure: measureField != null || info.fields.x2 === true && x2Field != null,
|
|
976
|
+
xValues,
|
|
977
|
+
xType,
|
|
978
|
+
yType,
|
|
979
|
+
domain,
|
|
980
|
+
total,
|
|
981
|
+
extremes: min && max ? {
|
|
982
|
+
maxLabel: max.label,
|
|
983
|
+
maxValue: max.value,
|
|
984
|
+
minLabel: min.label,
|
|
985
|
+
minValue: min.value
|
|
986
|
+
} : null,
|
|
987
|
+
folded: foldedKeys.size,
|
|
988
|
+
colorMode,
|
|
989
|
+
granularity: xType === "temporal" ? inferGranularity(xValues) : "day"
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
/** Fractions of the outer ring a radar's web is drawn at. */
|
|
993
|
+
const RADAR_RING_FRACTIONS = [
|
|
994
|
+
.25,
|
|
995
|
+
.5,
|
|
996
|
+
.75,
|
|
997
|
+
1
|
|
998
|
+
];
|
|
999
|
+
/** Where a radar's axis labels sit, as a fraction of the outer ring. */
|
|
1000
|
+
const RADAR_LABEL_RADIUS = 1.13;
|
|
1001
|
+
/**
|
|
1002
|
+
* The radar's grid: one row per axis per ring, on the same unit plane the
|
|
1003
|
+
* polygons are drawn on. Built here rather than in the spec because the spec
|
|
1004
|
+
* must stay structural — and because a ring then stands for a real value the
|
|
1005
|
+
* description can name, not just a fraction of the frame.
|
|
1006
|
+
*/
|
|
1007
|
+
function buildRadarWebRows(summary) {
|
|
1008
|
+
if (!summary || summary.xValues.length === 0) return [];
|
|
1009
|
+
const outer = summary.extremes && summary.extremes.maxValue > 0 ? summary.extremes.maxValue : 1;
|
|
1010
|
+
const count = summary.xValues.length;
|
|
1011
|
+
const web = [];
|
|
1012
|
+
summary.xValues.forEach((x, index) => {
|
|
1013
|
+
const angle = 2 * Math.PI * index / count;
|
|
1014
|
+
const sin = Math.sin(angle);
|
|
1015
|
+
const cos = Math.cos(angle);
|
|
1016
|
+
for (const k of [0, ...RADAR_RING_FRACTIONS]) web.push({
|
|
1017
|
+
x,
|
|
1018
|
+
ai: index,
|
|
1019
|
+
k,
|
|
1020
|
+
px: sin * k,
|
|
1021
|
+
py: cos * k,
|
|
1022
|
+
lx: sin * RADAR_LABEL_RADIUS,
|
|
1023
|
+
ly: cos * RADAR_LABEL_RADIUS,
|
|
1024
|
+
r: outer * k
|
|
1025
|
+
});
|
|
1026
|
+
});
|
|
1027
|
+
return web;
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Buckets a magnitude ramp for the legend row. Vega owns the continuous scale
|
|
1031
|
+
* on the plot; this is the reader's key to it, and — like the reference
|
|
1032
|
+
* heatmap's HIGH / MODERATE / LOW row — it is the only way a colour-encoded
|
|
1033
|
+
* measure is legible without hovering.
|
|
1034
|
+
*/
|
|
1035
|
+
function buildRampBuckets(domain, tokens, format, steps = 4) {
|
|
1036
|
+
if (!domain) return [];
|
|
1037
|
+
const [low, high] = domain;
|
|
1038
|
+
const ramp = tokens.sequential;
|
|
1039
|
+
const count = Math.max(2, Math.min(steps, ramp.length));
|
|
1040
|
+
if (!(high > low)) return [{
|
|
1041
|
+
color: ramp[ramp.length - 1],
|
|
1042
|
+
from: low,
|
|
1043
|
+
to: high,
|
|
1044
|
+
label: format(low)
|
|
1045
|
+
}];
|
|
1046
|
+
const width = (high - low) / count;
|
|
1047
|
+
return Array.from({ length: count }, (_, at) => {
|
|
1048
|
+
const from = low + width * at;
|
|
1049
|
+
const to = at === count - 1 ? high : low + width * (at + 1);
|
|
1050
|
+
return {
|
|
1051
|
+
color: ramp[Math.round(at / (count - 1) * (ramp.length - 1))],
|
|
1052
|
+
from,
|
|
1053
|
+
to,
|
|
1054
|
+
label: `${format(from)}–${format(to)}`
|
|
1055
|
+
};
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* One set of formatters for the axis, the tooltip, the legend, the table and
|
|
1060
|
+
* the description, so the same value never rounds two ways (Few's precision
|
|
1061
|
+
* pitfall).
|
|
1062
|
+
*/
|
|
1063
|
+
function createChartFormatters({ format = "compact", locale, timeZone, formatValue, xType, yType, granularity }) {
|
|
1064
|
+
const value = (input) => {
|
|
1065
|
+
if (input == null || !Number.isFinite(input)) return "—";
|
|
1066
|
+
return formatValue ? formatValue(input) : formatNumber(input, format, locale);
|
|
1067
|
+
};
|
|
1068
|
+
const position = (type) => (input) => {
|
|
1069
|
+
if (input == null) return "—";
|
|
1070
|
+
if (type === "temporal") {
|
|
1071
|
+
const date = input instanceof Date ? input : new Date(typeof input === "number" ? input : String(input));
|
|
1072
|
+
return Number.isNaN(date.getTime()) ? "—" : formatDate(date, {
|
|
1073
|
+
granularity,
|
|
1074
|
+
locale,
|
|
1075
|
+
timeZone
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
if (type === "quantitative") {
|
|
1079
|
+
const numeric = toNumber(input);
|
|
1080
|
+
return numeric == null ? "—" : formatNumber(numeric, "compact", locale);
|
|
1081
|
+
}
|
|
1082
|
+
return String(input);
|
|
1083
|
+
};
|
|
1084
|
+
return {
|
|
1085
|
+
formatValue: value,
|
|
1086
|
+
formatX: position(xType),
|
|
1087
|
+
formatY: position(yType),
|
|
1088
|
+
formatShare: (input) => input == null || !Number.isFinite(input) ? "—" : formatNumber(input, "percent:1", locale)
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* The card's own accessible description (the figure's `aria-describedby`
|
|
1093
|
+
* text): what the chart is, what it covers, where its extremes are, and what
|
|
1094
|
+
* has been folded, hidden or aggregated away. Amy Cesal's alt-text formula —
|
|
1095
|
+
* chart type, what data, why it is here — generated from the props, because a
|
|
1096
|
+
* card whose face carries no text has nothing else for a screen reader to read.
|
|
1097
|
+
*/
|
|
1098
|
+
function buildChartDescription({ name, type, state, summary, visibleKeys, formatters, labels, measureName, dimensionName, aggregate, stale }) {
|
|
1099
|
+
if (state === "loading") return labels.describeLoading(name);
|
|
1100
|
+
if (state === "error") return labels.describeError(name);
|
|
1101
|
+
if (state === "empty" || state === "no-match") return labels.describeEmpty(name);
|
|
1102
|
+
const info = CHART_TYPES[type];
|
|
1103
|
+
const parts = [`${name}: ${labels.chartOf(info.name, measureName, dimensionName)}.`];
|
|
1104
|
+
if (state === "insufficient") {
|
|
1105
|
+
parts.push(labels.insufficient + ".");
|
|
1106
|
+
return parts.join(" ");
|
|
1107
|
+
}
|
|
1108
|
+
if (!summary) return parts.join(" ");
|
|
1109
|
+
if (aggregate) parts.push(labels.describeAggregate(aggregate, measureName));
|
|
1110
|
+
const named = summary.series.filter((entry) => entry.label !== "");
|
|
1111
|
+
if (named.length > 1) parts.push(labels.describeSeries(named.length, named.map((entry) => entry.label).join(", ")));
|
|
1112
|
+
if (summary.xValues.length > 1) parts.push(labels.describeRange(formatters.formatX(summary.xValues[0]), formatters.formatX(summary.xValues[summary.xValues.length - 1])));
|
|
1113
|
+
if (info.color === "magnitude" && summary.domain) parts.push(labels.describeRamp(formatters.formatValue(summary.domain[0]), formatters.formatValue(summary.domain[1])));
|
|
1114
|
+
if (summary.extremes) parts.push(labels.describeExtremes(summary.extremes.maxLabel, formatters.formatValue(summary.extremes.maxValue), summary.extremes.minLabel, formatters.formatValue(summary.extremes.minValue)));
|
|
1115
|
+
if (info.intent === "part-to-whole" && summary.count > 0) parts.push(labels.describeTotal(formatters.formatValue(summary.total)));
|
|
1116
|
+
if (summary.folded > 0) parts.push(labels.describeFolded(summary.folded, labels.other));
|
|
1117
|
+
const hidden = summary.series.length - visibleKeys.length;
|
|
1118
|
+
if (hidden > 0) parts.push(labels.describeHidden(hidden));
|
|
1119
|
+
if (stale) parts.push(labels.describeStale);
|
|
1120
|
+
return parts.join(" ");
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* The plot's own `aria-label` — set live through `view.description`, so Vega
|
|
1124
|
+
* stamps it on the `graphics-document` node. Deliberately shorter than the
|
|
1125
|
+
* card description: the figure's description already carries the detail, and
|
|
1126
|
+
* repeating it makes a screen reader say everything twice.
|
|
1127
|
+
*/
|
|
1128
|
+
function buildChartPlotDescription({ type, measureName, dimensionName, labels }) {
|
|
1129
|
+
return labels.chartOf(CHART_TYPES[type].name, measureName, dimensionName);
|
|
1130
|
+
}
|
|
1131
|
+
/** How many rows the table twin renders before it says "first N of M". */
|
|
1132
|
+
const CHART_TABLE_ROW_CAP = 200;
|
|
1133
|
+
/**
|
|
1134
|
+
* Every value the plot draws, as text — the keyboard path to the data, since
|
|
1135
|
+
* Vega gives marks none. Columns follow the form: a part-to-whole chart gets a
|
|
1136
|
+
* share column, a range bar gets both bounds, a bubble gets its size, a
|
|
1137
|
+
* waterfall gets the running total the bars stand on.
|
|
1138
|
+
*/
|
|
1139
|
+
function buildChartTableModel({ type, summary, formatters, labels, measureName, dimensionName, seriesName, sizeName, cap = 200 }) {
|
|
1140
|
+
if (!summary || summary.rows.length === 0) return null;
|
|
1141
|
+
const info = CHART_TYPES[type];
|
|
1142
|
+
const multiSeries = summary.series.some((entry) => entry.label !== "");
|
|
1143
|
+
const columns = [];
|
|
1144
|
+
if (info.fields.x !== "none") columns.push({
|
|
1145
|
+
key: "x",
|
|
1146
|
+
label: dimensionName,
|
|
1147
|
+
numeric: false
|
|
1148
|
+
});
|
|
1149
|
+
if (info.fields.x2) columns.push({
|
|
1150
|
+
key: "x2",
|
|
1151
|
+
label: `${dimensionName} end`,
|
|
1152
|
+
numeric: false
|
|
1153
|
+
});
|
|
1154
|
+
if (multiSeries) columns.push({
|
|
1155
|
+
key: "s",
|
|
1156
|
+
label: seriesName,
|
|
1157
|
+
numeric: false
|
|
1158
|
+
});
|
|
1159
|
+
if (info.fields.value && info.fields.y !== "none") columns.push({
|
|
1160
|
+
key: "y",
|
|
1161
|
+
label: "Row",
|
|
1162
|
+
numeric: false
|
|
1163
|
+
});
|
|
1164
|
+
columns.push({
|
|
1165
|
+
key: "v",
|
|
1166
|
+
label: measureName,
|
|
1167
|
+
numeric: true
|
|
1168
|
+
});
|
|
1169
|
+
if (info.fields.size && sizeName) columns.push({
|
|
1170
|
+
key: "z",
|
|
1171
|
+
label: sizeName,
|
|
1172
|
+
numeric: true
|
|
1173
|
+
});
|
|
1174
|
+
if (type === "waterfall") columns.push({
|
|
1175
|
+
key: "hi",
|
|
1176
|
+
label: "Running total",
|
|
1177
|
+
numeric: true
|
|
1178
|
+
});
|
|
1179
|
+
if (info.intent === "part-to-whole" && summary.total !== 0) columns.push({
|
|
1180
|
+
key: "share",
|
|
1181
|
+
label: labels.tableShare,
|
|
1182
|
+
numeric: true
|
|
1183
|
+
});
|
|
1184
|
+
const body = summary.rows.slice(0, cap).map((row) => {
|
|
1185
|
+
const cells = [];
|
|
1186
|
+
for (const column of columns) switch (column.key) {
|
|
1187
|
+
case "x":
|
|
1188
|
+
cells.push(formatters.formatX(row.x));
|
|
1189
|
+
break;
|
|
1190
|
+
case "x2":
|
|
1191
|
+
cells.push(formatters.formatX(row.x2));
|
|
1192
|
+
break;
|
|
1193
|
+
case "s":
|
|
1194
|
+
cells.push(row.n ?? "");
|
|
1195
|
+
break;
|
|
1196
|
+
case "y":
|
|
1197
|
+
cells.push(formatters.formatY(row.y));
|
|
1198
|
+
break;
|
|
1199
|
+
case "v":
|
|
1200
|
+
cells.push(formatters.formatValue(row.v));
|
|
1201
|
+
break;
|
|
1202
|
+
case "z":
|
|
1203
|
+
cells.push(formatters.formatValue(row.z));
|
|
1204
|
+
break;
|
|
1205
|
+
case "hi":
|
|
1206
|
+
cells.push(formatters.formatValue(row.hi));
|
|
1207
|
+
break;
|
|
1208
|
+
case "share":
|
|
1209
|
+
cells.push(row.v == null ? "—" : formatters.formatShare(row.v / summary.total));
|
|
1210
|
+
break;
|
|
1211
|
+
default: cells.push("");
|
|
1212
|
+
}
|
|
1213
|
+
return cells;
|
|
1214
|
+
});
|
|
1215
|
+
return {
|
|
1216
|
+
columns,
|
|
1217
|
+
rows: body,
|
|
1218
|
+
shown: body.length,
|
|
1219
|
+
total: summary.rows.length
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
//#endregion
|
|
1223
|
+
export { CHART_OTHER_KEY, CHART_TABLE_ROW_CAP, CHART_TYPES, CHART_TYPE_ORDER, DEFAULT_CHART_LABELS, RADAR_RING_FRACTIONS, buildChartDescription, buildChartPlotDescription, buildChartTableModel, buildRadarWebRows, buildRampBuckets, chartSeriesColorAt, createChartFormatters, deriveChartState, hasEnoughData, inferFieldType, inferGranularity, missingChartFields, normalizeChartData, resolveChartColorMode, resolveChartLabels };
|