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.
Files changed (100) hide show
  1. package/package.json +1 -2
  2. package/src/core/animation/index.ts +0 -113
  3. package/src/core/animation/index.web.ts +0 -112
  4. package/src/core/hooks/index.ts +0 -66
  5. package/src/core/index.ts +0 -26
  6. package/src/core/layout/index.ts +0 -101
  7. package/src/core/math/index.ts +0 -72
  8. package/src/core/package.json +0 -13
  9. package/src/core/theme/ThemeProvider.tsx +0 -36
  10. package/src/core/theme/index.ts +0 -5
  11. package/src/core/theme/themes.ts +0 -132
  12. package/src/core/types/index.ts +0 -164
  13. package/src/core/utils/responsive.ts +0 -203
  14. package/src/core/utils/time.ts +0 -100
  15. package/src/index.ts +0 -13
  16. package/src/renderer-svg/adapters/index.ts +0 -84
  17. package/src/renderer-svg/index.ts +0 -8
  18. package/src/renderer-svg/package.json +0 -17
  19. package/src/renderer-svg/paths/arc.ts +0 -93
  20. package/src/renderer-svg/paths/index.ts +0 -6
  21. package/src/renderer-svg/paths/line.ts +0 -83
  22. package/src/renderer-svg/paths/rect.ts +0 -80
  23. package/src/renderer-svg/primitives/AnimatedCircle.tsx +0 -48
  24. package/src/renderer-svg/primitives/AnimatedPath.tsx +0 -48
  25. package/src/renderer-svg/primitives/Text.tsx +0 -73
  26. package/src/renderer-svg/primitives/index.ts +0 -6
  27. package/src/widgets/AreaChart/AreaChart.tsx +0 -213
  28. package/src/widgets/AreaChart/index.ts +0 -2
  29. package/src/widgets/AreaChart/types.ts +0 -34
  30. package/src/widgets/BarChart/BarChart.tsx +0 -249
  31. package/src/widgets/BarChart/index.ts +0 -10
  32. package/src/widgets/BarChart/types.ts +0 -27
  33. package/src/widgets/BoxPlot/BoxPlot.tsx +0 -252
  34. package/src/widgets/BoxPlot/index.ts +0 -2
  35. package/src/widgets/BoxPlot/types.ts +0 -27
  36. package/src/widgets/BubbleChart/BubbleChart.tsx +0 -175
  37. package/src/widgets/BubbleChart/index.ts +0 -2
  38. package/src/widgets/BubbleChart/types.ts +0 -33
  39. package/src/widgets/CandlestickChart/CandlestickChart.tsx +0 -204
  40. package/src/widgets/CandlestickChart/index.ts +0 -2
  41. package/src/widgets/CandlestickChart/types.ts +0 -29
  42. package/src/widgets/FunnelChart/FunnelChart.tsx +0 -172
  43. package/src/widgets/FunnelChart/index.ts +0 -2
  44. package/src/widgets/FunnelChart/types.ts +0 -22
  45. package/src/widgets/Gauge/Gauge.tsx +0 -235
  46. package/src/widgets/Gauge/index.ts +0 -5
  47. package/src/widgets/Gauge/types.ts +0 -19
  48. package/src/widgets/GroupedBarChart/GroupedBarChart.tsx +0 -190
  49. package/src/widgets/GroupedBarChart/index.ts +0 -2
  50. package/src/widgets/GroupedBarChart/types.ts +0 -30
  51. package/src/widgets/Heatmap/Heatmap.tsx +0 -216
  52. package/src/widgets/Heatmap/index.ts +0 -2
  53. package/src/widgets/Heatmap/types.ts +0 -27
  54. package/src/widgets/Histogram/Histogram.tsx +0 -173
  55. package/src/widgets/Histogram/index.ts +0 -2
  56. package/src/widgets/Histogram/types.ts +0 -18
  57. package/src/widgets/HorizontalBarChart/HorizontalBarChart.tsx +0 -125
  58. package/src/widgets/HorizontalBarChart/index.ts +0 -2
  59. package/src/widgets/HorizontalBarChart/types.ts +0 -23
  60. package/src/widgets/KPI/KPI.tsx +0 -222
  61. package/src/widgets/KPI/index.ts +0 -5
  62. package/src/widgets/KPI/types.ts +0 -19
  63. package/src/widgets/LineChart/LineChart.tsx +0 -364
  64. package/src/widgets/LineChart/index.ts +0 -10
  65. package/src/widgets/LineChart/types.ts +0 -34
  66. package/src/widgets/MultiLineSparkline/MultiLineSparkline.tsx +0 -234
  67. package/src/widgets/MultiLineSparkline/index.ts +0 -10
  68. package/src/widgets/MultiLineSparkline/types.ts +0 -25
  69. package/src/widgets/PieChart/PieChart.tsx +0 -275
  70. package/src/widgets/PieChart/index.ts +0 -10
  71. package/src/widgets/PieChart/types.ts +0 -26
  72. package/src/widgets/Progress/Progress.tsx +0 -201
  73. package/src/widgets/Progress/index.ts +0 -5
  74. package/src/widgets/Progress/types.ts +0 -19
  75. package/src/widgets/RadarChart/RadarChart.tsx +0 -213
  76. package/src/widgets/RadarChart/index.ts +0 -2
  77. package/src/widgets/RadarChart/types.ts +0 -29
  78. package/src/widgets/SankeyDiagram/SankeyDiagram.tsx +0 -272
  79. package/src/widgets/SankeyDiagram/index.ts +0 -2
  80. package/src/widgets/SankeyDiagram/types.ts +0 -29
  81. package/src/widgets/ScatterPlot/ScatterPlot.tsx +0 -167
  82. package/src/widgets/ScatterPlot/index.ts +0 -2
  83. package/src/widgets/ScatterPlot/types.ts +0 -32
  84. package/src/widgets/Sparkline/Sparkline.tsx +0 -203
  85. package/src/widgets/Sparkline/index.ts +0 -5
  86. package/src/widgets/Sparkline/types.ts +0 -18
  87. package/src/widgets/StackedBarChart/StackedBarChart.tsx +0 -181
  88. package/src/widgets/StackedBarChart/index.ts +0 -2
  89. package/src/widgets/StackedBarChart/types.ts +0 -29
  90. package/src/widgets/SunburstChart/SunburstChart.tsx +0 -176
  91. package/src/widgets/SunburstChart/index.ts +0 -2
  92. package/src/widgets/SunburstChart/types.ts +0 -22
  93. package/src/widgets/Treemap/Treemap.tsx +0 -191
  94. package/src/widgets/Treemap/index.ts +0 -2
  95. package/src/widgets/Treemap/types.ts +0 -23
  96. package/src/widgets/WaterfallChart/WaterfallChart.tsx +0 -226
  97. package/src/widgets/WaterfallChart/index.ts +0 -2
  98. package/src/widgets/WaterfallChart/types.ts +0 -26
  99. package/src/widgets/index.ts +0 -40
  100. package/src/widgets/package.json +0 -18
