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.

122 lines (121 loc) 4.33 kB
'use client'; import * as React from 'react'; import { useClick, useDismiss, useFloatingRootContext, useInteractions } from '@floating-ui/react'; import { useControlled } from '../../utils/useControlled.js'; import { useEventCallback } from '../../utils/useEventCallback.js'; import { useTransitionStatus } from '../../utils/useTransitionStatus.js'; import { useOpenInteractionType } from '../../utils/useOpenInteractionType.js'; import { mergeReactProps } from '../../utils/mergeReactProps.js'; import { useAfterExitAnimation } from '../../utils/useAfterExitAnimation.js'; import { translateOpenChangeReason } from '../../utils/translateOpenChangeReason.js'; export function useDialogRoot(parameters) { const { defaultOpen, dismissible, modal, onNestedDialogClose, onNestedDialogOpen, onOpenChange: onOpenChangeParameter, open: openParam } = parameters; const [open, setOpenUnwrapped] = useControlled({ controlled: openParam, default: defaultOpen, name: 'DialogRoot', state: 'open' }); const popupRef = React.useRef(null); const [titleElementId, setTitleElementId] = React.useState(undefined); const [descriptionElementId, setDescriptionElementId] = React.useState(undefined); const [triggerElement, setTriggerElement] = React.useState(null); const [popupElement, setPopupElement] = React.useState(null); const [popupElementId, setPopupElementId] = React.useState(undefined); const { mounted, setMounted, transitionStatus } = useTransitionStatus(open); const setOpen = useEventCallback((nextOpen, event, reason) => { onOpenChangeParameter?.(nextOpen, event, reason); setOpenUnwrapped(nextOpen); }); useAfterExitAnimation({ open, animatedElementRef: popupRef, onFinished: () => setMounted(false) }); const handleFloatingUIOpenChange = (nextOpen, event, reason) => { setOpen(nextOpen, event, translateOpenChangeReason(reason)); }; const context = useFloatingRootContext({ elements: { reference: triggerElement, floating: popupElement }, open, onOpenChange: handleFloatingUIOpenChange }); const [ownNestedOpenDialogs, setOwnNestedOpenDialogs] = React.useState(0); const isTopmost = ownNestedOpenDialogs === 0; const click = useClick(context); const dismiss = useDismiss(context, { outsidePressEvent: 'mousedown', outsidePress: isTopmost && dismissible, escapeKey: isTopmost }); const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss]); React.useEffect(() => { if (onNestedDialogOpen && open) { onNestedDialogOpen(ownNestedOpenDialogs); } if (onNestedDialogClose && !open) { onNestedDialogClose(); } return () => { if (onNestedDialogClose && open) { onNestedDialogClose(); } if (onNestedDialogOpen && !open) { onNestedDialogOpen(ownNestedOpenDialogs); } }; }, [open, onNestedDialogClose, onNestedDialogOpen, ownNestedOpenDialogs]); const handleNestedDialogOpen = React.useCallback(ownChildrenCount => { setOwnNestedOpenDialogs(ownChildrenCount + 1); }, []); const handleNestedDialogClose = React.useCallback(() => { setOwnNestedOpenDialogs(0); }, []); const { openMethod, triggerProps } = useOpenInteractionType(open); return React.useMemo(() => { return { modal, setOpen, open, titleElementId, setTitleElementId, descriptionElementId, setDescriptionElementId, popupElementId, setPopupElementId, onNestedDialogOpen: handleNestedDialogOpen, onNestedDialogClose: handleNestedDialogClose, nestedOpenDialogCount: ownNestedOpenDialogs, openMethod, mounted, transitionStatus, getTriggerProps: externalProps => getReferenceProps(mergeReactProps(externalProps, triggerProps)), getPopupProps: getFloatingProps, setTriggerElement, setPopupElement, popupRef, floatingRootContext: context }; }, [modal, open, setOpen, titleElementId, descriptionElementId, popupElementId, handleNestedDialogClose, handleNestedDialogOpen, ownNestedOpenDialogs, openMethod, mounted, transitionStatus, getReferenceProps, getFloatingProps, setTriggerElement, setPopupElement, triggerProps, popupRef, context]); }