import { Children, cloneElement, isValidElement, type ReactElement, type ReactNode, useCallback, useEffect, useMemo, useRef, } from 'react' import { AccessibilityInfo, findNodeHandle, Pressable, Text, View, } from 'react-native' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useA11y} from '#/state/a11y' /** * Conditionally wraps children in a `FocusTrap` component based on whether * screen reader support is enabled. THIS SHOULD BE USED SPARINGLY, only when * no better option is available. */ export function FocusScope({children}: {children: ReactNode}) { const {screenReaderEnabled} = useA11y() return screenReaderEnabled ? {children} : children } /** * `FocusTrap` is intended as a last-ditch effort to ensure that users keep * focus within a certain section of the app, like an overlay. * * It works by placing "guards" at the start and end of the active content. * Then when the user reaches either of those guards, it will announce that * they have reached the start or end of the content and tell them how to * remain within the active content section. */ function FocusTrap({children}: {children: ReactNode}) { const {_} = useLingui() const child = useRef(null) /* * Here we add a ref to the first child of this component. This currently * overrides any ref already on that first child, so we throw an error here * to prevent us from ever accidentally doing this. */ const decoratedChildren = useMemo(() => { return Children.toArray(children).map((node, i) => { if (i === 0 && isValidElement(node)) { const n = node as ReactElement if (n.props.ref !== undefined) { throw new Error( 'FocusScope needs to override the ref on its first child.', ) } return cloneElement(n, { ...n.props, ref: child, }) } return node }) }, [children]) const focusNode = useCallback((ref: View | null) => { if (!ref) return const node = findNodeHandle(ref) if (node) { AccessibilityInfo.setAccessibilityFocus(node) } }, []) useEffect(() => { setTimeout(() => { focusNode(child.current) }, 1e3) }, [focusNode]) return ( <> { switch (event.nativeEvent.actionName) { case 'activate': { focusNode(child.current) } } }}> {decoratedChildren} { switch (event.nativeEvent.actionName) { case 'activate': { focusNode(child.current) } } }}> ) } function Noop() { return ( {' '} ) }