UNPKG

@base-ui-components/react

Version:

Base UI is a library of headless ('unstyled') React components and low-level hooks. You gain complete control over your app's CSS and accessibility features.

84 lines (82 loc) 2.57 kB
'use client'; import * as React from 'react'; import { useFloating } from '@floating-ui/react'; import { useBaseUiId } from '../../utils/useBaseUiId.js'; import { useForkRef } from '../../utils/useForkRef.js'; import { mergeReactProps } from '../../utils/mergeReactProps.js'; import { useScrollLock } from '../../utils/useScrollLock.js'; import { useEnhancedEffect } from '../../utils/useEnhancedEffect.js'; import { translateOpenChangeReason } from '../../utils/translateOpenChangeReason.js'; export function useDialogPopup(parameters) { const { descriptionElementId, floatingRootContext, getPopupProps, id: idParam, initialFocus, modal, mounted, setOpen, open, openMethod, ref, setPopupElementId, setPopupElement, titleElementId } = parameters; const handleFloatingUIOpenChange = (isOpen, event, reason) => { setOpen(isOpen, event, translateOpenChangeReason(reason)); }; const { context, elements } = useFloating({ open, onOpenChange: handleFloatingUIOpenChange, rootContext: floatingRootContext }); const popupRef = React.useRef(null); const id = useBaseUiId(idParam); const handleRef = useForkRef(ref, popupRef, setPopupElement); useScrollLock(modal && open, elements.floating); // Default initial focus logic: // If opened by touch, focus the popup element to prevent the virtual keyboard from opening // (this is required for Android specifically as iOS handles this automatically). const defaultInitialFocus = React.useCallback(interactionType => { if (interactionType === 'touch') { return popupRef; } return 0; }, []); const resolvedInitialFocus = React.useMemo(() => { if (initialFocus == null) { return defaultInitialFocus(openMethod ?? ''); } if (typeof initialFocus === 'function') { return initialFocus(openMethod ?? ''); } return initialFocus; }, [defaultInitialFocus, initialFocus, openMethod]); useEnhancedEffect(() => { setPopupElementId(id); return () => { setPopupElementId(undefined); }; }, [id, setPopupElementId]); const getRootProps = externalProps => mergeReactProps(externalProps, { 'aria-labelledby': titleElementId ?? undefined, 'aria-describedby': descriptionElementId ?? undefined, 'aria-modal': open && modal ? true : undefined, role: 'dialog', tabIndex: -1, ...getPopupProps(), id, ref: handleRef, hidden: !mounted }); return { floatingContext: context, getRootProps, resolvedInitialFocus }; }