@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/dist/index.mjs CHANGED
@@ -1,4 +1,6 @@
1
- import { $ as resolveChartPalette, A as squarifyTreemap, B as getToolboxBounds, C as chordHitTest, D as computeSunburstGeometry, E as boxplotHitTest, F as doughnutInnerRadius, G as createAnimation, H as funnelStepColor, I as matrixFieldExtent, J as isMissingValue, K as getPadding, L as scatterGroupColors, M as computeFacetGrids, N as computeMatrixCells, O as sunburstValue, P as computePieMaxRadius, Q as CHART_PALETTES, R as scatterPointPositions, S as arcHitTest, T as vennHitTest, U as getHeatmapCategories, V as calendarHitTest, W as xToCategoryIndex, X as updateAnimation, Y as resolveTickExtendedRange, Z as waterfallSteps, _ as getSliderGeometry, at as INTERACTION, b as zoomToSlice, ct as resolveCursor, et as DEFAULT_I18N_ZH, f as chartOptionsSchema, g as getDataZoomConfig, h as createSvgRecorder, it as minOf, j as buildPieSlices, k as computeBins, m as renderChart, n as buildChartPrompt, ot as applyWheelZoom, p as validateOptions, q as getTheme, rt as maxOf, s as generateChartSpec, st as pickTooltipRows, t as lintChartSpec, u as parseDataTable, v as windowToX, w as sankeyHitTest, x as ganttHitTest, y as xToPercent, z as getLegendBounds } from "./ai-B5Fk3lBQ.js";
1
+ import { $ as isMissingValue, A as vennHitTest, B as doughnutInnerRadius, C as windowToX, D as arcHitTest, E as ganttHitTest, F as squarifyTreemap, G as getToolboxBounds, H as scatterGroupColors, I as buildPieSlices, J as getHeatmapCategories, K as calendarHitTest, L as computeFacetGrids, M as computeSunburstGeometry, N as sunburstValue, O as chordHitTest, P as computeBins, Q as getTheme, R as computeMatrixCells, S as getSliderGeometry, T as zoomToSlice, U as scatterPointPositions, V as matrixFieldExtent, W as getLegendBounds, X as createAnimation, Y as xToCategoryIndex, Z as getPadding, _ as chartOptionsSchema, at as INTERACTION, b as createSvgRecorder, ct as resolveCursor, et as resolveTickExtendedRange, h as parseDataTable, it as minOf, j as boxplotHitTest, k as sankeyHitTest, l as generateChartSpec, n as buildChartPrompt, nt as waterfallSteps, ot as applyWheelZoom, q as funnelStepColor, rt as maxOf, st as pickTooltipRows, t as lintChartSpec, tt as updateAnimation, v as validateOptions, w as xToPercent, x as getDataZoomConfig, y as renderChart, z as computePieMaxRadius } from "./ai-cRi95pS2.js";
2
+ import { a as staggerProgress, i as prefersReducedMotion, n as clearSeriesPalette, r as _plugin_vue_export_helper_default, t as applySeriesPalette } from "./palette-lwVAltky.js";
3
+ import { S as DEFAULT_I18N_ZH, b as resolveChartPalette, y as CHART_PALETTES } from "./schema-CM4hKlpc.js";
2
4
  import { Fragment, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, h, inject, nextTick, normalizeClass, normalizeStyle, onMounted, onUnmounted, openBlock, provide, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, watch, withCtx } from "vue";
3
5
  //#region src/connect.js
4
6
  var connectGroups = /* @__PURE__ */ new Map();
@@ -22,13 +24,6 @@ function broadcastConnect(group, source, apply) {
22
24
  });
23
25
  }
24
26
  //#endregion
25
- //#region \0plugin-vue:export-helper
26
- var _plugin_vue_export_helper_default = (sfc, props) => {
27
- const target = sfc.__vccOpts || sfc;
28
- for (const [key, val] of props) target[key] = val;
29
- return target;
30
- };
31
- //#endregion
32
27
  //#region src/chart.vue
33
28
  var _hoisted_1 = ["tabindex", "aria-label"];
34
29
  var _hoisted_2 = {
@@ -434,10 +429,18 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
434
429
  options: props.options
435
430
  };
436
431
  const animCfg = animOverride || props.options.animation;
437
- const animEnabled = animate && animCfg?.enabled !== false;
432
+ const animEnabled = animate && animCfg?.enabled !== false && !prefersReducedMotion();
433
+ const stagger = staggerOf(effectiveOptions.value);
438
434
  animationState = createAnimation(animCfg);
