diff options
Diffstat (limited to 'src/state/dialogs/index.tsx')
-rw-r--r-- | src/state/dialogs/index.tsx | 83 |
1 files changed, 69 insertions, 14 deletions
diff --git a/src/state/dialogs/index.tsx b/src/state/dialogs/index.tsx index 4cafaa086..951105a50 100644 --- a/src/state/dialogs/index.tsx +++ b/src/state/dialogs/index.tsx @@ -1,20 +1,38 @@ import React from 'react' -import {DialogControlProps} from '#/components/Dialog' +import {SharedValue, useSharedValue} from 'react-native-reanimated' +import {DialogControlRefProps} from '#/components/Dialog' +import {Provider as GlobalDialogsProvider} from '#/components/dialogs/Context' -const DialogContext = React.createContext<{ +interface IDialogContext { + /** + * The currently active `useDialogControl` hooks. + */ activeDialogs: React.MutableRefObject< - Map<string, React.MutableRefObject<DialogControlProps>> + Map<string, React.MutableRefObject<DialogControlRefProps>> > -}>({ - activeDialogs: { - current: new Map(), - }, -}) + /** + * The currently open dialogs, referenced by their IDs, generated from + * `useId`. + */ + openDialogs: React.MutableRefObject<Set<string>> + /** + * The counterpart to `accessibilityViewIsModal` for Android. This property + * applies to the parent of all non-modal views, and prevents TalkBack from + * navigating within content beneath an open dialog. + * + * @see https://reactnative.dev/docs/accessibility#importantforaccessibility-android + */ + importantForAccessibility: SharedValue<'auto' | 'no-hide-descendants'> +} + +const DialogContext = React.createContext<IDialogContext>({} as IDialogContext) const DialogControlContext = React.createContext<{ - closeAllDialogs(): void + closeAllDialogs(): boolean + setDialogIsOpen(id: string, isOpen: boolean): void }>({ - closeAllDialogs: () => {}, + closeAllDialogs: () => false, + setDialogIsOpen: () => {}, }) export function useDialogStateContext() { @@ -27,17 +45,54 @@ export function useDialogStateControlContext() { export function Provider({children}: React.PropsWithChildren<{}>) { const activeDialogs = React.useRef< - Map<string, React.MutableRefObject<DialogControlProps>> + Map<string, React.MutableRefObject<DialogControlRefProps>> >(new Map()) + const openDialogs = React.useRef<Set<string>>(new Set()) + const importantForAccessibility = useSharedValue< + 'auto' | 'no-hide-descendants' + >('auto') + const closeAllDialogs = React.useCallback(() => { activeDialogs.current.forEach(dialog => dialog.current.close()) + return openDialogs.current.size > 0 }, []) - const context = React.useMemo(() => ({activeDialogs}), []) - const controls = React.useMemo(() => ({closeAllDialogs}), [closeAllDialogs]) + + const setDialogIsOpen = React.useCallback( + (id: string, isOpen: boolean) => { + if (isOpen) { + openDialogs.current.add(id) + importantForAccessibility.value = 'no-hide-descendants' + } else { + openDialogs.current.delete(id) + if (openDialogs.current.size < 1) { + importantForAccessibility.value = 'auto' + } + } + }, + [importantForAccessibility], + ) + + const context = React.useMemo<IDialogContext>( + () => ({ + activeDialogs: { + current: new Map(), + }, + openDialogs: { + current: new Set(), + }, + importantForAccessibility, + }), + [importantForAccessibility], + ) + const controls = React.useMemo( + () => ({closeAllDialogs, setDialogIsOpen}), + [closeAllDialogs, setDialogIsOpen], + ) + return ( <DialogContext.Provider value={context}> <DialogControlContext.Provider value={controls}> - {children} + <GlobalDialogsProvider>{children}</GlobalDialogsProvider> </DialogControlContext.Provider> </DialogContext.Provider> ) |