Skip to content

Instantly share code, notes, and snippets.

@davispuh
Created November 16, 2022 20:11
Show Gist options
  • Save davispuh/e141fb1ebfa220dccb6a8c1e593db80c to your computer and use it in GitHub Desktop.
Save davispuh/e141fb1ebfa220dccb6a8c1e593db80c to your computer and use it in GitHub Desktop.
React Naviagtion patch experimenting
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