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.

97 lines (94 loc) 3.19 kB
import * as React from 'react'; import { useId } from '@base-ui-components/utils/useId'; import { useIsoLayoutEffect } from '@base-ui-components/utils/useIsoLayoutEffect'; import { createEventEmitter } from "../utils/createEventEmitter.js"; import { jsx as _jsx } from "react/jsx-runtime"; const FloatingNodeContext = /*#__PURE__*/React.createContext(null); if (process.env.NODE_ENV !== "production") FloatingNodeContext.displayName = "FloatingNodeContext"; const FloatingTreeContext = /*#__PURE__*/React.createContext(null); /** * Returns the parent node id for nested floating elements, if available. * Returns `null` for top-level floating elements. */ if (process.env.NODE_ENV !== "production") FloatingTreeContext.displayName = "FloatingTreeContext"; export const useFloatingParentNodeId = () => React.useContext(FloatingNodeContext)?.id || null; /** * Returns the nearest floating tree context, if available. */ export const useFloatingTree = () => React.useContext(FloatingTreeContext); /** * Registers a node into the `FloatingTree`, returning its id. * @see https://floating-ui.com/docs/FloatingTree */ export function useFloatingNodeId(customParentId) { const id = useId(); const tree = useFloatingTree(); const reactParentId = useFloatingParentNodeId(); const parentId = customParentId || reactParentId; useIsoLayoutEffect(() => { if (!id) { return undefined; } const node = { id, parentId }; tree?.addNode(node); return () => { tree?.removeNode(node); }; }, [tree, id, parentId]); return id; } /** * Provides parent node context for nested floating elements. * @see https://floating-ui.com/docs/FloatingTree * @internal */ export function FloatingNode(props) { const { children, id } = props; const parentId = useFloatingParentNodeId(); return /*#__PURE__*/_jsx(FloatingNodeContext.Provider, { value: React.useMemo(() => ({ id, parentId }), [id, parentId]), children: children }); } /** * Provides context for nested floating elements when they are not children of * each other on the DOM. * This is not necessary in all cases, except when there must be explicit communication between parent and child floating elements. It is necessary for: * - The `bubbles` option in the `useDismiss()` Hook * - Nested virtual list navigation * - Nested floating elements that each open on hover * - Custom communication between parent and child floating elements * @see https://floating-ui.com/docs/FloatingTree * @internal */ export function FloatingTree(props) { const { children } = props; const nodesRef = React.useRef([]); const addNode = React.useCallback(node => { nodesRef.current = [...nodesRef.current, node]; }, []); const removeNode = React.useCallback(node => { nodesRef.current = nodesRef.current.filter(n => n !== node); }, []); const [events] = React.useState(() => createEventEmitter()); return /*#__PURE__*/_jsx(FloatingTreeContext.Provider, { value: React.useMemo(() => ({ nodesRef, addNode, removeNode, events }), [addNode, removeNode, events]), children: children }); }