439
435
  animationState.isAnimating = animEnabled;
440
- if (animEnabled) {
436
+ if (animEnabled && stagger > 0 && hasInterpolatable(effectiveOptions.value)) {
437
+ isEnterAnimating = true;
438
+ runDataTween(zeroSnapshot(effectiveOptions.value), animCfg, stagger, () => {
439
+ isEnterAnimating = false;
440
+ emit("animation-end");
441
+ emit("ready");
442
+ });
443
+ } else if (animEnabled) {
441
444
  isEnterAnimating = true;
442
445
  animateFrame();
443
446
  } else {
@@ -504,39 +507,68 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
504
507
  }
505
508
  }
506
509
  function snapshotSeriesData(opt) {
507
- const map = /* @__PURE__ */ new Map();
508
- (opt.series || []).forEach((s) => map.set(s.name, (s.data || []).map((v) => isMissingValue(v) ? 0 : v)));
509
- return map;
510
+ const series = /* @__PURE__ */ new Map();
511
+ (opt.series || []).forEach((s) => series.set(s.name, (s.data || []).map((v) => isMissingValue(v) ? 0 : v)));
512
+ const pieData = /* @__PURE__ */ new Map();
513
+ (opt.pieData || []).forEach((d) => pieData.set(d && d.name, Number.isFinite(Number(d && d.value)) ? Number(d.value) : 0));
514
+ return {
515
+ series,
516
+ pieData
517
+ };
518
+ }
519
+ /** 有没有可插值的数值面(series / pieData 任一),都没有就不该走数据级补间 */
520
+ function hasInterpolatable(opt) {
521
+ if ((opt.series || []).some((s) => Array.isArray(s.data) && s.data.length)) return true;
522
+ return (opt.pieData || []).length > 0;
510
523
  }
511
524
  function sameStructure(a, b) {
512
525
  const la = a.labels || [];
513
526
  const lb = b.labels || [];
514
527
  if (la.length !== lb.length || la.some((v, i) => v !== lb[i])) return false;
515
- return (a.series || []).map((s) => s.name).join("|") === (b.series || []).map((s) => s.name).join("|");
528
+ if ((a.series || []).map((s) => s.name).join("|") !== (b.series || []).map((s) => s.name).join("|")) return false;
529
+ return (a.pieData || []).map((d) => d && d.name).join("|") === (b.pieData || []).map((d) => d && d.name).join("|");
516
530
  }
