Skip to content

Commit f677a3e

Browse files
committed
feat: add animated text example, fix computation
1 parent d8740f5 commit f677a3e

File tree

11 files changed

+136
-51
lines changed

11 files changed

+136
-51
lines changed

example/app/index.tsx

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,20 @@
1-
import { type AxisLabelComponentProps, LineChart } from "@codeherence/react-native-graph";
2-
import { useCallback, useState } from "react";
1+
import {
2+
type AxisLabelComponentProps,
3+
LineChart,
4+
type PanGestureHandlerOnChangeEventPayload,
5+
HoverGestureHandlerOnChangeEventPayload,
6+
} from "@codeherence/react-native-graph";
7+
import { useCallback, useEffect, useState } from "react";
38
import { Button, StyleSheet, Text, View } from "react-native";
9+
import { useAnimatedProps, useSharedValue } from "react-native-reanimated";
410
import { useSafeAreaInsets } from "react-native-safe-area-context";
5-
import { LineChartProps } from "src/components/LineChart";
11+
12+
import AnimatedText from "../components/AnimatedText";
613

714
const generateRandomData = (): [number, number][] => {
815
return Array.from({ length: 30 }, (_, i) => [i, Math.random() * 2000]);
916
};
1017

11-
type FilterUndefined<T> = T extends undefined ? never : T;
12-
13-
const onGestureChangeWorklet: FilterUndefined<LineChartProps["onPanGestureChange"]> = ({
14-
point,
15-
}) => {
16-
"worklet";
17-
console.log(point);
18-
};
19-
20-
const onHoverChangeWorklet: FilterUndefined<LineChartProps["onHoverGestureChange"]> = ({
21-
point,
22-
}) => {
23-
"worklet";
24-
console.log(point);
25-
};
26-
2718
const formatter = new Intl.NumberFormat("en-US", {
2819
style: "currency",
2920
currency: "USD",
@@ -33,22 +24,61 @@ const AxisLabel: React.FC<AxisLabelComponentProps> = ({ value }) => (
3324
<Text>{formatter.format(value)}</Text>
3425
);
3526

27+
const uiFormatter = (price: number) => {
28+
"worklet";
29+
30+
return new Intl.NumberFormat("en-US", {
31+
style: "currency",
32+
currency: "USD",
33+
}).format(price);
34+
};
35+
3636
export default () => {
3737
const { top, bottom } = useSafeAreaInsets();
38-
const [data, setData] = useState<[number, number][]>(generateRandomData());
38+
const [data, setData] = useState<[number, number][]>([]);
39+
40+
const latestPrice = useSharedValue("0");
3941

4042
// Randomize the data
41-
const handlePress = useCallback(() => setData(generateRandomData()), []);
43+
const refreshData = useCallback(() => {
44+
const newData = generateRandomData();
45+
latestPrice.value = formatter.format(newData[newData.length - 1]![1]);
46+
setData(newData);
47+
}, []);
48+
49+
useEffect(refreshData, []);
50+
51+
const onHoverChangeWorklet = useCallback((evt: HoverGestureHandlerOnChangeEventPayload) => {
52+
"worklet";
53+
latestPrice.value = uiFormatter(evt.point);
54+
}, []);
55+
56+
const onGestureChangeWorklet = useCallback((evt: PanGestureHandlerOnChangeEventPayload) => {
57+
"worklet";
58+
latestPrice.value = uiFormatter(evt.point);
59+
}, []);
60+
61+
const onEndWorklet = useCallback(() => {
62+
"worklet";
63+
latestPrice.value = uiFormatter(data[data.length - 1]![1]);
64+
}, [data]);
65+
66+
const animatedProps = useAnimatedProps(() => {
67+
return { text: latestPrice.value };
68+
}, []);
4269

4370
return (
4471
<View style={[styles.container, { paddingTop: top, paddingBottom: bottom }]}>
45-
<Button title="Randomize data" onPress={handlePress} />
72+
<Button title="Randomize data" onPress={refreshData} />
73+
<AnimatedText style={styles.price} animatedProps={animatedProps} />
4674
<LineChart
4775
points={data}
4876
style={styles.chart}
4977
TopAxisLabel={AxisLabel}
5078
BottomAxisLabel={AxisLabel}
5179
onPanGestureChange={onGestureChangeWorklet}
80+
onPanGestureEnd={onEndWorklet}
81+
onHoverGestureEnd={onEndWorklet}
5282
onHoverGestureChange={onHoverChangeWorklet}
5383
/>
5484
</View>
@@ -57,5 +87,6 @@ export default () => {
5787

5888
const styles = StyleSheet.create({
5989
container: { flex: 1 },
60-
chart: { flex: 1, maxHeight: "40%" },
90+
chart: { flex: 1 },
91+
price: { fontSize: 32 },
6192
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import AnimatedText from "react-native-animateable-text";
2+
3+
export default AnimatedText;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default () => <></>;

example/ios/Podfile.lock

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,8 @@ PODS:
10121012
- React-Mapbuffer (0.73.4):
10131013
- glog
10141014
- React-debug
1015+
- react-native-animateable-text (0.11.1):
1016+
- React-Core
10151017
- react-native-safe-area-context (4.8.2):
10161018
- React-Core
10171019
- react-native-skia (1.0.2):
@@ -1263,6 +1265,7 @@ DEPENDENCIES:
12631265
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`)
12641266
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
12651267
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
1268+
- react-native-animateable-text (from `../node_modules/react-native-animateable-text`)
12661269
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
12671270
- "react-native-skia (from `../node_modules/@shopify/react-native-skia`)"
12681271
- React-nativeconfig (from `../node_modules/react-native/ReactCommon`)
@@ -1396,6 +1399,8 @@ EXTERNAL SOURCES:
13961399
:path: "../node_modules/react-native/ReactCommon/logger"
13971400
React-Mapbuffer:
13981401
:path: "../node_modules/react-native/ReactCommon"
1402+
react-native-animateable-text:
1403+
:path: "../node_modules/react-native-animateable-text"
13991404
react-native-safe-area-context:
14001405
:path: "../node_modules/react-native-safe-area-context"
14011406
react-native-skia:
@@ -1501,6 +1506,7 @@ SPEC CHECKSUMS:
15011506
React-jsinspector: 9ac353eccf6ab54d1e0a33862ba91221d1e88460
15021507
React-logger: 0a57b68dd2aec7ff738195f081f0520724b35dab
15031508
React-Mapbuffer: 63913773ed7f96b814a2521e13e6d010282096ad
1509+
react-native-animateable-text: 79b31ec28f4ed14dab76b304eb74a24eeb2fa9c0
15041510
react-native-safe-area-context: 0ee144a6170530ccc37a0fd9388e28d06f516a89
15051511
react-native-skia: 0c09a4baff5db09a88dc6c9bfba25bcf4f5bbe8a
15061512
React-nativeconfig: d7af5bae6da70fa15ce44f045621cf99ed24087c

example/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"react": "18.2.0",
2626
"react-dom": "18.2.0",
2727
"react-native": "0.73.4",
28+
"react-native-animateable-text": "^0.11.1",
2829
"react-native-gesture-handler": "^2.15.0",
2930
"react-native-reanimated": "^3.7.2",
3031
"react-native-safe-area-context": "4.8.2",

example/yarn.lock

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2153,7 +2153,7 @@
21532153
hermes-parser "0.15.0"
21542154
nullthrows "^1.1.1"
21552155

2156-
"@react-native/normalize-color@^2.0.0", "@react-native/normalize-color@^2.1.0":
2156+
"@react-native/normalize-color@*", "@react-native/normalize-color@^2.0.0", "@react-native/normalize-color@^2.1.0":
21572157
version "2.1.0"
21582158
resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-2.1.0.tgz#939b87a9849e81687d3640c5efa2a486ac266f91"
21592159
integrity sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA==
@@ -4452,6 +4452,15 @@ depd@~1.1.2:
44524452
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
44534453
integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
44544454

4455+
deprecated-react-native-prop-types@^2.3.0:
4456+
version "2.3.0"
4457+
resolved "https://registry.yarnpkg.com/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-2.3.0.tgz#c10c6ee75ff2b6de94bb127f142b814e6e08d9ab"
4458+
integrity sha512-pWD0voFtNYxrVqvBMYf5gq3NA2GCpfodS1yNynTPc93AYA/KEMGeWDqqeUB6R2Z9ZofVhks2aeJXiuQqKNpesA==
4459+
dependencies:
4460+
"@react-native/normalize-color" "*"
4461+
invariant "*"
4462+
prop-types "*"
4463+
44554464
deprecated-react-native-prop-types@^5.0.0:
44564465
version "5.0.0"
44574466
resolved "https://registry.yarnpkg.com/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-5.0.0.tgz#02a12f090da7bd9e8c3ac53c31cf786a1315d302"
@@ -6200,7 +6209,7 @@ internal-slot@^1.0.5, internal-slot@^1.0.7:
62006209
hasown "^2.0.0"
62016210
side-channel "^1.0.4"
62026211

6203-
invariant@^2.2.4:
6212+
invariant@*, invariant@^2.2.4:
62046213
version "2.2.4"
62056214
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
62066215
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
@@ -8455,7 +8464,7 @@ prompts@^2.3.2, prompts@^2.4.2:
84558464
kleur "^3.0.3"
84568465
sisteransi "^1.0.5"
84578466

8458-
prop-types@^15.7.2, prop-types@^15.8.1:
8467+
prop-types@*, prop-types@^15.7.2, prop-types@^15.8.1:
84598468
version "15.8.1"
84608469
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
84618470
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -8603,6 +8612,14 @@ react-is@^17.0.1:
86038612
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
86048613
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
86058614

8615+
react-native-animateable-text@^0.11.1:
8616+
version "0.11.1"
8617+
resolved "https://registry.yarnpkg.com/react-native-animateable-text/-/react-native-animateable-text-0.11.1.tgz#5a7f8cf7302157dd49c8f93998f672a6f02169e8"
8618+
integrity sha512-IybfSDsk+Wiv6aWqt4kZZA+KxmDWHQLq2JIRfTqO1GrsQTIXBCgMAxcO0u4Kw486PxaekXKBRV0wpOIEA7P89g==
8619+
dependencies:
8620+
deprecated-react-native-prop-types "^2.3.0"
8621+
nullthrows "^1.1.1"
8622+
86068623
react-native-gesture-handler@^2.15.0:
86078624
version "2.15.0"
86088625
resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-2.15.0.tgz#f8e6c0451a7bdf065edb7b9be605480db402baa0"

src/components/LineChart/computations.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,9 @@ export const computeGraphData = (points: [number, number][]) => {
163163
const [minValueIndex, minValue] = getMinValue({ points });
164164
const [maxValueIndex, maxValue] = getMaxValue({ points });
165165

166-
const minValueXProportion = minValueIndex / points.length;
167-
const maxValueXProportion = maxValueIndex / points.length;
166+
// We subtract 1 since the index is 0-based
167+
const minValueXProportion = minValueIndex / (points.length - 1);
168+
const maxValueXProportion = maxValueIndex / (points.length - 1);
168169

169170
return {
170171
points,

src/components/LineChart/index.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,15 @@ import {
1313
DEFAULT_FORMATTER,
1414
DEFAULT_STROKE_WIDTH,
1515
} from "./constants";
16-
import { useGestures, type UseGestureProps } from "./useGestures";
17-
18-
type FilterNull<T> = T extends null ? never : T;
16+
import {
17+
HoverGestureHandlerOnBeginEventPayload,
18+
HoverGestureHandlerOnChangeEventPayload,
19+
HoverGestureHandlerOnEndEventPayload,
20+
PanGestureHandlerEventPayload,
21+
PanGestureHandlerOnBeginEventPayload,
22+
PanGestureHandlerOnChangeEventPayload,
23+
useGestures,
24+
} from "./useGestures";
1925

2026
export type LineChartProps = ViewProps & {
2127
/** Array of [x, y] points for the chart */
@@ -28,12 +34,12 @@ export type LineChartProps = ViewProps & {
2834
TopAxisLabel?: React.FC<AxisLabelComponentProps>;
2935
BottomAxisLabel?: React.FC<AxisLabelComponentProps>;
3036
/** Callback when the pan gesture begins. This function must be a worklet function. */
31-
onPanGestureBegin?: FilterNull<UseGestureProps["onPanGestureBegin"]>;
32-
onPanGestureChange?: FilterNull<UseGestureProps["onPanGestureChange"]>;
33-
onPanGestureEnd?: FilterNull<UseGestureProps["onPanGestureEnd"]>;
34-
onHoverGestureBegin?: FilterNull<UseGestureProps["onHoverGestureBegin"]>;
35-
onHoverGestureChange?: FilterNull<UseGestureProps["onHoverGestureChange"]>;
36-
onHoverGestureEnd?: FilterNull<UseGestureProps["onHoverGestureEnd"]>;
37+
onPanGestureBegin?: ((payload: PanGestureHandlerOnBeginEventPayload) => void) | null;
38+
onPanGestureChange?: ((payload: PanGestureHandlerOnChangeEventPayload) => void) | null;
39+
onPanGestureEnd?: ((payload: PanGestureHandlerEventPayload) => void) | null;
40+
onHoverGestureBegin?: ((payload: HoverGestureHandlerOnBeginEventPayload) => void) | null;
41+
onHoverGestureChange?: ((payload: HoverGestureHandlerOnChangeEventPayload) => void) | null;
42+
onHoverGestureEnd?: ((payload: HoverGestureHandlerOnEndEventPayload) => void) | null;
3743
};
3844

3945
export const LineChart: React.FC<LineChartProps> = ({

src/components/LineChart/useGestures.tsx

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,43 @@
11
import { SkPath } from "@shopify/react-native-skia";
22
import {
33
Gesture,
4-
type PanGestureHandlerEventPayload,
4+
type PanGestureHandlerEventPayload as ReanimatedPanGestureHandlerEventPayload,
55
type PanGestureChangeEventPayload,
66
} from "react-native-gesture-handler";
77
import { SharedValue, interpolate } from "react-native-reanimated";
88

99
import { getYForX } from "./computations";
1010

11-
type PanGestureHandlerOnBeginEventPayload = {
11+
export type PanGestureHandlerEventPayload = ReanimatedPanGestureHandlerEventPayload;
12+
export type PanGestureHandlerOnBeginEventPayload = {
1213
point: number;
1314
event: PanGestureHandlerEventPayload;
1415
};
15-
type PanGestureHandlerOnChangeEventPayload = {
16+
export type PanGestureHandlerOnChangeEventPayload = {
1617
point: number;
1718
event: PanGestureHandlerEventPayload & PanGestureChangeEventPayload;
1819
};
1920

2021
// Extract Hover Gesture onBegin args since it isn't exported by rngh
21-
type HoverGestureOnBegin = ReturnType<typeof Gesture.Hover>["onBegin"];
22-
type HoverGestureOnBeginCallBack = Parameters<HoverGestureOnBegin>[0];
23-
type HoverGestureHandlerOnBeginEventPayload = {
22+
export type HoverGestureOnBegin = ReturnType<typeof Gesture.Hover>["onBegin"];
23+
export type HoverGestureOnBeginCallBack = Parameters<HoverGestureOnBegin>[0];
24+
export type HoverGestureHandlerOnBeginEventPayload = {
2425
point: number;
2526
event: Parameters<HoverGestureOnBeginCallBack>[0];
2627
};
2728

2829
// Extract Hover Gesture onChange args since it isn't exported by rngh
29-
type HoverGestureOnChange = ReturnType<typeof Gesture.Hover>["onChange"];
30-
type HoverGestureOnChangeCallBack = Parameters<HoverGestureOnChange>[0];
31-
type HoverGestureHandlerOnChangeEventPayload = {
30+
export type HoverGestureOnChange = ReturnType<typeof Gesture.Hover>["onChange"];
31+
export type HoverGestureOnChangeCallBack = Parameters<HoverGestureOnChange>[0];
32+
export type HoverGestureHandlerOnChangeEventPayload = {
3233
point: number;
3334
event: Parameters<HoverGestureOnChangeCallBack>[0];
3435
};
3536

3637
// Extract Hover Gesture onEnd args since it isn't exported by rngh
37-
type HoverGestureOnEnd = ReturnType<typeof Gesture.Hover>["onEnd"];
38-
type HoverGestureOnEndCallBack = Parameters<HoverGestureOnEnd>[0];
39-
type HoverGestureHandlerOnEndEventPayload = Parameters<HoverGestureOnEndCallBack>[0];
38+
export type HoverGestureOnEnd = ReturnType<typeof Gesture.Hover>["onEnd"];
39+
export type HoverGestureOnEndCallBack = Parameters<HoverGestureOnEnd>[0];
40+
export type HoverGestureHandlerOnEndEventPayload = Parameters<HoverGestureOnEndCallBack>[0];
4041

4142
export interface UseGestureProps {
4243
x: SharedValue<number>;

src/components/index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
export { LineChart } from "./LineChart";
22
export type { LineChartProps } from "./LineChart";
3-
export { AxisLabelComponentProps } from "./LineChart/AxisLabel";
3+
export type { AxisLabelComponentProps } from "./LineChart/AxisLabel";
4+
export type {
5+
PanGestureHandlerOnBeginEventPayload,
6+
PanGestureHandlerOnChangeEventPayload,
7+
PanGestureHandlerEventPayload,
8+
HoverGestureHandlerOnBeginEventPayload,
9+
HoverGestureHandlerOnChangeEventPayload,
10+
HoverGestureHandlerOnEndEventPayload,
11+
} from "./LineChart/useGestures";

0 commit comments

Comments
 (0)