1import type { ReactNode } from "react";
2import { ScrollView, useWindowDimensions, View } from "react-native";
3import { KeyboardAvoidingView } from "react-native-keyboard-controller";
4import { useSafeAreaInsets } from "react-native-safe-area-context";
5
6import { fitSheetBottomInset, type FitSheetProps } from "./FitSheet.types";
7import { ScreenContent } from "./ScreenContent";
8export type { FitSheetProps } from "./FitSheet.types";
9
10/** The route sizes the sheet to this content, so the column shrinks to fit and stops short of the top. */
11export function FitSheet({
12 header,
13 footer,
14 avoidsKeyboard,
15 children,
16 contentContainerClassName = "gap-2 p-2",
17 contentContainerStyle,
18 ...scrollProps
19}: FitSheetProps) {
20 const insets = useSafeAreaInsets();
21 return (
22 // Material sheet drags follow the first nested-scrolling native view, and a header's
23 // Compose button is one, so the content renders first in a reversed column.
24 <SheetContainer avoidsKeyboard={avoidsKeyboard}>
25 <ScreenContent fitToContents>
26 <ScrollView
27 {...scrollProps}
28 nestedScrollEnabled
29 contentContainerClassName={contentContainerClassName}
30 className="shrink grow-0"
31 contentContainerStyle={[
32 footer ? undefined : { paddingBottom: fitSheetBottomInset(insets.bottom) },
33 contentContainerStyle,
34 ]}
35 >
36 {children}
37 {/* Partial-height sheets keep submit actions inside the scrolling form. */}
38 {footer ? (
39 <View
40 className="mt-4 bg-sheet-solid pt-3"
41 style={{ paddingBottom: Math.max(insets.bottom, 16) }}
42 >
43 <View style={{ minHeight: 48 }}>{footer}</View>
44 </View>
45 ) : null}
46 </ScrollView>
47 </ScreenContent>
48 {header}
49 </SheetContainer>
50 );
51}
52
53function SheetContainer(props: {
54 readonly avoidsKeyboard?: boolean;
55 readonly children: ReactNode;
56}) {
57 const { height } = useWindowDimensions();
58 const style = { maxHeight: height * 0.92 };
59 return props.avoidsKeyboard ? (
60 <KeyboardAvoidingView
61 automaticOffset
62 behavior="padding"
63 collapsable={false}
64 className="flex-col-reverse bg-sheet"
65 style={style}
66 >
67 {props.children}
68 </KeyboardAvoidingView>
69 ) : (
70 <View collapsable={false} className="flex-col-reverse bg-sheet" style={style}>
71 {props.children}
72 </View>
73 );
74}