@wil-works/evoke-charts 0.6.0 → 0.7.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/README.md +35 -1
- package/dist/3d.mjs +4992 -0
- package/dist/{ai-B5Fk3lBQ.js → ai-cRi95pS2.js} +383 -323
- package/dist/ai.mjs +2 -2
- package/dist/evoke-charts.css +192 -32
- package/dist/index.mjs +139 -162
- package/dist/palette-lwVAltky.js +117 -0
- package/dist/schema-CM4hKlpc.js +1102 -0
- package/package.json +4 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { C as DOWN_COLOR, D as getSeriesColors, E as formatValue, O as readChartToken, T as VOLUME_SERIES_NAME, b as resolveChartPalette, n as validateOptions3d, t as chart3dOptionsSchema, w as UP_COLOR, x as CHART_COLORS, y as CHART_PALETTES } from "./schema-CM4hKlpc.js";
|
|
1
2
|
//#region src/interactions.js
|
|
2
3
|
var INTERACTION = {
|
|
3
4
|
focusDimAlpha: .22,
|
|
@@ -97,307 +98,6 @@ function maxOf(values, ...extras) {
|
|
|
97
98
|
return max;
|
|
98
99
|
}
|
|
99
100
|
//#endregion
|
|
100
|
-
//#region src/types.js
|
|
101
|
-
var UP_COLOR = "#dc2626";
|
|
102
|
-
var DOWN_COLOR = "#16a34a";
|
|
103
|
-
var VOLUME_SERIES_NAME = "成交量";
|
|
104
|
-
var DEFAULT_I18N_ZH = {
|
|
105
|
-
tooltip: {
|
|
106
|
-
open: "开",
|
|
107
|
-
close: "收",
|
|
108
|
-
high: "高",
|
|
109
|
-
low: "低",
|
|
110
|
-
total: "总计",
|
|
111
|
-
average: "平均"
|
|
112
|
-
},
|
|
113
|
-
legend: {
|
|
114
|
-
show: "显示",
|
|
115
|
-
hide: "隐藏"
|
|
116
|
-
},
|
|
117
|
-
noData: "暂无数据"
|
|
118
|
-
};
|
|
119
|
-
/**
|
|
120
|
-
* 系列色令牌槽位 — 每槽按顺序尝试读取专用数据色令牌(--ev-color-series-N),
|
|
121
|
-
* 槽 1 回退 --ev-color-primary(品牌主色跟随);
|
|
122
|
-
* 任一槽无令牌时该槽回落内置成套色板(与语义状态色解耦的数据色板)。
|
|
123
|
-
* CHART_COLORS 仅作为 SSR / 无 DOM 环境的静态兜底。
|
|
124
|
-
*/
|
|
125
|
-
var SERIES_COLOR_SLOTS = [
|
|
126
|
-
["--ev-color-series-1", "--ev-color-primary"],
|
|
127
|
-
["--ev-color-series-2"],
|
|
128
|
-
["--ev-color-series-3"],
|
|
129
|
-
["--ev-color-series-4"],
|
|
130
|
-
["--ev-color-series-5"],
|
|
131
|
-
["--ev-color-series-6"],
|
|
132
|
-
["--ev-color-series-7"],
|
|
133
|
-
["--ev-color-series-8"]
|
|
134
|
-
];
|
|
135
|
-
function readChartToken(name, fallback) {
|
|
136
|
-
try {
|
|
137
|
-
if (typeof document === "undefined") return fallback;
|
|
138
|
-
return getComputedStyle(document.documentElement).getPropertyValue(name).trim() || fallback;
|
|
139
|
-
} catch (e) {
|
|
140
|
-
return fallback;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
function getSeriesColors(fallbackColors) {
|
|
144
|
-
return SERIES_COLOR_SLOTS.map((tokens, i) => {
|
|
145
|
-
for (const t of tokens) {
|
|
146
|
-
const v = readChartToken(t, "");
|
|
147
|
-
if (v) return v;
|
|
148
|
-
}
|
|
149
|
-
return fallbackColors[i];
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
var CHART_COLORS = {
|
|
153
|
-
/**
|
|
154
|
-
* 主色板 — 品牌蓝锚定的成套数据色板(与语义状态色解耦):
|
|
155
|
-
* 色相按取色顺序相邻差 ≥ 30°,明度带 44–68,青绿/金黄/天蓝/珊瑚/紫/石板
|
|
156
|
-
* 取材 AntV 经典系并经品牌蓝调协调;暗色板同色相提亮。
|
|
157
|
-
*/
|
|
158
|
-
primary: [
|
|
159
|
-
"#175DFF",
|
|
160
|
-
"#5AD8A6",
|
|
161
|
-
"#F6BD16",
|
|
162
|
-
"#6DC8EC",
|
|
163
|
-
"#E8684A",
|
|
164
|
-
"#9270CA",
|
|
165
|
-
"#FF9D4D",
|
|
166
|
-
"#5D7092"
|
|
167
|
-
],
|
|
168
|
-
/** 浅色系列 — primary 的 light-9/light-8/light-7 阶梯 */
|
|
169
|
-
light: [
|
|
170
|
-
"#e8f0ff",
|
|
171
|
-
"#d0e1ff",
|
|
172
|
-
"#b9d2ff"
|
|
173
|
-
],
|
|
174
|
-
/** 暗色模式系列 — 同色相提亮(对齐 evoke 暗色主色) */
|
|
175
|
-
dark: [
|
|
176
|
-
"#4d8bff",
|
|
177
|
-
"#5ad8a6",
|
|
178
|
-
"#f6bd16",
|
|
179
|
-
"#6dc8ec",
|
|
180
|
-
"#f08568",
|
|
181
|
-
"#a585e8",
|
|
182
|
-
"#ff9d4d",
|
|
183
|
-
"#8da3bf"
|
|
184
|
-
],
|
|
185
|
-
/** 热力图默认色阶(天亮蓝由浅到深) */
|
|
186
|
-
heatmapScale: [
|
|
187
|
-
"#f0f5ff",
|
|
188
|
-
"#c6d9ff",
|
|
189
|
-
"#8ab0ff",
|
|
190
|
-
"#4d86ff",
|
|
191
|
-
"#175DFF",
|
|
192
|
-
"#0040c2"
|
|
193
|
-
]
|
|
194
|
-
};
|
|
195
|
-
function formatValue(value, options) {
|
|
196
|
-
if (value === null || value === void 0 || Number.isNaN(value)) return "-";
|
|
197
|
-
const { decimals = 0, thousandSeparator = ",", prefix = "", suffix = "", abbreviate = false } = options || {};
|
|
198
|
-
let formatted;
|
|
199
|
-
if (abbreviate) {
|
|
200
|
-
const abs = Math.abs(value);
|
|
201
|
-
if (abs >= 1e8) formatted = (value / 1e8).toFixed(2) + "亿";
|
|
202
|
-
else if (abs >= 1e4) formatted = (value / 1e4).toFixed(2) + "万";
|
|
203
|
-
else if (abs >= 1e3) formatted = (value / 1e3).toFixed(2) + "K";
|
|
204
|
-
else formatted = value.toFixed(decimals);
|
|
205
|
-
} else {
|
|
206
|
-
const fixed = value.toFixed(decimals);
|
|
207
|
-
if (thousandSeparator && decimals === 0) formatted = fixed.replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator);
|
|
208
|
-
else if (thousandSeparator) {
|
|
209
|
-
const [intPart, decPart] = fixed.split(".");
|
|
210
|
-
formatted = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator) + (decPart ? "." + decPart : "");
|
|
211
|
-
} else formatted = fixed;
|
|
212
|
-
}
|
|
213
|
-
return prefix + formatted + suffix;
|
|
214
|
-
}
|
|
215
|
-
//#endregion
|
|
216
|
-
//#region src/palettes.js
|
|
217
|
-
var CHART_PALETTES = [
|
|
218
|
-
{
|
|
219
|
-
id: "classic",
|
|
220
|
-
name: "经典",
|
|
221
|
-
scene: "当前缺省色板的固化版,品牌蓝锚定;想保住默认观感不随换肤时用",
|
|
222
|
-
light: [
|
|
223
|
-
"#175DFF",
|
|
224
|
-
"#5AD8A6",
|
|
225
|
-
"#F6BD16",
|
|
226
|
-
"#6DC8EC",
|
|
227
|
-
"#E8684A",
|
|
228
|
-
"#9270CA",
|
|
229
|
-
"#FF9D4D",
|
|
230
|
-
"#5D7092"
|
|
231
|
-
],
|
|
232
|
-
dark: [
|
|
233
|
-
"#4d8bff",
|
|
234
|
-
"#5ad8a6",
|
|
235
|
-
"#f6bd16",
|
|
236
|
-
"#6dc8ec",
|
|
237
|
-
"#f08568",
|
|
238
|
-
"#a585e8",
|
|
239
|
-
"#ff9d4d",
|
|
240
|
-
"#8da3bf"
|
|
241
|
-
]
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
id: "aurora",
|
|
245
|
-
name: "极光",
|
|
246
|
-
scene: "科技 SaaS / 数据产品——冷调现代,蓝绿紫主打的仪表盘观感",
|
|
247
|
-
light: [
|
|
248
|
-
"#3B82F6",
|
|
249
|
-
"#10B981",
|
|
250
|
-
"#F59E0B",
|
|
251
|
-
"#8B5CF6",
|
|
252
|
-
"#EC4899",
|
|
253
|
-
"#06B6D4",
|
|
254
|
-
"#F97316",
|
|
255
|
-
"#64748B"
|
|
256
|
-
],
|
|
257
|
-
dark: [
|
|
258
|
-
"#60A5FA",
|
|
259
|
-
"#34D399",
|
|
260
|
-
"#FBBF24",
|
|
261
|
-
"#A78BFA",
|
|
262
|
-
"#F472B6",
|
|
263
|
-
"#22D3EE",
|
|
264
|
-
"#FB923C",
|
|
265
|
-
"#94A3B8"
|
|
266
|
-
]
|
|
267
|
-
},
|
|
268
|
-
{
|
|
269
|
-
id: "sunset",
|
|
270
|
-
name: "落日",
|
|
271
|
-
scene: "消费零售 / 生活方式 / 营销复盘——暖橙珊瑚主导,热情有温度",
|
|
272
|
-
light: [
|
|
273
|
-
"#F97316",
|
|
274
|
-
"#E11D48",
|
|
275
|
-
"#EAB308",
|
|
276
|
-
"#14B8A6",
|
|
277
|
-
"#8B5CF6",
|
|
278
|
-
"#FB7185",
|
|
279
|
-
"#0EA5E9",
|
|
280
|
-
"#A8A29E"
|
|
281
|
-
],
|
|
282
|
-
dark: [
|
|
283
|
-
"#FB923C",
|
|
284
|
-
"#FB7185",
|
|
285
|
-
"#FACC15",
|
|
286
|
-
"#2DD4BF",
|
|
287
|
-
"#A78BFA",
|
|
288
|
-
"#FDA4AF",
|
|
289
|
-
"#38BDF8",
|
|
290
|
-
"#BDB4AC"
|
|
291
|
-
]
|
|
292
|
-
},
|
|
293
|
-
{
|
|
294
|
-
id: "morandi",
|
|
295
|
-
name: "莫兰迪",
|
|
296
|
-
scene: "人文 / 咨询报告 / 印刷排版——低饱和灰调,长文配图不抢字",
|
|
297
|
-
light: [
|
|
298
|
-
"#A3AEBB",
|
|
299
|
-
"#C2A878",
|
|
300
|
-
"#B08890",
|
|
301
|
-
"#86A69D",
|
|
302
|
-
"#9A8FA8",
|
|
303
|
-
"#B7C4A0",
|
|
304
|
-
"#C98F6B",
|
|
305
|
-
"#8A8F98"
|
|
306
|
-
],
|
|
307
|
-
dark: [
|
|
308
|
-
"#B4BEC9",
|
|
309
|
-
"#D4BC8E",
|
|
310
|
-
"#C29AA3",
|
|
311
|
-
"#97B5AC",
|
|
312
|
-
"#ACA2BC",
|
|
313
|
-
"#C5D0AF",
|
|
314
|
-
"#D8A685",
|
|
315
|
-
"#9CA1A9"
|
|
316
|
-
]
|
|
317
|
-
},
|
|
318
|
-
{
|
|
319
|
-
id: "forest",
|
|
320
|
-
name: "林间",
|
|
321
|
-
scene: "健康 / 环保 / 农业供应链——自然绿主导,ESG 与可持续叙事",
|
|
322
|
-
light: [
|
|
323
|
-
"#16A34A",
|
|
324
|
-
"#0EA5E9",
|
|
325
|
-
"#CA8A04",
|
|
326
|
-
"#7C3AED",
|
|
327
|
-
"#DC2626",
|
|
328
|
-
"#0D9488",
|
|
329
|
-
"#D97706",
|
|
330
|
-
"#64748B"
|
|
331
|
-
],
|
|
332
|
-
dark: [
|
|
333
|
-
"#22C55E",
|
|
334
|
-
"#38BDF8",
|
|
335
|
-
"#EAB308",
|
|
336
|
-
"#8B5CF6",
|
|
337
|
-
"#EF4444",
|
|
338
|
-
"#14B8A6",
|
|
339
|
-
"#F59E0B",
|
|
340
|
-
"#94A3B8"
|
|
341
|
-
]
|
|
342
|
-
},
|
|
343
|
-
{
|
|
344
|
-
id: "ink",
|
|
345
|
-
name: "墨蓝",
|
|
346
|
-
scene: "金融 / 政企 / 严肃年报——克制深稳,深蓝为骨、暖色点缀",
|
|
347
|
-
light: [
|
|
348
|
-
"#1E40AF",
|
|
349
|
-
"#0F766E",
|
|
350
|
-
"#B45309",
|
|
351
|
-
"#6D28D9",
|
|
352
|
-
"#BE123C",
|
|
353
|
-
"#0369A1",
|
|
354
|
-
"#4D7C0F",
|
|
355
|
-
"#6B7280"
|
|
356
|
-
],
|
|
357
|
-
dark: [
|
|
358
|
-
"#6E9BF7",
|
|
359
|
-
"#2DD4BF",
|
|
360
|
-
"#D97706",
|
|
361
|
-
"#8B5CF6",
|
|
362
|
-
"#FB7185",
|
|
363
|
-
"#38BDF8",
|
|
364
|
-
"#84CC16",
|
|
365
|
-
"#9CA3AF"
|
|
366
|
-
]
|
|
367
|
-
},
|
|
368
|
-
{
|
|
369
|
-
id: "candy",
|
|
370
|
-
name: "糖果",
|
|
371
|
-
scene: "营销活动 / 年轻品牌 / 大屏展示——高饱和明快,视觉抓人",
|
|
372
|
-
light: [
|
|
373
|
-
"#EC4899",
|
|
374
|
-
"#8B5CF6",
|
|
375
|
-
"#F59E0B",
|
|
376
|
-
"#22C55E",
|
|
377
|
-
"#06B6D4",
|
|
378
|
-
"#EF4444",
|
|
379
|
-
"#6366F1",
|
|
380
|
-
"#9CA3AF"
|
|
381
|
-
],
|
|
382
|
-
dark: [
|
|
383
|
-
"#F472B6",
|
|
384
|
-
"#A78BFA",
|
|
385
|
-
"#FBBF24",
|
|
386
|
-
"#4ADE80",
|
|
387
|
-
"#22D3EE",
|
|
388
|
-
"#F87171",
|
|
389
|
-
"#818CF8",
|
|
390
|
-
"#A1A1AA"
|
|
391
|
-
]
|
|
392
|
-
}
|
|
393
|
-
];
|
|
394
|
-
/** 按 id 取色系在指定明暗模式下的 8 槽色值;未知 id 返回 null */
|
|
395
|
-
function resolveChartPalette(id, isDark) {
|
|
396
|
-
const palette = CHART_PALETTES.find((p) => p.id === id);
|
|
397
|
-
if (!palette) return null;
|
|
398
|
-
return (isDark ? palette.dark : palette.light).slice(0, 8);
|
|
399
|
-
}
|
|
400
|
-
//#endregion
|
|
401
101
|
//#region src/renderer/core.js
|
|
402
102
|
function estimateTextWidth(text, fontSize = 12) {
|
|
403
103
|
let width = 0;
|
|
@@ -851,9 +551,11 @@ function getPadding(options, containerWidth = 600) {
|
|
|
851
551
|
if (options.heatmap?.colorBar === true || options.heatmapColorBar === true) right2 += 44;
|
|
852
552
|
}
|
|
853
553
|
if (options.type === "bin") {
|
|
854
|
-
|
|
855
|
-
(options.series || []).forEach((s) =>
|
|
856
|
-
|
|
554
|
+
let maxFreq = 0;
|
|
555
|
+
(options.series || []).forEach((s) => {
|
|
556
|
+
const data = s.data || [];
|
|
557
|
+
for (let i = 0; i < data.length; i++) if (!isMissingValue(data[i])) maxFreq++;
|
|
558
|
+
});
|
|
857
559
|
const tickLabelWidth = String(maxFreq).length * 7;
|
|
858
560
|
left2 = pad.left ?? Math.min(90, Math.max(36, tickLabelWidth + 16));
|
|
859
561
|
bottom2 = Math.max(bottom2, 30);
|
|
@@ -2510,14 +2212,15 @@ function renderHeatmapChart(ctx) {
|
|
|
2510
2212
|
const heatmapConfig = options.heatmap || {};
|
|
2511
2213
|
const colorScale = heatmapConfig.colorScale || [...CHART_COLORS.heatmapScale];
|
|
2512
2214
|
const { xCategories, yCategories } = getHeatmapCategories(options);
|
|
2513
|
-
const values = heatmapData.map((d) => d.value);
|
|
2514
|
-
const minValue = minOf(values);
|
|
2515
|
-
const maxValue = maxOf(values);
|
|
2215
|
+
const values = heatmapData.map((d) => d.value).filter((v) => typeof v === "number" && Number.isFinite(v));
|
|
2216
|
+
const minValue = values.length ? minOf(values) : 0;
|
|
2217
|
+
const maxValue = values.length ? maxOf(values) : 1;
|
|
2516
2218
|
const valueRange = maxValue - minValue || 1;
|
|
2517
2219
|
const cellWidth = plotArea.width / xCategories.length;
|
|
2518
2220
|
const cellHeight = plotArea.height / yCategories.length;
|
|
2519
2221
|
function getColor(value, customColor) {
|
|
2520
2222
|
if (customColor) return customColor;
|
|
2223
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return colorScale[0];
|
|
2521
2224
|
const ratio = (value - minValue) / valueRange;
|
|
2522
2225
|
const index = Math.min(colorScale.length - 1, Math.floor(ratio * colorScale.length));
|
|
2523
2226
|
return colorScale[index];
|
|
@@ -3239,7 +2942,7 @@ function renderLineChart(ctx, yRange, side = "left") {
|
|
|
3239
2942
|
canvasCtx.clip();
|
|
3240
2943
|
segments.forEach(({ pts: seg, idxs }) => {
|
|
3241
2944
|
canvasCtx.beginPath();
|
|
3242
|
-
const baseValue = isStackedArea && filteredIndex > 0 ? stackedSeries.slice(0, filteredIndex).reduce((sum, s) => sum + (s.data[0] || 0), 0) : yRange.min;
|
|
2945
|
+
const baseValue = isStackedArea && filteredIndex > 0 ? stackedSeries.slice(0, filteredIndex).reduce((sum, s) => sum + (s.data[idxs[0]] || 0), 0) : yRange.min;
|
|
3243
2946
|
const baseY = isStackedArea && filteredIndex > 0 ? plotArea.y + plotArea.height - (baseValue - yRange.min) * progress / (yRange.max - yRange.min) * plotArea.height : plotArea.y + plotArea.height;
|
|
3244
2947
|
canvasCtx.moveTo(seg[0][0], baseY);
|
|
3245
2948
|
canvasCtx.lineTo(seg[0][0], seg[0][1]);
|
|
@@ -4034,7 +3737,7 @@ function renderScatterFacetChart(ctx, yRange) {
|
|
|
4034
3737
|
const y = area.y + area.height - (area.y + area.height - finalY) * yFactor;
|
|
4035
3738
|
const globalIdx = scatterData.indexOf(point);
|
|
4036
3739
|
const size = (point.size || 5) * Math.min(yFactor * 1.5, 1);
|
|
4037
|
-
const color = point.color || theme.colors[
|
|
3740
|
+
const color = point.color || theme.colors[globalIdx % theme.colors.length];
|
|
4038
3741
|
const isHover = globalIdx === hoverIndex;
|
|
4039
3742
|
canvasCtx.beginPath();
|
|
4040
3743
|
canvasCtx.arc(x, y, isHover ? size + 2 : size, 0, Math.PI * 2);
|
|
@@ -4519,7 +4222,10 @@ function computeBins(options, hiddenSeries) {
|
|
|
4519
4222
|
const binConfig = options.binConfig || {};
|
|
4520
4223
|
const visibleSeries = (options.series || []).filter((s) => !hiddenSeries.has(s.name));
|
|
4521
4224
|
const allValues = [];
|
|
4522
|
-
visibleSeries.forEach((s) =>
|
|
4225
|
+
visibleSeries.forEach((s) => {
|
|
4226
|
+
const data = s.data || [];
|
|
4227
|
+
for (let i = 0; i < data.length; i++) if (!isMissingValue(data[i])) allValues.push(data[i]);
|
|
4228
|
+
});
|
|
4523
4229
|
if (allValues.length === 0) return null;
|
|
4524
4230
|
const dataMin = minOf(allValues);
|
|
4525
4231
|
const range = maxOf(allValues) - dataMin || 1;
|
|
@@ -7058,7 +6764,7 @@ function createSvgRecorder(real) {
|
|
|
7058
6764
|
function gradientDefs() {
|
|
7059
6765
|
return gradients.map((g, i) => {
|
|
7060
6766
|
const stops = g.stops.map((s) => {
|
|
7061
|
-
let color = s.color;
|
|
6767
|
+
let color = escapeXml(String(s.color));
|
|
7062
6768
|
let opacity = 1;
|
|
7063
6769
|
const m = /^(#[0-9a-fA-F]{6})([0-9a-fA-F]{2})$/.exec(s.color);
|
|
7064
6770
|
if (m) {
|
|
@@ -7265,15 +6971,17 @@ function renderChart(canvas, params) {
|
|
|
7265
6971
|
b.min,
|
|
7266
6972
|
b.max,
|
|
7267
6973
|
...showOutliers ? b.outliers || [] : []
|
|
7268
|
-
]);
|
|
6974
|
+
]).filter((v) => typeof v === "number" && Number.isFinite(v));
|
|
6975
|
+
const valueLo = allValues.length ? minOf(allValues) : 0;
|
|
6976
|
+
const valueHi = allValues.length ? maxOf(allValues) : 1;
|
|
7269
6977
|
let range;
|
|
7270
6978
|
if (horizontal) range = renderHorizontalAxis(renderCtx, {
|
|
7271
|
-
min:
|
|
7272
|
-
max:
|
|
6979
|
+
min: valueLo,
|
|
6980
|
+
max: valueHi
|
|
7273
6981
|
});
|
|
7274
6982
|
else range = renderYAxis(renderCtx, "left", {
|
|
7275
|
-
min:
|
|
7276
|
-
max:
|
|
6983
|
+
min: valueLo,
|
|
6984
|
+
max: valueHi
|
|
7277
6985
|
});
|
|
7278
6986
|
leftRange = range;
|
|
7279
6987
|
renderBoxplotChart(renderCtx, range);
|
|
@@ -7473,7 +7181,8 @@ var chartOptionsSchema = {
|
|
|
7473
7181
|
properties: {
|
|
7474
7182
|
enabled: { type: "boolean" },
|
|
7475
7183
|
duration: { type: "number" },
|
|
7476
|
-
easing: { type: "string" }
|
|
7184
|
+
easing: { type: "string" },
|
|
7185
|
+
stagger: { type: "number" }
|
|
7477
7186
|
}
|
|
7478
7187
|
},
|
|
7479
7188
|
xAxis: {
|
|
@@ -8070,6 +7779,192 @@ function distinctCount(values) {
|
|
|
8070
7779
|
return new Set(values.filter((v) => v !== null && v !== "")).size;
|
|
8071
7780
|
}
|
|
8072
7781
|
//#endregion
|
|
7782
|
+
//#region src/ai/threed.js
|
|
7783
|
+
var THREE_HINT = /三维|立体|3d/i;
|
|
7784
|
+
function wantsThreed(hint = {}) {
|
|
7785
|
+
if (hint.intent === "threed") return true;
|
|
7786
|
+
const text = [hint.title, hint.hint].filter(Boolean).join(" ");
|
|
7787
|
+
return THREE_HINT.test(text);
|
|
7788
|
+
}
|
|
7789
|
+
function pivotGrid(cx, cy, cz) {
|
|
7790
|
+
const rows = cx.values.length;
|
|
7791
|
+
if (rows < 4) return null;
|
|
7792
|
+
const xs = [...new Set(cx.values.filter(Number.isFinite))].sort((a, b) => a - b);
|
|
7793
|
+
const ys = [...new Set(cy.values.filter(Number.isFinite))].sort((a, b) => a - b);
|
|
7794
|
+
if (xs.length < 2 || ys.length < 2) return null;
|
|
7795
|
+
if (xs.length * ys.length !== rows) return null;
|
|
7796
|
+
if (xs.length > 64 || ys.length > 64) return {
|
|
7797
|
+
tooLarge: true,
|
|
7798
|
+
x: xs.length,
|
|
7799
|
+
y: ys.length
|
|
7800
|
+
};
|
|
7801
|
+
const z = Array.from({ length: ys.length }, () => new Array(xs.length).fill(null));
|
|
7802
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7803
|
+
for (let i = 0; i < rows; i++) {
|
|
7804
|
+
const xi = xs.indexOf(cx.values[i]);
|
|
7805
|
+
const yi = ys.indexOf(cy.values[i]);
|
|
7806
|
+
if (xi < 0 || yi < 0) return null;
|
|
7807
|
+
const key = yi * xs.length + xi;
|
|
7808
|
+
if (seen.has(key)) return null;
|
|
7809
|
+
seen.add(key);
|
|
7810
|
+
z[yi][xi] = cz.values[i];
|
|
7811
|
+
}
|
|
7812
|
+
return {
|
|
7813
|
+
x: xs,
|
|
7814
|
+
y: ys,
|
|
7815
|
+
z
|
|
7816
|
+
};
|
|
7817
|
+
}
|
|
7818
|
+
function planThreed({ hint, intent, numbers, times, cats, table, labels }) {
|
|
7819
|
+
const asked = wantsThreed(hint);
|
|
7820
|
+
const natural = !asked && cats.length === 0 && times.length === 0 && numbers.length >= 3;
|
|
7821
|
+
if (!asked && !natural) return null;
|
|
7822
|
+
if (cats.length === 0 && times.length === 0) {
|
|
7823
|
+
if (numbers.length < 3) return {
|
|
7824
|
+
spec: null,
|
|
7825
|
+
report: [{
|
|
7826
|
+
level: "warn",
|
|
7827
|
+
message: "三维散点/曲面需要至少三个数值维度,已回退二维选型"
|
|
7828
|
+
}]
|
|
7829
|
+
};
|
|
7830
|
+
const [cx, cy, cz] = numbers;
|
|
7831
|
+
const grid = pivotGrid(cx, cy, cz);
|
|
7832
|
+
if (grid && !grid.tooLarge) return {
|
|
7833
|
+
spec: {
|
|
7834
|
+
type: "surface3d",
|
|
7835
|
+
surfaceData: {
|
|
7836
|
+
x: grid.x,
|
|
7837
|
+
y: grid.y,
|
|
7838
|
+
z: grid.z
|
|
7839
|
+
},
|
|
7840
|
+
xAxis: { name: cx.name },
|
|
7841
|
+
yAxis: { name: cy.name },
|
|
7842
|
+
zAxis: { name: cz.name }
|
|
7843
|
+
},
|
|
7844
|
+
report: [{
|
|
7845
|
+
level: "info",
|
|
7846
|
+
message: `三维曲面:${cx.name} × ${cy.name} 构成 ${grid.x.length}×${grid.y.length} 完整高度场(${cz.name})`
|
|
7847
|
+
}]
|
|
7848
|
+
};
|
|
7849
|
+
const report = [];
|
|
7850
|
+
if (grid && grid.tooLarge) report.push({
|
|
7851
|
+
level: "warn",
|
|
7852
|
+
message: `数值网格 ${grid.x}×${grid.y} 过大(任一边 > 64),曲面 spec 会爆炸,已改用三维散点`
|
|
7853
|
+
});
|
|
7854
|
+
return {
|
|
7855
|
+
spec: {
|
|
7856
|
+
type: "scatter3d",
|
|
7857
|
+
scatterData: table.rows.map((_, i) => ({
|
|
7858
|
+
x: cx.values[i],
|
|
7859
|
+
y: cy.values[i],
|
|
7860
|
+
z: cz.values[i]
|
|
7861
|
+
})),
|
|
7862
|
+
xAxis: { name: cx.name },
|
|
7863
|
+
yAxis: { name: cy.name },
|
|
7864
|
+
zAxis: { name: cz.name }
|
|
7865
|
+
},
|
|
7866
|
+
report: [...report, {
|
|
7867
|
+
level: "info",
|
|
7868
|
+
message: `三维散点:${cx.name} / ${cy.name} / ${cz.name} 三轴空间(二维散点会丢一维)`
|
|
7869
|
+
}]
|
|
7870
|
+
};
|
|
7871
|
+
}
|
|
7872
|
+
if (numbers.length === 0) return null;
|
|
7873
|
+
if (intent === "relation" && numbers.length >= 3) {
|
|
7874
|
+
const [cx, cy, cz] = numbers;
|
|
7875
|
+
const cat = cats[0];
|
|
7876
|
+
return {
|
|
7877
|
+
spec: {
|
|
7878
|
+
type: "scatter3d",
|
|
7879
|
+
scatterData: table.rows.map((_, i) => ({
|
|
7880
|
+
x: cx.values[i],
|
|
7881
|
+
y: cy.values[i],
|
|
7882
|
+
z: cz.values[i],
|
|
7883
|
+
label: cat ? String(cat.values[i]) : String(i + 1)
|
|
7884
|
+
})),
|
|
7885
|
+
xAxis: { name: cx.name },
|
|
7886
|
+
yAxis: { name: cy.name },
|
|
7887
|
+
zAxis: { name: cz.name }
|
|
7888
|
+
},
|
|
7889
|
+
report: [{
|
|
7890
|
+
level: "info",
|
|
7891
|
+
message: `三维散点:${cx.name} / ${cy.name} / ${cz.name} 三轴空间,${cat ? `「${cat.name}」作点标签` : "行号作点标签"}`
|
|
7892
|
+
}]
|
|
7893
|
+
};
|
|
7894
|
+
}
|
|
7895
|
+
const seriesList = numbers.slice(0, 8).map((c) => ({
|
|
7896
|
+
name: c.name,
|
|
7897
|
+
data: c.values
|
|
7898
|
+
}));
|
|
7899
|
+
const report = [];
|
|
7900
|
+
if (numbers.length > 8) report.push({
|
|
7901
|
+
level: "warn",
|
|
7902
|
+
message: `数值列超过 8 个,仅取前 8 列(${seriesList.map((s) => s.name).join("、")})`
|
|
7903
|
+
});
|
|
7904
|
+
if (intent === "share") {
|
|
7905
|
+
if (seriesList.length === 1 && labels.length <= 8) {
|
|
7906
|
+
const pieData = labels.map((name, i) => ({
|
|
7907
|
+
name,
|
|
7908
|
+
value: seriesList[0].data[i]
|
|
7909
|
+
})).filter((d) => typeof d.value === "number" && d.value > 0);
|
|
7910
|
+
return {
|
|
7911
|
+
spec: {
|
|
7912
|
+
type: "pie3d",
|
|
7913
|
+
pieData
|
|
7914
|
+
},
|
|
7915
|
+
report: [...report, {
|
|
7916
|
+
level: "info",
|
|
7917
|
+
message: `三维饼:${seriesList[0].name} 按类目占比(${pieData.length} 项)`
|
|
7918
|
+
}]
|
|
7919
|
+
};
|
|
7920
|
+
}
|
|
7921
|
+
if (seriesList.length >= 2 && seriesList.length <= 8) {
|
|
7922
|
+
const sums = seriesList.map((s) => s.data.reduce((acc, v) => typeof v === "number" && Number.isFinite(v) ? acc + v : acc, 0));
|
|
7923
|
+
if (sums.every((v) => v > 0)) return {
|
|
7924
|
+
spec: {
|
|
7925
|
+
type: "pie3d",
|
|
7926
|
+
pieData: seriesList.map((s, i) => ({
|
|
7927
|
+
name: s.name,
|
|
7928
|
+
value: sums[i]
|
|
7929
|
+
}))
|
|
7930
|
+
},
|
|
7931
|
+
report: [...report, {
|
|
7932
|
+
level: "info",
|
|
7933
|
+
message: `三维饼:按度量合计占比(${sums.length} 项)`
|
|
7934
|
+
}]
|
|
7935
|
+
};
|
|
7936
|
+
}
|
|
7937
|
+
}
|
|
7938
|
+
const text = [hint.title, hint.hint].filter(Boolean).join("");
|
|
7939
|
+
const isTrend = !/柱|条形/.test(text) && (times.length > 0 || intent === "trend");
|
|
7940
|
+
const spec = {
|
|
7941
|
+
type: isTrend ? "line3d" : "bar3d",
|
|
7942
|
+
labels,
|
|
7943
|
+
series: seriesList
|
|
7944
|
+
};
|
|
7945
|
+
if (seriesList.length > 1) spec.legend = { show: true };
|
|
7946
|
+
if (seriesList.length === 1) spec.zAxis = { name: seriesList[0].name };
|
|
7947
|
+
report.push({
|
|
7948
|
+
level: "info",
|
|
7949
|
+
message: isTrend ? "三维空间折线:维度有序看走势,落地投影与面带保留空间形态" : "三维柱林:类目 × 系列的空间量值对比"
|
|
7950
|
+
});
|
|
7951
|
+
return {
|
|
7952
|
+
spec,
|
|
7953
|
+
report
|
|
7954
|
+
};
|
|
7955
|
+
}
|
|
7956
|
+
var SPEC_RULES_3D = [
|
|
7957
|
+
"type 必填,取值限定三维 schema enum:bar3d / line3d / scatter3d / surface3d / pie3d",
|
|
7958
|
+
"bar3d / line3d 用 labels(X 类目)+ series(Y 系列,data 为标量数组),series[].data 长度必须与 labels 一致,系列建议 ≤5(过多互相遮挡)",
|
|
7959
|
+
"scatter3d 用 scatterData:{ x, y, z, label? } 三元组数组,三轴均为数值",
|
|
7960
|
+
"surface3d 用 surfaceData:{ x: 列值数组, y: 行值数组, z: 行×列矩阵 },z.length 必须等于 y.length 且每行长度等于 x.length",
|
|
7961
|
+
"pie3d 用 pieData:{ name, value } 数组,扇区 ≤8 项,正值才参与",
|
|
7962
|
+
"渲染组件是 <ev-chart3d>(来自 @wil-works/evoke-charts/3d 子入口);二维专属字段(annotations / dataZoom / yAxisRight / markLines 等)在三维 schema 中不存在,不要使用",
|
|
7963
|
+
"相机交互默认已含拖拽环绕、滚轮缩放与双击复位,一般无需配置 camera;展厅氛围场景才设 camera.autoRotate: true",
|
|
7964
|
+
"palette / theme / colors 与二维同一套:色系 id 限定三维 schema enum;theme.colors 与 palette 同时出现时以 theme.colors 为准",
|
|
7965
|
+
"数值格式化用 valueFormatter(函数)或 i18n 文案对象;不使用三维 schema 之外的字段"
|
|
7966
|
+
];
|
|
7967
|
+
//#endregion
|
|
8073
7968
|
//#region src/ai/spec.js
|
|
8074
7969
|
var INTENT_KEYWORDS = {
|
|
8075
7970
|
trend: [
|
|
@@ -8202,6 +8097,25 @@ function generateChartSpec(data, hint = {}) {
|
|
|
8202
8097
|
}];
|
|
8203
8098
|
const spec = {};
|
|
8204
8099
|
if (hint.title) spec.title = hint.title;
|
|
8100
|
+
const three = planThreed({
|
|
8101
|
+
hint,
|
|
8102
|
+
intent,
|
|
8103
|
+
numbers,
|
|
8104
|
+
times,
|
|
8105
|
+
cats,
|
|
8106
|
+
table,
|
|
8107
|
+
labels
|
|
8108
|
+
});
|
|
8109
|
+
if (three) {
|
|
8110
|
+
report.push(...three.report);
|
|
8111
|
+
if (three.spec) {
|
|
8112
|
+
Object.assign(spec, three.spec);
|
|
8113
|
+
return {
|
|
8114
|
+
spec,
|
|
8115
|
+
report
|
|
8116
|
+
};
|
|
8117
|
+
}
|
|
8118
|
+
}
|
|
8205
8119
|
if (intent === "relation" && numbers.length >= 2) {
|
|
8206
8120
|
const cat = cats[0];
|
|
8207
8121
|
spec.type = "scatter";
|
|
@@ -8630,22 +8544,151 @@ var SPEC_EXAMPLES = [
|
|
|
8630
8544
|
function formatExamples(maxCount = SPEC_EXAMPLES.length) {
|
|
8631
8545
|
return SPEC_EXAMPLES.slice(0, maxCount).map((e) => `需求:${e.requirement}\nSpec:${JSON.stringify(e.spec)}`).join("\n\n");
|
|
8632
8546
|
}
|
|
8547
|
+
var SPEC_EXAMPLES_3D = [
|
|
8548
|
+
{
|
|
8549
|
+
requirement: "三维看温度、湿度与设备故障率的关系",
|
|
8550
|
+
spec: {
|
|
8551
|
+
type: "scatter3d",
|
|
8552
|
+
title: "温度-湿度-故障率空间分布",
|
|
8553
|
+
scatterData: [
|
|
8554
|
+
{
|
|
8555
|
+
x: 22,
|
|
8556
|
+
y: 40,
|
|
8557
|
+
z: 1.2,
|
|
8558
|
+
label: "产线 A"
|
|
8559
|
+
},
|
|
8560
|
+
{
|
|
8561
|
+
x: 28,
|
|
8562
|
+
y: 55,
|
|
8563
|
+
z: 2.8,
|
|
8564
|
+
label: "产线 B"
|
|
8565
|
+
},
|
|
8566
|
+
{
|
|
8567
|
+
x: 31,
|
|
8568
|
+
y: 62,
|
|
8569
|
+
z: 4.5,
|
|
8570
|
+
label: "产线 C"
|
|
8571
|
+
},
|
|
8572
|
+
{
|
|
8573
|
+
x: 25,
|
|
8574
|
+
y: 48,
|
|
8575
|
+
z: 1.9,
|
|
8576
|
+
label: "产线 D"
|
|
8577
|
+
}
|
|
8578
|
+
],
|
|
8579
|
+
xAxis: { name: "温度(℃)" },
|
|
8580
|
+
yAxis: { name: "湿度(%)" },
|
|
8581
|
+
zAxis: { name: "故障率(%)" }
|
|
8582
|
+
}
|
|
8583
|
+
},
|
|
8584
|
+
{
|
|
8585
|
+
requirement: "转速和扭矩共同影响下的产出效率曲面",
|
|
8586
|
+
spec: {
|
|
8587
|
+
type: "surface3d",
|
|
8588
|
+
title: "转速-扭矩-效率高度场",
|
|
8589
|
+
surfaceData: {
|
|
8590
|
+
x: [
|
|
8591
|
+
800,
|
|
8592
|
+
1200,
|
|
8593
|
+
1600,
|
|
8594
|
+
2e3
|
|
8595
|
+
],
|
|
8596
|
+
y: [
|
|
8597
|
+
100,
|
|
8598
|
+
150,
|
|
8599
|
+
200
|
|
8600
|
+
],
|
|
8601
|
+
z: [
|
|
8602
|
+
[
|
|
8603
|
+
61,
|
|
8604
|
+
64,
|
|
8605
|
+
66,
|
|
8606
|
+
63
|
|
8607
|
+
],
|
|
8608
|
+
[
|
|
8609
|
+
67,
|
|
8610
|
+
72,
|
|
8611
|
+
75,
|
|
8612
|
+
70
|
|
8613
|
+
],
|
|
8614
|
+
[
|
|
8615
|
+
64,
|
|
8616
|
+
69,
|
|
8617
|
+
71,
|
|
8618
|
+
66
|
|
8619
|
+
]
|
|
8620
|
+
]
|
|
8621
|
+
},
|
|
8622
|
+
xAxis: { name: "转速(rpm)" },
|
|
8623
|
+
yAxis: { name: "扭矩(N·m)" },
|
|
8624
|
+
zAxis: { name: "效率(%)" }
|
|
8625
|
+
}
|
|
8626
|
+
},
|
|
8627
|
+
{
|
|
8628
|
+
requirement: "三维柱林对比四个季度的区域销量",
|
|
8629
|
+
spec: {
|
|
8630
|
+
type: "bar3d",
|
|
8631
|
+
title: "区域季度销量柱林",
|
|
8632
|
+
labels: [
|
|
8633
|
+
"一季度",
|
|
8634
|
+
"二季度",
|
|
8635
|
+
"三季度",
|
|
8636
|
+
"四季度"
|
|
8637
|
+
],
|
|
8638
|
+
series: [
|
|
8639
|
+
{
|
|
8640
|
+
name: "华东",
|
|
8641
|
+
data: [
|
|
8642
|
+
320,
|
|
8643
|
+
356,
|
|
8644
|
+
401,
|
|
8645
|
+
388
|
|
8646
|
+
]
|
|
8647
|
+
},
|
|
8648
|
+
{
|
|
8649
|
+
name: "华南",
|
|
8650
|
+
data: [
|
|
8651
|
+
280,
|
|
8652
|
+
302,
|
|
8653
|
+
346,
|
|
8654
|
+
331
|
|
8655
|
+
]
|
|
8656
|
+
},
|
|
8657
|
+
{
|
|
8658
|
+
name: "华北",
|
|
8659
|
+
data: [
|
|
8660
|
+
240,
|
|
8661
|
+
262,
|
|
8662
|
+
288,
|
|
8663
|
+
305
|
|
8664
|
+
]
|
|
8665
|
+
}
|
|
8666
|
+
],
|
|
8667
|
+
legend: { show: true },
|
|
8668
|
+
zAxis: { name: "销量(万)" }
|
|
8669
|
+
}
|
|
8670
|
+
}
|
|
8671
|
+
];
|
|
8672
|
+
function formatExamples3d(maxCount = SPEC_EXAMPLES_3D.length) {
|
|
8673
|
+
return SPEC_EXAMPLES_3D.slice(0, maxCount).map((e) => `需求:${e.requirement}\nSpec:${JSON.stringify(e.spec)}`).join("\n\n");
|
|
8674
|
+
}
|
|
8633
8675
|
//#endregion
|
|
8634
8676
|
//#region src/ai/prompt.js
|
|
8635
8677
|
var PREVIEW_ROWS = 8;
|
|
8636
|
-
function buildChartPrompt({ data, requirement, extraRules = [], examples = true } = {}) {
|
|
8678
|
+
function buildChartPrompt({ data, requirement, extraRules = [], examples = true, mode = "2d" } = {}) {
|
|
8679
|
+
const is3d = mode === "3d";
|
|
8637
8680
|
const table = parseDataTable(data);
|
|
8638
8681
|
let digest;
|
|
8639
8682
|
if (table) digest = `${table.headers.join(" | ")}\n${table.rows.slice(0, PREVIEW_ROWS).map((r) => table.headers.map((h) => r[h] === null || r[h] === void 0 ? "" : String(r[h])).join(" | ")).join("\n")}${table.rows.length > PREVIEW_ROWS ? `\n…共 ${table.rows.length} 行` : ""}`;
|
|
8640
8683
|
else if (data !== void 0 && data !== null) digest = typeof data === "string" ? data : JSON.stringify(data);
|
|
8641
8684
|
else digest = "(未提供数据,请基于需求虚构合理示例数据)";
|
|
8642
|
-
const rules = [...SPEC_RULES, ...extraRules];
|
|
8685
|
+
const rules = [...is3d ? SPEC_RULES_3D : SPEC_RULES, ...extraRules];
|
|
8643
8686
|
const sections = [
|
|
8644
|
-
"你是图表配置生成器。根据下面的数据与需求,产出 EvChart 的 options JSON(下称 Spec),它将被 <ev-chart :options> 直接渲染。",
|
|
8645
|
-
`## Options Schema\n${JSON.stringify(chartOptionsSchema)}`,
|
|
8687
|
+
is3d ? "你是三维图表配置生成器。根据下面的数据与需求,产出 EvChart3d 的 options JSON(下称 Spec),它将被 <ev-chart3d :options> 直接渲染(组件来自 @wil-works/evoke-charts/3d 子入口)。" : "你是图表配置生成器。根据下面的数据与需求,产出 EvChart 的 options JSON(下称 Spec),它将被 <ev-chart :options> 直接渲染。",
|
|
8688
|
+
`## Options Schema\n${JSON.stringify(is3d ? chart3dOptionsSchema : chartOptionsSchema)}`,
|
|
8646
8689
|
`## 数据预览\n${digest}`
|
|
8647
8690
|
];
|
|
8648
|
-
if (examples) sections.push(`## 示例\n${formatExamples()}`);
|
|
8691
|
+
if (examples) sections.push(`## 示例\n${is3d ? formatExamples3d() : formatExamples()}`);
|
|
8649
8692
|
sections.push(requirement ? `## 需求\n${requirement}` : "## 需求\n根据数据形状自行选择最合适的图表类型,并拟一个不超过 12 字的标题。");
|
|
8650
8693
|
sections.push(`## 硬性规则\n${rules.map((r, i) => `${i + 1}. ${r}`).join("\n")}`);
|
|
8651
8694
|
return sections.join("\n\n");
|
|
@@ -8754,6 +8797,23 @@ function lintChartSpec(spec, opts = {}) {
|
|
|
8754
8797
|
}],
|
|
8755
8798
|
spec: fixed
|
|
8756
8799
|
};
|
|
8800
|
+
if (typeof fixed.type === "string" && /3d$/.test(fixed.type)) {
|
|
8801
|
+
const { warnings } = validateOptions3d(fixed);
|
|
8802
|
+
warnings.forEach((w) => issues.push({
|
|
8803
|
+
level: "error",
|
|
8804
|
+
message: `${w.path} ${w.message}`,
|
|
8805
|
+
path: w.path
|
|
8806
|
+
}));
|
|
8807
|
+
if (!issues.length) issues.push({
|
|
8808
|
+
level: "info",
|
|
8809
|
+
message: "三维 spec 已过 schema 校验(几何层自检仅覆盖二维图型)",
|
|
8810
|
+
rule: "threed-schema-only"
|
|
8811
|
+
});
|
|
8812
|
+
return {
|
|
8813
|
+
issues,
|
|
8814
|
+
spec: fixed
|
|
8815
|
+
};
|
|
8816
|
+
}
|
|
8757
8817
|
const { warnings } = validateOptions(fixed);
|
|
8758
8818
|
warnings.forEach((w) => issues.push({
|
|
8759
8819
|
level: "error",
|
|
@@ -8827,4 +8887,4 @@ function lintChartSpec(spec, opts = {}) {
|
|
|
8827
8887
|
};
|
|
8828
8888
|
}
|
|
8829
8889
|
//#endregion
|
|
8830
|
-
export {
|
|
8890
|
+
export { isMissingValue as $, vennHitTest as A, doughnutInnerRadius as B, windowToX as C, arcHitTest as D, ganttHitTest as E, squarifyTreemap as F, getToolboxBounds as G, scatterGroupColors as H, buildPieSlices as I, getHeatmapCategories as J, calendarHitTest as K, computeFacetGrids as L, computeSunburstGeometry as M, sunburstValue as N, chordHitTest as O, computeBins as P, getTheme as Q, computeMatrixCells as R, getSliderGeometry as S, zoomToSlice as T, scatterPointPositions as U, matrixFieldExtent as V, getLegendBounds as W, createAnimation as X, xToCategoryIndex as Y, getPadding as Z, chartOptionsSchema as _, formatExamples as a, INTERACTION as at, createSvgRecorder as b, detectIntent as c, resolveCursor as ct, planThreed as d, resolveTickExtendedRange as et, wantsThreed as f, parseNumeric as g, parseDataTable as h, SPEC_EXAMPLES_3D as i, minOf as it, boxplotHitTest as j, sankeyHitTest as k, generateChartSpec as l, looksLikeTime as m, buildChartPrompt as n, waterfallSteps as nt, formatExamples3d as o, applyWheelZoom as ot, inferColumns as p, funnelStepColor as q, SPEC_EXAMPLES as r, maxOf as rt, SPEC_RULES as s, pickTooltipRows as st, lintChartSpec as t, updateAnimation as tt, SPEC_RULES_3D as u, validateOptions as v, xToPercent as w, getDataZoomConfig as x, renderChart as y, computePieMaxRadius as z };
|