UNPKG

@drivy/cobalt

Version:

Opinionated design system for Drivy's projects.

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