@agions/taroviz 1.3.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/cjs/index.js +1 -1
- package/dist/esm/index.js +3814 -2724
- package/package.json +1 -1
- package/src/__tests__/integration.test.tsx +12 -10
- package/src/adapters/BaseAdapter.ts +116 -0
- package/src/adapters/__tests__/index.test.ts +10 -10
- package/src/adapters/index.ts +63 -74
- package/src/adapters/swan/index.ts +26 -223
- package/src/adapters/tt/index.ts +28 -225
- package/src/adapters/types.ts +36 -0
- package/src/adapters/weapp/index.ts +29 -189
- package/src/charts/bar/index.tsx +5 -9
- package/src/charts/boxplot/__tests__/index.test.tsx +130 -0
- package/src/charts/boxplot/index.tsx +18 -0
- package/src/charts/boxplot/types.ts +46 -0
- package/src/charts/candlestick/__tests__/index.test.tsx +37 -0
- package/src/charts/candlestick/index.tsx +13 -0
- package/src/charts/common/BaseChartWrapper.tsx +47 -38
- package/src/charts/funnel/index.tsx +5 -9
- package/src/charts/gauge/index.tsx +5 -9
- package/src/charts/graph/__tests__/index.test.tsx +41 -0
- package/src/charts/graph/index.tsx +13 -0
- package/src/charts/heatmap/index.tsx +5 -9
- package/src/charts/index.ts +10 -1
- package/src/charts/line/index.tsx +4 -7
- package/src/charts/parallel/__tests__/index.test.tsx +164 -0
- package/src/charts/parallel/index.tsx +18 -0
- package/src/charts/parallel/types.ts +73 -0
- package/src/charts/pie/index.tsx +5 -10
- package/src/charts/radar/index.tsx +5 -9
- package/src/charts/scatter/index.tsx +5 -9
- package/src/charts/types.ts +48 -4
- package/src/charts/wordcloud/__tests__/index.test.tsx +36 -0
- package/src/charts/wordcloud/index.tsx +13 -0
- package/src/core/animation/AnimationManager.ts +15 -0
- package/src/core/components/Annotation.tsx +26 -21
- package/src/core/components/BaseChart.tsx +280 -1105
- package/src/core/components/ErrorBoundary.tsx +4 -1
- package/src/core/components/LazyChart.tsx +42 -55
- package/src/core/components/hooks/index.ts +20 -0
- package/src/core/components/hooks/useChartEvents.ts +143 -0
- package/src/core/components/hooks/useChartInit.ts +80 -0
- package/src/core/components/hooks/usePerformance.ts +186 -0
- package/src/core/components/hooks/useVirtualScroll.ts +156 -0
- package/src/core/echarts.ts +1 -1
- package/src/core/themes/ThemeManager.ts +31 -15
- package/src/core/types/index.ts +2 -2
- package/src/core/utils/chartInstances.ts +10 -3
- package/src/core/utils/chartUtils.ts +46 -0
- package/src/core/utils/common.ts +14 -1
- package/src/core/utils/export/ExportUtils.ts +13 -22
- package/src/core/utils/performance/PerformanceAnalyzer.ts +32 -5
- package/src/core/utils/uuid.ts +1 -1
- package/src/editor/EnhancedThemeEditor.tsx +624 -0
- package/src/editor/ThemeEditor.tsx +1 -6
- package/src/hooks/__tests__/index.test.tsx +14 -11
- package/src/hooks/__tests__/useDataTransform.test.ts +159 -0
- package/src/hooks/index.ts +54 -19
- package/src/hooks/useDataTransform.ts +503 -0
- package/src/index.ts +27 -9
- package/src/main.tsx +4 -4
- package/src/themes/__tests__/index.test.ts +2 -2
- package/src/themes/index.ts +13 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { render, screen } from '@testing-library/react';
|
|
6
|
+
import '@testing-library/jest-dom';
|
|
7
|
+
import ParallelChart from '../index';
|
|
8
|
+
|
|
9
|
+
// Mock ECharts and adapters
|
|
10
|
+
jest.mock('echarts/core', () => ({
|
|
11
|
+
use: jest.fn(),
|
|
12
|
+
init: jest.fn(() => ({
|
|
13
|
+
setOption: jest.fn(),
|
|
14
|
+
showLoading: jest.fn(),
|
|
15
|
+
hideLoading: jest.fn(),
|
|
16
|
+
on: jest.fn(),
|
|
17
|
+
off: jest.fn(),
|
|
18
|
+
dispose: jest.fn(),
|
|
19
|
+
resize: jest.fn(),
|
|
20
|
+
})),
|
|
21
|
+
getInstanceByDom: jest.fn(),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
// Mock ECharts components
|
|
25
|
+
jest.mock('echarts/components', () => ({
|
|
26
|
+
GridComponent: jest.fn(),
|
|
27
|
+
TooltipComponent: jest.fn(),
|
|
28
|
+
TitleComponent: jest.fn(),
|
|
29
|
+
LegendComponent: jest.fn(),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
// Mock BaseChartWrapper
|
|
33
|
+
jest.mock('../../common/BaseChartWrapper', () => ({
|
|
34
|
+
__esModule: true,
|
|
35
|
+
default: (props: any) => (
|
|
36
|
+
<div
|
|
37
|
+
data-testid="parallel-chart"
|
|
38
|
+
className={`taroviz-parallel ${props.className || ''}`}
|
|
39
|
+
style={{ width: props.width || '100%', height: props.height || 300, ...props.style }}
|
|
40
|
+
>
|
|
41
|
+
<div data-testid="chart-option">{JSON.stringify(props.option)}</div>
|
|
42
|
+
</div>
|
|
43
|
+
),
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
describe('ParallelChart', () => {
|
|
47
|
+
const basicOption = {
|
|
48
|
+
title: { text: '平行坐标图测试' },
|
|
49
|
+
parallel: {
|
|
50
|
+
left: '5%',
|
|
51
|
+
right: '10%',
|
|
52
|
+
bottom: '10%',
|
|
53
|
+
top: '20%',
|
|
54
|
+
height: '50%',
|
|
55
|
+
},
|
|
56
|
+
parallelAxisDefault: {
|
|
57
|
+
type: 'value' as const,
|
|
58
|
+
name: '指标',
|
|
59
|
+
},
|
|
60
|
+
series: [{
|
|
61
|
+
type: 'parallel' as const,
|
|
62
|
+
lineStyle: { width: 2, opacity: 0.5 },
|
|
63
|
+
data: [
|
|
64
|
+
[1, 55, 9, 56, 0.46, 2, 35],
|
|
65
|
+
[2, 25, 11, 21, 0.65, 2, 33],
|
|
66
|
+
[3, 56, 7, 63, 0.92, 3, 45],
|
|
67
|
+
],
|
|
68
|
+
}],
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
describe('Basic Rendering', () => {
|
|
72
|
+
it('should render without crashing', () => {
|
|
73
|
+
render(<ParallelChart option={basicOption} />);
|
|
74
|
+
expect(screen.getByTestId('parallel-chart')).toBeInTheDocument();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should render with custom width and height', () => {
|
|
78
|
+
render(<ParallelChart option={basicOption} width={600} height={500} />);
|
|
79
|
+
expect(screen.getByTestId('parallel-chart')).toBeInTheDocument();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should have correct display name', () => {
|
|
83
|
+
expect(ParallelChart.displayName).toBe('ParallelChart');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should pass option to wrapper', () => {
|
|
87
|
+
render(<ParallelChart option={basicOption} />);
|
|
88
|
+
expect(screen.getByTestId('chart-option')).toHaveTextContent('平行坐标图测试');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('Props', () => {
|
|
93
|
+
it('should accept className prop', () => {
|
|
94
|
+
render(<ParallelChart option={basicOption} className="test-class" />);
|
|
95
|
+
expect(screen.getByTestId('parallel-chart')).toHaveClass('test-class');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should accept style prop', () => {
|
|
99
|
+
const style = { padding: '10px' };
|
|
100
|
+
render(<ParallelChart option={basicOption} style={style} />);
|
|
101
|
+
expect(screen.getByTestId('parallel-chart')).toBeInTheDocument();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should accept loading prop', () => {
|
|
105
|
+
render(<ParallelChart option={basicOption} loading={true} />);
|
|
106
|
+
expect(screen.getByTestId('parallel-chart')).toBeInTheDocument();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should accept theme prop', () => {
|
|
110
|
+
render(<ParallelChart option={basicOption} theme="dark" />);
|
|
111
|
+
expect(screen.getByTestId('parallel-chart')).toBeInTheDocument();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('Chart Options', () => {
|
|
116
|
+
it('should render with expandable axis', () => {
|
|
117
|
+
const expandableOption = {
|
|
118
|
+
...basicOption,
|
|
119
|
+
parallel: {
|
|
120
|
+
...basicOption.parallel,
|
|
121
|
+
axisExpandable: true,
|
|
122
|
+
axisExpandCenter: 3,
|
|
123
|
+
axisExpandCount: 3,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
render(<ParallelChart option={expandableOption} />);
|
|
127
|
+
expect(screen.getByTestId('parallel-chart')).toBeInTheDocument();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should render with custom lineStyle', () => {
|
|
131
|
+
const customLineOption = {
|
|
132
|
+
...basicOption,
|
|
133
|
+
series: [{
|
|
134
|
+
type: 'parallel' as const,
|
|
135
|
+
lineStyle: { width: 3, color: '#1890ff', opacity: 0.8 },
|
|
136
|
+
data: [[1, 55, 9, 56, 0.46, 2, 35]],
|
|
137
|
+
}],
|
|
138
|
+
};
|
|
139
|
+
render(<ParallelChart option={customLineOption} />);
|
|
140
|
+
expect(screen.getByTestId('parallel-chart')).toBeInTheDocument();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should render with category axis', () => {
|
|
144
|
+
const categoryOption = {
|
|
145
|
+
title: { text: '分类轴测试' },
|
|
146
|
+
parallel: { left: '5%', right: '10%', bottom: '10%', top: '20%' },
|
|
147
|
+
parallelAxisDefault: {
|
|
148
|
+
type: 'category' as const,
|
|
149
|
+
data: ['A', 'B', 'C', 'D', 'E'],
|
|
150
|
+
name: '分类',
|
|
151
|
+
},
|
|
152
|
+
series: [{
|
|
153
|
+
type: 'parallel' as const,
|
|
154
|
+
data: [
|
|
155
|
+
['A', 55, 9, 56, 0.46, 2, 35],
|
|
156
|
+
['B', 25, 11, 21, 0.65, 2, 33],
|
|
157
|
+
],
|
|
158
|
+
}],
|
|
159
|
+
};
|
|
160
|
+
render(<ParallelChart option={categoryOption} />);
|
|
161
|
+
expect(screen.getByTestId('parallel-chart')).toBeInTheDocument();
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 平行坐标图组件
|
|
3
|
+
* 用于展示高维数据各维度之间的关系
|
|
4
|
+
*/
|
|
5
|
+
import React, { memo } from 'react';
|
|
6
|
+
import BaseChartWrapper from '../common/BaseChartWrapper';
|
|
7
|
+
import { ParallelChartProps } from './types';
|
|
8
|
+
|
|
9
|
+
const ParallelChart: React.FC<ParallelChartProps> = memo((props) => (
|
|
10
|
+
<BaseChartWrapper {...props} chartType="parallel" />
|
|
11
|
+
));
|
|
12
|
+
|
|
13
|
+
ParallelChart.displayName = 'ParallelChart';
|
|
14
|
+
|
|
15
|
+
export default ParallelChart;
|
|
16
|
+
|
|
17
|
+
// 导出类型
|
|
18
|
+
export type { ParallelChartProps, ParallelOption, ParallelAxisSetting } from './types';
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 平行坐标图类型定义
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type ParallelChartProps = {
|
|
6
|
+
option?: ParallelOption;
|
|
7
|
+
width?: string | number;
|
|
8
|
+
height?: string | number;
|
|
9
|
+
className?: string;
|
|
10
|
+
style?: React.CSSProperties;
|
|
11
|
+
onEvents?: Record<string, (params: any) => void>;
|
|
12
|
+
loading?: boolean;
|
|
13
|
+
loadingOption?: any;
|
|
14
|
+
theme?: string;
|
|
15
|
+
onChartReady?: (chart: any) => void;
|
|
16
|
+
opts?: {
|
|
17
|
+
devicePixelRatio?: number;
|
|
18
|
+
renderer?: 'canvas' | 'svg';
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export interface ParallelOption {
|
|
23
|
+
title?: any;
|
|
24
|
+
legend?: any;
|
|
25
|
+
parallel?: ParallelAxisSetting;
|
|
26
|
+
parallelAxisDefault?: ParallelAxisItem;
|
|
27
|
+
grid?: any;
|
|
28
|
+
tooltip?: any;
|
|
29
|
+
series: ParallelSeriesItem[];
|
|
30
|
+
dataset?: any;
|
|
31
|
+
color?: string[];
|
|
32
|
+
backgroundColor?: any;
|
|
33
|
+
textStyle?: any;
|
|
34
|
+
[key: string]: any;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ParallelAxisSetting {
|
|
38
|
+
left?: number | string;
|
|
39
|
+
right?: number | string;
|
|
40
|
+
top?: number | string;
|
|
41
|
+
bottom?: number | string;
|
|
42
|
+
width?: number | string;
|
|
43
|
+
height?: number | string;
|
|
44
|
+
axisExpandable?: boolean;
|
|
45
|
+
axisExpandCenter?: number;
|
|
46
|
+
axisExpandCount?: number;
|
|
47
|
+
axisExpandWidth?: number;
|
|
48
|
+
z?: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ParallelAxisItem {
|
|
52
|
+
type?: 'value' | 'category';
|
|
53
|
+
name?: string;
|
|
54
|
+
nameLocation?: 'start' | 'middle' | 'center' | 'end';
|
|
55
|
+
nameTextStyle?: any;
|
|
56
|
+
nameGap?: number;
|
|
57
|
+
silent?: boolean;
|
|
58
|
+
data?: any[];
|
|
59
|
+
dimension?: number;
|
|
60
|
+
parallelIndex?: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface ParallelSeriesItem {
|
|
64
|
+
type: 'parallel';
|
|
65
|
+
name?: string;
|
|
66
|
+
data?: any[];
|
|
67
|
+
lineStyle?: any;
|
|
68
|
+
emphasis?: any;
|
|
69
|
+
smooth?: boolean | number;
|
|
70
|
+
symbol?: string;
|
|
71
|
+
symbolSize?: number;
|
|
72
|
+
itemStyle?: any;
|
|
73
|
+
}
|
package/src/charts/pie/index.tsx
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* PieChart组件
|
|
3
3
|
*/
|
|
4
|
-
import React from 'react';
|
|
5
|
-
|
|
4
|
+
import React, { memo } from 'react';
|
|
6
5
|
import BaseChartWrapper from '../common/BaseChartWrapper';
|
|
7
6
|
import { BaseChartProps } from '../types';
|
|
8
|
-
|
|
9
|
-
// 导入统一注册的 echarts
|
|
10
7
|
import '@/core/echarts';
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
* 饼图组件
|
|
14
|
-
*/
|
|
15
|
-
const PieChart: React.FC<BaseChartProps> = (props) => (
|
|
9
|
+
const PieChart: React.FC<BaseChartProps> = memo((props) => (
|
|
16
10
|
<BaseChartWrapper {...props} chartType="pie-chart" />
|
|
17
|
-
);
|
|
11
|
+
));
|
|
12
|
+
PieChart.displayName = 'PieChart';
|
|
18
13
|
|
|
19
14
|
export default PieChart;
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* RadarChart组件
|
|
3
3
|
*/
|
|
4
|
-
import React from 'react';
|
|
5
|
-
|
|
4
|
+
import React, { memo } from 'react';
|
|
6
5
|
import BaseChartWrapper from '../common/BaseChartWrapper';
|
|
7
6
|
import { RadarChartProps } from '../types';
|
|
8
|
-
|
|
9
7
|
import '@/core/echarts';
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
* 雷达图组件
|
|
13
|
-
*/
|
|
14
|
-
const RadarChart: React.FC<RadarChartProps> = (props) => (
|
|
9
|
+
const RadarChart: React.FC<RadarChartProps> = memo((props) => (
|
|
15
10
|
<BaseChartWrapper {...props} chartType="radar-chart" />
|
|
16
|
-
);
|
|
11
|
+
));
|
|
12
|
+
RadarChart.displayName = 'RadarChart';
|
|
17
13
|
|
|
18
14
|
export default RadarChart;
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* ScatterChart组件
|
|
3
3
|
*/
|
|
4
|
-
import React from 'react';
|
|
5
|
-
|
|
4
|
+
import React, { memo } from 'react';
|
|
6
5
|
import BaseChartWrapper from '../common/BaseChartWrapper';
|
|
7
6
|
import { ScatterChartProps } from '../types';
|
|
8
|
-
|
|
9
7
|
import '@/core/echarts';
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
* 散点图组件
|
|
13
|
-
*/
|
|
14
|
-
const ScatterChart: React.FC<ScatterChartProps> = (props) => (
|
|
9
|
+
const ScatterChart: React.FC<ScatterChartProps> = memo((props) => (
|
|
15
10
|
<BaseChartWrapper {...props} chartType="scatter-chart" />
|
|
16
|
-
);
|
|
11
|
+
));
|
|
12
|
+
ScatterChart.displayName = 'ScatterChart';
|
|
17
13
|
|
|
18
14
|
export default ScatterChart;
|
package/src/charts/types.ts
CHANGED
|
@@ -713,7 +713,7 @@ export interface SankeyChartProps extends BaseChartProps {
|
|
|
713
713
|
* 箱线图属性
|
|
714
714
|
* 用于展示数据的分布情况
|
|
715
715
|
*/
|
|
716
|
-
export interface BoxplotChartProps extends BaseChartProps {
|
|
716
|
+
export interface BoxplotChartProps extends Omit<BaseChartProps, 'data'> {
|
|
717
717
|
/** 图表类型 */
|
|
718
718
|
type?: 'boxplot';
|
|
719
719
|
|
|
@@ -743,9 +743,9 @@ export interface BoxplotChartProps extends BaseChartProps {
|
|
|
743
743
|
* K线图/股票图属性
|
|
744
744
|
* 用于展示股票、外汇等金融数据
|
|
745
745
|
*/
|
|
746
|
-
export interface CandlestickChartProps extends BaseChartProps {
|
|
746
|
+
export interface CandlestickChartProps extends Omit<BaseChartProps, 'data'> {
|
|
747
747
|
/** K线数据数组,每项为 [open, close, lowest, highest] */
|
|
748
|
-
|
|
748
|
+
candlestickData?: number[][];
|
|
749
749
|
|
|
750
750
|
/** X轴数据 */
|
|
751
751
|
xAxisData?: (string | number)[];
|
|
@@ -874,4 +874,48 @@ export interface ChartBuilderOptions<T extends BaseChartProps = BaseChartProps>
|
|
|
874
874
|
}
|
|
875
875
|
|
|
876
876
|
// 重新导出 ECharts 类型
|
|
877
|
-
export type { EChartsOption, ECharts
|
|
877
|
+
export type { EChartsOption, ECharts } from 'echarts';
|
|
878
|
+
|
|
879
|
+
// EChartsCoreOption 是 ECBasicOption 的别名
|
|
880
|
+
export type { ECBasicOption as EChartsCoreOption } from 'echarts/types/dist/shared';
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* 词云图属性
|
|
884
|
+
* 用于展示文本数据的词频分布
|
|
885
|
+
*/
|
|
886
|
+
export interface WordCloudChartProps extends BaseChartProps {
|
|
887
|
+
/** 词云数据 - 也可以通过 option.series[0].data 传入 */
|
|
888
|
+
wordCloudData?: Array<{
|
|
889
|
+
name: string;
|
|
890
|
+
value: number;
|
|
891
|
+
textStyle?: Record<string, unknown>;
|
|
892
|
+
emphasis?: Record<string, unknown>;
|
|
893
|
+
}>;
|
|
894
|
+
|
|
895
|
+
/** 词云形状 */
|
|
896
|
+
shape?: 'circle' | 'cardioid' | 'diamond' | 'triangle' | 'star' | 'pentagon' | 'square';
|
|
897
|
+
|
|
898
|
+
/** 字体大小范围 */
|
|
899
|
+
sizeRange?: [number, number];
|
|
900
|
+
|
|
901
|
+
/** 词云旋转角度范围 */
|
|
902
|
+
rotationRange?: [number, number];
|
|
903
|
+
|
|
904
|
+
/** 旋转步长 */
|
|
905
|
+
rotationStep?: number;
|
|
906
|
+
|
|
907
|
+
/** 词云间距 */
|
|
908
|
+
gridSize?: number;
|
|
909
|
+
|
|
910
|
+
/** 是否绘制词云轮廓 */
|
|
911
|
+
drawOutOfBound?: boolean;
|
|
912
|
+
|
|
913
|
+
/** 文字样式 */
|
|
914
|
+
textStyle?: Record<string, unknown>;
|
|
915
|
+
|
|
916
|
+
/** 强调状态 */
|
|
917
|
+
emphasis?: {
|
|
918
|
+
focus?: 'self' | 'adjacency';
|
|
919
|
+
textStyle?: Record<string, unknown>;
|
|
920
|
+
};
|
|
921
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @version v1.5.0
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { render } from '@testing-library/react';
|
|
7
|
+
import WordCloudChart from '../index';
|
|
8
|
+
|
|
9
|
+
describe('WordCloudChart', () => {
|
|
10
|
+
it('renders without crashing', () => {
|
|
11
|
+
const { container } = render(<WordCloudChart />);
|
|
12
|
+
expect(container).toBeTruthy();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('renders with custom width and height', () => {
|
|
16
|
+
const { container } = render(<WordCloudChart width={600} height={400} />);
|
|
17
|
+
expect(container.firstChild).toHaveStyle({ width: '600px', height: '400px' });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('renders with word data', () => {
|
|
21
|
+
const option = {
|
|
22
|
+
series: [
|
|
23
|
+
{
|
|
24
|
+
type: 'wordCloud' as const,
|
|
25
|
+
data: [
|
|
26
|
+
{ name: 'JavaScript', value: 10000 },
|
|
27
|
+
{ name: 'TypeScript', value: 8000 },
|
|
28
|
+
{ name: 'React', value: 6000 },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
} as any;
|
|
33
|
+
const { container } = render(<WordCloudChart option={option} />);
|
|
34
|
+
expect(container).toBeTruthy();
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordCloudChart组件
|
|
3
|
+
*/
|
|
4
|
+
import React, { memo } from 'react';
|
|
5
|
+
import BaseChartWrapper from '../common/BaseChartWrapper';
|
|
6
|
+
import { WordCloudChartProps } from '../types';
|
|
7
|
+
|
|
8
|
+
const WordCloudChart: React.FC<WordCloudChartProps> = memo((props) => (
|
|
9
|
+
<BaseChartWrapper {...props} chartType="wordcloud-chart" />
|
|
10
|
+
));
|
|
11
|
+
WordCloudChart.displayName = 'WordCloudChart';
|
|
12
|
+
|
|
13
|
+
export default WordCloudChart;
|
|
@@ -141,6 +141,9 @@ export class AnimationManager {
|
|
|
141
141
|
public static getInstance(config?: AnimationManagerConfig): AnimationManager {
|
|
142
142
|
if (!AnimationManager.instance) {
|
|
143
143
|
AnimationManager.instance = new AnimationManager(config);
|
|
144
|
+
} else if (config) {
|
|
145
|
+
// 如果传入了新配置,更新配置
|
|
146
|
+
AnimationManager.instance.updateConfig(config);
|
|
144
147
|
}
|
|
145
148
|
return AnimationManager.instance;
|
|
146
149
|
}
|
|
@@ -345,6 +348,18 @@ export class AnimationManager {
|
|
|
345
348
|
};
|
|
346
349
|
}
|
|
347
350
|
|
|
351
|
+
/**
|
|
352
|
+
* 更新配置
|
|
353
|
+
*/
|
|
354
|
+
public updateConfig(config: AnimationManagerConfig): void {
|
|
355
|
+
if (config.defaultConfig) {
|
|
356
|
+
this.defaultConfig = config.defaultConfig;
|
|
357
|
+
}
|
|
358
|
+
if (config.performance) {
|
|
359
|
+
this.updatePerformanceConfig(config.performance);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
348
363
|
/**
|
|
349
364
|
* 获取性能配置
|
|
350
365
|
*/
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* TaroViz 图表标注组件
|
|
3
3
|
* 支持在图表上添加标记线、标记区域、散点等标注
|
|
4
4
|
*/
|
|
5
|
-
import
|
|
5
|
+
import { useMemo } from 'react';
|
|
6
6
|
import type { EChartsOption } from 'echarts';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -209,7 +209,9 @@ export function convertAnnotationToMarkArea(config: MarkAreaConfig): EChartsOpti
|
|
|
209
209
|
/**
|
|
210
210
|
* 将散点标注配置转换为 ECharts 格式
|
|
211
211
|
*/
|
|
212
|
-
export function convertAnnotationToScatter(
|
|
212
|
+
export function convertAnnotationToScatter(
|
|
213
|
+
config: ScatterAnnotationConfig
|
|
214
|
+
): EChartsOption['series'] {
|
|
213
215
|
const { data, symbol, symbolSize, itemStyle, label } = config;
|
|
214
216
|
|
|
215
217
|
return [
|
|
@@ -227,7 +229,7 @@ export function convertAnnotationToScatter(config: ScatterAnnotationConfig): ECh
|
|
|
227
229
|
},
|
|
228
230
|
data,
|
|
229
231
|
},
|
|
230
|
-
},
|
|
232
|
+
} as any,
|
|
231
233
|
];
|
|
232
234
|
}
|
|
233
235
|
|
|
@@ -239,18 +241,22 @@ export function useAnnotation(props: AnnotationProps): EChartsOption {
|
|
|
239
241
|
const { type, markLine, markArea, scatter } = props;
|
|
240
242
|
|
|
241
243
|
return useMemo(() => {
|
|
242
|
-
|
|
244
|
+
// 使用 any 避免类型复杂性问题
|
|
245
|
+
const series: any[] = [];
|
|
243
246
|
|
|
244
247
|
if (type === 'line' && markLine) {
|
|
245
|
-
|
|
248
|
+
const markLineResult = convertAnnotationToMarkLine(markLine);
|
|
249
|
+
series.push(...(Array.isArray(markLineResult) ? markLineResult : [markLineResult]));
|
|
246
250
|
}
|
|
247
251
|
|
|
248
252
|
if (type === 'area' && markArea) {
|
|
249
|
-
|
|
253
|
+
const markAreaResult = convertAnnotationToMarkArea(markArea);
|
|
254
|
+
series.push(...(Array.isArray(markAreaResult) ? markAreaResult : [markAreaResult]));
|
|
250
255
|
}
|
|
251
256
|
|
|
252
257
|
if (type === 'scatter' && scatter) {
|
|
253
|
-
|
|
258
|
+
const scatterResult = convertAnnotationToScatter(scatter);
|
|
259
|
+
series.push(...(Array.isArray(scatterResult) ? scatterResult : [scatterResult]));
|
|
254
260
|
}
|
|
255
261
|
|
|
256
262
|
return { series };
|
|
@@ -263,46 +269,42 @@ export function useAnnotation(props: AnnotationProps): EChartsOption {
|
|
|
263
269
|
export const AnnotationPresets = {
|
|
264
270
|
/** 平均线 */
|
|
265
271
|
averageLine: (color = '#1890ff'): MarkLineConfig => ({
|
|
266
|
-
data: [{ type: 'average', name: '平均值' }],
|
|
272
|
+
data: [{ type: 'average', name: '平均值' }] as any,
|
|
267
273
|
lineStyle: { color, type: 'dashed', width: 2 },
|
|
268
274
|
label: { show: true, position: 'end', color },
|
|
269
275
|
}),
|
|
270
276
|
|
|
271
277
|
/** 最大值线 */
|
|
272
278
|
maxLine: (color = '#f5222d'): MarkLineConfig => ({
|
|
273
|
-
data: [{ type: 'max', name: '最大值' }],
|
|
279
|
+
data: [{ type: 'max', name: '最大值' }] as any,
|
|
274
280
|
lineStyle: { color, type: 'dashed', width: 2 },
|
|
275
281
|
label: { show: true, position: 'end', color },
|
|
276
282
|
}),
|
|
277
283
|
|
|
278
284
|
/** 最小值线 */
|
|
279
285
|
minLine: (color = '#52c41a'): MarkLineConfig => ({
|
|
280
|
-
data: [{ type: 'min', name: '最小值' }],
|
|
286
|
+
data: [{ type: 'min', name: '最小值' }] as any,
|
|
281
287
|
lineStyle: { color, type: 'dashed', width: 2 },
|
|
282
288
|
label: { show: true, position: 'end', color },
|
|
283
289
|
}),
|
|
284
290
|
|
|
285
291
|
/** 警戒线 */
|
|
286
292
|
thresholdLine: (value: number, color = '#faad14'): MarkLineConfig => ({
|
|
287
|
-
data: [{ yAxis: value, name: '警戒线' }],
|
|
293
|
+
data: [{ yAxis: value, name: '警戒线' }] as any,
|
|
288
294
|
lineStyle: { color, type: 'solid', width: 2 },
|
|
289
295
|
label: { show: true, position: 'start', color },
|
|
290
296
|
}),
|
|
291
297
|
|
|
292
298
|
/** 目标区域 */
|
|
293
299
|
targetArea: (min: number, max: number, color = 'rgba(82, 196, 26, 0.1)'): MarkAreaConfig => ({
|
|
294
|
-
data: [
|
|
295
|
-
[{ yAxis: min }, { yAxis: max }],
|
|
296
|
-
],
|
|
300
|
+
data: [[{ yAxis: min }, { yAxis: max }]],
|
|
297
301
|
style: { color, opacity: 0.3 },
|
|
298
302
|
label: { show: true, position: 'inside', color: '#52c41a' },
|
|
299
303
|
}),
|
|
300
304
|
|
|
301
305
|
/** 预警区域 */
|
|
302
306
|
warningArea: (min: number, max: number, color = 'rgba(250, 173, 20, 0.1)'): MarkAreaConfig => ({
|
|
303
|
-
data: [
|
|
304
|
-
[{ yAxis: min }, { yAxis: max }],
|
|
305
|
-
],
|
|
307
|
+
data: [[{ yAxis: min }, { yAxis: max }]],
|
|
306
308
|
style: { color, opacity: 0.3 },
|
|
307
309
|
label: { show: true, position: 'inside', color: '#faad14' },
|
|
308
310
|
}),
|
|
@@ -319,17 +321,20 @@ export function createCompositeAnnotation(
|
|
|
319
321
|
scatter?: ScatterAnnotationConfig;
|
|
320
322
|
}>
|
|
321
323
|
): EChartsOption {
|
|
322
|
-
const allSeries:
|
|
324
|
+
const allSeries: any[] = [];
|
|
323
325
|
|
|
324
326
|
annotations.forEach((annotation) => {
|
|
325
327
|
if (annotation.type === 'line' && annotation.markLine) {
|
|
326
|
-
|
|
328
|
+
const result = convertAnnotationToMarkLine(annotation.markLine);
|
|
329
|
+
allSeries.push(...(Array.isArray(result) ? result : [result]));
|
|
327
330
|
}
|
|
328
331
|
if (annotation.type === 'area' && annotation.markArea) {
|
|
329
|
-
|
|
332
|
+
const result = convertAnnotationToMarkArea(annotation.markArea);
|
|
333
|
+
allSeries.push(...(Array.isArray(result) ? result : [result]));
|
|
330
334
|
}
|
|
331
335
|
if (annotation.type === 'scatter' && annotation.scatter) {
|
|
332
|
-
|
|
336
|
+
const result = convertAnnotationToScatter(annotation.scatter);
|
|
337
|
+
allSeries.push(...(Array.isArray(result) ? result : [result]));
|
|
333
338
|
}
|
|
334
339
|
});
|
|
335
340
|
|