Skip to content

Commit d3a82d2

Browse files
committed
feat: add SurfaceComponent prop to header
1 parent 45d0563 commit d3a82d2

File tree

11 files changed

+154
-3
lines changed

11 files changed

+154
-3
lines changed

example/ios/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -720,4 +720,4 @@ SPEC CHECKSUMS:
720720

721721
PODFILE CHECKSUM: 675d5c47e1336b7b792d33fad9d8fbf19b97d359
722722

723-
COCOAPODS: 1.11.3
723+
COCOAPODS: 1.12.1

example/src/navigation/AppNavigation.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
ProfileScreen,
88
SectionListUsageScreen,
99
SimpleUsageScreen,
10+
SurfaceComponentUsageScreen,
1011
TwitterProfileScreen,
1112
} from '../screens';
1213

@@ -23,6 +24,10 @@ export default () => (
2324
<Stack.Screen name="SimpleUsageScreen" component={SimpleUsageScreen} />
2425
<Stack.Screen name="FlatListUsageScreen" component={FlatListUsageScreen} />
2526
<Stack.Screen name="SectionListUsageScreen" component={SectionListUsageScreen} />
27+
<Stack.Screen
28+
name="HeaderSurfaceComponentUsageScreen"
29+
component={SurfaceComponentUsageScreen}
30+
/>
2631
<Stack.Screen name="TwitterProfileScreen" component={TwitterProfileScreen} />
2732
</Stack.Navigator>
2833
);

example/src/navigation/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export type RootStackParamList = {
88
FlatListUsageScreen: undefined;
99
SectionListUsageScreen: undefined;
1010
TwitterProfileScreen: undefined;
11+
HeaderSurfaceComponentUsageScreen: undefined;
1112
};
1213

1314
// Overrides the typing for useNavigation in @react-navigation/native to support the internal
@@ -37,6 +38,11 @@ export type SectionListUsageScreenNavigationProps = NativeStackScreenProps<
3738
'SectionListUsageScreen'
3839
>;
3940

41+
export type SurfaceComponentUsageScreenNavigationProps = NativeStackScreenProps<
42+
RootStackParamList,
43+
'HeaderSurfaceComponentUsageScreen'
44+
>;
45+
4046
export type TwitterProfileScreenNavigationProps = NativeStackScreenProps<
4147
RootStackParamList,
4248
'TwitterProfileScreen'

