UNPKG

@base-ui/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.

128 lines (126 loc) 5.23 kB
"use strict"; 'use client'; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.useFloating = useFloating; var React = _interopRequireWildcard(require("react")); var _useIsoLayoutEffect = require("@base-ui/utils/useIsoLayoutEffect"); var _reactDom = require("@floating-ui/react-dom"); var _dom = require("@floating-ui/utils/dom"); var _FloatingTree = require("../components/FloatingTree"); var _useFloatingRootContext = require("./useFloatingRootContext"); /** * Provides data to position a floating element and context to add interactions. * @see https://floating-ui.com/docs/useFloating */ function useFloating(options = {}) { const { nodeId, externalTree } = options; const internalStore = (0, _useFloatingRootContext.useFloatingRootContext)(options); const store = options.rootContext || internalStore; const referenceElement = store.useState('referenceElement'); const floatingElement = store.useState('floatingElement'); const domReferenceElement = store.useState('domReferenceElement'); const open = store.useState('open'); const floatingId = store.useState('floatingId'); const [positionReference, setPositionReferenceRaw] = React.useState(null); const [localDomReference, setLocalDomReference] = React.useState(undefined); const [localFloatingElement, setLocalFloatingElement] = React.useState(undefined); const domReferenceRef = React.useRef(null); const tree = (0, _FloatingTree.useFloatingTree)(externalTree); const storeElements = React.useMemo(() => ({ reference: referenceElement, floating: floatingElement, domReference: domReferenceElement }), [referenceElement, floatingElement, domReferenceElement]); const position = (0, _reactDom.useFloating)({ ...options, elements: { ...storeElements, ...(positionReference && { reference: positionReference }) } }); const localDomReferenceElement = (0, _dom.isElement)(localDomReference) ? localDomReference : null; const syncedFloatingElement = localFloatingElement === undefined ? store.state.floatingElement : localFloatingElement; store.useSyncedValue('referenceElement', localDomReference ?? null); store.useSyncedValue('domReferenceElement', localDomReference === undefined ? domReferenceElement : localDomReferenceElement); store.useSyncedValue('floatingElement', syncedFloatingElement); const setPositionReference = React.useCallback(node => { const computedPositionReference = (0, _dom.isElement)(node) ? { getBoundingClientRect: () => node.getBoundingClientRect(), getClientRects: () => node.getClientRects(), contextElement: node } : node; // Store the positionReference in state if the DOM reference is specified externally via the // `elements.reference` option. This ensures that it won't be overridden on future renders. setPositionReferenceRaw(computedPositionReference); position.refs.setReference(computedPositionReference); }, [position.refs]); const setReference = React.useCallback(node => { if ((0, _dom.isElement)(node) || node === null) { domReferenceRef.current = node; setLocalDomReference(node); } // Backwards-compatibility for passing a virtual element to `reference` // after it has set the DOM reference. if ((0, _dom.isElement)(position.refs.reference.current) || position.refs.reference.current === null || // Don't allow setting virtual elements using the old technique back to // `null` to support `positionReference` + an unstable `reference` // callback ref. node !== null && !(0, _dom.isElement)(node)) { position.refs.setReference(node); } }, [position.refs, setLocalDomReference]); const setFloating = React.useCallback(node => { setLocalFloatingElement(node); position.refs.setFloating(node); }, [position.refs]); const refs = React.useMemo(() => ({ ...position.refs, setReference, setFloating, setPositionReference, domReference: domReferenceRef }), [position.refs, setReference, setFloating, setPositionReference]); const elements = React.useMemo(() => ({ ...position.elements, domReference: domReferenceElement }), [position.elements, domReferenceElement]); const context = React.useMemo(() => ({ ...position, dataRef: store.context.dataRef, open, onOpenChange: store.setOpen, events: store.context.events, floatingId, refs, elements, nodeId, rootStore: store }), [position, refs, elements, nodeId, store, open, floatingId]); (0, _useIsoLayoutEffect.useIsoLayoutEffect)(() => { if (domReferenceElement) { domReferenceRef.current = domReferenceElement; } }, [domReferenceElement]); (0, _useIsoLayoutEffect.useIsoLayoutEffect)(() => { store.context.dataRef.current.floatingContext = context; const node = tree?.nodesRef.current.find(n => n.id === nodeId); if (node) { node.context = context; } }); return React.useMemo(() => ({ ...position, context, refs, elements, rootStore: store }), [position, refs, elements, context, store]); }