@@ -1,175 +0,0 @@
1
- /**
2
- * BubbleChart Widget - Scatter plot with size dimension (3D data)
3
- */
4
- import React, { memo, useMemo } from 'react';
5
- import { View, Text as RNText, StyleSheet } from 'react-native';
6
- import Svg, { Circle, Line as SvgLine } from 'react-native-svg';
7
- import { useWidgetDimensions, useWidgetTheme, normalize } from '../../core';
8
- import { BubbleChartWidgetProps } from './types';
9
-
10
- export const BubbleChart = memo<BubbleChartWidgetProps>(({
11
- data: widgetData,
12
- width,
13
- height,
14
- loading = false,
15
- theme: themeOverride,
16
- showXAxis = true,
17
- showYAxis = true,
18
- showGrid = true,
19
- showLegend = true,
20
- minBubbleSize = 5,
21
- maxBubbleSize = 30,
22
- testID,
23
- }) => {
24
- const theme = useWidgetTheme(themeOverride);
25
- const dimensions = useWidgetDimensions(width, height, 350, 300);
26
-
27
- if (loading) {
28
- return (
29
- <View style={[styles.container, { width: dimensions.width, height: dimensions.height, backgroundColor: theme.colors.surface, borderRadius: theme.radius.md }]} testID={`${testID}-loading`}>
30
- <RNText style={[styles.loadingText, { color: theme.colors.textSecondary }]}>Loading...</RNText>
31
- </View>
32
- );
33
- }
34
-
35
- if (!widgetData || !widgetData.series || widgetData.series.length === 0) {
36
- return (
37
- <View style={[styles.container, { width: dimensions.width, height: dimensions.height, backgroundColor: theme.colors.surface, borderRadius: theme.radius.md }]} testID={`${testID}-empty`}>
38
- <RNText style={[styles.emptyText, { color: theme.colors.textSecondary }]}>No data</RNText>
39
- </View>
40
- );
41
- }
42
-
43
- const { series, title } = widgetData;
44
- const padding = theme.spacing.md;
45
- const titleHeight = title ? theme.fontScale.md + theme.spacing.sm : 0;
46
- const legendHeight = showLegend ? 30 : 0;
47
- const xAxisHeight = showXAxis ? 30 : 0;
48
- const yAxisWidth = showYAxis ? 40 : 0;
49
-
50
- const chartWidth = dimensions.width - padding * 2 - yAxisWidth;
51
- const chartHeight = dimensions.height - padding * 2 - titleHeight - legendHeight - xAxisHeight;
52
-
53
- const { globalMinX, globalMaxX, globalMinY, globalMaxY, globalMinSize, globalMaxSize } = useMemo(() => {
54
- let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity, minS = Infinity, maxS = -Infinity;
55
- series.forEach(s => {
56
- s.data.forEach(point => {
57
- if (point.x < minX) minX = point.x;
58
- if (point.x > maxX) maxX = point.x;
59
- if (point.y < minY) minY = point.y;
60
- if (point.y > maxY) maxY = point.y;
61
- if (point.size < minS) minS = point.size;
62
- if (point.size > maxS) maxS = point.size;
63
- });
64
- });
65
- return { globalMinX: minX, globalMaxX: maxX, globalMinY: minY, globalMaxY: maxY, globalMinSize: minS, globalMaxSize: maxS };
66
- }, [series]);
67
-
68
- const yAxisLabels = useMemo(() => {
69
- const range = globalMaxY - globalMinY;
70
- const step = range / 4;
71
- return Array.from({ length: 5 }, (_, i) => ({
72
- value: globalMinY + i * step,
73
- y: chartHeight - (i * chartHeight) / 4,
74
- }));
75
- }, [globalMinY, globalMaxY, chartHeight]);
76
-
77
- const seriesBubbles = useMemo(() => {
78
- return series.map(s => {
79
- const bubbles = s.data.map(point => {
80
- const normalizedX = normalize(point.x, globalMinX, globalMaxX);
81
- const normalizedY = normalize(point.y, globalMinY, globalMaxY);
82
- const normalizedSize = normalize(point.size, globalMinSize, globalMaxSize);
83
- const radius = minBubbleSize + normalizedSize * (maxBubbleSize - minBubbleSize);
84
-
85
- return {
86
- cx: normalizedX * chartWidth,
87
- cy: chartHeight - normalizedY * chartHeight,
88
- r: radius,
89
- label: point.label,
90
- };
91
- });
92
-
93
- return {
94
- bubbles,
95
- color: s.color,
96
- label: s.label,
97
- };
98
- });
99
- }, [series, chartWidth, chartHeight, globalMinX, globalMaxX, globalMinY, globalMaxY, globalMinSize, globalMaxSize, minBubbleSize, maxBubbleSize]);
100
-
101
- return (
102
- <View style={[styles.wrapper, { width: dimensions.width, height: dimensions.height, backgroundColor: theme.colors.surface, borderRadius: theme.radius.md, padding }]} testID={testID}>
103
- {title && (
104
- <RNText style={[styles.title, { color: theme.colors.text, fontSize: theme.fontScale.md, fontWeight: 'bold', marginBottom: theme.spacing.sm }]}>
105
- {title}
106
- </RNText>
107
- )}
108
-
109
- <View style={styles.chartRow}>
110
- {showYAxis && (
111
- <View style={[styles.yAxis, { width: yAxisWidth }]}>
112
- {yAxisLabels.map((label, index) => (
113
- <RNText key={`y-${index}`} style={[styles.yAxisLabel, { color: theme.colors.textSecondary, fontSize: theme.fontScale.xs, top: label.y - 6 }]}>
114
- {label.value.toFixed(0)}
115
- </RNText>
116
- ))}
117
- </View>
118
- )}
119
-
120
- <View>
121
- <Svg width={chartWidth} height={chartHeight}>
122
- {showGrid && yAxisLabels.map((label, index) => (
123
- <SvgLine key={`grid-${index}`} x1={0} y1={label.y} x2={chartWidth} y2={label.y} stroke={theme.colors.borderLight} strokeWidth={1} />
124
- ))}
125
-
126
- {seriesBubbles.map((series, seriesIndex) => (
127
- series.bubbles.map((bubble, bubbleIndex) => (
128
- <Circle
129
- key={`series-${seriesIndex}-bubble-${bubbleIndex}`}
130
- cx={bubble.cx}
131
- cy={bubble.cy}
132
- r={bubble.r}
133
- fill={series.color}
134
- opacity={0.6}
135
- stroke={series.color}
136
- strokeWidth={2}
137
- />
138
- ))
139
- ))}
140
- </Svg>
141
- </View>
142
- </View>
143
-
144
- {showLegend && (
145
- <View style={styles.legend}>
146
- {seriesBubbles.map((series, index) => (
147
- series.label && (
148
- <View key={`legend-${index}`} style={styles.legendItem}>
149
- <View style={[styles.legendColor, { backgroundColor: series.color }]} />
150
- <RNText style={[styles.legendText, { color: theme.colors.textSecondary, fontSize: theme.fontScale.xs }]}>{series.label}</RNText>
151
- </View>
152
- )
153
- ))}
154
- </View>
155
- )}
156
- </View>
157
- );
158
- });
159
-
160
- BubbleChart.displayName = 'BubbleChart';
161
-
162
- const styles = StyleSheet.create({
163
- wrapper: { justifyContent: 'flex-start', alignItems: 'flex-start' },
164
- container: { justifyContent: 'center', alignItems: 'center' },
165
- loadingText: { fontSize: 16 },
166
- emptyText: { fontSize: 16 },
167
- title: { textAlign: 'center', width: '100%' },
168
- chartRow: { flexDirection: 'row', alignItems: 'flex-start' },
169
- yAxis: { position: 'relative', marginRight: 8 },
170
- yAxisLabel: { position: 'absolute', right: 0, textAlign: 'right' },
171
- legend: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'center', marginTop: 12, gap: 12 },
172
- legendItem: { flexDirection: 'row', alignItems: 'center', gap: 6 },
173
- legendColor: { width: 12, height: 12, borderRadius: 6 },
174
- legendText: { textTransform: 'uppercase', letterSpacing: 0.5 },
175
- });
@@ -1,2 +0,0 @@
1
- export { BubbleChart } from './BubbleChart';
2
- export type { BubbleChartWidgetProps, BubbleChartData, BubbleSeries, BubbleDataPoint } from './types';
@@ -1,33 +0,0 @@
1
- /**
2
- * BubbleChart Widget types - 3D scatter plot with size dimension
3
- */
4
- import { BaseWidgetProps } from '../../core';
5
-
6
- export interface BubbleDataPoint {
7
- x: number;
8
- y: number;
9
- size: number;
10
- label?: string;
11
- }
12
-
13
- export interface BubbleSeries {
14
- data: BubbleDataPoint[];
15
- color: string;
16
- label?: string;
17
- }
18
-
19
- export interface BubbleChartData {
20
- series: BubbleSeries[];
21
- title?: string;
22
- xAxisLabel?: string;
23
- yAxisLabel?: string;
24
- }
25
-
26
- export interface BubbleChartWidgetProps extends BaseWidgetProps<BubbleChartData> {
27
- showXAxis?: boolean;
28
- showYAxis?: boolean;
29
- showGrid?: boolean;
30
- showLegend?: boolean;
31
- minBubbleSize?: number;
32
- maxBubbleSize?: number;
33
- }
@@ -1,204 +0,0 @@
1
- /**
2
- * CandlestickChart Widget - For stock/financial data
3
- */
4
- import React, { memo, useMemo } from 'react';
5
- import { View, Text as RNText, StyleSheet } from 'react-native';
6
- import Svg, { Rect, Line as SvgLine } from 'react-native-svg';
7
- import { useWidgetDimensions, useWidgetTheme, normalize, formatTimeLabel, reduceLabels } from '../../core';
8
- import { CandlestickChartWidgetProps } from './types';
9
-
10
- export const CandlestickChart = memo<CandlestickChartWidgetProps>(({
11
- data: widgetData,
12
- width,
13
- height,
14
- loading = false,
15
- theme: themeOverride,
16
- showXAxis = true,
17
- showYAxis = true,
18
- showGrid = true,
19
- candleWidth = 8,
20
- candleSpacing = 4,
21
- upColor,
22
- downColor,
23
- maxCandles = 30,
24
- testID,
25
- }) => {
26
- const theme = useWidgetTheme(themeOverride);
27
- const dimensions = useWidgetDimensions(width, height, 350, 300);
28
-
29
- if (loading) {
30
- return (
31
- <View style={[styles.container, { width: dimensions.width, height: dimensions.height, backgroundColor: theme.colors.surface, borderRadius: theme.radius.md }]} testID={`${testID}-loading`}>
32
- <RNText style={[styles.loadingText, { color: theme.colors.textSecondary }]}>Loading...</RNText>
33
- </View>
34
- );
35
- }
36
-
37
- if (!widgetData || !widgetData.data || widgetData.data.length === 0) {
38
- return (
39
- <View style={[styles.container, { width: dimensions.width, height: dimensions.height, backgroundColor: theme.colors.surface, borderRadius: theme.radius.md }]} testID={`${testID}-empty`}>
40
- <RNText style={[styles.emptyText, { color: theme.colors.textSecondary }]}>No data</RNText>
41
- </View>
42
- );
43
- }
44
-
45
- const { data, title, timeInterval = 'day' } = widgetData;
46
- const displayData = data.slice(-maxCandles); // Show last N candles
47
-
48
- const padding = theme.spacing.md;
49
- const titleHeight = title ? theme.fontScale.md + theme.spacing.sm : 0;
50
- const xAxisHeight = showXAxis ? 30 : 0;
51
- const yAxisWidth = showYAxis ? 40 : 0;
52
-
53
- const chartWidth = dimensions.width - padding * 2 - yAxisWidth;
54
- const chartHeight = dimensions.height - padding * 2 - titleHeight - xAxisHeight;
55
-
56
- const colorUp = upColor || theme.colors.chartPositive;
57
- const colorDown = downColor || theme.colors.chartNegative;
58
-
59
- // Find min/max prices
60
- const { minPrice, maxPrice } = useMemo(() => {
61
- const lows = displayData.map(d => d.low);
62
- const highs = displayData.map(d => d.high);
63
- return {
64
- minPrice: Math.min(...lows),
65
- maxPrice: Math.max(...highs),
66
- };
67
- }, [displayData]);
68
-
69
- const yAxisLabels = useMemo(() => {
70
- const range = maxPrice - minPrice;
71
- const step = range / 4;
72
- return Array.from({ length: 5 }, (_, i) => ({
73
- value: minPrice + i * step,
74
- y: chartHeight - (i * chartHeight) / 4,
75
- }));
76
- }, [minPrice, maxPrice, chartHeight]);
77
-
78
- const xLabels = useMemo(() => {
79
- return displayData.map(d => formatTimeLabel(d.date, timeInterval));
80
- }, [displayData, timeInterval]);
81
-
82
- const xAxisLabelsReduced = useMemo(() => {
83
- return reduceLabels(xLabels, 6);
84
- }, [xLabels]);
85
-
86
- const totalCandleWidth = candleWidth + candleSpacing;
87
- const chartStartX = Math.max(0, (chartWidth - displayData.length * totalCandleWidth) / 2);
88
-
89
- const candles = useMemo(() => {
90
- return displayData.map((point, index) => {
91
- const isUp = point.close >= point.open;
92
- const color = isUp ? colorUp : colorDown;
93
-
94
- // Normalize prices to chart height
95
- const highY = chartHeight - normalize(point.high, minPrice, maxPrice) * chartHeight;
96
- const lowY = chartHeight - normalize(point.low, minPrice, maxPrice) * chartHeight;
97
- const openY = chartHeight - normalize(point.open, minPrice, maxPrice) * chartHeight;
98
- const closeY = chartHeight - normalize(point.close, minPrice, maxPrice) * chartHeight;
99
-
100
- const candleX = chartStartX + index * totalCandleWidth;
101
- const candleCenterX = candleX + candleWidth / 2;
102
-
103
- // Candle body
104
- const bodyTop = Math.min(openY, closeY);
105
- const bodyHeight = Math.max(Math.abs(closeY - openY), 1); // Minimum 1px
106
-
107
- return {
108
- // High-Low line (wick)
109
- wickX: candleCenterX,
110
- wickTop: highY,
111
- wickBottom: lowY,
112
-
113
- // Body rectangle
114
- bodyX: candleX,
115
- bodyY: bodyTop,
116
- bodyWidth: candleWidth,
117
- bodyHeight: bodyHeight,
118
-
119
- color,
120
- isUp,
121
- };
122
- });
123
- }, [displayData, chartHeight, minPrice, maxPrice, colorUp, colorDown, candleWidth, totalCandleWidth, chartStartX]);
124
-
125
- return (
126
- <View style={[styles.wrapper, { width: dimensions.width, height: dimensions.height, backgroundColor: theme.colors.surface, borderRadius: theme.radius.md, padding }]} testID={testID}>
127
- {title && (
128
- <RNText style={[styles.title, { color: theme.colors.text, fontSize: theme.fontScale.md, fontWeight: 'bold', marginBottom: theme.spacing.sm }]}>
129
- {title}
130
- </RNText>
131
- )}
132
-
133
- <View style={styles.chartRow}>
134
- {showYAxis && (
135
- <View style={[styles.yAxis, { width: yAxisWidth }]}>
136
- {yAxisLabels.map((label, index) => (
137
- <RNText key={`y-${index}`} style={[styles.yAxisLabel, { color: theme.colors.textSecondary, fontSize: theme.fontScale.xs, top: label.y - 6 }]}>
138
- {label.value.toFixed(0)}
139
- </RNText>
140
- ))}
141
- </View>
142
- )}
143
-
144
- <View>
145
- <Svg width={chartWidth} height={chartHeight}>
146
- {showGrid && yAxisLabels.map((label, index) => (
147
- <SvgLine key={`grid-${index}`} x1={0} y1={label.y} x2={chartWidth} y2={label.y} stroke={theme.colors.borderLight} strokeWidth={1} />
148
- ))}
149
-
150
- {candles.map((candle, index) => (
151
- <React.Fragment key={`candle-${index}`}>
152
- {/* Wick (high-low line) */}
153
- <SvgLine
154
- x1={candle.wickX}
155
- y1={candle.wickTop}
156
- x2={candle.wickX}
157
- y2={candle.wickBottom}
158
- stroke={candle.color}
159
- strokeWidth={1}
160
- />
161
-
162
- {/* Body */}
163
- <Rect
164
- x={candle.bodyX}
165
- y={candle.bodyY}
166
- width={candle.bodyWidth}
167
- height={candle.bodyHeight}
168
- fill={candle.color}
169
- stroke={candle.color}
170
- strokeWidth={1}
171
- />
172
- </React.Fragment>
173
- ))}
174
- </Svg>
175
-
176
- {showXAxis && (
177
- <View style={[styles.xAxis, { width: chartWidth }]}>
178
- {xAxisLabelsReduced.map((item, index) => (
179
- <RNText key={`x-${index}`} style={[styles.xAxisLabel, { color: theme.colors.textSecondary, fontSize: theme.fontScale.xs, left: chartStartX + (item.index * totalCandleWidth) - 20 }]}>
180
- {item.label}
181
- </RNText>
182
- ))}
183
- </View>
184
- )}
185
- </View>
186
- </View>
187
- </View>
188
- );
189
- });
190
-
191
- CandlestickChart.displayName = 'CandlestickChart';
192
-
193
- const styles = StyleSheet.create({
194
- wrapper: { justifyContent: 'flex-start', alignItems: 'flex-start' },
195
- container: { justifyContent: 'center', alignItems: 'center' },
196
- loadingText: { fontSize: 16 },
197
- emptyText: { fontSize: 16 },
198
- title: { textAlign: 'center', width: '100%' },
199
- chartRow: { flexDirection: 'row', alignItems: 'flex-start' },
200
- yAxis: { position: 'relative', marginRight: 8 },
201
- yAxisLabel: { position: 'absolute', right: 0, textAlign: 'right' },
202
- xAxis: { position: 'relative', height: 30, marginTop: 4 },
203
- xAxisLabel: { position: 'absolute', width: 40, textAlign: 'center' },
204
- });
@@ -1,2 +0,0 @@
1
- export { CandlestickChart } from './CandlestickChart';
2
- export type { CandlestickChartWidgetProps, CandlestickChartData, CandlestickDataPoint } from './types';
@@ -1,29 +0,0 @@
1
- /**
2
- * CandlestickChart Widget types
3
- */
4
- import { BaseWidgetProps, TimeInterval } from '../../core';
5
-
6
- export interface CandlestickDataPoint {
7
- date: Date;
8
- open: number;
9
- high: number;
10
- low: number;
11
- close: number;
12
- }
13
-
14
- export interface CandlestickChartData {
15
- data: CandlestickDataPoint[];
16
- title?: string;
17
- timeInterval?: TimeInterval;
18
- }
19
-
20
- export interface CandlestickChartWidgetProps extends BaseWidgetProps<CandlestickChartData> {
21
- showXAxis?: boolean;
22
- showYAxis?: boolean;
23
- showGrid?: boolean;
24
- candleWidth?: number;
25
- candleSpacing?: number;
26
- upColor?: string;
27
- downColor?: string;
28
- maxCandles?: number;
29
- }
@@ -1,172 +0,0 @@
1
- /**
2
- * FunnelChart Widget - Conversion funnel visualization
3
- */
4
- import React, { memo, useMemo } from 'react';
5
- import { View, Text as RNText, StyleSheet } from 'react-native';
6
- import Svg, { Polygon } from 'react-native-svg';
7
- import { useWidgetDimensions, useWidgetTheme, normalize } from '../../core';
8
- import { FunnelChartWidgetProps } from './types';
9
-
10
- export const FunnelChart = memo<FunnelChartWidgetProps>(({
11
- data: widgetData,
12
- width,
13
- height,
14
- loading = false,
15
- theme: themeOverride,
16
- showLabels = true,
17
- showValues = true,
18
- showPercentages = true,
19
- stageSpacing = 8,
20
- testID,
21
- }) => {
22
- const theme = useWidgetTheme(themeOverride);
23
- const dimensions = useWidgetDimensions(width, height, 300, 400);
24
-
25
- if (loading) {
26
- return (
27
- <View style={[styles.container, { width: dimensions.width, height: dimensions.height, backgroundColor: theme.colors.surface, borderRadius: theme.radius.md }]} testID={`${testID}-loading`}>
28
- <RNText style={[styles.loadingText, { color: theme.colors.textSecondary }]}>Loading...</RNText>
29
- </View>
30
- );
31
- }
32
-
33
- if (!widgetData || !widgetData.stages || widgetData.stages.length === 0) {
34
- return (
35
- <View style={[styles.container, { width: dimensions.width, height: dimensions.height, backgroundColor: theme.colors.surface, borderRadius: theme.radius.md }]} testID={`${testID}-empty`}>
36
- <RNText style={[styles.emptyText, { color: theme.colors.textSecondary }]}>No data</RNText>
37
- </View>
38
- );
39
- }
40
-
41
- const { stages, title } = widgetData;
42
- const padding = theme.spacing.md;
43
- const titleHeight = title ? theme.fontScale.md + theme.spacing.sm : 0;
44
- const labelWidth = showLabels ? 100 : 0;
45
-
46
- const chartWidth = dimensions.width - padding * 2 - labelWidth;
47
- const chartHeight = dimensions.height - padding * 2 - titleHeight;
48
-
49
- const maxValue = stages[0].value;
50
- const totalSpacing = (stages.length - 1) * stageSpacing;
51
- const stageHeight = (chartHeight - totalSpacing) / stages.length;
52
-
53
- const funnelStages = useMemo(() => {
54
- return stages.map((stage, index) => {
55
- const normalizedValue = normalize(stage.value, 0, maxValue);
56
- const topWidth = normalizedValue * chartWidth;
57
- const bottomWidth = index < stages.length - 1
58
- ? normalize(stages[index + 1].value, 0, maxValue) * chartWidth
59
- : topWidth * 0.7; // Last stage tapers to 70%
60
-
61
- const y = index * (stageHeight + stageSpacing);
62
- const centerX = chartWidth / 2;
63
-
64
- // Create trapezoid points (top-left, top-right, bottom-right, bottom-left)
65
- const points = `
66
- ${centerX - topWidth / 2},${y}
67
- ${centerX + topWidth / 2},${y}
68
- ${centerX + bottomWidth / 2},${y + stageHeight}
69
- ${centerX - bottomWidth / 2},${y + stageHeight}
70
- `;
71
-
72
- const percentage = ((stage.value / maxValue) * 100).toFixed(1);
73
- const conversionRate = index > 0
74
- ? ((stage.value / stages[index - 1].value) * 100).toFixed(1)
75
- : '100.0';
76
-
77
- return {
78
- points,
79
- color: stage.color || getDefaultColor(index, theme),
80
- label: stage.label,
81
- value: stage.value,
82
- percentage,
83
- conversionRate,
84
- y: y + stageHeight / 2,
85
- };
86
- });
87
- }, [stages, chartWidth, chartHeight, stageHeight, stageSpacing, maxValue, theme]);
88
-
89
- return (
90
- <View style={[styles.wrapper, { width: dimensions.width, height: dimensions.height, backgroundColor: theme.colors.surface, borderRadius: theme.radius.md, padding }]} testID={testID}>
91
- {title && (
92
- <RNText style={[styles.title, { color: theme.colors.text, fontSize: theme.fontScale.md, fontWeight: 'bold', marginBottom: theme.spacing.sm }]}>
93
- {title}
94
- </RNText>
95
- )}
96
-
97
- <View style={styles.chartRow}>
98
- <View>
99
- <Svg width={chartWidth} height={chartHeight}>
100
- {funnelStages.map((stage, index) => (
101
- <Polygon
102
- key={`stage-${index}`}
103
- points={stage.points}
104
- fill={stage.color}
105
- opacity={0.9}
106
- />
107
- ))}
108
- </Svg>
109
- </View>
110
-
111
- {showLabels && (
112
- <View style={[styles.labelsContainer, { width: labelWidth, height: chartHeight }]}>
113
- {funnelStages.map((stage, index) => (
114
- <View
115
- key={`label-${index}`}
116
- style={[styles.labelItem, { top: stage.y - 20, height: 40 }]}
117
- >
118
- <RNText
119
- style={[styles.label, { color: theme.colors.text, fontSize: theme.fontScale.sm }]}
120
- numberOfLines={1}
121
- >
122
- {stage.label}
123
- </RNText>
124
- {showValues && (
125
- <RNText
126
- style={[styles.value, { color: theme.colors.textSecondary, fontSize: theme.fontScale.xs }]}
127
- >
128
- {stage.value.toLocaleString()}
129
- </RNText>
130
- )}
131
- {showPercentages && (
132
- <RNText
133
- style={[styles.percentage, { color: theme.colors.textSecondary, fontSize: theme.fontScale.xs }]}
134
- >
135
- {index > 0 ? `${stage.conversionRate}%` : '100%'}
136
- </RNText>
137
- )}
138
- </View>
139
- ))}
140
- </View>
141
- )}
142
- </View>
143
- </View>
144
- );
145
- });
146
-
147
- FunnelChart.displayName = 'FunnelChart';
148
-
149
- function getDefaultColor(index: number, theme: any): string {
150
- const colors = [
151
- theme.colors.chartPrimary,
152
- theme.colors.chartSecondary,
153
- theme.colors.chartTertiary,
154
- theme.colors.chartQuaternary,
155
- theme.colors.chartPositive,
156
- ];
157
- return colors[index % colors.length];
158
- }
159
-
160
- const styles = StyleSheet.create({
161
- wrapper: { justifyContent: 'flex-start', alignItems: 'flex-start' },
162
- container: { justifyContent: 'center', alignItems: 'center' },
163
- loadingText: { fontSize: 16 },
164
- emptyText: { fontSize: 16 },
165
- title: { textAlign: 'center', width: '100%' },
166
- chartRow: { flexDirection: 'row', flex: 1 },
167
- labelsContainer: { position: 'relative', marginLeft: 12 },
168
- labelItem: { position: 'absolute', justifyContent: 'center' },
169
- label: { fontWeight: '600', marginBottom: 2 },
170
- value: { marginBottom: 1 },
171
- percentage: { fontWeight: '500' },
172
- });
@@ -1,2 +0,0 @@
1
- export { FunnelChart } from './FunnelChart';
2
- export type { FunnelChartWidgetProps, FunnelChartData, FunnelStage } from './types';
@@ -1,22 +0,0 @@
1
- /**
2
- * FunnelChart Widget types
3
- */
4
- import { BaseWidgetProps } from '../../core';
5
-
6
- export interface FunnelStage {
7
- label: string;
8
- value: number;
9
- color?: string;
10
- }
11
-
12
- export interface FunnelChartData {
13
- stages: FunnelStage[];
14
- title?: string;
15
- }
16
-
17
- export interface FunnelChartWidgetProps extends BaseWidgetProps<FunnelChartData> {
18
- showLabels?: boolean;
19
- showValues?: boolean;
20
- showPercentages?: boolean;
21
- stageSpacing?: number;
22
- }