about summary refs log tree commit diff
path: root/src/view/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com')
-rw-r--r--src/view/com/pager/PagerHeaderContext.tsx30
-rw-r--r--src/view/com/pager/PagerWithHeader.tsx25
-rw-r--r--src/view/com/pager/PagerWithHeader.web.tsx16
3 files changed, 52 insertions, 19 deletions
diff --git a/src/view/com/pager/PagerHeaderContext.tsx b/src/view/com/pager/PagerHeaderContext.tsx
index fd4cc7463..c979f7a6d 100644
--- a/src/view/com/pager/PagerHeaderContext.tsx
+++ b/src/view/com/pager/PagerHeaderContext.tsx
@@ -1,40 +1,48 @@
 import React, {useContext} from 'react'
 import {SharedValue} from 'react-native-reanimated'
 
-import {isIOS} from '#/platform/detection'
+import {isNative} from '#/platform/detection'
 
-export const PagerHeaderContext =
-  React.createContext<SharedValue<number> | null>(null)
+export const PagerHeaderContext = React.createContext<{
+  scrollY: SharedValue<number>
+  headerHeight: number
+} | null>(null)
 
 /**
- * Passes the scrollY value to the pager header's banner, so it can grow on
- * overscroll on iOS. Not necessary to use this context provider on other platforms.
+ * Passes information about the scroll position and header height down via
+ * context for the pager header to consume.
  *
- * @platform ios
+ * @platform ios, android
  */
 export function PagerHeaderProvider({
   scrollY,
+  headerHeight,
   children,
 }: {
   scrollY: SharedValue<number>
+  headerHeight: number
   children: React.ReactNode
 }) {
+  const value = React.useMemo(
+    () => ({scrollY, headerHeight}),
+    [scrollY, headerHeight],
+  )
   return (
-    <PagerHeaderContext.Provider value={scrollY}>
+    <PagerHeaderContext.Provider value={value}>
       {children}
     </PagerHeaderContext.Provider>
   )
 }
 
 export function usePagerHeaderContext() {
-  const scrollY = useContext(PagerHeaderContext)
-  if (isIOS) {
-    if (!scrollY) {
+  const ctx = useContext(PagerHeaderContext)
+  if (isNative) {
+    if (!ctx) {
       throw new Error(
         'usePagerHeaderContext must be used within a HeaderProvider',
       )
     }
-    return {scrollY}
+    return ctx
   } else {
     return null
   }
diff --git a/src/view/com/pager/PagerWithHeader.tsx b/src/view/com/pager/PagerWithHeader.tsx
index 617445964..dcf141f84 100644
--- a/src/view/com/pager/PagerWithHeader.tsx
+++ b/src/view/com/pager/PagerWithHeader.tsx
@@ -38,7 +38,11 @@ export interface PagerWithHeaderProps {
     | ((props: PagerWithHeaderChildParams) => JSX.Element)
   items: string[]
   isHeaderReady: boolean
-  renderHeader?: () => JSX.Element
+  renderHeader?: ({
+    setMinimumHeight,
+  }: {
+    setMinimumHeight: (height: number) => void
+  }) => JSX.Element
   initialPage?: number
   onPageSelected?: (index: number) => void
   onCurrentPageSelected?: (index: number) => void
@@ -83,7 +87,9 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
     const renderTabBar = React.useCallback(
       (props: RenderTabBarFnProps) => {
         return (
-          <PagerHeaderProvider scrollY={scrollY}>
+          <PagerHeaderProvider
+            scrollY={scrollY}
+            headerHeight={headerOnlyHeight}>
             <PagerTabBar
               headerOnlyHeight={headerOnlyHeight}
               items={items}
@@ -237,7 +243,11 @@ let PagerTabBar = ({
   items: string[]
   testID?: string
   scrollY: SharedValue<number>
-  renderHeader?: () => JSX.Element
+  renderHeader?: ({
+    setMinimumHeight,
+  }: {
+    setMinimumHeight: (height: number) => void
+  }) => JSX.Element
   onHeaderOnlyLayout: (height: number) => void
   onTabBarLayout: (e: LayoutChangeEvent) => void
   onCurrentPageSelected?: (index: number) => void
@@ -246,8 +256,13 @@ let PagerTabBar = ({
   dragProgress: SharedValue<number>
   dragState: SharedValue<'idle' | 'dragging' | 'settling'>
 }): React.ReactNode => {
+  const [minimumHeaderHeight, setMinimumHeaderHeight] = React.useState(0)
   const headerTransform = useAnimatedStyle(() => {
-    const translateY = Math.min(scrollY.get(), headerOnlyHeight) * -1
+    const translateY =
+      Math.min(
+        scrollY.get(),
+        Math.max(headerOnlyHeight - minimumHeaderHeight, 0),
+      ) * -1
     return {
       transform: [
         {
@@ -267,7 +282,7 @@ let PagerTabBar = ({
         ref={headerRef}
         pointerEvents={isIOS ? 'auto' : 'box-none'}
         collapsable={false}>
-        {renderHeader?.()}
+        {renderHeader?.({setMinimumHeight: setMinimumHeaderHeight})}
         {
           // It wouldn't be enough to place `onLayout` on the parent node because
           // this would risk measuring before `isHeaderReady` has turned `true`.
diff --git a/src/view/com/pager/PagerWithHeader.web.tsx b/src/view/com/pager/PagerWithHeader.web.tsx
index 3335532b3..98b32b347 100644
--- a/src/view/com/pager/PagerWithHeader.web.tsx
+++ b/src/view/com/pager/PagerWithHeader.web.tsx
@@ -21,7 +21,11 @@ export interface PagerWithHeaderProps {
     | ((props: PagerWithHeaderChildParams) => JSX.Element)
   items: string[]
   isHeaderReady: boolean
-  renderHeader?: () => JSX.Element
+  renderHeader?: ({
+    setMinimumHeight,
+  }: {
+    setMinimumHeight: () => void
+  }) => JSX.Element
   initialPage?: number
   onPageSelected?: (index: number) => void
   onCurrentPageSelected?: (index: number) => void
@@ -115,7 +119,11 @@ let PagerTabBar = ({
   currentPage: number
   items: string[]
   testID?: string
-  renderHeader?: () => JSX.Element
+  renderHeader?: ({
+    setMinimumHeight,
+  }: {
+    setMinimumHeight: () => void
+  }) => JSX.Element
   isHeaderReady: boolean
   onCurrentPageSelected?: (index: number) => void
   onSelect?: (index: number) => void
@@ -123,7 +131,7 @@ let PagerTabBar = ({
 }): React.ReactNode => {
   return (
     <>
-      <Layout.Center>{renderHeader?.()}</Layout.Center>
+      <Layout.Center>{renderHeader?.({setMinimumHeight: noop})}</Layout.Center>
       {tabBarAnchor}
       <Layout.Center
         style={web([
@@ -175,3 +183,5 @@ function toArray<T>(v: T | T[]): T[] {
   }
   return [v]
 }
+
+function noop() {}