@agions/taroviz 1.6.0 → 1.9.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.
Files changed (35) hide show
  1. package/README.md +30 -21
  2. package/dist/cjs/index.js +1 -1
  3. package/dist/esm/index.js +49471 -2199
  4. package/package.json +2 -1
  5. package/src/adapters/__tests__/index.test.ts +4 -2
  6. package/src/adapters/h5/index.ts +16 -0
  7. package/src/adapters/types.ts +28 -120
  8. package/src/charts/common/BaseChartWrapper.tsx +193 -32
  9. package/src/charts/index.ts +5 -1
  10. package/src/charts/liquid/index.tsx +227 -0
  11. package/src/charts/liquid/types.ts +130 -0
  12. package/src/charts/tree/index.tsx +117 -0
  13. package/src/charts/tree/types.ts +174 -0
  14. package/src/charts/types.ts +1 -1
  15. package/src/components/DataFilter/index.tsx +587 -0
  16. package/src/core/animation/AnimationManager.ts +69 -42
  17. package/src/core/components/BaseChart.tsx +72 -9
  18. package/src/core/types/common.ts +21 -110
  19. package/src/core/types/index.ts +4 -135
  20. package/src/core/types/platform.ts +38 -230
  21. package/src/core/utils/drillDown.ts +643 -0
  22. package/src/core/utils/export/ExportUtils.ts +10 -1
  23. package/src/core/utils/performance/PerformanceAnalyzer.ts +21 -1
  24. package/src/core/utils/performance/types.ts +5 -0
  25. package/src/hooks/__tests__/index.test.tsx +7 -5
  26. package/src/hooks/index.ts +41 -2
  27. package/src/hooks/useAnimation.ts +427 -0
  28. package/src/hooks/useChartConnect.ts +362 -0
  29. package/src/hooks/useChartDownload.ts +692 -0
  30. package/src/hooks/useDataZoom.ts +323 -0
  31. package/src/hooks/usePerformance.ts +291 -0
  32. package/src/index.ts +25 -2
  33. package/src/themes/__tests__/index.test.ts +7 -13
  34. package/src/themes/index.ts +3 -0
  35. package/src/themes/useAutoTheme.ts +66 -0
