@agions/taroviz 1.11.5 → 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 (118) hide show
  1. package/CHANGELOG.md +245 -0
  2. package/README.md +31 -46
  3. package/dist/cjs/index.js +1 -1
  4. package/dist/cjs/vendors.js +1 -1
  5. package/dist/cjs/vendors~echarts.js +1 -1
  6. package/dist/esm/index.js +1 -14270
  7. package/dist/esm/vendors.js +1 -16770
  8. package/dist/esm/vendors~echarts.js +1 -59417
  9. package/package.json +10 -15
  10. package/src/adapters/h5/index.ts +38 -38
  11. package/src/adapters/index.ts +32 -34
  12. package/src/adapters/types.ts +23 -55
  13. package/src/charts/boxplot/types.ts +2 -2
  14. package/src/charts/common/BaseChartWrapper.tsx +9 -7
  15. package/src/charts/createChartComponent.tsx +9 -21
  16. package/src/charts/createOptionChartComponent.tsx +32 -0
  17. package/src/charts/funnel/__tests__/index.test.tsx +99 -0
  18. package/src/charts/funnel/index.tsx +64 -0
  19. package/src/charts/funnel/types.ts +6 -0
  20. package/src/charts/graph/__tests__/index.test.tsx +116 -0
  21. package/src/charts/graph/index.tsx +70 -0
  22. package/src/charts/graph/types.ts +6 -0
  23. package/src/charts/heatmap/__tests__/index.test.tsx +139 -0
  24. package/src/charts/heatmap/index.tsx +107 -0
  25. package/src/charts/heatmap/types.ts +6 -0
  26. package/src/charts/index.ts +47 -57
  27. package/src/charts/liquid/__tests__/index.test.tsx +52 -0
  28. package/src/charts/liquid/index.tsx +7 -133
  29. package/src/charts/liquid/types.ts +6 -6
  30. package/src/charts/parallel/types.ts +3 -3
  31. package/src/charts/radar/__tests__/index.test.tsx +210 -0
  32. package/src/charts/radar/index.tsx +147 -0
  33. package/src/charts/radar/types.ts +13 -0
  34. package/src/charts/sankey/__tests__/index.test.tsx +124 -0
  35. package/src/charts/sankey/index.tsx +70 -0
  36. package/src/charts/sankey/types.ts +6 -0
  37. package/src/charts/tree/__tests__/index.test.tsx +71 -0
  38. package/src/charts/tree/index.tsx +1 -1
  39. package/src/charts/tree/types.ts +8 -8
  40. package/src/charts/types.ts +208 -106
  41. package/src/charts/wordcloud/__tests__/index.test.tsx +106 -0
  42. package/src/charts/wordcloud/index.tsx +79 -0
  43. package/src/charts/wordcloud/types.ts +6 -0
  44. package/src/components/DataFilter/index.tsx +7 -6
  45. package/src/core/animation/types.ts +6 -6
  46. package/src/core/components/Annotation.tsx +6 -6
  47. package/src/core/components/BaseChart.tsx +97 -133
  48. package/src/core/components/LazyChart.tsx +3 -8
  49. package/src/core/components/hooks/index.ts +6 -2
  50. package/src/core/components/hooks/usePerformance.ts +8 -2
  51. package/src/core/components/hooks/useVirtualScroll.ts +2 -1
  52. package/src/core/types/common.ts +2 -1
  53. package/src/core/types/platform.ts +1 -0
  54. package/src/core/utils/__tests__/deepClone.test.ts +317 -0
  55. package/src/core/utils/__tests__/index.test.ts +2 -1
  56. package/src/core/utils/chartInstances.ts +13 -0
  57. package/src/core/utils/common.ts +20 -36
  58. package/src/core/utils/deepClone.ts +114 -0
  59. package/src/core/utils/download.ts +22 -28
  60. package/src/core/utils/drillDown.ts +1 -0
  61. package/src/core/utils/events.ts +12 -0
  62. package/src/core/utils/export/ExportUtils.ts +2 -1
  63. package/src/core/utils/format.ts +44 -0
  64. package/src/core/utils/index.ts +18 -159
  65. package/src/core/utils/merge.ts +25 -0
  66. package/src/core/utils/performance/PerformanceAnalyzer.ts +3 -1
  67. package/src/core/utils/performance/hooks.ts +7 -0
  68. package/src/core/utils/performance/index.ts +2 -0
  69. package/src/{hooks → core/utils/performance}/useAnimation.ts +6 -5
  70. package/src/{hooks → core/utils/performance}/useDataZoom.ts +7 -2
  71. package/src/{hooks → core/utils/performance}/usePerformance.ts +39 -39
  72. package/src/{hooks → core/utils/performance}/usePerformanceHooks.ts +39 -39
  73. package/src/core/utils/runtime.ts +190 -0
  74. package/src/editor/components/ThemeSelector.tsx +3 -3
  75. package/src/hooks/chartConnectHelpers.ts +6 -0
  76. package/src/hooks/index.ts +54 -626
  77. package/src/hooks/types.ts +27 -0
  78. package/src/hooks/useChartAutoResize.ts +73 -0
  79. package/src/hooks/useChartConnect.ts +5 -1
  80. package/src/hooks/useChartDownload.ts +1 -1
  81. package/src/hooks/useChartHistory.ts +1 -3
  82. package/src/hooks/useChartInit.ts +59 -0
  83. package/src/hooks/useChartOptions.ts +259 -0
  84. package/src/hooks/useChartPerformance.ts +109 -0
  85. package/src/hooks/useChartSelection.ts +23 -12
  86. package/src/hooks/useChartTheme.ts +51 -0
  87. package/src/hooks/useDataTransform.ts +19 -4
  88. package/src/index.ts +5 -10
  89. package/src/react-dom.d.ts +3 -3
  90. package/src/themes/index.ts +30 -855
  91. package/src/themes/palettes/blue-green.ts +13 -0
  92. package/src/themes/palettes/chalk.ts +13 -0
  93. package/src/themes/palettes/cyber.ts +44 -0
  94. package/src/themes/palettes/dark.ts +52 -0
  95. package/src/themes/palettes/default.ts +52 -0
  96. package/src/themes/palettes/elegant.ts +34 -0
  97. package/src/themes/palettes/forest.ts +13 -0
  98. package/src/themes/palettes/glass.ts +49 -0
  99. package/src/themes/palettes/golden.ts +13 -0
  100. package/src/themes/palettes/neon.ts +43 -0
  101. package/src/themes/palettes/ocean.ts +39 -0
  102. package/src/themes/palettes/pastel.ts +37 -0
  103. package/src/themes/palettes/purple-passion.ts +13 -0
  104. package/src/themes/palettes/retro.ts +33 -0
  105. package/src/themes/palettes/sunset.ts +40 -0
  106. package/src/themes/palettes/walden.ts +13 -0
  107. package/src/themes/registry.ts +184 -0
  108. package/src/themes/types.ts +213 -0
  109. package/src/core/utils/codeGenerator/CodeGenerator.ts +0 -669
  110. package/src/core/utils/codeGenerator/index.ts +0 -13
  111. package/src/core/utils/codeGenerator/types.ts +0 -198
  112. package/src/core/utils/configGenerator/ConfigGenerator.ts +0 -583
  113. package/src/core/utils/configGenerator/index.ts +0 -13
  114. package/src/core/utils/configGenerator/types.ts +0 -449
  115. package/src/core/utils/debug/DebugPanel.tsx +0 -640
  116. package/src/core/utils/debug/debugger.ts +0 -322
  117. package/src/core/utils/debug/index.ts +0 -21
  118. package/src/core/utils/debug/types.ts +0 -142
