@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.
Files changed (64) hide show
  1. package/README.md +4 -4
  2. package/dist/cjs/index.js +1 -1
  3. package/dist/esm/index.js +3814 -2724
  4. package/package.json +1 -1
  5. package/src/__tests__/integration.test.tsx +12 -10
  6. package/src/adapters/BaseAdapter.ts +116 -0
  7. package/src/adapters/__tests__/index.test.ts +10 -10
  8. package/src/adapters/index.ts +63 -74
  9. package/src/adapters/swan/index.ts +26 -223
  10. package/src/adapters/tt/index.ts +28 -225
  11. package/src/adapters/types.ts +36 -0
  12. package/src/adapters/weapp/index.ts +29 -189
  13. package/src/charts/bar/index.tsx +5 -9
  14. package/src/charts/boxplot/__tests__/index.test.tsx +130 -0
  15. package/src/charts/boxplot/index.tsx +18 -0
  16. package/src/charts/boxplot/types.ts +46 -0
  17. package/src/charts/candlestick/__tests__/index.test.tsx +37 -0
  18. package/src/charts/candlestick/index.tsx +13 -0
  19. package/src/charts/common/BaseChartWrapper.tsx +47 -38
  20. package/src/charts/funnel/index.tsx +5 -9
  21. package/src/charts/gauge/index.tsx +5 -9
  22. package/src/charts/graph/__tests__/index.test.tsx +41 -0
  23. package/src/charts/graph/index.tsx +13 -0
  24. package/src/charts/heatmap/index.tsx +5 -9
  25. package/src/charts/index.ts +10 -1
  26. package/src/charts/line/index.tsx +4 -7
  27. package/src/charts/parallel/__tests__/index.test.tsx +164 -0
  28. package/src/charts/parallel/index.tsx +18 -0
  29. package/src/charts/parallel/types.ts +73 -0
  30. package/src/charts/pie/index.tsx +5 -10
  31. package/src/charts/radar/index.tsx +5 -9
  32. package/src/charts/scatter/index.tsx +5 -9
  33. package/src/charts/types.ts +48 -4
  34. package/src/charts/wordcloud/__tests__/index.test.tsx +36 -0
  35. package/src/charts/wordcloud/index.tsx +13 -0
  36. package/src/core/animation/AnimationManager.ts +15 -0
  37. package/src/core/components/Annotation.tsx +26 -21
  38. package/src/core/components/BaseChart.tsx +280 -1105
  39. package/src/core/components/ErrorBoundary.tsx +4 -1
  40. package/src/core/components/LazyChart.tsx +42 -55
  41. package/src/core/components/hooks/index.ts +20 -0
  42. package/src/core/components/hooks/useChartEvents.ts +143 -0
  43. package/src/core/components/hooks/useChartInit.ts +80 -0
  44. package/src/core/components/hooks/usePerformance.ts +186 -0
  45. package/src/core/components/hooks/useVirtualScroll.ts +156 -0
  46. package/src/core/echarts.ts +1 -1
  47. package/src/core/themes/ThemeManager.ts +31 -15
  48. package/src/core/types/index.ts +2 -2
  49. package/src/core/utils/chartInstances.ts +10 -3
  50. package/src/core/utils/chartUtils.ts +46 -0
  51. package/src/core/utils/common.ts +14 -1
  52. package/src/core/utils/export/ExportUtils.ts +13 -22
  53. package/src/core/utils/performance/PerformanceAnalyzer.ts +32 -5
  54. package/src/core/utils/uuid.ts +1 -1
  55. package/src/editor/EnhancedThemeEditor.tsx +624 -0
  56. package/src/editor/ThemeEditor.tsx +1 -6
  57. package/src/hooks/__tests__/index.test.tsx +14 -11
  58. package/src/hooks/__tests__/useDataTransform.test.ts +159 -0
  59. package/src/hooks/index.ts +54 -19
  60. package/src/hooks/useDataTransform.ts +503 -0
  61. package/src/index.ts +27 -9
  62. package/src/main.tsx +4 -4
  63. package/src/themes/__tests__/index.test.ts +2 -2
  64. package/src/themes/index.ts +13 -0
