@agions/taroviz 1.6.0 → 1.7.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 +26 -21
- package/dist/cjs/index.js +1 -1
- package/dist/esm/index.js +49185 -2194
- package/package.json +2 -1
- package/src/charts/index.ts +5 -1
- package/src/charts/liquid/index.tsx +226 -0
- package/src/charts/liquid/types.ts +130 -0
- package/src/charts/tree/index.tsx +117 -0
- package/src/charts/tree/types.ts +174 -0
- package/src/components/DataFilter/index.tsx +587 -0
- package/src/core/utils/drillDown.ts +643 -0
- package/src/hooks/index.ts +39 -1
- package/src/hooks/useChartConnect.ts +362 -0
- package/src/hooks/useChartDownload.ts +692 -0
- package/src/hooks/useDataZoom.ts +323 -0
- package/src/index.ts +25 -2
- package/src/themes/index.ts +3 -0
- package/src/themes/useAutoTheme.ts +66 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useChartConnect - 图表联动 Hook
|
|
3
|
+
* 实现多个图表之间的联动(chartConnect),当一个图表被操作时,其他联动图表同步变化
|
|
4
|
+
*/
|
|
5
|
+
import { useRef, useCallback, useEffect } from 'react';
|
|
6
|
+
import type { ChartInstance } from './index';
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// 类型定义
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/** 联动事件类型 */
|
|
13
|
+
export type ConnectEventType = 'click' | 'hover' | 'select' | 'dataZoom';
|
|
14
|
+
|
|
15
|
+
/** 联动配置选项 */
|
|
16
|
+
export interface UseChartConnectOptions {
|
|
17
|
+
/** 要绑定的图表 ID 列表 */
|
|
18
|
+
chartIds: string[];
|
|
19
|
+
/** 联动触发的事件类型 */
|
|
20
|
+
events?: ConnectEventType[];
|
|
21
|
+
/** 是否自动绑定/解绑 */
|
|
22
|
+
autoBind?: boolean;
|
|
23
|
+
/** 联动组名称 */
|
|
24
|
+
groupName?: string;
|
|
25
|
+
/** 是否禁用 */
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
/** 联动回调 */
|
|
28
|
+
onConnect?: (sourceId: string, targetId: string, payload: any) => void;
|
|
29
|
+
/** 事件过滤函数 */
|
|
30
|
+
eventFilter?: (event: string, params: any) => boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** 图表联动项 */
|
|
34
|
+
interface ChartConnectItem {
|
|
35
|
+
instance: ChartInstance;
|
|
36
|
+
id: string;
|
|
37
|
+
handlers: Map<string, any>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** 联动返回值 */
|
|
41
|
+
export interface UseChartConnectReturn {
|
|
42
|
+
/** 绑定图表到联动组 */
|
|
43
|
+
connect: (chartInstance: ChartInstance, chartId?: string) => void;
|
|
44
|
+
/** 解除图表联动 */
|
|
45
|
+
disconnect: (chartInstance: ChartInstance, chartId?: string) => void;
|
|
46
|
+
/** 触发联动事件 */
|
|
47
|
+
dispatchConnect: (sourceId: string, payload: any) => void;
|
|
48
|
+
/** 批量连接图表 */
|
|
49
|
+
connectAll: (charts: Array<{ instance: ChartInstance; id: string }>) => void;
|
|
50
|
+
/** 批量断开图表 */
|
|
51
|
+
disconnectAll: () => void;
|
|
52
|
+
/** 联动组是否已连接 */
|
|
53
|
+
isConnected: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// Hook 实现
|
|
58
|
+
// ============================================================================
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 使用图表联动
|
|
62
|
+
* @param options 配置选项
|
|
63
|
+
* @returns 图表联动操作接口
|
|
64
|
+
*/
|
|
65
|
+
export function useChartConnect(options: UseChartConnectOptions): UseChartConnectReturn {
|
|
66
|
+
const {
|
|
67
|
+
chartIds = [],
|
|
68
|
+
events = ['click', 'hover', 'select', 'dataZoom'],
|
|
69
|
+
autoBind = false,
|
|
70
|
+
groupName,
|
|
71
|
+
disabled = false,
|
|
72
|
+
onConnect,
|
|
73
|
+
eventFilter,
|
|
74
|
+
} = options;
|
|
75
|
+
|
|
76
|
+
// Refs
|
|
77
|
+
const chartsRef = useRef<Map<string, ChartConnectItem>>(new Map());
|
|
78
|
+
const connectedRef = useRef(false);
|
|
79
|
+
const optionsRef = useRef(options);
|
|
80
|
+
optionsRef.current = options;
|
|
81
|
+
|
|
82
|
+
// 存储事件处理器的映射
|
|
83
|
+
const eventHandlersRef = useRef<Map<string, Map<string, any>>>(new Map());
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 创建联动事件处理器
|
|
87
|
+
*/
|
|
88
|
+
const createConnectHandler = useCallback(
|
|
89
|
+
(sourceId: string, eventType: ConnectEventType) => {
|
|
90
|
+
return (params: any) => {
|
|
91
|
+
if (disabled) return;
|
|
92
|
+
|
|
93
|
+
// 应用事件过滤器
|
|
94
|
+
if (eventFilter && !eventFilter(eventType, params)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 分发联动事件到所有其他图表
|
|
99
|
+
dispatchToOthers(sourceId, eventType, params);
|
|
100
|
+
|
|
101
|
+
// 触发回调
|
|
102
|
+
const currentOptions = optionsRef.current;
|
|
103
|
+
if (currentOptions.onConnect) {
|
|
104
|
+
chartsRef.current.forEach((item, targetId) => {
|
|
105
|
+
if (targetId !== sourceId) {
|
|
106
|
+
currentOptions.onConnect?.(sourceId, targetId, { eventType, params });
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
[disabled, eventFilter]
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 分发事件到其他图表
|
|
117
|
+
*/
|
|
118
|
+
const dispatchToOthers = useCallback(
|
|
119
|
+
(sourceId: string, eventType: ConnectEventType, params: any) => {
|
|
120
|
+
chartsRef.current.forEach((item, targetId) => {
|
|
121
|
+
if (targetId === sourceId) return;
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
switch (eventType) {
|
|
125
|
+
case 'click':
|
|
126
|
+
item.instance.dispatchAction?.({
|
|
127
|
+
type: 'showTip',
|
|
128
|
+
seriesIndex: params?.seriesIndex,
|
|
129
|
+
dataIndex: params?.dataIndex,
|
|
130
|
+
});
|
|
131
|
+
break;
|
|
132
|
+
|
|
133
|
+
case 'hover':
|
|
134
|
+
item.instance.dispatchAction?.({
|
|
135
|
+
type: 'showTip',
|
|
136
|
+
seriesIndex: params?.seriesIndex,
|
|
137
|
+
dataIndex: params?.dataIndex,
|
|
138
|
+
});
|
|
139
|
+
break;
|
|
140
|
+
|
|
141
|
+
case 'select':
|
|
142
|
+
item.instance.dispatchAction?.({
|
|
143
|
+
type: 'toggleSelect',
|
|
144
|
+
seriesIndex: params?.seriesIndex,
|
|
145
|
+
dataIndex: params?.dataIndex,
|
|
146
|
+
});
|
|
147
|
+
break;
|
|
148
|
+
|
|
149
|
+
case 'dataZoom':
|
|
150
|
+
item.instance.dispatchAction?.({
|
|
151
|
+
type: 'dataZoom',
|
|
152
|
+
start: params?.start,
|
|
153
|
+
end: params?.end,
|
|
154
|
+
dataZoomIndex: params?.dataZoomIndex,
|
|
155
|
+
dataZoomIndexs: params?.dataZoomIndexs,
|
|
156
|
+
});
|
|
157
|
+
break;
|
|
158
|
+
|
|
159
|
+
default:
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
} catch (e) {
|
|
163
|
+
console.warn(`[useChartConnect] Failed to dispatch ${eventType} to ${targetId}:`, e);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
},
|
|
167
|
+
[]
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 绑定单个图表的联动事件
|
|
172
|
+
*/
|
|
173
|
+
const bindChartEvents = useCallback(
|
|
174
|
+
(chartInstance: ChartInstance, chartId: string) => {
|
|
175
|
+
if (!chartInstance) return;
|
|
176
|
+
|
|
177
|
+
const handlers = new Map<string, any>();
|
|
178
|
+
|
|
179
|
+
// 为每个事件类型创建并绑定处理器
|
|
180
|
+
events.forEach((eventType) => {
|
|
181
|
+
const handler = createConnectHandler(chartId, eventType);
|
|
182
|
+
handlers.set(eventType, handler);
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
chartInstance.on(eventType, handler);
|
|
186
|
+
} catch (e) {
|
|
187
|
+
console.warn(`[useChartConnect] Failed to bind ${eventType} event:`, e);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
eventHandlersRef.current.set(chartId, handlers);
|
|
192
|
+
},
|
|
193
|
+
[events, createConnectHandler]
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* 解绑单个图表的联动事件
|
|
198
|
+
*/
|
|
199
|
+
const unbindChartEvents = useCallback((chartId: string) => {
|
|
200
|
+
const handlers = eventHandlersRef.current.get(chartId);
|
|
201
|
+
if (!handlers) return;
|
|
202
|
+
|
|
203
|
+
const chartItem = chartsRef.current.get(chartId);
|
|
204
|
+
if (!chartItem) return;
|
|
205
|
+
|
|
206
|
+
handlers.forEach((handler, eventType) => {
|
|
207
|
+
try {
|
|
208
|
+
chartItem.instance.off(eventType, handler);
|
|
209
|
+
} catch (e) {
|
|
210
|
+
console.warn(`[useChartConnect] Failed to unbind ${eventType} event:`, e);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
eventHandlersRef.current.delete(chartId);
|
|
215
|
+
}, []);
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 绑定图表到联动组
|
|
219
|
+
*/
|
|
220
|
+
const connect = useCallback(
|
|
221
|
+
(chartInstance: ChartInstance, chartId?: string) => {
|
|
222
|
+
const id = chartId || `chart_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
223
|
+
|
|
224
|
+
// 检查是否已连接
|
|
225
|
+
if (chartsRef.current.has(id)) {
|
|
226
|
+
console.warn(`[useChartConnect] Chart ${id} is already connected`);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// 存储图表实例
|
|
231
|
+
chartsRef.current.set(id, {
|
|
232
|
+
instance: chartInstance,
|
|
233
|
+
id,
|
|
234
|
+
handlers: new Map(),
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// 绑定事件
|
|
238
|
+
bindChartEvents(chartInstance, id);
|
|
239
|
+
connectedRef.current = true;
|
|
240
|
+
|
|
241
|
+
// 如果有 groupName,设置图表组
|
|
242
|
+
if (groupName && chartInstance.group !== undefined) {
|
|
243
|
+
try {
|
|
244
|
+
(chartInstance as any).group = groupName;
|
|
245
|
+
} catch (e) {
|
|
246
|
+
console.warn('[useChartConnect] Failed to set chart group:', e);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
[bindChartEvents, groupName]
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* 解除图表联动
|
|
255
|
+
*/
|
|
256
|
+
const disconnect = useCallback(
|
|
257
|
+
(chartInstance: ChartInstance, chartId?: string) => {
|
|
258
|
+
// 查找图表 ID
|
|
259
|
+
let targetId = chartId;
|
|
260
|
+
if (!targetId) {
|
|
261
|
+
chartsRef.current.forEach((item, id) => {
|
|
262
|
+
if (item.instance === chartInstance) {
|
|
263
|
+
targetId = id;
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (!targetId || !chartsRef.current.has(targetId)) {
|
|
269
|
+
console.warn(`[useChartConnect] Chart ${targetId} not found in connection group`);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 解绑事件
|
|
274
|
+
unbindChartEvents(targetId);
|
|
275
|
+
|
|
276
|
+
// 移除图表
|
|
277
|
+
chartsRef.current.delete(targetId);
|
|
278
|
+
|
|
279
|
+
// 更新连接状态
|
|
280
|
+
connectedRef.current = chartsRef.current.size > 0;
|
|
281
|
+
},
|
|
282
|
+
[unbindChartEvents]
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* 触发联动事件
|
|
287
|
+
*/
|
|
288
|
+
const dispatchConnect = useCallback(
|
|
289
|
+
(sourceId: string, payload: any) => {
|
|
290
|
+
if (disabled) return;
|
|
291
|
+
|
|
292
|
+
const chartItem = chartsRef.current.get(sourceId);
|
|
293
|
+
if (!chartItem) {
|
|
294
|
+
console.warn(`[useChartConnect] Source chart ${sourceId} not found`);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const { eventType, params } = payload || {};
|
|
299
|
+
if (!eventType) {
|
|
300
|
+
console.warn('[useChartConnect] Payload must include eventType');
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// 分发事件到其他图表
|
|
305
|
+
dispatchToOthers(sourceId, eventType, params);
|
|
306
|
+
},
|
|
307
|
+
[disabled, dispatchToOthers]
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* 批量连接图表
|
|
312
|
+
*/
|
|
313
|
+
const connectAll = useCallback(
|
|
314
|
+
(charts: Array<{ instance: ChartInstance; id: string }>) => {
|
|
315
|
+
charts.forEach(({ instance, id }) => {
|
|
316
|
+
connect(instance, id);
|
|
317
|
+
});
|
|
318
|
+
},
|
|
319
|
+
[connect]
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* 批量断开所有图表
|
|
324
|
+
*/
|
|
325
|
+
const disconnectAll = useCallback(() => {
|
|
326
|
+
chartsRef.current.forEach((item, id) => {
|
|
327
|
+
unbindChartEvents(id);
|
|
328
|
+
});
|
|
329
|
+
chartsRef.current.clear();
|
|
330
|
+
connectedRef.current = false;
|
|
331
|
+
}, [unbindChartEvents]);
|
|
332
|
+
|
|
333
|
+
// 自动绑定
|
|
334
|
+
useEffect(() => {
|
|
335
|
+
if (autoBind && chartIds.length > 0) {
|
|
336
|
+
// 自动绑定需要在外部调用 connect 方法
|
|
337
|
+
// 这里只是记录需要绑定的 ID 列表
|
|
338
|
+
}
|
|
339
|
+
}, [autoBind, chartIds]);
|
|
340
|
+
|
|
341
|
+
// 清理:组件卸载时断开所有图表
|
|
342
|
+
useEffect(() => {
|
|
343
|
+
return () => {
|
|
344
|
+
disconnectAll();
|
|
345
|
+
};
|
|
346
|
+
}, [disconnectAll]);
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
connect,
|
|
350
|
+
disconnect,
|
|
351
|
+
dispatchConnect,
|
|
352
|
+
connectAll,
|
|
353
|
+
disconnectAll,
|
|
354
|
+
isConnected: connectedRef.current,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// ============================================================================
|
|
359
|
+
// 导出
|
|
360
|
+
// ============================================================================
|
|
361
|
+
|
|
362
|
+
export default useChartConnect;
|