import { useRef } from 'react'; import { Pressable, ScrollView, StyleSheet, View } from 'react-native'; import { useFocusTrap } from '../../../hooks/use-focus-trap'; import { useMenuKeyboardNavigation } from '../../../hooks/use-menu-keyboard'; import { useReducedMotion } from '../../../hooks/use-reduced-motion'; import { FOCUS_VISIBLE_RING } from '../../../lib/focus-ring'; import { MotiView } from '../../../moti/components/view'; import { AnimatePresence } from '../../../moti/presence/animate-presence'; import { TIMING_BASE, TIMING_INSTANT } from '../../../theme/motion'; import { BUTTON_SIZE } from '../../buttons/Button/button-scale'; import { OverlayBlur } from '../Overlay/overlay-blur'; import type { ActionFABProps } from './action-fab'; import { ActionFABOption } from './action-fab-item'; import { useActionFABDismiss } from './use-action-fab-dismiss'; import type { ActionFABFrame } from './use-action-fab-frame'; type ActionFABMenuProps = Required< Pick< ActionFABProps, 'title' | 'items' | 'position' | 'menuWidth' | 'bottomInset' | 'overlay' | 'closeAccessibilityLabel' | 'testID' | 'menuTestID' > > & { open: boolean; close: () => void; menuId: string; frame: ActionFABFrame; window: { width: number; height: number }; teleported: boolean; }; /** Own focus inside the teleported subtree too: Android mounts it after its parent. */ export function ActionFABMenu({ open, close, title, items, menuId, frame, window, position, menuWidth, bottomInset, overlay, teleported, closeAccessibilityLabel, testID, menuTestID, }: ActionFABMenuProps) { const controlsRef = useRef(null); const reduce = useReducedMotion(); useFocusTrap(controlsRef, open); useMenuKeyboardNavigation(controlsRef, open); useActionFABDismiss(controlsRef, open, close); const anchor = position === 'bottom-left' ? { left: 16 } : { right: 16 }; const triggerSide = BUTTON_SIZE.lg.px; const panelBottom = bottomInset + triggerSide + 12; const maxHeight = Math.max(0, frame.height - panelBottom - 16); const backdropFrame = { position: 'absolute' as const, left: -frame.x, top: -frame.y, width: window.width, height: window.height, }; return ( {open && overlay === 'blur' ? : null} {open && overlay !== 'none' ? ( ) : null} {open ? ( ) : null} {open ? ( {items.map((item, index) => ( ))} ) : null} {open ? ( ) : null} ); }