Skip to content
This repository has been archived by the owner on Jan 5, 2022. It is now read-only.

feat: Big header <-> Default header transition #159

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 27 additions & 14 deletions example/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -24,6 +25,7 @@ export type StackParamList = {
DefaultHeader: undefined;
StickyHeader: undefined;
BackgroundHeader: undefined;
BigHeader: undefined;
SubHeader: undefined;
WithCustomHeader: undefined;
CustomHeaderDetail: undefined;
Expand All @@ -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) {
Expand Down Expand Up @@ -109,7 +112,16 @@ function App() {
}}
/>

{/* Sample 2: Sub Header */}
{/* Sample 2: Big Header */}
<Stack.Screen
name="BigHeader"
component={BigHeaderScreen}
options={{
title: 'Big Header',
}}
/>

{/* Sample 3: Sub Header */}
<Stack.Screen
name="SubHeader"
component={SubHeaderScreen}
Expand All @@ -120,7 +132,7 @@ function App() {
}}
/>

{/* Sample 3: Custom Header */}
{/* Sample 4: Custom Header */}
<Stack.Screen
name="WithCustomHeader"
component={CustomHeaderScreen}
Expand All @@ -130,29 +142,30 @@ function App() {
}}
/>

{/* Sample 5: Show Header Manually */}
<Stack.Screen
name="Detail"
component={DetailScreen}
name="ShowHeaderScreen"
component={ShowHeaderScreen}
options={{
title: 'Detail Screen',
headerTintColor: 'white',
title: 'Default Header',
}}
/>

<Stack.Screen
name="CustomHeaderDetail"
name="Detail"
component={DetailScreen}
options={{
title: 'Detail Screen',
header: renderCustomHeader,
}}
/>

<Stack.Screen
name="ShowHeaderScreen"
component={ShowHeaderScreen}
name="CustomHeaderDetail"
component={DetailScreen}
options={{
headerTintColor: 'white',
title: 'Default Header',
title: 'Detail Screen',
header: renderCustomHeader,
}}
/>
</Stack.Navigator>
Expand Down
84 changes: 84 additions & 0 deletions example/src/BigHeaderScreen.tsx
Original file line number Diff line number Diff line change
@@ -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<StackParamList>;
};

const BigHeaderScreen = ({ navigation }: ScreenProps) => {
const {
onScroll,
containerPaddingTop,
scrollIndicatorInsetTop,
} = useCollapsibleBigHeader({
navigationOptions: {
title: 'John Doe',
headerStyle: {
height: 250,
},
headerBackground: (
<>
<Image
source={{
uri:
'https://artwork.wallartprints.com/media/catalog/category/mountain-pictures.jpg',
}}
style={{ flex: 1 }}
/>
<View
style={{
position: 'absolute',
width: '100%',
alignItems: 'center',
top: 80,
}}>
<Image
source={{
uri:
'https://media.istockphoto.com/vectors/default-profile-picture-avatar-photo-placeholder-vector-illustration-vector-id1223671392?b=1&k=6&m=1223671392&s=612x612&w=0&h=5VMcL3a_1Ni5rRHX0LkaA25lD_0vkhFsb1iVm1HKVSQ=',
}}
style={{
width: 100,
height: 100,
borderColor: 'lightgray',
borderWidth: 6,
borderRadius: 50,
}}
/>
<TouchableOpacity>
<Text style={{ fontSize: 20, color: 'white', marginTop: 16 }}>
Edit Profile
</Text>
</TouchableOpacity>
</View>
</>
),
},
config: {
collapsedColor: 'white',
},
});

return (
<Animated.FlatList
data={data}
onScroll={onScroll}
contentContainerStyle={{ paddingTop: containerPaddingTop }}
scrollIndicatorInsets={{ top: scrollIndicatorInsetTop }}
renderItem={createRow(() => navigation.navigate('Detail'))}
keyExtractor={(item: any) => item.toString()}
/>
);
};

export { BigHeaderScreen };
2 changes: 2 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
useCollapsibleHeader,
useCollapsibleBigHeader,
useCollapsibleSubHeader,
Collapsible,
UseCollapsibleOptions,
Expand All @@ -14,6 +15,7 @@ import { CollapsibleSubHeaderAnimator } from './src/CollapsibleSubHeaderAnimator

export {
useCollapsibleHeader,
useCollapsibleBigHeader,
useCollapsibleSubHeader,
setSafeBounceHeight,
disableExpoTranslucentStatusBar,
Expand Down
55 changes: 51 additions & 4 deletions src/core.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -21,6 +20,7 @@ import { createCollapsibleCustomHeaderAnimator } from './createCollapsibleCustom

enum CollapsibleHeaderType {
Default,
BigHeader,
SubHeader,
}

Expand Down Expand Up @@ -114,6 +114,10 @@ const useCollapsibleHeader = (
}
}
const safeBounceHeight = getSafeBounceHeight();
const minHeaderVisibleHeight =
collapsibleHeaderType === CollapsibleHeaderType.BigHeader
? getDefaultHeaderHeight(isLandscape)
: 0;

const animatedDiffClampY = Animated.diffClamp(
positionY,
Expand All @@ -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: {
Expand All @@ -147,13 +155,45 @@ const useCollapsibleHeader = (
}),
headerTransparent: true,
};

if (navigationOptions.header) {
Object.assign(options, {
header: createCollapsibleCustomHeaderAnimator(
navigationOptions.header
),
});
}

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);
}

Expand Down Expand Up @@ -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,
};
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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/[email protected]":
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" "*"

Expand Down