@@ -0,0 +1,503 @@
1
+ /**
2
+ * useDataTransform - 数据转换 Hook
3
+ * 提供便捷的数据转换功能,将原始数据转换为 ECharts 配置
4
+ */
5
+ import { useMemo } from 'react';
6
+ import type { EChartsOption } from 'echarts';
7
+
8
+ // ============================================================================
9
+ // 类型定义
10
+ // ============================================================================
11
+
12
+ /** 原始数据条目 */
13
+ export interface DataItem {
14
+ name?: string;
15
+ value?: number | number[];
16
+ [key: string]: unknown;
17
+ }
18
+
19
+ /** 数据源 */
20
+ export interface DataSource {
21
+ categories?: (string | number)[];
22
+ series?: DataItem[];
23
+ rows?: Record<string, unknown>[];
24
+ columns?: string[];
25
+ }
26
+
27
+ /** 聚合方式 */
28
+ export type AggregationType = 'sum' | 'average' | 'max' | 'min' | 'count' | 'first' | 'last';
29
+
30
+ /** 时间周期 */
31
+ export type TimePeriod = 'day' | 'week' | 'month' | 'quarter' | 'year';
32
+
33
+ /** 转换选项 */
34
+ export interface TransformOptions {
35
+ /** 数据源 */
36
+ data: DataSource;
37
+ /** 图表类型 */
38
+ chartType: 'line' | 'bar' | 'pie' | 'scatter' | 'radar' | 'heatmap';
39
+ /** 映射配置 */
40
+ mapping?: {
41
+ /** 类别字段 (X轴) */
42
+ xField?: string;
43
+ /** 值字段 (Y轴) */
44
+ yField?: string;
45
+ /** 系列字段 */
46
+ seriesField?: string;
47
+ /** 大小字段 (用于散点图) */
48
+ sizeField?: string;
49
+ /** 颜色字段 */
50
+ colorField?: string;
51
+ /** 名称字段 (用于饼图等) */
52
+ nameField?: string;
53
+ /** 值字段 (用于饼图等) */
54
+ valueField?: string;
55
+ };
56
+ /** 聚合配置 */
57
+ aggregation?: {
58
+ field: string;
59
+ method: AggregationType;
60
+ };
61
+ /** 排序配置 */
62
+ sort?: {
63
+ field: string;
64
+ order: 'asc' | 'desc';
65
+ };
66
+ /** 过滤配置 */
67
+ filter?: (item: Record<string, unknown>) => boolean;
68
+ /** 转换后的额外配置 */
69
+ extraConfig?: Partial<EChartsOption>;
70
+ }
71
+
72
+ /** 表格数据转换选项 */
73
+ export interface TableTransformOptions {
74
+ /** 表格数据 */
75
+ data: Record<string, unknown>[];
76
+ /** 列配置 */
77
+ columns?: Array<{
78
+ field: string;
79
+ label?: string;
80
+ aggregation?: AggregationType;
81
+ color?: string;
82
+ }>;
83
+ /** 是否转置 */
84
+ transpose?: boolean;
85
+ /** 额外配置 */
86
+ extraConfig?: Partial<EChartsOption>;
87
+ }
88
+
89
+ /** 时间序列转换选项 */
90
+ export interface TimeSeriesTransformOptions {
91
+ /** 数据 */
92
+ data: DataItem[];
93
+ /** 日期字段 */
94
+ dateField: string;
95
+ /** 值字段 */
96
+ valueField: string;
97
+ /** 分组字段 (可选) */
98
+ groupField?: string;
99
+ /** 周期 */
100
+ period?: TimePeriod;
101
+ /** 聚合方式 */
102
+ aggregation?: AggregationType;
103
+ /** 填充方式 */
104
+ fillMissing?: 'zero' | 'forward' | 'interpolate';
105
+ /** 转换后的额外配置 */
106
+ extraConfig?: Partial<EChartsOption>;
107
+ }
108
+
109
+ // ============================================================================
110
+ // 数据转换 Hook
111
+ // ============================================================================
112
+
113
+ /**
114
+ * 使用数据转换
115
+ * @param options 转换选项
116
+ * @returns 转换后的 ECharts 配置
117
+ */
118
+ export function useDataTransform(options: TransformOptions): EChartsOption {
119
+ const { data, chartType, mapping = {}, extraConfig = {} } = options;
120
+
121
+ return useMemo(() => {
122
+ switch (chartType) {
123
+ case 'line':
124
+ case 'bar':
125
+ return transformLineOrBar(data, chartType, mapping, extraConfig);
126
+
127
+ case 'pie':
128
+ return transformPie(data, mapping, extraConfig);
129
+
130
+ case 'scatter':
131
+ return transformScatter(data, mapping, extraConfig);
132
+
133
+ case 'radar':
134
+ return transformRadar(data, mapping, extraConfig);
135
+
136
+ case 'heatmap':
137
+ return transformHeatmap(data, mapping, extraConfig);
138
+
139
+ default:
140
+ return {};
141
+ }
142
+ }, [data, chartType, mapping, extraConfig]);
143
+ }
144
+
145
+ /**
146
+ * 使用表格数据转换
147
+ * 将表格数据转换为 ECharts 配置
148
+ */
149
+ export function useTableTransform(options: TableTransformOptions): EChartsOption {
150
+ const { data, columns = [], transpose = false, extraConfig = {} } = options;
151
+
152
+ return useMemo(() => {
153
+ if (!data || data.length === 0) {
154
+ return {};
155
+ }
156
+
157
+ const firstRow = data[0];
158
+ const fields = columns.length > 0 ? columns.map((c) => c.field) : Object.keys(firstRow);
159
+
160
+ // Build columns map for O(1) lookup instead of O(n) find
161
+ const columnsMap = new Map(columns.map((c) => [c.field, c]));
162
+
163
+ const categories = transpose ? fields : data.map((row) => String(row[fields[0]] || ''));
164
+
165
+ // For non-transpose, we skip the first field (it's used for categories)
166
+ const seriesFields = transpose ? fields : fields.slice(1);
167
+
168
+ const series = seriesFields.map((fieldName: string | Record<string, unknown>) => {
169
+ const key = typeof fieldName === 'string' ? fieldName : '';
170
+ const colConfig = columnsMap.get(key);
171
+ // values calculation is the same regardless of transpose
172
+ const values = data.map((row) => Number(row[key as keyof typeof row]) || 0);
173
+
174
+ return {
175
+ name: colConfig?.label || key,
176
+ type: 'bar' as const,
177
+ data: values,
178
+ itemStyle: colConfig?.color ? { color: colConfig.color } : undefined,
179
+ };
180
+ });
181
+
182
+ return {
183
+ xAxis: { type: 'category', data: categories },
184
+ yAxis: { type: 'value' },
185
+ series,
186
+ ...extraConfig,
187
+ };
188
+ }, [data, columns, transpose, extraConfig]);
189
+ }
190
+
191
+ /**
192
+ * 使用时间序列转换
193
+ * 将时间序列数据转换为 ECharts 配置
194
+ */
195
+ export function useTimeSeriesTransform(options: TimeSeriesTransformOptions): EChartsOption {
196
+ const {
197
+ data,
198
+ dateField,
199
+ valueField,
200
+ groupField,
201
+ period = 'day',
202
+ aggregation = 'sum',
203
+ fillMissing = 'forward',
204
+ extraConfig = {},
205
+ } = options;
206
+
207
+ return useMemo(() => {
208
+ if (!data || data.length === 0) {
209
+ return {};
210
+ }
211
+
212
+ // 按时间分组
213
+ const groupedData = groupByTime(data, dateField, period);
214
+ const categories = Object.keys(groupedData).sort();
215
+
216
+ if (groupField) {
217
+ // 多系列
218
+ const groups = new Set(data.map((d) => String(d[groupField])));
219
+ const series = Array.from(groups).map((group) => {
220
+ const groupValues = categories.map((date) => {
221
+ const items = groupedData[date]?.filter((d) => String(d[groupField]) === group) || [];
222
+ return aggregateValues(items, valueField, aggregation, fillMissing);
223
+ });
224
+ return { name: group, type: 'line', data: groupValues, smooth: true };
225
+ });
226
+ return {
227
+ xAxis: { type: 'category', data: categories },
228
+ yAxis: { type: 'value' },
229
+ series,
230
+ ...extraConfig,
231
+ };
232
+ } else {
233
+ // 单系列
234
+ const values = categories.map((date) =>
235
+ aggregateValues(groupedData[date] || [], valueField, aggregation, fillMissing)
236
+ );
237
+ return {
238
+ xAxis: { type: 'category', data: categories },
239
+ yAxis: { type: 'value' },
240
+ series: [{ type: 'line', data: values, smooth: true }],
241
+ ...extraConfig,
242
+ };
243
+ }
244
+ }, [data, dateField, valueField, groupField, period, aggregation, fillMissing, extraConfig]);
245
+ }
246
+
247
+ // ============================================================================
248
+ // 辅助函数
249
+ // ============================================================================
250
+
251
+ function transformLineOrBar(
252
+ data: DataSource,
253
+ chartType: 'line' | 'bar',
254
+ mapping: TransformOptions['mapping'],
255
+ extraConfig: Partial<EChartsOption>
256
+ ): EChartsOption {
257
+ const { xField = 'name', yField = 'value', seriesField } = mapping || {};
258
+
259
+ const categories = data.categories || data.rows?.map((r) => String(r[xField])) || [];
260
+ const seriesData = data.series || data.rows || [];
261
+
262
+ if (seriesField) {
263
+ const groups = new Map<string, DataItem[]>();
264
+ seriesData.forEach((item) => {
265
+ const key = String((item as Record<string, unknown>)[seriesField] || 'default');
266
+ if (!groups.has(key)) groups.set(key, []);
267
+ groups.get(key)!.push(item);
268
+ });
269
+ const series = Array.from(groups.entries()).map(([name, items]) => ({
270
+ name,
271
+ type: chartType,
272
+ data: items.map((item) => (item as Record<string, unknown>)[yField] ?? 0),
273
+ }));
274
+ return {
275
+ xAxis: { type: 'category', data: categories },
276
+ yAxis: { type: 'value' },
277
+ series,
278
+ ...extraConfig,
279
+ };
280
+ }
281
+
282
+ const series = [
283
+ {
284
+ type: chartType as 'line' | 'bar',
285
+ data: seriesData.map((item) => (item as Record<string, unknown>)[yField] ?? 0),
286
+ },
287
+ ];
288
+
289
+ return {
290
+ xAxis: { type: 'category', data: categories },
291
+ yAxis: { type: 'value' },
292
+ series,
293
+ ...extraConfig,
294
+ };
295
+ }
296
+
297
+ function transformPie(
298
+ data: DataSource,
299
+ mapping: TransformOptions['mapping'],
300
+ extraConfig: Partial<EChartsOption>
301
+ ): EChartsOption {
302
+ const { nameField = 'name', valueField = 'value' } = mapping || {};
303
+
304
+ const seriesData: Array<{ name: string; value: number }> = (data.series || data.rows || []).map(
305
+ (item) => ({
306
+ name: String((item as Record<string, unknown>)[nameField] || ''),
307
+ value: Number((item as Record<string, unknown>)[valueField]) || 0,
308
+ })
309
+ );
310
+
311
+ return {
312
+ series: [{ type: 'pie', radius: '60%', data: seriesData }],
313
+ ...extraConfig,
314
+ };
315
+ }
316
+
317
+ function transformScatter(
318
+ data: DataSource,
319
+ mapping: TransformOptions['mapping'],
320
+ extraConfig: Partial<EChartsOption>
321
+ ): EChartsOption {
322
+ const { xField = 'x', yField = 'y', sizeField } = mapping || {};
323
+
324
+ const seriesData = (data.series || data.rows || []).map((item) => {
325
+ const record = item as Record<string, unknown>;
326
+ const point: (number | string)[] = [Number(record[xField]) || 0, Number(record[yField]) || 0];
327
+ if (sizeField) point.push(Number(record[sizeField]) || 1);
328
+ return point;
329
+ });
330
+
331
+ return {
332
+ xAxis: { type: 'value', scale: true },
333
+ yAxis: { type: 'value', scale: true },
334
+ series: [{ type: 'scatter', data: seriesData }],
335
+ ...extraConfig,
336
+ };
337
+ }
338
+
339
+ function transformRadar(
340
+ data: DataSource,
341
+ mapping: TransformOptions['mapping'],
342
+ extraConfig: Partial<EChartsOption>
343
+ ): EChartsOption {
344
+ const { nameField = 'name', valueField = 'value' } = mapping || {};
345
+
346
+ const indicators = (data.series || data.rows || []).map((item) => {
347
+ const record = item as Record<string, unknown>;
348
+ return {
349
+ name: String(record[nameField] || ''),
350
+ max: Math.max(Number(record[valueField]) || 100, 100),
351
+ };
352
+ });
353
+
354
+ const values = (data.series || data.rows || []).map(
355
+ (item) => Number((item as Record<string, unknown>)[valueField]) || 0
356
+ );
357
+
358
+ return {
359
+ radar: { indicator: indicators },
360
+ series: [{ type: 'radar', data: [{ value: values }] }],
361
+ ...extraConfig,
362
+ };
363
+ }
364
+
365
+ function transformHeatmap(
366
+ data: DataSource,
367
+ mapping: TransformOptions['mapping'],
368
+ extraConfig: Partial<EChartsOption>
369
+ ): EChartsOption {
370
+ const { xField = 'x', yField = 'y', valueField = 'value' } = mapping || {};
371
+
372
+ const xCategories = [
373
+ ...new Set(
374
+ (data.series || data.rows || []).map((d) => String((d as Record<string, unknown>)[xField]))
375
+ ),
376
+ ];
377
+ const yCategories = [
378
+ ...new Set(
379
+ (data.series || data.rows || []).map((d) => String((d as Record<string, unknown>)[yField]))
380
+ ),
381
+ ];
382
+
383
+ const seriesData = (data.series || data.rows || []).map((item) => {
384
+ const record = item as Record<string, unknown>;
385
+ const xIndex = xCategories.indexOf(String(record[xField]));
386
+ const yIndex = yCategories.indexOf(String(record[yField]));
387
+ return [xIndex, yIndex, Number(record[valueField]) || 0];
388
+ });
389
+
390
+ return {
391
+ xAxis: { type: 'category', data: xCategories },
392
+ yAxis: { type: 'category', data: yCategories },
393
+ visualMap: { min: 0, calculable: true },
394
+ series: [{ type: 'heatmap', data: seriesData }],
395
+ ...extraConfig,
396
+ };
397
+ }
398
+
399
+ function groupByTime(
400
+ data: DataItem[],
401
+ dateField: string,
402
+ period: TimePeriod
403
+ ): Record<string, DataItem[]> {
404
+ return data.reduce(
405
+ (acc, item) => {
406
+ const date = new Date(String((item as Record<string, unknown>)[dateField]));
407
+ let key: string;
408
+
409
+ switch (period) {
410
+ case 'day':
411
+ key = date.toISOString().split('T')[0];
412
+ break;
413
+ case 'week': {
414
+ const week = getWeekNumber(date);
415
+ key = `${date.getFullYear()}-W${week}`;
416
+ break;
417
+ }
418
+ case 'month':
419
+ key = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
420
+ break;
421
+ case 'quarter':
422
+ key = `${date.getFullYear()}-Q${Math.ceil((date.getMonth() + 1) / 3)}`;
423
+ break;
424
+ case 'year':
425
+ key = String(date.getFullYear());
426
+ break;
427
+ default:
428
+ key = date.toISOString().split('T')[0];
429
+ }
430
+
431
+ if (!acc[key]) acc[key] = [];
432
+ acc[key].push(item);
433
+ return acc;
434
+ },
435
+ {} as Record<string, DataItem[]>
436
+ );
437
+ }
438
+
439
+ function getWeekNumber(date: Date): number {
440
+ const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
441
+ const dayNum = d.getUTCDay() || 7;
442
+ d.setUTCDate(d.getUTCDate() + 4 - dayNum);
443
+ const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
444
+ return Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
445
+ }
446
+
447
+ function aggregateValues(
448
+ items: DataItem[],
449
+ field: string,
450
+ method: AggregationType,
451
+ fillMissing?: 'zero' | 'forward' | 'interpolate'
452
+ ): number {
453
+ if (items.length === 0) {
454
+ if (fillMissing === 'zero') return 0;
455
+ return NaN;
456
+ }
457
+
458
+ const values = items.map((item) => Number((item as Record<string, unknown>)[field]) || 0);
459
+
460
+ switch (method) {
461
+ case 'sum': {
462
+ let sum = 0;
463
+ for (let i = 0; i < values.length; i++) sum += values[i];
464
+ return sum;
465
+ }
466
+ case 'average': {
467
+ if (values.length === 0) return 0;
468
+ let sum = 0;
469
+ for (let i = 0; i < values.length; i++) sum += values[i];
470
+ return sum / values.length;
471
+ }
472
+ case 'max': {
473
+ let max = values[0];
474
+ for (let i = 1; i < values.length; i++) if (values[i] > max) max = values[i];
475
+ return max;
476
+ }
477
+ case 'min': {
478
+ let min = values[0];
479
+ for (let i = 1; i < values.length; i++) if (values[i] < min) min = values[i];
480
+ return min;
481
+ }
482
+ case 'count':
483
+ return values.length;
484
+ case 'first':
485
+ return values[0];
486
+ case 'last':
487
+ return values[values.length - 1];
488
+ default:
489
+ return values[0];
490
+ }
491
+ }
492
+
493
+ // ============================================================================
494
+ // 导出
495
+ // ============================================================================
496
+
497
+ export const useTransform = useDataTransform;
498
+
499
+ export default {
500
+ useDataTransform,
501
+ useTableTransform,
502
+ useTimeSeriesTransform,
503
+ };
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * TaroViz - 基于 Taro 和 ECharts 的多端图表组件库
3
- * @version 1.2.1
3
+ * @version 1.6.0
4
4
  */
