react-native-metrify 0.1.0-alpha.1 → 0.1.0-alpha.2
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/package.json +1 -2
- package/src/core/animation/index.ts +0 -113
- package/src/core/animation/index.web.ts +0 -112
- package/src/core/hooks/index.ts +0 -66
- package/src/core/index.ts +0 -26
- package/src/core/layout/index.ts +0 -101
- package/src/core/math/index.ts +0 -72
- package/src/core/package.json +0 -13
- package/src/core/theme/ThemeProvider.tsx +0 -36
- package/src/core/theme/index.ts +0 -5
- package/src/core/theme/themes.ts +0 -132
- package/src/core/types/index.ts +0 -164
- package/src/core/utils/responsive.ts +0 -203
- package/src/core/utils/time.ts +0 -100
- package/src/index.ts +0 -13
- package/src/renderer-svg/adapters/index.ts +0 -84
- package/src/renderer-svg/index.ts +0 -8
- package/src/renderer-svg/package.json +0 -17
- package/src/renderer-svg/paths/arc.ts +0 -93
- package/src/renderer-svg/paths/index.ts +0 -6
- package/src/renderer-svg/paths/line.ts +0 -83
- package/src/renderer-svg/paths/rect.ts +0 -80
- package/src/renderer-svg/primitives/AnimatedCircle.tsx +0 -48
- package/src/renderer-svg/primitives/AnimatedPath.tsx +0 -48
- package/src/renderer-svg/primitives/Text.tsx +0 -73
- package/src/renderer-svg/primitives/index.ts +0 -6
- package/src/widgets/AreaChart/AreaChart.tsx +0 -213
- package/src/widgets/AreaChart/index.ts +0 -2
- package/src/widgets/AreaChart/types.ts +0 -34
- package/src/widgets/BarChart/BarChart.tsx +0 -249
- package/src/widgets/BarChart/index.ts +0 -10
- package/src/widgets/BarChart/types.ts +0 -27
- package/src/widgets/BoxPlot/BoxPlot.tsx +0 -252
- package/src/widgets/BoxPlot/index.ts +0 -2
- package/src/widgets/BoxPlot/types.ts +0 -27
- package/src/widgets/BubbleChart/BubbleChart.tsx +0 -175
- package/src/widgets/BubbleChart/index.ts +0 -2
- package/src/widgets/BubbleChart/types.ts +0 -33
- package/src/widgets/CandlestickChart/CandlestickChart.tsx +0 -204
- package/src/widgets/CandlestickChart/index.ts +0 -2
- package/src/widgets/CandlestickChart/types.ts +0 -29
- package/src/widgets/FunnelChart/FunnelChart.tsx +0 -172
- package/src/widgets/FunnelChart/index.ts +0 -2
- package/src/widgets/FunnelChart/types.ts +0 -22
- package/src/widgets/Gauge/Gauge.tsx +0 -235
- package/src/widgets/Gauge/index.ts +0 -5
- package/src/widgets/Gauge/types.ts +0 -19
- package/src/widgets/GroupedBarChart/GroupedBarChart.tsx +0 -190
- package/src/widgets/GroupedBarChart/index.ts +0 -2
- package/src/widgets/GroupedBarChart/types.ts +0 -30
- package/src/widgets/Heatmap/Heatmap.tsx +0 -216
- package/src/widgets/Heatmap/index.ts +0 -2
- package/src/widgets/Heatmap/types.ts +0 -27
- package/src/widgets/Histogram/Histogram.tsx +0 -173
- package/src/widgets/Histogram/index.ts +0 -2
- package/src/widgets/Histogram/types.ts +0 -18
- package/src/widgets/HorizontalBarChart/HorizontalBarChart.tsx +0 -125
- package/src/widgets/HorizontalBarChart/index.ts +0 -2
- package/src/widgets/HorizontalBarChart/types.ts +0 -23
- package/src/widgets/KPI/KPI.tsx +0 -222
- package/src/widgets/KPI/index.ts +0 -5
- package/src/widgets/KPI/types.ts +0 -19
- package/src/widgets/LineChart/LineChart.tsx +0 -364
- package/src/widgets/LineChart/index.ts +0 -10
- package/src/widgets/LineChart/types.ts +0 -34
- package/src/widgets/MultiLineSparkline/MultiLineSparkline.tsx +0 -234
- package/src/widgets/MultiLineSparkline/index.ts +0 -10
- package/src/widgets/MultiLineSparkline/types.ts +0 -25
- package/src/widgets/PieChart/PieChart.tsx +0 -275
- package/src/widgets/PieChart/index.ts +0 -10
- package/src/widgets/PieChart/types.ts +0 -26
- package/src/widgets/Progress/Progress.tsx +0 -201
- package/src/widgets/Progress/index.ts +0 -5
- package/src/widgets/Progress/types.ts +0 -19
- package/src/widgets/RadarChart/RadarChart.tsx +0 -213
- package/src/widgets/RadarChart/index.ts +0 -2
- package/src/widgets/RadarChart/types.ts +0 -29
- package/src/widgets/SankeyDiagram/SankeyDiagram.tsx +0 -272
- package/src/widgets/SankeyDiagram/index.ts +0 -2
- package/src/widgets/SankeyDiagram/types.ts +0 -29
- package/src/widgets/ScatterPlot/ScatterPlot.tsx +0 -167
- package/src/widgets/ScatterPlot/index.ts +0 -2
- package/src/widgets/ScatterPlot/types.ts +0 -32
- package/src/widgets/Sparkline/Sparkline.tsx +0 -203
- package/src/widgets/Sparkline/index.ts +0 -5
- package/src/widgets/Sparkline/types.ts +0 -18
- package/src/widgets/StackedBarChart/StackedBarChart.tsx +0 -181
- package/src/widgets/StackedBarChart/index.ts +0 -2
- package/src/widgets/StackedBarChart/types.ts +0 -29
- package/src/widgets/SunburstChart/SunburstChart.tsx +0 -176
- package/src/widgets/SunburstChart/index.ts +0 -2
- package/src/widgets/SunburstChart/types.ts +0 -22
- package/src/widgets/Treemap/Treemap.tsx +0 -191
- package/src/widgets/Treemap/index.ts +0 -2
- package/src/widgets/Treemap/types.ts +0 -23
- package/src/widgets/WaterfallChart/WaterfallChart.tsx +0 -226
- package/src/widgets/WaterfallChart/index.ts +0 -2
- package/src/widgets/WaterfallChart/types.ts +0 -26
- package/src/widgets/index.ts +0 -40
- package/src/widgets/package.json +0 -18
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SVG line path generators
|
|
3
|
-
*/
|
|
4
|
-
import { Point } from '../../core/layout';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Creates a simple line path from points
|
|
8
|
-
*/
|
|
9
|
-
export function createLinePath(points: Point[]): string {
|
|
10
|
-
if (points.length === 0) return '';
|
|
11
|
-
|
|
12
|
-
const pathParts = points.map((point, index) => {
|
|
13
|
-
const command = index === 0 ? 'M' : 'L';
|
|
14
|
-
return `${command} ${point.x} ${point.y}`;
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
return pathParts.join(' ');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Creates a smooth curve path using quadratic bezier curves
|
|
22
|
-
*/
|
|
23
|
-
export function createSmoothLinePath(points: Point[]): string {
|
|
24
|
-
if (points.length === 0) return '';
|
|
25
|
-
if (points.length === 1) return `M ${points[0].x} ${points[0].y}`;
|
|
26
|
-
|
|
27
|
-
const pathParts: string[] = [`M ${points[0].x} ${points[0].y}`];
|
|
28
|
-
|
|
29
|
-
for (let i = 0; i < points.length - 1; i++) {
|
|
30
|
-
const current = points[i];
|
|
31
|
-
const next = points[i + 1];
|
|
32
|
-
|
|
33
|
-
// Calculate control point (midpoint)
|
|
34
|
-
const controlX = (current.x + next.x) / 2;
|
|
35
|
-
const controlY = (current.y + next.y) / 2;
|
|
36
|
-
|
|
37
|
-
if (i === 0) {
|
|
38
|
-
// First segment: use simple control point
|
|
39
|
-
pathParts.push(`Q ${current.x} ${current.y} ${controlX} ${controlY}`);
|
|
40
|
-
} else {
|
|
41
|
-
// Subsequent segments: smooth curve
|
|
42
|
-
pathParts.push(`T ${controlX} ${controlY}`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Final point
|
|
47
|
-
const lastPoint = points[points.length - 1];
|
|
48
|
-
pathParts.push(`T ${lastPoint.x} ${lastPoint.y}`);
|
|
49
|
-
|
|
50
|
-
return pathParts.join(' ');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Creates an area path (filled under line)
|
|
55
|
-
*/
|
|
56
|
-
export function createAreaPath(
|
|
57
|
-
points: Point[],
|
|
58
|
-
baselineY: number
|
|
59
|
-
): string {
|
|
60
|
-
if (points.length === 0) return '';
|
|
61
|
-
|
|
62
|
-
const linePath = createLinePath(points);
|
|
63
|
-
const lastPoint = points[points.length - 1];
|
|
64
|
-
const firstPoint = points[0];
|
|
65
|
-
|
|
66
|
-
return `${linePath} L ${lastPoint.x} ${baselineY} L ${firstPoint.x} ${baselineY} Z`;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Creates a smooth area path
|
|
71
|
-
*/
|
|
72
|
-
export function createSmoothAreaPath(
|
|
73
|
-
points: Point[],
|
|
74
|
-
baselineY: number
|
|
75
|
-
): string {
|
|
76
|
-
if (points.length === 0) return '';
|
|
77
|
-
|
|
78
|
-
const linePath = createSmoothLinePath(points);
|
|
79
|
-
const lastPoint = points[points.length - 1];
|
|
80
|
-
const firstPoint = points[0];
|
|
81
|
-
|
|
82
|
-
return `${linePath} L ${lastPoint.x} ${baselineY} L ${firstPoint.x} ${baselineY} Z`;
|
|
83
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SVG rectangle path generators
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export interface RectConfig {
|
|
6
|
-
x: number;
|
|
7
|
-
y: number;
|
|
8
|
-
width: number;
|
|
9
|
-
height: number;
|
|
10
|
-
radius?: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Creates a rounded rectangle path
|
|
15
|
-
*/
|
|
16
|
-
export function createRoundedRectPath(config: RectConfig): string {
|
|
17
|
-
const { x, y, width, height, radius = 0 } = config;
|
|
18
|
-
|
|
19
|
-
if (radius === 0) {
|
|
20
|
-
return `M ${x} ${y} L ${x + width} ${y} L ${x + width} ${y + height} L ${x} ${y + height} Z`;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const r = Math.min(radius, width / 2, height / 2);
|
|
24
|
-
|
|
25
|
-
return [
|
|
26
|
-
`M ${x + r} ${y}`,
|
|
27
|
-
`L ${x + width - r} ${y}`,
|
|
28
|
-
`Q ${x + width} ${y} ${x + width} ${y + r}`,
|
|
29
|
-
`L ${x + width} ${y + height - r}`,
|
|
30
|
-
`Q ${x + width} ${y + height} ${x + width - r} ${y + height}`,
|
|
31
|
-
`L ${x + r} ${y + height}`,
|
|
32
|
-
`Q ${x} ${y + height} ${x} ${y + height - r}`,
|
|
33
|
-
`L ${x} ${y + r}`,
|
|
34
|
-
`Q ${x} ${y} ${x + r} ${y}`,
|
|
35
|
-
'Z',
|
|
36
|
-
].join(' ');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Creates a rectangle with only specific corners rounded
|
|
41
|
-
*/
|
|
42
|
-
export function createPartiallyRoundedRectPath(
|
|
43
|
-
config: RectConfig & {
|
|
44
|
-
radiusTopLeft?: number;
|
|
45
|
-
radiusTopRight?: number;
|
|
46
|
-
radiusBottomRight?: number;
|
|
47
|
-
radiusBottomLeft?: number;
|
|
48
|
-
}
|
|
49
|
-
): string {
|
|
50
|
-
const {
|
|
51
|
-
x,
|
|
52
|
-
y,
|
|
53
|
-
width,
|
|
54
|
-
height,
|
|
55
|
-
radiusTopLeft = 0,
|
|
56
|
-
radiusTopRight = 0,
|
|
57
|
-
radiusBottomRight = 0,
|
|
58
|
-
radiusBottomLeft = 0,
|
|
59
|
-
} = config;
|
|
60
|
-
|
|
61
|
-
const rtl = Math.min(radiusTopLeft, width / 2, height / 2);
|
|
62
|
-
const rtr = Math.min(radiusTopRight, width / 2, height / 2);
|
|
63
|
-
const rbr = Math.min(radiusBottomRight, width / 2, height / 2);
|
|
64
|
-
const rbl = Math.min(radiusBottomLeft, width / 2, height / 2);
|
|
65
|
-
|
|
66
|
-
return [
|
|
67
|
-
`M ${x + rtl} ${y}`,
|
|
68
|
-
`L ${x + width - rtr} ${y}`,
|
|
69
|
-
rtr > 0 ? `Q ${x + width} ${y} ${x + width} ${y + rtr}` : '',
|
|
70
|
-
`L ${x + width} ${y + height - rbr}`,
|
|
71
|
-
rbr > 0 ? `Q ${x + width} ${y + height} ${x + width - rbr} ${y + height}` : '',
|
|
72
|
-
`L ${x + rbl} ${y + height}`,
|
|
73
|
-
rbl > 0 ? `Q ${x} ${y + height} ${x} ${y + height - rbl}` : '',
|
|
74
|
-
`L ${x} ${y + rtl}`,
|
|
75
|
-
rtl > 0 ? `Q ${x} ${y} ${x + rtl} ${y}` : '',
|
|
76
|
-
'Z',
|
|
77
|
-
]
|
|
78
|
-
.filter(Boolean)
|
|
79
|
-
.join(' ');
|
|
80
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Animated SVG Circle primitive
|
|
3
|
-
*/
|
|
4
|
-
import React, { memo } from 'react';
|
|
5
|
-
import Animated from 'react-native-reanimated';
|
|
6
|
-
import { Circle } from 'react-native-svg';
|
|
7
|
-
|
|
8
|
-
const AnimatedSVGCircle = Animated.createAnimatedComponent(Circle);
|
|
9
|
-
|
|
10
|
-
export interface AnimatedCircleProps {
|
|
11
|
-
cx: number;
|
|
12
|
-
cy: number;
|
|
13
|
-
r: number;
|
|
14
|
-
fill?: string;
|
|
15
|
-
stroke?: string;
|
|
16
|
-
strokeWidth?: number;
|
|
17
|
-
opacity?: number;
|
|
18
|
-
testID?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Memoized animated circle component
|
|
23
|
-
*/
|
|
24
|
-
export const AnimatedCircle = memo<AnimatedCircleProps>(({
|
|
25
|
-
cx,
|
|
26
|
-
cy,
|
|
27
|
-
r,
|
|
28
|
-
fill = 'transparent',
|
|
29
|
-
stroke = 'transparent',
|
|
30
|
-
strokeWidth = 1,
|
|
31
|
-
opacity = 1,
|
|
32
|
-
testID,
|
|
33
|
-
}) => {
|
|
34
|
-
return (
|
|
35
|
-
<AnimatedSVGCircle
|
|
36
|
-
cx={cx}
|
|
37
|
-
cy={cy}
|
|
38
|
-
r={r}
|
|
39
|
-
fill={fill}
|
|
40
|
-
stroke={stroke}
|
|
41
|
-
strokeWidth={strokeWidth}
|
|
42
|
-
opacity={opacity}
|
|
43
|
-
testID={testID}
|
|
44
|
-
/>
|
|
45
|
-
);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
AnimatedCircle.displayName = 'AnimatedCircle';
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Animated SVG Path primitive
|
|
3
|
-
*/
|
|
4
|
-
import React, { memo } from 'react';
|
|
5
|
-
import Animated from 'react-native-reanimated';
|
|
6
|
-
import { Path } from 'react-native-svg';
|
|
7
|
-
|
|
8
|
-
const AnimatedSVGPath = Animated.createAnimatedComponent(Path);
|
|
9
|
-
|
|
10
|
-
export interface AnimatedPathProps {
|
|
11
|
-
d: string;
|
|
12
|
-
stroke?: string;
|
|
13
|
-
strokeWidth?: number;
|
|
14
|
-
fill?: string;
|
|
15
|
-
opacity?: number;
|
|
16
|
-
strokeLinecap?: 'butt' | 'round' | 'square';
|
|
17
|
-
strokeLinejoin?: 'miter' | 'round' | 'bevel';
|
|
18
|
-
testID?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Memoized animated path component
|
|
23
|
-
*/
|
|
24
|
-
export const AnimatedPath = memo<AnimatedPathProps>(({
|
|
25
|
-
d,
|
|
26
|
-
stroke = 'transparent',
|
|
27
|
-
strokeWidth = 1,
|
|
28
|
-
fill = 'transparent',
|
|
29
|
-
opacity = 1,
|
|
30
|
-
strokeLinecap = 'round',
|
|
31
|
-
strokeLinejoin = 'round',
|
|
32
|
-
testID,
|
|
33
|
-
}) => {
|
|
34
|
-
return (
|
|
35
|
-
<AnimatedSVGPath
|
|
36
|
-
d={d}
|
|
37
|
-
stroke={stroke}
|
|
38
|
-
strokeWidth={strokeWidth}
|
|
39
|
-
fill={fill}
|
|
40
|
-
opacity={opacity}
|
|
41
|
-
strokeLinecap={strokeLinecap}
|
|
42
|
-
strokeLinejoin={strokeLinejoin}
|
|
43
|
-
testID={testID}
|
|
44
|
-
/>
|
|
45
|
-
);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
AnimatedPath.displayName = 'AnimatedPath';
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SVG Text primitive with centered text helper
|
|
3
|
-
*/
|
|
4
|
-
import React, { memo } from 'react';
|
|
5
|
-
import { Text as SVGText } from 'react-native-svg';
|
|
6
|
-
|
|
7
|
-
export interface TextProps {
|
|
8
|
-
x: number;
|
|
9
|
-
y: number;
|
|
10
|
-
text: string;
|
|
11
|
-
fontSize?: number;
|
|
12
|
-
fontWeight?: string | number;
|
|
13
|
-
fill?: string;
|
|
14
|
-
textAnchor?: 'start' | 'middle' | 'end';
|
|
15
|
-
alignmentBaseline?: 'baseline' | 'text-bottom' | 'alphabetic' | 'ideographic' | 'middle' | 'central' | 'mathematical' | 'text-top' | 'bottom' | 'center' | 'top' | 'text-before-edge' | 'text-after-edge' | 'before-edge' | 'after-edge' | 'hanging';
|
|
16
|
-
opacity?: number;
|
|
17
|
-
testID?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* SVG Text component with sensible defaults
|
|
22
|
-
*/
|
|
23
|
-
export const Text = memo<TextProps>(({
|
|
24
|
-
x,
|
|
25
|
-
y,
|
|
26
|
-
text,
|
|
27
|
-
fontSize = 14,
|
|
28
|
-
fontWeight = 'normal',
|
|
29
|
-
fill = '#000000',
|
|
30
|
-
textAnchor = 'middle',
|
|
31
|
-
alignmentBaseline = 'middle',
|
|
32
|
-
opacity = 1,
|
|
33
|
-
testID,
|
|
34
|
-
}) => {
|
|
35
|
-
return (
|
|
36
|
-
<SVGText
|
|
37
|
-
x={x}
|
|
38
|
-
y={y}
|
|
39
|
-
fontSize={fontSize}
|
|
40
|
-
fontWeight={fontWeight}
|
|
41
|
-
fill={fill}
|
|
42
|
-
textAnchor={textAnchor}
|
|
43
|
-
alignmentBaseline={alignmentBaseline}
|
|
44
|
-
opacity={opacity}
|
|
45
|
-
testID={testID}
|
|
46
|
-
>
|
|
47
|
-
{text}
|
|
48
|
-
</SVGText>
|
|
49
|
-
);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
Text.displayName = 'Text';
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Helper to create centered text props
|
|
56
|
-
*/
|
|
57
|
-
export function createCenteredTextProps(
|
|
58
|
-
x: number,
|
|
59
|
-
y: number,
|
|
60
|
-
text: string,
|
|
61
|
-
fontSize?: number,
|
|
62
|
-
fill?: string
|
|
63
|
-
): TextProps {
|
|
64
|
-
return {
|
|
65
|
-
x,
|
|
66
|
-
y,
|
|
67
|
-
text,
|
|
68
|
-
fontSize,
|
|
69
|
-
fill,
|
|
70
|
-
textAnchor: 'middle',
|
|
71
|
-
alignmentBaseline: 'middle',
|
|
72
|
-
};
|
|
73
|
-
}
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AreaChart Widget - Filled area under line
|
|
3
|
-
*/
|
|
4
|
-
import React, { memo, useMemo } from 'react';
|
|
5
|
-
import { View, Text as RNText, StyleSheet } from 'react-native';
|
|
6
|
-
import Svg, { Defs, LinearGradient, Stop, Line as SvgLine } from 'react-native-svg';
|
|
7
|
-
import {
|
|
8
|
-
useWidgetDimensions,
|
|
9
|
-
useWidgetTheme,
|
|
10
|
-
normalize,
|
|
11
|
-
formatTimeLabel,
|
|
12
|
-
reduceLabels,
|
|
13
|
-
} from '../../core';
|
|
14
|
-
import {
|
|
15
|
-
createLinePath,
|
|
16
|
-
createAreaPath,
|
|
17
|
-
AnimatedPath,
|
|
18
|
-
} from '../../renderer-svg';
|
|
19
|
-
import { AreaChartWidgetProps } from './types';
|
|
20
|
-
|
|
21
|
-
export const AreaChart = memo<AreaChartWidgetProps>(({
|
|
22
|
-
data: widgetData,
|
|
23
|
-
width,
|
|
24
|
-
height,
|
|
25
|
-
loading = false,
|
|
26
|
-
theme: themeOverride,
|
|
27
|
-
showXAxis = true,
|
|
28
|
-
showYAxis = true,
|
|
29
|
-
showGrid = true,
|
|
30
|
-
showLegend = true,
|
|
31
|
-
showGradient = true,
|
|
32
|
-
stacked = false,
|
|
33
|
-
curveStyle = 'linear',
|
|
34
|
-
maxXLabels = 6,
|
|
35
|
-
testID,
|
|
36
|
-
}) => {
|
|
37
|
-
const theme = useWidgetTheme(themeOverride);
|
|
38
|
-
const dimensions = useWidgetDimensions(width, height, 350, 250);
|
|
39
|
-
|
|
40
|
-
if (loading) {
|
|
41
|
-
return (
|
|
42
|
-
<View style={[styles.container, { width: dimensions.width, height: dimensions.height, backgroundColor: theme.colors.surface, borderRadius: theme.radius.md }]} testID={`${testID}-loading`}>
|
|
43
|
-
<RNText style={[styles.loadingText, { color: theme.colors.textSecondary }]}>Loading...</RNText>
|
|
44
|
-
</View>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!widgetData || !widgetData.series || widgetData.series.length === 0) {
|
|
49
|
-
return (
|
|
50
|
-
<View style={[styles.container, { width: dimensions.width, height: dimensions.height, backgroundColor: theme.colors.surface, borderRadius: theme.radius.md }]} testID={`${testID}-empty`}>
|
|
51
|
-
<RNText style={[styles.emptyText, { color: theme.colors.textSecondary }]}>No data</RNText>
|
|
52
|
-
</View>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const { series, title, timeInterval } = widgetData;
|
|
57
|
-
const padding = theme.spacing.md;
|
|
58
|
-
const titleHeight = title ? theme.fontScale.md + theme.spacing.sm : 0;
|
|
59
|
-
const legendHeight = showLegend ? 30 : 0;
|
|
60
|
-
const xAxisHeight = showXAxis ? 30 : 0;
|
|
61
|
-
const yAxisWidth = showYAxis ? 40 : 0;
|
|
62
|
-
|
|
63
|
-
const chartWidth = dimensions.width - padding * 2 - yAxisWidth;
|
|
64
|
-
const chartHeight = dimensions.height - padding * 2 - titleHeight - legendHeight - xAxisHeight;
|
|
65
|
-
|
|
66
|
-
const { globalMinY, globalMaxY, xLabels } = useMemo(() => {
|
|
67
|
-
let minY = Infinity;
|
|
68
|
-
let maxY = -Infinity;
|
|
69
|
-
const allXValues: (number | Date)[] = [];
|
|
70
|
-
|
|
71
|
-
series.forEach(s => {
|
|
72
|
-
s.data.forEach(point => {
|
|
73
|
-
if (point.y < minY) minY = point.y;
|
|
74
|
-
if (point.y > maxY) maxY = point.y;
|
|
75
|
-
allXValues.push(point.x);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
const labels = allXValues.map((x, idx) => {
|
|
80
|
-
if (x instanceof Date) {
|
|
81
|
-
return timeInterval ? formatTimeLabel(x, timeInterval) : x.toLocaleDateString();
|
|
82
|
-
}
|
|
83
|
-
return x.toString();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
return { globalMinY: minY, globalMaxY: maxY, xLabels: labels };
|
|
87
|
-
}, [series, timeInterval]);
|
|
88
|
-
|
|
89
|
-
const yAxisLabels = useMemo(() => {
|
|
90
|
-
const range = globalMaxY - globalMinY;
|
|
91
|
-
const step = range / 4;
|
|
92
|
-
return Array.from({ length: 5 }, (_, i) => ({
|
|
93
|
-
value: globalMinY + i * step,
|
|
94
|
-
y: chartHeight - (i * chartHeight) / 4,
|
|
95
|
-
}));
|
|
96
|
-
}, [globalMinY, globalMaxY, chartHeight]);
|
|
97
|
-
|
|
98
|
-
const xAxisLabelsReduced = useMemo(() => {
|
|
99
|
-
return reduceLabels(xLabels, maxXLabels);
|
|
100
|
-
}, [xLabels, maxXLabels]);
|
|
101
|
-
|
|
102
|
-
const seriesPaths = useMemo(() => {
|
|
103
|
-
return series.map((s, seriesIndex) => {
|
|
104
|
-
const points = s.data.map((point, index) => {
|
|
105
|
-
const x = (index / (s.data.length - 1 || 1)) * chartWidth;
|
|
106
|
-
const normalizedY = normalize(point.y, globalMinY, globalMaxY);
|
|
107
|
-
const y = chartHeight - normalizedY * chartHeight;
|
|
108
|
-
return { x, y };
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
const linePath = createLinePath(points);
|
|
112
|
-
const areaPath = createAreaPath(points, chartHeight);
|
|
113
|
-
|
|
114
|
-
return {
|
|
115
|
-
linePath,
|
|
116
|
-
areaPath,
|
|
117
|
-
color: s.color,
|
|
118
|
-
label: s.label,
|
|
119
|
-
gradientId: `gradient-${seriesIndex}`,
|
|
120
|
-
};
|
|
121
|
-
});
|
|
122
|
-
}, [series, chartWidth, chartHeight, globalMinY, globalMaxY]);
|
|
123
|
-
|
|
124
|
-
return (
|
|
125
|
-
<View style={[styles.wrapper, { width: dimensions.width, height: dimensions.height, backgroundColor: theme.colors.surface, borderRadius: theme.radius.md, padding }]} testID={testID}>
|
|
126
|
-
{title && (
|
|
127
|
-
<RNText style={[styles.title, { color: theme.colors.text, fontSize: theme.fontScale.md, fontWeight: 'bold', marginBottom: theme.spacing.sm }]}>
|
|
128
|
-
{title}
|
|
129
|
-
</RNText>
|
|
130
|
-
)}
|
|
131
|
-
|
|
132
|
-
<View style={styles.chartRow}>
|
|
133
|
-
{showYAxis && (
|
|
134
|
-
<View style={[styles.yAxis, { width: yAxisWidth }]}>
|
|
135
|
-
{yAxisLabels.map((label, index) => (
|
|
136
|
-
<RNText key={`y-${index}`} style={[styles.yAxisLabel, { color: theme.colors.textSecondary, fontSize: theme.fontScale.xs, top: label.y - 6 }]}>
|
|
137
|
-
{label.value.toFixed(0)}
|
|
138
|
-
</RNText>
|
|
139
|
-
))}
|
|
140
|
-
</View>
|
|
141
|
-
)}
|
|
142
|
-
|
|
143
|
-
<View>
|
|
144
|
-
<Svg width={chartWidth} height={chartHeight}>
|
|
145
|
-
{showGradient && (
|
|
146
|
-
<Defs>
|
|
147
|
-
{seriesPaths.map((series, index) => (
|
|
148
|
-
<LinearGradient key={series.gradientId} id={series.gradientId} x1="0" y1="0" x2="0" y2="1">
|
|
149
|
-
<Stop offset="0%" stopColor={series.color} stopOpacity="0.4" />
|
|
150
|
-
<Stop offset="100%" stopColor={series.color} stopOpacity="0.05" />
|
|
151
|
-
</LinearGradient>
|
|
152
|
-
))}
|
|
153
|
-
</Defs>
|
|
154
|
-
)}
|
|
155
|
-
|
|
156
|
-
{showGrid && yAxisLabels.map((label, index) => (
|
|
157
|
-
<SvgLine key={`grid-${index}`} x1={0} y1={label.y} x2={chartWidth} y2={label.y} stroke={theme.colors.borderLight} strokeWidth={1} />
|
|
158
|
-
))}
|
|
159
|
-
|
|
160
|
-
{seriesPaths.map((series, index) => (
|
|
161
|
-
<React.Fragment key={`series-${index}`}>
|
|
162
|
-
<AnimatedPath d={series.areaPath} fill={showGradient ? `url(#${series.gradientId})` : series.color} opacity={showGradient ? 1 : 0.3} />
|
|
163
|
-
<AnimatedPath d={series.linePath} stroke={series.color} strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" fill="transparent" />
|
|
164
|
-
</React.Fragment>
|
|
165
|
-
))}
|
|
166
|
-
</Svg>
|
|
167
|
-
|
|
168
|
-
{showXAxis && (
|
|
169
|
-
<View style={[styles.xAxis, { width: chartWidth }]}>
|
|
170
|
-
{xAxisLabelsReduced.map((item, index) => (
|
|
171
|
-
<RNText key={`x-${index}`} style={[styles.xAxisLabel, { color: theme.colors.textSecondary, fontSize: theme.fontScale.xs, left: (item.index / (xLabels.length - 1 || 1)) * chartWidth - 20 }]}>
|
|
172
|
-
{item.label}
|
|
173
|
-
</RNText>
|
|
174
|
-
))}
|
|
175
|
-
</View>
|
|
176
|
-
)}
|
|
177
|
-
</View>
|
|
178
|
-
</View>
|
|
179
|
-
|
|
180
|
-
{showLegend && (
|
|
181
|
-
<View style={styles.legend}>
|
|
182
|
-
{seriesPaths.map((series, index) => (
|
|
183
|
-
series.label && (
|
|
184
|
-
<View key={`legend-${index}`} style={styles.legendItem}>
|
|
185
|
-
<View style={[styles.legendColor, { backgroundColor: series.color }]} />
|
|
186
|
-
<RNText style={[styles.legendText, { color: theme.colors.textSecondary, fontSize: theme.fontScale.xs }]}>{series.label}</RNText>
|
|
187
|
-
</View>
|
|
188
|
-
)
|
|
189
|
-
))}
|
|
190
|
-
</View>
|
|
191
|
-
)}
|
|
192
|
-
</View>
|
|
193
|
-
);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
AreaChart.displayName = 'AreaChart';
|
|
197
|
-
|
|
198
|
-
const styles = StyleSheet.create({
|
|
199
|
-
wrapper: { justifyContent: 'flex-start', alignItems: 'flex-start' },
|
|
200
|
-
container: { justifyContent: 'center', alignItems: 'center' },
|
|
201
|
-
loadingText: { fontSize: 16 },
|
|
202
|
-
emptyText: { fontSize: 16 },
|
|
203
|
-
title: { textAlign: 'center', width: '100%' },
|
|
204
|
-
chartRow: { flexDirection: 'row', alignItems: 'flex-start' },
|
|
205
|
-
yAxis: { position: 'relative', marginRight: 8 },
|
|
206
|
-
yAxisLabel: { position: 'absolute', right: 0, textAlign: 'right' },
|
|
207
|
-
xAxis: { position: 'relative', height: 30, marginTop: 4 },
|
|
208
|
-
xAxisLabel: { position: 'absolute', width: 40, textAlign: 'center' },
|
|
209
|
-
legend: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'center', marginTop: 12, gap: 12 },
|
|
210
|
-
legendItem: { flexDirection: 'row', alignItems: 'center', gap: 6 },
|
|
211
|
-
legendColor: { width: 12, height: 12, borderRadius: 2 },
|
|
212
|
-
legendText: { textTransform: 'uppercase', letterSpacing: 0.5 },
|
|
213
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AreaChart Widget types
|
|
3
|
-
*/
|
|
4
|
-
import { BaseWidgetProps, TimeInterval } from '../../core';
|
|
5
|
-
|
|
6
|
-
export interface AreaChartDataPoint {
|
|
7
|
-
x: number | Date;
|
|
8
|
-
y: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface AreaChartSeries {
|
|
12
|
-
data: AreaChartDataPoint[];
|
|
13
|
-
color: string;
|
|
14
|
-
label?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface AreaChartData {
|
|
18
|
-
series: AreaChartSeries[];
|
|
19
|
-
title?: string;
|
|
20
|
-
timeInterval?: TimeInterval;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type AreaChartStyle = 'smooth' | 'linear';
|
|
24
|
-
|
|
25
|
-
export interface AreaChartWidgetProps extends BaseWidgetProps<AreaChartData> {
|
|
26
|
-
showXAxis?: boolean;
|
|
27
|
-
showYAxis?: boolean;
|
|
28
|
-
showGrid?: boolean;
|
|
29
|
-
showLegend?: boolean;
|
|
30
|
-
showGradient?: boolean;
|
|
31
|
-
stacked?: boolean;
|
|
32
|
-
curveStyle?: AreaChartStyle;
|
|
33
|
-
maxXLabels?: number;
|
|
34
|
-
}
|