@@ -0,0 +1,323 @@
1
+ /**
2
+ * useDataZoom - 数据缩放 Hook
3
+ * 控制图表的数据缩放区域(dataZoom),支持拖拽和滚轮缩放
4
+ */
5
+ import { useRef, useCallback, useEffect } from 'react';
6
+ import type { RefObject } from 'react';
7
+ import type { ChartInstance } from './index';
8
+
9
+ // ============================================================================
10
+ // 类型定义
11
+ // ============================================================================
12
+
13
+ /** dataZoom 类型 */
14
+ export type DataZoomType = 'inside' | 'slider';
15
+
16
+ /** dataZoom 配置选项 */
17
+ export interface UseDataZoomOptions {
18
+ /** dataZoom 类型: inside=内置型(滚轮/双指), slider=滑块型 */
19
+ type?: DataZoomType;
20
+ /** 起始位置 (0-100) */
21
+ start?: number;
22
+ /** 结束位置 (0-100) */
23
+ end?: number;
24
+ /** 最小跨度 (0-100) */
25
+ minSpan?: number;
26
+ /** 最大跨度 (0-100) */
27
+ maxSpan?: number;
28
+ /** 是否锁定缩放 */
29
+ zoomLock?: boolean;
30
+ /** 节流时间 (ms) */
31
+ throttle?: number;
32
+ /** 是否禁用 */
33
+ disabled?: boolean;
34
+ /** 是否显示缩放痕迹 */
35
+ brushSelect?: boolean;
36
+ /** 缩放模式: 'scale' | 'mix' */
37
+ zoomMode?: 'scale' | 'mix';
38
+ /** 是否在组件卸载时重置缩放 */
39
+ resetOnUnmount?: boolean;
40
+ /** 缩放变化回调 */
41
+ onZoomChange?: (range: { start: number; end: number }) => void;
42
+ }
43
+
44
+ /** 缩放范围 */
45
+ export interface ZoomRange {
46
+ start: number;
47
+ end: number;
48
+ }
49
+
50
+ /** dataZoom 返回值 */
51
+ export interface UseDataZoomReturn {
52
+ /** 绑定 dataZoom 到图表实例 */
53
+ bindDataZoom: (chartInstance: ChartInstance) => void;
54
+ /** 设置缩放范围 */
55
+ setZoomRange: (start: number, end: number) => void;
56
+ /** 重置缩放到初始状态 */
57
+ resetZoom: () => void;
58
+ /** 获取当前缩放范围 */
59
+ getZoomRange: () => ZoomRange;
60
+ /** 起始位置的原始值 (Ref) */
61
+ startValue: RefObject<number | string | Date | undefined>;
62
+ /** 结束位置的原始值 (Ref) */
63
+ endValue: RefObject<number | string | Date | undefined>;
64
+ /** 绑定事件处理 */
65
+ bindEvents: (chartInstance: ChartInstance) => void;
66
+ /** 缩放变化回调 */
67
+ onZoomChange?: (range: ZoomRange) => void;
68
+ }
69
+
70
+ // ============================================================================
71
+ // Hook 实现
72
+ // ============================================================================
73
+
74
+ /**
75
+ * 使用图表数据缩放
76
+ * @param options 配置选项
77
+ * @returns dataZoom 操作接口
78
+ */
79
+ export function useDataZoom(options: UseDataZoomOptions = {}): UseDataZoomReturn {
80
+ const {
81
+ type = 'inside',
82
+ start = 0,
83
+ end = 100,
84
+ minSpan,
85
+ maxSpan,
86
+ zoomLock = false,
87
+ throttle = 16,
88
+ disabled = false,
89
+ brushSelect = false,
90
+ zoomMode = 'scale',
91
+ resetOnUnmount = false,
92
+ onZoomChange,
93
+ } = options;
94
+
95
+ // Refs
96
+ const chartRef = useRef<ChartInstance | null>(null);
97
+ const startValueRef = useRef<number | string | Date | undefined>(undefined);
98
+ const endValueRef = useRef<number | string | Date | undefined>(undefined);
99
+ const isBindingRef = useRef(false);
100
+ const throttledHandlerRef = useRef<((...args: any[]) => void) | null>(null);
101
+
102
+ // 节流处理
103
+ const throttledCallback = useCallback(
104
+ <T extends (...args: any[]) => void>(fn: T): T => {
105
+ let lastCall = 0;
106
+ return ((...args: any[]) => {
107
+ const now = Date.now();
108
+ if (now - lastCall >= throttle) {
109
+ lastCall = now;
110
+ fn(...args);
111
+ }
112
+ }) as T;
113
+ },
114
+ [throttle]
115
+ );
116
+
117
+ // 构建 dataZoom 配置
118
+ const buildDataZoomConfig = useCallback(() => {
119
+ const config: any[] = [];
120
+
121
+ // 内置型 dataZoom
122
+ if (type === 'inside' || zoomMode === 'mix') {
123
+ config.push({
124
+ type: 'inside',
125
+ start,
126
+ end,
127
+ zoomLock,
128
+ disabled,
129
+ zoomOnMouseWheel: true,
130
+ moveOnMouseMove: false,
131
+ moveOnMouseWheel: false,
132
+ preventDefaultMouseMove: true,
133
+ });
134
+ }
135
+
136
+ // 滑块型 dataZoom
137
+ if (type === 'slider' || zoomMode === 'mix') {
138
+ config.push({
139
+ type: 'slider',
140
+ start,
141
+ end,
142
+ minSpan,
143
+ maxSpan,
144
+ zoomLock,
145
+ disabled,
146
+ show: true,
147
+ brushSelect,
148
+ throttle,
149
+ });
150
+ }
151
+
152
+ return config;
153
+ }, [type, start, end, minSpan, maxSpan, zoomLock, disabled, brushSelect, throttle]);
154
+
155
+ // 绑定 dataZoom 到图表
156
+ const bindDataZoom = useCallback(
157
+ (chartInstance: ChartInstance) => {
158
+ if (!chartInstance || isBindingRef.current) return;
159
+
160
+ isBindingRef.current = true;
161
+ chartRef.current = chartInstance;
162
+
163
+ try {
164
+ // 设置 dataZoom 配置
165
+ const dataZoomConfig = buildDataZoomConfig();
166
+ chartInstance.setOption(
167
+ {
168
+ dataZoom: dataZoomConfig,
169
+ },
170
+ false,
171
+ true
172
+ );
173
+ } catch (e) {
174
+ console.warn('[useDataZoom] Failed to bind dataZoom:', e);
175
+ }
176
+
177
+ isBindingRef.current = false;
178
+ },
179
+ [buildDataZoomConfig]
180
+ );
181
+
182
+ // 解绑 dataZoom
183
+ const unbindDataZoom = useCallback(
184
+ (chartInstance: ChartInstance) => {
185
+ if (!chartInstance) return;
186
+
187
+ try {
188
+ // 通过 dispatchAction 关闭 dataZoom
189
+ chartInstance.dispatchAction?.({
190
+ type: 'dataZoom',
191
+ start: 0,
192
+ end: 100,
193
+ });
194
+ } catch (e) {
195
+ console.warn('[useDataZoom] Failed to unbind dataZoom:', e);
196
+ }
197
+ },
198
+ []
199
+ );
200
+
201
+ // 设置缩放范围
202
+ const setZoomRange = useCallback((newStart: number, newEnd: number) => {
203
+ const chart = chartRef.current;
204
+ if (!chart) return;
205
+
206
+ const clampedStart = Math.max(0, Math.min(100, newStart));
207
+ const clampedEnd = Math.max(0, Math.min(100, newEnd));
208
+
209
+ try {
210
+ chart.dispatchAction?.({
211
+ type: 'dataZoom',
212
+ start: clampedStart,
213
+ end: clampedEnd,
214
+ });
215
+ } catch (e) {
216
+ console.warn('[useDataZoom] Failed to set zoom range:', e);
217
+ }
218
+ }, []);
219
+
220
+ // 重置缩放
221
+ const resetZoom = useCallback(() => {
222
+ setZoomRange(start, end);
223
+ }, [setZoomRange, start, end]);
224
+
225
+ // 获取当前缩放范围
226
+ const getZoomRange = useCallback((): ZoomRange => {
227
+ const chart = chartRef.current;
228
+ if (!chart) {
229
+ return { start, end };
230
+ }
231
+
232
+ try {
233
+ const option = chart.getOption?.();
234
+ const dataZoom = option?.dataZoom as any[];
235
+ if (Array.isArray(dataZoom) && dataZoom.length > 0) {
236
+ // 优先获取 inside 类型的范围
237
+ const insideZoom = dataZoom.find((dz: any) => dz.type === 'inside');
238
+ const sliderZoom = dataZoom.find((dz: any) => dz.type === 'slider');
239
+ const zoom = insideZoom || sliderZoom || dataZoom[0];
240
+ return {
241
+ start: zoom.start ?? start,
242
+ end: zoom.end ?? end,
243
+ };
244
+ }
245
+ } catch (e) {
246
+ console.warn('[useDataZoom] Failed to get zoom range:', e);
247
+ }
248
+
249
+ return { start, end };
250
+ }, [start, end]);
251
+
252
+ // 绑定事件
253
+ const bindEvents = useCallback(
254
+ (chartInstance: ChartInstance) => {
255
+ if (!chartInstance) return;
256
+
257
+ // 创建缩放变化处理器
258
+ const handleZoomChange = (params: any) => {
259
+ if (disabled) return;
260
+
261
+ const { start: newStart, end: newEnd } = params || {};
262
+ if (newStart !== undefined) startValueRef.current = newStart;
263
+ if (newEnd !== undefined) endValueRef.current = newEnd;
264
+
265
+ onZoomChange?.({ start: newStart ?? start, end: newEnd ?? end });
266
+ };
267
+
268
+ // 使用节流包装
269
+ throttledHandlerRef.current = throttledCallback(handleZoomChange);
270
+
271
+ try {
272
+ // 监听 dataZoom 事件
273
+ chartInstance.on('datazoom', throttledHandlerRef.current);
274
+ } catch (e) {
275
+ console.warn('[useDataZoom] Failed to bind events:', e);
276
+ }
277
+ },
278
+ [disabled, onZoomChange, throttledCallback]
279
+ );
280
+
281
+ // 解绑事件
282
+ const unbindEvents = useCallback(
283
+ (chartInstance: ChartInstance) => {
284
+ if (!chartInstance || !throttledHandlerRef.current) return;
285
+
286
+ try {
287
+ chartInstance.off('datazoom', throttledHandlerRef.current);
288
+ } catch (e) {
289
+ console.warn('[useDataZoom] Failed to unbind events:', e);
290
+ }
291
+ },
292
+ []
293
+ );
294
+
295
+ // 清理:组件卸载时
296
+ useEffect(() => {
297
+ return () => {
298
+ const chart = chartRef.current;
299
+ if (chart) {
300
+ if (resetOnUnmount) {
301
+ unbindDataZoom(chart);
302
+ }
303
+ unbindEvents(chart);
304
+ }
305
+ };
306
+ }, [resetOnUnmount, unbindDataZoom, unbindEvents]);
307
+
308
+ return {
309
+ bindDataZoom,
310
+ setZoomRange,
311
+ resetZoom,
312
+ getZoomRange,
313
+ startValue: startValueRef,
314
+ endValue: endValueRef,
315
+ bindEvents,
316
+ };
317
+ }
318
+
319
+ // ============================================================================
320
+ // 导出
321
+ // ============================================================================
322
+
323
+ export default useDataZoom;
@@ -0,0 +1,291 @@
1
+ /**
2
+ * usePerformance - 图表性能监控 Hook
3
+ * 提供实时性能指标监控和报告功能
4
+ */
5
+ import { useState, useEffect, useCallback, useRef } from 'react';
6
+ import { PerformanceAnalyzer, PerformanceMetricType, PerformanceMetric } from '../core/utils/performance';
7
+
8
+ /**
9
+ * 性能监控配置
10
+ */
11
+ export interface UsePerformanceOptions {
12
+ /** 是否启用监控 */
13
+ enabled?: boolean;
14
+ /** 采样间隔 (ms) */
15
+ sampleInterval?: number;
16
+ /** 是否显示实时 FPS */
17
+ showFPS?: boolean;
18
+ /** FPS 告警阈值 */
19
+ fpsWarningThreshold?: number;
20
+ /** FPS 严重告警阈值 */
21
+ fpsCriticalThreshold?: number;
22
+ /** 自动启动 */
23
+ autoStart?: boolean;
24
+ }
25
+
26
+ /**
27
+ * 性能指标状态
28
+ */
29
+ export interface PerformanceState {
30
+ /** 当前 FPS */
31
+ fps: number;
32
+ /** 平均 FPS */
33
+ avgFps: number;
34
+ /** 最低 FPS */
35
+ minFps: number;
36
+ /** 最高 FPS */
37
+ maxFps: number;
38
+ /** FPS 状态: normal | warning | critical */
39
+ fpsStatus: 'normal' | 'warning' | 'critical';
40
+ /** 帧率历史 */
41
+ fpsHistory: number[];
42
+ /** 是否正在监控 */
43
+ isMonitoring: boolean;
44
+ }
45
+
46
+ /**
47
+ * 性能监控返回值
48
+ */
49
+ export interface UsePerformanceReturn {
50
+ /** 性能状态 */
51
+ state: PerformanceState;
52
+ /** 开始监控 */
53
+ start: () => void;
54
+ /** 停止监控 */
55
+ stop: () => void;
56
+ /** 重置统计数据 */
57
+ reset: () => void;
58
+ /** 获取性能报告 */
59
+ getReport: () => PerformanceMetric[];
60
+ /** FPS 告警回调 */
61
+ onFpsWarning?: (fps: number) => void;
62
+ }
63
+
64
+ /**
65
+ * 使用图表性能监控
66
+ * @param options 配置选项
67
+ * @returns 性能监控接口
68
+ */
69
+ export function usePerformance(options: UsePerformanceOptions = {}): UsePerformanceReturn {
70
+ const {
71
+ enabled = true,
72
+ sampleInterval = 1000,
73
+ showFPS = true,
74
+ fpsWarningThreshold = 30,
75
+ fpsCriticalThreshold = 15,
76
+ autoStart = true,
77
+ } = options;
78
+
79
+ // Refs
80
+ const analyzerRef = useRef<PerformanceAnalyzer | null>(null);
81
+ const fpsHistoryRef = useRef<number[]>([]);
82
+ const animationFrameRef = useRef<number | null>(null);
83
+ const lastFrameTimeRef = useRef<number>(0);
84
+ const fpsAccumulatorRef = useRef<{ frames: number; lastTime: number }>({ frames: 0, lastTime: 0 });
85
+
86
+ // State
87
+ const [state, setState] = useState<PerformanceState>({
88
+ fps: 60,
89
+ avgFps: 60,
90
+ minFps: 60,
91
+ maxFps: 60,
92
+ fpsStatus: 'normal',
93
+ fpsHistory: [],
94
+ isMonitoring: false,
95
+ });
96
+
97
+ /**
98
+ * 计算 FPS 状态
99
+ */
100
+ const calculateFpsStatus = useCallback(
101
+ (fps: number): 'normal' | 'warning' | 'critical' => {
102
+ if (fps <= fpsCriticalThreshold) return 'critical';
103
+ if (fps <= fpsWarningThreshold) return 'warning';
104
+ return 'normal';
105
+ },
106
+ [fpsWarningThreshold, fpsCriticalThreshold]
107
+ );
108
+
109
+ /**
110
+ * 更新 FPS 计算
111
+ */
112
+ const updateFps = useCallback(() => {
113
+ if (!enabled) return;
114
+
115
+ const now = performance.now();
116
+ const delta = now - lastFrameTimeRef.current;
117
+ lastFrameTimeRef.current = now;
118
+
119
+ // 累积帧数
120
+ fpsAccumulatorRef.current.frames++;
121
+
122
+ // 每秒计算一次 FPS
123
+ if (now - fpsAccumulatorRef.current.lastTime >= 1000) {
124
+ const fps = Math.round((fpsAccumulatorRef.current.frames * 1000) / (now - fpsAccumulatorRef.current.lastTime));
125
+ const history = fpsHistoryRef.current;
126
+
127
+ // 更新历史
128
+ history.push(fps);
129
+ if (history.length > 60) {
130
+ history.shift();
131
+ }
132
+
133
+ // 计算统计数据
134
+ const avgFps = Math.round(history.reduce((a, b) => a + b, 0) / history.length);
135
+ const minFps = Math.min(...history);
136
+ const maxFps = Math.max(...history);
137
+
138
+ setState(prev => ({
139
+ ...prev,
140
+ fps,
141
+ avgFps,
142
+ minFps,
143
+ maxFps,
144
+ fpsStatus: calculateFpsStatus(fps),
145
+ fpsHistory: [...history],
146
+ }));
147
+
148
+ // 重置计数器
149
+ fpsAccumulatorRef.current.frames = 0;
150
+ fpsAccumulatorRef.current.lastTime = now;
151
+ }
152
+
153
+ // 继续下一帧
154
+ animationFrameRef.current = requestAnimationFrame(updateFps);
155
+ }, [enabled, calculateFpsStatus]);
156
+
157
+ /**
158
+ * 开始监控
159
+ */
160
+ const start = useCallback(() => {
161
+ if (!enabled) return;
162
+
163
+ // Prevent starting multiple RAF loops
164
+ if (animationFrameRef.current !== null) return;
165
+
166
+ // 初始化分析器
167
+ if (!analyzerRef.current) {
168
+ analyzerRef.current = PerformanceAnalyzer.getInstance({
169
+ enabled: true,
170
+ sampleInterval,
171
+ autoStart: false,
172
+ });
173
+ }
174
+
175
+ analyzerRef.current.start();
176
+
177
+ // 重置 FPS 计算
178
+ fpsAccumulatorRef.current = { frames: 0, lastTime: performance.now() };
179
+ lastFrameTimeRef.current = performance.now();
180
+ fpsHistoryRef.current = [];
181
+
182
+ // 开始 FPS 监控循环
183
+ animationFrameRef.current = requestAnimationFrame(updateFps);
184
+
185
+ setState(prev => ({ ...prev, isMonitoring: true }));
186
+ }, [enabled, sampleInterval, updateFps]);
187
+
188
+ /**
189
+ * 停止监控
190
+ */
191
+ const stop = useCallback(() => {
192
+ // 停止 FPS 监控
193
+ if (animationFrameRef.current !== null) {
194
+ cancelAnimationFrame(animationFrameRef.current);
195
+ animationFrameRef.current = null;
196
+ }
197
+
198
+ // 停止分析器
199
+ analyzerRef.current?.stop();
200
+
201
+ setState(prev => ({ ...prev, isMonitoring: false }));
202
+ }, []);
203
+
204
+ /**
205
+ * 重置统计数据
206
+ */
207
+ const reset = useCallback(() => {
208
+ PerformanceAnalyzer.resetInstance();
209
+ fpsHistoryRef.current = [];
210
+ setState(prev => ({
211
+ ...prev,
212
+ fps: 60,
213
+ avgFps: 60,
214
+ minFps: 60,
215
+ maxFps: 60,
216
+ fpsStatus: 'normal',
217
+ fpsHistory: [],
218
+ }));
219
+ }, []);
220
+
221
+ /**
222
+ * 获取性能报告
223
+ */
224
+ const getReport = useCallback((): PerformanceMetric[] => {
225
+ if (!analyzerRef.current) return [];
226
+
227
+ try {
228
+ const report = analyzerRef.current.getAllMetrics?.();
229
+ if (!report) return [];
230
+ return Array.from(report.values()).flat();
231
+ } catch {
232
+ return [];
233
+ }
234
+ }, []);
235
+
236
+ // 自动启动
237
+ useEffect(() => {
238
+ if (autoStart && enabled) {
239
+ start();
240
+ }
241
+
242
+ return () => {
243
+ stop();
244
+ };
245
+ }, [autoStart, enabled, start, stop]);
246
+
247
+ return {
248
+ state,
249
+ start,
250
+ stop,
251
+ reset,
252
+ getReport,
253
+ };
254
+ }
255
+
256
+ /**
257
+ * 轻量级 FPS 监控 Hook
258
+ * 用于实时显示图表 FPS
259
+ */
260
+ export function useFpsMonitor(): number {
261
+ const [fps, setFps] = useState(60);
262
+ const frameCountRef = useRef(0);
263
+ const lastTimeRef = useRef(performance.now());
264
+
265
+ useEffect(() => {
266
+ let animationId: number;
267
+
268
+ const tick = () => {
269
+ frameCountRef.current++;
270
+ const now = performance.now();
271
+ const elapsed = now - lastTimeRef.current;
272
+
273
+ if (elapsed >= 1000) {
274
+ const currentFps = Math.round((frameCountRef.current * 1000) / elapsed);
275
+ setFps(currentFps);
276
+ frameCountRef.current = 0;
277
+ lastTimeRef.current = now;
278
+ }
279
+
280
+ animationId = requestAnimationFrame(tick);
281
+ };
282
+
283
+ animationId = requestAnimationFrame(tick);
284
+
285
+ return () => cancelAnimationFrame(animationId);
286
+ }, []);
287
+
288
+ return fps;
289
+ }
290
+
291
+ export default usePerformance;
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * TaroViz - 基于 Taro 和 ECharts 的多端图表组件库
3
- * @version 1.6.0
3
+ * @version 1.7.0
4
4
  */
