@bashem/rn-charts 0.0.4 → 0.0.5

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 (79) hide show
  1. package/README.md +1 -1
  2. package/lib/module/skia/AreaChart/AreaChart.js +60 -51
  3. package/lib/module/skia/AreaChart/AreaChart.js.map +1 -1
  4. package/lib/module/skia/AreaChart/useAreaChart.js +18 -14
  5. package/lib/module/skia/AreaChart/useAreaChart.js.map +1 -1
  6. package/lib/module/skia/BarChart/BarChart.js +156 -69
  7. package/lib/module/skia/BarChart/BarChart.js.map +1 -1
  8. package/lib/module/skia/BarChart/useBarChart.js +53 -41
  9. package/lib/module/skia/BarChart/useBarChart.js.map +1 -1
  10. package/lib/module/skia/Common/HorizontalLabelView.js +121 -0
  11. package/lib/module/skia/Common/HorizontalLabelView.js.map +1 -0
  12. package/lib/module/skia/Common/VerticalLabelView.js +100 -0
  13. package/lib/module/skia/Common/VerticalLabelView.js.map +1 -0
  14. package/lib/module/skia/Common/useComponentLayout.js +13 -0
  15. package/lib/module/skia/Common/useComponentLayout.js.map +1 -0
  16. package/lib/module/skia/HeatMap/DateHeatMap.js +472 -0
  17. package/lib/module/skia/HeatMap/DateHeatMap.js.map +1 -0
  18. package/lib/module/skia/HeatMap/HeatMap.js +121 -33
  19. package/lib/module/skia/HeatMap/HeatMap.js.map +1 -1
  20. package/lib/module/skia/HeatMap/useHeatMap.js +132 -48
  21. package/lib/module/skia/HeatMap/useHeatMap.js.map +1 -1
  22. package/lib/module/skia/PieChart/PieChart.js +21 -17
  23. package/lib/module/skia/PieChart/PieChart.js.map +1 -1
  24. package/lib/module/skia/PieChart/usePieChart.js +241 -30
  25. package/lib/module/skia/PieChart/usePieChart.js.map +1 -1
  26. package/lib/module/skia/Popup.js +7 -5
  27. package/lib/module/skia/Popup.js.map +1 -1
  28. package/lib/module/skia/common.js +4 -41
  29. package/lib/module/skia/common.js.map +1 -1
  30. package/lib/typescript/src/index.d.ts +1 -0
  31. package/lib/typescript/src/index.d.ts.map +1 -1
  32. package/lib/typescript/src/skia/AreaChart/AreaChart.d.ts +15 -3
  33. package/lib/typescript/src/skia/AreaChart/AreaChart.d.ts.map +1 -1
  34. package/lib/typescript/src/skia/AreaChart/useAreaChart.d.ts +13 -14
  35. package/lib/typescript/src/skia/AreaChart/useAreaChart.d.ts.map +1 -1
  36. package/lib/typescript/src/skia/BarChart/BarChart.d.ts +16 -2
  37. package/lib/typescript/src/skia/BarChart/BarChart.d.ts.map +1 -1
  38. package/lib/typescript/src/skia/BarChart/useBarChart.d.ts +17 -5
  39. package/lib/typescript/src/skia/BarChart/useBarChart.d.ts.map +1 -1
  40. package/lib/typescript/src/skia/Common/HorizontalLabelView.d.ts +33 -0
  41. package/lib/typescript/src/skia/Common/HorizontalLabelView.d.ts.map +1 -0
  42. package/lib/typescript/src/skia/Common/VerticalLabelView.d.ts +24 -0
  43. package/lib/typescript/src/skia/Common/VerticalLabelView.d.ts.map +1 -0
  44. package/lib/typescript/src/skia/Common/useComponentLayout.d.ts +4 -0
  45. package/lib/typescript/src/skia/Common/useComponentLayout.d.ts.map +1 -0
  46. package/lib/typescript/src/skia/HeatMap/DateHeatMap.d.ts +1 -0
  47. package/lib/typescript/src/skia/HeatMap/DateHeatMap.d.ts.map +1 -0
  48. package/lib/typescript/src/skia/HeatMap/HeatMap.d.ts +28 -12
  49. package/lib/typescript/src/skia/HeatMap/HeatMap.d.ts.map +1 -1
  50. package/lib/typescript/src/skia/HeatMap/useHeatMap.d.ts +15 -4
  51. package/lib/typescript/src/skia/HeatMap/useHeatMap.d.ts.map +1 -1
  52. package/lib/typescript/src/skia/PieChart/PieChart.d.ts +5 -1
  53. package/lib/typescript/src/skia/PieChart/PieChart.d.ts.map +1 -1
  54. package/lib/typescript/src/skia/PieChart/usePieChart.d.ts +6 -1
  55. package/lib/typescript/src/skia/PieChart/usePieChart.d.ts.map +1 -1
  56. package/lib/typescript/src/skia/Popup.d.ts.map +1 -1
  57. package/lib/typescript/src/skia/common.d.ts +3 -10
  58. package/lib/typescript/src/skia/common.d.ts.map +1 -1
  59. package/package.json +6 -3
  60. package/src/index.tsx +6 -4
  61. package/src/skia/AreaChart/AreaChart.tsx +85 -62
  62. package/src/skia/AreaChart/useAreaChart.ts +25 -26
  63. package/src/skia/BarChart/BarChart.tsx +163 -95
  64. package/src/skia/BarChart/useBarChart.ts +55 -44
  65. package/src/skia/Common/HorizontalLabelView.tsx +153 -0
  66. package/src/skia/Common/VerticalLabelView.tsx +113 -0
  67. package/src/skia/Common/useComponentLayout.ts +14 -0
  68. package/src/skia/HeatMap/DateHeatMap.tsx +470 -0
  69. package/src/skia/HeatMap/HeatMap.tsx +168 -54
  70. package/src/skia/HeatMap/useHeatMap.ts +139 -65
  71. package/src/skia/PieChart/PieChart.tsx +16 -11
  72. package/src/skia/PieChart/usePieChart.ts +316 -66
  73. package/src/skia/Popup.tsx +38 -36
  74. package/src/skia/common.ts +8 -46
  75. package/lib/module/skia/Common/VerticalLabel.js +0 -73
  76. package/lib/module/skia/Common/VerticalLabel.js.map +0 -1
  77. package/lib/typescript/src/skia/Common/VerticalLabel.d.ts +0 -17
  78. package/lib/typescript/src/skia/Common/VerticalLabel.d.ts.map +0 -1
  79. package/src/skia/Common/VerticalLabel.tsx +0 -91
