Created
November 16, 2022 20:11
-
-
Save davispuh/e141fb1ebfa220dccb6a8c1e593db80c to your computer and use it in GitHub Desktop.
React Naviagtion patch experimenting
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/packages/drawer/src/views/DrawerView.tsx b/packages/drawer/src/views/DrawerView.tsx | |
index 19363aad..f53897c4 100644 | |
--- a/packages/drawer/src/views/DrawerView.tsx | |
+++ b/packages/drawer/src/views/DrawerView.tsx | |
@@ -343,5 +343,7 @@ export default function DrawerView({ navigation, ...rest }: Props) { | |
const styles = StyleSheet.create({ | |
content: { | |
flex: 1, | |
+ maxHeight: '100%', | |
+ overflow: 'clip' as any, | |
}, | |
}); | |
diff --git a/packages/drawer/src/views/modern/Drawer.tsx b/packages/drawer/src/views/modern/Drawer.tsx | |
index 52eefb5b..2b9d2005 100644 | |
--- a/packages/drawer/src/views/modern/Drawer.tsx | |
+++ b/packages/drawer/src/views/modern/Drawer.tsx | |
@@ -381,6 +381,7 @@ const styles = StyleSheet.create({ | |
top: 0, | |
bottom: 0, | |
maxWidth: '100%', | |
+ maxHeight: '100%', | |
width: DEFAULT_DRAWER_WIDTH, | |
}, | |
content: { | |
diff --git a/packages/elements/src/Screen.tsx b/packages/elements/src/Screen.tsx | |
index 9ace55b5..cfbdc4ef 100644 | |
--- a/packages/elements/src/Screen.tsx | |
+++ b/packages/elements/src/Screen.tsx | |
@@ -25,6 +25,7 @@ type Props = { | |
header: React.ReactNode; | |
headerShown?: boolean; | |
headerStatusBarHeight?: number; | |
+ // eslint-disable-next-line react/no-unused-prop-types | |
headerTransparent?: boolean; | |
style?: StyleProp<ViewStyle>; | |
children: React.ReactNode; | |
@@ -42,7 +43,6 @@ export default function Screen(props: Props) { | |
modal = false, | |
header, | |
headerShown = true, | |
- headerTransparent, | |
headerStatusBarHeight = isParentHeaderShown ? 0 : insets.top, | |
navigation, | |
route, | |
@@ -60,17 +60,6 @@ export default function Screen(props: Props) { | |
importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'} | |
style={[styles.container, style]} | |
> | |
- <View style={styles.content}> | |
- <HeaderShownContext.Provider | |
- value={isParentHeaderShown || headerShown !== false} | |
- > | |
- <HeaderHeightContext.Provider | |
- value={headerShown ? headerHeight : parentHeaderHeight ?? 0} | |
- > | |
- {children} | |
- </HeaderHeightContext.Provider> | |
- </HeaderShownContext.Provider> | |
- </View> | |
{headerShown ? ( | |
<NavigationContext.Provider value={navigation}> | |
<NavigationRouteContext.Provider value={route}> | |
@@ -80,13 +69,31 @@ export default function Screen(props: Props) { | |
setHeaderHeight(height); | |
}} | |
- style={headerTransparent ? styles.absolute : styles.sticky} | |
+ style={styles.sticky} | |
> | |
{header} | |
</View> | |
</NavigationRouteContext.Provider> | |
</NavigationContext.Provider> | |
) : null} | |
+ <View | |
+ style={{ | |
+ ...styles.content, | |
+ // need this to fix scrolling, so that we can scroll to last item | |
+ // and prevent that NativeStack has extra scrollbar | |
+ maxHeight: `calc(100% - ${headerHeight}px)`, | |
+ }} | |
+ > | |
+ <HeaderShownContext.Provider | |
+ value={isParentHeaderShown || headerShown !== false} | |
+ > | |
+ <HeaderHeightContext.Provider | |
+ value={headerShown ? headerHeight : parentHeaderHeight ?? 0} | |
+ > | |
+ {children} | |
+ </HeaderHeightContext.Provider> | |
+ </HeaderShownContext.Provider> | |
+ </View> | |
</Background> | |
); | |
} | |
@@ -94,17 +101,9 @@ export default function Screen(props: Props) { | |
const styles = StyleSheet.create({ | |
container: { | |
flex: 1, | |
- flexDirection: 'column-reverse', | |
}, | |
- // This is necessary to avoid applying 'column-reverse' to screen content | |
content: { | |
- flex: 1, | |
- }, | |
- absolute: { | |
- position: 'absolute', | |
- top: 0, | |
- left: 0, | |
- right: 0, | |
+ overflow: 'scroll', | |
}, | |
sticky: { | |
position: 'sticky' as any, | |
diff --git a/packages/stack/src/views/Header/HeaderContainer.tsx b/packages/stack/src/views/Header/HeaderContainer.tsx | |
index bbad9f4e..d4d73e08 100644 | |
--- a/packages/stack/src/views/Header/HeaderContainer.tsx | |
+++ b/packages/stack/src/views/Header/HeaderContainer.tsx | |
@@ -49,144 +49,136 @@ export default function HeaderContainer({ | |
const parentHeaderBack = React.useContext(HeaderBackContext); | |
return ( | |
- <Animated.View pointerEvents="box-none" style={[styles.sticky, style]}> | |
- {scenes.slice(-3).map((scene, i, self) => { | |
- if ((mode === 'screen' && i !== self.length - 1) || !scene) { | |
- return null; | |
- } | |
- | |
- const { | |
- header, | |
- headerMode, | |
- headerShown = true, | |
- headerTransparent, | |
- headerStyleInterpolator, | |
- } = scene.descriptor.options; | |
- | |
- if (headerMode !== mode || !headerShown) { | |
- return null; | |
- } | |
- | |
- const isFocused = focusedRoute.key === scene.descriptor.route.key; | |
- const previousScene = getPreviousScene({ | |
- route: scene.descriptor.route, | |
- }); | |
- | |
- let headerBack = parentHeaderBack; | |
- | |
- if (previousScene) { | |
- const { options, route } = previousScene.descriptor; | |
- | |
- headerBack = previousScene | |
- ? { title: getHeaderTitle(options, route.name) } | |
- : parentHeaderBack; | |
- } | |
- | |
- // If the screen is next to a headerless screen, we need to make the header appear static | |
- // This makes the header look like it's moving with the screen | |
- const previousDescriptor = self[i - 1]?.descriptor; | |
- const nextDescriptor = self[i + 1]?.descriptor; | |
- | |
- const { | |
- headerShown: previousHeaderShown = true, | |
- headerMode: previousHeaderMode, | |
- } = previousDescriptor?.options || {}; | |
- | |
- // If any of the next screens don't have a header or header is part of the screen | |
- // Then we need to move this header offscreen so that it doesn't cover it | |
- const nextHeaderlessScene = self.slice(i + 1).find((scene) => { | |
+ <View style={styles.header}> | |
+ <Animated.View pointerEvents="box-none" style={style}> | |
+ {scenes.slice(-3).map((scene, i, self) => { | |
+ if ((mode === 'screen' && i !== self.length - 1) || !scene) { | |
+ return null; | |
+ } | |
+ | |
const { | |
- headerShown: currentHeaderShown = true, | |
- headerMode: currentHeaderMode, | |
- } = scene?.descriptor.options || {}; | |
- | |
- return currentHeaderShown === false || currentHeaderMode === 'screen'; | |
- }); | |
- | |
- const { gestureDirection: nextHeaderlessGestureDirection } = | |
- nextHeaderlessScene?.descriptor.options || {}; | |
- | |
- const isHeaderStatic = | |
- ((previousHeaderShown === false || previousHeaderMode === 'screen') && | |
- // We still need to animate when coming back from next scene | |
- // A hacky way to check this is if the next scene exists | |
- !nextDescriptor) || | |
- nextHeaderlessScene; | |
- | |
- const props: StackHeaderProps = { | |
- layout, | |
- back: headerBack, | |
- progress: scene.progress, | |
- options: scene.descriptor.options, | |
- route: scene.descriptor.route, | |
- navigation: scene.descriptor | |
- .navigation as StackNavigationProp<ParamListBase>, | |
- styleInterpolator: | |
- mode === 'float' | |
- ? isHeaderStatic | |
- ? nextHeaderlessGestureDirection === 'vertical' || | |
- nextHeaderlessGestureDirection === 'vertical-inverted' | |
- ? forSlideUp | |
- : nextHeaderlessGestureDirection === 'horizontal-inverted' | |
- ? forSlideRight | |
- : forSlideLeft | |
- : headerStyleInterpolator | |
- : forNoAnimation, | |
- }; | |
- | |
- return ( | |
- <NavigationContext.Provider | |
- key={scene.descriptor.route.key} | |
- value={scene.descriptor.navigation} | |
- > | |
- <NavigationRouteContext.Provider value={scene.descriptor.route}> | |
- <View | |
- onLayout={ | |
- onContentHeightChange | |
- ? (e) => { | |
- const { height } = e.nativeEvent.layout; | |
- | |
- onContentHeightChange({ | |
- route: scene.descriptor.route, | |
- height, | |
- }); | |
- } | |
- : undefined | |
- } | |
- pointerEvents={isFocused ? 'box-none' : 'none'} | |
- accessibilityElementsHidden={!isFocused} | |
- importantForAccessibility={ | |
- isFocused ? 'auto' : 'no-hide-descendants' | |
- } | |
- style={ | |
- // Avoid positioning the focused header absolutely | |
- // Otherwise accessibility tools don't seem to be able to find it | |
- (mode === 'float' && !isFocused) || headerTransparent | |
- ? styles.header | |
- : null | |
- } | |
- > | |
- {header !== undefined ? header(props) : <Header {...props} />} | |
- </View> | |
- </NavigationRouteContext.Provider> | |
- </NavigationContext.Provider> | |
- ); | |
- })} | |
- </Animated.View> | |
+ header, | |
+ headerMode, | |
+ headerShown = true, | |
+ headerStyleInterpolator, | |
+ } = scene.descriptor.options; | |
+ | |
+ if (headerMode !== mode || !headerShown) { | |
+ return null; | |
+ } | |
+ | |
+ const isFocused = focusedRoute.key === scene.descriptor.route.key; | |
+ const previousScene = getPreviousScene({ | |
+ route: scene.descriptor.route, | |
+ }); | |
+ | |
+ let headerBack = parentHeaderBack; | |
+ | |
+ if (previousScene) { | |
+ const { options, route } = previousScene.descriptor; | |
+ | |
+ headerBack = previousScene | |
+ ? { title: getHeaderTitle(options, route.name) } | |
+ : parentHeaderBack; | |
+ } | |
+ | |
+ // If the screen is next to a headerless screen, we need to make the header appear static | |
+ // This makes the header look like it's moving with the screen | |
+ const previousDescriptor = self[i - 1]?.descriptor; | |
+ const nextDescriptor = self[i + 1]?.descriptor; | |
+ | |
+ const { | |
+ headerShown: previousHeaderShown = true, | |
+ headerMode: previousHeaderMode, | |
+ } = previousDescriptor?.options || {}; | |
+ | |
+ // If any of the next screens don't have a header or header is part of the screen | |
+ // Then we need to move this header offscreen so that it doesn't cover it | |
+ const nextHeaderlessScene = self.slice(i + 1).find((scene) => { | |
+ const { | |
+ headerShown: currentHeaderShown = true, | |
+ headerMode: currentHeaderMode, | |
+ } = scene?.descriptor.options || {}; | |
+ | |
+ return ( | |
+ currentHeaderShown === false || currentHeaderMode === 'screen' | |
+ ); | |
+ }); | |
+ | |
+ const { gestureDirection: nextHeaderlessGestureDirection } = | |
+ nextHeaderlessScene?.descriptor.options || {}; | |
+ | |
+ const isHeaderStatic = | |
+ ((previousHeaderShown === false || | |
+ previousHeaderMode === 'screen') && | |
+ // We still need to animate when coming back from next scene | |
+ // A hacky way to check this is if the next scene exists | |
+ !nextDescriptor) || | |
+ nextHeaderlessScene; | |
+ | |
+ const props: StackHeaderProps = { | |
+ layout, | |
+ back: headerBack, | |
+ progress: scene.progress, | |
+ options: scene.descriptor.options, | |
+ route: scene.descriptor.route, | |
+ navigation: scene.descriptor | |
+ .navigation as StackNavigationProp<ParamListBase>, | |
+ styleInterpolator: | |
+ mode === 'float' | |
+ ? isHeaderStatic | |
+ ? nextHeaderlessGestureDirection === 'vertical' || | |
+ nextHeaderlessGestureDirection === 'vertical-inverted' | |
+ ? forSlideUp | |
+ : nextHeaderlessGestureDirection === 'horizontal-inverted' | |
+ ? forSlideRight | |
+ : forSlideLeft | |
+ : headerStyleInterpolator | |
+ : forNoAnimation, | |
+ }; | |
+ | |
+ return ( | |
+ <NavigationContext.Provider | |
+ key={scene.descriptor.route.key} | |
+ value={scene.descriptor.navigation} | |
+ > | |
+ <NavigationRouteContext.Provider value={scene.descriptor.route}> | |
+ <View | |
+ onLayout={ | |
+ onContentHeightChange | |
+ ? (e) => { | |
+ const { height } = e.nativeEvent.layout; | |
+ | |
+ onContentHeightChange({ | |
+ route: scene.descriptor.route, | |
+ height, | |
+ }); | |
+ } | |
+ : undefined | |
+ } | |
+ pointerEvents={isFocused ? 'box-none' : 'none'} | |
+ accessibilityElementsHidden={!isFocused} | |
+ importantForAccessibility={ | |
+ isFocused ? 'auto' : 'no-hide-descendants' | |
+ } | |
+ > | |
+ {header !== undefined ? header(props) : <Header {...props} />} | |
+ </View> | |
+ </NavigationRouteContext.Provider> | |
+ </NavigationContext.Provider> | |
+ ); | |
+ })} | |
+ </Animated.View> | |
+ </View> | |
); | |
} | |
const styles = StyleSheet.create({ | |
header: { | |
- position: 'absolute', | |
- top: 0, | |
- left: 0, | |
- right: 0, | |
- }, | |
- sticky: { | |
position: 'sticky' as any, | |
top: 0, | |
left: 0, | |
right: 0, | |
+ zIndex: 10, | |
}, | |
}); | |
diff --git a/packages/stack/src/views/Stack/CardContainer.tsx b/packages/stack/src/views/Stack/CardContainer.tsx | |
index 44907d22..aa98d1f1 100644 | |
--- a/packages/stack/src/views/Stack/CardContainer.tsx | |
+++ b/packages/stack/src/views/Stack/CardContainer.tsx | |
@@ -274,11 +274,22 @@ function CardContainer({ | |
: 'flex', | |
...StyleSheet.absoluteFillObject, | |
position: 'relative', | |
+ maxHeight: '100%', | |
}, | |
]} | |
> | |
<View style={styles.container}> | |
<ModalPresentationContext.Provider value={modal}> | |
+ {headerMode !== 'float' | |
+ ? renderHeader({ | |
+ mode: 'screen', | |
+ layout, | |
+ scenes: [previousScene, scene], | |
+ getPreviousScene, | |
+ getFocusedRoute, | |
+ onContentHeightChange: onHeaderHeightChange, | |
+ }) | |
+ : null} | |
<View style={styles.scene}> | |
<HeaderBackContext.Provider value={headerBack}> | |
<HeaderShownContext.Provider | |
@@ -292,16 +303,6 @@ function CardContainer({ | |
</HeaderShownContext.Provider> | |
</HeaderBackContext.Provider> | |
</View> | |
- {headerMode !== 'float' | |
- ? renderHeader({ | |
- mode: 'screen', | |
- layout, | |
- scenes: [previousScene, scene], | |
- getPreviousScene, | |
- getFocusedRoute, | |
- onContentHeightChange: onHeaderHeightChange, | |
- }) | |
- : null} | |
</ModalPresentationContext.Provider> | |
</View> | |
</Card> | |
@@ -313,7 +314,6 @@ export default React.memo(CardContainer); | |
const styles = StyleSheet.create({ | |
container: { | |
flex: 1, | |
- flexDirection: 'column-reverse', | |
}, | |
scene: { | |
flex: 1, | |
diff --git a/packages/stack/src/views/Stack/CardStack.tsx b/packages/stack/src/views/Stack/CardStack.tsx | |
index 1cdb7100..7e07c1b8 100755 | |
--- a/packages/stack/src/views/Stack/CardStack.tsx | |
+++ b/packages/stack/src/views/Stack/CardStack.tsx | |
@@ -494,12 +494,17 @@ export default class CardStack extends React.Component<Props, State> { | |
const { scenes, layout, gestures, headerHeights } = this.state; | |
const focusedRoute = state.routes[state.index]; | |
- const focusedHeaderHeight = headerHeights[focusedRoute.key]; | |
+ let focusedHeaderHeight = headerHeights[focusedRoute.key]; | |
const isFloatHeaderAbsolute = this.state.scenes.slice(-2).some((scene) => { | |
const options = scene.descriptor.options ?? {}; | |
const { headerMode, headerTransparent, headerShown = true } = options; | |
+ if (!headerShown) { | |
+ // otherwise page's height is extended even when header is not visible | |
+ focusedHeaderHeight = 0; | |
+ } | |
+ | |
if ( | |
headerTransparent || | |
headerShown === false || |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment