@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,213 @@
|
|
|
1
|
+
import { withResponsiveContainer } from '../../chart-container';
|
|
2
|
+
import { useChartTheme, withChartTheme } from '../../chart-theme.context';
|
|
3
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
4
|
+
import { useWindowDimensions } from 'react-native';
|
|
5
|
+
import Svg, { Defs, G, LinearGradient, Path, Stop, Text as SvgText } from 'react-native-svg';
|
|
6
|
+
// SVG Gauge Component
|
|
7
|
+
const SVGGaugeChart = ({ value, max = 100, showInnerArc = false, width, height, animationDuration = 1000, colors, axisBgColor: axisBgColorProp, axisWidth: axisWidthProp, tickColor: tickColorProp, }) => {
|
|
8
|
+
const { theme: chartTheme } = useChartTheme(undefined, colors);
|
|
9
|
+
// Use theme colors; allow overrides from props
|
|
10
|
+
const gradientColors = chartTheme.series.map(item => item.color);
|
|
11
|
+
const inactiveTickColor = tickColorProp ?? axisBgColorProp ?? chartTheme.axis.r.tickColor;
|
|
12
|
+
const labelColor = chartTheme.axis.r.tickLabelColor;
|
|
13
|
+
const innerTickColor = chartTheme.axis.r.lineColor;
|
|
14
|
+
const arcColor = axisBgColorProp ?? chartTheme.grid.r.lineColor;
|
|
15
|
+
const dimensions = useWindowDimensions();
|
|
16
|
+
const [animatedValue, setAnimatedValue] = useState(value);
|
|
17
|
+
const currentValueRef = useRef(value); // Stores the current animated value synchronously
|
|
18
|
+
const animationFrameRef = useRef(undefined);
|
|
19
|
+
const startTimeRef = useRef(undefined);
|
|
20
|
+
// Animation effect when value changes
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
// Start from the current animated position (stored in ref)
|
|
23
|
+
const startValue = currentValueRef.current;
|
|
24
|
+
const endValue = value;
|
|
25
|
+
const startTime = new Date().getTime();
|
|
26
|
+
startTimeRef.current = startTime;
|
|
27
|
+
const animate = () => {
|
|
28
|
+
const currentTime = new Date().getTime();
|
|
29
|
+
const elapsed = currentTime - startTime;
|
|
30
|
+
const progress = Math.min(elapsed / animationDuration, 1);
|
|
31
|
+
// Easing function (easeOutCubic for smooth deceleration)
|
|
32
|
+
const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
|
|
33
|
+
const easedProgress = easeOutCubic(progress);
|
|
34
|
+
const currentValue = startValue + (endValue - startValue) * easedProgress;
|
|
35
|
+
// Update both state and ref synchronously
|
|
36
|
+
currentValueRef.current = currentValue;
|
|
37
|
+
setAnimatedValue(currentValue);
|
|
38
|
+
if (progress < 1) {
|
|
39
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
// Cancel any ongoing animation
|
|
43
|
+
if (animationFrameRef.current) {
|
|
44
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
45
|
+
}
|
|
46
|
+
animationFrameRef.current = requestAnimationFrame(animate);
|
|
47
|
+
return () => {
|
|
48
|
+
if (animationFrameRef.current) {
|
|
49
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}, [value, animationDuration]);
|
|
53
|
+
// If height not provided, use 90% of available height
|
|
54
|
+
// If width not provided, use the same as height to keep it square
|
|
55
|
+
const calculatedHeight = height ?? dimensions.height * 0.9;
|
|
56
|
+
const calculatedWidth = width ?? calculatedHeight;
|
|
57
|
+
const size = Math.min(calculatedWidth, calculatedHeight);
|
|
58
|
+
const strokeWidth = 12;
|
|
59
|
+
const center = size / 2;
|
|
60
|
+
const radius = (size - strokeWidth) / 2 - 20;
|
|
61
|
+
const tickStrokeWidth = axisWidthProp ?? 3;
|
|
62
|
+
const startAngle = -135;
|
|
63
|
+
const endAngle = 135;
|
|
64
|
+
const totalAngle = endAngle - startAngle;
|
|
65
|
+
const percentage = (animatedValue / max) * 100;
|
|
66
|
+
const valueAngle = (percentage / 100) * totalAngle;
|
|
67
|
+
const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
|
|
68
|
+
const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0;
|
|
69
|
+
return {
|
|
70
|
+
x: centerX + radius * Math.cos(angleInRadians),
|
|
71
|
+
y: centerY + radius * Math.sin(angleInRadians),
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
const describeArc = (centerX, centerY, radius, startAngle, endAngle) => {
|
|
75
|
+
const start = polarToCartesian(centerX, centerY, radius, endAngle);
|
|
76
|
+
const end = polarToCartesian(centerX, centerY, radius, startAngle);
|
|
77
|
+
const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1';
|
|
78
|
+
return `M ${start.x} ${start.y} A ${radius} ${radius} 0 ${largeArcFlag} 0 ${end.x} ${end.y}`;
|
|
79
|
+
};
|
|
80
|
+
// Helper function to interpolate between two colors
|
|
81
|
+
const interpolateColor = (color1, color2, factor) => {
|
|
82
|
+
const hex = (color) => {
|
|
83
|
+
const hex = color.replace('#', '');
|
|
84
|
+
return {
|
|
85
|
+
r: parseInt(hex.substring(0, 2), 16),
|
|
86
|
+
g: parseInt(hex.substring(2, 4), 16),
|
|
87
|
+
b: parseInt(hex.substring(4, 6), 16),
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
const c1 = hex(color1);
|
|
91
|
+
const c2 = hex(color2);
|
|
92
|
+
const r = Math.round(c1.r + factor * (c2.r - c1.r));
|
|
93
|
+
const g = Math.round(c1.g + factor * (c2.g - c1.g));
|
|
94
|
+
const b = Math.round(c1.b + factor * (c2.b - c1.b));
|
|
95
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
96
|
+
};
|
|
97
|
+
// Helper function to get color from gradient array based on position
|
|
98
|
+
const getGradientColor = (factor) => {
|
|
99
|
+
if (gradientColors.length === 0)
|
|
100
|
+
return '#FF6B6B';
|
|
101
|
+
if (gradientColors.length === 1)
|
|
102
|
+
return gradientColors[0];
|
|
103
|
+
// Determine which two colors to interpolate between
|
|
104
|
+
const scaledPosition = factor * (gradientColors.length - 1);
|
|
105
|
+
const lowerIndex = Math.floor(scaledPosition);
|
|
106
|
+
const upperIndex = Math.min(lowerIndex + 1, gradientColors.length - 1);
|
|
107
|
+
const localFactor = scaledPosition - lowerIndex;
|
|
108
|
+
return interpolateColor(gradientColors[lowerIndex], gradientColors[upperIndex], localFactor);
|
|
109
|
+
};
|
|
110
|
+
// Generate tick marks
|
|
111
|
+
const numTicks = 50;
|
|
112
|
+
const ticks = [];
|
|
113
|
+
const activeTickCount = Math.round((valueAngle / totalAngle) * numTicks);
|
|
114
|
+
const outerTickLength = 20;
|
|
115
|
+
for (let i = 0; i <= numTicks; i++) {
|
|
116
|
+
const angle = startAngle + (i / numTicks) * totalAngle;
|
|
117
|
+
const isActive = i <= activeTickCount;
|
|
118
|
+
const isMajor = i % 5 === 0;
|
|
119
|
+
const tickLength = outerTickLength;
|
|
120
|
+
const outerPoint = polarToCartesian(center, center, radius + 10, angle);
|
|
121
|
+
const innerPoint = polarToCartesian(center, center, radius + 10 - tickLength, angle);
|
|
122
|
+
// Calculate gradient factor for active ticks
|
|
123
|
+
let tickColor;
|
|
124
|
+
if (isActive) {
|
|
125
|
+
const gradientFactor = numTicks > 0 ? i / numTicks : 0;
|
|
126
|
+
tickColor = getGradientColor(gradientFactor);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
tickColor = inactiveTickColor;
|
|
130
|
+
}
|
|
131
|
+
ticks.push(<Path key={i} d={`M ${outerPoint.x} ${outerPoint.y} L ${innerPoint.x} ${innerPoint.y}`} stroke={tickColor} strokeWidth={tickStrokeWidth} strokeLinecap="round"/>);
|
|
132
|
+
}
|
|
133
|
+
// Generate scale labels and inner ticks
|
|
134
|
+
const scaleLabels = [];
|
|
135
|
+
const innerTicks = [];
|
|
136
|
+
const majorTickPositions = [0, 20, 40, 60, 80, 100];
|
|
137
|
+
const labelRadius = radius - 40;
|
|
138
|
+
const innerTickLength = 4;
|
|
139
|
+
const minorTickLength = 2;
|
|
140
|
+
const gapFromOuterTicks = 4; // Gap between outer and inner elements
|
|
141
|
+
const innerTickOuterRadius = radius - outerTickLength - gapFromOuterTicks;
|
|
142
|
+
// Add major ticks and labels
|
|
143
|
+
for (const labelValue of majorTickPositions) {
|
|
144
|
+
const labelAngle = startAngle + (labelValue / max) * totalAngle;
|
|
145
|
+
const labelPosition = polarToCartesian(center, center, labelRadius, labelAngle);
|
|
146
|
+
// Add scale label
|
|
147
|
+
scaleLabels.push(<SvgText key={`label-${labelValue}`} x={labelPosition.x} y={labelPosition.y} textAnchor="middle" fontSize="12" fill={labelColor}>
|
|
148
|
+
{labelValue}
|
|
149
|
+
</SvgText>);
|
|
150
|
+
// Add major inner tick mark
|
|
151
|
+
const tickOuterPoint = polarToCartesian(center, center, innerTickOuterRadius, labelAngle);
|
|
152
|
+
const tickInnerPoint = polarToCartesian(center, center, innerTickOuterRadius - innerTickLength, labelAngle);
|
|
153
|
+
innerTicks.push(<Path key={`inner-tick-${labelValue}`} d={`M ${tickOuterPoint.x} ${tickOuterPoint.y} L ${tickInnerPoint.x} ${tickInnerPoint.y}`} stroke={innerTickColor} strokeWidth={2} strokeLinecap="round"/>);
|
|
154
|
+
}
|
|
155
|
+
// Add minor ticks (9 ticks between each major tick)
|
|
156
|
+
// Major ticks are at 0, 20, 40, 60, 80, 100
|
|
157
|
+
// So we need ticks at every 2 units, excluding the major positions
|
|
158
|
+
for (let value = 0; value <= max; value += 2) {
|
|
159
|
+
// Skip if this is a major tick position
|
|
160
|
+
if (majorTickPositions.includes(value))
|
|
161
|
+
continue;
|
|
162
|
+
const tickAngle = startAngle + (value / max) * totalAngle;
|
|
163
|
+
const tickOuterPoint = polarToCartesian(center, center, innerTickOuterRadius, tickAngle);
|
|
164
|
+
const tickInnerPoint = polarToCartesian(center, center, innerTickOuterRadius - minorTickLength, tickAngle);
|
|
165
|
+
innerTicks.push(<Path key={`inner-minor-tick-${value}`} d={`M ${tickOuterPoint.x} ${tickOuterPoint.y} L ${tickInnerPoint.x} ${tickInnerPoint.y}`} stroke={inactiveTickColor} strokeWidth={1} strokeLinecap="round"/>);
|
|
166
|
+
}
|
|
167
|
+
// Calculate the animated end angle for the outer arc
|
|
168
|
+
const animatedEndAngle = startAngle + valueAngle;
|
|
169
|
+
const outerArcRadius = radius + 10;
|
|
170
|
+
return (<Svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
|
|
171
|
+
<Defs>
|
|
172
|
+
<LinearGradient id="gaugeGradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
173
|
+
<Stop offset="0%" stopColor={gradientColors[0]} stopOpacity="1"/>
|
|
174
|
+
<Stop offset="100%" stopColor={gradientColors[gradientColors.length - 1]} stopOpacity="1"/>
|
|
175
|
+
</LinearGradient>
|
|
176
|
+
|
|
177
|
+
{/* Radial gradient for a more dynamic look */}
|
|
178
|
+
<LinearGradient id="outerArcGradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
179
|
+
<Stop offset="0%" stopColor={gradientColors[0]} stopOpacity="0.8"/>
|
|
180
|
+
<Stop offset="100%" stopColor={gradientColors[gradientColors.length - 1]} stopOpacity="0.8"/>
|
|
181
|
+
</LinearGradient>
|
|
182
|
+
</Defs>
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
{/* Tick marks */}
|
|
186
|
+
<G>{ticks}</G>
|
|
187
|
+
|
|
188
|
+
{/* Inner arc connecting the inner ticks (from 0 to 100) */}
|
|
189
|
+
{showInnerArc && (<Path d={describeArc(center, center, innerTickOuterRadius, startAngle, endAngle)} fill="none" stroke={arcColor} strokeWidth={1} opacity={0.5}/>)}
|
|
190
|
+
|
|
191
|
+
{/* Inner scale ticks */}
|
|
192
|
+
<G>{innerTicks}</G>
|
|
193
|
+
|
|
194
|
+
{/* Scale labels */}
|
|
195
|
+
<G>{scaleLabels}</G>
|
|
196
|
+
|
|
197
|
+
{/* Value display */}
|
|
198
|
+
<SvgText x={center} y={center + 10} textAnchor="middle" fontSize="56" fontWeight="bold" fill={chartTheme.axis.r.tickLabelColor}>
|
|
199
|
+
{Math.round(animatedValue)}
|
|
200
|
+
</SvgText>
|
|
201
|
+
|
|
202
|
+
{/* Label */}
|
|
203
|
+
<SvgText x={center} y={center + 30} textAnchor="middle" fontSize="14" fill={labelColor}>
|
|
204
|
+
Value
|
|
205
|
+
</SvgText>
|
|
206
|
+
</Svg>);
|
|
207
|
+
};
|
|
208
|
+
const ChartComponent = ({ value = 46, min = 0, max = 90, width, height, axisBgColor, axisWidth, tickColor, showInnerArc, animationDuration, ...props }) => {
|
|
209
|
+
return (<SVGGaugeChart value={value} max={max} width={width} height={height} colors={props.colors} axisBgColor={axisBgColor} axisWidth={axisWidth} tickColor={tickColor} showInnerArc={showInnerArc} animationDuration={animationDuration}/>);
|
|
210
|
+
};
|
|
211
|
+
export const DigitalGauge = Object.assign(withResponsiveContainer(withChartTheme(ChartComponent), 'value'), {
|
|
212
|
+
displayName: 'DigitalGauge',
|
|
213
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ChartTheme } from '../chart-theme.context';
|
|
2
|
+
/**
|
|
3
|
+
* Common props shared by all gauge chart components.
|
|
4
|
+
* Individual gauge components extend this interface with their specific props.
|
|
5
|
+
*/
|
|
6
|
+
export interface BaseGaugeProps {
|
|
7
|
+
/**
|
|
8
|
+
* Current value to display on the gauge.
|
|
9
|
+
*/
|
|
10
|
+
value: number;
|
|
11
|
+
/**
|
|
12
|
+
* Minimum value of the gauge scale.
|
|
13
|
+
* @default 0
|
|
14
|
+
*/
|
|
15
|
+
min?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Maximum value of the gauge scale.
|
|
18
|
+
* @default 100
|
|
19
|
+
*/
|
|
20
|
+
max?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Width of the chart in pixels.
|
|
23
|
+
*/
|
|
24
|
+
width?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Height of the chart in pixels.
|
|
27
|
+
*/
|
|
28
|
+
height?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Partial theme override for customizing chart appearance.
|
|
31
|
+
*/
|
|
32
|
+
theme?: Partial<ChartTheme>;
|
|
33
|
+
/**
|
|
34
|
+
* Colors for the chart.
|
|
35
|
+
* @default theme.itemStyles.map(item => item.color)
|
|
36
|
+
*/
|
|
37
|
+
colors?: string[];
|
|
38
|
+
/**
|
|
39
|
+
* Background color for the axis/track.
|
|
40
|
+
*/
|
|
41
|
+
axisBgColor?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Width of the axis line in pixels.
|
|
44
|
+
*/
|
|
45
|
+
axisWidth?: number;
|
|
46
|
+
/**
|
|
47
|
+
* Color for tick marks and split lines.
|
|
48
|
+
*/
|
|
49
|
+
tickColor?: string;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=gauge.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gauge.types.d.ts","sourceRoot":"","sources":["../../../../components/chart/gauge/gauge.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAE5B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
|
File without changes
|
package/gauge/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { BaseGaugeProps } from './gauge.types';
|
|
2
|
+
export { SimpleGauge } from './simple/simple.gauge';
|
|
3
|
+
export { DigitalGauge } from './digital/digital.gauge';
|
|
4
|
+
export { SpeedometerGauge } from './speedometer/speedometer.gauge';
|
|
5
|
+
export { RadialGauge } from './radial/radial.gauge';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/chart/gauge/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC"}
|
package/gauge/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { BaseGaugeProps } from '../gauge.types';
|
|
3
|
+
/**
|
|
4
|
+
* Props for the RadialGauge component.
|
|
5
|
+
* A composite gauge with both radial progress bar and traditional gauge display.
|
|
6
|
+
*/
|
|
7
|
+
interface RadialGaugeProps extends BaseGaugeProps {
|
|
8
|
+
}
|
|
9
|
+
export declare const RadialGauge: ((props: RadialGaugeProps & {
|
|
10
|
+
theme?: Partial<import("../..").ChartTheme>;
|
|
11
|
+
} & {
|
|
12
|
+
width?: number | string;
|
|
13
|
+
height?: number | string;
|
|
14
|
+
}) => React.JSX.Element) & {
|
|
15
|
+
displayName: string;
|
|
16
|
+
};
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=radial.gauge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"radial.gauge.d.ts","sourceRoot":"","sources":["../../../../../components/chart/gauge/radial/radial.gauge.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAqC,MAAM,OAAO,CAAC;AAE1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAUrD;;;GAGG;AACH,UAAU,gBAAiB,SAAQ,cAAc;CAAG;AAgSpD,eAAO,MAAM,WAAW;;;;;;;CAEtB,CAAC"}
|
|
@@ -0,0 +1,284 @@
|
|
|
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 } from 'echarts/charts';
|
|
5
|
+
import { GaugeChart } from 'echarts/charts';
|
|
6
|
+
import { PolarComponent } from 'echarts/components';
|
|
7
|
+
import * as echarts from 'echarts/core';
|
|
8
|
+
import React, { useEffect, useMemo, useRef } from 'react';
|
|
9
|
+
import { View, StyleSheet } from 'react-native';
|
|
10
|
+
// Register necessary components for this chart
|
|
11
|
+
echarts.use([
|
|
12
|
+
SkiaRenderer,
|
|
13
|
+
GaugeChart,
|
|
14
|
+
BarChart,
|
|
15
|
+
PolarComponent,
|
|
16
|
+
]);
|
|
17
|
+
const ChartComponent = ({ value, min = 0, max = 100, width = 220, height = 240, axisBgColor: axisBgColorProp, axisWidth: axisWidthProp, tickColor: tickColorProp, ...props }) => {
|
|
18
|
+
const { theme: chartTheme } = useChartTheme(props.theme, props.colors);
|
|
19
|
+
const chartRef = useRef(null);
|
|
20
|
+
const radialChartRef = useRef(null);
|
|
21
|
+
// Radial background chart option (textRadialOption)
|
|
22
|
+
const radialOption = useMemo(() => {
|
|
23
|
+
// Calculate radius as percentage of the smaller dimension with gap
|
|
24
|
+
const minDim = Math.min(width, height);
|
|
25
|
+
const innerRadius = minDim * 0.30; // ~77px at 240px - inner edge of radial ring
|
|
26
|
+
const outerRadius = minDim * 0.48; // ~115px at 240px - outer edge of radial ring
|
|
27
|
+
const backgroundColor = axisBgColorProp ?? chartTheme.axis.r.tickColor;
|
|
28
|
+
const progressColor = chartTheme.series[0].color;
|
|
29
|
+
return {
|
|
30
|
+
polar: {
|
|
31
|
+
center: ['50%', '50%'],
|
|
32
|
+
radius: [innerRadius, outerRadius],
|
|
33
|
+
},
|
|
34
|
+
angleAxis: {
|
|
35
|
+
max: max,
|
|
36
|
+
startAngle: 225,
|
|
37
|
+
endAngle: -45,
|
|
38
|
+
clockwise: true,
|
|
39
|
+
show: false
|
|
40
|
+
},
|
|
41
|
+
radiusAxis: {
|
|
42
|
+
type: 'category',
|
|
43
|
+
data: ['Data'],
|
|
44
|
+
show: false
|
|
45
|
+
},
|
|
46
|
+
series: [
|
|
47
|
+
{
|
|
48
|
+
type: 'bar',
|
|
49
|
+
data: [max],
|
|
50
|
+
coordinateSystem: 'polar',
|
|
51
|
+
name: 'Background',
|
|
52
|
+
tooltip: {
|
|
53
|
+
show: false,
|
|
54
|
+
},
|
|
55
|
+
itemStyle: {
|
|
56
|
+
color: backgroundColor,
|
|
57
|
+
borderRadius: [50, 50],
|
|
58
|
+
},
|
|
59
|
+
animation: false,
|
|
60
|
+
barWidth: '50%',
|
|
61
|
+
emphasis: {
|
|
62
|
+
disabled: true,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
type: 'bar',
|
|
67
|
+
data: [value],
|
|
68
|
+
coordinateSystem: 'polar',
|
|
69
|
+
name: 'Value',
|
|
70
|
+
itemStyle: {
|
|
71
|
+
color: progressColor,
|
|
72
|
+
borderRadius: [50, 50],
|
|
73
|
+
},
|
|
74
|
+
barWidth: '50%',
|
|
75
|
+
barGap: '-100%',
|
|
76
|
+
emphasis: {
|
|
77
|
+
disabled: true,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
]
|
|
81
|
+
};
|
|
82
|
+
}, [chartTheme, value, max, width, height, axisBgColorProp]);
|
|
83
|
+
// Gauge option (progressArcOption)
|
|
84
|
+
const gaugeOption = useMemo(() => {
|
|
85
|
+
// Gauge radius smaller than radial inner ring to create a gap
|
|
86
|
+
const minDim = Math.min(width, height);
|
|
87
|
+
const gaugeRadius = minDim * 0.32; // ~65px at 240px - creates gap with radial inner ring (77px)
|
|
88
|
+
const axisLineColor = axisBgColorProp ?? chartTheme.axis.r.tickColor;
|
|
89
|
+
const pointerColor = chartTheme.axis.r.lineColor;
|
|
90
|
+
const tickColor = tickColorProp ?? chartTheme.axis.r.tickColor;
|
|
91
|
+
const splitLineColor = tickColorProp ?? chartTheme.axis.r.tickColor;
|
|
92
|
+
const labelColor = chartTheme.axis.r.tickLabelColor;
|
|
93
|
+
const anchorColor = chartTheme.axis.r.lineColor;
|
|
94
|
+
const anchorBorderColor = tickColorProp ?? chartTheme.axis.r.tickColor;
|
|
95
|
+
const valueColor = chartTheme.axis.r.tickLabelColor;
|
|
96
|
+
const unitColor = chartTheme.axis.r.tickLabelColor;
|
|
97
|
+
return {
|
|
98
|
+
series: [
|
|
99
|
+
{
|
|
100
|
+
type: 'gauge',
|
|
101
|
+
center: ['50%', '50%'],
|
|
102
|
+
radius: gaugeRadius,
|
|
103
|
+
startAngle: 225,
|
|
104
|
+
endAngle: -45,
|
|
105
|
+
min: min,
|
|
106
|
+
max: max,
|
|
107
|
+
splitNumber: 5,
|
|
108
|
+
axisLine: {
|
|
109
|
+
show: false,
|
|
110
|
+
distance: 1,
|
|
111
|
+
length: 8,
|
|
112
|
+
lineStyle: {
|
|
113
|
+
width: axisWidthProp ?? 1,
|
|
114
|
+
color: [[1, axisLineColor]],
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
pointer: {
|
|
118
|
+
length: '80%',
|
|
119
|
+
width: 8,
|
|
120
|
+
icon: 'path://M-4,0 L-2,-50 A1, 1 0 0 1 2, -50 L4,0 A1, 1 0 0 1 -4, 0 Z',
|
|
121
|
+
itemStyle: {
|
|
122
|
+
color: pointerColor,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
axisTick: {
|
|
126
|
+
distance: 1,
|
|
127
|
+
length: 4,
|
|
128
|
+
splitNumber: 16,
|
|
129
|
+
lineStyle: {
|
|
130
|
+
color: tickColor,
|
|
131
|
+
width: 0.5,
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
splitLine: {
|
|
135
|
+
distance: 1,
|
|
136
|
+
length: 8,
|
|
137
|
+
lineStyle: {
|
|
138
|
+
color: splitLineColor,
|
|
139
|
+
width: 1,
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
axisLabel: {
|
|
143
|
+
show: true,
|
|
144
|
+
distance: 16,
|
|
145
|
+
fontSize: 12,
|
|
146
|
+
color: labelColor,
|
|
147
|
+
formatter: function (value) {
|
|
148
|
+
if (value === 20 || value === 80) {
|
|
149
|
+
return value.toString();
|
|
150
|
+
}
|
|
151
|
+
return '';
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
anchor: {
|
|
155
|
+
show: true,
|
|
156
|
+
showAbove: true,
|
|
157
|
+
size: 8,
|
|
158
|
+
itemStyle: {
|
|
159
|
+
color: anchorColor,
|
|
160
|
+
borderColor: anchorBorderColor,
|
|
161
|
+
borderWidth: 3,
|
|
162
|
+
shadowBlur: 3,
|
|
163
|
+
shadowColor: 'rgba(0,0,0,0.15)',
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
detail: {
|
|
167
|
+
valueAnimation: true,
|
|
168
|
+
formatter: function (value) {
|
|
169
|
+
return '{value|' + Math.round(value) + '}{unit|/100}';
|
|
170
|
+
},
|
|
171
|
+
offsetCenter: [0, '70%'],
|
|
172
|
+
rich: {
|
|
173
|
+
value: {
|
|
174
|
+
fontSize: 40,
|
|
175
|
+
fontWeight: 'bold',
|
|
176
|
+
color: valueColor,
|
|
177
|
+
},
|
|
178
|
+
unit: {
|
|
179
|
+
verticalAlign: 'bottom',
|
|
180
|
+
fontSize: 16,
|
|
181
|
+
color: unitColor,
|
|
182
|
+
fontWeight: 'normal',
|
|
183
|
+
},
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
data: [{ value: value }],
|
|
187
|
+
}
|
|
188
|
+
],
|
|
189
|
+
style: {
|
|
190
|
+
backgroundColor: 'transparent',
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}, [chartTheme, value, min, max, width, height, axisBgColorProp, axisWidthProp, tickColorProp]);
|
|
194
|
+
// Initialize radial background chart
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
let chart;
|
|
197
|
+
if (radialChartRef.current) {
|
|
198
|
+
try {
|
|
199
|
+
chart = echarts.init(radialChartRef.current, 'light', {
|
|
200
|
+
width: width,
|
|
201
|
+
height: height,
|
|
202
|
+
});
|
|
203
|
+
chart.setOption(radialOption);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
console.warn('Radial chart initialization error:', error);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return () => {
|
|
210
|
+
if (chart) {
|
|
211
|
+
try {
|
|
212
|
+
chart.dispose();
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
console.warn('Radial chart disposal error:', error);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
}, [radialOption, width, height]);
|
|
220
|
+
// Initialize gauge chart (same size as radial chart)
|
|
221
|
+
useEffect(() => {
|
|
222
|
+
let chart;
|
|
223
|
+
if (chartRef.current) {
|
|
224
|
+
try {
|
|
225
|
+
chart = echarts.init(chartRef.current, 'light', {
|
|
226
|
+
width: width,
|
|
227
|
+
height: height,
|
|
228
|
+
});
|
|
229
|
+
chart.setOption(gaugeOption);
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
console.warn('Gauge chart initialization error:', error);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return () => {
|
|
236
|
+
if (chart) {
|
|
237
|
+
try {
|
|
238
|
+
chart.dispose();
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
console.warn('Gauge chart disposal error:', error);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
}, [gaugeOption, width, height]);
|
|
246
|
+
return (<View style={[styles.container, { width, height }]}>
|
|
247
|
+
<View style={styles.radialChart}>
|
|
248
|
+
<SkiaChart ref={radialChartRef}/>
|
|
249
|
+
</View>
|
|
250
|
+
<View style={styles.gaugeChart}>
|
|
251
|
+
<SkiaChart ref={chartRef}/>
|
|
252
|
+
</View>
|
|
253
|
+
</View>);
|
|
254
|
+
};
|
|
255
|
+
const styles = StyleSheet.create({
|
|
256
|
+
container: {
|
|
257
|
+
position: 'relative',
|
|
258
|
+
justifyContent: 'center',
|
|
259
|
+
alignItems: 'center',
|
|
260
|
+
},
|
|
261
|
+
radialChart: {
|
|
262
|
+
position: 'absolute',
|
|
263
|
+
top: 0,
|
|
264
|
+
left: 0,
|
|
265
|
+
right: 0,
|
|
266
|
+
bottom: 0,
|
|
267
|
+
justifyContent: 'center',
|
|
268
|
+
alignItems: 'center',
|
|
269
|
+
backgroundColor: 'transparent',
|
|
270
|
+
},
|
|
271
|
+
gaugeChart: {
|
|
272
|
+
position: 'absolute',
|
|
273
|
+
top: 0,
|
|
274
|
+
left: 0,
|
|
275
|
+
right: 0,
|
|
276
|
+
bottom: 0,
|
|
277
|
+
justifyContent: 'center',
|
|
278
|
+
alignItems: 'center',
|
|
279
|
+
backgroundColor: 'transparent',
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
export const RadialGauge = Object.assign(withResponsiveContainer(withChartTheme(ChartComponent), 'value'), {
|
|
283
|
+
displayName: 'RadialGauge',
|
|
284
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { BaseGaugeProps } from '../gauge.types';
|
|
3
|
+
/**
|
|
4
|
+
* Props for the SimpleGauge component.
|
|
5
|
+
* A gauge chart with customizable title and detail text labels.
|
|
6
|
+
*/
|
|
7
|
+
interface SimpleGaugeProps extends BaseGaugeProps {
|
|
8
|
+
/**
|
|
9
|
+
* Title text displayed on the gauge.
|
|
10
|
+
* @default 'Metric'
|
|
11
|
+
*/
|
|
12
|
+
title?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Detail text showing the value.
|
|
15
|
+
* @default '30%'
|
|
16
|
+
*/
|
|
17
|
+
detailText?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare const SimpleGauge: ((props: SimpleGaugeProps & {
|
|
20
|
+
theme?: Partial<import("../..").ChartTheme>;
|
|
21
|
+
} & {
|
|
22
|
+
width?: number | string;
|
|
23
|
+
height?: number | string;
|
|
24
|
+
}) => React.JSX.Element) & {
|
|
25
|
+
displayName: string;
|
|
26
|
+
};
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=simple.gauge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simple.gauge.d.ts","sourceRoot":"","sources":["../../../../../components/chart/gauge/simple/simple.gauge.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAqC,MAAM,OAAO,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAQrD;;;GAGG;AACH,UAAU,gBAAiB,SAAQ,cAAc;IAC/C;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA2GD,eAAO,MAAM,WAAW;;;;;;;CAEtB,CAAC"}
|