@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.
- package/CHANGELOG.md +245 -0
- package/README.md +31 -46
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/vendors.js +1 -1
- package/dist/cjs/vendors~echarts.js +1 -1
- package/dist/esm/index.js +1 -14270
- package/dist/esm/vendors.js +1 -16770
- package/dist/esm/vendors~echarts.js +1 -59417
- package/package.json +10 -15
- package/src/adapters/h5/index.ts +38 -38
- package/src/adapters/index.ts +32 -34
- package/src/adapters/types.ts +23 -55
- package/src/charts/boxplot/types.ts +2 -2
- package/src/charts/common/BaseChartWrapper.tsx +9 -7
- package/src/charts/createChartComponent.tsx +9 -21
- package/src/charts/createOptionChartComponent.tsx +32 -0
- package/src/charts/funnel/__tests__/index.test.tsx +99 -0
- package/src/charts/funnel/index.tsx +64 -0
- package/src/charts/funnel/types.ts +6 -0
- package/src/charts/graph/__tests__/index.test.tsx +116 -0
- package/src/charts/graph/index.tsx +70 -0
- package/src/charts/graph/types.ts +6 -0
- package/src/charts/heatmap/__tests__/index.test.tsx +139 -0
- package/src/charts/heatmap/index.tsx +107 -0
- package/src/charts/heatmap/types.ts +6 -0
- package/src/charts/index.ts +47 -57
- package/src/charts/liquid/__tests__/index.test.tsx +52 -0
- package/src/charts/liquid/index.tsx +7 -133
- package/src/charts/liquid/types.ts +6 -6
- package/src/charts/parallel/types.ts +3 -3
- package/src/charts/radar/__tests__/index.test.tsx +210 -0
- package/src/charts/radar/index.tsx +147 -0
- package/src/charts/radar/types.ts +13 -0
- package/src/charts/sankey/__tests__/index.test.tsx +124 -0
- package/src/charts/sankey/index.tsx +70 -0
- package/src/charts/sankey/types.ts +6 -0
- package/src/charts/tree/__tests__/index.test.tsx +71 -0
- package/src/charts/tree/index.tsx +1 -1
- package/src/charts/tree/types.ts +8 -8
- package/src/charts/types.ts +208 -106
- package/src/charts/wordcloud/__tests__/index.test.tsx +106 -0
- package/src/charts/wordcloud/index.tsx +79 -0
- package/src/charts/wordcloud/types.ts +6 -0
- package/src/components/DataFilter/index.tsx +7 -6
- package/src/core/animation/types.ts +6 -6
- package/src/core/components/Annotation.tsx +6 -6
- package/src/core/components/BaseChart.tsx +97 -133
- package/src/core/components/LazyChart.tsx +3 -8
- package/src/core/components/hooks/index.ts +6 -2
- package/src/core/components/hooks/usePerformance.ts +8 -2
- package/src/core/components/hooks/useVirtualScroll.ts +2 -1
- package/src/core/types/common.ts +2 -1
- package/src/core/types/platform.ts +1 -0
- package/src/core/utils/__tests__/deepClone.test.ts +317 -0
- package/src/core/utils/__tests__/index.test.ts +2 -1
- package/src/core/utils/chartInstances.ts +13 -0
- package/src/core/utils/common.ts +20 -36
- package/src/core/utils/deepClone.ts +114 -0
- package/src/core/utils/download.ts +22 -28
- package/src/core/utils/drillDown.ts +1 -0
- package/src/core/utils/events.ts +12 -0
- package/src/core/utils/export/ExportUtils.ts +2 -1
- package/src/core/utils/format.ts +44 -0
- package/src/core/utils/index.ts +18 -159
- package/src/core/utils/merge.ts +25 -0
- package/src/core/utils/performance/PerformanceAnalyzer.ts +3 -1
- package/src/core/utils/performance/hooks.ts +7 -0
- package/src/core/utils/performance/index.ts +2 -0
- package/src/{hooks → core/utils/performance}/useAnimation.ts +6 -5
- package/src/{hooks → core/utils/performance}/useDataZoom.ts +7 -2
- package/src/{hooks → core/utils/performance}/usePerformance.ts +39 -39
- package/src/{hooks → core/utils/performance}/usePerformanceHooks.ts +39 -39
- package/src/core/utils/runtime.ts +190 -0
- package/src/editor/components/ThemeSelector.tsx +3 -3
- package/src/hooks/chartConnectHelpers.ts +6 -0
- package/src/hooks/index.ts +54 -626
- package/src/hooks/types.ts +27 -0
- package/src/hooks/useChartAutoResize.ts +73 -0
- package/src/hooks/useChartConnect.ts +5 -1
- package/src/hooks/useChartDownload.ts +1 -1
- package/src/hooks/useChartHistory.ts +1 -3
- package/src/hooks/useChartInit.ts +59 -0
- package/src/hooks/useChartOptions.ts +259 -0
- package/src/hooks/useChartPerformance.ts +109 -0
- package/src/hooks/useChartSelection.ts +23 -12
- package/src/hooks/useChartTheme.ts +51 -0
- package/src/hooks/useDataTransform.ts +19 -4
- package/src/index.ts +5 -10
- package/src/react-dom.d.ts +3 -3
- package/src/themes/index.ts +30 -855
- package/src/themes/palettes/blue-green.ts +13 -0
- package/src/themes/palettes/chalk.ts +13 -0
- package/src/themes/palettes/cyber.ts +44 -0
- package/src/themes/palettes/dark.ts +52 -0
- package/src/themes/palettes/default.ts +52 -0
- package/src/themes/palettes/elegant.ts +34 -0
- package/src/themes/palettes/forest.ts +13 -0
- package/src/themes/palettes/glass.ts +49 -0
- package/src/themes/palettes/golden.ts +13 -0
- package/src/themes/palettes/neon.ts +43 -0
- package/src/themes/palettes/ocean.ts +39 -0
- package/src/themes/palettes/pastel.ts +37 -0
- package/src/themes/palettes/purple-passion.ts +13 -0
- package/src/themes/palettes/retro.ts +33 -0
- package/src/themes/palettes/sunset.ts +40 -0
- package/src/themes/palettes/walden.ts +13 -0
- package/src/themes/registry.ts +184 -0
- package/src/themes/types.ts +213 -0
- package/src/core/utils/codeGenerator/CodeGenerator.ts +0 -669
- package/src/core/utils/codeGenerator/index.ts +0 -13
- package/src/core/utils/codeGenerator/types.ts +0 -198
- package/src/core/utils/configGenerator/ConfigGenerator.ts +0 -583
- package/src/core/utils/configGenerator/index.ts +0 -13
- package/src/core/utils/configGenerator/types.ts +0 -449
- package/src/core/utils/debug/DebugPanel.tsx +0 -640
- package/src/core/utils/debug/debugger.ts +0 -322
- package/src/core/utils/debug/index.ts +0 -21
- 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 | ((
|
|
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 | ((
|
|
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
|
-
|
|
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 | ((
|
|
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: (
|
|
294
|
-
data: [{ yAxis:
|
|
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?: (
|
|
52
|
-
onClick?: (
|
|
53
|
-
onDataZoom?: (
|
|
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;
|
|
66
|
+
onZoom?: (data: { start: number; end: number; _dataZoomIndex: number }) => void;
|
|
69
67
|
enableDataFiltering?: boolean;
|
|
70
|
-
|
|
71
|
-
onDataFiltered?: (
|
|
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?: (
|
|
75
|
-
onLegendUnselect?: (
|
|
76
|
-
onLegendSelectAll?: (
|
|
77
|
-
onLegendInverseSelect?: (
|
|
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
|
-
|
|
78
|
+
_params: EChartsMouseEventParams | EChartsMouseEventParams[]
|
|
81
79
|
) => React.ReactNode;
|
|
82
80
|
customTooltipStyle?: React.CSSProperties;
|
|
83
|
-
onTooltipShow?: (
|
|
84
|
-
onTooltipHide?: (
|
|
85
|
-
onExport?: (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
|
176
|
-
if (enableDataFiltering &&
|
|
177
|
-
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 {
|
|
181
|
-
if (seriesItem.
|
|
182
|
-
const filtered = filterDataByKeys(seriesItem.
|
|
183
|
-
if (onDataFiltered) onDataFiltered(filtered,
|
|
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,
|
|
183
|
+
return { ...seriesItem, _data: filtered.slice(start, end) };
|
|
195
184
|
}
|
|
196
|
-
return { ...seriesItem,
|
|
185
|
+
return { ...seriesItem, _data: filtered };
|
|
197
186
|
}
|
|
198
187
|
return seriesItem;
|
|
199
188
|
});
|
|
200
189
|
}
|
|
201
190
|
}
|
|
202
191
|
|
|
203
|
-
// Inject
|
|
192
|
+
// Inject _dataZoom when enableZoom is true (keyboard-accessible zoom)
|
|
204
193
|
if (_enableZoom) {
|
|
205
|
-
processed =
|
|
206
|
-
// Avoid duplicate
|
|
207
|
-
const existingDzArr = Array.isArray(processed.
|
|
208
|
-
? (processed.
|
|
209
|
-
: processed.
|
|
210
|
-
? [processed.
|
|
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.
|
|
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
|
|
223
|
-
const animConfig = generateEChartsAnimationConfig(animation,
|
|
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
|
-
|
|
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
|
-
(
|
|
240
|
-
chartInstanceRef.current =
|
|
241
|
-
adapterRef.current =
|
|
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,
|
|
247
|
+
if (chartId) registerChart(chartId, _instance);
|
|
259
248
|
|
|
260
249
|
// Setup internal event handlers for linkage + virtual scroll
|
|
261
|
-
if (
|
|
250
|
+
if (_instance) {
|
|
262
251
|
// Click linkage
|
|
263
252
|
if (linkageConfig.enableClickLinkage && chartId && linkageConfig.linkedChartIds) {
|
|
264
|
-
|
|
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:
|
|
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
|
-
|
|
274
|
-
const p =
|
|
262
|
+
_instance.on('_datazoom', (_params: unknown) => {
|
|
263
|
+
const p = _params as {
|
|
275
264
|
start?: number;
|
|
276
265
|
end?: number;
|
|
277
|
-
|
|
278
|
-
batch?: Array<{ start?: number; end?: 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
|
-
|
|
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: '
|
|
293
|
+
type: '_dataZoom',
|
|
305
294
|
start: p.start,
|
|
306
295
|
end: p.end,
|
|
307
|
-
|
|
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
|
-
|
|
316
|
-
const p =
|
|
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
|
-
|
|
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
|
-
|
|
341
|
-
onTooltipShow?.(
|
|
329
|
+
_instance.on('tooltipshow', (_params: unknown) =>
|
|
330
|
+
onTooltipShow?.(_params as EChartsTooltipEventParams)
|
|
342
331
|
);
|
|
343
|
-
|
|
344
|
-
onTooltipHide?.(
|
|
332
|
+
_instance.on('tooltiphide', (_params: unknown) =>
|
|
333
|
+
onTooltipHide?.(_params as EChartsTooltipEventParams)
|
|
345
334
|
);
|
|
346
|
-
|
|
335
|
+
_instance.setOption({
|
|
347
336
|
tooltip: {
|
|
348
|
-
formatter: (
|
|
349
|
-
String(customTooltipContent(
|
|
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
|
-
|
|
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:
|
|
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 ||
|
|
391
|
+
if (!onDataUpdate || _dataUpdateOptions?.enabled === false) return;
|
|
428
392
|
|
|
429
|
-
const delay =
|
|
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,
|
|
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('
|
|
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.
|
|
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 {
|
|
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
|
|
16
|
+
export interface UseChartPerformanceMetricsOptions {
|
|
17
17
|
enabled?: boolean;
|
|
18
18
|
onPerformance?: (data: PerformanceData) => void;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
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 =
|
|
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[] };
|
package/src/core/types/common.ts
CHANGED
|
@@ -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
|
|
124
|
+
export type _ChartEventListener = Record<string, (_params: ChartEventParams) => void>;
|
|
124
125
|
|
|
125
126
|
/**
|
|
126
127
|
* 图表渲染器类型
|