@agions/taroviz 1.11.1 → 2.0.3

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.
Files changed (164) hide show
  1. package/CHANGELOG.md +245 -0
  2. package/README.md +104 -302
  3. package/dist/cjs/index.js +1 -1
  4. package/dist/cjs/vendors.js +1 -0
  5. package/dist/cjs/vendors~echarts.js +1 -0
  6. package/dist/esm/index.js +1 -58151
  7. package/dist/esm/vendors.js +1 -0
  8. package/dist/esm/vendors~echarts.js +1 -0
  9. package/package.json +19 -25
  10. package/src/adapters/MiniAppAdapter.ts +136 -0
  11. package/src/adapters/__tests__/index.test.ts +1 -1
  12. package/src/adapters/h5/__tests__/index.test.ts +4 -2
  13. package/src/adapters/h5/index.ts +63 -64
  14. package/src/adapters/harmony/index.ts +23 -245
  15. package/src/adapters/index.ts +49 -45
  16. package/src/adapters/swan/index.ts +6 -69
  17. package/src/adapters/tt/index.ts +7 -70
  18. package/src/adapters/types.ts +25 -58
  19. package/src/adapters/weapp/index.ts +6 -69
  20. package/src/charts/__tests__/testUtils.tsx +87 -0
  21. package/src/charts/boxplot/__tests__/index.test.tsx +49 -103
  22. package/src/charts/boxplot/index.tsx +2 -1
  23. package/src/charts/boxplot/types.ts +17 -16
  24. package/src/charts/common/BaseChartWrapper.tsx +90 -82
  25. package/src/charts/common/__mocks__/BaseChartWrapper.tsx +17 -0
  26. package/src/charts/createChartComponent.tsx +36 -0
  27. package/src/charts/createOptionChartComponent.tsx +32 -0
  28. package/src/charts/funnel/__tests__/index.test.tsx +99 -0
  29. package/src/charts/funnel/index.tsx +60 -10
  30. package/src/charts/funnel/types.ts +6 -0
  31. package/src/charts/graph/__tests__/index.test.tsx +102 -33
  32. package/src/charts/graph/index.tsx +66 -9
  33. package/src/charts/graph/types.ts +6 -0
  34. package/src/charts/heatmap/__tests__/index.test.tsx +139 -0
  35. package/src/charts/heatmap/index.tsx +103 -10
  36. package/src/charts/heatmap/types.ts +6 -0
  37. package/src/charts/index.ts +74 -26
  38. package/src/charts/liquid/__tests__/index.test.tsx +52 -0
  39. package/src/charts/liquid/index.tsx +239 -182
  40. package/src/charts/liquid/types.ts +11 -11
  41. package/src/charts/parallel/__tests__/index.test.tsx +40 -67
  42. package/src/charts/parallel/index.tsx +2 -1
  43. package/src/charts/parallel/types.ts +19 -18
  44. package/src/charts/radar/__tests__/index.test.tsx +210 -0
  45. package/src/charts/radar/index.tsx +143 -10
  46. package/src/charts/radar/types.ts +13 -0
  47. package/src/charts/sankey/__tests__/index.test.tsx +124 -0
  48. package/src/charts/sankey/index.tsx +62 -10
  49. package/src/charts/sankey/types.ts +6 -0
  50. package/src/charts/tree/__tests__/index.test.tsx +71 -0
  51. package/src/charts/tree/index.tsx +5 -2
  52. package/src/charts/tree/types.ts +9 -9
  53. package/src/charts/types.ts +208 -106
  54. package/src/charts/utils.ts +9 -7
  55. package/src/charts/wordcloud/__tests__/index.test.tsx +98 -31
  56. package/src/charts/wordcloud/index.tsx +75 -9
  57. package/src/charts/wordcloud/types.ts +6 -0
  58. package/src/components/DataFilter/index.tsx +32 -10
  59. package/src/core/animation/types.ts +6 -6
  60. package/src/core/components/Annotation.tsx +6 -7
  61. package/src/core/components/BaseChart.tsx +110 -168
  62. package/src/core/components/ErrorBoundary.tsx +17 -4
  63. package/src/core/components/LazyChart.tsx +54 -55
  64. package/src/core/components/hooks/index.ts +6 -2
  65. package/src/core/components/hooks/useChartInit.ts +6 -3
  66. package/src/core/components/hooks/usePerformance.ts +8 -2
  67. package/src/core/components/hooks/useVirtualScroll.ts +2 -1
  68. package/src/core/index.ts +1 -1
  69. package/src/core/themes/ThemeManager.ts +1 -1
  70. package/src/core/types/common.ts +2 -1
  71. package/src/core/types/index.ts +0 -12
  72. package/src/core/types/platform.ts +3 -5
  73. package/src/core/utils/__tests__/deepClone.test.ts +317 -0
  74. package/src/core/utils/__tests__/index.test.ts +2 -1
  75. package/src/core/utils/chartInstances.ts +13 -0
  76. package/src/core/utils/common.ts +20 -29
  77. package/src/core/utils/deepClone.ts +114 -0
  78. package/src/core/utils/download.ts +128 -0
  79. package/src/core/utils/drillDown.ts +34 -353
  80. package/src/core/utils/drillDownHelpers.ts +426 -0
  81. package/src/core/utils/events.ts +12 -0
  82. package/src/core/utils/export/ExportUtils.ts +36 -67
  83. package/src/core/utils/format.ts +44 -0
  84. package/src/core/utils/index.ts +21 -154
  85. package/src/core/utils/merge.ts +25 -0
  86. package/src/core/utils/performance/PerformanceAnalyzer.ts +38 -21
  87. package/src/core/utils/performance/hooks.ts +7 -0
  88. package/src/core/utils/performance/index.ts +2 -0
  89. package/src/{hooks → core/utils/performance}/useAnimation.ts +45 -41
  90. package/src/core/utils/performance/useDataZoom.ts +324 -0
  91. package/src/{hooks → core/utils/performance}/usePerformance.ts +49 -41
  92. package/src/core/utils/performance/usePerformanceHooks.ts +278 -0
  93. package/src/core/utils/performanceUtils.ts +310 -0
  94. package/src/core/utils/runtime.ts +190 -0
  95. package/src/core/utils/setOptionUtils.ts +59 -0
  96. package/src/core/version.ts +14 -0
  97. package/src/editor/EnhancedThemeEditor.tsx +362 -540
  98. package/src/editor/ThemeEditor.tsx +55 -321
  99. package/src/editor/components/ThemeBasicSettings.tsx +113 -0
  100. package/src/editor/components/ThemeColorEditor.tsx +105 -0
  101. package/src/editor/components/ThemeSelector.tsx +70 -0
  102. package/src/editor/hooks/useThemeEditorState.ts +201 -0
  103. package/src/editor/index.ts +10 -2
  104. package/src/hooks/__tests__/index.test.tsx +3 -1
  105. package/src/hooks/chartConnectHelpers.ts +341 -0
  106. package/src/hooks/index.ts +55 -660
  107. package/src/hooks/types.ts +189 -0
  108. package/src/hooks/useChartAutoResize.ts +73 -0
  109. package/src/hooks/useChartConnect.ts +92 -238
  110. package/src/hooks/useChartDownload.ts +25 -27
  111. package/src/hooks/useChartHistory.ts +34 -49
  112. package/src/hooks/useChartInit.ts +59 -0
  113. package/src/hooks/useChartOptions.ts +259 -0
  114. package/src/hooks/useChartPerformance.ts +109 -0
  115. package/src/hooks/useChartSelection.ts +52 -49
  116. package/src/hooks/useChartTheme.ts +51 -0
  117. package/src/hooks/useDataTransform.ts +19 -4
  118. package/src/hooks/utils/chartDownloadUtils.ts +40 -53
  119. package/src/hooks/utils/dataTransformUtils.ts +22 -0
  120. package/src/index.ts +48 -34
  121. package/src/main.tsx +4 -9
  122. package/src/react-dom.d.ts +3 -3
  123. package/src/themes/index.ts +30 -855
  124. package/src/themes/palettes/blue-green.ts +13 -0
  125. package/src/themes/palettes/chalk.ts +13 -0
  126. package/src/themes/palettes/cyber.ts +44 -0
  127. package/src/themes/palettes/dark.ts +52 -0
  128. package/src/themes/palettes/default.ts +52 -0
  129. package/src/themes/palettes/elegant.ts +34 -0
  130. package/src/themes/palettes/forest.ts +13 -0
  131. package/src/themes/palettes/glass.ts +49 -0
  132. package/src/themes/palettes/golden.ts +13 -0
  133. package/src/themes/palettes/neon.ts +43 -0
  134. package/src/themes/palettes/ocean.ts +39 -0
  135. package/src/themes/palettes/pastel.ts +37 -0
  136. package/src/themes/palettes/purple-passion.ts +13 -0
  137. package/src/themes/palettes/retro.ts +33 -0
  138. package/src/themes/palettes/sunset.ts +40 -0
  139. package/src/themes/palettes/walden.ts +13 -0
  140. package/src/themes/registry.ts +184 -0
  141. package/src/themes/types.ts +213 -0
  142. package/src/charts/bar/__tests__/index.test.tsx +0 -113
  143. package/src/charts/bar/index.tsx +0 -14
  144. package/src/charts/candlestick/__tests__/index.test.tsx +0 -40
  145. package/src/charts/candlestick/index.tsx +0 -13
  146. package/src/charts/gauge/index.tsx +0 -14
  147. package/src/charts/line/__tests__/index.test.tsx +0 -107
  148. package/src/charts/line/index.tsx +0 -15
  149. package/src/charts/pie/__tests__/index.test.tsx +0 -112
  150. package/src/charts/pie/index.tsx +0 -14
  151. package/src/charts/scatter/index.tsx +0 -14
  152. package/src/charts/sunburst/index.tsx +0 -18
  153. package/src/charts/treemap/index.tsx +0 -18
  154. package/src/core/utils/codeGenerator/CodeGenerator.ts +0 -669
  155. package/src/core/utils/codeGenerator/index.ts +0 -13
  156. package/src/core/utils/codeGenerator/types.ts +0 -198
  157. package/src/core/utils/configGenerator/ConfigGenerator.ts +0 -583
  158. package/src/core/utils/configGenerator/index.ts +0 -13
  159. package/src/core/utils/configGenerator/types.ts +0 -445
  160. package/src/core/utils/debug/DebugPanel.tsx +0 -637
  161. package/src/core/utils/debug/debugger.ts +0 -322
  162. package/src/core/utils/debug/index.ts +0 -21
  163. package/src/core/utils/debug/types.ts +0 -142
  164. package/src/hooks/useDataZoom.ts +0 -323
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
1
2
  /**
2
3
  * DataFilter - 数据筛选器 UI 组件
3
4
  * 提供独立的数据筛选交互界面,支持多种筛选类型
@@ -32,7 +33,15 @@ export interface FilterField {
32
33
  }
33
34
 
34
35
  /** 筛选值类型 */
