UNPKG

@yamada-ui/react

Version:

React UI components of the Yamada, by the Yamada, for the Yamada built with React and Emotion

210 lines (206 loc) • 6.62 kB
"use client"; import { getEventRelatedTarget, runKeyAction } from "../../utils/dom.js"; import { useSafeLayoutEffect, useUnmountEffect } from "../../utils/effect.js"; import { assignRef, mergeRefs } from "../../utils/ref.js"; import { utils_exports } from "../../utils/index.js"; import { useEnvironment } from "../../core/system/environment-provider.js"; import { useSplitProps } from "../../core/components/props.js"; import { useDisclosure } from "../../hooks/use-disclosure/use-disclosure.js"; import { useEventListener } from "../../hooks/use-event-listener/index.js"; import { useFocusOnShow } from "../../hooks/use-focus/index.js"; import { useOutsideClick } from "../../hooks/use-outside-click/index.js"; import { popperProps, usePopper } from "../../hooks/use-popper/index.js"; import { useCallback, useEffect, useId, useRef } from "react"; //#region src/components/popover/use-popover.tsx const usePopover = ({ autoFocus = true, autoUpdate, modal = false, blockScrollOnMount = modal, closeOnBlur = true, closeOnEsc = true, closeOnScroll, defaultOpen, disabled, elements, flip, gutter, initialFocusRef, matchWidth, middleware, offset, open: openProp, openOnClick = true, placement = "end", platform, preventOverflow, strategy, transferFocus = true, transform, updateRef, whileElementsMounted, onClose: onCloseProp, onOpen: onOpenProp } = {}) => { const { getDocument } = useEnvironment(); const headerId = useId(); const bodyId = useId(); const contentId = useId(); const anchorRef = useRef(null); const triggerRef = useRef(null); const contentRef = useRef(null); const openTimeout = useRef(void 0); const closeTimeout = useRef(void 0); const { open, onClose, onOpen } = useDisclosure({ defaultOpen, open: openProp, onClose: onCloseProp, onOpen: onOpenProp }); const { refs, update, getPopperProps } = usePopper({ autoUpdate, elements, flip, gutter, matchWidth, middleware, offset, open, placement, platform, preventOverflow, strategy, transform, whileElementsMounted }); assignRef(updateRef, update); const onKeyDown = useCallback((ev) => { runKeyAction(ev, { Escape: () => { if (!closeOnEsc) return; onClose(); triggerRef.current?.focus(); } }); }, [closeOnEsc, onClose]); const onBlur = useCallback((ev) => { const relatedTarget = getEventRelatedTarget(ev); const popup = relatedTarget?.hasAttribute("data-popup"); if ((0, utils_exports.contains)(triggerRef.current, relatedTarget)) return; if ((0, utils_exports.contains)(contentRef.current, relatedTarget)) return; if ((0, utils_exports.contains)(contentRef.current, ev.target) && popup) return; if (closeOnBlur) onClose(); }, [closeOnBlur, onClose]); useEventListener(getDocument(), "scroll", () => { if (open && closeOnScroll) onClose(); }); useFocusOnShow(contentRef, { focusTarget: initialFocusRef, shouldFocus: autoFocus, visible: open }); useOutsideClick({ ref: [contentRef, triggerRef], enabled: open && closeOnBlur, handler: onClose }); useSafeLayoutEffect(() => { const el = contentRef.current; const hasHeader = !!getDocument()?.getElementById(headerId); const hasBody = !!getDocument()?.getElementById(bodyId); if (el && hasHeader) (0, utils_exports.setAttribute)(el, "aria-labelledby", headerId); if (el && hasBody) (0, utils_exports.setAttribute)(el, "aria-describedby", bodyId); }, [ open, headerId, bodyId ]); useEffect(() => { if (!open || !modal) return; return (0, utils_exports.focusTrap)(contentRef.current); }, [open, modal]); useEffect(() => { if (!open || !blockScrollOnMount) return; return (0, utils_exports.scrollLock)(contentRef.current); }, [ open, modal, blockScrollOnMount ]); useEffect(() => { if (!open || modal || !transferFocus) return; return (0, utils_exports.focusTransfer)(contentRef.current, triggerRef.current); }, [ open, modal, transferFocus ]); useUnmountEffect(() => { clearTimeout(openTimeout.current); clearTimeout(closeTimeout.current); }); const getTriggerProps = useCallback(({ ref,...props } = {}) => ({ "aria-controls": open ? contentId : void 0, "aria-disabled": (0, utils_exports.ariaAttr)(disabled), "aria-expanded": open, "aria-haspopup": "dialog", role: "button", ...props, ref: mergeRefs(ref, triggerRef, (node) => { if (anchorRef.current == null) refs.setReference(node); }), onBlur: (0, utils_exports.handlerAll)(props.onBlur, (ev) => !(0, utils_exports.contains)(contentRef.current, getEventRelatedTarget(ev)) && closeOnBlur ? onClose() : void 0), onClick: (0, utils_exports.handlerAll)(props.onClick, !open ? !disabled && openOnClick ? onOpen : void 0 : onClose), onKeyDown: (0, utils_exports.handlerAll)(props.onKeyDown, onKeyDown) }), [ closeOnBlur, contentId, disabled, onClose, onKeyDown, onOpen, open, openOnClick, refs ]); const getAnchorProps = useCallback(({ ref,...props } = {}) => ({ ...props, ref: mergeRefs(ref, anchorRef, refs.setReference) }), [refs.setReference]); const getPositionerProps = useCallback((props) => { return getPopperProps(props); }, [getPopperProps]); const getContentProps = useCallback(({ ref,...props } = {}) => ({ id: contentId, "aria-hidden": !open, "aria-modal": modal ? "true" : void 0, "data-close": (0, utils_exports.dataAttr)(!open), "data-open": (0, utils_exports.dataAttr)(open), "data-popup": (0, utils_exports.dataAttr)(true), role: "dialog", tabIndex: -1, ...props, ref: mergeRefs(ref, contentRef), onBlur: (0, utils_exports.handlerAll)(props.onBlur, onBlur), onKeyDown: (0, utils_exports.handlerAll)(props.onKeyDown, onKeyDown) }), [ contentId, open, modal, onBlur, onKeyDown ]); const getHeaderProps = useCallback((props) => ({ id: headerId, ...props }), [headerId]); return { open, getAnchorProps, getBodyProps: useCallback((props) => ({ id: bodyId, ...props }), [bodyId]), getContentProps, getFooterProps: useCallback((props) => ({ ...props }), []), getHeaderProps, getPositionerProps, getTriggerProps, onClose, onOpen }; }; const popoverProps = [ ...popperProps, "autoFocus", "transferFocus", "blockScrollOnMount", "closeOnBlur", "closeOnEsc", "closeOnScroll", "openOnClick", "disabled", "initialFocusRef", "modal", "updateRef", "defaultOpen", "onOpen", "onClose", "animationScheme", "duration" ]; const usePopoverProps = (props, omitKeys) => { return useSplitProps(props, popoverProps.filter((key) => !omitKeys?.includes(key))); }; //#endregion export { popoverProps, usePopover, usePopoverProps }; //# sourceMappingURL=use-popover.js.map