5
5
 
6
6
  // 核心组件
@@ -59,6 +59,25 @@ export { default as WordCloudChart } from './charts/wordcloud';
59
59
  export { default as BoxplotChart } from './charts/boxplot';
60
60
  export { default as ParallelChart } from './charts/parallel';
61
61
 
62
+ // v1.7.0 新增组件
63
+ export { DataFilter, type DataFilterProps, type FilterField, type FilterValues } from './components/DataFilter';
64
+ export {
65
+ createDrillDown,
66
+ canDrillDown,
67
+ buildHierarchy,
68
+ createRegionDrillDown,
69
+ createCategoryDrillDown,
70
+ type DrillDownConfig,
71
+ type DrillDownSource,
72
+ type DrillDownReturn,
73
+ type DrillDownEventParams,
74
+ type DrillUpEventParams,
75
+ } from './core/utils/drillDown';
76
+
77
+ // v1.7.0 新增图表组件
78
+ export { default as LiquidChart } from './charts/liquid';
79
+ export { default as TreeChart } from './charts/tree';
80
+
62
81
  // 适配器
63
82
  export { getAdapter, detectPlatform, getEnv } from './adapters';
64
83
  export { default as H5Adapter } from './adapters/h5';
@@ -152,10 +171,14 @@ export {
152
171
  useDataTransform,
153
172
  useTableTransform,
154
173
  useTimeSeriesTransform,
174
+ // v1.7.0 新增 Hooks
175
+ useDataZoom,
176
+ useChartConnect,
177
+ useChartDownload,
155
178
  } from './hooks';