35
- export type FilterValue = string | number | boolean | [number, number] | [string, string] | string[] | number[] | undefined;
36
+ export type FilterValue =
37
+ | string
38
+ | number
39
+ | boolean
40
+ | [number, number]
41
+ | [string, string]
42
+ | string[]
43
+ | number[]
44
+ | undefined;
36
45
 
37
46
  /** 筛选器完整值 */
38
47
  export interface FilterValues {
@@ -74,7 +83,8 @@ export interface DataFilterProps {
74
83
  // ============================================================================
75
84
 
76
85
  const BASE_STYLE: React.CSSProperties = {
77
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
86
+ fontFamily:
87
+ '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
78
88
  fontSize: '14px',
79
89
  color: '#333',
80
90
  };
@@ -122,7 +132,13 @@ interface SelectFilterProps {
122
132
  compact?: boolean;
123
133
  }
124
134
 
125
- const SelectFilter: React.FC<SelectFilterProps> = ({ field, value, onChange, disabled, compact }) => {
135
+ const SelectFilter: React.FC<SelectFilterProps> = ({
136
+ field,
137
+ value,
138
+ onChange,
139
+ disabled,
140
+ compact,
141
+ }) => {
126
142
  const handleChange = useCallback(
127
143
  (e: React.ChangeEvent<HTMLSelectElement>) => {
128
144
  const val = e.target.value;
@@ -241,7 +257,13 @@ interface CheckboxFilterProps {
241
257
  compact?: boolean;
242
258
  }
243
259
 
244
- const CheckboxFilter: React.FC<CheckboxFilterProps> = ({ field, value, onChange, disabled, compact }) => {
260
+ const CheckboxFilter: React.FC<CheckboxFilterProps> = ({
261
+ field,
262
+ value,
263
+ onChange,
264
+ disabled,
265
+ compact,
266
+ }) => {
245
267
  const selectedValues = useMemo(() => {
246
268
  if (Array.isArray(value)) {
247
269
  return new Set(value.map(String));
@@ -380,13 +402,13 @@ const DateFilter: React.FC<DateFilterProps> = ({ field, value, onChange, disable
380
402
  * ```tsx
381
403
  * <DataFilter
382
404
  * fields={[
383
- * { key: 'category', label: '分类', type: 'select', options: [{ label: '苹果', value: 'apple' }] },
384
- * { key: 'price', label: '价格区间', type: 'range', min: 0, max: 1000 },
385
- * { key: 'tags', label: '标签', type: 'checkbox', options: [{ label: '热门', value: 'hot' }] },
386
- * { key: 'date', label: '日期', type: 'date' },
405
+ * { _key: 'category', label: '分类', type: 'select', options: [{ label: '苹果', _value: 'apple' }] },
406
+ * { _key: 'price', label: '价格区间', type: 'range', min: 0, max: 1000 },
407
+ * { _key: 'tags', label: '标签', type: 'checkbox', options: [{ label: '热门', _value: 'hot' }] },
408
+ * { _key: 'date', label: '日期', type: 'date' },
387
409
  * ]}
388
- * value={filters}
389
- * onChange={(filters) => setFilters(filters)}
410
+ * _value={_filters}
411
+ * onChange={(_filters) => setFilters(_filters)}
390
412
  * layout="horizontal"
391
413
  * showReset
392
414
  * />
@@ -241,33 +241,33 @@ export enum AnimationEventType {
241
241
  /**
242
242
  * 动画开始事件
243
243
  */
244
- ANIMATION_START = 'animationStart',
244
+ _ANIMATION_START = 'animationStart',
245
245
 
246
246
  /**
247
247
  * 动画更新事件
248
248
  */
249
- ANIMATION_UPDATE = 'animationUpdate',
249
+ _ANIMATION_UPDATE = 'animationUpdate',
250
250
 
251
251
  /**
252
252
  * 动画结束事件
253
253
  */
254
- ANIMATION_END = 'animationEnd',
254
+ _ANIMATION_END = 'animationEnd',
255
255
 
256
256
  /**
257
257
  * 动画取消事件
258
258
  */
259
- ANIMATION_CANCEL = 'animationCancel',
259
+ _ANIMATION_CANCEL = 'animationCancel',
260
260
 
261
261
  /**
262
262
  * 动画重复事件
263
263
  */
264
- ANIMATION_REPEAT = 'animationRepeat',
264
+ _ANIMATION_REPEAT = 'animationRepeat',
265
265
  }
266
266
 
267
267
  /**
268
268
  * 动画事件回调类型
269
269
  */
270
- export type AnimationEventHandler = (event: {
270
+ export type AnimationEventHandler = (_event: {
271
271
  type: AnimationEventType;
272
272
  animationType: AnimationType;
273
273
  chartId?: string;
@@ -57,7 +57,7 @@ export interface MarkLineConfig {
57
57
  label?: {
58
58
  show?: boolean;
59
59
  position?: 'start' | 'middle' | 'end' | 'insideStartBottom' | 'insideStartTop';
60
- formatter?: string | ((value: any) => string);
60
+ formatter?: string | ((_value: unknown) => string);
61
61
  color?: string;
62
62
  fontSize?: number;
63
63
  };
@@ -81,7 +81,7 @@ export interface MarkAreaConfig {
81
81
  label?: {
82
82
  show?: boolean;
83
83
  position?: 'top' | 'bottom' | 'left' | 'right' | 'inside';
84
- formatter?: string | ((value: any) => string);
84
+ formatter?: string | ((_value: unknown) => string);
85
85
  color?: string;
86
86
  fontSize?: number;
87
87
  };
@@ -98,7 +98,7 @@ export interface ScatterAnnotationConfig {
98
98
  /** 数据点 */
99
99
  data: Array<{
100
100
  coord: [number | string, number];
101
- value?: number;
101
+ _value?: number;
102
102
  name: string;
103
103
  }>;
104
104
  /** 符号类型 */
@@ -111,7 +111,7 @@ export interface ScatterAnnotationConfig {
111
111
  label?: {
112
112
  show?: boolean;
113
113
  position?: string;
114
- formatter?: string | ((value: unknown) => string);
114
+ formatter?: string | ((_value: unknown) => string);
115
115
  color?: string;
116
116
  };
117
117
  }
@@ -243,7 +243,6 @@ export function useAnnotation(props: AnnotationProps): EChartsOption {
243
243
  const { type, markLine, markArea, scatter } = props;
244
244
 
245
245
  return useMemo(() => {
246
- // 使用 any 避免类型复杂性问题
247
246
  const series: any[] = [];
248
247
 
249
248
  if (type === 'line' && markLine) {
@@ -291,8 +290,8 @@ export const AnnotationPresets = {
291
290
  }),
292
291
 
293
292
  /** 警戒线 */
294
- thresholdLine: (value: number, color = '#faad14'): MarkLineConfig => ({
295
- data: [{ yAxis: value, name: '警戒线' }],
293
+ thresholdLine: (_value: number, color = '#faad14'): MarkLineConfig => ({
294
+ data: [{ yAxis: _value, name: '警戒线' }],
296
295
  lineStyle: { color, type: 'solid', width: 2 },
297
296
  label: { show: true, position: 'start', color },
298
297
  }),
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
1
2
  /**
2
3
  * TaroViz 基础图表组件
3
4
  * 所有图表组件的基类
@@ -6,21 +7,22 @@
6
7
  * 所有具体的图表组件(如折线图、柱状图等)都继承自该组件
7
8
  */
8
9
  import React, { useEffect, useRef, useMemo, useCallback } from 'react';
10
+ import { deepClone } from '../utils/deepClone';
9
11
 
10
12
  import { generateEChartsAnimationConfig } from '../animation';
11
13
  import { EChartsOption, EChartsType, AnimationConfig } from '../types';
12
14
  import type { DataZoomComponentOption } from 'echarts';
13
15
  import { registerChart, removeChart, getChart } from '../utils/chartInstances';
14
- import { DebugPanel, DebugPanelOptions, updateDebugInfo } from '../utils/debug';
15
16
  import { PerformanceAnalyzer } from '../utils/performance';
16
17
  import { normalizeSize, calculateDataLength, filterDataByKeys } from '../utils/chartUtils';
17
18
  import BaseChartWrapper from '../../charts/common/BaseChartWrapper';
18
19
  import type { BaseChartProps } from '../../charts/types';
19
20
  import type {
20
21
  EChartsMouseEventParams,
21
- EChartsDataZoomEventParams,
22
- EChartsLegendEventParams,
23
22
  EChartsTooltipEventParams,
23
+ ChartEventParams,
24
+ ChartExportOptions,
25
+ ChartLinkageConfig,
24
26
  } from '../types/common';
25
27
  import type { ECElementEvent } from 'echarts';
26
28
 
@@ -28,46 +30,8 @@ import type { ECElementEvent } from 'echarts';
28
30
  // 接口定义
29
31
  // ============================================================================
30
32
 
31
- /** 图表事件参数类型 */
32
- export interface ChartEventParams extends Record<string, unknown> {
33
- componentType?: string;
34
- componentSubType?: string;
35
- componentIndex?: number;
36
- seriesType?: string;
37
- seriesIndex?: number;
38
- seriesId?: string;
39
- seriesName?: string;
40
- name?: string;
41
- dataIndex?: number;
42
- data?: unknown;
43
- dataType?: string;
44
- value?: unknown;
45
- color?: string;
46
- borderColor?: string;
47
- dimensionNames?: string[];
48
- encode?: Record<string, number[]>;
49
- marker?: string;
50
- status?: string;
51
- dimensionIndex?: number;
52
- percent?: number;
53
- }
54
-
55
- /** 图表导出选项 */
56
- export interface ChartExportOptions {
57
- type?: 'png' | 'jpeg' | 'svg';
58
- filename?: string;
59
- pixelRatio?: number;
60
- backgroundColor?: string;
61
- }
62
-
63
- /** 图表联动配置 */
64
- export interface ChartLinkageConfig {
65
- linkedChartIds?: string[];
66
- enableClickLinkage?: boolean;
67
- enableZoomLinkage?: boolean;
68
- enableLegendLinkage?: boolean;
69
- enableFilterLinkage?: boolean;
70
- }
33
+ /** core/types 导入共享类型 */
34
+ export { ChartEventParams, ChartExportOptions, ChartLinkageConfig } from '../types/common';
71
35
 
72
36
  // ============================================================================
73
37
  // ChartProps - 与原有接口保持完全兼容
@@ -77,15 +41,14 @@ export interface ChartProps {
77
41
  chartId?: string;
78
42
  option?: EChartsOption;
79
43
  animation?: AnimationConfig;
80
- debug?: boolean | DebugPanelOptions;
81
44
  width?: number | string;
82
45
  height?: number | string;
83
46
  theme?: string | object;
84
47
  autoResize?: boolean;
85
48
  direction?: 'ltr' | 'rtl';
86
- onInit?: (instance: EChartsType) => void;
87
- onClick?: (params: ChartEventParams) => void;
88
- onDataZoom?: (params: ChartEventParams) => void;
49
+ onInit?: (_instance: EChartsType) => void;
50
+ onClick?: (_params: ChartEventParams) => void;
51
+ onDataZoom?: (_params: ChartEventParams) => void;
89
52
  style?: React.CSSProperties;
90
53
  className?: string;
91
54
  children?: React.ReactNode;
@@ -100,28 +63,30 @@ export interface ChartProps {
100
63
  dataSize: number;
101
64
  }) => void;
102
65
  enableZoom?: boolean;
103
- onZoom?: (data: { start: number; end: number; dataZoomIndex: number }) => void;
66
+ onZoom?: (data: { start: number; end: number; _dataZoomIndex: number }) => void;
104
67
  enableDataFiltering?: boolean;
105
- filters?: Record<string, string | number | boolean | string[] | null>;
106
- onDataFiltered?: (filteredData: unknown[], filters: Record<string, unknown>) => void;
68
+ _filters?: Record<string, string | number | boolean | string[] | null>;
69
+ onDataFiltered?: (_filteredData: unknown[], _filters: Record<string, unknown>) => void;
107
70
  enableLegendInteraction?: boolean;
108
71
  legendInteractionMode?: 'single' | 'multiple' | 'all';
109
- onLegendSelect?: (params: { name: string; selected: Record<string, boolean> }) => void;
110
- onLegendUnselect?: (params: { name: string; selected: Record<string, boolean> }) => void;
111
- onLegendSelectAll?: (params: { selected: Record<string, boolean> }) => void;
112
- onLegendInverseSelect?: (params: { selected: Record<string, boolean> }) => void;
72
+ onLegendSelect?: (_params: { name: string; selected: Record<string, boolean> }) => void;
73
+ onLegendUnselect?: (_params: { name: string; selected: Record<string, boolean> }) => void;
74
+ onLegendSelectAll?: (_params: { selected: Record<string, boolean> }) => void;
75
+ onLegendInverseSelect?: (_params: { selected: Record<string, boolean> }) => void;
113
76
  enableCustomTooltip?: boolean;
114
- customTooltipContent?: (params: EChartsMouseEventParams | EChartsMouseEventParams[]) => React.ReactNode;
77
+ customTooltipContent?: (
78
+ _params: EChartsMouseEventParams | EChartsMouseEventParams[]
79
+ ) => React.ReactNode;
115
80
  customTooltipStyle?: React.CSSProperties;
116
- onTooltipShow?: (params: EChartsTooltipEventParams) => void;
117
- onTooltipHide?: (params: EChartsTooltipEventParams) => void;
118
- onExport?: (dataURL: string, options: ChartExportOptions) => void;
81
+ onTooltipShow?: (_params: EChartsTooltipEventParams) => void;
82
+ onTooltipHide?: (_params: EChartsTooltipEventParams) => void;
83
+ onExport?: (_dataURL: string, options: ChartExportOptions) => void;
119
84
  linkageConfig?: ChartLinkageConfig;
120
85
  onDataUpdate?: (
121
86
  oldOption: EChartsOption | undefined,
122
87
  newOption: EChartsOption | undefined
123
88
  ) => void;
124
- dataUpdateOptions?: { enabled?: boolean; deepCompare?: boolean; debounceDelay?: number };
89
+ _dataUpdateOptions?: { enabled?: boolean; deepCompare?: boolean; debounceDelay?: number };
125
90
  }
126
91
 
127
92
  // ============================================================================
@@ -133,7 +98,6 @@ const BaseChart: React.FC<ChartProps> = (props) => {
133
98
  chartId,
134
99
  option,
135
100
  animation,
136
- debug,
137
101
  width = '100%',
138
102
  height = '300px',
139
103
  theme,
@@ -153,7 +117,7 @@ const BaseChart: React.FC<ChartProps> = (props) => {
153
117
  enableZoom: _enableZoom = false,
154
118
  onZoom,
155
119
  enableDataFiltering = false,
156
- filters = {},
120
+ _filters = {},
157
121
  onDataFiltered,
158
122
  enableLegendInteraction = false,
159
123
  legendInteractionMode = 'single',
@@ -169,7 +133,7 @@ const BaseChart: React.FC<ChartProps> = (props) => {
169
133
  onExport: _onExport,
170
134
  linkageConfig = {},
171
135
  onDataUpdate,
172
- dataUpdateOptions = {},
136
+ _dataUpdateOptions = {},
173
137
  } = props;
174
138
 
175
139
  // Refs
@@ -190,31 +154,22 @@ const BaseChart: React.FC<ChartProps> = (props) => {
190
154
  });
191
155
  const oldOptionRef = useRef<EChartsOption | undefined>(option);
192
156
  const adapterRef = useRef<unknown>(null);
193
- const debugConfigRef = useRef<DebugPanelOptions | null>(null);
194
157
  const performanceAnalyzerRef = useRef<PerformanceAnalyzer | null>(null);
195
158
 
196
- // Debug config
197
- const debugConfig = useMemo(() => {
198
- if (!debug) return null;
199
- return typeof debug === 'boolean'
200
- ? { enabled: debug, autoExpand: false }
201
- : { enabled: true, ...debug };
202
- }, [debug]);
203
-
204
- // Wrapper option that applies virtual scroll + data filtering
159
+ // Wrapper option that applies virtual scroll + _data filtering
205
160
  const wrappedOption = useMemo(() => {
206
161
  if (!option) return undefined;
207
162
  let processed: Record<string, unknown> = { ...option };
208
163
 
209
- // Apply data filtering
210
- if (enableDataFiltering && filters && Object.keys(filters).length > 0) {
211
- processed = JSON.parse(JSON.stringify(processed)) as typeof processed;
164
+ // Apply _data filtering
165
+ if (enableDataFiltering && _filters && Object.keys(_filters).length > 0) {
166
+ processed = deepClone(processed);
212
167
  if (processed.series && Array.isArray(processed.series)) {
213
168
  processed.series = (processed.series as unknown[]).map((s: unknown) => {
214
- const seriesItem = s as { data?: unknown[]; [key: string]: unknown };
215
- if (seriesItem.data && Array.isArray(seriesItem.data)) {
216
- const filtered = filterDataByKeys(seriesItem.data, filters);
217
- if (onDataFiltered) onDataFiltered(filtered, filters);
169
+ const seriesItem = s as { _data?: unknown[]; [key: string]: unknown };
170
+ if (seriesItem._data && Array.isArray(seriesItem._data)) {
171
+ const filtered = filterDataByKeys(seriesItem._data, _filters);
172
+ if (onDataFiltered) onDataFiltered(filtered, _filters);
218
173
  if (virtualScroll) {
219
174
  virtualScrollRef.current.totalDataCount = filtered.length;
220
175
  virtualScrollRef.current.totalPages = Math.ceil(
@@ -225,24 +180,26 @@ const BaseChart: React.FC<ChartProps> = (props) => {
225
180
  start + virtualScrollPageSize + virtualScrollPreloadSize,
226
181
  filtered.length
227
182
  );
228
- return { ...seriesItem, data: filtered.slice(start, end) };
183
+ return { ...seriesItem, _data: filtered.slice(start, end) };
229
184
  }
230
- return { ...seriesItem, data: filtered };
185
+ return { ...seriesItem, _data: filtered };
231
186
  }
232
187
  return seriesItem;
233
188
  });
234
189
  }
235
190
  }
236
191
 
237
- // Inject dataZoom when enableZoom is true (keyboard-accessible zoom)
192
+ // Inject _dataZoom when enableZoom is true (keyboard-accessible zoom)
238
193
  if (_enableZoom) {
239
- processed = JSON.parse(JSON.stringify(processed));
240
- // Avoid duplicate dataZoom entries
241
- const existingDzArr = Array.isArray(processed.dataZoom)
242
- ? processed.dataZoom as DataZoomComponentOption[]
243
- : processed.dataZoom ? [processed.dataZoom as DataZoomComponentOption] : [];
194
+ processed = deepClone(processed);
195
+ // Avoid duplicate _dataZoom entries
196
+ const existingDzArr = Array.isArray(processed._dataZoom)
197
+ ? (processed._dataZoom as DataZoomComponentOption[])
198
+ : processed._dataZoom
199
+ ? [processed._dataZoom as DataZoomComponentOption]
200
+ : [];
244
201
  if (!existingDzArr.some((dz) => dz?.type === 'inside')) {
245
- processed.dataZoom = [
202
+ processed._dataZoom = [
246
203
  ...(existingDzArr || []),
247
204
  // Inside (mouse wheel + keyboard) — wired to keyboard nav in BaseChartWrapper
248
205
  { type: 'inside', start: 0, end: 100, zoomOnMouseWheel: true, moveOnMouseMove: false },
@@ -251,15 +208,15 @@ const BaseChart: React.FC<ChartProps> = (props) => {
251
208
  }
252
209
 
253
210
  // Apply animation config
254
- const dataLength = calculateDataLength(processed);
255
- const animConfig = generateEChartsAnimationConfig(animation, dataLength);
211
+ const _dataLength = calculateDataLength(processed);
212
+ const animConfig = generateEChartsAnimationConfig(animation, _dataLength);
256
213
  return { ...processed, ...animConfig } as EChartsOption;
257
214
  }, [
258
215
  option,
259
216
  animation,
260
217
  _enableZoom,
261
218
  enableDataFiltering,
262
- filters,
219
+ _filters,
263
220
  virtualScroll,
264
221
  virtualScrollPageSize,
265
222
  virtualScrollPreloadSize,
@@ -268,9 +225,9 @@ const BaseChart: React.FC<ChartProps> = (props) => {
268
225
 
269
226
  // Internal chartInit that wraps the user's callback
270
227
  const handleChartInit = useCallback(
271
- (instance: EChartsType) => {
272
- chartInstanceRef.current = instance;
273
- adapterRef.current = instance as unknown;
228
+ (_instance: EChartsType) => {
229
+ chartInstanceRef.current = _instance;
230
+ adapterRef.current = _instance as unknown;
274
231
 
275
232
  // Performance monitoring init
276
233
  if (enablePerformanceMonitoring) {
@@ -287,28 +244,33 @@ const BaseChart: React.FC<ChartProps> = (props) => {
287
244
  performanceRef.current.initStartTime = Date.now();
288
245
 
289
246
  // Register for linkage
290
- if (chartId) registerChart(chartId, instance);
247
+ if (chartId) registerChart(chartId, _instance);
291
248
 
292
249
  // Setup internal event handlers for linkage + virtual scroll
293
- if (instance) {
250
+ if (_instance) {
294
251
  // Click linkage
295
252
  if (linkageConfig.enableClickLinkage && chartId && linkageConfig.linkedChartIds) {
296
- instance.on('click', (params: ECElementEvent) => {
253
+ _instance.on('click', (_params: ECElementEvent) => {
297
254
  linkageConfig.linkedChartIds!.forEach((lid) => {
298
255
  const linked = getChart(lid);
299
- if (linked) linked.dispatchAction({ type: 'highlight', name: params.name });
256
+ if (linked) linked.dispatchAction({ type: 'highlight', name: _params.name });
300
257
  });
301
258
  });
302
259
  }
303
260
 
304
261
  // Zoom + zoom linkage + virtual scroll page update
305
- instance.on('datazoom', (params: unknown) => {
306
- const p = params as { start?: number; end?: number; dataZoomIndex?: number; batch?: Array<{ start?: number; end?: number; dataZoomIndex?: number }> };
262
+ _instance.on('_datazoom', (_params: unknown) => {
263
+ const p = _params as {
264
+ start?: number;
265
+ end?: number;
266
+ _dataZoomIndex?: number;
267
+ batch?: Array<{ start?: number; end?: number; _dataZoomIndex?: number }>;
268
+ };
307
269
  if (onZoom)
308
270
  onZoom({
309
271
  start: p.start || 0,
310
272
  end: p.end || 100,
311
- dataZoomIndex: p.dataZoomIndex || 0,
273
+ _dataZoomIndex: p._dataZoomIndex || 0,
312
274
  });
313
275
  if (virtualScroll && !virtualScrollRef.current.isScrolling) {
314
276
  virtualScrollRef.current.isScrolling = true;
@@ -328,10 +290,10 @@ const BaseChart: React.FC<ChartProps> = (props) => {
328
290
  const linked = getChart(lid);
329
291
  if (linked)
330
292
  linked.dispatchAction({
331
- type: 'dataZoom',
293
+ type: '_dataZoom',
332
294
  start: p.start,
333
295
  end: p.end,
334
- dataZoomIndex: p.dataZoomIndex,
296
+ _dataZoomIndex: p._dataZoomIndex,
335
297
  });
336
298
  });
337
299
  }
@@ -339,8 +301,8 @@ const BaseChart: React.FC<ChartProps> = (props) => {
339
301
 
340
302
  // Legend interaction
341
303
  if (enableLegendInteraction) {
342
- instance.on('legendselectchanged', (params: unknown) => {
343
- const p = params as { name?: string; selected: Record<string, boolean> };
304
+ _instance.on('legendselectchanged', (_params: unknown) => {
305
+ const p = _params as { name?: string; selected: Record<string, boolean> };
344
306
  const { name, selected } = p;
345
307
  if (linkageConfig.enableLegendLinkage && chartId && linkageConfig.linkedChartIds) {
346
308
  linkageConfig.linkedChartIds!.forEach((lid) => {
@@ -353,7 +315,7 @@ const BaseChart: React.FC<ChartProps> = (props) => {
353
315
  Object.keys(selected).forEach((k) => {
354
316
  newSelected[k] = k === name;
355
317
  });
356
- instance.setOption({ legend: { selected: newSelected } });
318
+ _instance.setOption({ legend: { selected: newSelected } });
357
319
  if (name !== undefined) onLegendSelect?.({ name, selected: newSelected });
358
320
  } else {
359
321
  if (name !== undefined && selected[name]) onLegendSelect?.({ name, selected });
@@ -364,11 +326,16 @@ const BaseChart: React.FC<ChartProps> = (props) => {
364
326
 
365
327
  // Custom tooltip
366
328
  if (enableCustomTooltip && customTooltipContent) {
367
- instance.on('tooltipshow', (params: unknown) => onTooltipShow?.(params as EChartsTooltipEventParams));
368
- instance.on('tooltiphide', (params: unknown) => onTooltipHide?.(params as EChartsTooltipEventParams));
369
- instance.setOption({
329
+ _instance.on('tooltipshow', (_params: unknown) =>
330
+ onTooltipShow?.(_params as EChartsTooltipEventParams)
331
+ );
332
+ _instance.on('tooltiphide', (_params: unknown) =>
333
+ onTooltipHide?.(_params as EChartsTooltipEventParams)
334
+ );
335
+ _instance.setOption({
370
336
  tooltip: {
371
- formatter: (params: unknown) => String(customTooltipContent(params as EChartsMouseEventParams)),
337
+ formatter: (_params: unknown) =>
338
+ String(customTooltipContent(_params as EChartsMouseEventParams)),
372
339
  ...(customTooltipStyle && {
373
340
  backgroundColor: 'transparent',
374
341
  borderColor: 'transparent',
@@ -379,32 +346,7 @@ const BaseChart: React.FC<ChartProps> = (props) => {
379
346
  }
380
347
  }
381
348
 
382
- // Update debug panel
383
- if (debugConfigRef.current?.enabled) {
384
- updateDebugInfo({
385
- instance: {
386
- id: chartId,
387
- type: 'ECharts',
388
- renderer: 'canvas',
389
- width: typeof width === 'number' ? width : undefined,
390
- height: typeof height === 'number' ? height : undefined,
391
- platform: 'web',
392
- },
393
- config: wrappedOption,
394
- data: {
395
- series: Array.isArray(wrappedOption?.series) ? wrappedOption.series : [],
396
- totalDataCount: calculateDataLength(wrappedOption),
397
- currentDataCount: calculateDataLength(wrappedOption),
398
- },
399
- performance: {
400
- initTime: 0,
401
- renderTime: 0,
402
- dataSize: JSON.stringify(wrappedOption).length,
403
- },
404
- });
405
- }
406
-
407
- onInit?.(instance);
349
+ onInit?.(_instance);
408
350
  performanceRef.current.initEndTime = Date.now();
409
351
  },
410
352
  [
@@ -437,7 +379,7 @@ const BaseChart: React.FC<ChartProps> = (props) => {
437
379
  renderTime: p.renderEndTime - p.renderStartTime,
438
380
  initTime: p.initEndTime - p.initStartTime,
439
381
  updateTime: p.updateEndTime - p.updateStartTime,
440
- dataSize: JSON.stringify(option).length,
382
+ dataSize: estimateDataSize(option),
441
383
  });
442
384
  }
443
385
  }, [option, onPerformance]);
@@ -446,9 +388,9 @@ const BaseChart: React.FC<ChartProps> = (props) => {
446
388
  const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
447
389
 
448
390
  useEffect(() => {
449
- if (!onDataUpdate || dataUpdateOptions?.enabled === false) return;
391
+ if (!onDataUpdate || _dataUpdateOptions?.enabled === false) return;
450
392
 
451
- const delay = dataUpdateOptions?.debounceDelay ?? 0;
393
+ const delay = _dataUpdateOptions?.debounceDelay ?? 0;
452
394
 
453
395
  if (debounceTimerRef.current) {
454
396
  clearTimeout(debounceTimerRef.current);
@@ -476,7 +418,7 @@ const BaseChart: React.FC<ChartProps> = (props) => {
476
418
  debounceTimerRef.current = null;
477
419
  }
478
420
  };
479
- }, [option, onDataUpdate, dataUpdateOptions]);
421
+ }, [option, onDataUpdate, _dataUpdateOptions]);
480
422
 
481
423
  // Cleanup on unmount
482
424
  useEffect(() => {
@@ -512,38 +454,38 @@ const BaseChart: React.FC<ChartProps> = (props) => {
512
454
  return (
513
455
  <>
514
456
  <BaseChartWrapper {...wrapperProps} />
515
- {debugConfig?.enabled && (
516
- <DebugPanel
517
- options={debugConfig}
518
- debugInfo={{
519
- instance: {
520
- id: chartId,
521
- type: 'ECharts',
522
- renderer: 'canvas',
523
- width: typeof width === 'number' ? width : undefined,
524
- height: typeof height === 'number' ? height : undefined,
525
- platform: 'web',
526
- },
527
- config: option,
528
- data: {
529
- series: Array.isArray(option?.series) ? option.series : [],
530
- totalDataCount: calculateDataLength(option),
531
- currentDataCount: calculateDataLength(option),
532
- },
533
- performance: {
534
- initTime: performanceRef.current.initEndTime - performanceRef.current.initStartTime,
535
- renderTime:
536
- performanceRef.current.renderEndTime - performanceRef.current.renderStartTime,
537
- updateTime: 0,
538
- dataSize: JSON.stringify(option).length,
539
- },
540
- }}
541
- />
542
- )}
543
457
  </>
544
458
  );
545
459
  };
546
460
 
461
+ /**
462
+ * Lightweight estimation of option data size without expensive JSON.stringify.
463
+ * Counts approximate character length by summing string values and array lengths.
464
+ */
465
+ function estimateDataSize(option: unknown): number {
466
+ if (option == null) return 0;
467
+ if (typeof option === 'string') return option.length;
468
+ if (typeof option !== 'object') return 8; // number/boolean approximation
469
+
470
+ let size = 0;
471
+ const obj = option as Record<string, unknown>;
472
+ for (const key in obj) {
473
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
474
+ size += key.length + 1; // key + colon
475
+ const val = obj[key];
476
+ if (typeof val === 'string') {
477
+ size += val.length + 2; // quotes
478
+ } else if (Array.isArray(val)) {
479
+ size += val.length * 4; // rough per-element estimate
480
+ } else if (typeof val === 'object' && val !== null) {
481
+ size += estimateDataSize(val);
482
+ } else {
483
+ size += 8; // number/boolean
484
+ }
485
+ }
486
+ return size;
487
+ }
488
+
547
489
  function normalizeSizeObject(
548
490
  width: number | string,
549
491
  height: number | string,