@@ -1,16 +1,20 @@
1
1
  import { useMemo, useState } from "react";
2
- import { rect } from "@shopify/react-native-skia";
2
+ import { rect, type SkHostRect } from "@shopify/react-native-skia";
3
3
  import { arrayFrom, isDefined } from "../../util/util";
4
- import type { BarData, BarChartStyle, StackValue, BarChartProps } from "./BarChart";
4
+ import type { StackValue, BarChartProps } from "./BarChart";
5
5
  import { useWindowDimensions } from "react-native";
6
- import { getCommonStyleFont, getPaddings } from "../common";
6
+ import { getPaddings } from "../common";
7
+ import { useSharedValue } from "react-native-reanimated";
8
+ import { scheduleOnRN } from "react-native-worklets";
7
9
 
8
10
  export default function useBarChart(
9
11
  {
10
12
  data,
11
13
  style,
12
14
  maxValue,
13
- minValue
15
+ minValue,
16
+ yLabels,
17
+ overscanRatio
14
18
  }: BarChartProps
15
19
  ) {
16
20
  const { maxValueCalculated, minValueCalculated } = useMemo(() => {
@@ -47,7 +51,7 @@ export default function useBarChart(
47
51
  }, [data, maxValue]);
48
52
 
49
53
  const steps = useMemo(() => arrayFrom(1, 0.2), []);
50
- const [tooltip, setTooltip] = useState<{ centerX: number, centerY: number, data: StackValue; } | undefined>(undefined);
54
+ const [tooltip, setTooltip] = useState<{ centerX: number, centerY: number, rect: SkHostRect, data: StackValue, xLabel?: string; } | undefined>(undefined);
51
55
  const [startX, setStartX] = useState<number>(0);
52
56
 
53
57
  const {
@@ -59,25 +63,26 @@ export default function useBarChart(
59
63
 
60
64
  const chartBarWidth = style?.barWidth ?? 100;
61
65
  const chartBarSpacing = style?.barSpacing ?? 0;
62
- const verticalLabelWidth = 35;
66
+
67
+ const [verticalLabelWidth, setVerticalLabelWidth] = useState(style?.verticalLabelStyle?.width ?? 0);
63
68
  const chartHeight = style?.height ?? 200;
64
- const strokeWidth = 2;
65
- const bottomLabelHeight = 20;
66
- const canvasHeight = chartHeight + bottomLabelHeight;
69
+ const verticalStrokeWidth = style?.verticalLabelStyle?.strokeWidth ?? 0;
70
+ const horizontalStrokeWidth = style?.horizontalLabelStyle?.strokeWidth ?? 0;
71
+ const [bottomLabelHeight, setBottomLabelHeight] = useState(20);
67
72
  const { width: windowWidth } = useWindowDimensions();
68
73
  const totalWidth = style?.width ?? windowWidth;
69
74
  const totalHeight = chartHeight;
70
-
75
+
71
76
  const initialSpacing = style?.firstBarLeadingSpacing ?? 0;
72
77
  const endSpacing = style?.lastBarTrailingSpacing ?? chartBarSpacing;
73
78
 
74
79
  const scrollAreaWidth = initialSpacing + data.length * chartBarWidth + (Math.max(0, data.length - 1) * chartBarSpacing) + endSpacing;
75
80
  const canvasWidth = Math.min(scrollAreaWidth, totalWidth - verticalLabelWidth - paddingRight - paddingLeft);
76
- const { font } = getCommonStyleFont(style);
81
+ const overscanArea = totalWidth * (overscanRatio ?? 0.5);
82
+ const leftBoundary = startX - overscanArea;
77
83
 
78
84
  const rectangles = useMemo(() => {
79
- let leftBoundary = Math.max(0, startX);
80
- let rightBoundary = startX + totalWidth;
85
+ let rightBoundary = startX + totalWidth + overscanArea;
81
86
 
82
87
  let startArrayIndex = Math.floor(Math.max(leftBoundary - initialSpacing, 0) / (chartBarWidth + chartBarSpacing));
83
88
  let endArrayIndex = Math.min(Math.ceil(rightBoundary / (chartBarWidth + chartBarSpacing)), data.length);
@@ -85,19 +90,19 @@ export default function useBarChart(
85
90
  return data.slice(startArrayIndex, endArrayIndex)
86
91
  .map((bar, xIndex) => {
87
92
  let previousHeight = 0;
88
- const x = initialSpacing + (xIndex + startArrayIndex) * (chartBarWidth + chartBarSpacing) - leftBoundary;
93
+ const x = initialSpacing + (xIndex + startArrayIndex) * (chartBarWidth + chartBarSpacing);
89
94
  return {
90
95
  bars: bar.values.map((item, yIndex) => {
91
96
  const barHeight =
92
97
  ((item.value - minValueCalculated) /
93
- (maxValueCalculated - minValueCalculated)) *
98
+ Math.max(maxValueCalculated - minValueCalculated, 1)) *
94
99
  chartHeight;
95
100
 
96
101
  const y =
97
- chartHeight - barHeight - previousHeight - strokeWidth;
102
+ chartHeight - barHeight - previousHeight;
98
103
 
99
104
  previousHeight += barHeight;
100
- return rect(x, y, chartBarWidth, barHeight);
105
+ return { rect: rect(x, y, chartBarWidth, barHeight), stackValue: item };
101
106
  }),
102
107
  label: bar.label,
103
108
  dataIndex: xIndex + startArrayIndex,
@@ -110,7 +115,6 @@ export default function useBarChart(
110
115
  chartBarSpacing,
111
116
  maxValueCalculated,
112
117
  minValueCalculated,
113
- strokeWidth,
114
118
  startX
115
119
  ]);
116
120
 
@@ -120,19 +124,18 @@ export default function useBarChart(
120
124
  return;
121
125
  }
122
126
 
123
- let xIndex = -1;
124
- let startingXIndex = 0;
125
-
126
- if (touchedX >= rectangles[0]!.x && touchedX <= rectangles[0]!.x + rectangles[0]!.bars[0]!.width) {
127
- xIndex = 0;
128
- startingXIndex = Math.max(0, rectangles[0]!.x);
129
- } else if (touchedX >= rectangles[0]!.x) {
130
- xIndex = Math.floor((touchedX - (rectangles[0]!.x + chartBarWidth) - chartBarSpacing) / (chartBarWidth + chartBarSpacing)) + 1;
131
- startingXIndex = rectangles[xIndex]!.x;
127
+ let firstX = rectangles[0]!.x - startX
128
+ let spaceBetween = touchedX - firstX
129
+ if(spaceBetween < 0) {
130
+ setTooltip(undefined);
131
+ return;
132
132
  }
133
133
 
134
- if (xIndex === -1 || (touchedX < rectangles[xIndex]!.x || touchedX > rectangles[xIndex]!.x + chartBarWidth)) {
135
- console.log('Touch is outside the bar width, ignoring.');
134
+ let xIndex = Math.floor(spaceBetween / (chartBarWidth + chartBarSpacing));
135
+ let startingXIndex = rectangles[xIndex]!.x - startX;
136
+
137
+ if (xIndex === -1 || (touchedX < rectangles[xIndex]!.x - startX|| touchedX > rectangles[xIndex]!.x - startX + chartBarWidth)) {
138
+ console.log('Touch is outside the bar width, ignoring.', xIndex);
136
139
  setTooltip(undefined);
137
140
  return;
138
141
  }
@@ -149,7 +152,7 @@ export default function useBarChart(
149
152
  ) {
150
153
  const barHeight =
151
154
  ((categoryData[yIndex]!.value - minValueCalculated) /
152
- (maxValueCalculated - minValueCalculated)) *
155
+ Math.max(maxValueCalculated - minValueCalculated, 1)) *
153
156
  chartHeight;
154
157
  yPassed += barHeight;
155
158
  lastBarHeight = barHeight;
@@ -163,28 +166,32 @@ export default function useBarChart(
163
166
  }
164
167
 
165
168
  setTooltip({
166
- centerX: startingXIndex + chartBarWidth / 2,
169
+ centerX: startingXIndex + verticalLabelWidth + paddingLeft + chartBarWidth / 2,
167
170
  centerY:
168
- chartHeight - yPassed - strokeWidth + lastBarHeight / 2,
171
+ chartHeight - yPassed + paddingTop + lastBarHeight / 2,
172
+ rect: rect(startingXIndex, chartHeight - yPassed, chartBarWidth, lastBarHeight),
169
173
  data: categoryData[yIndex - 1]!,
174
+ xLabel: data[xIndex]?.label
170
175
  });
171
176
  };
172
177
 
178
+ const offset = useSharedValue(0);
179
+
173
180
  function onScroll(translateX: number) {
174
- setTooltip(undefined);
175
- setStartX((prev) => {
176
- let newX = prev + translateX;
177
- if (newX < 0) return 0;
178
- if (newX + canvasWidth > scrollAreaWidth)
179
- return Math.max(0, scrollAreaWidth - canvasWidth);
180
- return newX;
181
- });
181
+ 'worklet';
182
+ let prev = offset.value;
183
+ let newX = Math.max(0, prev + translateX);
184
+ if (newX + canvasWidth > scrollAreaWidth)
185
+ newX = Math.max(0, scrollAreaWidth - canvasWidth);
186
+ offset.set(newX);
187
+ scheduleOnRN(setTooltip, undefined);
188
+ scheduleOnRN(setStartX, newX);
182
189
  }
183
190
 
184
191
  return {
192
+ offset,
185
193
  maxValueCalculated,
186
194
  minValueCalculated,
187
- canvasHeight,
188
195
  canvasWidth,
189
196
  steps,
190
197
  scrollAreaWidth,
@@ -194,17 +201,21 @@ export default function useBarChart(
194
201
  paddingLeft,
195
202
  paddingRight,
196
203
  verticalLabelWidth,
204
+ setVerticalLabelWidth,
197
205
  chartBarWidth,
198
206
  chartBarSpacing,
199
- strokeWidth,
200
207
  rectangles,
201
208
  tooltip,
202
209
  bottomLabelHeight,
203
- font,
210
+ setBottomLabelHeight,
204
211
  setTooltip,
205
212
  touchHandler,
206
213
  onScroll,
214
+ startX,
207
215
  totalHeight,
208
- totalWidth
216
+ totalWidth,
217
+ yLabels,
218
+ verticalStrokeWidth,
219
+ horizontalStrokeWidth
209
220
  };
210
221
  }
@@ -0,0 +1,153 @@
1
+ import { View, type LayoutChangeEvent } from "react-native";
2
+ import { getPaddings, type CommonStyle } from '../common';
3
+ import React, { useEffect, useState } from "react";
4
+ import { Canvas, Group, Line, type AnimatedProp, type Transforms3d } from "@shopify/react-native-skia";
5
+ import Animated, { useAnimatedStyle, type SharedValue } from "react-native-reanimated";
6
+
7
+ export interface HorizontalLabelStyle {
8
+ height?: number;
9
+ backgroundColor?: string;
10
+ strokeWidth?: number;
11
+ strokeColor?: string;
12
+ strokePosition?: "top" | "bottom";
13
+ }
14
+
15
+ export interface HorizontalLabelStyleExtended extends HorizontalLabelStyle {
16
+ viewPosition?: "top" | "bottom";
17
+ }
18
+
19
+ interface HorizontalLabelViewStyle extends CommonStyle {
20
+ width: number;
21
+ left?: number;
22
+ horizontalLabelStyle?: HorizontalLabelStyleExtended;
23
+ };
24
+
25
+ interface HorizontalLabelProps<T> {
26
+ labels: T[];
27
+ positions: number[];
28
+ style: HorizontalLabelViewStyle;
29
+ onLayout?: (event: LayoutChangeEvent) => void;
30
+ children?: (index: number, data: T) => React.JSX.Element | undefined;
31
+ labelSkiaView?: (
32
+ yPosition: number,
33
+ index: number,
34
+ data: T,
35
+ ) => React.JSX.Element | undefined;
36
+ transform?: AnimatedProp<Transforms3d>;
37
+ xOffset?: SharedValue<number>;
38
+ }
39
+
40
+ function HorizontalLabelView<T>({
41
+ labels,
42
+ positions,
43
+ style,
44
+ onLayout,
45
+ children,
46
+ labelSkiaView,
47
+ transform,
48
+ xOffset
49
+ }: HorizontalLabelProps<T>) {
50
+ const {
51
+ width,
52
+ backgroundColor,
53
+ left,
54
+ horizontalLabelStyle = {}
55
+ } = style;
56
+
57
+ const {
58
+ height,
59
+ strokeColor = "white",
60
+ strokePosition = "top",
61
+ strokeWidth = 0,
62
+ } = horizontalLabelStyle;
63
+ const { paddingLeft = 0 } = getPaddings(style);
64
+
65
+ const [maxHeight, setMaxHeight] = useState(height);
66
+ useEffect(() => {
67
+ setMaxHeight(height);
68
+ }, [height]);
69
+
70
+ return (
71
+ <View
72
+ style={{
73
+ left,
74
+ width: width,
75
+ height: (maxHeight ?? 0),
76
+ backgroundColor,
77
+ flexDirection: "row-reverse",
78
+ justifyContent: "flex-end",
79
+ overflow: "hidden"
80
+ }}
81
+ onLayout={(event) => {
82
+ onLayout?.(event);
83
+ }}
84
+ >
85
+ <View style={{ position: "relative", height: (maxHeight ?? 0), top: strokeWidth, paddingVertical: 0 }}>
86
+ {labels.map((label, index) => {
87
+ return <LabelViewWrapper
88
+ key={index}
89
+ leftPosition={positions[index]}
90
+ xOffset={xOffset}
91
+ onLayout={(event) => {
92
+ let height = event.nativeEvent.layout.height;
93
+ if (horizontalLabelStyle.height !== undefined)
94
+ setMaxHeight(horizontalLabelStyle.height);
95
+ else {
96
+ if (index === 0) {
97
+ setMaxHeight(height + strokeWidth);
98
+ } else {
99
+ setMaxHeight(prevHeight => Math.max(height + strokeWidth, prevHeight ?? 0));
100
+ }
101
+ }
102
+ }}
103
+ >
104
+ {children?.(index, label)}
105
+ </LabelViewWrapper>;
106
+ })}
107
+ </View>
108
+ {(labelSkiaView || strokeWidth > 0) &&
109
+ <Canvas style={{ position: "absolute", width, height: labelSkiaView ? (maxHeight ?? 0) : strokeWidth, }}>
110
+ <Group transform={transform}>
111
+ {labelSkiaView && labels.map((label, index) => {
112
+ let yPosition = positions[index];
113
+ if (yPosition === undefined) return null;
114
+ return <Group key={index}>{labelSkiaView(yPosition, index, label)}</Group>;
115
+ })}
116
+ {strokeWidth > 0 &&
117
+ <Line
118
+ p1={{ x: paddingLeft, y: strokePosition === "top" ? strokeWidth / 2 : (maxHeight ?? height ?? 0) - strokeWidth / 2 }}
119
+ p2={{ x: width, y: strokePosition === "top" ? strokeWidth / 2 : (maxHeight ?? height ?? 0) - strokeWidth / 2 }}
120
+ color={strokeColor}
121
+ strokeWidth={strokeWidth}
122
+ />
123
+ }
124
+ </Group>
125
+ </Canvas>
126
+ }
127
+ </View >
128
+ );
129
+ }
130
+
131
+ interface LabelViewWrapperProps {
132
+ leftPosition?: number;
133
+ xOffset?: SharedValue<number>;
134
+ onLayout: ((event: LayoutChangeEvent) => void) | undefined;
135
+ children?: React.JSX.Element | undefined;
136
+ };
137
+
138
+ function LabelViewWrapper(props: LabelViewWrapperProps) {
139
+ const labelContainerStyle = useAnimatedStyle(() => {
140
+ return {
141
+ position: "absolute", left: props.leftPosition ? props.leftPosition - (props.xOffset?.get() ?? 0) : undefined,
142
+ };
143
+ }, [props.leftPosition]);
144
+
145
+ return <Animated.View
146
+ style={labelContainerStyle}
147
+ onLayout={(event) => props.onLayout?.(event)}
148
+ >
149
+ {props.children}
150
+ </Animated.View >;
151
+ }
152
+
153
+ export default HorizontalLabelView;
@@ -0,0 +1,113 @@
1
+ import { View, type LayoutChangeEvent } from "react-native";
2
+ import { Canvas, Group, Line } from '@shopify/react-native-skia';
3
+ import { getPaddings, type CommonStyle } from '../common';
4
+ import React, { useMemo, useState } from "react";
5
+
6
+ export interface VerticalLabelStyle {
7
+ width?: number;
8
+ backgroundColor?: string;
9
+ strokeWidth?: number;
10
+ strokeColor?: string;
11
+ }
12
+
13
+ interface VerticalLabelViewStyles extends CommonStyle {
14
+ height: number;
15
+ top?: number;
16
+ verticalLabelStyle?: VerticalLabelStyle;
17
+ }
18
+
19
+ interface VerticalLabelProps {
20
+ labelPercentages: number[];
21
+ styles: VerticalLabelViewStyles;
22
+ onLayout?: (event: LayoutChangeEvent) => void;
23
+ children: (percentage: number, index: number) => React.JSX.Element | undefined;
24
+ labelSkiaView?: (
25
+ percentage: number,
26
+ yPosition: number,
27
+ index: number
28
+ ) => React.JSX.Element | undefined;
29
+ }
30
+
31
+ function VerticalLabelView({
32
+ labelSkiaView,
33
+ labelPercentages,
34
+ styles,
35
+ onLayout,
36
+ children
37
+ }: VerticalLabelProps) {
38
+ const {
39
+ top,
40
+ height,
41
+ backgroundColor,
42
+ verticalLabelStyle = {}
43
+ } = styles;
44
+ const {
45
+ width,
46
+ strokeWidth = 0,
47
+ strokeColor = 'white',
48
+ } = verticalLabelStyle;
49
+
50
+ const { paddingTop = 0, paddingBottom = 0 } = getPaddings(styles);
51
+ const [maxWidth, setMaxWidth] = useState(width);
52
+ useMemo(() => {
53
+ setMaxWidth(width);
54
+ }, [width]);
55
+
56
+ return (
57
+ <View
58
+ style={{
59
+ width: (maxWidth ?? 0),
60
+ top,
61
+ height,
62
+ backgroundColor,
63
+ flexDirection: "row-reverse",
64
+ justifyContent: "flex-end"
65
+ }}
66
+ onLayout={(event) => {
67
+ onLayout?.(event);
68
+ }}
69
+ >
70
+ <View style={{ position: "relative", width: (maxWidth ?? 0) - strokeWidth, paddingVertical: 0, height: height }}>
71
+ {labelPercentages.map((percentage, index) => {
72
+ return <View
73
+ key={percentage}
74
+ style={{ position: "absolute", top: (1 - percentage) * height, backgroundColor: "purple" }}
75
+ onLayout={(event) => {
76
+ let width = event.nativeEvent.layout.width + strokeWidth;
77
+ if (verticalLabelStyle.width !== undefined)
78
+ setMaxWidth(verticalLabelStyle.width);
79
+ else {
80
+ if (index === 0) {
81
+ setMaxWidth(width + strokeWidth);
82
+ } else {
83
+ setMaxWidth(prevWidth => Math.max(width + strokeWidth, prevWidth ?? 0));
84
+ }
85
+ }
86
+ }}
87
+ >
88
+ {children(percentage, index)}
89
+ </View>;
90
+ })}
91
+ </View>
92
+
93
+ {(labelSkiaView || strokeWidth > 0) &&
94
+ <Canvas style={{ position: "absolute", left: 0, width, height, }}>
95
+ {labelSkiaView && labelPercentages.map((percentage, index) => {
96
+ return <Group key={percentage}>{labelSkiaView(percentage, (1 - percentage) * height, index)}</Group>;
97
+ })}
98
+
99
+ {strokeWidth > 0 &&
100
+ <Line
101
+ p1={{ x: (maxWidth ?? width ?? 0) - strokeWidth / 2, y: paddingTop }}
102
+ p2={{ x: (maxWidth ?? width ?? 0) - strokeWidth / 2, y: height - paddingBottom }}
103
+ color={strokeColor}
104
+ strokeWidth={strokeWidth}
105
+ />
106
+ }
107
+ </Canvas>
108
+ }
109
+ </View>
110
+ );
111
+ }
112
+
113
+ export default VerticalLabelView;
@@ -0,0 +1,14 @@
1
+ import { useCallback, useState } from "react";
2
+ import type { LayoutChangeEvent, LayoutRectangle } from "react-native";
3
+
4
+ function useComponentLayout(): [LayoutRectangle | undefined, (event: LayoutChangeEvent) => void] {
5
+ const [size, setSize] = useState<LayoutRectangle | undefined>(undefined);
6
+
7
+ const onLayout = useCallback((event: LayoutChangeEvent) => {
8
+ setSize(event.nativeEvent.layout);
9
+ }, []);
10
+
11
+ return [size, onLayout];
12
+ };
13
+
14
+ export default useComponentLayout;