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.

158 lines (157 loc) 4.94 kB
"use strict"; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.FloatingDelayGroup = FloatingDelayGroup; exports.useDelayGroup = useDelayGroup; var React = _interopRequireWildcard(require("react")); var _useTimeout = require("@base-ui-components/utils/useTimeout"); var _useIsoLayoutEffect = require("@base-ui-components/utils/useIsoLayoutEffect"); var _useHover = require("../hooks/useHover"); var _createBaseUIEventDetails = require("../../utils/createBaseUIEventDetails"); var _reasons = require("../../utils/reasons"); var _jsxRuntime = require("react/jsx-runtime"); const FloatingDelayGroupContext = /*#__PURE__*/React.createContext({ hasProvider: false, timeoutMs: 0, delayRef: { current: 0 }, initialDelayRef: { current: 0 }, timeout: new _useTimeout.Timeout(), currentIdRef: { current: null }, currentContextRef: { current: null } }); if (process.env.NODE_ENV !== "production") FloatingDelayGroupContext.displayName = "FloatingDelayGroupContext"; /** * Experimental next version of `FloatingDelayGroup` to become the default * in the future. This component is not yet stable. * Provides context for a group of floating elements that should share a * `delay`. Unlike `FloatingDelayGroup`, `useDelayGroup` with this * component does not cause a re-render of unrelated consumers of the * context when the delay changes. * @see https://floating-ui.com/docs/FloatingDelayGroup * @internal */ function FloatingDelayGroup(props) { const { children, delay, timeoutMs = 0 } = props; const delayRef = React.useRef(delay); const initialDelayRef = React.useRef(delay); const currentIdRef = React.useRef(null); const currentContextRef = React.useRef(null); const timeout = (0, _useTimeout.useTimeout)(); return /*#__PURE__*/(0, _jsxRuntime.jsx)(FloatingDelayGroupContext.Provider, { value: React.useMemo(() => ({ hasProvider: true, delayRef, initialDelayRef, currentIdRef, timeoutMs, currentContextRef, timeout }), [timeoutMs, timeout]), children: children }); } /** * Enables grouping when called inside a component that's a child of a * `FloatingDelayGroup`. * @see https://floating-ui.com/docs/FloatingDelayGroup * @internal */ function useDelayGroup(context, options = { open: false }) { const store = 'rootStore' in context ? context.rootStore : context; const floatingId = store.useState('floatingId'); const { enabled = true, open } = options; const groupContext = React.useContext(FloatingDelayGroupContext); const { currentIdRef, delayRef, timeoutMs, initialDelayRef, currentContextRef, hasProvider, timeout } = groupContext; const [isInstantPhase, setIsInstantPhase] = React.useState(false); (0, _useIsoLayoutEffect.useIsoLayoutEffect)(() => { function unset() { setIsInstantPhase(false); currentContextRef.current?.setIsInstantPhase(false); currentIdRef.current = null; currentContextRef.current = null; delayRef.current = initialDelayRef.current; } if (!enabled) { return undefined; } if (!currentIdRef.current) { return undefined; } if (!open && currentIdRef.current === floatingId) { setIsInstantPhase(false); if (timeoutMs) { timeout.start(timeoutMs, unset); return () => { timeout.clear(); }; } unset(); } return undefined; }, [enabled, open, floatingId, currentIdRef, delayRef, timeoutMs, initialDelayRef, currentContextRef, timeout]); (0, _useIsoLayoutEffect.useIsoLayoutEffect)(() => { if (!enabled) { return; } if (!open) { return; } const prevContext = currentContextRef.current; const prevId = currentIdRef.current; currentContextRef.current = { onOpenChange: store.setOpen, setIsInstantPhase }; currentIdRef.current = floatingId; delayRef.current = { open: 0, close: (0, _useHover.getDelay)(initialDelayRef.current, 'close') }; if (prevId !== null && prevId !== floatingId) { timeout.clear(); setIsInstantPhase(true); prevContext?.setIsInstantPhase(true); prevContext?.onOpenChange(false, (0, _createBaseUIEventDetails.createChangeEventDetails)(_reasons.REASONS.none)); } else { setIsInstantPhase(false); prevContext?.setIsInstantPhase(false); } }, [enabled, open, floatingId, store, currentIdRef, delayRef, timeoutMs, initialDelayRef, currentContextRef, timeout]); (0, _useIsoLayoutEffect.useIsoLayoutEffect)(() => { return () => { currentContextRef.current = null; }; }, [currentContextRef]); return React.useMemo(() => ({ hasProvider, delayRef, isInstantPhase }), [hasProvider, delayRef, isInstantPhase]); }