@@ -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: unknown) => 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: unknown) => 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
  }
@@ -290,8 +290,8 @@ export const AnnotationPresets = {
290
290
  }),
291
291
 
292
292
  /** 警戒线 */
293
- thresholdLine: (value: number, color = '#faad14'): MarkLineConfig => ({
294
- data: [{ yAxis: value, name: '警戒线' }],
293
+ thresholdLine: (_value: number, color = '#faad14'): MarkLineConfig => ({
294
+ data: [{ yAxis: _value, name: '警戒线' }],
295
295
  lineStyle: { color, type: 'solid', width: 2 },
296
296
  label: { show: true, position: 'start', color },
297
297
  }),
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
1
2
  /**
2
3
  * TaroViz 基础图表组件
3
4
  * 所有图表组件的基类
@@ -6,20 +7,18 @@
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,
24
23
  ChartEventParams,
25
24
  ChartExportOptions,
@@ -42,15 +41,14 @@ export interface ChartProps {
42
41
  chartId?: string;
43
42
  option?: EChartsOption;
44
43
  animation?: AnimationConfig;
45
- debug?: boolean | DebugPanelOptions;
46
44
  width?: number | string;
47
45
  height?: number | string;
48
46
  theme?: string | object;
49
47
  autoResize?: boolean;
50
48
  direction?: 'ltr' | 'rtl';
51
- onInit?: (instance: EChartsType) => void;
52
- onClick?: (params: ChartEventParams) => void;
53
- onDataZoom?: (params: ChartEventParams) => void;
49
+ onInit?: (_instance: EChartsType) => void;
50
+ onClick?: (_params: ChartEventParams) => void;
51
+ onDataZoom?: (_params: ChartEventParams) => void;
54
52
  style?: React.CSSProperties;
55
53
  className?: string;
56
54
  children?: React.ReactNode;
@@ -65,30 +63,30 @@ export interface ChartProps {
65
63
  dataSize: number;
66
64
  }) => void;
67
65
  enableZoom?: boolean;
68
- onZoom?: (data: { start: number; end: number; dataZoomIndex: number }) => void;
66
+ onZoom?: (data: { start: number; end: number; _dataZoomIndex: number }) => void;
69
67
  enableDataFiltering?: boolean;
70
- filters?: Record<string, string | number | boolean | string[] | null>;
71
- 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;
72
70
  enableLegendInteraction?: boolean;
73
71
  legendInteractionMode?: 'single' | 'multiple' | 'all';
74
- onLegendSelect?: (params: { name: string; selected: Record<string, boolean> }) => void;
75
- onLegendUnselect?: (params: { name: string; selected: Record<string, boolean> }) => void;
76
- onLegendSelectAll?: (params: { selected: Record<string, boolean> }) => void;
77
- 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;
78
76
  enableCustomTooltip?: boolean;
79
77
  customTooltipContent?: (
80
- params: EChartsMouseEventParams | EChartsMouseEventParams[]
78
+ _params: EChartsMouseEventParams | EChartsMouseEventParams[]
81
79
  ) => React.ReactNode;
82
80
  customTooltipStyle?: React.CSSProperties;
83
- onTooltipShow?: (params: EChartsTooltipEventParams) => void;
84
- onTooltipHide?: (params: EChartsTooltipEventParams) => void;
85
- onExport?: (dataURL: string, options: ChartExportOptions) => void;
81
+ onTooltipShow?: (_params: EChartsTooltipEventParams) => void;
82
+ onTooltipHide?: (_params: EChartsTooltipEventParams) => void;
83
+ onExport?: (_dataURL: string, options: ChartExportOptions) => void;
86
84
  linkageConfig?: ChartLinkageConfig;
87
85
  onDataUpdate?: (
88
86
  oldOption: EChartsOption | undefined,
89
87
  newOption: EChartsOption | undefined
90
88
  ) => void;
91
- dataUpdateOptions?: { enabled?: boolean; deepCompare?: boolean; debounceDelay?: number };
89
+ _dataUpdateOptions?: { enabled?: boolean; deepCompare?: boolean; debounceDelay?: number };
92
90
  }
93
91
 
94
92
  // ============================================================================
@@ -100,7 +98,6 @@ const BaseChart: React.FC<ChartProps> = (props) => {
100
98
  chartId,
101
99
  option,
102
100
  animation,
103
- debug,
104
101
  width = '100%',
105
102
  height = '300px',
106
103
  theme,
@@ -120,7 +117,7 @@ const BaseChart: React.FC<ChartProps> = (props) => {
120
117
  enableZoom: _enableZoom = false,
121
118
  onZoom,
122
119
  enableDataFiltering = false,
123
- filters = {},
120
+ _filters = {},
124
121
  onDataFiltered,
125
122
  enableLegendInteraction = false,
126
123
  legendInteractionMode = 'single',
@@ -136,7 +133,7 @@ const BaseChart: React.FC<ChartProps> = (props) => {
136
133
  onExport: _onExport,
137
134
  linkageConfig = {},
138
135
  onDataUpdate,
139
- dataUpdateOptions = {},
136
+ _dataUpdateOptions = {},
140
137
  } = props;
141
138
 
142
139
  // Refs
@@ -159,28 +156,20 @@ const BaseChart: React.FC<ChartProps> = (props) => {
159
156
  const adapterRef = useRef<unknown>(null);
160
157
  const performanceAnalyzerRef = useRef<PerformanceAnalyzer | null>(null);
161
158
 
162
- // Debug config
163
- const debugConfig = useMemo(() => {
164
- if (!debug) return null;
165
- return typeof debug === 'boolean'
166
- ? { enabled: debug, autoExpand: false }
167
- : { enabled: true, ...debug };
168
- }, [debug]);
169
-
170
- // Wrapper option that applies virtual scroll + data filtering
159
+ // Wrapper option that applies virtual scroll + _data filtering
171
160
  const wrappedOption = useMemo(() => {
172
161
  if (!option) return undefined;
173
162
  let processed: Record<string, unknown> = { ...option };
174
163
 
175
- // Apply data filtering
176
- if (enableDataFiltering && filters && Object.keys(filters).length > 0) {
177
- 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);
178
167
  if (processed.series && Array.isArray(processed.series)) {
179
168
  processed.series = (processed.series as unknown[]).map((s: unknown) => {
180
- const seriesItem = s as { data?: unknown[]; [key: string]: unknown };
181
- if (seriesItem.data && Array.isArray(seriesItem.data)) {
182
- const filtered = filterDataByKeys(seriesItem.data, filters);
183
- 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);
184
173
  if (virtualScroll) {
185
174
  virtualScrollRef.current.totalDataCount = filtered.length;
186
175
  virtualScrollRef.current.totalPages = Math.ceil(
@@ -191,26 +180,26 @@ const BaseChart: React.FC<ChartProps> = (props) => {
191
180
  start + virtualScrollPageSize + virtualScrollPreloadSize,
192
181
  filtered.length
193
182
  );
194
- return { ...seriesItem, data: filtered.slice(start, end) };
183
+ return { ...seriesItem, _data: filtered.slice(start, end) };
195
184
  }
196
- return { ...seriesItem, data: filtered };
185
+ return { ...seriesItem, _data: filtered };
197
186
  }
198
187
  return seriesItem;
199
188
  });
200
189
  }
201
190
  }
202
191
 
203
- // Inject dataZoom when enableZoom is true (keyboard-accessible zoom)
192
+ // Inject _dataZoom when enableZoom is true (keyboard-accessible zoom)
204
193
  if (_enableZoom) {
205
- processed = JSON.parse(JSON.stringify(processed));
206
- // Avoid duplicate dataZoom entries
207
- const existingDzArr = Array.isArray(processed.dataZoom)
208
- ? (processed.dataZoom as DataZoomComponentOption[])
209
- : processed.dataZoom
210
- ? [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]
211
200
  : [];
212
201
  if (!existingDzArr.some((dz) => dz?.type === 'inside')) {
213
- processed.dataZoom = [
202
+ processed._dataZoom = [
214
203
  ...(existingDzArr || []),
215
204
  // Inside (mouse wheel + keyboard) — wired to keyboard nav in BaseChartWrapper
216
205
  { type: 'inside', start: 0, end: 100, zoomOnMouseWheel: true, moveOnMouseMove: false },
@@ -219,15 +208,15 @@ const BaseChart: React.FC<ChartProps> = (props) => {
219
208
  }
220
209
 
221
210
  // Apply animation config
222
- const dataLength = calculateDataLength(processed);
223
- const animConfig = generateEChartsAnimationConfig(animation, dataLength);
211
+ const _dataLength = calculateDataLength(processed);
212
+ const animConfig = generateEChartsAnimationConfig(animation, _dataLength);
224
213
  return { ...processed, ...animConfig } as EChartsOption;
225
214
  }, [
226
215
  option,
227
216
  animation,
228
217
  _enableZoom,
229
218
  enableDataFiltering,
230
- filters,
219
+ _filters,
231
220
  virtualScroll,
232
221
  virtualScrollPageSize,
233
222
  virtualScrollPreloadSize,
@@ -236,9 +225,9 @@ const BaseChart: React.FC<ChartProps> = (props) => {
236
225
 
237
226
  // Internal chartInit that wraps the user's callback
238
227
  const handleChartInit = useCallback(
239
- (instance: EChartsType) => {
240
- chartInstanceRef.current = instance;
241
- adapterRef.current = instance as unknown;
228
+ (_instance: EChartsType) => {
229
+ chartInstanceRef.current = _instance;
230
+ adapterRef.current = _instance as unknown;
242
231
 
243
232
  // Performance monitoring init
244
233
  if (enablePerformanceMonitoring) {
@@ -255,33 +244,33 @@ const BaseChart: React.FC<ChartProps> = (props) => {
255
244
  performanceRef.current.initStartTime = Date.now();
256
245
 
257
246
  // Register for linkage
258
- if (chartId) registerChart(chartId, instance);
247
+ if (chartId) registerChart(chartId, _instance);
259
248
 
260
249
  // Setup internal event handlers for linkage + virtual scroll
261
- if (instance) {
250
+ if (_instance) {
262
251
  // Click linkage
263
252
  if (linkageConfig.enableClickLinkage && chartId && linkageConfig.linkedChartIds) {
264
- instance.on('click', (params: ECElementEvent) => {
253
+ _instance.on('click', (_params: ECElementEvent) => {
265
254
  linkageConfig.linkedChartIds!.forEach((lid) => {
266
255
  const linked = getChart(lid);
267
- if (linked) linked.dispatchAction({ type: 'highlight', name: params.name });
256
+ if (linked) linked.dispatchAction({ type: 'highlight', name: _params.name });
268
257
  });
269
258
  });
270
259
  }
271
260
 
272
261
  // Zoom + zoom linkage + virtual scroll page update
273
- instance.on('datazoom', (params: unknown) => {
274
- const p = params as {
262
+ _instance.on('_datazoom', (_params: unknown) => {
263
+ const p = _params as {
275
264
  start?: number;
276
265
  end?: number;
277
- dataZoomIndex?: number;
278
- batch?: Array<{ start?: number; end?: number; dataZoomIndex?: number }>;
266
+ _dataZoomIndex?: number;
267
+ batch?: Array<{ start?: number; end?: number; _dataZoomIndex?: number }>;
279
268
  };
280
269
  if (onZoom)
281
270
  onZoom({
282
271
  start: p.start || 0,
283
272
  end: p.end || 100,
284
- dataZoomIndex: p.dataZoomIndex || 0,
273
+ _dataZoomIndex: p._dataZoomIndex || 0,
285
274
  });
286
275
  if (virtualScroll && !virtualScrollRef.current.isScrolling) {
287
276
  virtualScrollRef.current.isScrolling = true;
@@ -301,10 +290,10 @@ const BaseChart: React.FC<ChartProps> = (props) => {
301
290
  const linked = getChart(lid);
302
291
  if (linked)
303
292
  linked.dispatchAction({
304
- type: 'dataZoom',
293
+ type: '_dataZoom',
305
294
  start: p.start,
306
295
  end: p.end,
307
- dataZoomIndex: p.dataZoomIndex,
296
+ _dataZoomIndex: p._dataZoomIndex,
308
297
  });
309
298
  });
310
299
  }
@@ -312,8 +301,8 @@ const BaseChart: React.FC<ChartProps> = (props) => {
312
301
 
313
302
  // Legend interaction
314
303
  if (enableLegendInteraction) {
315
- instance.on('legendselectchanged', (params: unknown) => {
316
- 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> };
317
306
  const { name, selected } = p;
318
307
  if (linkageConfig.enableLegendLinkage && chartId && linkageConfig.linkedChartIds) {
319
308
  linkageConfig.linkedChartIds!.forEach((lid) => {
@@ -326,7 +315,7 @@ const BaseChart: React.FC<ChartProps> = (props) => {
326
315
  Object.keys(selected).forEach((k) => {
327
316
  newSelected[k] = k === name;
328
317
  });
329
- instance.setOption({ legend: { selected: newSelected } });
318
+ _instance.setOption({ legend: { selected: newSelected } });
330
319
  if (name !== undefined) onLegendSelect?.({ name, selected: newSelected });
331
320
  } else {
332
321
  if (name !== undefined && selected[name]) onLegendSelect?.({ name, selected });
@@ -337,16 +326,16 @@ const BaseChart: React.FC<ChartProps> = (props) => {
337
326
 
338
327
  // Custom tooltip
339
328
  if (enableCustomTooltip && customTooltipContent) {
340
- instance.on('tooltipshow', (params: unknown) =>
341
- onTooltipShow?.(params as EChartsTooltipEventParams)
329
+ _instance.on('tooltipshow', (_params: unknown) =>
330
+ onTooltipShow?.(_params as EChartsTooltipEventParams)
342
331
  );
343
- instance.on('tooltiphide', (params: unknown) =>
344
- onTooltipHide?.(params as EChartsTooltipEventParams)
332
+ _instance.on('tooltiphide', (_params: unknown) =>
333
+ onTooltipHide?.(_params as EChartsTooltipEventParams)
345
334
  );
346
- instance.setOption({
335
+ _instance.setOption({
347
336
  tooltip: {
348
- formatter: (params: unknown) =>
349
- String(customTooltipContent(params as EChartsMouseEventParams)),
337
+ formatter: (_params: unknown) =>
338
+ String(customTooltipContent(_params as EChartsMouseEventParams)),
350
339
  ...(customTooltipStyle && {
351
340
  backgroundColor: 'transparent',
352
341
  borderColor: 'transparent',
@@ -357,32 +346,7 @@ const BaseChart: React.FC<ChartProps> = (props) => {
357
346
  }
358
347
  }
359
348
 
360
- // Update debug panel
361
- if (debugConfig?.enabled) {
362
- updateDebugInfo({
363
- instance: {
364
- id: chartId,
365
- type: 'ECharts',
366
- renderer: 'canvas',
367
- width: typeof width === 'number' ? width : undefined,
368
- height: typeof height === 'number' ? height : undefined,
369
- platform: 'web',
370
- },
371
- config: wrappedOption,
372
- data: {
373
- series: Array.isArray(wrappedOption?.series) ? wrappedOption.series : [],
374
- totalDataCount: calculateDataLength(wrappedOption),
375
- currentDataCount: calculateDataLength(wrappedOption),
376
- },
377
- performance: {
378
- initTime: 0,
379
- renderTime: 0,
380
- dataSize: JSON.stringify(wrappedOption).length,
381
- },
382
- });
383
- }
384
-
385
- onInit?.(instance);
349
+ onInit?.(_instance);
386
350
  performanceRef.current.initEndTime = Date.now();
387
351
  },
388
352
  [
@@ -415,7 +379,7 @@ const BaseChart: React.FC<ChartProps> = (props) => {
415
379
  renderTime: p.renderEndTime - p.renderStartTime,
416
380
  initTime: p.initEndTime - p.initStartTime,
417
381
  updateTime: p.updateEndTime - p.updateStartTime,
418
- dataSize: JSON.stringify(option).length,
382
+ dataSize: estimateDataSize(option),
419
383
  });
420
384
  }
421
385
  }, [option, onPerformance]);
@@ -424,9 +388,9 @@ const BaseChart: React.FC<ChartProps> = (props) => {
424
388
  const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
425
389
 
426
390
  useEffect(() => {
427
- if (!onDataUpdate || dataUpdateOptions?.enabled === false) return;
391
+ if (!onDataUpdate || _dataUpdateOptions?.enabled === false) return;
428
392
 
429
- const delay = dataUpdateOptions?.debounceDelay ?? 0;
393
+ const delay = _dataUpdateOptions?.debounceDelay ?? 0;
430
394
 
431
395
  if (debounceTimerRef.current) {
432
396
  clearTimeout(debounceTimerRef.current);
@@ -454,7 +418,7 @@ const BaseChart: React.FC<ChartProps> = (props) => {
454
418
  debounceTimerRef.current = null;
455
419
  }
456
420
  };
457
- }, [option, onDataUpdate, dataUpdateOptions]);
421
+ }, [option, onDataUpdate, _dataUpdateOptions]);
458
422
 
459
423
  // Cleanup on unmount
460
424
  useEffect(() => {
@@ -490,38 +454,38 @@ const BaseChart: React.FC<ChartProps> = (props) => {
490
454
  return (
491
455
  <>
492
456
  <BaseChartWrapper {...wrapperProps} />
493
- {debugConfig?.enabled && (
494
- <DebugPanel
495
- options={debugConfig}
496
- debugInfo={{
497
- instance: {
498
- id: chartId,
499
- type: 'ECharts',
500
- renderer: 'canvas',
501
- width: typeof width === 'number' ? width : undefined,
502
- height: typeof height === 'number' ? height : undefined,
503
- platform: 'web',
504
- },
505
- config: option,
506
- data: {
507
- series: Array.isArray(option?.series) ? option.series : [],
508
- totalDataCount: calculateDataLength(option),
509
- currentDataCount: calculateDataLength(option),
510
- },
511
- performance: {
512
- initTime: performanceRef.current.initEndTime - performanceRef.current.initStartTime,
513
- renderTime:
514
- performanceRef.current.renderEndTime - performanceRef.current.renderStartTime,
515
- updateTime: 0,
516
- dataSize: JSON.stringify(option).length,
517
- },
518
- }}
519
- />
520
- )}
521
457
  </>
522
458
  );
523
459
  };
524
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
+
525
489
  function normalizeSizeObject(
526
490
  width: number | string,
527
491
  height: number | string,
@@ -30,9 +30,8 @@ export const LazyLineChart = createLazyComponent('LineChart');
30
30
  export const LazyBarChart = createLazyComponent('BarChart');
31
31
  export const LazyPieChart = createLazyComponent('PieChart');
32
32
  export const LazyScatterChart = createLazyComponent('ScatterChart');
33
- export const LazyRadarChart = createLazyComponent('RadarChart');
33
+ export const LazyRadarChart = createLazyComponent('RadarChartCustom');
34
34
  export const LazyHeatmapChart = createLazyComponent('HeatmapChart');
35
- export const LazyGaugeChart = createLazyComponent('GaugeChart');
36
35
  export const LazyFunnelChart = createLazyComponent('FunnelChart');
37
36
  export const LazyTreeMapChart = createLazyComponent('TreeMapChart');
38
37
  export const LazySunburstChart = createLazyComponent('SunburstChart');
@@ -42,17 +41,13 @@ export const LazySankeyChart = createLazyComponent('SankeyChart');
42
41
  * 图表类型到懒加载模块的映射
43
42
  * 用于预加载功能
44
43
  */
45
- const LAZY_CHART_MODULES: Record<
46
- string,
47
- () => Promise<{ default: ComponentType<BaseChartProps> }>
48
- > = {
44
+ const LAZY_CHART_MODULES: Record<string, () => Promise<{ default: React.ComponentType<any> }>> = {
49
45
  line: () => import('../../charts').then((m) => ({ default: m.LineChart })),
50
46
  bar: () => import('../../charts').then((m) => ({ default: m.BarChart })),
51
47
  pie: () => import('../../charts').then((m) => ({ default: m.PieChart })),
52
48
  scatter: () => import('../../charts').then((m) => ({ default: m.ScatterChart })),
53
- radar: () => import('../../charts').then((m) => ({ default: m.RadarChart })),
49
+ radar: () => import('../../charts').then((m) => ({ default: m.RadarChartCustom })),
54
50
  heatmap: () => import('../../charts').then((m) => ({ default: m.HeatmapChart })),
55
- gauge: () => import('../../charts').then((m) => ({ default: m.GaugeChart })),
56
51
  funnel: () => import('../../charts').then((m) => ({ default: m.FunnelChart })),
57
52
  treemap: () => import('../../charts').then((m) => ({ default: m.TreeMapChart })),
58
53
  sunburst: () => import('../../charts').then((m) => ({ default: m.SunburstChart })),
@@ -16,5 +16,9 @@ export type {
16
16
  export { useVirtualScroll } from './useVirtualScroll';
17
17
  export type { UseVirtualScrollOptions, VirtualScrollState } from './useVirtualScroll';
18
18
 
19
- export { usePerformance } from './usePerformance';
20
- export type { PerformanceData, UsePerformanceOptions } from './usePerformance';
19
+ export { useChartPerformanceMetrics, usePerformance } from './usePerformance';
20
+ export type {
21
+ PerformanceData,
22
+ UseChartPerformanceMetricsOptions,
23
+ UsePerformanceOptions,
24
+ } from './usePerformance';
@@ -13,12 +13,15 @@ export interface PerformanceData {
13
13
  dataSize: number;
14
14
  }
15
15
 
16
- export interface UsePerformanceOptions {
16
+ export interface UseChartPerformanceMetricsOptions {
17
17
  enabled?: boolean;
18
18
  onPerformance?: (data: PerformanceData) => void;
19
19
  }
20
20
 
21
- export function usePerformance(options: UsePerformanceOptions = {}) {
21
+ /** @deprecated Use `UseChartPerformanceMetricsOptions` instead */
22
+ export type UsePerformanceOptions = UseChartPerformanceMetricsOptions;
23
+
24
+ export function useChartPerformanceMetrics(options: UseChartPerformanceMetricsOptions = {}) {
22
25
  const { enabled = false, onPerformance } = options;
23
26
 
24
27
  const analyzerRef = useRef<PerformanceAnalyzer | null>(null);
@@ -184,3 +187,6 @@ export function usePerformance(options: UsePerformanceOptions = {}) {
184
187
  dispose,
185
188
  };
186
189
  }
190
+
191
+ /** @deprecated Use `useChartPerformanceMetrics` instead */
192
+ export const usePerformance = useChartPerformanceMetrics;
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { useRef, useCallback } from 'react';
6
6
  import type { EChartsOption } from 'echarts';
7
+ import { deepClone } from '../../../core/utils/deepClone';
7
8
 
8
9
  export interface UseVirtualScrollOptions {
9
10
  enabled?: boolean;
@@ -71,7 +72,7 @@ export function useVirtualScroll(options: UseVirtualScrollOptions = {}) {
71
72
  }
72
73
 
73
74
  // 深拷贝避免修改原始数据
74
- const processedOption = JSON.parse(JSON.stringify(originalOption)) as EChartsOption;
75
+ const processedOption = deepClone(originalOption) as EChartsOption;
75
76
 
76
77
  (processedOption.series as unknown[]).forEach((seriesItem: unknown, _index: number) => {
77
78
  const s = seriesItem as { data?: unknown[] };
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
1
2
  /**
2
3
  * ECharts 类型定义
3
4
  */
@@ -120,7 +121,7 @@ export type EChartsEventParams =
120
121
  /**
121
122
  * 图表事件监听器
122
123
  */
123
- export type ChartEventListener = Record<string, (params: ChartEventParams) => void>;
124
+ export type _ChartEventListener = Record<string, (_params: ChartEventParams) => void>;
124
125
 
125
126
  /**
126
127
  * 图表渲染器类型
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
1
2
  /**
2
3
  * 支持的平台类型
3
4
  */