517
- function interpolateOptions(prev, next, t) {
531
+ function interpolateOptions(prev, next, t, stagger = 0) {
518
532
  const offset = next.__sliceStart || 0;
533
+ const cols = Math.max(1, (next.labels || []).length);
519
534
  const series = (next.series || []).map((s) => {
520
- const old = prev.get(s.name);
535
+ const old = prev.series.get(s.name);
521
536
  if (!old) return s;
522
537
  const data = (s.data || []).map((v, i) => {
523
538
  if (isMissingValue(v)) return v;
524
539
  const from = old[i + offset] ?? 0;
525
- return from + (v - from) * t;
540
+ const ti = staggerProgress(t, i, cols, stagger);
541
+ return from + (v - from) * ti;
526
542
  });
527
543
  return {
528
544
  ...s,
529
545
  data
530
546
  };
531
547
  });
532
- return {
548
+ const out = {
533
549
  ...next,
534
550
  series
535
551
  };
552
+ if (Array.isArray(next.pieData)) {
553
+ const sliceCount = Math.max(1, next.pieData.length);
554
+ out.pieData = next.pieData.map((d, i) => {
555
+ const to = Number(d && d.value);
556
+ if (!Number.isFinite(to)) return d;
557
+ const from = prev.pieData.get(d && d.name) ?? 0;
558
+ const ti = staggerProgress(t, i, sliceCount, stagger);
559
+ return {
560
+ ...d,
561
+ value: from + (to - from) * ti
562
+ };
563
+ });
564
+ }
565
+ return out;
536
566
  }
537
567
  function dataSignature(opt) {
538
- const total = (opt.series || []).reduce((sum, s) => sum + (s.data || []).reduce((acc, v) => acc + (isMissingValue(v) ? 0 : v), 0), 0);
539
- return `${(opt.series || []).length}\u7CFB\u5217/${(opt.labels || []).length}\u7C7B\u76EE/\u03A3${Math.round(total)}`;
568
+ const seriesTotal = (opt.series || []).reduce((sum, s) => sum + (s.data || []).reduce((acc, v) => acc + (isMissingValue(v) ? 0 : v), 0), 0);
569
+ const pieTotal = (opt.pieData || []).reduce((sum, d) => sum + (Number.isFinite(Number(d && d.value)) ? Math.abs(Number(d.value)) : 0), 0);
570
+ const pieCount = (opt.pieData || []).length;
571
+ return `${(opt.series || []).length}\u7CFB\u5217/${pieCount}\u997C\u9879/${(opt.labels || []).length}\u7C7B\u76EE/\u03A3${Math.round(seriesTotal + pieTotal)}`;
540
572
  }
541
573
  function stopTweenAnimation() {
542
574
  if (tweenFrameId !== null) {
@@ -545,28 +577,32 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
545
577
  }
546
578
  tweenState = null;
547
579
  }
548
- function startTweenIfNeeded(prevOptions) {
549
- const nextOptions = props.options;
550
- if (nextOptions.animation?.enabled === false) return false;
551
- if (!sameStructure(prevOptions, nextOptions)) return false;
552
- const prevSnapshot = snapshotSeriesData(prevOptions);
553
- if (!(nextOptions.series || []).some((s) => {
554
- const old = prevSnapshot.get(s.name);
555
- if (!old) return false;
556
- return (s.data || []).some((v, i) => (isMissingValue(v) ? 0 : v) !== (old[i] ?? 0));
557
- })) return false;
580
+ /** 错峰占比(0 = 关闭,沿用统一 progress 的进场动画) */
581
+ function staggerOf(opt) {
582
+ const v = opt && opt.animation ? opt.animation.stagger : void 0;
583
+ return Number.isFinite(v) ? Math.max(0, Math.min(1, v)) : 0;
584
+ }
585
+ /** 全零数据快照 — 分段进场的起点(逐条长到目标值) */
586
+ function zeroSnapshot(opt) {
587
+ const series = /* @__PURE__ */ new Map();
588
+ (opt.series || []).forEach((s) => series.set(s.name, (s.data || []).map(() => 0)));
589
+ const pieData = /* @__PURE__ */ new Map();
590
+ (opt.pieData || []).forEach((d) => pieData.set(d && d.name, 0));
591
+ return {
592
+ series,
593
+ pieData
594
+ };
595
+ }
596
+ /** 数据补间主循环 — 更新补间与分段进场共用,按类目错峰插值后以 progress = 1 重绘 */
597
+ function runDataTween(fromSnapshot, cfg, stagger, onDone) {
558
598
  stopAnimation();
559
599
  stopHoverAnimation();
560
- isEnterAnimating = false;
561
600
  stopTweenAnimation();
562
- if (!canvasRef.value) return false;
563
- tweenState = createAnimation(nextOptions.animation);
564
- const fromSig = dataSignature(prevOptions);
565
- const toSig = dataSignature(nextOptions);
601
+ tweenState = createAnimation(cfg);
566
602
  function frame() {
567
603
  if (!tweenState) return;
568
604
  const isAnimating = updateAnimation(tweenState);
569
- const tweened = interpolateOptions(prevSnapshot, effectiveOptions.value, tweenState.progress);
605
+ const tweened = interpolateOptions(fromSnapshot, effectiveOptions.value, tweenState.progress, stagger);
570
606
  try {
571
607
  renderChart(canvasRef.value, buildRenderParams(1, tweened));
572
608
  } catch (err) {
@@ -577,16 +613,40 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
577
613
  else {
578
614
  tweenFrameId = null;
579
615
  tweenState = null;
580
- emit("data-update", {
581
- from: fromSig,
582
- to: toSig
583
- });
584
- emit("animation-end");
616
+ if (onDone) onDone();
585
617
  }
586
618
  }
587
619
  tweenFrameId = requestAnimationFrame(frame);
588
620
  return true;
589
621
  }
622
+ function startTweenIfNeeded(prevOptions) {
623
+ const nextOptions = props.options;
624
+ if (nextOptions.animation?.enabled === false) return false;
625
+ if (prefersReducedMotion()) return false;
626
+ if (!hasInterpolatable(nextOptions) || !sameStructure(prevOptions, nextOptions)) return false;
627
+ const prevSnapshot = snapshotSeriesData(prevOptions);
628
+ const seriesChanged = (nextOptions.series || []).some((s) => {
629
+ const old = prevSnapshot.series.get(s.name);
630
+ if (!old) return false;
631
+ return (s.data || []).some((v, i) => (isMissingValue(v) ? 0 : v) !== (old[i] ?? 0));
632
+ });
633
+ const pieChanged = (nextOptions.pieData || []).some((d) => {
634
+ const old = prevSnapshot.pieData.get(d && d.name);
635
+ const v = Number(d && d.value);
636
+ return old !== void 0 && Number.isFinite(v) && v !== old;
637
+ });
638
+ if (!seriesChanged && !pieChanged) return false;
639
+ isEnterAnimating = false;
640
+ const fromSig = dataSignature(prevOptions);
641
+ const toSig = dataSignature(nextOptions);
642
+ return runDataTween(prevSnapshot, nextOptions.animation, staggerOf(nextOptions), () => {
643
+ emit("data-update", {
644
+ from: fromSig,
645
+ to: toSig
646
+ });
647
+ emit("animation-end");
648
+ });
649
+ }
590
650
  function handleRenderError(err) {
591
651
  const msg = err instanceof Error ? err.message : "Chart render error";
592
652
  internalError.value = msg;
@@ -901,9 +961,9 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
901
961
  b.min,
902
962
  b.max,
903
963
  ...showOutliers ? b.outliers || [] : []
904
- ]);
964
+ ]).filter((v) => typeof v === "number" && Number.isFinite(v));
905
965
  const axisCfg = options.boxHorizontal ? options.xAxis || {} : options.yAxis || {};
906
- const ext = resolveTickExtendedRange(axisCfg.min ?? minOf(allValues), axisCfg.max ?? maxOf(allValues), axisCfg.ticks || 5);
966
+ const ext = resolveTickExtendedRange(axisCfg.min ?? (allValues.length ? minOf(allValues) : 0), axisCfg.max ?? (allValues.length ? maxOf(allValues) : 1), axisCfg.ticks || 5);
907
967
  return boxplotHitTest(canvasX, canvasY, plotArea, options, theme, hiddenSeries.value, {
908
968
  min: ext.min,
909
969
  max: ext.max
@@ -1204,7 +1264,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
1204
1264
  params: visibleSeries2.map((s, sIdx) => ({
1205
1265
  seriesName: s.name,
1206
1266
  name: labels2[i] || `Point ${i + 1}`,
1207
- value: s.data[i],
1267
+ value: (s.data || [])[i],
1208
1268
  color: s.color || theme.colors[options.series.indexOf(s) % theme.colors.length],
1209
1269
  dataIndex: i,
1210
1270
  seriesIndex: sIdx
@@ -1223,7 +1283,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
1223
1283
  params: visibleSeries2.map((s, sIdx) => ({
1224
1284
  seriesName: s.name,
1225
1285
  name: labels2[i],
1226
- value: s.data[i],
1286
+ value: (s.data || [])[i],
1227
1287
  color: s.color || theme.colors[options.series.indexOf(s) % theme.colors.length],
1228
1288
  dataIndex: i,
1229
1289
  seriesIndex: sIdx
@@ -1274,13 +1334,13 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
1274
1334
  if (visibleSeries.length === 0) return null;
1275
1335
  const volume = (options.volumeData || [])[dataIndex];
1276
1336
  if (props.options.tooltip?.showAllSeries !== false && visibleSeries.length > 1) {
1277
- if (visibleSeries.every((s) => isMissingValue(s.data[dataIndex]))) return null;
1337
+ if (visibleSeries.every((s) => isMissingValue((s.data || [])[dataIndex]))) return null;
1278
1338
  return {
1279
1339
  index: dataIndex,
1280
1340
  params: visibleSeries.map((s, sIdx) => ({
1281
1341
  seriesName: s.name,
1282
1342
  name: labels[dataIndex],
1283
- value: s.data[dataIndex],
1343
+ value: (s.data || [])[dataIndex],
1284
1344
  volume,
1285
1345
  color: s.color || theme.colors[options.series.indexOf(s) % theme.colors.length],
1286
1346
  dataIndex: dataIndex + sliceStartOffset(),
@@ -1289,14 +1349,14 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
1289
1349
  };
1290
1350
  } else {
1291
1351
  const series = visibleSeries[0];
1292
- if (isMissingValue(series.data[dataIndex])) return null;
1352
+ if (isMissingValue((series.data || [])[dataIndex])) return null;
1293
1353
  const color = series.color || theme.colors[0];
1294
1354
  return {
1295
1355
  index: dataIndex,
1296
1356
  params: {
1297
1357
  seriesName: series.name,
1298
1358
  name: labels[dataIndex],
1299
- value: series.data[dataIndex],
1359
+ value: (series.data || [])[dataIndex],
1300
1360
  volume,
1301
1361
  color,
1302
1362
  dataIndex: dataIndex + sliceStartOffset(),
@@ -1598,7 +1658,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
1598
1658
  tooltipVisible.value = true;
1599
1659
  redraw();
1600
1660
  }
1601
- function handlePointerMove(e) {
1661
+ function handlePointerMove(e, fromKeyboard = false) {
1602
1662
  const canvas = canvasRef.value;
1603
1663
  if (canvas) {
1604
1664
  const rect = canvas.getBoundingClientRect();
@@ -1664,9 +1724,9 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
1664
1724
  hoverAnimProgress = 1;
1665
1725
  scheduleHoverRedraw();
1666
1726
  } else scheduleHoverRedraw();
1667
- if (prevIndex === -1) {
1727
+ if (prevIndex === -1 || fromKeyboard) {
1668
1728
  const emitParams = Array.isArray(result.params) ? result.params[0] : result.params;
1669
- emit("hover", {
1729
+ if (prevIndex === -1) emit("hover", {
1670
1730
  seriesName: emitParams.seriesName,
1671
1731
  dataIndex: emitParams.dataIndex,
1672
1732
  value: emitParams.value,
@@ -1824,7 +1884,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
1824
1884
  function handleChartKeydown(e) {
1825
1885
  const options = props.options;
1826
1886
  if (!KEYNAV_TYPES.has(options.type)) return;
1827
- const count = (options.labels || []).length;
1887
+ const count = (effectiveOptions.value.labels || []).length;
1828
1888
  if (count === 0) return;
1829
1889
  if (e.key === "Enter" || e.key === " ") {
1830
1890
  e.preventDefault();
@@ -1863,7 +1923,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
1863
1923
  clientX,
1864
1924
  clientY,
1865
1925
  pointerType: "mouse"
1866
- });
1926
+ }, true);
1867
1927
  }
1868
1928
  function handleFocusOut() {
1869
1929
  if (clearTransientState()) {
@@ -1927,10 +1987,18 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
1927
1987
  if (Array.isArray(value)) return value.map(cloneSpec);
1928
1988
  const out = {};
1929
1989
  Object.keys(value).forEach((k) => {
1990
+ if (k === "__proto__") return;
1930
1991
  out[k] = cloneSpec(value[k]);
1931
1992
  });
1932
1993
  return out;
1933
1994
  }
1995
+ function assignSpec(target, source) {
1996
+ Object.keys(source).forEach((k) => {
1997
+ if (k === "__proto__") return;
1998
+ target[k] = source[k];
1999
+ });
2000
+ return target;
2001
+ }
1934
2002
  const IS_DEV = typeof import.meta !== "undefined" && false;
1935
2003
  const devWarned = /* @__PURE__ */ new Set();
1936
2004
  function runDevValidation() {
@@ -1945,13 +2013,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
1945
2013
  }
1946
2014
  let prevOptionsSnapshot = null;
1947
2015
  watch(() => props.options, (next) => {
1948
- cachedDataExtent = null;
1949
- cachedPlotArea = null;
1950
- cachedPadding = null;
1951
- scatterHitCache = null;
1952
- legendBoundsCache = null;
1953
- ganttLayoutCache = null;
1954
- sankeyLayoutCache = null;
2016
+ invalidateHitCaches();
1955
2017
  internalError.value = null;
1956
2018
  runDevValidation();
1957
2019
  if (focusSeries.value !== emphasisSeriesName.value) {
@@ -2127,16 +2189,20 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
2127
2189
  };
2128
2190
  return layout;
2129
2191
  }
2192
+ function invalidateHitCaches() {
2193
+ cachedDataExtent = null;
2194
+ cachedPlotArea = null;
2195
+ cachedPadding = null;
2196
+ scatterHitCache = null;
2197
+ legendBoundsCache = null;
2198
+ ganttLayoutCache = null;
2199
+ sankeyLayoutCache = null;
2200
+ }
2130
2201
  __expose({
2131
2202
  refresh: () => render(true),
2132
2203
  update(newOptions) {
2133
- Object.assign(props.options, newOptions);
2134
- cachedDataExtent = null;
2135
- cachedPadding = null;
2136
- scatterHitCache = null;
2137
- legendBoundsCache = null;
2138
- ganttLayoutCache = null;
2139
- sankeyLayoutCache = null;
2204
+ assignSpec(props.options, newOptions);
2205
+ invalidateHitCaches();
2140
2206
  specVersion.value++;
2141
2207
  debouncedRender(true);
2142
2208
  },
@@ -2206,7 +2272,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
2206
2272
  if (v < min) min = v;
2207
2273
  if (v > max) max = v;
2208
2274
  };
2209
- if (opt.series) opt.series.forEach((s) => s.data.forEach((v) => !isMissingValue(v) && consider(v)));
2275
+ if (opt.series) opt.series.forEach((s) => (s.data || []).forEach((v) => !isMissingValue(v) && consider(v)));
2210
2276
  if (opt.scatterData) opt.scatterData.forEach((p) => consider(p.y));
2211
2277
  if (opt.candleData) opt.candleData.forEach((c) => {
2212
2278
  consider(c.high);
@@ -2279,6 +2345,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
2279
2345
  mouseX: -1,
2280
2346
  mouseY: -1,
2281
2347
  showCrosshair: false,
2348
+ zoomRange: void 0,
2282
2349
  brushRect: null,
2283
2350
  hoveredToolbox: null,
2284
2351
  focusSeries: null
@@ -2299,7 +2366,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
2299
2366
  if (spec === null || typeof spec !== "object") return;
2300
2367
  const next = cloneSpec(spec);
2301
2368
  Object.keys(props.options).forEach((k) => delete props.options[k]);
2302
- Object.assign(props.options, next);
2369
+ assignSpec(props.options, next);
2303
2370
  hiddenSeries.value = /* @__PURE__ */ new Set();
2304
2371
  focusSeries.value = null;
2305
2372
  brushRect.value = null;
@@ -2321,8 +2388,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
2321
2388
  p: zoom.position,
2322
2389
  h: zoom.height
2323
2390
  }) : "";
2324
- cachedDataExtent = null;
2325
- cachedPlotArea = null;
2391
+ invalidateHitCaches();
2326
2392
  specVersion.value++;
2327
2393
  debouncedRender(true);
2328
2394
  },
@@ -2452,7 +2518,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
2452
2518
  ], 44, _hoisted_1);
2453
2519
  };
2454
2520
  }
2455
- }), [["__scopeId", "data-v-62da63e1"]]);
2521
+ }), [["__scopeId", "data-v-af1c6d5e"]]);
2456
2522
  //#endregion
2457
2523
  //#region src/useChart.js
2458
2524
  var USE_CHART_KEY = /* @__PURE__ */ Symbol("ev-chart-context");
@@ -2516,95 +2582,6 @@ function injectChartContext() {
2516
2582
  return inject(USE_CHART_KEY, null);
2517
2583
  }
2518
2584
  //#endregion
2519
- //#region src/palette.js
2520
- /**
2521
- * 配色方案应用工具 — 中性契约的官方实现
2522
- *
2523
- * 把一套数据色板写入 --ev-color-series-1..8 槽位令牌并派发 'ev-theme-change',
2524
- * 页面上的全部图表即时重绘。任何宿主(组件库 / 站点 / 独立页面)都可直接调用,
2525
- * 不必各自实现令牌写入与事件广播。
2526
- *
2527
- * palette 两种形态:
2528
- * - string[] 单套色值,明暗模式共用;
2529
- * - { light, dark } 浅/暗两组,随 html.dark 自动换挡(经注入样式表实现)。
2530
- *
2531
- * clearSeriesPalette() 移除覆写(内联属性 + 注入样式表),回到内置成套色板。
2532
- */
2533
- var SLOT_COUNT = 8;
2534
- var PALETTE_EVENT = "ev-theme-change";
2535
- var STYLE_ID = "data-ev-series-palette";
2536
- var paletteStyleEl = null;
2537
- function dispatchPaletteChange(dark) {
2538
- if (typeof document === "undefined") return;
2539
- const isDark = typeof dark === "boolean" ? dark : document.documentElement.classList.contains("dark");
2540
- document.documentElement.dispatchEvent(new CustomEvent(PALETTE_EVENT, {
2541
- bubbles: true,
2542
- detail: { dark: isDark }
2543
- }));
2544
- }
2545
- function removeInlineSlots() {
2546
- if (typeof document === "undefined") return;
2547
- for (let i = 1; i <= SLOT_COUNT; i++) document.documentElement.style.removeProperty(`--ev-color-series-${i}`);
2548
- }
2549
- function ensurePaletteStyleEl() {
2550
- if (typeof document === "undefined") return null;
2551
- if (!paletteStyleEl || !paletteStyleEl.isConnected) {
2552
- paletteStyleEl = document.createElement("style");
2553
- paletteStyleEl.setAttribute(STYLE_ID, "");
2554
- document.head.appendChild(paletteStyleEl);
2555
- }
2556
- return paletteStyleEl;
2557
- }
2558
- function normalizeSlots(colors) {
2559
- const out = {};
2560
- if (!Array.isArray(colors)) return out;
2561
- colors.slice(0, SLOT_COUNT).forEach((color, i) => {
2562
- if (color) out[`--ev-color-series-${i + 1}`] = color;
2563
- });
2564
- return out;
2565
- }
2566
- /**
2567
- * 应用一套数据色板
2568
- * @param {string[] | { light: string[], dark: string[] }} palette
2569
- * 数组:写入内联槽位(明暗共用);{ light, dark }:注入样式表,随 html.dark 自动换挡
2570
- * @param {{ dark?: boolean, target?: HTMLElement }} [options]
2571
- * dark 手动指定当前暗色态(默认探测 html.dark);target 改写注入目标
2572
- * @returns {boolean} 是否写入成功
2573
- */
2574
- function applySeriesPalette(palette, options = {}) {
2575
- if (typeof document === "undefined") return false;
2576
- const el = options.target ?? document.documentElement;
2577
- if (!el || !palette) return false;
2578
- if (Array.isArray(palette)) {
2579
- paletteStyleEl?.remove();
2580
- paletteStyleEl = null;
2581
- for (const [name, value] of Object.entries(normalizeSlots(palette))) el.style.setProperty(name, value);
2582
- dispatchPaletteChange(options.dark);
2583
- return true;
2584
- }
2585
- const { light, dark } = palette;
2586
- if (!Array.isArray(light) && !Array.isArray(dark)) return false;
2587
- removeInlineSlots();
2588
- const styleEl = ensurePaletteStyleEl();
2589
- if (styleEl) {
2590
- const vars = (colors) => colors.map((c, i) => `--ev-color-series-${i + 1}: ${c}`).join(";");
2591
- styleEl.textContent = `${light ? `:root { ${vars(light)}; }` : ""}${dark ? ` html.dark { ${vars(dark)}; }` : ""}`;
2592
- }
2593
- dispatchPaletteChange(options.dark);
2594
- return true;
2595
- }
2596
- /**
2597
- * 清除配色方案覆写(内联槽位 + 注入样式表),回到内置成套色板
2598
- */
2599
- function clearSeriesPalette(options = {}) {
2600
- if (typeof document === "undefined") return false;
2601
- paletteStyleEl?.remove();
2602
- paletteStyleEl = null;
2603
- removeInlineSlots();
2604
- dispatchPaletteChange(options.dark);
2605
- return true;
2606
- }
2607
- //#endregion
2608
2585
  //#region src/index.js
2609
2586
  var components = { EvChart: chart_default };
2610
2587
  function install(app, _options = {}) {
@@ -0,0 +1,117 @@
1
+ import { y as CHART_PALETTES } from "./schema-CM4hKlpc.js";
2
+ //#region src/motion.js
3
+ /**
4
+ * 系统级「减弱动态效果」是否开启
5
+ * 命中时调用方应跳过进场 / 补间 / 惯性,直接出终态图;
6
+ * SSR 或无 matchMedia 的老环境按「不减弱」处理,绝不抛错。
7
+ */
8
+ function prefersReducedMotion() {
9
+ try {
10
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
11
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
12
+ } catch {
13
+ return false;
14
+ }
15
+ }
16
+ /**
17
+ * 分段错峰:把全局 progress 映射为第 index 个条目的局部 progress
18
+ *
19
+ * count 个条目里,第 index 个从 stagger * index/(count-1) 处起步,所有条目都在
20
+ * progress = 1 时收齐;stagger = 0(或 count = 1)即全部同步,返回原 progress。
21
+ * 局部进度自带 1/(1-stagger) 的压缩,条目越多、错峰越均匀。
22
+ */
23
+ function staggerProgress(progress, index, count, stagger) {
24
+ const p = Number.isFinite(progress) ? progress : 1;
25
+ const s = Number.isFinite(stagger) ? Math.max(0, Math.min(1, stagger)) : 0;
26
+ if (!(s > 0) || !(count > 1)) return p;
27
+ const start = s * ((Number.isFinite(index) ? Math.max(0, Math.min(count - 1, index)) : 0) / (count - 1));
28
+ return Math.max(0, Math.min(1, (p - start) / (1 - s)));
29
+ }
30
+ //#endregion
31
+ //#region \0plugin-vue:export-helper
32
+ var _plugin_vue_export_helper_default = (sfc, props) => {
33
+ const target = sfc.__vccOpts || sfc;
34
+ for (const [key, val] of props) target[key] = val;
35
+ return target;
36
+ };
37
+ var PALETTE_EVENT = "ev-theme-change";
38
+ var STYLE_ID = "data-ev-series-palette";
39
+ var paletteStyleEl = null;
40
+ function dispatchPaletteChange(dark) {
41
+ if (typeof document === "undefined") return;
42
+ const isDark = typeof dark === "boolean" ? dark : document.documentElement.classList.contains("dark");
43
+ document.documentElement.dispatchEvent(new CustomEvent(PALETTE_EVENT, {
44
+ bubbles: true,
45
+ detail: { dark: isDark }
46
+ }));
47
+ }
48
+ function removeInlineSlots() {
49
+ if (typeof document === "undefined") return;
50
+ for (let i = 1; i <= 8; i++) document.documentElement.style.removeProperty(`--ev-color-series-${i}`);
51
+ }
52
+ function ensurePaletteStyleEl() {
53
+ if (typeof document === "undefined") return null;
54
+ if (!paletteStyleEl || !paletteStyleEl.isConnected) {
55
+ paletteStyleEl = document.createElement("style");
56
+ paletteStyleEl.setAttribute(STYLE_ID, "");
57
+ document.head.appendChild(paletteStyleEl);
58
+ }
59
+ return paletteStyleEl;
60
+ }
61
+ function normalizeSlots(colors) {
62
+ const out = {};
63
+ if (!Array.isArray(colors)) return out;
64
+ colors.slice(0, 8).forEach((color, i) => {
65
+ if (color) out[`--ev-color-series-${i + 1}`] = color;
66
+ });
67
+ return out;
68
+ }
69
+ /**
70
+ * 应用一套数据色板
71
+ * @param {string | string[] | { light: string[], dark: string[] }} palette
72
+ * 色系 id:按内置注册表取色;数组:写入内联槽位(明暗共用);
73
+ * { light, dark }:注入样式表,随 html.dark 自动换挡
74
+ * @param {{ dark?: boolean, target?: HTMLElement }} [options]
75
+ * dark 手动指定当前暗色态(默认探测 html.dark);target 改写注入目标
76
+ * @returns {boolean} 是否写入成功
77
+ */
78
+ function applySeriesPalette(palette, options = {}) {
79
+ if (typeof document === "undefined") return false;
80
+ const el = options.target ?? document.documentElement;
81
+ if (!el || !palette) return false;
82
+ if (typeof palette === "string") {
83
+ const found = CHART_PALETTES.find((p) => p.id === palette);
84
+ if (!found) return false;
85
+ return applySeriesPalette((typeof options.dark === "boolean" ? options.dark : document.documentElement.classList.contains("dark")) ? found.dark : found.light, options);
86
+ }
87
+ if (Array.isArray(palette)) {
88
+ paletteStyleEl?.remove();
89
+ paletteStyleEl = null;
90
+ for (const [name, value] of Object.entries(normalizeSlots(palette))) el.style.setProperty(name, value);
91
+ dispatchPaletteChange(options.dark);
92
+ return true;
93
+ }
94
+ const { light, dark } = palette;
95
+ if (!Array.isArray(light) && !Array.isArray(dark)) return false;
96
+ removeInlineSlots();
97
+ const styleEl = ensurePaletteStyleEl();
98
+ if (styleEl) {
99
+ const vars = (colors) => colors.map((c, i) => `--ev-color-series-${i + 1}: ${c}`).join(";");
100
+ styleEl.textContent = `${light ? `:root { ${vars(light)}; }` : ""}${dark ? ` html.dark { ${vars(dark)}; }` : ""}`;
101
+ }
102
+ dispatchPaletteChange(options.dark);
103
+ return true;
104
+ }
105
+ /**
106
+ * 清除配色方案覆写(内联槽位 + 注入样式表),回到内置成套色板
107
+ */
108
+ function clearSeriesPalette(options = {}) {
109
+ if (typeof document === "undefined") return false;
110
+ paletteStyleEl?.remove();
111
+ paletteStyleEl = null;
112
+ removeInlineSlots();
113
+ dispatchPaletteChange(options.dark);
114
+ return true;
115
+ }
116
+ //#endregion
117
+ export { staggerProgress as a, prefersReducedMotion as i, clearSeriesPalette as n, _plugin_vue_export_helper_default as r, applySeriesPalette as t };