Skip to content

Commit 5ff9fd7

Browse files
committed
fixup! feat: graph events
1 parent 6e077a5 commit 5ff9fd7

8 files changed

Lines changed: 134 additions & 30 deletions

File tree

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -190,18 +190,15 @@ See this [example `<SelectionDot />` component](./example/src/components/CustomS
190190
### `EventComponent`
191191
A component that is used to render an event.
192192

193+
See this [example `<GraphEvent/>` component](./example/src/components/GraphEvent.tsx).
194+
193195
### `EventTooltipComponent`
194196
An additional event component that is rendered if the `SelectionDot` overlaps an `Event`.
195-
197+
See this [example `<GraphEventTooltip/>` component](./example/src/components/GraphEventTooltip.tsx).
196198
### `onEventHover`
197199
Callback called when an `Event` is hovered on.
198200

199-
> Events related props require `animated` and `enablePanGesture` to be `true`.
200-
201201
Example:
202-
203-
<img src="./img/events.png" align="right" height="200" />
204-
205202
```jsx
206203
<LineGraph
207204
points={priceHistory}
@@ -212,6 +209,10 @@ Example:
212209
EventComponent={DefaultEventComponent}
213210
/>
214211
```
212+
> Events related props require `animated` and `enablePanGesture` to be `true`.
213+
214+
<img src="./img/events.gif" align="right" height="250" />
215+
215216
## Sponsor
216217

217218
<img src="./img/pinkpanda.png" align="right" height="50">
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,54 @@
11
import React, { useEffect } from 'react'
22
import {
3+
runOnJS,
34
useDerivedValue,
45
useSharedValue,
56
withSpring,
67
withTiming,
78
} from 'react-native-reanimated'
89

9-
import { Circle, Group } from '@shopify/react-native-skia'
10-
11-
import { EventComponentProps } from './LineGraphProps'
10+
import {
11+
Circle,
12+
Group,
13+
TwoPointConicalGradient,
14+
vec,
15+
} from '@shopify/react-native-skia'
16+
import { EventComponentProps } from '../../../src/LineGraphProps'
1217

1318
const EVENT_SIZE = 6
1419
const ACTIVE_EVENT_SIZE = 8
1520
const ENTERING_ANIMATION_DURATION = 750
1621

17-
export function DefaultGraphEvent({
22+
export function GraphEvent({
1823
isGraphActive,
1924
fingerX,
2025
eventX,
2126
eventY,
2227
color,
28+
index,
29+
onEventHover,
2330
}: EventComponentProps) {
24-
const isEventActive = useDerivedValue(
25-
() =>
31+
const isEventActive = useDerivedValue(() => {
32+
// If the finger is on X position of the event.
33+
if (
2634
isGraphActive.value &&
2735
Math.abs(fingerX.value - eventX) < ACTIVE_EVENT_SIZE
28-
)
36+
) {
37+
if (onEventHover) runOnJS(onEventHover)(index, true)
38+
39+
return true
40+
}
41+
42+
if (onEventHover) runOnJS(onEventHover)(index, false)
43+
return false
44+
})
2945

3046
const dotRadius = useDerivedValue(() =>
3147
withSpring(isEventActive.value ? ACTIVE_EVENT_SIZE : EVENT_SIZE)
3248
)
33-
49+
const gradientEndRadius = useDerivedValue(() =>
50+
withSpring(dotRadius.value / 2)
51+
)
3452
const animatedOpacity = useSharedValue(0)
3553

3654
useEffect(() => {
@@ -42,7 +60,15 @@ export function DefaultGraphEvent({
4260

4361
return (
4462
<Group opacity={animatedOpacity}>
45-
<Circle cx={eventX} cy={eventY} r={dotRadius} color={color} />
63+
<Circle cx={eventX} cy={eventY} r={dotRadius} color={color}>
64+
<TwoPointConicalGradient
65+
start={vec(eventX, eventY)}
66+
startR={0}
67+
end={vec(eventX, eventY)}
68+
endR={gradientEndRadius}
69+
colors={['white', color]}
70+
/>
71+
</Circle>
4672
</Group>
4773
)
4874
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import React from 'react'
2+
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'
3+
import { Dimensions, Platform, StyleSheet, Text, View } from 'react-native'
4+
import { EventTooltipComponentProps } from '../../../src/LineGraphProps'
5+
6+
export type TransactionEventTooltipProps = EventTooltipComponentProps<{}>
7+
8+
const SCREEN_WIDTH = Dimensions.get('screen').width
9+
const ANIMATION_DURATION = 200
10+
const TOOLTIP_LEFT_OFFSET = 25
11+
const TOOLTIP_RIGHT_OFFSET = 145
12+
13+
export const GraphEventTooltip = ({
14+
eventX,
15+
eventY,
16+
}: TransactionEventTooltipProps) => {
17+
const tooltipPositionStyle = {
18+
left:
19+
eventX > SCREEN_WIDTH / 2
20+
? eventX - TOOLTIP_RIGHT_OFFSET
21+
: eventX + TOOLTIP_LEFT_OFFSET,
22+
top: eventY,
23+
}
24+
return (
25+
<Animated.View
26+
style={[styles.tooltip, tooltipPositionStyle]}
27+
entering={FadeIn.duration(ANIMATION_DURATION)}
28+
exiting={FadeOut.duration(ANIMATION_DURATION)}
29+
>
30+
<View style={styles.content}>
31+
<Text style={styles.textNote}>
32+
Here you can display {'\n'}
33+
any information you {'\n'}
34+
want about the event.
35+
</Text>
36+
</View>
37+
</Animated.View>
38+
)
39+
}
40+
41+
const styles = StyleSheet.create({
42+
tooltip: {
43+
position: 'absolute',
44+
backgroundColor: 'white',
45+
paddingHorizontal: 10,
46+
47+
borderRadius: 20,
48+
// add shadow based on platform
49+
...Platform.select({
50+
ios: {
51+
shadowColor: 'black',
52+
shadowOffset: { width: 0, height: 2 },
53+
shadowOpacity: 0.5,
54+
shadowRadius: 3,
55+
},
56+
android: {
57+
elevation: 3,
58+
},
59+
}),
60+
},
61+
content: {
62+
paddingVertical: 12,
63+
},
64+
textNote: {
65+
color: 'gray',
66+
fontSize: 10,
67+
},
68+
})

example/src/screens/GraphPage.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { LineGraph } from 'react-native-graph'
44
import StaticSafeAreaInsets from 'react-native-static-safe-area-insets'
55
import type { GraphRange } from '../../../src/LineGraphProps'
66
import { SelectionDot } from '../components/CustomSelectionDot'
7+
import { GraphEvent } from '../components/GraphEvent'
8+
import { GraphEventTooltip } from '../components/GraphEventTooltip'
79
import { Toggle } from '../components/Toggle'
810
import {
911
generateRandomGraphEvents,
@@ -34,6 +36,7 @@ export function GraphPage() {
3436
const [enableIndicator, setEnableIndicator] = useState(false)
3537
const [indicatorPulsating, setIndicatorPulsating] = useState(false)
3638
const [enableEvents, setEnableEvents] = useState(false)
39+
const [enableEventTooltip, setEnableEventTooltip] = useState(false)
3740

3841
const [points, setPoints] = useState(POINTS)
3942
const [events, setEvents] = useState(EVENTS)
@@ -109,6 +112,8 @@ export function GraphPage() {
109112
horizontalPadding={enableIndicator ? 15 : 0}
110113
indicatorPulsating={indicatorPulsating}
111114
events={enableEvents ? events : []}
115+
EventComponent={enableEvents ? GraphEvent : null}
116+
EventTooltipComponent={enableEventTooltip ? GraphEventTooltip : null}
112117
/>
113118

114119
<Button title="Refresh" onPress={refreshData} />
@@ -162,6 +167,11 @@ export function GraphPage() {
162167
isEnabled={enableEvents}
163168
setIsEnabled={setEnableEvents}
164169
/>
170+
<Toggle
171+
title="Enable event tooltip:"
172+
isEnabled={enableEventTooltip}
173+
setIsEnabled={setEnableEventTooltip}
174+
/>
165175
</ScrollView>
166176

167177
<View style={styles.spacer} />

img/events.gif

1.18 MB
Loading

img/events.png

-71.1 KB
Binary file not shown.

src/AnimatedLineGraph.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ import { getSixDigitHex } from './utils/getSixDigitHex'
4848
import { usePanGesture } from './hooks/usePanGesture'
4949
import { getYForX } from './GetYForX'
5050
import { hexToRgba } from './utils/hexToRgba'
51-
import { DefaultGraphEvent } from './DefaultGraphEvent'
5251
import { useEventTooltipProps } from './hooks/useEventTooltipProps'
5352

5453
const INDICATOR_RADIUS = 7
@@ -80,8 +79,8 @@ export function AnimatedLineGraph<TEventPayload extends object>({
8079
TopAxisLabel,
8180
BottomAxisLabel,
8281
events,
83-
EventComponent = DefaultGraphEvent,
84-
EventTooltipComponent,
82+
EventComponent = null,
83+
EventTooltipComponent = null,
8584
onEventHover,
8685
...props
8786
}: AnimatedLineGraphProps<TEventPayload>): React.ReactElement {

src/hooks/useEventTooltipProps.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from 'react'
1+
import { useCallback, useState } from 'react'
22

33
import {
44
EventTooltipComponentProps,
@@ -13,18 +13,18 @@ export const useEventTooltipProps = <TEventPayload extends object>(
1313
onEventHover?: () => void
1414
) => {
1515
const [activeEventIndex, setActiveEventIndex] = useState<number | null>(null)
16-
const handleDisplayEventTooltip = (
17-
eventIndex: number,
18-
isDisplayed: boolean
19-
) => {
20-
if (activeEventIndex === eventIndex && !isDisplayed)
21-
setActiveEventIndex(null)
16+
const handleDisplayEventTooltip = useCallback(
17+
(eventIndex: number, isDisplayed: boolean) => {
18+
if (activeEventIndex === eventIndex && !isDisplayed)
19+
setActiveEventIndex(null)
2220

23-
if (activeEventIndex === null && isDisplayed) {
24-
onEventHover?.()
25-
setActiveEventIndex(eventIndex)
26-
}
27-
}
21+
if (activeEventIndex === null && isDisplayed) {
22+
onEventHover?.()
23+
setActiveEventIndex(eventIndex)
24+
}
25+
},
26+
[activeEventIndex, onEventHover]
27+
)
2828
const activeEvent =
2929
eventsWithCords && typeof activeEventIndex === 'number'
3030
? eventsWithCords[activeEventIndex]

0 commit comments

Comments
 (0)