@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.
61 lines (59 loc) • 2.2 kB
JavaScript
'use client';
import * as React from 'react';
import { useIsoLayoutEffect } from '@base-ui/utils/useIsoLayoutEffect';
import { isElement } from '@floating-ui/utils/dom';
import { FloatingRootStore } from "../components/FloatingRootStore.js";
/**
* Keeps a FloatingRootStore in sync with the provided PopupStore.
* Uses the provided FloatingRootStore when one exists, otherwise creates one once and updates it on every render.
*/
export function useSyncedFloatingRootContext(options) {
const {
popupStore,
treatPopupAsFloatingElement = false,
floatingRootContext: floatingRootContextProp,
floatingId,
nested,
onOpenChange
} = options;
const open = popupStore.useState('open');
const referenceElement = popupStore.useState('activeTriggerElement');
const floatingElement = popupStore.useState(treatPopupAsFloatingElement ? 'popupElement' : 'positionerElement');
const triggerElements = popupStore.context.triggerElements;
const handleOpenChange = onOpenChange;
const internalStoreRef = React.useRef(null);
if (floatingRootContextProp === undefined && internalStoreRef.current === null) {
internalStoreRef.current = new FloatingRootStore({
open,
transitionStatus: undefined,
referenceElement,
floatingElement,
triggerElements,
onOpenChange: handleOpenChange,
floatingId,
syncOnly: true,
nested
});
}
const store = floatingRootContextProp ?? internalStoreRef.current;
popupStore.useSyncedValue('floatingId', floatingId);
useIsoLayoutEffect(() => {
const valuesToSync = {
open,
floatingId,
referenceElement,
floatingElement
};
if (isElement(referenceElement)) {
valuesToSync.domReferenceElement = referenceElement;
}
if (store.state.positionReference === store.state.referenceElement) {
valuesToSync.positionReference = referenceElement;
}
store.update(valuesToSync);
}, [open, floatingId, referenceElement, floatingElement, store]);
// Keep non-reactive context values fresh for interactions that call `store.setOpen`.
store.context.onOpenChange = handleOpenChange;
store.context.nested = nested;
return store;
}