@wavemaker/react-native-echarts 1.0.0-dev.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 (145) hide show
  1. package/.npmignore +4 -0
  2. package/LICENSE +21 -0
  3. package/README.md +39 -0
  4. package/area/area-chart.d.ts +12 -0
  5. package/area/area-chart.d.ts.map +1 -0
  6. package/area/area-chart.js +404 -0
  7. package/area/area-chart.props.d.ts +64 -0
  8. package/area/area-chart.props.d.ts.map +1 -0
  9. package/area/area-chart.props.js +0 -0
  10. package/area/index.d.ts +3 -0
  11. package/area/index.d.ts.map +1 -0
  12. package/area/index.js +1 -0
  13. package/axis.d.ts +3 -0
  14. package/axis.d.ts.map +1 -0
  15. package/axis.js +16 -0
  16. package/bar/bar-chart.d.ts +11 -0
  17. package/bar/bar-chart.d.ts.map +1 -0
  18. package/bar/bar-chart.js +6 -0
  19. package/bar/bar-chart.props.d.ts +7 -0
  20. package/bar/bar-chart.props.d.ts.map +1 -0
  21. package/bar/bar-chart.props.js +0 -0
  22. package/bar/index.d.ts +3 -0
  23. package/bar/index.d.ts.map +1 -0
  24. package/bar/index.js +1 -0
  25. package/bubble/bubble-chart.d.ts +13 -0
  26. package/bubble/bubble-chart.d.ts.map +1 -0
  27. package/bubble/bubble-chart.js +305 -0
  28. package/bubble/bubble-chart.props.d.ts +26 -0
  29. package/bubble/bubble-chart.props.d.ts.map +1 -0
  30. package/bubble/bubble-chart.props.js +0 -0
  31. package/bubble/index.d.ts +3 -0
  32. package/bubble/index.d.ts.map +1 -0
  33. package/bubble/index.js +1 -0
  34. package/candlestick/candlestick-chart.d.ts +12 -0
  35. package/candlestick/candlestick-chart.d.ts.map +1 -0
  36. package/candlestick/candlestick-chart.js +292 -0
  37. package/candlestick/candlestick-chart.props.d.ts +51 -0
  38. package/candlestick/candlestick-chart.props.d.ts.map +1 -0
  39. package/candlestick/candlestick-chart.props.js +0 -0
  40. package/candlestick/index.d.ts +3 -0
  41. package/candlestick/index.d.ts.map +1 -0
  42. package/candlestick/index.js +1 -0
  43. package/chart-container.d.ts +6 -0
  44. package/chart-container.d.ts.map +1 -0
  45. package/chart-container.js +63 -0
  46. package/chart-theme.context.d.ts +191 -0
  47. package/chart-theme.context.d.ts.map +1 -0
  48. package/chart-theme.context.js +276 -0
  49. package/column/column-chart.d.ts +13 -0
  50. package/column/column-chart.d.ts.map +1 -0
  51. package/column/column-chart.js +481 -0
  52. package/column/column-chart.props.d.ts +83 -0
  53. package/column/column-chart.props.d.ts.map +1 -0
  54. package/column/column-chart.props.js +0 -0
  55. package/column/index.d.ts +3 -0
  56. package/column/index.d.ts.map +1 -0
  57. package/column/index.js +1 -0
  58. package/gauge/digital/digital.gauge.d.ts +28 -0
  59. package/gauge/digital/digital.gauge.d.ts.map +1 -0
  60. package/gauge/digital/digital.gauge.js +213 -0
  61. package/gauge/gauge.types.d.ts +51 -0
  62. package/gauge/gauge.types.d.ts.map +1 -0
  63. package/gauge/gauge.types.js +0 -0
  64. package/gauge/index.d.ts +6 -0
  65. package/gauge/index.d.ts.map +1 -0
  66. package/gauge/index.js +4 -0
  67. package/gauge/radial/radial.gauge.d.ts +18 -0
  68. package/gauge/radial/radial.gauge.d.ts.map +1 -0
  69. package/gauge/radial/radial.gauge.js +284 -0
  70. package/gauge/simple/simple.gauge.d.ts +28 -0
  71. package/gauge/simple/simple.gauge.d.ts.map +1 -0
  72. package/gauge/simple/simple.gauge.js +102 -0
  73. package/gauge/speedometer/speedometer.gauge.d.ts +35 -0
  74. package/gauge/speedometer/speedometer.gauge.d.ts.map +1 -0
  75. package/gauge/speedometer/speedometer.gauge.js +241 -0
  76. package/geo/geo-chart.d.ts +15 -0
  77. package/geo/geo-chart.d.ts.map +1 -0
  78. package/geo/geo-chart.js +200 -0
  79. package/geo/geo-chart.props.d.ts +96 -0
  80. package/geo/geo-chart.props.d.ts.map +1 -0
  81. package/geo/geo-chart.props.js +0 -0
  82. package/geo/index.d.ts +7 -0
  83. package/geo/index.d.ts.map +1 -0
  84. package/geo/index.js +3 -0
  85. package/geo/us-chart.d.ts +15 -0
  86. package/geo/us-chart.d.ts.map +1 -0
  87. package/geo/us-chart.js +15 -0
  88. package/geo/world-chart.d.ts +15 -0
  89. package/geo/world-chart.d.ts.map +1 -0
  90. package/geo/world-chart.js +10 -0
  91. package/index.d.ts +17 -0
  92. package/index.d.ts.map +1 -0
  93. package/index.js +15 -0
  94. package/line/index.d.ts +3 -0
  95. package/line/index.d.ts.map +1 -0
  96. package/line/index.js +1 -0
  97. package/line/line-chart.d.ts +9 -0
  98. package/line/line-chart.d.ts.map +1 -0
  99. package/line/line-chart.js +8 -0
  100. package/line/line-chart.props.d.ts +12 -0
  101. package/line/line-chart.props.d.ts.map +1 -0
  102. package/line/line-chart.props.js +0 -0
  103. package/package.json +39 -0
  104. package/pie/index.d.ts +4 -0
  105. package/pie/index.d.ts.map +1 -0
  106. package/pie/index.js +2 -0
  107. package/pie/pie-chart.d.ts +13 -0
  108. package/pie/pie-chart.d.ts.map +1 -0
  109. package/pie/pie-chart.js +222 -0
  110. package/pie/pie-chart.props.d.ts +97 -0
  111. package/pie/pie-chart.props.d.ts.map +1 -0
  112. package/pie/pie-chart.props.js +12 -0
  113. package/props/cartesian.d.ts +120 -0
  114. package/props/cartesian.d.ts.map +1 -0
  115. package/props/cartesian.js +0 -0
  116. package/props/common.d.ts +28 -0
  117. package/props/common.d.ts.map +1 -0
  118. package/props/common.js +0 -0
  119. package/radar/index.d.ts +3 -0
  120. package/radar/index.d.ts.map +1 -0
  121. package/radar/index.js +1 -0
  122. package/radar/radar-chart.d.ts +12 -0
  123. package/radar/radar-chart.d.ts.map +1 -0
  124. package/radar/radar-chart.js +197 -0
  125. package/radar/radar-chart.props.d.ts +80 -0
  126. package/radar/radar-chart.props.d.ts.map +1 -0
  127. package/radar/radar-chart.props.js +0 -0
  128. package/radial/index.d.ts +3 -0
  129. package/radial/index.d.ts.map +1 -0
  130. package/radial/index.js +1 -0
  131. package/radial/radial-chart.d.ts +12 -0
  132. package/radial/radial-chart.d.ts.map +1 -0
  133. package/radial/radial-chart.js +235 -0
  134. package/radial/radial-chart.props.d.ts +74 -0
  135. package/radial/radial-chart.props.d.ts.map +1 -0
  136. package/radial/radial-chart.props.js +0 -0
  137. package/scatter/index.d.ts +3 -0
  138. package/scatter/index.d.ts.map +1 -0
  139. package/scatter/index.js +1 -0
  140. package/scatter/scatter-chart.d.ts +13 -0
  141. package/scatter/scatter-chart.d.ts.map +1 -0
  142. package/scatter/scatter-chart.js +310 -0
  143. package/scatter/scatter-chart.props.d.ts +36 -0
  144. package/scatter/scatter-chart.props.d.ts.map +1 -0
  145. package/scatter/scatter-chart.props.js +0 -0
