Skip to content
Merged
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
5 changes: 5 additions & 0 deletions example/src/navigation/AppNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
SimpleUsageScreen,
SurfaceComponentUsageScreen,
TwitterProfileScreen,
AbsoluteHeaderBlurSurface,
} from '../screens';

const Stack = createNativeStackNavigator<RootStackParamList>();
Expand All @@ -31,5 +32,9 @@ export default () => (
component={SurfaceComponentUsageScreen}
/>
<Stack.Screen name="TwitterProfileScreen" component={TwitterProfileScreen} />
<Stack.Screen
name="AbsoluteHeaderBlurSurfaceUsageScreen"
component={AbsoluteHeaderBlurSurface}
/>
</Stack.Navigator>
);
6 changes: 6 additions & 0 deletions example/src/navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type RootStackParamList = {
SectionListUsageScreen: undefined;
TwitterProfileScreen: undefined;
HeaderSurfaceComponentUsageScreen: undefined;
AbsoluteHeaderBlurSurfaceUsageScreen: undefined;
};

// Overrides the typing for useNavigation in @react-navigation/native to support the internal
Expand Down Expand Up @@ -53,3 +54,8 @@ export type TwitterProfileScreenNavigationProps = NativeStackScreenProps<
RootStackParamList,
'TwitterProfileScreen'
>;

export type AbsoluteHeaderBlurSurfaceUsageScreenNavigationProps = NativeStackScreenProps<
RootStackParamList,
'AbsoluteHeaderBlurSurfaceUsageScreen'
>;
6 changes: 6 additions & 0 deletions example/src/screens/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ const SCREEN_LIST_CONFIG: ScreenConfigItem[] = [
route: 'TwitterProfileScreen',
description: 'Rebuilding the Twitter profile header with this library.',
},
{
name: 'Absolute Header with Blurred Surface',
route: 'AbsoluteHeaderBlurSurfaceUsageScreen',
description: 'An example of an absolutely-positioned header with a BlurView surface.',
},
];