example/src/screens/Home.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ const SCREEN_LIST_CONFIG: ScreenConfigItem[] = [
3333
route: 'SectionListUsageScreen',
3434
description: "A simple example of the library's SectionList.",
3535
},
36+
{
37+
name: 'Header SurfaceComponent Interpolation',
38+
route: 'HeaderSurfaceComponentUsageScreen',
39+
description:
40+
'A simple example of the library using the SurfaceComponent prop for its header to animate the background color of the header.',
41+
},
3642
{
3743
name: 'Twitter Profile',
3844
route: 'TwitterProfileScreen',

example/src/screens/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ export { default as ProfileScreen } from './Profile';
55
export { default as SimpleUsageScreen } from './usage/Simple';
66
export { default as FlatListUsageScreen } from './usage/FlatList';
77
export { default as SectionListUsageScreen } from './usage/SectionList';
8+
export { default as SurfaceComponentUsageScreen } from './usage/SurfaceComponent';
89
export { default as TwitterProfileScreen } from './usage/TwitterProfile';
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import React, { useMemo, useState } from 'react';
2+
import { RefreshControl, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
3+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
4+
import { useNavigation } from '@react-navigation/native';
5+
import {
6+
FadingView,
7+
Header,
8+
LargeHeader,
9+
ScrollViewWithHeaders,
10+
} from '@codeherence/react-native-header';
11+
import type {
12+
ScrollHeaderProps,
13+
ScrollLargeHeaderProps,
14+
SurfaceComponentProps,
15+
} from '@codeherence/react-native-header';
16+
import { range } from '../../utils';
17+
import { Avatar, BackButton } from '../../components';
18+
import { RANDOM_IMAGE_NUM } from '../../constants';
19+
import type { SurfaceComponentUsageScreenNavigationProps } from '../../navigation';
20+
21+
const SURFACE_BG_COLOR = 'cyan';
22+
23+
const HeaderSurface: React.FC<SurfaceComponentProps> = ({ showNavBar }) => (
24+
<FadingView
25+
opacity={showNavBar}
26+
style={[StyleSheet.absoluteFill, { backgroundColor: SURFACE_BG_COLOR }]}
27+
/>
28+
);
29+
30+
const HeaderComponent: React.FC<ScrollHeaderProps> = ({ showNavBar }) => {
31+
const navigation = useNavigation();
32+
const onPressProfile = () => navigation.navigate('Profile');
33+
34+
return (
35+
<Header
36+
showNavBar={showNavBar}
37+
headerCenterFadesIn={false}
38+
headerCenter={
39+
<Text style={styles.navBarTitle} numberOfLines={1}>
40+
Header
41+
</Text>
42+
}
43+
headerRight={
44+
<TouchableOpacity onPress={onPressProfile}>
45+
<Avatar size="sm" source={{ uri: `https://i.pravatar.cc/128?img=${RANDOM_IMAGE_NUM}` }} />
46+
</TouchableOpacity>
47+
}
48+
headerRightFadesIn
49+
headerLeft={<BackButton />}
50+
SurfaceComponent={HeaderSurface}
51+
/>
52+
);
53+
};
54+
55+
const LargeHeaderComponent: React.FC<ScrollLargeHeaderProps> = () => {
56+
const navigation = useNavigation();
57+
const onPressProfile = () => navigation.navigate('Profile');
58+
59+
return (
60+
<LargeHeader headerStyle={styles.largeHeaderStyle}>
61+
<TouchableOpacity onPress={onPressProfile}>
62+
<Avatar size="sm" source={{ uri: `https://i.pravatar.cc/128?img=${RANDOM_IMAGE_NUM}` }} />
63+
</TouchableOpacity>
64+
</LargeHeader>
65+
);
66+
};
67+
68+
const SurfaceComponentUsage: React.FC<SurfaceComponentUsageScreenNavigationProps> = () => {
69+
const { bottom } = useSafeAreaInsets();
70+
const [refreshing, setRefreshing] = useState(false);
71+
72+
const data = useMemo(() => range({ end: 100 }), []);
73+
74+
const onRefresh = async () => {
75+
if (refreshing) return;
76+
77+
setRefreshing(true);
78+
// Mimic some asynchronous task
79+
await new Promise((res) => setTimeout(res, 2500));
80+
setRefreshing(false);
81+
};
82+
83+
return (
84+
<ScrollViewWithHeaders
85+
HeaderComponent={HeaderComponent}
86+
LargeHeaderComponent={LargeHeaderComponent}
87+
contentContainerStyle={{ paddingBottom: bottom }}
88+
refreshControl={
89+
<RefreshControl refreshing={refreshing} colors={['#8E8E93']} onRefresh={onRefresh} />
90+
}
91+
>
92+
<View style={styles.children}>
93+
{data.map((i) => (
94+
<Text key={`text-${i}`}>Scroll to see header animation</Text>
95+
))}
96+
</View>
97+
</ScrollViewWithHeaders>
98+
);
99+
};
100+
101+
export default SurfaceComponentUsage;
102+
103+
const styles = StyleSheet.create({
104+
children: { marginTop: 16, paddingHorizontal: 16 },
105+
navBarTitle: { fontSize: 16, fontWeight: 'bold' },
106+
largeHeaderStyle: { flexDirection: 'row-reverse' },
107+
});

src/components/containers/FadingView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ type FadingViewProps = {
3131
/**
3232
* The children to be rendered inside the FadingView.
3333
*/
34-
children: React.ReactNode;
34+
children?: React.ReactNode;
3535
} & React.ComponentProps<typeof Animated.View>;
3636

3737
const FadingView = forwardRef<Animated.View, FadingViewProps>(

src/components/headers/Header.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const Header: React.FC<HeaderProps> = ({
2424
ignoreTopSafeArea = false,
2525
borderColor,
2626
borderWidth,
27+
SurfaceComponent,
2728
}) => {
2829
const { top } = useSafeAreaInsets();
2930
const dimensions = useWindowDimensions();
@@ -40,6 +41,8 @@ const Header: React.FC<HeaderProps> = ({
4041

4142
return (
4243
<View>
44+
{SurfaceComponent && SurfaceComponent({ showNavBar })}
45+
4346
<View style={[styles.container, headerStyle, !ignoreTopSafeArea && { paddingTop: top }]}>
4447
{headerLeftFadesIn ? (
4548
<FadingView
@@ -125,6 +128,7 @@ const styles = StyleSheet.create({
125128
width: '100%',
126129
alignItems: 'center',
127130
justifyContent: 'flex-start',
131+
backgroundColor: 'transparent',
128132
},
129133
leftContainer: {
130134
flexDirection: 'row',
@@ -133,13 +137,15 @@ const styles = StyleSheet.create({
133137
justifyContent: 'flex-start',
134138
alignItems: 'center',
135139
overflow: 'hidden',
140+
backgroundColor: 'transparent',
136141
},
137142
centerContainer: {
138143
flex: 1,
139144
flexDirection: 'row',
140145
paddingHorizontal: 6,
141146
alignItems: 'center',
142147
justifyContent: 'center',
148+
backgroundColor: 'transparent',
143149
},
144150
rightContainer: {
145151
flexDirection: 'row-reverse',
@@ -148,6 +154,7 @@ const styles = StyleSheet.create({
148154
alignItems: 'center',
149155
justifyContent: 'flex-start',
150156
overflow: 'hidden',
157+
backgroundColor: 'transparent',
151158
},
152159
noFlex: { display: 'none' },
153160
});

src/components/headers/types.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
import type { StyleProp, ViewStyle } from 'react-native';
22
import type Animated from 'react-native-reanimated';
33

4+
export interface SurfaceComponentProps {
5+
/**
6+
* Animated value between 0 and 1 that indicates whether the small header's content should be
7+
* visible. This is used to animate the header's content in and out.
8+
*
9+
* @type {Animated.SharedValue<number>}
10+
*/
11+
showNavBar: Animated.SharedValue<number>;
12+
}
13+
414
/**
515
* The props for the navigation bar component.
616
*/
@@ -111,6 +121,14 @@ export interface HeaderProps {
111121
* @type {number}
112122
*/
113123
borderWidth?: number;
124+
/**
125+
* A custom component to be rendered as the header's surface. This is useful if you want to
126+
* customize the header's surface with a background/blur.
127+
*
128+
* @param {SurfaceComponentProps} props
129+
* @returns {React.ReactNode}
130+
*/
131+
SurfaceComponent?: (props: SurfaceComponentProps) => React.ReactNode;
114132
}
115133

116134
/**

src/components/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ export type {
1111
SharedScrollContainerProps,
1212
} from './containers';
1313
export { Header, LargeHeader } from './headers';
14-
export type { HeaderProps, LargeHeaderProps } from './headers/types';
14+
export type { HeaderProps, LargeHeaderProps, SurfaceComponentProps } from './headers/types';

0 commit comments

Comments
 (0)