@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,102 @@
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 { GaugeChart } from 'echarts/charts';
5
+ import * as echarts from 'echarts/core';
6
+ import React, { useEffect, useMemo, useRef } from 'react';
7
+ // Register necessary components for this chart
8
+ echarts.use([
9
+ SkiaRenderer,
10
+ GaugeChart,
11
+ ]);
12
+ const ChartComponent = ({ value, min = 0, max = 100, width = 220, height = 240, title = 'Metric', detailText = '30%', axisBgColor: axisBgColorProp, axisWidth = 50, tickColor: tickColorProp, ...props }) => {
13
+ const { theme: chartTheme } = useChartTheme(props.theme, props.colors);
14
+ const chartRef = useRef(null);
15
+ const option = useMemo(() => {
16
+ const backgroundColor = axisBgColorProp ?? chartTheme.axis.r.tickColor;
17
+ const mainColor = chartTheme.series[0].color;
18
+ const pointerColor = chartTheme.axis.r.lineColor;
19
+ const titleColor = tickColorProp ?? chartTheme.axis.r.tickLabelColor;
20
+ const detailColor = tickColorProp ?? chartTheme.axis.r.tickLabelColor;
21
+ return {
22
+ series: [
23
+ {
24
+ type: 'gauge',
25
+ center: ['50%', '65%'],
26
+ radius: '90%',
27
+ startAngle: 170,
28
+ endAngle: 10,
29
+ min: min,
30
+ max: max,
31
+ axisLine: {
32
+ lineStyle: {
33
+ width: axisWidth,
34
+ color: [
35
+ [value / max, mainColor],
36
+ [1, backgroundColor],
37
+ ],
38
+ },
39
+ },
40
+ pointer: {
41
+ length: '70%',
42
+ width: 8,
43
+ icon: 'path://M-2,0 L0,-51 L 2,0 A1, 1 0 0 1 -2, 0 Z',
44
+ itemStyle: {
45
+ color: pointerColor,
46
+ },
47
+ },
48
+ axisTick: {
49
+ show: false,
50
+ },
51
+ splitLine: {
52
+ show: false,
53
+ },
54
+ axisLabel: {
55
+ show: false,
56
+ },
57
+ title: {
58
+ offsetCenter: [0, '85%'],
59
+ fontSize: 13,
60
+ fontWeight: '600',
61
+ color: titleColor,
62
+ },
63
+ detail: {
64
+ offsetCenter: [0, '115%'],
65
+ fontSize: 11,
66
+ color: detailColor,
67
+ },
68
+ data: [{ value: value, name: title, detail: { formatter: detailText } }],
69
+ },
70
+ ],
71
+ };
72
+ }, [chartTheme, value, min, max, title, detailText, axisBgColorProp, axisWidth, tickColorProp]);
73
+ useEffect(() => {
74
+ let chart;
75
+ if (chartRef.current) {
76
+ try {
77
+ chart = echarts.init(chartRef.current, 'light', {
78
+ width: width,
79
+ height: height,
80
+ });
81
+ chart.setOption(option);
82
+ }
83
+ catch (error) {
84
+ console.warn('Chart initialization error:', error);
85
+ }
86
+ }
87
+ return () => {
88
+ if (chart) {
89
+ try {
90
+ chart.dispose();
91
+ }
92
+ catch (error) {
93
+ console.warn('Chart disposal error:', error);
94
+ }
95
+ }
96
+ };
97
+ }, [option, width, height]);
98
+ return <SkiaChart ref={chartRef}/>;
99
+ };
100
+ export const SimpleGauge = Object.assign(withResponsiveContainer(withChartTheme(ChartComponent), 'value'), {
101
+ displayName: 'SimpleGauge',
102
+ });
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ import type { BaseGaugeProps } from '../gauge.types';
3
+ /**
4
+ * Props for the SpeedometerGauge component.
5
+ * A semi-circular gauge chart with color-coded segments and modern design.
6
+ */
7
+ interface SpeedometerGaugeProps extends BaseGaugeProps {
8
+ /**
9
+ * Array of colors for the axis segments.
10
+ * If not provided, uses theme colors.
11
+ * Example: ['#e74c3c', '#3498db', '#2ecc71']
12
+ */
13
+ axisColors?: string[];
14
+ /**
15
+ * Array of endpoint values for each color segment.
16
+ * The length should match axisColors length.
17
+ * Each value represents the endpoint (threshold) for that color segment.
18
+ * Example: [40, 80, 120] means:
19
+ * - First color: 0 to 40
20
+ * - Second color: 40 to 80
21
+ * - Third color: 80 to 120
22
+ * If not provided, segments are divided evenly based on max value.
23
+ */
24
+ axisColorLengths?: number[];
25
+ }
26
+ export declare const SpeedometerGauge: ((props: SpeedometerGaugeProps & {
27
+ theme?: Partial<import("../..").ChartTheme>;
28
+ } & {
29
+ width?: number | string;
30
+ height?: number | string;
31
+ }) => React.JSX.Element) & {
32
+ displayName: string;
33
+ };
34
+ export {};
35
+ //# sourceMappingURL=speedometer.gauge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speedometer.gauge.d.ts","sourceRoot":"","sources":["../../../../../components/chart/gauge/speedometer/speedometer.gauge.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAqC,MAAM,OAAO,CAAC;AAE1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAQrD;;;GAGG;AACH,UAAU,qBAAsB,SAAQ,cAAc;IACpD;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;;;;;;;;OASG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AA2PD,eAAO,MAAM,gBAAgB;;;;;;;CAE3B,CAAC"}
@@ -0,0 +1,241 @@
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 { GaugeChart } from 'echarts/charts';
5
+ import * as echarts from 'echarts/core';
6
+ import React, { useEffect, useMemo, useRef } from 'react';
7
+ import { View, StyleSheet } from 'react-native';
8
+ // Register necessary components for this chart
9
+ echarts.use([
10
+ SkiaRenderer,
11
+ GaugeChart,
12
+ ]);
13
+ const ChartComponent = ({ value = 85, min = 0, max = 100, width = 400, height = 250, axisColors, axisColorLengths, axisBgColor = 'transparent', axisWidth = 30, tickColor: tickColorProp, ...props }) => {
14
+ const { theme: chartTheme } = useChartTheme(props.theme, props.colors);
15
+ const chartRef = useRef(null);
16
+ const axisRef = useRef(null);
17
+ const axisOption = useMemo(() => {
18
+ return {
19
+ series: [
20
+ {
21
+ type: 'gauge',
22
+ center: ['50%', '50%'], // Positioned lower to show semi-circle with flat base at bottom
23
+ radius: '90%',
24
+ startAngle: 200, // Start from left (0 position)
25
+ endAngle: -20, // End at right (100 position) - creates top semi-circle
26
+ min: min,
27
+ max: max,
28
+ splitNumber: 6, // 10 major divisions (0, 10, 20, ..., 100)
29
+ axisLine: {
30
+ lineStyle: {
31
+ width: 50,
32
+ color: [[1, axisBgColor]],
33
+ },
34
+ },
35
+ pointer: {
36
+ show: false,
37
+ },
38
+ axisTick: {
39
+ show: false,
40
+ },
41
+ splitLine: {
42
+ show: false,
43
+ },
44
+ axisLabel: {
45
+ show: false,
46
+ },
47
+ detail: {
48
+ show: false, // Hide the detail text to match the image
49
+ },
50
+ data: [{ value: max }],
51
+ }
52
+ ]
53
+ };
54
+ }, [max, min, axisBgColor, axisWidth]);
55
+ const option = useMemo(() => {
56
+ const pointerColor = chartTheme.axis.r.lineColor || '#1e3a8a'; // Use axis line color for needle
57
+ const whiteColor = '#ffffff';
58
+ const tickColor = tickColorProp || chartTheme.axis.r.tickColor || chartTheme.grid.r.lineColor || '#666666'; // Use prop or theme tick color
59
+ const labelColor = chartTheme.axis.r.tickLabelColor || '#666666'; // Use theme label color
60
+ const backgroundColor = chartTheme.axis.r.tickColor || '#DDDDDD'; // Background color from theme
61
+ // Determine colors to use
62
+ let colors;
63
+ if (axisColors && axisColors.length > 0) {
64
+ colors = axisColors;
65
+ }
66
+ else {
67
+ // Fall back to theme colors
68
+ colors = [
69
+ chartTheme.series[0].color,
70
+ chartTheme.series.length > 1 ? chartTheme.series[1].color : chartTheme.series[0].color
71
+ ];
72
+ }
73
+ // Determine segment endpoints
74
+ let endpoints;
75
+ if (axisColorLengths && axisColorLengths.length > 0) {
76
+ endpoints = axisColorLengths;
77
+ }
78
+ else {
79
+ // Default: divide evenly based on number of colors
80
+ const segmentSize = (max - min) / colors.length;
81
+ endpoints = colors.map((_, index) => min + (index + 1) * segmentSize);
82
+ }
83
+ // Ensure endpoints don't exceed max and match colors length
84
+ const numSegments = Math.min(colors.length, endpoints.length);
85
+ const finalEndpoints = endpoints.slice(0, numSegments).map(endpoint => Math.min(endpoint, max));
86
+ const finalColors = colors.slice(0, numSegments);
87
+ // Build color array for ECharts (as percentage thresholds)
88
+ const axisLineColors = finalEndpoints.map((endpoint, index) => [
89
+ endpoint / max,
90
+ finalColors[index],
91
+ ]);
92
+ // Ensure the last segment goes to 1.0 (100%)
93
+ if (axisLineColors.length > 0 && axisLineColors[axisLineColors.length - 1][0] < 1) {
94
+ axisLineColors[axisLineColors.length - 1][0] = 1;
95
+ }
96
+ return {
97
+ backgroundColor: 'transparent',
98
+ series: [
99
+ {
100
+ type: 'gauge',
101
+ center: ['50%', '50%'], // Positioned lower to show semi-circle with flat base at bottom
102
+ radius: '90%',
103
+ startAngle: 200, // Start from left (0 position)
104
+ endAngle: -20, // End at right (100 position) - creates top semi-circle
105
+ min: min,
106
+ max: max,
107
+ splitNumber: (max - min) / 10, // 10 major divisions (0, 10, 20, ..., 100)
108
+ axisLine: {
109
+ lineStyle: {
110
+ width: axisWidth,
111
+ color: axisLineColors,
112
+ },
113
+ },
114
+ pointer: {
115
+ itemStyle: {
116
+ color: pointerColor,
117
+ },
118
+ length: '70%',
119
+ width: 4,
120
+ icon: 'path://M-2,0 L0,-50 L2,0 A1,1 0 0,1 -2,0 Z', // Triangular pointer
121
+ },
122
+ axisTick: {
123
+ distance: 0,
124
+ length: 0,
125
+ splitNumber: 20, // More tick marks for finer divisions
126
+ lineStyle: {
127
+ color: tickColor,
128
+ width: 1,
129
+ },
130
+ },
131
+ splitLine: {
132
+ distance: 0,
133
+ length: 10,
134
+ lineStyle: {
135
+ color: tickColor,
136
+ width: 1.5,
137
+ },
138
+ show: function (value) {
139
+ return value % 20 === 0;
140
+ },
141
+ },
142
+ axisLabel: {
143
+ show: true,
144
+ distance: axisWidth,
145
+ color: labelColor,
146
+ fontSize: 12,
147
+ fontWeight: 'bold',
148
+ formatter: function (value) {
149
+ return value % 20 === 0 ? value.toString() : '';
150
+ },
151
+ },
152
+ anchor: {
153
+ show: true,
154
+ showAbove: true,
155
+ size: 14,
156
+ itemStyle: {
157
+ color: whiteColor,
158
+ borderColor: pointerColor,
159
+ borderWidth: 2,
160
+ shadowBlur: 4,
161
+ shadowColor: 'rgba(0, 0, 0, 0.2)',
162
+ },
163
+ },
164
+ detail: {
165
+ show: false, // Hide the detail text to match the image
166
+ },
167
+ data: [{ value: value }],
168
+ }
169
+ ],
170
+ };
171
+ }, [chartTheme, value, min, max, axisColors, axisColorLengths, axisWidth, tickColorProp]);
172
+ useEffect(() => {
173
+ let chart;
174
+ if (chartRef.current) {
175
+ try {
176
+ chart = echarts.init(chartRef.current, 'light', {
177
+ width: width,
178
+ height: height,
179
+ });
180
+ chart.setOption(option);
181
+ }
182
+ catch (error) {
183
+ console.warn('Chart initialization error:', error);
184
+ }
185
+ }
186
+ return () => {
187
+ if (chart) {
188
+ try {
189
+ chart.dispose();
190
+ }
191
+ catch (error) {
192
+ console.warn('Chart disposal error:', error);
193
+ }
194
+ }
195
+ };
196
+ }, [option, width, height]);
197
+ useEffect(() => {
198
+ let chart;
199
+ if (axisRef.current) {
200
+ chart = echarts.init(axisRef.current, 'light', {
201
+ width: width,
202
+ height: height,
203
+ });
204
+ }
205
+ chart.setOption(axisOption);
206
+ return () => {
207
+ if (chart) {
208
+ chart.dispose();
209
+ }
210
+ };
211
+ }, [axisOption, width, height]);
212
+ return (<View style={[styles.container, { width, height }]}>
213
+ {/* Blue transparent background overlay */}
214
+ <View style={styles.chartContainer}>
215
+ <SkiaChart ref={axisRef}/>
216
+ <View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, width: '100%', height: '100%' }}>
217
+ <SkiaChart ref={chartRef}/>
218
+ </View>
219
+ </View>
220
+ </View>);
221
+ };
222
+ const styles = StyleSheet.create({
223
+ container: {
224
+ position: 'relative',
225
+ justifyContent: 'center',
226
+ alignItems: 'center',
227
+ },
228
+ chartContainer: {
229
+ position: 'absolute',
230
+ top: 0,
231
+ left: 0,
232
+ right: 0,
233
+ bottom: 0,
234
+ justifyContent: 'center',
235
+ alignItems: 'center',
236
+ backgroundColor: 'transparent',
237
+ },
238
+ });
239
+ export const SpeedometerGauge = Object.assign(withResponsiveContainer(withChartTheme(ChartComponent), 'value'), {
240
+ displayName: 'SpeedometerGauge',
241
+ });
@@ -0,0 +1,15 @@
1
+ import type { GeoChartProps } from './geo-chart.props';
2
+ import React from 'react';
3
+ import type { GeoJSONMap } from './geo-chart.props';
4
+ export type { GeoChartProps, GeoChartSelectEvent, GeoDataItem, GeoJSONMap } from './geo-chart.props';
5
+ /** Optional context to provide mapJson without passing as prop (avoids Storybook serialization issues). */
6
+ export declare const GeoMapJsonContext: React.Context<GeoJSONMap | null>;
7
+ export declare const GeoChart: ((props: GeoChartProps & {
8
+ theme?: Partial<import("..").ChartTheme>;
9
+ } & {
10
+ width?: number | string;
11
+ height?: number | string;
12
+ }) => React.JSX.Element) & {
13
+ displayName: string;
14
+ };
15
+ //# sourceMappingURL=geo-chart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geo-chart.d.ts","sourceRoot":"","sources":["../../../../components/chart/geo/geo-chart.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAoC,MAAM,mBAAmB,CAAC;AAKzF,OAAO,KAAgE,MAAM,OAAO,CAAC;AAErF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAErG,2GAA2G;AAC3G,eAAO,MAAM,iBAAiB,kCAAyC,CAAC;AAuNxE,eAAO,MAAM,QAAQ;;;;;;;CAEnB,CAAC"}
@@ -0,0 +1,200 @@
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 { MapChart } from 'echarts/charts';
5
+ import { TooltipComponent, VisualMapComponent } from 'echarts/components';
6
+ import * as echarts from 'echarts/core';
7
+ import React, { createContext, useContext, useEffect, useMemo, useRef } from 'react';
8
+ import worldJson from '../../../data/world.json';
9
+ /** Optional context to provide mapJson without passing as prop (avoids Storybook serialization issues). */
10
+ export const GeoMapJsonContext = createContext(null);
11
+ echarts.use([
12
+ TooltipComponent,
13
+ VisualMapComponent,
14
+ SkiaRenderer,
15
+ MapChart,
16
+ ]);
17
+ const DEFAULT_MAP_NAME = 'world';
18
+ const ChartComponent = ({ data, mapJson: mapJsonProp, mapName = DEFAULT_MAP_NAME, width = 400, height = 300, showLegend = true, showHighlighter = true, tooltipFormatter, visualMapMin, visualMapMax, visualMapMode = 'continuous', piecewisePieces, onSelect, ...props }) => {
19
+ const { theme } = useChartTheme(props.theme, props.colors);
20
+ const chartRef = useRef(null);
21
+ const onSelectRef = useRef(onSelect);
22
+ onSelectRef.current = onSelect;
23
+ const contextMapJson = useContext(GeoMapJsonContext);
24
+ const mapJson = mapJsonProp ?? contextMapJson ?? worldJson;
25
+ useEffect(() => {
26
+ if (mapJson?.type === 'FeatureCollection' && mapJson.features?.length) {
27
+ try {
28
+ echarts.registerMap(mapName, mapJson);
29
+ }
30
+ catch (e) {
31
+ console.warn('GeoChart: registerMap failed', e);
32
+ }
33
+ }
34
+ }, [mapName, mapJson]);
35
+ const valueRange = useMemo(() => {
36
+ if (!Array.isArray(data) || data.length === 0)
37
+ return { min: 0, max: 100 };
38
+ const values = data.map((d) => d.value).filter((v) => typeof v === 'number');
39
+ if (values.length === 0)
40
+ return { min: 0, max: 100 };
41
+ const min = visualMapMin ?? Math.min(...values);
42
+ const max = visualMapMax ?? Math.max(...values);
43
+ return { min: min === max ? min - 1 : min, max: min === max ? max + 1 : max };
44
+ }, [data, visualMapMin, visualMapMax]);
45
+ const option = useMemo(() => {
46
+ if (!Array.isArray(data) || data.length === 0) {
47
+ return { series: [{ type: 'map', map: mapName, data: [] }] };
48
+ }
49
+ const seriesColor = theme.series[0]?.color ?? '#3b82f6';
50
+ const tooltipConfig = {
51
+ trigger: 'item',
52
+ formatter: tooltipFormatter
53
+ ? (params) => {
54
+ const p = params?.data ?? params;
55
+ return tooltipFormatter({ name: p?.name ?? '', value: p?.value ?? 0 });
56
+ }
57
+ : undefined,
58
+ backgroundColor: theme.tooltip.backgroundColor,
59
+ borderColor: theme.tooltip.borderColor,
60
+ borderWidth: theme.tooltip.borderWidth,
61
+ borderRadius: theme.tooltip.borderRadius,
62
+ padding: theme.tooltip.padding,
63
+ };
64
+ const visualMapConfig = showLegend
65
+ ? visualMapMode === 'piecewise' && piecewisePieces?.length
66
+ ? {
67
+ type: 'piecewise',
68
+ min: valueRange.min,
69
+ max: valueRange.max,
70
+ pieces: piecewisePieces.map((p) => ({
71
+ min: p.min,
72
+ max: p.max,
73
+ label: p.label,
74
+ color: p.color,
75
+ })),
76
+ textStyle: {
77
+ color: theme.legend.textColor,
78
+ fontSize: theme.legend.fontSize,
79
+ },
80
+ right: 10,
81
+ bottom: 20,
82
+ orient: 'vertical',
83
+ }
84
+ : {
85
+ type: 'continuous',
86
+ min: valueRange.min,
87
+ max: valueRange.max,
88
+ text: [String(valueRange.max), String(valueRange.min)],
89
+ realtime: false,
90
+ calculable: true,
91
+ inRange: {
92
+ color: [theme.series[1]?.color ?? '#93c5fd', seriesColor],
93
+ },
94
+ textStyle: {
95
+ color: theme.legend.textColor,
96
+ fontSize: theme.legend.fontSize,
97
+ },
98
+ right: 10,
99
+ bottom: 20,
100
+ orient: 'vertical',
101
+ }
102
+ : undefined;
103
+ const seriesConfig = {
104
+ type: 'map',
105
+ map: mapName,
106
+ roam: true,
107
+ data: data,
108
+ itemStyle: {
109
+ ...(visualMapConfig ? {} : { areaColor: '#f0f0f0' }),
110
+ borderColor: '#ccc',
111
+ borderWidth: 1,
112
+ },
113
+ emphasis: showHighlighter
114
+ ? {
115
+ itemStyle: {
116
+ areaColor: seriesColor,
117
+ borderColor: '#333',
118
+ borderWidth: 1,
119
+ },
120
+ label: {
121
+ show: true,
122
+ color: theme.legend.textColor,
123
+ },
124
+ }
125
+ : { disabled: true },
126
+ };
127
+ const config = {
128
+ tooltip: tooltipConfig,
129
+ series: [seriesConfig],
130
+ };
131
+ if (visualMapConfig)
132
+ config.visualMap = visualMapConfig;
133
+ return config;
134
+ }, [
135
+ data,
136
+ mapName,
137
+ theme,
138
+ valueRange,
139
+ showLegend,
140
+ showHighlighter,
141
+ tooltipFormatter,
142
+ visualMapMode,
143
+ piecewisePieces,
144
+ ]);
145
+ useEffect(() => {
146
+ let chart;
147
+ if (chartRef.current) {
148
+ try {
149
+ chart = echarts.init(chartRef.current, 'light', {
150
+ width,
151
+ height,
152
+ });
153
+ chart.setOption(option);
154
+ const handleMapClick = (params) => {
155
+ const cb = onSelectRef.current;
156
+ if (typeof cb !== 'function')
157
+ return;
158
+ if (params.componentType !== 'series')
159
+ return;
160
+ if (params.seriesType !== 'map')
161
+ return;
162
+ const rawName = params.name ?? params.data?.name;
163
+ if (rawName == null || rawName === '')
164
+ return;
165
+ let value = 0;
166
+ if (params.data && typeof params.data === 'object' && 'value' in params.data) {
167
+ value = Number(params.data.value);
168
+ }
169
+ else if (typeof params.value === 'number') {
170
+ value = params.value;
171
+ }
172
+ const event = {
173
+ name: String(rawName),
174
+ value,
175
+ };
176
+ cb(event);
177
+ };
178
+ chart.on('click', handleMapClick);
179
+ }
180
+ catch (error) {
181
+ console.warn('GeoChart initialization error:', error);
182
+ }
183
+ }
184
+ return () => {
185
+ if (chart) {
186
+ try {
187
+ chart.dispose();
188
+ }
189
+ catch (error) {
190
+ console.warn('GeoChart disposal error:', error);
191
+ }
192
+ }
193
+ };
194
+ }, [option, width, height]);
195
+ return <SkiaChart ref={chartRef} useRNGH/>;
196
+ };
197
+ const GeoChartComponent = withResponsiveContainer(withChartTheme(ChartComponent));
198
+ export const GeoChart = Object.assign(GeoChartComponent, {
199
+ displayName: 'GeoChart',
200
+ });