UNPKG

@drivy/cobalt

Version:

Opinionated design system for Drivy's projects.

149 lines (146 loc) 5.61 kB
import { useState, useRef, useCallback, useEffect } from 'react'; import { registerPopover } from './popoverRegistry.js'; import { useDesktopPopoverCore } from './useDesktopPopoverCore.js'; function normalizeDelay(delay) { var _a, _b; if (typeof delay === "number") { return { open: delay, close: delay }; } return { open: (_a = delay === null || delay === void 0 ? void 0 : delay.open) !== null && _a !== void 0 ? _a : 0, close: (_b = delay === null || delay === void 0 ? void 0 : delay.close) !== null && _b !== void 0 ? _b : 0, }; } function useSingletonPopover({ trigger = "mouseenter", delay = 0, interactive = false, gracePeriod = 100, ...options }) { const [isOpen, setIsOpen] = useState(false); const [referenceElement, setReferenceElement] = useState(null); const [content, setContent] = useState(null); const openTimeoutRef = useRef(null); const closeTimeoutRef = useRef(null); const delays = normalizeDelay(delay); const clearOpenTimeout = useCallback(() => { if (openTimeoutRef.current != null) { window.clearTimeout(openTimeoutRef.current); openTimeoutRef.current = null; } }, []); const clearCloseTimeout = useCallback(() => { if (closeTimeoutRef.current != null) { window.clearTimeout(closeTimeoutRef.current); closeTimeoutRef.current = null; } }, []); useEffect(() => { return () => { if (openTimeoutRef.current != null) { window.clearTimeout(openTimeoutRef.current); } if (closeTimeoutRef.current != null) { window.clearTimeout(closeTimeoutRef.current); } }; }, []); const closePopover = useCallback((immediate = false, useGracePeriod = false) => { clearOpenTimeout(); const run = () => { setIsOpen(false); }; const closeDelay = useGracePeriod ? Math.max(delays.close, gracePeriod) : delays.close; if (immediate || closeDelay === 0) { clearCloseTimeout(); run(); return; } clearCloseTimeout(); closeTimeoutRef.current = window.setTimeout(run, closeDelay); }, [clearOpenTimeout, clearCloseTimeout, delays.close, gracePeriod]); useEffect(() => { return registerPopover(() => { closePopover(true); }); }, [closePopover]); const core = useDesktopPopoverCore({ ...options, isOpen, onOpenChange: setIsOpen, referenceElement, trigger: "manual", interactive, getFloatingExtraProps: interactive && trigger === "mouseenter" ? () => ({ onMouseEnter: () => { clearCloseTimeout(); }, onMouseLeave: () => { closePopover(false, true); }, }) : undefined, }); const openWithItem = (item, element, immediate = false) => { clearCloseTimeout(); const run = () => { setReferenceElement(element); core.refs.setReference(element); setContent(item.content); setIsOpen(true); }; const shouldOpenImmediately = immediate || isOpen || delays.open === 0; if (shouldOpenImmediately) { clearOpenTimeout(); run(); return; } clearOpenTimeout(); openTimeoutRef.current = window.setTimeout(run, delays.open); }; const getReferenceProps = (item, userProps = {}) => { const { onMouseEnter, onMouseLeave, onPointerEnter, onPointerLeave, onClick, ...restUserProps } = userProps; return core.getReferenceProps({ ...restUserProps, onMouseEnter: (e) => { onMouseEnter === null || onMouseEnter === void 0 ? void 0 : onMouseEnter(e); if (trigger === "mouseenter") { openWithItem(item, e.currentTarget); } }, onPointerEnter: (e) => { onPointerEnter === null || onPointerEnter === void 0 ? void 0 : onPointerEnter(e); }, onMouseLeave: (e) => { onMouseLeave === null || onMouseLeave === void 0 ? void 0 : onMouseLeave(e); if (trigger === "mouseenter" && !interactive) { closePopover(false, true); } }, onPointerLeave: (e) => { onPointerLeave === null || onPointerLeave === void 0 ? void 0 : onPointerLeave(e); }, onClick: (e) => { onClick === null || onClick === void 0 ? void 0 : onClick(e); if (trigger === "click") { const sameReference = referenceElement === e.currentTarget; setReferenceElement(e.currentTarget); core.refs.setReference(e.currentTarget); setContent(item.content); if (sameReference && isOpen) { setIsOpen(false); } else { setIsOpen(true); } } }, }); }; return { getReferenceProps, renderFloating: () => content == null ? null : core.renderFloating(content), close: () => closePopover(true), isOpen, }; } export { useSingletonPopover }; //# sourceMappingURL=useSingletonPopover.js.map