156
179
 
157
180
  /**
158
181
  * 库信息
159
182
  */
160
183
  export const name = 'taroviz';
161
- export const version = '1.6.0';
184
+ export const version = '1.7.0';
@@ -1,4 +1,4 @@
1
- import { getTheme, registerTheme, defaultTheme, darkTheme } from '../index';
1
+ import { getTheme, registerTheme, defaultTheme, darkTheme, getThemeByName } from '../index';
2
2
 
3
3
  describe('Theme System', () => {
4
4
  describe('getTheme', () => {
@@ -11,7 +11,7 @@ describe('Theme System', () => {
11
11
  const result = getTheme({ darkMode: true });
12
12
  expect(result.darkMode).toBe(true);
13
13
  expect(result.theme).toBe('dark');
14
- expect(result.backgroundColor).toBe('#0f1117');
14
+ expect(result.backgroundColor).toBe('#1a1a2e');
15
15
  });
16
16
 
17
17
  it('should merge custom options with defaultTheme', () => {
@@ -50,16 +50,10 @@ describe('Theme System', () => {
50
50
  });
51
51
 
52
52
  describe('registerTheme', () => {
53
- it('should log when registering a theme', () => {
54
- // Mock console.log
55
- const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
56
-
53
+ it('should register a theme', () => {
57
54
  registerTheme('custom-theme', { backgroundColor: '#123' });
58
-
59
- expect(consoleSpy).toHaveBeenCalledWith('Registering theme: custom-theme');
60
-
61
- // Restore console.log
62
- consoleSpy.mockRestore();
55
+ const result = getThemeByName('custom-theme');
56
+ expect(result?.backgroundColor).toBe('#123');
63
57
  });
64
58
  });
65
59
 
@@ -70,8 +64,8 @@ describe('Theme System', () => {
70
64
  expect(defaultTheme).toHaveProperty('colors');
71
65
  expect(Array.isArray(defaultTheme.colors)).toBe(true);
72
66
  expect(defaultTheme.colors).toHaveLength(9);
73
- expect(defaultTheme).toHaveProperty('backgroundColor', 'transparent');
74
- expect(defaultTheme).toHaveProperty('textColor', '#333');
67
+ expect(defaultTheme).toHaveProperty('backgroundColor', '#ffffff');
68
+ expect(defaultTheme).toHaveProperty('textColor', '#333333');
75
69
  expect(defaultTheme).toHaveProperty('fontFamily');
76
70
  });
77
71
  });
@@ -871,3 +871,6 @@ export default {
871
871
  getLightThemes,
872
872
  getDarkThemes,
873
873
  };
874
+
875
+ export { useAutoTheme } from './useAutoTheme';
876
+ export type { UseAutoThemeOptions } from './useAutoTheme';