@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.
- package/.npmignore +4 -0
- package/LICENSE +21 -0
- package/README.md +39 -0
- package/area/area-chart.d.ts +12 -0
- package/area/area-chart.d.ts.map +1 -0
- package/area/area-chart.js +404 -0
- package/area/area-chart.props.d.ts +64 -0
- package/area/area-chart.props.d.ts.map +1 -0
- package/area/area-chart.props.js +0 -0
- package/area/index.d.ts +3 -0
- package/area/index.d.ts.map +1 -0
- package/area/index.js +1 -0
- package/axis.d.ts +3 -0
- package/axis.d.ts.map +1 -0
- package/axis.js +16 -0
- package/bar/bar-chart.d.ts +11 -0
- package/bar/bar-chart.d.ts.map +1 -0
- package/bar/bar-chart.js +6 -0
- package/bar/bar-chart.props.d.ts +7 -0
- package/bar/bar-chart.props.d.ts.map +1 -0
- package/bar/bar-chart.props.js +0 -0
- package/bar/index.d.ts +3 -0
- package/bar/index.d.ts.map +1 -0
- package/bar/index.js +1 -0
- package/bubble/bubble-chart.d.ts +13 -0
- package/bubble/bubble-chart.d.ts.map +1 -0
- package/bubble/bubble-chart.js +305 -0
- package/bubble/bubble-chart.props.d.ts +26 -0
- package/bubble/bubble-chart.props.d.ts.map +1 -0
- package/bubble/bubble-chart.props.js +0 -0
- package/bubble/index.d.ts +3 -0
- package/bubble/index.d.ts.map +1 -0
- package/bubble/index.js +1 -0
- package/candlestick/candlestick-chart.d.ts +12 -0
- package/candlestick/candlestick-chart.d.ts.map +1 -0
- package/candlestick/candlestick-chart.js +292 -0
- package/candlestick/candlestick-chart.props.d.ts +51 -0
- package/candlestick/candlestick-chart.props.d.ts.map +1 -0
- package/candlestick/candlestick-chart.props.js +0 -0
- package/candlestick/index.d.ts +3 -0
- package/candlestick/index.d.ts.map +1 -0
- package/candlestick/index.js +1 -0
- package/chart-container.d.ts +6 -0
- package/chart-container.d.ts.map +1 -0
- package/chart-container.js +63 -0
- package/chart-theme.context.d.ts +191 -0
- package/chart-theme.context.d.ts.map +1 -0
- package/chart-theme.context.js +276 -0
- package/column/column-chart.d.ts +13 -0
- package/column/column-chart.d.ts.map +1 -0
- package/column/column-chart.js +481 -0
- package/column/column-chart.props.d.ts +83 -0
- package/column/column-chart.props.d.ts.map +1 -0
- package/column/column-chart.props.js +0 -0
- package/column/index.d.ts +3 -0
- package/column/index.d.ts.map +1 -0
- package/column/index.js +1 -0
- package/gauge/digital/digital.gauge.d.ts +28 -0
- package/gauge/digital/digital.gauge.d.ts.map +1 -0
- package/gauge/digital/digital.gauge.js +213 -0
- package/gauge/gauge.types.d.ts +51 -0
- package/gauge/gauge.types.d.ts.map +1 -0
- package/gauge/gauge.types.js +0 -0
- package/gauge/index.d.ts +6 -0
- package/gauge/index.d.ts.map +1 -0
- package/gauge/index.js +4 -0
- package/gauge/radial/radial.gauge.d.ts +18 -0
- package/gauge/radial/radial.gauge.d.ts.map +1 -0
- package/gauge/radial/radial.gauge.js +284 -0
- package/gauge/simple/simple.gauge.d.ts +28 -0
- package/gauge/simple/simple.gauge.d.ts.map +1 -0
- package/gauge/simple/simple.gauge.js +102 -0
- package/gauge/speedometer/speedometer.gauge.d.ts +35 -0
- package/gauge/speedometer/speedometer.gauge.d.ts.map +1 -0
- package/gauge/speedometer/speedometer.gauge.js +241 -0
- package/geo/geo-chart.d.ts +15 -0
- package/geo/geo-chart.d.ts.map +1 -0
- package/geo/geo-chart.js +200 -0
- package/geo/geo-chart.props.d.ts +96 -0
- package/geo/geo-chart.props.d.ts.map +1 -0
- package/geo/geo-chart.props.js +0 -0
- package/geo/index.d.ts +7 -0
- package/geo/index.d.ts.map +1 -0
- package/geo/index.js +3 -0
- package/geo/us-chart.d.ts +15 -0
- package/geo/us-chart.d.ts.map +1 -0
- package/geo/us-chart.js +15 -0
- package/geo/world-chart.d.ts +15 -0
- package/geo/world-chart.d.ts.map +1 -0
- package/geo/world-chart.js +10 -0
- package/index.d.ts +17 -0
- package/index.d.ts.map +1 -0
- package/index.js +15 -0
- package/line/index.d.ts +3 -0
- package/line/index.d.ts.map +1 -0
- package/line/index.js +1 -0
- package/line/line-chart.d.ts +9 -0
- package/line/line-chart.d.ts.map +1 -0
- package/line/line-chart.js +8 -0
- package/line/line-chart.props.d.ts +12 -0
- package/line/line-chart.props.d.ts.map +1 -0
- package/line/line-chart.props.js +0 -0
- package/package.json +39 -0
- package/pie/index.d.ts +4 -0
- package/pie/index.d.ts.map +1 -0
- package/pie/index.js +2 -0
- package/pie/pie-chart.d.ts +13 -0
- package/pie/pie-chart.d.ts.map +1 -0
- package/pie/pie-chart.js +222 -0
- package/pie/pie-chart.props.d.ts +97 -0
- package/pie/pie-chart.props.d.ts.map +1 -0
- package/pie/pie-chart.props.js +12 -0
- package/props/cartesian.d.ts +120 -0
- package/props/cartesian.d.ts.map +1 -0
- package/props/cartesian.js +0 -0
- package/props/common.d.ts +28 -0
- package/props/common.d.ts.map +1 -0
- package/props/common.js +0 -0
- package/radar/index.d.ts +3 -0
- package/radar/index.d.ts.map +1 -0
- package/radar/index.js +1 -0
- package/radar/radar-chart.d.ts +12 -0
- package/radar/radar-chart.d.ts.map +1 -0
- package/radar/radar-chart.js +197 -0
- package/radar/radar-chart.props.d.ts +80 -0
- package/radar/radar-chart.props.d.ts.map +1 -0
- package/radar/radar-chart.props.js +0 -0
- package/radial/index.d.ts +3 -0
- package/radial/index.d.ts.map +1 -0
- package/radial/index.js +1 -0
- package/radial/radial-chart.d.ts +12 -0
- package/radial/radial-chart.d.ts.map +1 -0
- package/radial/radial-chart.js +235 -0
- package/radial/radial-chart.props.d.ts +74 -0
- package/radial/radial-chart.props.d.ts.map +1 -0
- package/radial/radial-chart.props.js +0 -0
- package/scatter/index.d.ts +3 -0
- package/scatter/index.d.ts.map +1 -0
- package/scatter/index.js +1 -0
- package/scatter/scatter-chart.d.ts +13 -0
- package/scatter/scatter-chart.d.ts.map +1 -0
- package/scatter/scatter-chart.js +310 -0
- package/scatter/scatter-chart.props.d.ts +36 -0
- package/scatter/scatter-chart.props.d.ts.map +1 -0
- 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"}
|
package/geo/geo-chart.js
ADDED
|
@@ -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
|
+
});
|