about summary refs log tree commit diff
path: root/src/view/com/util/anim/TriggerableAnimated.tsx
blob: 2a3cbb957b48505851102baaef0be38c146350f0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import React from 'react'
import {Animated, StyleProp, View, ViewStyle} from 'react-native'
import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'

type CreateAnimFn = (interp: Animated.Value) => Animated.CompositeAnimation
type FinishCb = () => void

interface TriggeredAnimation {
  start: CreateAnimFn
  style: (
    interp: Animated.Value,
  ) => Animated.WithAnimatedValue<StyleProp<ViewStyle>>
}

export interface TriggerableAnimatedRef {
  trigger: (anim: TriggeredAnimation, onFinish?: FinishCb) => void
}

type TriggerableAnimatedProps = React.PropsWithChildren<{}>

type PropsInner = TriggerableAnimatedProps & {
  anim: TriggeredAnimation
  onFinish: () => void
}

export const TriggerableAnimated = React.forwardRef<
  TriggerableAnimatedRef,
  TriggerableAnimatedProps
>(({children, ...props}, ref) => {
  const [anim, setAnim] = React.useState<TriggeredAnimation | undefined>(
    undefined,
  )
  const [finishCb, setFinishCb] = React.useState<FinishCb | undefined>(
    undefined,
  )
  React.useImperativeHandle(ref, () => ({
    trigger(v: TriggeredAnimation, cb?: FinishCb) {
      setFinishCb(() => cb) // note- wrap in function due to react behaviors around setstate
      setAnim(v)
    },
  }))
  const onFinish = () => {
    finishCb?.()
    setAnim(undefined)
    setFinishCb(undefined)
  }
  return (
    <View key="triggerable">
      {anim ? (
        <AnimatingView anim={anim} onFinish={onFinish} {...props}>
          {children}
        </AnimatingView>
      ) : (
        children
      )}
    </View>
  )
})

function AnimatingView({
  anim,
  onFinish,
  children,
}: React.PropsWithChildren<PropsInner>) {
  const interp = useAnimatedValue(0)
  React.useEffect(() => {
    anim?.start(interp).start(() => {
      onFinish()
    })
  })
  const animStyle = anim?.style(interp)
  return <Animated.View style={animStyle}>{children}</Animated.View>
}