5
5
 
6
6
  // 核心组件
@@ -45,11 +45,20 @@ export { default as HeatmapChart } from './charts/heatmap';
45
45
  export { default as GaugeChart } from './charts/gauge';
46
46
  export { default as FunnelChart } from './charts/funnel';
47
47
 
48
- // 扩展图表组件 (新增)
48
+ // 扩展图表组件
49
49
  export { default as TreeMapChart } from './charts/treemap';
50
50
  export { default as SunburstChart } from './charts/sunburst';
51
51
  export { default as SankeyChart } from './charts/sankey';
52
52
 
53
+ // 新增图表组件
54
+ export { default as GraphChart } from './charts/graph';
55
+ export { default as CandlestickChart } from './charts/candlestick';
56
+ export { default as WordCloudChart } from './charts/wordcloud';
57
+
58
+ // v1.6.0 新增图表组件
59
+ export { default as BoxplotChart } from './charts/boxplot';
60
+ export { default as ParallelChart } from './charts/parallel';
61
+
53
62
  // 适配器
54
63
  export { getAdapter, detectPlatform, getEnv } from './adapters';
55
64
  export { default as H5Adapter } from './adapters/h5';
@@ -70,7 +79,7 @@ export {
70
79
  getThemesByTag,
71
80
  } from './themes';
