@agions/taroviz 1.7.0 → 1.9.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/esm/index.js CHANGED
@@ -346,6 +346,21 @@ var H5Adapter = /** @class */function () {
346
346
  this.instance = null;
347
347
  }
348
348
  };
349
+ /**
350
+ * 触发图表行为
351
+ */
352
+ H5Adapter.prototype.dispatchAction = function (payload) {
353
+ if (this.instance) {
354
+ this.instance.dispatchAction(payload);
355
+ }
356
+ };
357
+ /**
358
+ * 获取DataURL
359
+ */
360
+ H5Adapter.prototype.getDataURL = function (opts) {
361
+ var _a;
362
+ return (_a = this.instance) === null || _a === void 0 ? void 0 : _a.getDataURL(opts);
363
+ };
349
364
  /**
350
365
  * 处理图表大小变化
351
366
  */
@@ -685,16 +700,12 @@ var ChartEventType;
685
700
  ChartEventType["BRUSHSELECTED"] = "brushselected";
686
701
  ChartEventType["RENDERED"] = "rendered";
687
702
  ChartEventType["FINISHED"] = "finished";
688
- // 自定义事件
689
703
  ChartEventType["CHART_READY"] = "chartReady";
690
704
  ChartEventType["CHART_RESIZE"] = "chartResize";
691
705
  ChartEventType["CHART_ERROR"] = "chartError";
692
706
  ChartEventType["CHART_DISPOSE"] = "chartDispose";
693
707
  })(ChartEventType || (ChartEventType = {}));
694
708
  ;// ./src/core/types/platform.ts
695
- /**
696
- * 支持的平台类型
697
- */
698
709
  var PlatformType;
