[Layout] Bleed profile banner into safe area (#6967)

* bleed profile banner into safe area

(cherry picked from commit 50b3a4d0c6fd94b583ffe4efa65de35c81ae7f4e)

* pointer events none when hidden

(cherry picked from commit bae2c7b2dd6d7f858a98812196628308c0877755)

* fix web

(cherry picked from commit e3f9597170375f2903b6e567b963f008ec95aed1)

* add status bar shadow

* rm log

* rm mini header

* speed up animation

* pass bool rather than int in light status bar
This commit is contained in:
Samuel Newman
2024-12-12 17:46:19 +00:00
committed by GitHub
parent 4b32b0a71b
commit ffc63dc85f
14 changed files with 322 additions and 47 deletions
+19 -11
View File
@@ -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
}
+20 -5
View File
@@ -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`.
+13 -3
View File
@@ -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() {}