const HeaderComponent: React.FC<ScrollHeaderProps> = ({ showNavBar }) => {
Expand Down Expand Up @@ -83,6 +88,7 @@ const Home: React.FC<HomeScreenNavigationProps> = ({ navigation }) => {

return (
<ScrollViewWithHeaders
disableAutoFixScroll
HeaderComponent={HeaderComponent}
LargeHeaderComponent={LargeHeaderComponent}
contentContainerStyle={{ paddingBottom: bottom }}
Expand Down
1 change: 1 addition & 0 deletions example/src/screens/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export { default as FlashListUsageScreen } from './usage/FlashList';
export { default as SectionListUsageScreen } from './usage/SectionList';
export { default as SurfaceComponentUsageScreen } from './usage/SurfaceComponent';
export { default as TwitterProfileScreen } from './usage/TwitterProfile';
export { default as AbsoluteHeaderBlurSurface } from './usage/AbsoluteHeaderBlurSurface';
105 changes: 105 additions & 0 deletions example/src/screens/usage/AbsoluteHeaderBlurSurface.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import type { AbsoluteHeaderBlurSurfaceUsageScreenNavigationProps } from '../../navigation';
import {
FadingView,
Header,
LargeHeader,
ScalingView,
ScrollHeaderProps,
ScrollLargeHeaderProps,
ScrollViewWithHeaders,
SurfaceComponentProps,
} from '@codeherence/react-native-header';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { BlurView } from '@react-native-community/blur';
import { BackButton } from '../../components';
import { range } from '../../utils';

const HeaderSurface: React.FC<SurfaceComponentProps> = ({ showNavBar }) => {
return (
<FadingView opacity={showNavBar} style={StyleSheet.absoluteFill}>
<BlurView style={StyleSheet.absoluteFill} blurType="light" />
</FadingView>
);
};

const HeaderComponent: React.FC<ScrollHeaderProps> = ({ showNavBar }) => {
const insets = useSafeAreaInsets();

return (
<Header
showNavBar={showNavBar}
noBottomBorder
headerStyle={{ height: 44 + insets.top }}
headerCenter={<Text style={styles.headerTitle}>react-native-header</Text>}
headerLeft={<BackButton />}
SurfaceComponent={HeaderSurface}
/>
);
};

const LargeHeaderComponent: React.FC<ScrollLargeHeaderProps> = ({ scrollY }) => {
return (
<LargeHeader>
<ScalingView scrollY={scrollY} style={styles.leftHeader}>
<Text style={styles.title}>Large Header</Text>
</ScalingView>
</LargeHeader>
);
};

const TransparentSurface: React.FC<AbsoluteHeaderBlurSurfaceUsageScreenNavigationProps> = () => {
const { bottom } = useSafeAreaInsets();

return (
<ScrollViewWithHeaders
absoluteHeader
HeaderComponent={HeaderComponent}
LargeHeaderComponent={LargeHeaderComponent}
style={styles.container}
contentContainerStyle={{ paddingBottom: bottom + 12 }}
>
<View style={styles.boxes}>
{range({ end: 20 }).map((i) => (
<View key={`box-${i}`} style={i % 2 === 0 ? styles.redBox : styles.greenBox} />
))}
</View>
</ScrollViewWithHeaders>
);
};

export default TransparentSurface;

const styles = StyleSheet.create({
container: {
flex: 1,
zIndex: -100,
},
contentContainer: {
paddingTop: 44,
},
redBox: {
backgroundColor: 'red',
height: 200,
width: 200,
},
greenBox: {
backgroundColor: 'green',
height: 200,
width: 200,
},
headerTitle: {
fontSize: 16,
fontWeight: 'bold',
},
boxes: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
gap: 12,
flexWrap: 'wrap',
},
title: { fontSize: 32, fontWeight: 'bold' },
leftHeader: { gap: 2 },
});
39 changes: 37 additions & 2 deletions src/components/containers/FlashList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ const FlashListWithHeadersInputComp = <ItemT extends any = any>(
disableAutoFixScroll = false,
/** At the moment, we will not allow overriding of this since the scrollHandler needs it. */
onScroll: _unusedOnScroll,
absoluteHeader = false,
initialAbsoluteHeaderHeight = 0,
contentContainerStyle = {},
automaticallyAdjustsScrollIndicatorInsets,
...rest
}: AnimatedFlashListType<ItemT>,
ref: React.Ref<FlashList<ItemT>>
Expand All @@ -50,11 +54,15 @@ const FlashListWithHeadersInputComp = <ItemT extends any = any>(
largeHeaderOpacity,
scrollHandler,
debouncedFixScroll,
absoluteHeaderHeight,
onAbsoluteHeaderLayout,
} = useScrollContainerLogic({
scrollRef,
largeHeaderShown,
disableAutoFixScroll,
largeHeaderExists: !!LargeHeaderComponent,
absoluteHeader,
initialAbsoluteHeaderHeight,
});

return (
Expand All @@ -66,7 +74,7 @@ const FlashListWithHeadersInputComp = <ItemT extends any = any>(
!ignoreRightSafeArea && { paddingRight: insets.right },
]}
>
{HeaderComponent({ showNavBar, scrollY })}
{!absoluteHeader && HeaderComponent({ showNavBar, scrollY })}
<AnimatedFlashList
ref={scrollRef}
scrollEventThrottle={16}
Expand All @@ -89,6 +97,19 @@ const FlashListWithHeadersInputComp = <ItemT extends any = any>(
debouncedFixScroll();
if (onMomentumScrollEnd) onMomentumScrollEnd(e);
}}
// eslint-disable-next-line react-native/no-inline-styles
contentContainerStyle={{
// The reason why we do this is because FlashList does not support an array of
// styles (will throw a warning when you supply one).
...contentContainerStyle,
paddingTop: absoluteHeader ? absoluteHeaderHeight : 0,
}}
automaticallyAdjustsScrollIndicatorInsets={
automaticallyAdjustsScrollIndicatorInsets !== undefined
? automaticallyAdjustsScrollIndicatorInsets
: !absoluteHeader
}
scrollIndicatorInsets={{ top: absoluteHeader ? absoluteHeaderHeight : 0 }}
ListHeaderComponent={
LargeHeaderComponent ? (
<View
Expand All @@ -106,6 +127,12 @@ const FlashListWithHeadersInputComp = <ItemT extends any = any>(
}
{...rest}
/>

{absoluteHeader && (
<View style={styles.absoluteHeader} onLayout={onAbsoluteHeaderLayout}>
{HeaderComponent({ showNavBar, scrollY })}
</View>
)}
</View>
);
};
Expand All @@ -117,4 +144,12 @@ const FlashListWithHeaders = React.forwardRef(FlashListWithHeadersInputComp) as

export default FlashListWithHeaders;

const styles = StyleSheet.create({ container: { flex: 1 } });
const styles = StyleSheet.create({
container: { flex: 1 },
absoluteHeader: {
position: 'absolute',
top: 0,
right: 0,
left: 0,
},
});
38 changes: 36 additions & 2 deletions src/components/containers/FlatList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ const FlatListWithHeadersInputComp = <ItemT extends unknown>(
disableAutoFixScroll = false,
/** At the moment, we will not allow overriding of this since the scrollHandler needs it. */
onScroll: _unusedOnScroll,
absoluteHeader = false,
initialAbsoluteHeaderHeight = 0,
contentContainerStyle,
automaticallyAdjustsScrollIndicatorInsets,
...rest
}: AnimatedFlatListProps<ItemT> & SharedScrollContainerProps,
ref: React.Ref<Animated.FlatList<ItemT> | null>
Expand All @@ -46,11 +50,15 @@ const FlatListWithHeadersInputComp = <ItemT extends unknown>(
largeHeaderOpacity,
scrollHandler,
debouncedFixScroll,
absoluteHeaderHeight,
onAbsoluteHeaderLayout,
} = useScrollContainerLogic({
scrollRef,
largeHeaderShown,
disableAutoFixScroll,
largeHeaderExists: !!LargeHeaderComponent,
absoluteHeader,
initialAbsoluteHeaderHeight,
});

return (
Expand All @@ -62,7 +70,7 @@ const FlatListWithHeadersInputComp = <ItemT extends unknown>(
!ignoreRightSafeArea && { paddingRight: insets.right },
]}
>
{HeaderComponent({ showNavBar, scrollY })}
{!absoluteHeader && HeaderComponent({ showNavBar, scrollY })}
<Animated.FlatList
ref={scrollRef}
scrollEventThrottle={16}
Expand All @@ -85,6 +93,18 @@ const FlatListWithHeadersInputComp = <ItemT extends unknown>(
debouncedFixScroll();
if (onMomentumScrollEnd) onMomentumScrollEnd(e);
}}
contentContainerStyle={[
// @ts-ignore
// Unfortunately there are issues with Reanimated typings, so will ignore for now.
contentContainerStyle,
absoluteHeader ? { paddingTop: absoluteHeaderHeight } : undefined,
]}
automaticallyAdjustsScrollIndicatorInsets={
automaticallyAdjustsScrollIndicatorInsets !== undefined
? automaticallyAdjustsScrollIndicatorInsets
: !absoluteHeader
}
scrollIndicatorInsets={{ top: absoluteHeader ? absoluteHeaderHeight : 0 }}
ListHeaderComponent={
LargeHeaderComponent ? (
<View
Expand All @@ -102,6 +122,12 @@ const FlatListWithHeadersInputComp = <ItemT extends unknown>(
}
{...rest}
/>

{absoluteHeader && (
<View style={styles.absoluteHeader} onLayout={onAbsoluteHeaderLayout}>
{HeaderComponent({ showNavBar, scrollY })}
</View>
)}
</View>
);
};
Expand All @@ -114,4 +140,12 @@ const FlatListWithHeaders = React.forwardRef(FlatListWithHeadersInputComp) as <I

export default FlatListWithHeaders;

const styles = StyleSheet.create({ container: { flex: 1 } });
const styles = StyleSheet.create({
container: { flex: 1 },
absoluteHeader: {
position: 'absolute',
top: 0,
right: 0,
left: 0,
},
});
Loading