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.

102 lines (98 loc) 4.13 kB
import { EMPTY_OBJECT } from '@base-ui/utils/empty'; import { FloatingRootStore } from "../../floating-ui-react/components/FloatingRootStore.mjs"; import { getEmptyRootContext } from "../../floating-ui-react/utils/getEmptyRootContext.mjs"; /** * State common to all popup stores. */ export function createInitialPopupStoreState() { return { open: false, openProp: undefined, mounted: false, transitionStatus: undefined, floatingRootContext: getEmptyRootContext(), floatingId: undefined, triggerCount: 0, preventUnmountingOnClose: false, payload: undefined, activeTriggerId: null, activeTriggerElement: null, triggerIdProp: undefined, popupElement: null, positionerElement: null, activeTriggerProps: EMPTY_OBJECT, inactiveTriggerProps: EMPTY_OBJECT, popupProps: EMPTY_OBJECT }; } export function createPopupFloatingRootContext(triggerElements, floatingId, nested = false) { return new FloatingRootStore({ open: false, transitionStatus: undefined, floatingElement: null, referenceElement: null, triggerElements, floatingId, syncOnly: true, nested, onOpenChange: undefined }); } const activeTriggerIdSelector = state => state.triggerIdProp ?? state.activeTriggerId; const openSelector = state => state.openProp ?? state.open; const popupIdSelector = state => { const popupId = state.popupElement?.id ?? state.floatingId; return popupId || undefined; }; function triggerOwnsOpenPopup(state, triggerId) { return triggerId !== undefined && openSelector(state) && activeTriggerIdSelector(state) === triggerId; } function triggerOwnsOpenPopupOrIsOnlyTrigger(state, triggerId) { if (triggerOwnsOpenPopup(state, triggerId)) { return true; } return triggerId !== undefined && openSelector(state) && activeTriggerIdSelector(state) == null && state.triggerCount === 1; } export const popupStoreSelectors = { open: openSelector, mounted: state => state.mounted, transitionStatus: state => state.transitionStatus, floatingRootContext: state => state.floatingRootContext, triggerCount: state => state.triggerCount, preventUnmountingOnClose: state => state.preventUnmountingOnClose, payload: state => state.payload, activeTriggerId: activeTriggerIdSelector, activeTriggerElement: state => state.mounted ? state.activeTriggerElement : null, popupId: popupIdSelector, /** * Whether the trigger with the given ID was used to open the popup. */ isTriggerActive: (state, triggerId) => triggerId !== undefined && activeTriggerIdSelector(state) === triggerId, /** * Whether the popup is open and was activated by a trigger with the given ID. */ isOpenedByTrigger: (state, triggerId) => triggerOwnsOpenPopup(state, triggerId), /** * Whether the popup is mounted and was activated by a trigger with the given ID. */ isMountedByTrigger: (state, triggerId) => triggerId !== undefined && activeTriggerIdSelector(state) === triggerId && state.mounted, triggerProps: (state, isActive) => isActive ? state.activeTriggerProps : state.inactiveTriggerProps, /** * Popup id for the trigger that currently owns the open popup. */ triggerPopupId: (state, triggerId) => triggerOwnsOpenPopupOrIsOnlyTrigger(state, triggerId) ? popupIdSelector(state) : undefined, popupProps: state => state.popupProps, popupElement: state => state.popupElement, positionerElement: state => state.positionerElement }; /** * Store members a detached handle-backed trigger reads or invokes for trigger registration and data * forwarding. `set`/`update` are included only for trigger-count and trigger-data bookkeeping; on a * detached (inert) store they are intentionally no-ops, so a write through them is not guaranteed to * be durable. Component handle-store views Pick these from their concrete store (preserving its * context and selectors) and add any component-specific trigger-invoked members such as `setOpen`. */ /** * The subset of a popup store that trigger registration and data forwarding rely on. Narrow enough * that an inert store can be passed while detached. */