@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
JavaScript
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
});
}