@@ -0,0 +1,305 @@
1
+ import { withResponsiveContainer } from '../chart-container';
2
+ import { useChartTheme, withChartTheme } from '../chart-theme.context';
3
+ import { SkiaChart, SkiaRenderer } from '@wuba/react-native-echarts';
4
+ import { ScatterChart as EChartsScatterChart } from 'echarts/charts';
5
+ import { GridComponent, LegendComponent, TooltipComponent, } from 'echarts/components';
6
+ import * as echarts from 'echarts/core';
7
+ import React, { useEffect, useMemo, useRef } from 'react';
8
+ echarts.use([
9
+ TooltipComponent,
10
+ GridComponent,
11
+ LegendComponent,
12
+ SkiaRenderer,
13
+ EChartsScatterChart,
14
+ ]);
15
+ function scaleSize(value, dataMin, dataMax, outMin, outMax) {
16
+ if (dataMax <= dataMin)
17
+ return outMin;
18
+ const t = (value - dataMin) / (dataMax - dataMin);
19
+ return outMin + t * (outMax - outMin);
20
+ }
21
+ const ChartComponent = ({ data, width = 220, height = 350, symbol = 'circle', sizeRange = [8, 50], showXAxis = true, showXAxisTicks = true, showYAxis = true, showYAxisTicks = true, showXAxisSplitLines = true, showYAxisSplitLines = true, boundaryGap = false, grid, showLegend = false, showHighlighter = true, xAxisTickLabelFormatter, yAxisTickLabelFormatter, xAxisLabel, yAxisLabel, onSelect, ...props }) => {
22
+ const { theme } = useChartTheme(props.theme, props.colors);
23
+ const chartRef = useRef(null);
24
+ const onSelectRef = useRef(onSelect);
25
+ onSelectRef.current = onSelect;
26
+ const selectContextRef = useRef({ normalizedSeries: [] });
27
+ const normalizedSeries = useMemo(() => {
28
+ if (!Array.isArray(data) || data.length === 0)
29
+ return [];
30
+ const first = data[0];
31
+ if (Array.isArray(first) && first.length >= 2 && typeof first[0] === 'number') {
32
+ const rows = data;
33
+ const normalized = rows.map((row) => {
34
+ const x = Number(row[0]);
35
+ const y = Number(row[1]);
36
+ const size = row.length >= 3 && typeof row[2] === 'number' ? Number(row[2]) : 10;
37
+ return [x, y, size];
38
+ });
39
+ return [{ data: normalized }];
40
+ }
41
+ if (typeof first === 'object' && first !== null && 'data' in first) {
42
+ return data.map((s) => ({
43
+ name: s.name,
44
+ data: s.data.map((row) => {
45
+ const x = Number(row[0]);
46
+ const y = Number(row[1]);
47
+ const size = row.length >= 3 && typeof row[2] === 'number' ? Number(row[2]) : 10;
48
+ return [x, y, size];
49
+ }),
50
+ }));
51
+ }
52
+ return [];
53
+ }, [data]);
54
+ const hasNamedSeries = useMemo(() => normalizedSeries.some((s) => 'name' in s && s.name), [normalizedSeries]);
55
+ selectContextRef.current = { normalizedSeries };
56
+ const option = useMemo(() => {
57
+ const tooltipConfig = showHighlighter
58
+ ? {
59
+ trigger: 'item',
60
+ axisPointer: {
61
+ type: 'cross',
62
+ lineStyle: {
63
+ type: 'line',
64
+ width: 1,
65
+ color: theme.series[0]?.color ?? '#999',
66
+ },
67
+ },
68
+ formatter: (params) => {
69
+ const p = Array.isArray(params) ? params[0] : params;
70
+ const d = p?.data;
71
+ if (!d || !Array.isArray(d))
72
+ return '';
73
+ const name = p.seriesName ? `${p.seriesName}<br/>` : '';
74
+ return `${name}x: ${d[0]}, y: ${d[1]}${d[2] != null ? `, size: ${d[2]}` : ''}`;
75
+ },
76
+ }
77
+ : {
78
+ trigger: 'item',
79
+ formatter: (params) => {
80
+ const p = Array.isArray(params) ? params[0] : params;
81
+ const d = p?.data;
82
+ if (!d || !Array.isArray(d))
83
+ return '';
84
+ const name = p.seriesName ? `${p.seriesName}<br/>` : '';
85
+ return `${name}x: ${d[0]}, y: ${d[1]}${d[2] != null ? `, size: ${d[2]}` : ''}`;
86
+ },
87
+ };
88
+ const xAxisConfig = {
89
+ type: 'value',
90
+ boundaryGap,
91
+ ...(xAxisLabel != null && xAxisLabel !== '' && {
92
+ name: xAxisLabel,
93
+ nameLocation: 'middle',
94
+ nameGap: 25,
95
+ nameTextStyle: { color: theme.axis.x.tickLabelColor },
96
+ }),
97
+ axisLabel: {
98
+ show: showXAxis || xAxisTickLabelFormatter != null,
99
+ color: theme.axis.x.tickLabelColor,
100
+ ...(xAxisTickLabelFormatter && { formatter: xAxisTickLabelFormatter }),
101
+ },
102
+ axisLine: showXAxis
103
+ ? {
104
+ show: true,
105
+ lineStyle: {
106
+ color: theme.axis.x.lineColor,
107
+ width: theme.axis.x.lineWidth,
108
+ },
109
+ }
110
+ : { show: false },
111
+ axisTick: {
112
+ show: showXAxisTicks,
113
+ lineStyle: {
114
+ color: theme.axis.x.tickColor,
115
+ width: theme.axis.x.tickWidth,
116
+ },
117
+ },
118
+ splitLine: {
119
+ show: showXAxisSplitLines,
120
+ lineStyle: {
121
+ color: theme.axis.x.splitLineColor,
122
+ width: theme.axis.x.splitLineWidth,
123
+ },
124
+ },
125
+ };
126
+ const yAxisConfig = {
127
+ type: 'value',
128
+ ...(yAxisLabel != null && yAxisLabel !== '' && {
129
+ name: yAxisLabel,
130
+ nameLocation: 'middle',
131
+ nameGap: 40,
132
+ nameTextStyle: { color: theme.axis.y.tickLabelColor },
133
+ }),
134
+ axisLabel: {
135
+ show: showYAxis || yAxisTickLabelFormatter != null,
136
+ color: theme.axis.y.tickLabelColor,
137
+ ...(yAxisTickLabelFormatter && { formatter: yAxisTickLabelFormatter }),
138
+ },
139
+ axisLine: showYAxis
140
+ ? {
141
+ show: true,
142
+ lineStyle: {
143
+ color: theme.axis.y.lineColor,
144
+ width: theme.axis.y.lineWidth,
145
+ },
146
+ }
147
+ : { show: false },
148
+ axisTick: {
149
+ show: showYAxisTicks,
150
+ lineStyle: {
151
+ color: theme.axis.y.tickColor,
152
+ width: theme.axis.y.tickWidth,
153
+ },
154
+ },
155
+ splitLine: {
156
+ show: showYAxisSplitLines,
157
+ lineStyle: {
158
+ color: theme.axis.y.splitLineColor,
159
+ width: theme.axis.y.splitLineWidth,
160
+ },
161
+ },
162
+ };
163
+ const legendConfig = showLegend && hasNamedSeries
164
+ ? {
165
+ data: normalizedSeries
166
+ .filter((s) => 'name' in s && s.name)
167
+ .map((s) => s.name),
168
+ textStyle: {
169
+ color: theme.legend.textColor,
170
+ fontSize: theme.legend.fontSize,
171
+ },
172
+ backgroundColor: theme.legend.backgroundColor,
173
+ }
174
+ : undefined;
175
+ const [sizeMinPx, sizeMaxPx] = sizeRange;
176
+ const seriesConfig = [];
177
+ normalizedSeries.forEach((s, index) => {
178
+ const seriesColor = theme.series[index % theme.series.length].color;
179
+ const seriesData = 'data' in s ? s.data : [];
180
+ const seriesName = 'name' in s && s.name ? s.name : undefined;
181
+ const sizes = seriesData.map((d) => d[2]);
182
+ const dataSizeMin = sizes.length ? Math.min(...sizes) : 0;
183
+ const dataSizeMax = sizes.length ? Math.max(...sizes) : 10;
184
+ const scatterSeries = {
185
+ type: 'scatter',
186
+ data: seriesData,
187
+ symbol: symbol === 'none' ? 'circle' : symbol,
188
+ symbolSize: (value, params) => {
189
+ const d = params.data;
190
+ const sizeVal = Array.isArray(d) && typeof d[2] === 'number' ? d[2] : 10;
191
+ return scaleSize(sizeVal, dataSizeMin, dataSizeMax, sizeMinPx, sizeMaxPx);
192
+ },
193
+ itemStyle: { color: seriesColor },
194
+ emphasis: showHighlighter
195
+ ? {
196
+ focus: 'self',
197
+ scale: true,
198
+ itemStyle: {
199
+ color: seriesColor,
200
+ borderColor: '#FFFFFF',
201
+ borderWidth: 2,
202
+ },
203
+ }
204
+ : { focus: 'none', scale: false },
205
+ };
206
+ if (seriesName)
207
+ scatterSeries.name = seriesName;
208
+ seriesConfig.push(scatterSeries);
209
+ });
210
+ const config = {
211
+ tooltip: tooltipConfig,
212
+ xAxis: xAxisConfig,
213
+ yAxis: yAxisConfig,
214
+ series: seriesConfig,
215
+ };
216
+ if (legendConfig)
217
+ config.legend = legendConfig;
218
+ if (grid)
219
+ config.grid = grid;
220
+ return config;
221
+ }, [
222
+ theme,
223
+ normalizedSeries,
224
+ symbol,
225
+ sizeRange,
226
+ showXAxis,
227
+ showXAxisTicks,
228
+ showYAxis,
229
+ showYAxisTicks,
230
+ showXAxisSplitLines,
231
+ showYAxisSplitLines,
232
+ boundaryGap,
233
+ grid,
234
+ showLegend,
235
+ hasNamedSeries,
236
+ showHighlighter,
237
+ xAxisTickLabelFormatter,
238
+ yAxisTickLabelFormatter,
239
+ xAxisLabel,
240
+ yAxisLabel,
241
+ ]);
242
+ useEffect(() => {
243
+ let chart;
244
+ if (chartRef.current) {
245
+ try {
246
+ chart = echarts.init(chartRef.current, 'light', { width, height });
247
+ chart.setOption(option);
248
+ const handleSeriesClick = (params) => {
249
+ const cb = onSelectRef.current;
250
+ if (typeof cb !== 'function')
251
+ return;
252
+ if (params.componentType !== 'series')
253
+ return;
254
+ if (params.seriesType !== 'scatter')
255
+ return;
256
+ const seriesIndex = params.seriesIndex;
257
+ const dataIndex = params.dataIndex;
258
+ if (typeof seriesIndex !== 'number' ||
259
+ typeof dataIndex !== 'number' ||
260
+ dataIndex < 0) {
261
+ return;
262
+ }
263
+ const { normalizedSeries: ns } = selectContextRef.current;
264
+ const s = ns[seriesIndex];
265
+ if (!s?.data || !Array.isArray(s.data))
266
+ return;
267
+ const point = s.data[dataIndex];
268
+ if (!point || point.length < 2)
269
+ return;
270
+ const seriesName = s.name != null && s.name !== ''
271
+ ? String(s.name)
272
+ : `Series ${seriesIndex + 1}`;
273
+ const event = {
274
+ seriesIndex,
275
+ dataIndex,
276
+ seriesName,
277
+ x: point[0],
278
+ y: Number(point[1]),
279
+ ...(typeof point[2] === 'number' ? { z: point[2] } : {}),
280
+ };
281
+ cb(event);
282
+ };
283
+ chart.on('click', handleSeriesClick);
284
+ }
285
+ catch (error) {
286
+ console.warn('Chart initialization error:', error);
287
+ }
288
+ }
289
+ return () => {
290
+ if (chart) {
291
+ try {
292
+ chart.dispose();
293
+ }
294
+ catch (error) {
295
+ console.warn('Chart disposal error:', error);
296
+ }
297
+ }
298
+ };
299
+ }, [option, width, height]);
300
+ return <SkiaChart ref={chartRef} useRNGH/>;
301
+ };
302
+ const BubbleChartComponent = withResponsiveContainer(withChartTheme(ChartComponent));
303
+ export const BubbleChart = Object.assign(BubbleChartComponent, {
304
+ displayName: 'BubbleChart',
305
+ });
@@ -0,0 +1,26 @@
1
+ import type { ScatterChartProps } from '../scatter/scatter-chart.props';
2
+ /**
3
+ * Bubble data: [x, y, size]. The third value (size) drives the bubble radius.
4
+ * - Single series: [number, number, number][]
5
+ * - Multiple series: Array<{ name: string; data: [number, number, number][] }>
6
+ */
7
+ export type BubbleSeriesData = [number, number, number][] | Array<{
8
+ name: string;
9
+ data: [number, number, number][];
10
+ }>;
11
+ /**
12
+ * Props for BubbleChart.
13
+ * common -> cartesian -> scatter -> bubble
14
+ */
15
+ export interface BubbleChartProps extends Omit<ScatterChartProps, 'data' | 'symbolSize'> {
16
+ /**
17
+ * Bubble data: array of [x, y, size] per series. Size drives bubble radius.
18
+ */
19
+ data: BubbleSeriesData;
20
+ /**
21
+ * Pixel size range [min, max] for scaling the third data value to bubble radius.
22
+ * @default [8, 50]
23
+ */
24
+ sizeRange?: [number, number];
25
+ }
26
+ //# sourceMappingURL=bubble-chart.props.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bubble-chart.props.d.ts","sourceRoot":"","sources":["../../../../components/chart/bubble/bubble-chart.props.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAExE;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GACxB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAC1B,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;CAAE,CAAC,CAAC;AAE9D;;;GAGG;AACH,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,YAAY,CAAC;IACtF;;OAEG;IACH,IAAI,EAAE,gBAAgB,CAAC;IACvB;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B"}
File without changes
@@ -0,0 +1,3 @@
1
+ export { BubbleChart } from './bubble-chart';
2
+ export type { BubbleChartProps, BubbleSeriesData } from './bubble-chart.props';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/chart/bubble/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1 @@
1
+ export { BubbleChart } from './bubble-chart';
@@ -0,0 +1,12 @@
1
+ import type { CandlestickChartProps } from './candlestick-chart.props';
2
+ import React from 'react';
3
+ export type { CandlestickChartProps, CandlestickData, CandlestickItem } from './candlestick-chart.props';
4
+ export declare const CandlestickChart: ((props: CandlestickChartProps & {
5
+ theme?: Partial<import("..").ChartTheme>;
6
+ } & {
7
+ width?: number | string;
8
+ height?: number | string;
9
+ }) => React.JSX.Element) & {
10
+ displayName: string;
11
+ };
12
+ //# sourceMappingURL=candlestick-chart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"candlestick-chart.d.ts","sourceRoot":"","sources":["../../../../components/chart/candlestick/candlestick-chart.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAmB,MAAM,2BAA2B,CAAC;AASxF,OAAO,KAAqC,MAAM,OAAO,CAAC;AAG1D,YAAY,EAAE,qBAAqB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAyUzG,eAAO,MAAM,gBAAgB;;;;;;;CAE3B,CAAC"}
@@ -0,0 +1,292 @@
1
+ import { withResponsiveContainer } from '../chart-container';
2
+ import { useChartTheme, withChartTheme } from '../chart-theme.context';
3
+ import { SkiaChart, SkiaRenderer } from '@wuba/react-native-echarts';
4
+ import { BarChart, CandlestickChart as EChartsCandlestickChart, LineChart } from 'echarts/charts';
5
+ import { GridComponent, LegendComponent, TooltipComponent, } from 'echarts/components';
6
+ import * as echarts from 'echarts/core';
7
+ import React, { useEffect, useMemo, useRef } from 'react';
8
+ echarts.use([
9
+ TooltipComponent,
10
+ GridComponent,
11
+ LegendComponent,
12
+ SkiaRenderer,
13
+ EChartsCandlestickChart,
14
+ BarChart,
15
+ LineChart,
16
+ ]);
17
+ const ChartComponent = ({ data, xAxisData, volumeData, ma5, ma10, ma20, width = 220, height = 350, boundaryGap = true, showXAxis = true, showXAxisTicks = true, showYAxis = true, showYAxisTicks = true, showXAxisSplitLines = true, showYAxisSplitLines = true, grid, showLegend = false, showHighlighter = true, positiveColor = '#008000', negativeColor = '#FF2C2C', xAxisTickLabelFormatter, yAxisTickLabelFormatter, xAxisTicks, xAxisLabel, yAxisLabel, onSelect, ...props }) => {
18
+ const { theme } = useChartTheme(props.theme, undefined);
19
+ const chartRef = useRef(null);
20
+ const onSelectRef = useRef(onSelect);
21
+ onSelectRef.current = onSelect;
22
+ const selectContextRef = useRef({ categories: [], ohlcData: [] });
23
+ const categories = useMemo(() => {
24
+ if (xAxisTicks != null && xAxisTicks.length > 0)
25
+ return xAxisTicks;
26
+ if (xAxisData?.length)
27
+ return xAxisData.map(String);
28
+ return data.map((_, i) => String(i));
29
+ }, [xAxisTicks, xAxisData, data]);
30
+ const hasVolume = volumeData != null && volumeData.length > 0;
31
+ const hasMA = (ma5?.length ?? 0) > 0 || (ma10?.length ?? 0) > 0 || (ma20?.length ?? 0) > 0;
32
+ selectContextRef.current = { categories, ohlcData: data };
33
+ const option = useMemo(() => {
34
+ if (!data?.length)
35
+ return { series: [] };
36
+ const tooltipConfig = showHighlighter
37
+ ? {
38
+ trigger: 'axis',
39
+ axisPointer: { type: 'cross' },
40
+ }
41
+ : { trigger: 'axis' };
42
+ const volumeColorUp = positiveColor + '40';
43
+ const volumeColorDown = negativeColor + '40';
44
+ const mainGrid = hasVolume
45
+ ? { left: '10%', right: '8%', top: '8%', height: '55%' }
46
+ : grid ?? { left: '10%', right: '8%', top: '8%', bottom: '15%' };
47
+ const volumeGrid = hasVolume
48
+ ? { left: '10%', right: '8%', top: '72%', height: '18%' }
49
+ : undefined;
50
+ const xAxisMain = {
51
+ type: 'category',
52
+ data: categories,
53
+ boundaryGap,
54
+ ...(xAxisLabel != null && xAxisLabel !== '' && {
55
+ name: xAxisLabel,
56
+ nameLocation: 'middle',
57
+ nameGap: 25,
58
+ nameTextStyle: { color: theme.axis.x.tickLabelColor },
59
+ }),
60
+ axisLabel: {
61
+ show: showXAxis || xAxisTickLabelFormatter != null,
62
+ color: theme.axis.x.tickLabelColor,
63
+ formatter: xAxisTickLabelFormatter ?? undefined,
64
+ },
65
+ axisLine: showXAxis
66
+ ? { show: true, lineStyle: { color: theme.axis.x.lineColor, width: theme.axis.x.lineWidth } }
67
+ : { show: false },
68
+ axisTick: {
69
+ show: showXAxisTicks,
70
+ lineStyle: { color: theme.axis.x.tickColor, width: theme.axis.x.tickWidth },
71
+ },
72
+ splitLine: {
73
+ show: showXAxisSplitLines,
74
+ lineStyle: { color: theme.axis.x.splitLineColor, width: theme.axis.x.splitLineWidth },
75
+ },
76
+ };
77
+ if (hasVolume) {
78
+ xAxisMain.gridIndex = 0;
79
+ xAxisMain.axisLabel = { ...xAxisMain.axisLabel, show: false };
80
+ }
81
+ const yAxisMain = {
82
+ type: 'value',
83
+ scale: true,
84
+ ...(yAxisLabel != null && yAxisLabel !== '' && {
85
+ name: yAxisLabel,
86
+ nameLocation: 'middle',
87
+ nameGap: 40,
88
+ nameTextStyle: { color: theme.axis.y.tickLabelColor },
89
+ }),
90
+ axisLabel: {
91
+ show: showYAxis || yAxisTickLabelFormatter != null,
92
+ color: theme.axis.y.tickLabelColor,
93
+ formatter: yAxisTickLabelFormatter ?? undefined,
94
+ },
95
+ axisLine: showYAxis
96
+ ? { show: true, lineStyle: { color: theme.axis.y.lineColor, width: theme.axis.y.lineWidth } }
97
+ : { show: false },
98
+ axisTick: {
99
+ show: showYAxisTicks,
100
+ lineStyle: { color: theme.axis.y.tickColor, width: theme.axis.y.tickWidth },
101
+ },
102
+ splitLine: {
103
+ show: showYAxisSplitLines,
104
+ lineStyle: { color: theme.axis.y.splitLineColor, width: theme.axis.y.splitLineWidth },
105
+ },
106
+ };
107
+ if (hasVolume)
108
+ yAxisMain.gridIndex = 0;
109
+ const candlestickSeries = {
110
+ type: 'candlestick',
111
+ data,
112
+ itemStyle: {
113
+ color: positiveColor,
114
+ color0: negativeColor,
115
+ borderColor: positiveColor,
116
+ borderColor0: negativeColor,
117
+ },
118
+ emphasis: showHighlighter ? { focus: 'self' } : { focus: 'none' },
119
+ };
120
+ if (hasVolume) {
121
+ candlestickSeries.xAxisIndex = 0;
122
+ candlestickSeries.yAxisIndex = 0;
123
+ }
124
+ const series = [candlestickSeries];
125
+ if (ma5?.length) {
126
+ series.push({
127
+ type: 'line',
128
+ name: 'MA5',
129
+ data: ma5,
130
+ smooth: true,
131
+ symbol: 'none',
132
+ lineStyle: { width: 2, color: theme.series[2]?.color ?? '#ff9800' },
133
+ xAxisIndex: hasVolume ? 0 : undefined,
134
+ yAxisIndex: hasVolume ? 0 : undefined,
135
+ });
136
+ }
137
+ if (ma10?.length) {
138
+ series.push({
139
+ type: 'line',
140
+ name: 'MA10',
141
+ data: ma10,
142
+ smooth: true,
143
+ symbol: 'none',
144
+ lineStyle: { width: 2, color: theme.series[3]?.color ?? '#2196f3' },
145
+ xAxisIndex: hasVolume ? 0 : undefined,
146
+ yAxisIndex: hasVolume ? 0 : undefined,
147
+ });
148
+ }
149
+ if (ma20?.length) {
150
+ series.push({
151
+ type: 'line',
152
+ name: 'MA20',
153
+ data: ma20,
154
+ smooth: true,
155
+ symbol: 'none',
156
+ lineStyle: { width: 2, color: theme.series[4]?.color ?? '#9c27b0' },
157
+ xAxisIndex: hasVolume ? 0 : undefined,
158
+ yAxisIndex: hasVolume ? 0 : undefined,
159
+ });
160
+ }
161
+ const config = {
162
+ tooltip: tooltipConfig,
163
+ grid: hasVolume ? [mainGrid, volumeGrid] : mainGrid,
164
+ xAxis: hasVolume ? [xAxisMain, { ...xAxisMain, gridIndex: 1, axisLabel: { ...xAxisMain.axisLabel, show: true } }] : xAxisMain,
165
+ yAxis: hasVolume
166
+ ? [
167
+ yAxisMain,
168
+ {
169
+ type: 'value',
170
+ gridIndex: 1,
171
+ axisLabel: { show: true, color: theme.axis.y.tickLabelColor },
172
+ axisLine: { show: false },
173
+ axisTick: { show: false },
174
+ splitLine: { show: showYAxisSplitLines, lineStyle: { color: theme.axis.y.splitLineColor } },
175
+ },
176
+ ]
177
+ : yAxisMain,
178
+ series,
179
+ };
180
+ if (hasVolume) {
181
+ const volumeBarData = volumeData.map((v, i) => {
182
+ const ohlc = data[i];
183
+ const isUp = ohlc && ohlc[1] >= ohlc[0];
184
+ return { value: v, itemStyle: { color: isUp ? volumeColorUp : volumeColorDown } };
185
+ });
186
+ series.push({
187
+ type: 'bar',
188
+ name: 'Volume',
189
+ xAxisIndex: 1,
190
+ yAxisIndex: 1,
191
+ data: volumeBarData,
192
+ emphasis: showHighlighter ? { focus: 'self' } : { focus: 'none' },
193
+ });
194
+ }
195
+ if (showLegend && hasMA) {
196
+ config.legend = {
197
+ data: ['MA5', 'MA10', 'MA20'].filter((name) => {
198
+ if (name === 'MA5')
199
+ return ma5?.length;
200
+ if (name === 'MA10')
201
+ return ma10?.length;
202
+ return ma20?.length;
203
+ }),
204
+ textStyle: { color: theme.legend.textColor, fontSize: theme.legend.fontSize },
205
+ backgroundColor: theme.legend.backgroundColor,
206
+ };
207
+ }
208
+ return config;
209
+ }, [
210
+ data,
211
+ categories,
212
+ volumeData,
213
+ ma5,
214
+ ma10,
215
+ ma20,
216
+ hasVolume,
217
+ hasMA,
218
+ theme,
219
+ boundaryGap,
220
+ showXAxis,
221
+ showXAxisTicks,
222
+ showYAxis,
223
+ showYAxisTicks,
224
+ showXAxisSplitLines,
225
+ showYAxisSplitLines,
226
+ grid,
227
+ showLegend,
228
+ showHighlighter,
229
+ positiveColor,
230
+ negativeColor,
231
+ xAxisTickLabelFormatter,
232
+ yAxisTickLabelFormatter,
233
+ xAxisTicks,
234
+ xAxisLabel,
235
+ yAxisLabel,
236
+ ]);
237
+ useEffect(() => {
238
+ let chart;
239
+ if (chartRef.current) {
240
+ try {
241
+ chart = echarts.init(chartRef.current, 'light', { width, height });
242
+ chart.setOption(option);
243
+ const handleSeriesClick = (params) => {
244
+ const cb = onSelectRef.current;
245
+ if (typeof cb !== 'function')
246
+ return;
247
+ if (params.componentType !== 'series')
248
+ return;
249
+ if (params.seriesType !== 'candlestick')
250
+ return;
251
+ const dataIndex = params.dataIndex;
252
+ if (typeof dataIndex !== 'number' || dataIndex < 0)
253
+ return;
254
+ const { categories: cats, ohlcData } = selectContextRef.current;
255
+ const row = ohlcData[dataIndex];
256
+ if (!row || row.length < 4)
257
+ return;
258
+ const [open, close, low, high] = row;
259
+ const x = cats[dataIndex] ?? dataIndex;
260
+ const event = {
261
+ seriesIndex: params.seriesIndex ?? 0,
262
+ dataIndex,
263
+ seriesName: 'Candlestick',
264
+ x,
265
+ y: close,
266
+ ohlc: { open, close, low, high },
267
+ };
268
+ cb(event);
269
+ };
270
+ chart.on('click', handleSeriesClick);
271
+ }
272
+ catch (error) {
273
+ console.warn('Candlestick chart initialization error:', error);
274
+ }
275
+ }
276
+ return () => {
277
+ if (chart) {
278
+ try {
279
+ chart.dispose();
280
+ }
281
+ catch (error) {
282
+ console.warn('Candlestick chart disposal error:', error);
283
+ }
284
+ }
285
+ };
286
+ }, [option, width, height]);
287
+ return <SkiaChart ref={chartRef} useRNGH/>;
288
+ };
289
+ const CandlestickChartComponent = withResponsiveContainer(withChartTheme(ChartComponent));
290
+ export const CandlestickChart = Object.assign(CandlestickChartComponent, {
291
+ displayName: 'CandlestickChart',
292
+ });