Created
November 16, 2022 20:11
-
-
Save davispuh/e141fb1ebfa220dccb6a8c1e593db80c to your computer and use it in GitHub Desktop.
Revisions
-
davispuh created this gist
Nov 16, 2022 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,459 @@ 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 ||