import { PlatformPressable, useHeaderHeight } from '@react-navigation/elements' import { NativeStackNavigationOptions } from '@react-navigation/native-stack' import React, { ReactNode } from 'react' import { Platform, StyleSheet, View, useWindowDimensions, type AccessibilityRole, type StyleProp, type ViewStyle, } from 'react-native' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { useTheme } from '../../hooks' import { isLiquidGlassEnabled, MAX_FONT_SIZE_MULTIPLIER_LANDMARK } from '../../utils' import type { ButtonProps, TextButtonProps } from '../display' import { Button, Heading, Icon, IconString, Text, TextButton } from '../display' // ================================= // ====== Exports ================== // ================================= /** * Screen options for the formsheet's NativeStackNavigation route * * Returns a factory rather than an object because callers assign it at module * scope, which runs before ChatProvider reports the host's liquid glass * capability. React Navigation calls the factory per render, so the sheet * corner radius is resolved once that capability is known. * * @param {string} options.headerTitle - Doesn't show in UI but good to have for semantic reasons. * @param {number[]} [options.sheetAllowedDetents] - Controls the height increments the sheet grows to. To prevent bugs in Android, do not specify more then two values and use [0.94] instead of [1] to go full screen. * @param {NativeStackNavigationOptions} [options.rest] - Adds or overrides any valid NativeStackNavigationOptions * @returns {() => NativeStackNavigationOptions} Factory producing merged NativeStackNavigationOptions */ export const getFormSheetScreenOptions = ({ headerTitle, sheetAllowedDetents = [0.25], ...rest }: NativeStackNavigationOptions = {}) => (): NativeStackNavigationOptions => ({ presentation: 'formSheet', headerShown: false, sheetAllowedDetents, sheetCornerRadius: isLiquidGlassEnabled() ? undefined : 16, sheetGrabberVisible: true, headerTitle, ...rest, }) const FormSheet = { Root: FormSheetRoot, Action: FormSheetAction, Header: FormSheetHeader, HeaderTitle: FormSheetHeaderTitle, HeaderActions: FormSheetHeaderActions, HeaderTextButton: FormSheetHeaderTextButton, HeaderButton: FormSheetHeaderButton, } as const type FormSheetComponents = { Root: React.FC Action: React.FC Header: React.FC HeaderTitle: React.FC HeaderActions: React.FC HeaderTextButton: React.FC HeaderButton: React.FC } export default FormSheet as FormSheetComponents export type { FormSheetActionProps, FormSheetHeaderActionsProps, FormSheetHeaderButtonProps, FormSheetHeaderProps, FormSheetHeaderTextButtonProps, FormSheetHeaderTitleProps, FormSheetRootProps, } // ==================================== // ====== ActionsFormSheetRoot ======== // ==================================== interface FormSheetRootProps { children: ReactNode style?: StyleProp contentStyle?: StyleProp showAndroidSheetGrabber?: boolean fitToContents?: boolean } export function FormSheetRoot({ children, style, contentStyle, showAndroidSheetGrabber = true, fitToContents = false, }: FormSheetRootProps) { const styles = useStyles({ fitToContents }) return ( {children} {showAndroidSheetGrabber && } ) } FormSheetRoot.displayName = 'FormSheet.Root' // ==================================== // ====== AndroidSheetGrabber ========= // ==================================== function AndroidSheetGrabber() { const styles = useStyles() return Platform.select({ android: , ios: null, }) } // ==================================== // ====== FormSheetHeader ============= // ==================================== interface FormSheetHeaderProps { children: ReactNode style?: StyleProp } function FormSheetHeader({ children, style }: FormSheetHeaderProps) { const styles = useStyles() return ( {children} ) } FormSheetHeader.displayName = 'FormSheet.Header' // ==================================== // ====== FormSheetHeaderTitle ======== // ==================================== interface FormSheetHeaderTitleProps { children: ReactNode } function FormSheetHeaderTitle({ children }: FormSheetHeaderTitleProps) { const styles = useStyles() return ( {children} ) } FormSheetHeaderTitle.displayName = 'FormSheet.HeaderTitle' // ==================================== // ====== FormSheetHeaderActions ====== // ==================================== interface FormSheetHeaderActionsProps { children: ReactNode } function FormSheetHeaderActions({ children }: FormSheetHeaderActionsProps) { const styles = useStyles() return {children} } FormSheetHeaderActions.displayName = 'FormSheet.HeaderActions' // =========================================== // ====== FormSheetHeaderTextButton ========== // =========================================== // We are omitting `onLongPress` because we are instead supporting `accessibilityShowsLargeContentViewer`. // This is triggered by an `onLongPress` event when an iOS device is in Apple's accessible font scale range. // Triggering this prop will display an almost full screen iOS a11y button. interface FormSheetHeaderTextButtonProps extends Omit { children: string } function FormSheetHeaderTextButton(props: FormSheetHeaderTextButtonProps) { return ( ) } FormSheetHeaderTextButton.displayName = 'FormSheet.HeaderTextButton' // ========================================= // ====== FormSheetHeaderButton ============ // ========================================= // Same `onLongPress`note as `FormSheetHeaderTextButtonProps` interface FormSheetHeaderButtonProps extends Omit {} function FormSheetHeaderButton(props: FormSheetHeaderButtonProps) { return (