about summary refs log tree commit diff
path: root/src/view/com/pager/Pager.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/pager/Pager.tsx')
-rw-r--r--src/view/com/pager/Pager.tsx220
1 files changed, 114 insertions, 106 deletions
diff --git a/src/view/com/pager/Pager.tsx b/src/view/com/pager/Pager.tsx
index f62bffc53..8cc346903 100644
--- a/src/view/com/pager/Pager.tsx
+++ b/src/view/com/pager/Pager.tsx
@@ -1,16 +1,22 @@
-import React, {forwardRef, useCallback, useContext} from 'react'
+import {
+  useCallback,
+  useContext,
+  useImperativeHandle,
+  useRef,
+  useState,
+} from 'react'
 import {View} from 'react-native'
 import {DrawerGestureContext} from 'react-native-drawer-layout'
 import {Gesture, GestureDetector} from 'react-native-gesture-handler'
 import PagerView, {
-  PagerViewOnPageScrollEventData,
-  PagerViewOnPageSelectedEvent,
-  PagerViewOnPageSelectedEventData,
-  PageScrollStateChangedNativeEventData,
+  type PagerViewOnPageScrollEventData,
+  type PagerViewOnPageSelectedEvent,
+  type PagerViewOnPageSelectedEventData,
+  type PageScrollStateChangedNativeEventData,
 } from 'react-native-pager-view'
 import Animated, {
   runOnJS,
-  SharedValue,
+  type SharedValue,
   useEvent,
   useHandler,
   useSharedValue,
@@ -36,8 +42,12 @@ export interface RenderTabBarFnProps {
 export type RenderTabBarFn = (props: RenderTabBarFnProps) => JSX.Element
 
 interface Props {
+  ref?: React.Ref<PagerRef>
   initialPage?: number
   renderTabBar: RenderTabBarFn
+  // tab pressed, yet to scroll to page
+  onTabPressed?: (index: number) => void
+  // scroll settled
   onPageSelected?: (index: number) => void
   onPageScrollStateChanged?: (
     scrollState: 'idle' | 'dragging' | 'settling',
@@ -47,114 +57,112 @@ interface Props {
 
 const AnimatedPagerView = Animated.createAnimatedComponent(PagerView)
 
-export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>(
-  function PagerImpl(
-    {
-      children,
-      initialPage = 0,
-      renderTabBar,
-      onPageScrollStateChanged: parentOnPageScrollStateChanged,
-      onPageSelected: parentOnPageSelected,
-      testID,
-    }: React.PropsWithChildren<Props>,
-    ref,
-  ) {
-    const [selectedPage, setSelectedPage] = React.useState(initialPage)
-    const pagerView = React.useRef<PagerView>(null)
+export function Pager({
+  ref,
+  children,
+  initialPage = 0,
+  renderTabBar,
+  onPageSelected: parentOnPageSelected,
+  onTabPressed: parentOnTabPressed,
+  onPageScrollStateChanged: parentOnPageScrollStateChanged,
+  testID,
+}: React.PropsWithChildren<Props>) {
+  const [selectedPage, setSelectedPage] = useState(initialPage)
+  const pagerView = useRef<PagerView>(null)
 
-    const [isIdle, setIsIdle] = React.useState(true)
-    const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled()
-    useFocusEffect(
-      useCallback(() => {
-        const canSwipeDrawer = selectedPage === 0 && isIdle
-        setDrawerSwipeDisabled(!canSwipeDrawer)
-        return () => {
-          setDrawerSwipeDisabled(false)
-        }
-      }, [setDrawerSwipeDisabled, selectedPage, isIdle]),
-    )
+  const [isIdle, setIsIdle] = useState(true)
+  const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled()
+  useFocusEffect(
+    useCallback(() => {
+      const canSwipeDrawer = selectedPage === 0 && isIdle
+      setDrawerSwipeDisabled(!canSwipeDrawer)
+      return () => {
+        setDrawerSwipeDisabled(false)
+      }
+    }, [setDrawerSwipeDisabled, selectedPage, isIdle]),
+  )
 
-    React.useImperativeHandle(ref, () => ({
-      setPage: (index: number) => {
-        pagerView.current?.setPage(index)
-      },
-    }))
+  useImperativeHandle(ref, () => ({
+    setPage: (index: number) => {
+      pagerView.current?.setPage(index)
+    },
+  }))
 
-    const onPageSelectedJSThread = React.useCallback(
-      (nextPosition: number) => {
-        setSelectedPage(nextPosition)
-        parentOnPageSelected?.(nextPosition)
-      },
-      [setSelectedPage, parentOnPageSelected],
-    )
+  const onPageSelectedJSThread = useCallback(
+    (nextPosition: number) => {
+      setSelectedPage(nextPosition)
+      parentOnPageSelected?.(nextPosition)
+    },
+    [setSelectedPage, parentOnPageSelected],
+  )
 
-    const onTabBarSelect = React.useCallback(
-      (index: number) => {
-        pagerView.current?.setPage(index)
-      },
-      [pagerView],
-    )
+  const onTabBarSelect = useCallback(
+    (index: number) => {
+      parentOnTabPressed?.(index)
+      pagerView.current?.setPage(index)
+    },
+    [pagerView, parentOnTabPressed],
+  )
 
-    const dragState = useSharedValue<'idle' | 'settling' | 'dragging'>('idle')
-    const dragProgress = useSharedValue(selectedPage)
-    const didInit = useSharedValue(false)
-    const handlePageScroll = usePagerHandlers(
-      {
-        onPageScroll(e: PagerViewOnPageScrollEventData) {
-          'worklet'
-          if (didInit.get() === false) {
-            // On iOS, there's a spurious scroll event with 0 position
-            // even if a different page was supplied as the initial page.
-            // Ignore it and wait for the first confirmed selection instead.
-            return
-          }
-          dragProgress.set(e.offset + e.position)
-        },
-        onPageScrollStateChanged(e: PageScrollStateChangedNativeEventData) {
-          'worklet'
-          runOnJS(setIsIdle)(e.pageScrollState === 'idle')
-          if (dragState.get() === 'idle' && e.pageScrollState === 'settling') {
-            // This is a programmatic scroll on Android.
-            // Stay "idle" to match iOS and avoid confusing downstream code.
-            return
-          }
-          dragState.set(e.pageScrollState)
-          parentOnPageScrollStateChanged?.(e.pageScrollState)
-        },
-        onPageSelected(e: PagerViewOnPageSelectedEventData) {
-          'worklet'
-          didInit.set(true)
-          runOnJS(onPageSelectedJSThread)(e.position)
-        },
+  const dragState = useSharedValue<'idle' | 'settling' | 'dragging'>('idle')
+  const dragProgress = useSharedValue(selectedPage)
+  const didInit = useSharedValue(false)
+  const handlePageScroll = usePagerHandlers(
+    {
+      onPageScroll(e: PagerViewOnPageScrollEventData) {
+        'worklet'
+        if (didInit.get() === false) {
+          // On iOS, there's a spurious scroll event with 0 position
+          // even if a different page was supplied as the initial page.
+          // Ignore it and wait for the first confirmed selection instead.
+          return
+        }
+        dragProgress.set(e.offset + e.position)
+      },
+      onPageScrollStateChanged(e: PageScrollStateChangedNativeEventData) {
+        'worklet'
+        runOnJS(setIsIdle)(e.pageScrollState === 'idle')
+        if (dragState.get() === 'idle' && e.pageScrollState === 'settling') {
+          // This is a programmatic scroll on Android.
+          // Stay "idle" to match iOS and avoid confusing downstream code.
+          return
+        }
+        dragState.set(e.pageScrollState)
+        parentOnPageScrollStateChanged?.(e.pageScrollState)
+      },
+      onPageSelected(e: PagerViewOnPageSelectedEventData) {
+        'worklet'
+        didInit.set(true)
+        runOnJS(onPageSelectedJSThread)(e.position)
       },
-      [parentOnPageScrollStateChanged],
-    )
+    },
+    [parentOnPageScrollStateChanged],
+  )
 
-    const drawerGesture = useContext(DrawerGestureContext) ?? Gesture.Native() // noop for web
-    const nativeGesture =
-      Gesture.Native().requireExternalGestureToFail(drawerGesture)
+  const drawerGesture = useContext(DrawerGestureContext) ?? Gesture.Native() // noop for web
+  const nativeGesture =
+    Gesture.Native().requireExternalGestureToFail(drawerGesture)
 
-    return (
-      <View testID={testID} style={[a.flex_1, native(a.overflow_hidden)]}>
-        {renderTabBar({
-          selectedPage,
-          onSelect: onTabBarSelect,
-          dragProgress,
-          dragState,
-        })}
-        <GestureDetector gesture={nativeGesture}>
-          <AnimatedPagerView
-            ref={pagerView}
-            style={[a.flex_1]}
-            initialPage={initialPage}
-            onPageScroll={handlePageScroll}>
-            {children}
-          </AnimatedPagerView>
-        </GestureDetector>
-      </View>
-    )
-  },
-)
+  return (
+    <View testID={testID} style={[a.flex_1, native(a.overflow_hidden)]}>
+      {renderTabBar({
+        selectedPage,
+        onSelect: onTabBarSelect,
+        dragProgress,
+        dragState,
+      })}
+      <GestureDetector gesture={nativeGesture}>
+        <AnimatedPagerView
+          ref={pagerView}
+          style={[a.flex_1]}
+          initialPage={initialPage}
+          onPageScroll={handlePageScroll}>
+          {children}
+        </AnimatedPagerView>
+      </GestureDetector>
+    </View>
+  )
+}
 
 function usePagerHandlers(
   handlers: {