699
710
  (function (PlatformType) {
700
711
  PlatformType["H5"] = "h5";
@@ -1579,43 +1590,171 @@ var __generator = undefined && undefined.__generator || function (thisArg, body)
1579
1590
  /**
1580
1591
  * 基础图表包装组件
1581
1592
  * 提供统一的图表初始化、渲染和生命周期管理
1593
+ *
1594
+ * 无障碍支持 (WCAG):
1595
+ * - role="application" + keyboard navigation for zoom/pan
1596
+ * - Hidden data table with aria-live for screen readers
1597
+ * - Respects prefers-reduced-motion
1582
1598
  */
1583
1599
 
1584
1600
 
1585
1601
 
1586
1602
 
1587
- /**
1588
- * 基础图表包装组件
1589
- * 提供统一的图表初始化、渲染和生命周期管理
1590
- */
1603
+ /** Extract series data from an ECharts option for screen reader exposure */
1604
+ function extractSeriesData(option) {
1605
+ var opt = option;
1606
+ if (!(opt === null || opt === void 0 ? void 0 : opt.series) || !Array.isArray(opt.series)) return [];
1607
+ return opt.series.filter(function (s) {
1608
+ return (s === null || s === void 0 ? void 0 : s.data) && Array.isArray(s.data);
1609
+ }).map(function (s) {
1610
+ return {
1611
+ name: s.name || '系列',
1612
+ data: s.data
1613
+ };
1614
+ });
1615
+ }
1616
+ /** Build a human-readable aria-label from chart option */
1617
+ function buildAriaLabel(chartType, option) {
1618
+ var seriesData = extractSeriesData(option);
1619
+ if (!seriesData.length) {
1620
+ return chartType === 'chart' ? '空图表' : "".concat(chartType, " \u7A7A\u56FE\u8868");
1621
+ }
1622
+ var totalPoints = seriesData.reduce(function (sum, s) {
1623
+ return sum + s.data.length;
1624
+ }, 0);
1625
+ var seriesNames = seriesData.map(function (s) {
1626
+ return s.name;
1627
+ }).join('、');
1628
+ return "".concat(chartType, "\u56FE\u8868\uFF0C\u5305\u542B").concat(seriesData.length, "\u4E2A\u7CFB\u5217\uFF08").concat(seriesNames, "\uFF09\uFF0C\u5171").concat(totalPoints, "\u4E2A\u6570\u636E\u70B9");
1629
+ }
1630
+ // ─── Keyboard navigation step sizes ───────────────────────────────────────
1631
+ var ZOOM_STEP = 5; // % per key press
1632
+ var PAN_STEP = 10; // % pan per arrow key
1591
1633
  var BaseChartWrapper = function (_a) {
1634
+ var _b, _c;
1592
1635
  var option = _a.option,
1593
- _b = _a.width,
1594
- width = _b === void 0 ? '100%' : _b,
1595
- _c = _a.height,
1596
- height = _c === void 0 ? '300px' : _c,
1636
+ _d = _a.width,
1637
+ width = _d === void 0 ? '100%' : _d,
1638
+ _e = _a.height,
1639
+ height = _e === void 0 ? '300px' : _e,
1597
1640
  theme = _a.theme,
1598
- _d = _a.style,
1599
- style = _d === void 0 ? {} : _d,
1600
- _e = _a.className,
1601
- className = _e === void 0 ? '' : _e,
1602
- _f = _a.autoResize,
1603
- autoResize = _f === void 0 ? true : _f,
1604
- _g = _a.loading,
1605
- loading = _g === void 0 ? false : _g,
1641
+ _f = _a.style,
1642
+ style = _f === void 0 ? {} : _f,
1643
+ _g = _a.className,
1644
+ className = _g === void 0 ? '' : _g,
1645
+ _h = _a.autoResize,
1646
+ autoResize = _h === void 0 ? true : _h,
1647
+ _j = _a.loading,
1648
+ loading = _j === void 0 ? false : _j,
1606
1649
  loadingOption = _a.loadingOption,
1607
1650
  onChartInit = _a.onChartInit,
1608
1651
  onChartReady = _a.onChartReady,
1609
- _h = _a.renderer,
1610
- renderer = _h === void 0 ? 'canvas' : _h,
1611
- _j = _a.onEvents,
1612
- onEvents = _j === void 0 ? {} : _j,
1613
- _k = _a.chartType,
1614
- chartType = _k === void 0 ? 'chart' : _k;
1652
+ _k = _a.renderer,
1653
+ renderer = _k === void 0 ? 'canvas' : _k,
1654
+ _l = _a.onEvents,
1655
+ onEvents = _l === void 0 ? {} : _l,
1656
+ _m = _a.chartType,
1657
+ chartType = _m === void 0 ? 'chart' : _m;
1615
1658
  var chartId = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)("".concat(chartType, "-").concat((0,_core_utils__WEBPACK_IMPORTED_MODULE_2__/* .uuid */ .uR)()));
1616
1659
  var chartInstance = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
1617
1660
  var containerRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
1618
- // 使用 useMemo 缓存适配器配置,并处理类型问题
1661
+ var isMountedRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(true);
1662
+ var cleanupRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
1663
+ var tableId = (0,react__WEBPACK_IMPORTED_MODULE_0__.useId)(); // unique id for aria-describedby
1664
+ var seriesData = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(function () {
1665
+ return extractSeriesData(option);
1666
+ }, [option]);
1667
+ var ariaLabel = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(function () {
1668
+ return buildAriaLabel(chartType, option);
1669
+ }, [chartType, option]);
1670
+ // Keyboard handler for zoom/pan — attached to the chart container
1671
+ var handleKeyDown = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(function (e) {
1672
+ var _a, _b, _c, _d, _e, _f;
1673
+ var instance = chartInstance.current;
1674
+ if (!instance) return;
1675
+ // ECharts dataZoom dispatch — works for any chart with dataZoom axis
1676
+ var dispatchZoom = function (startDelta, endDelta) {
1677
+ instance.dispatchAction({
1678
+ type: 'dataZoom',
1679
+ startDelta: startDelta,
1680
+ endDelta: endDelta
1681
+ });
1682
+ };
1683
+ // Home = reset zoom to full range
1684
+ if (e.key === 'Home') {
1685
+ e.preventDefault();
1686
+ instance.dispatchAction({
1687
+ type: 'dataZoom',
1688
+ start: 0,
1689
+ end: 100
1690
+ });
1691
+ return;
1692
+ }
1693
+ switch (e.key) {
1694
+ case '+':
1695
+ case '=':
1696
+ {
1697
+ e.preventDefault();
1698
+ // Zoom in (narrow range) — decrease end by ZOOM_STEP
1699
+ var end = instance.getOption();
1700
+ var dz = (_a = end === null || end === void 0 ? void 0 : end.dataZoom) === null || _a === void 0 ? void 0 : _a[0];
1701
+ if (dz) {
1702
+ var newEnd = Math.max(0, ((_b = dz.end) !== null && _b !== void 0 ? _b : 100) - ZOOM_STEP);
1703
+ var newStart = Math.max(0, ((_c = dz.start) !== null && _c !== void 0 ? _c : 0) - ZOOM_STEP);
1704
+ instance.dispatchAction({
1705
+ type: 'dataZoom',
1706
+ start: newStart,
1707
+ end: newEnd
1708
+ });
1709
+ }
1710
+ break;
1711
+ }
1712
+ case '-':
1713
+ case '_':
1714
+ {
1715
+ e.preventDefault();
1716
+ // Zoom out (expand range) — increase end by ZOOM_STEP
1717
+ var end = instance.getOption();
1718
+ var dz = (_d = end === null || end === void 0 ? void 0 : end.dataZoom) === null || _d === void 0 ? void 0 : _d[0];
1719
+ if (dz) {
1720
+ var newEnd = Math.min(100, ((_e = dz.end) !== null && _e !== void 0 ? _e : 100) + ZOOM_STEP);
1721
+ var newStart = Math.min(((_f = dz.start) !== null && _f !== void 0 ? _f : 0) + ZOOM_STEP, newEnd);
1722
+ instance.dispatchAction({
1723
+ type: 'dataZoom',
1724
+ start: newStart,
1725
+ end: newEnd
1726
+ });
1727
+ }
1728
+ break;
1729
+ }
1730
+ case 'ArrowLeft':
1731
+ {
1732
+ e.preventDefault();
1733
+ dispatchZoom(-PAN_STEP, 0);
1734
+ break;
1735
+ }
1736
+ case 'ArrowRight':
1737
+ {
1738
+ e.preventDefault();
1739
+ dispatchZoom(PAN_STEP, 0);
1740
+ break;
1741
+ }
1742
+ case 'ArrowUp':
1743
+ {
1744
+ e.preventDefault();
1745
+ dispatchZoom(0, -PAN_STEP);
1746
+ break;
1747
+ }
1748
+ case 'ArrowDown':
1749
+ {
1750
+ e.preventDefault();
1751
+ dispatchZoom(0, PAN_STEP);
1752
+ break;
1753
+ }
1754
+ // No default — let other keys pass through for accessibility tools
1755
+ }
1756
+ }, []);
1757
+ // Use memo to cache adapter config
1619
1758
  var adapterConfig = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(function () {
1620
1759
  return (0,_utils__WEBPACK_IMPORTED_MODULE_3__/* .processAdapterConfig */ .X)({
1621
1760
  canvasId: chartId.current,
@@ -1628,8 +1767,9 @@ var BaseChartWrapper = function (_a) {
1628
1767
  option: option
1629
1768
  });
1630
1769
  }, [width, height, theme, autoResize, renderer, option]);
1631
- // 处理图表初始化
1770
+ // Handle chart initialization
1632
1771
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {
1772
+ isMountedRef.current = true;
1633
1773
  var initChart = function () {
1634
1774
  return __awaiter(void 0, void 0, void 0, function () {
1635
1775
  var initConfig, adapter;
@@ -1638,18 +1778,21 @@ var BaseChartWrapper = function (_a) {
1638
1778
  case 0:
1639
1779
  initConfig = (0,_utils__WEBPACK_IMPORTED_MODULE_3__/* .processAdapterConfig */ .X)(__assign(__assign({}, adapterConfig), {
1640
1780
  onInit: function (instance) {
1781
+ if (!isMountedRef.current) {
1782
+ instance.dispose();
1783
+ return;
1784
+ }
1641
1785
  chartInstance.current = instance;
1642
- // 绑定事件
1643
1786
  if (onEvents) {
1644
- Object.keys(onEvents).forEach(function (eventName) {
1645
- instance.on(eventName, onEvents[eventName]);
1787
+ Object.entries(onEvents).forEach(function (_a) {
1788
+ var eventName = _a[0],
1789
+ handler = _a[1];
1790
+ instance.on(eventName, handler);
1646
1791
  });
1647
1792
  }
1648
- // 初始化回调
1649
1793
  if (onChartInit) {
1650
1794
  onChartInit(instance);
1651
1795
  }
1652
- // 准备好回调
1653
1796
  if (onChartReady) {
1654
1797
  onChartReady(instance);
1655
1798
  }
@@ -1658,41 +1801,43 @@ var BaseChartWrapper = function (_a) {
1658
1801
  return [4 /*yield*/, (0,_adapters__WEBPACK_IMPORTED_MODULE_1__/* .getAdapter */ .cK)(initConfig)];
1659
1802
  case 1:
1660
1803
  adapter = _a.sent();
1804
+ if (!isMountedRef.current) {
1805
+ return [2 /*return*/];
1806
+ }
1661
1807
  adapter.init();
1662
- // 返回清理函数
1663
- return [2 /*return*/, function () {
1808
+ cleanupRef.current = function () {
1664
1809
  if (chartInstance.current) {
1665
- // 解绑事件
1666
1810
  if (onEvents) {
1667
- Object.keys(onEvents).forEach(function (eventName) {
1668
- var _a;
1669
- (_a = chartInstance.current) === null || _a === void 0 ? void 0 : _a.off(eventName);
1811
+ Object.entries(onEvents).forEach(function (_a) {
1812
+ var eventName = _a[0];
1813
+ chartInstance.current.off(eventName);
1670
1814
  });
1671
1815
  }
1672
1816
  chartInstance.current.dispose();
1673
1817
  chartInstance.current = null;
1674
1818
  }
1675
- }];
1819
+ };
1820
+ return [2 /*return*/];
1676
1821
  }
1677
1822
  });
1678
1823
  });
1679
1824
  };
1680
- // 执行异步初始化并获取清理函数
1681
- var cleanupPromise = initChart();
1682
- // 返回清理函数
1825
+ initChart();
1683
1826
  return function () {
1684
- cleanupPromise.then(function (cleanup) {
1685
- return cleanup === null || cleanup === void 0 ? void 0 : cleanup();
1686
- });
1827
+ isMountedRef.current = false;
1828
+ if (cleanupRef.current) {
1829
+ cleanupRef.current();
1830
+ cleanupRef.current = null;
1831
+ }
1687
1832
  };
1688
1833
  }, [adapterConfig, onChartInit, onChartReady, onEvents]);
1689
- // 更新配置
1834
+ // Update config
1690
1835
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {
1691
1836
  if (chartInstance.current && option) {
1692
1837
  chartInstance.current.setOption(option, true);
1693
1838
  }
1694
1839
  }, [option]);
1695
- // 控制加载状态
1840
+ // Loading state
1696
1841
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {
1697
1842
  if (chartInstance.current) {
1698
1843
  if (loading) {
@@ -1702,16 +1847,51 @@ var BaseChartWrapper = function (_a) {
1702
1847
  }
1703
1848
  }
1704
1849
  }, [loading, loadingOption]);
1705
- // 自定义样式
1850
+ // Merged style
1706
1851
  var mergedStyle = __assign({
1707
1852
  width: typeof width === 'number' ? "".concat(width, "px") : width,
1708
1853
  height: typeof height === 'number' ? "".concat(height, "px") : height
1709
1854
  }, style);
1710
- return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
1855
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("table", {
1856
+ id: tableId,
1857
+ "aria-label": "".concat(chartType, " \u56FE\u8868\u6570\u636E"),
1858
+ style: {
1859
+ position: 'absolute',
1860
+ width: 1,
1861
+ height: 1,
1862
+ overflow: 'hidden',
1863
+ clip: 'rect(0,0,0,0)',
1864
+ clipPath: 'inset(50%)',
1865
+ whiteSpace: 'nowrap'
1866
+ },
1867
+ "aria-live": "polite",
1868
+ "aria-atomic": "false"
1869
+ }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("caption", null, ariaLabel), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("thead", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("tr", null, seriesData.map(function (s, i) {
1870
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("th", {
1871
+ key: i,
1872
+ scope: "col"
1873
+ }, s.name);
1874
+ }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("tbody", null, Array.from({
1875
+ length: Math.min(20, (_c = (_b = seriesData[0]) === null || _b === void 0 ? void 0 : _b.data.length) !== null && _c !== void 0 ? _c : 0)
1876
+ }).map(function (_, rowIdx) {
1877
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("tr", {
1878
+ key: rowIdx
1879
+ }, seriesData.map(function (s, colIdx) {
1880
+ var _a;
1881
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("td", {
1882
+ key: colIdx
1883
+ }, String((_a = s.data[rowIdx]) !== null && _a !== void 0 ? _a : ''));
1884
+ }));
1885
+ }))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
1711
1886
  className: "taroviz-".concat(chartType, " ").concat(className),
1712
1887
  style: mergedStyle,
1713
- ref: containerRef
1714
- });
1888
+ ref: containerRef,
1889
+ role: "application",
1890
+ "aria-label": ariaLabel,
1891
+ "aria-describedby": tableId,
1892
+ tabIndex: 0,
1893
+ onKeyDown: handleKeyDown
1894
+ }));
1715
1895
  };
1716
1896
  /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (BaseChartWrapper);
1717
1897
 
@@ -2212,20 +2392,31 @@ var __assign = undefined && undefined.__assign || function () {
2212
2392
  return __assign.apply(this, arguments);
2213
2393
  };
2214
2394
  /**
2215
- * 动画预设集合
2395
+ * Checks the OS/browser prefers-reduced-motion setting.
2396
+ * Returns true if the user has requested reduced motion.
2397
+ */
2398
+ function prefersReducedMotion() {
2399
+ if (typeof window === 'undefined') return false;
2400
+ return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
2401
+ }
2402
+ /**
2403
+ * Professional animation presets following frontend-design-pro skill guidelines:
2404
+ * - Easing: cubic-bezier(0.16, 1, 0.3, 1) ("cubicOut") for natural deceleration
2405
+ * - Durations: 100-200ms micro, 300-500ms transitions, never >600ms
2406
+ * - NO bounce/elastic — anti-patterns that feel廉价 (cheap)
2216
2407
  */
2217
2408
  var DEFAULT_ANIMATION_PRESETS = [{
2218
2409
  name: 'default',
2219
- description: '默认动画配置',
2410
+ description: '默认动画配置 — 专业级 (300-500ms, cubicOut)',
2220
2411
  config: {
2221
2412
  enabled: true,
2222
- duration: 1000,
2413
+ duration: 400,
2223
2414
  easing: 'cubicOut',
2224
- appearDuration: 1200,
2415
+ appearDuration: 450,
2225
2416
  appearEasing: 'cubicOut',
2226
- updateDuration: 800,
2417
+ updateDuration: 300,
2227
2418
  updateEasing: 'cubicOut',
2228
- disappearDuration: 600,
2419
+ disappearDuration: 250,
2229
2420
  disappearEasing: 'cubicIn',
2230
2421
  threshold: 1000,
2231
2422
  progressive: true,
@@ -2233,70 +2424,76 @@ var DEFAULT_ANIMATION_PRESETS = [{
2233
2424
  }
2234
2425
  }, {
2235
2426
  name: 'fast',
2236
- description: '快速动画配置',
2427
+ description: '快速动画配置 — 微交互 (150-200ms)',
2237
2428
  config: {
2238
2429
  enabled: true,
2239
- duration: 500,
2240
- easing: 'linear',
2241
- appearDuration: 600,
2242
- appearEasing: 'linear',
2243
- updateDuration: 400,
2244
- updateEasing: 'linear',
2245
- disappearDuration: 300,
2246
- disappearEasing: 'linear',
2430
+ duration: 150,
2431
+ easing: 'cubicOut',
2432
+ appearDuration: 200,
2433
+ appearEasing: 'cubicOut',
2434
+ updateDuration: 150,
2435
+ updateEasing: 'cubicOut',
2436
+ disappearDuration: 100,
2437
+ disappearEasing: 'cubicIn',
2247
2438
  threshold: 2000,
2248
2439
  progressive: true,
2249
2440
  progressiveStep: 1000
2250
2441
  }
2251
2442
  }, {
2252
2443
  name: 'slow',
2253
- description: '慢速动画配置',
2444
+ description: '慢速动画配置 — 页面过渡 (500-600ms, capped)',
2254
2445
  config: {
2255
2446
  enabled: true,
2256
- duration: 2000,
2447
+ duration: 500,
2257
2448
  easing: 'cubicInOut',
2258
- appearDuration: 2400,
2449
+ appearDuration: 600,
2259
2450
  appearEasing: 'cubicInOut',
2260
- updateDuration: 1600,
2451
+ updateDuration: 400,
2261
2452
  updateEasing: 'cubicInOut',
2262
- disappearDuration: 1200,
2453
+ disappearDuration: 300,
2263
2454
  disappearEasing: 'cubicInOut',
2264
2455
  threshold: 500,
2265
2456
  progressive: true,
2266
2457
  progressiveStep: 250
2267
2458
  }
2268
- }, {
2459
+ },
2460
+ // DEPRECATED — bounce is an anti-pattern per frontend-design-pro skill
2461
+ {
2269
2462
  name: 'bounce',
2270
- description: '弹跳动画配置',
2463
+ description: '[已废弃] 弹跳动画 — 请使用 default 或 fast',
2271
2464
  config: {
2272
- enabled: true,
2273
- duration: 1500,
2274
- easing: 'bounceOut',
2275
- appearDuration: 1800,
2276
- appearEasing: 'bounceOut',
2277
- updateDuration: 1200,
2278
- updateEasing: 'bounceOut',
2279
- disappearDuration: 900,
2280
- disappearEasing: 'bounceIn',
2465
+ enabled: false,
2466
+ // disabled by default — anti-pattern
2467
+ duration: 0,
2468
+ easing: 'cubicOut',
2469
+ appearDuration: 0,
2470
+ appearEasing: 'cubicOut',
2471
+ updateDuration: 0,
2472
+ updateEasing: 'cubicOut',
2473
+ disappearDuration: 0,
2474
+ disappearEasing: 'cubicIn',
2281
2475
  threshold: 500,
2282
- progressive: true,
2476
+ progressive: false,
2283
2477
  progressiveStep: 250
2284
2478
  }
2285
- }, {
2479
+ },
2480
+ // DEPRECATED — elastic is an anti-pattern per frontend-design-pro skill
2481
+ {
2286
2482
  name: 'elastic',
2287
- description: '弹性动画配置',
2483
+ description: '[已废弃] 弹性动画 — 请使用 default 或 fast',
2288
2484
  config: {
2289
- enabled: true,
2290
- duration: 1500,
2291
- easing: 'elasticOut',
2292
- appearDuration: 1800,
2293
- appearEasing: 'elasticOut',
2294
- updateDuration: 1200,
2295
- updateEasing: 'elasticOut',
2296
- disappearDuration: 900,
2297
- disappearEasing: 'elasticIn',
2485
+ enabled: false,
2486
+ // disabled by default — anti-pattern
2487
+ duration: 0,
2488
+ easing: 'cubicOut',
2489
+ appearDuration: 0,
2490
+ appearEasing: 'cubicOut',
2491
+ updateDuration: 0,
2492
+ updateEasing: 'cubicOut',
2493
+ disappearDuration: 0,
2494
+ disappearEasing: 'cubicIn',
2298
2495
  threshold: 500,
2299
- progressive: true,
2496
+ progressive: false,
2300
2497
  progressiveStep: 250
2301
2498
  }
2302
2499
  }];
@@ -2392,6 +2589,7 @@ var AnimationManager = /** @class */function () {
2392
2589
  };
2393
2590
  /**
2394
2591
  * 根据数据量和动画类型获取优化后的动画配置
2592
+ * 尊重 prefers-reduced-motion 无障碍设置
2395
2593
  */
2396
2594
  AnimationManager.prototype.getOptimizedConfig = function (config, dataLength) {
2397
2595
  if (config === void 0) {
@@ -2402,6 +2600,16 @@ var AnimationManager = /** @class */function () {
2402
2600
  }
2403
2601
  // 合并配置:用户配置 > 默认配置
2404
2602
  var mergedConfig = __assign(__assign({}, this.defaultConfig), config);
2603
+ // Respect OS/browser prefers-reduced-motion setting (WCAG)
2604
+ if (prefersReducedMotion()) {
2605
+ return __assign(__assign({}, mergedConfig), {
2606
+ enabled: false,
2607
+ duration: 0,
2608
+ appearDuration: 0,
2609
+ updateDuration: 0,
2610
+ disappearDuration: 0
2611
+ });
2612
+ }
2405
2613
  // 根据数据量优化动画
2406
2614
  if (mergedConfig.threshold && dataLength > mergedConfig.threshold) {
2407
2615
  mergedConfig.enabled = false;
@@ -48788,9 +48996,18 @@ var PerformanceAnalyzer = /** @class */function () {
48788
48996
  }
48789
48997
  }
48790
48998
  /**
48791
- * 获取单例实例
48999
+ * 获取实例
49000
+ * - 传入 chartId 时:每个 chartId 获得独立实例(指标隔离)
49001
+ * - 不传 chartId 时:全局单例(向后兼容)
48792
49002
  */
48793
49003
  PerformanceAnalyzer.getInstance = function (config) {
49004
+ if (config === null || config === void 0 ? void 0 : config.chartId) {
49005
+ if (!PerformanceAnalyzer.instances.has(config.chartId)) {
49006
+ PerformanceAnalyzer.instances.set(config.chartId, new PerformanceAnalyzer(config));
49007
+ }
49008
+ return PerformanceAnalyzer.instances.get(config.chartId);
49009
+ }
49010
+ // Legacy: global singleton
48794
49011
  if (!PerformanceAnalyzer.instance) {
48795
49012
  PerformanceAnalyzer.instance = new PerformanceAnalyzer(config);
48796
49013
  }
@@ -48805,6 +49022,16 @@ var PerformanceAnalyzer = /** @class */function () {
48805
49022
  PerformanceAnalyzer.instance = null;
48806
49023
  }
48807
49024
  };
49025
+ /**
49026
+ * 重置所有图表实例(用于测试/清理)
49027
+ */
49028
+ PerformanceAnalyzer.resetAllInstances = function () {
49029
+ PerformanceAnalyzer.resetInstance();
49030
+ PerformanceAnalyzer.instances.forEach(function (analyzer) {
49031
+ return analyzer.stop();
49032
+ });
49033
+ PerformanceAnalyzer.instances.clear();
49034
+ };
48808
49035
  /**
48809
49036
  * 注册事件处理器
48810
49037
  */
@@ -49215,6 +49442,8 @@ var PerformanceAnalyzer = /** @class */function () {
49215
49442
  return this.analyze().score;
49216
49443
  };
49217
49444
  PerformanceAnalyzer.instance = null;
49445
+ // Per-chart isolated instances
49446
+ PerformanceAnalyzer.instances = new Map();
49218
49447
  return PerformanceAnalyzer;
49219
49448
  }();
49220
49449
 
@@ -49287,6 +49516,15 @@ var BaseChart_assign = undefined && undefined.__assign || function () {
49287
49516
  };
49288
49517
  return BaseChart_assign.apply(this, arguments);
49289
49518
  };
49519
+ var BaseChart_spreadArray = undefined && undefined.__spreadArray || function (to, from, pack) {
49520
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
49521
+ if (ar || !(i in from)) {
49522
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
49523
+ ar[i] = from[i];
49524
+ }
49525
+ }
49526
+ return to.concat(ar || Array.prototype.slice.call(from));
49527
+ };
49290
49528
  /**
49291
49529
  * TaroViz 基础图表组件
49292
49530
  * 所有图表组件的基类
@@ -49420,11 +49658,30 @@ var BaseChart = function (props) {
49420
49658
  });
49421
49659
  }
49422
49660
  }
49661
+ // Inject dataZoom when enableZoom is true (keyboard-accessible zoom)
49662
+ if (_enableZoom) {
49663
+ processed = JSON.parse(JSON.stringify(processed));
49664
+ // Avoid duplicate dataZoom entries
49665
+ var existingDzArr = Array.isArray(processed.dataZoom) ? processed.dataZoom : processed.dataZoom ? [processed.dataZoom] : [];
49666
+ if (!existingDzArr.some(function (dz) {
49667
+ return (dz === null || dz === void 0 ? void 0 : dz.type) === 'inside';
49668
+ })) {
49669
+ processed.dataZoom = BaseChart_spreadArray(BaseChart_spreadArray([], existingDzArr || [], true), [
49670
+ // Inside (mouse wheel + keyboard) — wired to keyboard nav in BaseChartWrapper
49671
+ {
49672
+ type: 'inside',
49673
+ start: 0,
49674
+ end: 100,
49675
+ zoomOnMouseWheel: true,
49676
+ moveOnMouseMove: false
49677
+ }], false);
49678
+ }
49679
+ }
49423
49680
  // Apply animation config
49424
49681
  var dataLength = calculateDataLength(processed);
49425
49682
  var animConfig = (0,core_animation/* generateEChartsAnimationConfig */.ek)(animation, dataLength);
49426
49683
  return BaseChart_assign(BaseChart_assign({}, processed), animConfig);
49427
- }, [option, animation, enableDataFiltering, filters, virtualScroll, virtualScrollPageSize, virtualScrollPreloadSize, onDataFiltered]);
49684
+ }, [option, animation, _enableZoom, enableDataFiltering, filters, virtualScroll, virtualScrollPageSize, virtualScrollPreloadSize, onDataFiltered]);
49428
49685
  // Internal chartInit that wraps the user's callback
49429
49686
  var handleChartInit = (0,external_react_.useCallback)(function (instance) {
49430
49687
  var _a;
@@ -49433,6 +49690,7 @@ var BaseChart = function (props) {
49433
49690
  // Performance monitoring init
49434
49691
  if (enablePerformanceMonitoring) {
49435
49692
  performanceAnalyzerRef.current = PerformanceAnalyzer.getInstance({
49693
+ chartId: chartId,
49436
49694
  enabled: true,
49437
49695
  metrics: ['initTime', 'renderTime', 'updateTime', 'dataSize', 'frameRate'],
49438
49696
  sampleInterval: 1000,
@@ -49588,15 +49846,36 @@ var BaseChart = function (props) {
49588
49846
  });
49589
49847
  }
49590
49848
  }, [option, onPerformance]);
49591
- // Data update callback
49849
+ // Data update callback — supports debounce
49850
+ var debounceTimerRef = (0,external_react_.useRef)(null);
49592
49851
  (0,external_react_.useEffect)(function () {
49593
- if (onDataUpdate && (dataUpdateOptions === null || dataUpdateOptions === void 0 ? void 0 : dataUpdateOptions.enabled) !== false) {
49852
+ var _a;
49853
+ if (!onDataUpdate || (dataUpdateOptions === null || dataUpdateOptions === void 0 ? void 0 : dataUpdateOptions.enabled) === false) return;
49854
+ var delay = (_a = dataUpdateOptions === null || dataUpdateOptions === void 0 ? void 0 : dataUpdateOptions.debounceDelay) !== null && _a !== void 0 ? _a : 0;
49855
+ if (debounceTimerRef.current) {
49856
+ clearTimeout(debounceTimerRef.current);
49857
+ }
49858
+ if (delay > 0) {
49859
+ debounceTimerRef.current = setTimeout(function () {
49860
+ var oldOpt = oldOptionRef.current;
49861
+ if (oldOpt !== option) {
49862
+ onDataUpdate(oldOpt, option);
49863
+ oldOptionRef.current = option;
49864
+ }
49865
+ }, delay);
49866
+ } else {
49594
49867
  var oldOpt = oldOptionRef.current;
49595
49868
  if (oldOpt !== option) {
49596
49869
  onDataUpdate(oldOpt, option);
49597
49870
  oldOptionRef.current = option;
49598
49871
  }
49599
49872
  }
49873
+ return function () {
49874
+ if (debounceTimerRef.current) {
49875
+ clearTimeout(debounceTimerRef.current);
49876
+ debounceTimerRef.current = null;
49877
+ }
49878
+ };
49600
49879
  }, [option, onDataUpdate, dataUpdateOptions]);
49601
49880
  // Cleanup on unmount
49602
49881
  (0,external_react_.useEffect)(function () {
@@ -50957,8 +51236,10 @@ var LiquidChart = /*#__PURE__*/(0,external_react_.memo)(function (props) {
50957
51236
  chartInstance.current = instance;
50958
51237
  // 绑定事件
50959
51238
  if (onEvents) {
50960
- Object.keys(onEvents).forEach(function (eventName) {
50961
- instance.on(eventName, onEvents[eventName]);
51239
+ Object.entries(onEvents).forEach(function (_a) {
51240
+ var eventName = _a[0],
51241
+ handler = _a[1];
51242
+ instance.on(eventName, handler);
50962
51243
  });
50963
51244
  }
50964
51245
  if (onChartInit) {
@@ -56695,7 +56976,7 @@ function useChartTheme(theme, darkMode) {
56695
56976
  */
56696
56977
  function useChartData(data, transformer) {
56697
56978
  return (0,external_react_.useMemo)(function () {
56698
- if (!data) {
56979
+ if (!data || Array.isArray(data) && data.length === 0) {
56699
56980
  return {};
56700
56981
  }
56701
56982
  return transformer(data);