72
81
 
73
- // 主题管理器 (新增)
82
+ // 主题管理器
74
83
  export {
75
84
  themeManager,
76
85
  PRESET_THEMES,
@@ -81,11 +90,17 @@ export {
81
90
 
82
91
  // 编辑器
83
92
  export { ThemeEditor } from './editor';
93
+ export { default as EnhancedThemeEditor } from './editor/EnhancedThemeEditor';
94
+ export type { EnhancedThemeEditorProps, ThemeExportOptions } from './editor/EnhancedThemeEditor';
84
95
 
85
- // 错误边界组件 (新增)
86
- export { ErrorBoundary, withErrorBoundary, type ErrorBoundaryProps } from './core/components/ErrorBoundary';
96
+ // 错误边界组件
97
+ export {
98
+ ErrorBoundary,
99
+ withErrorBoundary,
100
+ type ErrorBoundaryProps,
101
+ } from './core/components/ErrorBoundary';
87
102
 
88
- // 懒加载组件 (新增)
103
+ // 懒加载组件
89
104
  export {
90
105
  withLazyLoad,
91
106
  preloadChart,
@@ -94,7 +109,7 @@ export {
94
109
  LazyChartRegistry,
95
110
  } from './core/components/LazyChart';
96
111
 
97
- // 标注系统 (新增)
112
+ // 标注系统
98
113
  export {
99
114
  useAnnotation,
100
115
  convertAnnotationToMarkLine,
@@ -109,7 +124,7 @@ export {
109
124
  type ScatterAnnotationConfig,
110
125
  } from './core/components/Annotation';
111
126
 
112
- // 导出工具 (新增)
127
+ // 导出工具
113
128
  export {
114
129
  exportChart,
115
130
  type ExportImageOptions,
@@ -134,10 +149,13 @@ export {
134
149
  useFullscreen,
135
150
  useExport,
136
151
  useChartTools,
152
+ useDataTransform,
153
+ useTableTransform,
154
+ useTimeSeriesTransform,
137
155
  } from './hooks';
138
156
 
139
157
  /**
140
158
  * 库信息
141
159
  */
142
160
  export const name = 'taroviz';
143
- export const version = '1.2.1';
161
+ export const version = '1.6.0';
package/src/main.tsx CHANGED
@@ -1,13 +1,13 @@
1
1
  import React, { useState } from 'react';
2
2
  import { createRoot } from 'react-dom/client';
3
3
 
4
- import type { ChartOptions, EChartsType } from './core/types';
4
+ import type { EChartsType } from './core/types';
5
5
 
6
6
  import { BaseChart, LineChart, BarChart, PieChart } from './index';
7
7
 
8
- // Type assertion helper
9
- const asChartOptions = <T extends Record<string, any>>(options: T): ChartOptions => {
10
- return options as ChartOptions;
8
+ // Type assertion helper - use any to bypass complex type mismatches
9
+ const asChartOptions = <T extends Record<string, any>>(options: T): any => {
10
+ return options;
11
11
  };
12
12
 
13
13
  // Example data for various chart types
@@ -83,8 +83,8 @@ describe('Theme System', () => {
83
83
  expect(darkTheme).toHaveProperty('colors');
84
84
  expect(Array.isArray(darkTheme.colors)).toBe(true);
85
85
  expect(darkTheme.colors).toHaveLength(9);
86
- expect(darkTheme).toHaveProperty('backgroundColor', '#0f1117');
87
- expect(darkTheme).toHaveProperty('textColor', '#e1e1e1');
86
+ expect(darkTheme).toHaveProperty('backgroundColor', '#1a1a2e');
87
+ expect(darkTheme).toHaveProperty('textColor', '#e8e8e8');
88
88
  expect(darkTheme).toHaveProperty('fontFamily');
89
89
  });
90
90
  });
@@ -703,6 +703,18 @@ export function unregisterTheme(name: string): void {
703
703
  themeRegistry.delete(name);
704
704
  }
705
705
 
706
+ /**
707
+ * 重置主题注册表(清除所有已注册的主题,恢复内置主题)
708
+ * 主要用于测试环境
709
+ */
710
+ export function resetThemeRegistry(): void {
711
+ themeRegistry.clear();
712
+ // 重新注册所有内置主题
713
+ Object.entries(builtinThemes).forEach(([name, theme]) => {
714
+ themeRegistry.set(name, theme as ThemeOptions);
715
+ });
716
+ }
717
+
706
718
  /**
707
719
  * 动态切换主题
708
720
  * @param theme 主题名称或主题配置
@@ -853,6 +865,7 @@ export default {
853
865
  getRegisteredThemes,
854
866
  getThemeByName,
855
867
  unregisterTheme,
868
+ resetThemeRegistry,
856
869
  switchTheme,
857
870
  getThemesByTag,
858
871
  getLightThemes,