diff --git a/example/App.tsx b/example/App.tsx index a42805e..56c215c 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -10,6 +10,7 @@ import 'react-native-gesture-handler'; import { DefaultHeaderScreen } from './src/DefaultHeaderScreen'; import { StickyHeaderScreen } from './src/StickyHeaderScreen'; import { BackgroundHeaderScreen } from './src/BackgroundHeaderScreen'; +import { BigHeaderScreen } from './src/BigHeaderScreen'; import { SubHeaderScreen } from './src/SubHeaderScreen'; import { CustomHeaderScreen, @@ -24,6 +25,7 @@ export type StackParamList = { DefaultHeader: undefined; StickyHeader: undefined; BackgroundHeader: undefined; + BigHeader: undefined; SubHeader: undefined; WithCustomHeader: undefined; CustomHeaderDetail: undefined; @@ -38,9 +40,10 @@ const samples: { title: string; routeName: keyof StackParamList }[] = [ { title: 'Sample 1-1: Default Header', routeName: 'DefaultHeader' }, { title: 'Sample 1-2: Sticky Header', routeName: 'StickyHeader' }, { title: 'Sample 1-3: Background Header', routeName: 'BackgroundHeader' }, - { title: 'Sample 2: Sub Header', routeName: 'SubHeader' }, - { title: 'Sample 3: Custom Header', routeName: 'WithCustomHeader' }, - { title: 'Sample 4: Show Header Manually', routeName: 'ShowHeaderScreen' }, + { title: 'Sample 2: Big Header', routeName: 'BigHeader' }, + { title: 'Sample 3: Sub Header', routeName: 'SubHeader' }, + { title: 'Sample 4: Custom Header', routeName: 'WithCustomHeader' }, + { title: 'Sample 5: Show Header Manually', routeName: 'ShowHeaderScreen' }, ]; function HomeScreen({ navigation }: ScreenProps) { @@ -109,7 +112,16 @@ function App() { }} /> - {/* Sample 2: Sub Header */} + {/* Sample 2: Big Header */} + + + {/* Sample 3: Sub Header */} - {/* Sample 3: Custom Header */} + {/* Sample 4: Custom Header */} + {/* Sample 5: Show Header Manually */} diff --git a/example/src/BigHeaderScreen.tsx b/example/src/BigHeaderScreen.tsx new file mode 100644 index 0000000..05a974f --- /dev/null +++ b/example/src/BigHeaderScreen.tsx @@ -0,0 +1,84 @@ +import * as React from 'react'; +import { Animated, View, Text, Image, TouchableOpacity } from 'react-native'; +import { StackNavigationProp } from '@react-navigation/stack'; +import { useCollapsibleBigHeader } from 'react-navigation-collapsible'; + +import { StackParamList } from '../App'; +import { createRow } from './Row'; + +const data: number[] = []; +for (let i = 0; i < 100; i++) { + data.push(i); +} + +type ScreenProps = { + navigation: StackNavigationProp; +}; + +const BigHeaderScreen = ({ navigation }: ScreenProps) => { + const { + onScroll, + containerPaddingTop, + scrollIndicatorInsetTop, + } = useCollapsibleBigHeader({ + navigationOptions: { + title: 'John Doe', + headerStyle: { + height: 250, + }, + headerBackground: ( + <> + + + + + + Edit Profile + + + + + ), + }, + config: { + collapsedColor: 'white', + }, + }); + + return ( + navigation.navigate('Detail'))} + keyExtractor={(item: any) => item.toString()} + /> + ); +}; + +export { BigHeaderScreen }; diff --git a/index.ts b/index.ts index b5ca8c5..2124d66 100644 --- a/index.ts +++ b/index.ts @@ -1,5 +1,6 @@ import { useCollapsibleHeader, + useCollapsibleBigHeader, useCollapsibleSubHeader, Collapsible, UseCollapsibleOptions, @@ -14,6 +15,7 @@ import { CollapsibleSubHeaderAnimator } from './src/CollapsibleSubHeaderAnimator export { useCollapsibleHeader, + useCollapsibleBigHeader, useCollapsibleSubHeader, setSafeBounceHeight, disableExpoTranslucentStatusBar, diff --git a/src/core.tsx b/src/core.tsx index f510dde..f9b4c60 100644 --- a/src/core.tsx +++ b/src/core.tsx @@ -5,7 +5,6 @@ import { NativeScrollEvent, useWindowDimensions, } from 'react-native'; -import { StackHeaderProps } from '@react-navigation/stack'; import { useRoute, useNavigation } from '@react-navigation/native'; import shallowequal from 'shallowequal'; @@ -21,6 +20,7 @@ import { createCollapsibleCustomHeaderAnimator } from './createCollapsibleCustom enum CollapsibleHeaderType { Default, + BigHeader, SubHeader, } @@ -114,6 +114,10 @@ const useCollapsibleHeader = ( } } const safeBounceHeight = getSafeBounceHeight(); + const minHeaderVisibleHeight = + collapsibleHeaderType === CollapsibleHeaderType.BigHeader + ? getDefaultHeaderHeight(isLandscape) + : 0; const animatedDiffClampY = Animated.diffClamp( positionY, @@ -126,10 +130,14 @@ const useCollapsibleHeader = ( outputRange: [0, 1], extrapolate: 'clamp', }); - const translateY = Animated.multiply(progress, -headerHeight); + const heightMoveTo = -(headerHeight - minHeaderVisibleHeight); + const translateY = Animated.multiply(progress, heightMoveTo); const opacity = Animated.subtract(1, progress); - if (collapsibleHeaderType === CollapsibleHeaderType.Default) { + if ( + collapsibleHeaderType === CollapsibleHeaderType.Default || + collapsibleHeaderType === CollapsibleHeaderType.BigHeader + ) { const options = { ...navigationOptions, headerStyle: { @@ -147,6 +155,7 @@ const useCollapsibleHeader = ( }), headerTransparent: true, }; + if (navigationOptions.header) { Object.assign(options, { header: createCollapsibleCustomHeaderAnimator( @@ -154,6 +163,37 @@ const useCollapsibleHeader = ( ), }); } + + if ( + navigationOptions.headerBackground && + collapsibleHeaderType === CollapsibleHeaderType.BigHeader + ) { + const startToVisible = 0.5; + const defaultHeaderTranslateY = progress.interpolate({ + inputRange: [0, startToVisible, 1], + outputRange: [-1000, -heightMoveTo * 0.25, -heightMoveTo * 0.5], + }); + const defaultHeaderOpacity = progress.interpolate({ + inputRange: [0, startToVisible, 1], + outputRange: [0, 0, 1], + }); + options.headerStyle = { + ...options.headerStyle, + opacity: defaultHeaderOpacity, + }; + Object.assign(options, { + headerTitleStyle: { + transform: [{ translateY: defaultHeaderTranslateY }], + }, + headerLeftContainerStyle: { + transform: [{ translateY: defaultHeaderTranslateY }], + }, + headerRightContainerStyle: { + transform: [{ translateY: defaultHeaderTranslateY }], + }, + }); + } + navigation.setOptions(options); } @@ -187,7 +227,14 @@ const useCollapsibleHeader = ( ); }; +const useCollapsibleBigHeader = (options?: UseCollapsibleOptions) => + useCollapsibleHeader(options, CollapsibleHeaderType.BigHeader); + const useCollapsibleSubHeader = (options?: UseCollapsibleOptions) => useCollapsibleHeader(options, CollapsibleHeaderType.SubHeader); -export { useCollapsibleHeader, useCollapsibleSubHeader }; +export { + useCollapsibleHeader, + useCollapsibleBigHeader, + useCollapsibleSubHeader, +}; diff --git a/yarn.lock b/yarn.lock index 7dde3e4..af4c0cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -33,10 +33,10 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== -"@types/react-native@^0.63.25": - version "0.63.30" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.63.30.tgz#a0bd61e61b7a020a538ea22901be2f5d178fe02f" - integrity sha512-8/PrOjuUaPTCfMeW12ubseZPUGdbRhxYDa/aT+0D0KWVTe60b4H/gJrcfJmBXC6EcCFcimuTzQCv8/S03slYqA== +"@types/react-native@0.63.25": + version "0.63.25" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.63.25.tgz#a08bbe17a75cce993f52655a8fe75f30bf77e965" + integrity sha512-cRm+1iQecewpFYOArYJoM1qGd0JpFJ6f97KqIy9H2GawAdWkgyarSk8CBy4SMt2WOtPkysCu2EG7UwIT3vNeaA== dependencies: "@types/react" "*"