@jobber/components 8.14.1 → 8.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Autocomplete-es.js +1 -1
- package/dist/BottomSheet-cjs.js +109 -77
- package/dist/BottomSheet-es.js +103 -75
- package/dist/Card/index.cjs +2 -3
- package/dist/Card/index.mjs +2 -3
- package/dist/DataDump/index.cjs +2 -3
- package/dist/DataDump/index.mjs +2 -3
- package/dist/InputNumberExperimental-cjs.js +16 -17
- package/dist/InputNumberExperimental-es.js +2 -3
- package/dist/Menu/index.cjs +2 -3
- package/dist/Menu/index.mjs +2 -3
- package/dist/Menu-cjs.js +11 -56
- package/dist/Menu-es.js +4 -49
- package/dist/MenuSubmenuTrigger-cjs.js +1608 -1793
- package/dist/MenuSubmenuTrigger-es.js +1340 -1522
- package/dist/Modal/index.mjs +1 -1
- package/dist/NumberFieldInput-cjs.js +53 -54
- package/dist/NumberFieldInput-es.js +2 -3
- package/dist/Page/index.cjs +2 -3
- package/dist/Page/index.mjs +2 -3
- package/dist/{DrawerRoot-cjs.js → ScrollAreaViewport-cjs.js} +5488 -4449
- package/dist/{DrawerRoot-es.js → ScrollAreaViewport-es.js} +5423 -4393
- package/dist/{useValueChanged-cjs.js → clamp-cjs.js} +573 -321
- package/dist/{useValueChanged-es.js → clamp-es.js} +569 -322
- package/dist/floating-ui.react-es.js +1 -1
- package/dist/index.cjs +2 -3
- package/dist/index.mjs +2 -3
- package/dist/primitives/BottomSheet/BottomSheet.d.ts +15 -16
- package/dist/primitives/BottomSheet/BottomSheet.types.d.ts +27 -0
- package/dist/primitives/BottomSheet/BottomSheet.utils.d.ts +6 -0
- package/dist/primitives/BottomSheet/index.cjs +8 -3
- package/dist/primitives/BottomSheet/index.d.ts +1 -0
- package/dist/primitives/BottomSheet/index.mjs +8 -3
- package/dist/primitives/InputNumberExperimental/index.cjs +2 -3
- package/dist/primitives/InputNumberExperimental/index.mjs +2 -3
- package/dist/primitives/index.cjs +8 -9
- package/dist/primitives/index.mjs +8 -9
- package/dist/styles.css +62 -1
- package/dist/unstyledPrimitives/index.cjs +6373 -5868
- package/dist/unstyledPrimitives/index.d.ts +2 -0
- package/dist/unstyledPrimitives/index.mjs +6325 -5822
- package/dist/useRenderElement-cjs.js +30 -30
- package/dist/useRenderElement-es.js +31 -31
- package/package.json +2 -2
- package/dist/useBaseUiId-cjs.js +0 -275
- package/dist/useBaseUiId-es.js +0 -251
|
@@ -1,65 +1,13 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { u as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { a as useButton, u as useBaseUiId, b as useCompositeRootContext } from './useBaseUiId-es.js';
|
|
2
|
+
import { u as useFloatingParentNodeId, F as FloatingRootStore, P as PopupTriggerMap, g as useFloatingTree, h as getTarget, i as isTypeableElement, m as matchesFocusVisible, j as isTargetInsideEnabledTrigger, k as createAttribute, l as activeElement, n as contains, T as TYPEABLE_SELECTOR, o as getNodeChildren, p as getFloatingFocusElement, q as isTypeableCombobox, r as isIndexOutOfListBounds, s as getMinListIndex, t as getMaxListIndex, v as createGridCellMap, w as isListIndexDisabled, x as getGridNavigatedIndex, y as getGridCellIndices, z as getGridCellIndexOfCorner, A as ARROW_DOWN, B as ARROW_LEFT, C as ARROW_RIGHT, E as findNonDisabledListIndex, G as enqueueFocus, H as ARROW_UP, I as isElementVisible, J as useDirection, K as DISABLED_TRANSITIONS_STYLE, L as useDialogRootContext, M as popupStateMapping, N as COMPOSITE_KEYS, O as FloatingFocusManager, Q as FloatingPortal, R as POPUP_COLLISION_AVOIDANCE, U as InternalBackdrop, V as inertValue, W as FloatingNode, X as DROPDOWN_COLLISION_AVOIDANCE, Y as popupStoreSelectors, Z as createSelector, _ as ReactStore, $ as createInitialPopupStoreState, a0 as FloatingTreeStore, a1 as fastComponent, a2 as useOnFirstRender, a3 as useFloatingNodeId, a4 as useOpenInteractionType, a5 as useImplicitActiveTrigger, a6 as useOpenStateTransitions, a7 as useScrollLock, a8 as useSyncedFloatingRootContext, a9 as useDismiss, aa as useRole, ab as TYPEAHEAD_RESET_MS, ac as useInteractions, ad as FloatingTree, ae as fastComponentRef, af as useTriggerDataForwarding, ag as useClick, ah as getTabbableBeforeElement, ai as isOutsideEvent, aj as getTabbableAfterElement, ak as getNextTabbable, al as pressableTriggerOpenStateMapping, am as FocusGuard, an as PATIENT_CLICK_THRESHOLD, ao as useTriggerRegistration, ap as triggerOpenStateMapping } from './ScrollAreaViewport-es.js';
|
|
3
|
+
import { a as useRefWithInit, E as EMPTY_OBJECT, u as useRenderElement, f as formatErrorMessage, m as mergeProps, b as useMergedRefs, d as EMPTY_ARRAY } from './useRenderElement-es.js';
|
|
4
|
+
import { C as isMouseLikePointerType, D as useId, d as useIsoLayoutEffect, b as useTimeout, k as createChangeEventDetails, E as triggerFocus, F as isMac, G as isSafari, s as ownerDocument, H as triggerPress, I as escapeKey, f as useOnMount, T as Timeout, a as useStableCallback, J as triggerHover, i as useValueAsRef, A as stopEvent, K as listNavigation, L as focusOut, M as isVirtualClick, N as isVirtualPointerEvent, y as useButton, O as closePress, u as useBaseUiId, P as itemPress, t as transitionStatusMapping, e as useOpenChangeComplete, Q as outsidePress, R as useAnimationsFinished, S as siblingOpen, h as useControlled, c as useTransitionStatus, U as imperativeAction, V as useCompositeRootContext, W as cancelOpen } from './clamp-es.js';
|
|
6
5
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
7
6
|
import { b as isElement, k as getWindow, a as isHTMLElement, d as isLastTraversableNode, e as getParentNode } from './floating-ui.utils.dom-es.js';
|
|
8
7
|
import { e as evaluate, h as getPaddingObject, b as getAlignmentAxis, d as getAlignment, q as getAxisLength, n as clamp, c as getSide, g as getSideAxis } from './index.esm-es.js';
|
|
9
8
|
import { u as useFloating$1, h as hide$1, f as flip, d as size, b as autoUpdate, o as offset, s as shift, l as limitShift } from './floating-ui.react-dom-es.js';
|
|
10
9
|
import * as ReactDOM from 'react-dom';
|
|
11
10
|
|
|
12
|
-
/**
|
|
13
|
-
* A button that closes the dialog.
|
|
14
|
-
* Renders a `<button>` element.
|
|
15
|
-
*
|
|
16
|
-
* Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)
|
|
17
|
-
*/
|
|
18
|
-
const DialogClose = /*#__PURE__*/React.forwardRef(function DialogClose(componentProps, forwardedRef) {
|
|
19
|
-
const {
|
|
20
|
-
render,
|
|
21
|
-
className,
|
|
22
|
-
disabled = false,
|
|
23
|
-
nativeButton = true,
|
|
24
|
-
...elementProps
|
|
25
|
-
} = componentProps;
|
|
26
|
-
const {
|
|
27
|
-
store
|
|
28
|
-
} = useDialogRootContext();
|
|
29
|
-
const open = store.useState('open');
|
|
30
|
-
function handleClick(event) {
|
|
31
|
-
if (open) {
|
|
32
|
-
store.setOpen(false, createChangeEventDetails(closePress, event.nativeEvent));
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
const {
|
|
36
|
-
getButtonProps,
|
|
37
|
-
buttonRef
|
|
38
|
-
} = useButton({
|
|
39
|
-
disabled,
|
|
40
|
-
native: nativeButton
|
|
41
|
-
});
|
|
42
|
-
const state = {
|
|
43
|
-
disabled
|
|
44
|
-
};
|
|
45
|
-
return useRenderElement('button', componentProps, {
|
|
46
|
-
state,
|
|
47
|
-
ref: [forwardedRef, buttonRef],
|
|
48
|
-
props: [{
|
|
49
|
-
onClick: handleClick
|
|
50
|
-
}, elementProps, getButtonProps]
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
if (process.env.NODE_ENV !== "production") DialogClose.displayName = "DialogClose";
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* A button that closes the drawer.
|
|
57
|
-
* Renders a `<button>` element.
|
|
58
|
-
*
|
|
59
|
-
* Documentation: [Base UI Drawer](https://base-ui.com/react/components/drawer)
|
|
60
|
-
*/
|
|
61
|
-
const DrawerClose = DialogClose;
|
|
62
|
-
|
|
63
11
|
function resolveValue(value, pointerType) {
|
|
64
12
|
if (pointerType != null && !isMouseLikePointerType(pointerType)) {
|
|
65
13
|
return 0;
|
|
@@ -86,109 +34,6 @@ function isClickLikeOpenEvent(openEventType, interactedInside) {
|
|
|
86
34
|
return interactedInside || openEventType === 'click' || openEventType === 'mousedown';
|
|
87
35
|
}
|
|
88
36
|
|
|
89
|
-
/**
|
|
90
|
-
* Opens or closes the floating element when clicking the reference element.
|
|
91
|
-
* @see https://floating-ui.com/docs/useClick
|
|
92
|
-
*/
|
|
93
|
-
function useClick(context, props = {}) {
|
|
94
|
-
const store = 'rootStore' in context ? context.rootStore : context;
|
|
95
|
-
const dataRef = store.context.dataRef;
|
|
96
|
-
const {
|
|
97
|
-
enabled = true,
|
|
98
|
-
event: eventOption = 'click',
|
|
99
|
-
toggle = true,
|
|
100
|
-
ignoreMouse = false,
|
|
101
|
-
stickIfOpen = true,
|
|
102
|
-
touchOpenDelay = 0,
|
|
103
|
-
reason = triggerPress
|
|
104
|
-
} = props;
|
|
105
|
-
const pointerTypeRef = React.useRef(undefined);
|
|
106
|
-
const frame = useAnimationFrame();
|
|
107
|
-
const touchOpenTimeout = useTimeout();
|
|
108
|
-
const reference = React.useMemo(() => ({
|
|
109
|
-
onPointerDown(event) {
|
|
110
|
-
pointerTypeRef.current = event.pointerType;
|
|
111
|
-
},
|
|
112
|
-
onMouseDown(event) {
|
|
113
|
-
const pointerType = pointerTypeRef.current;
|
|
114
|
-
const nativeEvent = event.nativeEvent;
|
|
115
|
-
const open = store.select('open');
|
|
116
|
-
|
|
117
|
-
// Ignore all buttons except for the "main" button.
|
|
118
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
|
|
119
|
-
if (event.button !== 0 || eventOption === 'click' || isMouseLikePointerType(pointerType, true) && ignoreMouse) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
const openEvent = dataRef.current.openEvent;
|
|
123
|
-
const openEventType = openEvent?.type;
|
|
124
|
-
const hasClickedOnInactiveTrigger = store.select('domReferenceElement') !== event.currentTarget;
|
|
125
|
-
const nextOpen = open && hasClickedOnInactiveTrigger || !(open && toggle && (openEvent && stickIfOpen ? openEventType === 'click' || openEventType === 'mousedown' : true));
|
|
126
|
-
|
|
127
|
-
// Animations sometimes won't run on a typeable element if using a rAF.
|
|
128
|
-
// Focus is always set on these elements. For touch, we may delay opening.
|
|
129
|
-
if (isTypeableElement(nativeEvent.target)) {
|
|
130
|
-
const details = createChangeEventDetails(reason, nativeEvent, nativeEvent.target);
|
|
131
|
-
if (nextOpen && pointerType === 'touch' && touchOpenDelay > 0) {
|
|
132
|
-
touchOpenTimeout.start(touchOpenDelay, () => {
|
|
133
|
-
store.setOpen(true, details);
|
|
134
|
-
});
|
|
135
|
-
} else {
|
|
136
|
-
store.setOpen(nextOpen, details);
|
|
137
|
-
}
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Capture the currentTarget before the rAF.
|
|
142
|
-
// as React sets it to null after the event handler completes.
|
|
143
|
-
const eventCurrentTarget = event.currentTarget;
|
|
144
|
-
|
|
145
|
-
// Wait until focus is set on the element. This is an alternative to
|
|
146
|
-
// `event.preventDefault()` to avoid :focus-visible from appearing when using a pointer.
|
|
147
|
-
frame.request(() => {
|
|
148
|
-
const details = createChangeEventDetails(reason, nativeEvent, eventCurrentTarget);
|
|
149
|
-
if (nextOpen && pointerType === 'touch' && touchOpenDelay > 0) {
|
|
150
|
-
touchOpenTimeout.start(touchOpenDelay, () => {
|
|
151
|
-
store.setOpen(true, details);
|
|
152
|
-
});
|
|
153
|
-
} else {
|
|
154
|
-
store.setOpen(nextOpen, details);
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
},
|
|
158
|
-
onClick(event) {
|
|
159
|
-
if (eventOption === 'mousedown-only') {
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
const pointerType = pointerTypeRef.current;
|
|
163
|
-
if (eventOption === 'mousedown' && pointerType) {
|
|
164
|
-
pointerTypeRef.current = undefined;
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
if (isMouseLikePointerType(pointerType, true) && ignoreMouse) {
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
const open = store.select('open');
|
|
171
|
-
const openEvent = dataRef.current.openEvent;
|
|
172
|
-
const hasClickedOnInactiveTrigger = store.select('domReferenceElement') !== event.currentTarget;
|
|
173
|
-
const nextOpen = open && hasClickedOnInactiveTrigger || !(open && toggle && (openEvent && stickIfOpen ? isClickLikeEvent(openEvent) : true));
|
|
174
|
-
const details = createChangeEventDetails(reason, event.nativeEvent, event.currentTarget);
|
|
175
|
-
if (nextOpen && pointerType === 'touch' && touchOpenDelay > 0) {
|
|
176
|
-
touchOpenTimeout.start(touchOpenDelay, () => {
|
|
177
|
-
store.setOpen(true, details);
|
|
178
|
-
});
|
|
179
|
-
} else {
|
|
180
|
-
store.setOpen(nextOpen, details);
|
|
181
|
-
}
|
|
182
|
-
},
|
|
183
|
-
onKeyDown() {
|
|
184
|
-
pointerTypeRef.current = undefined;
|
|
185
|
-
}
|
|
186
|
-
}), [dataRef, eventOption, ignoreMouse, store, stickIfOpen, toggle, frame, touchOpenTimeout, touchOpenDelay, reason]);
|
|
187
|
-
return React.useMemo(() => enabled ? {
|
|
188
|
-
reference
|
|
189
|
-
} : EMPTY_OBJECT, [enabled, reference]);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
37
|
function useFloatingRootContext(options) {
|
|
193
38
|
const {
|
|
194
39
|
open = false,
|
|
@@ -1973,1452 +1818,1460 @@ function safePolygon(options = {}) {
|
|
|
1973
1818
|
return fn;
|
|
1974
1819
|
}
|
|
1975
1820
|
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
* Renders an `<h2>` element.
|
|
1979
|
-
*
|
|
1980
|
-
* Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)
|
|
1981
|
-
*/
|
|
1982
|
-
const DialogTitle = /*#__PURE__*/React.forwardRef(function DialogTitle(componentProps, forwardedRef) {
|
|
1983
|
-
const {
|
|
1984
|
-
render,
|
|
1985
|
-
className,
|
|
1986
|
-
id: idProp,
|
|
1987
|
-
...elementProps
|
|
1988
|
-
} = componentProps;
|
|
1989
|
-
const {
|
|
1990
|
-
store
|
|
1991
|
-
} = useDialogRootContext();
|
|
1992
|
-
const id = useBaseUiId(idProp);
|
|
1993
|
-
store.useSyncedValueWithCleanup('titleElementId', id);
|
|
1994
|
-
return useRenderElement('h2', componentProps, {
|
|
1995
|
-
ref: forwardedRef,
|
|
1996
|
-
props: [{
|
|
1997
|
-
id
|
|
1998
|
-
}, elementProps]
|
|
1999
|
-
});
|
|
2000
|
-
});
|
|
2001
|
-
if (process.env.NODE_ENV !== "production") DialogTitle.displayName = "DialogTitle";
|
|
1821
|
+
function getPseudoElementBounds(element) {
|
|
1822
|
+
const elementRect = element.getBoundingClientRect();
|
|
2002
1823
|
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
const
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
className,
|
|
2013
|
-
disabled = false,
|
|
2014
|
-
nativeButton = true,
|
|
2015
|
-
id: idProp,
|
|
2016
|
-
payload,
|
|
2017
|
-
handle,
|
|
2018
|
-
...elementProps
|
|
2019
|
-
} = componentProps;
|
|
2020
|
-
const dialogRootContext = useDialogRootContext(true);
|
|
2021
|
-
const store = handle?.store ?? dialogRootContext?.store;
|
|
2022
|
-
if (!store) {
|
|
2023
|
-
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: <Dialog.Trigger> must be used within <Dialog.Root> or provided with a handle.' : formatErrorMessage(79));
|
|
1824
|
+
// Avoid "Not implemented: window.getComputedStyle(elt, pseudoElt)"
|
|
1825
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
1826
|
+
return elementRect;
|
|
1827
|
+
}
|
|
1828
|
+
const beforeStyles = window.getComputedStyle(element, '::before');
|
|
1829
|
+
const afterStyles = window.getComputedStyle(element, '::after');
|
|
1830
|
+
const hasPseudoElements = beforeStyles.content !== 'none' || afterStyles.content !== 'none';
|
|
1831
|
+
if (!hasPseudoElements) {
|
|
1832
|
+
return elementRect;
|
|
2024
1833
|
}
|
|
2025
|
-
const thisTriggerId = useBaseUiId(idProp);
|
|
2026
|
-
const floatingContext = store.useState('floatingRootContext');
|
|
2027
|
-
const isOpenedByThisTrigger = store.useState('isOpenedByTrigger', thisTriggerId);
|
|
2028
|
-
const triggerElementRef = React.useRef(null);
|
|
2029
|
-
const {
|
|
2030
|
-
registerTrigger,
|
|
2031
|
-
isMountedByThisTrigger
|
|
2032
|
-
} = useTriggerDataForwarding(thisTriggerId, triggerElementRef, store, {
|
|
2033
|
-
payload
|
|
2034
|
-
});
|
|
2035
|
-
const {
|
|
2036
|
-
getButtonProps,
|
|
2037
|
-
buttonRef
|
|
2038
|
-
} = useButton({
|
|
2039
|
-
disabled,
|
|
2040
|
-
native: nativeButton
|
|
2041
|
-
});
|
|
2042
|
-
const click = useClick(floatingContext, {
|
|
2043
|
-
enabled: floatingContext != null
|
|
2044
|
-
});
|
|
2045
|
-
const localInteractionProps = useInteractions([click]);
|
|
2046
|
-
const state = {
|
|
2047
|
-
disabled,
|
|
2048
|
-
open: isOpenedByThisTrigger
|
|
2049
|
-
};
|
|
2050
|
-
const rootTriggerProps = store.useState('triggerProps', isMountedByThisTrigger);
|
|
2051
|
-
return useRenderElement('button', componentProps, {
|
|
2052
|
-
state,
|
|
2053
|
-
ref: [buttonRef, forwardedRef, registerTrigger, triggerElementRef],
|
|
2054
|
-
props: [localInteractionProps.getReferenceProps(), rootTriggerProps, {
|
|
2055
|
-
[CLICK_TRIGGER_IDENTIFIER]: '',
|
|
2056
|
-
id: thisTriggerId
|
|
2057
|
-
}, elementProps, getButtonProps],
|
|
2058
|
-
stateAttributesMapping: triggerOpenStateMapping
|
|
2059
|
-
});
|
|
2060
|
-
});
|
|
2061
|
-
if (process.env.NODE_ENV !== "production") DialogTrigger.displayName = "DialogTrigger";
|
|
2062
|
-
|
|
2063
|
-
/**
|
|
2064
|
-
* A heading that labels the drawer.
|
|
2065
|
-
* Renders an `<h2>` element.
|
|
2066
|
-
*
|
|
2067
|
-
* Documentation: [Base UI Drawer](https://base-ui.com/react/components/drawer)
|
|
2068
|
-
*/
|
|
2069
|
-
const DrawerTitle = DialogTitle;
|
|
2070
1834
|
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
*/
|
|
2077
|
-
const DrawerTrigger = DialogTrigger;
|
|
1835
|
+
// Get dimensions of pseudo-elements
|
|
1836
|
+
const beforeWidth = parseFloat(beforeStyles.width) || 0;
|
|
1837
|
+
const beforeHeight = parseFloat(beforeStyles.height) || 0;
|
|
1838
|
+
const afterWidth = parseFloat(afterStyles.width) || 0;
|
|
1839
|
+
const afterHeight = parseFloat(afterStyles.height) || 0;
|
|
2078
1840
|
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
const context = React.useContext(MenuPositionerContext);
|
|
2083
|
-
if (context === undefined && !optional) {
|
|
2084
|
-
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: MenuPositionerContext is missing. MenuPositioner parts must be placed within <Menu.Positioner>.' : formatErrorMessage(33));
|
|
2085
|
-
}
|
|
2086
|
-
return context;
|
|
2087
|
-
}
|
|
1841
|
+
// Calculate max dimensions including pseudo-elements
|
|
1842
|
+
const totalWidth = Math.max(elementRect.width, beforeWidth, afterWidth);
|
|
1843
|
+
const totalHeight = Math.max(elementRect.height, beforeHeight, afterHeight);
|
|
2088
1844
|
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
1845
|
+
// Calculate the differences to extend the bounds
|
|
1846
|
+
const widthDiff = totalWidth - elementRect.width;
|
|
1847
|
+
const heightDiff = totalHeight - elementRect.height;
|
|
1848
|
+
return {
|
|
1849
|
+
left: elementRect.left - widthDiff / 2,
|
|
1850
|
+
right: elementRect.right + widthDiff / 2,
|
|
1851
|
+
top: elementRect.top - heightDiff / 2,
|
|
1852
|
+
bottom: elementRect.bottom + heightDiff / 2
|
|
1853
|
+
};
|
|
2097
1854
|
}
|
|
2098
1855
|
|
|
2099
|
-
const
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
1856
|
+
const CompositeListContext = /*#__PURE__*/React.createContext({
|
|
1857
|
+
register: () => {},
|
|
1858
|
+
unregister: () => {},
|
|
1859
|
+
subscribeMapChange: () => {
|
|
1860
|
+
return () => {};
|
|
1861
|
+
},
|
|
1862
|
+
elementsRef: {
|
|
1863
|
+
current: []
|
|
1864
|
+
},
|
|
1865
|
+
nextIndexRef: {
|
|
1866
|
+
current: 0
|
|
2105
1867
|
}
|
|
2106
|
-
|
|
1868
|
+
});
|
|
1869
|
+
if (process.env.NODE_ENV !== "production") CompositeListContext.displayName = "CompositeListContext";
|
|
1870
|
+
function useCompositeListContext() {
|
|
1871
|
+
return React.useContext(CompositeListContext);
|
|
2107
1872
|
}
|
|
2108
1873
|
|
|
1874
|
+
/* eslint-disable no-bitwise */
|
|
2109
1875
|
/**
|
|
2110
|
-
*
|
|
2111
|
-
*
|
|
2112
|
-
* onMouseMove, onClick, and onMouseUp handlers.
|
|
1876
|
+
* Provides context for a list of items in a composite component.
|
|
1877
|
+
* @internal
|
|
2113
1878
|
*/
|
|
2114
|
-
function
|
|
1879
|
+
function CompositeList(props) {
|
|
2115
1880
|
const {
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
} = params;
|
|
2125
|
-
const {
|
|
2126
|
-
events: menuEvents
|
|
2127
|
-
} = store.useState('floatingTreeRoot');
|
|
2128
|
-
const contextMenuContext = useContextMenuRootContext(true);
|
|
2129
|
-
const isContextMenu = contextMenuContext !== undefined;
|
|
2130
|
-
return React.useMemo(() => ({
|
|
2131
|
-
id,
|
|
2132
|
-
role: 'menuitem',
|
|
2133
|
-
tabIndex: highlighted ? 0 : -1,
|
|
2134
|
-
onKeyDown(event) {
|
|
2135
|
-
if (event.key === ' ' && typingRef?.current) {
|
|
2136
|
-
event.preventDefault();
|
|
2137
|
-
}
|
|
2138
|
-
},
|
|
2139
|
-
onMouseMove(event) {
|
|
2140
|
-
if (!nodeId) {
|
|
2141
|
-
return;
|
|
2142
|
-
}
|
|
1881
|
+
children,
|
|
1882
|
+
elementsRef,
|
|
1883
|
+
labelsRef,
|
|
1884
|
+
onMapChange: onMapChangeProp
|
|
1885
|
+
} = props;
|
|
1886
|
+
const onMapChange = useStableCallback(onMapChangeProp);
|
|
1887
|
+
const nextIndexRef = React.useRef(0);
|
|
1888
|
+
const listeners = useRefWithInit(createListeners).current;
|
|
2143
1889
|
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
1890
|
+
// We use a stable `map` to avoid O(n^2) re-allocation costs for large lists.
|
|
1891
|
+
// `mapTick` is our re-render trigger mechanism. We also need to update the
|
|
1892
|
+
// elements and label refs, but there's a lot of async work going on and sometimes
|
|
1893
|
+
// the effect that handles `onMapChange` gets called after those refs have been
|
|
1894
|
+
// filled, and we don't want to lose those values by setting their lengths to `0`.
|
|
1895
|
+
// We also need to have them at the proper length because floating-ui uses that
|
|
1896
|
+
// information for list navigation.
|
|
1897
|
+
|
|
1898
|
+
const map = useRefWithInit(createMap).current;
|
|
1899
|
+
// `mapTick` uses a counter rather than objects for low precision-loss risk and better memory efficiency
|
|
1900
|
+
const [mapTick, setMapTick] = React.useState(0);
|
|
1901
|
+
const lastTickRef = React.useRef(mapTick);
|
|
1902
|
+
const register = useStableCallback((node, metadata) => {
|
|
1903
|
+
map.set(node, metadata ?? null);
|
|
1904
|
+
lastTickRef.current += 1;
|
|
1905
|
+
setMapTick(lastTickRef.current);
|
|
1906
|
+
});
|
|
1907
|
+
const unregister = useStableCallback(node => {
|
|
1908
|
+
map.delete(node);
|
|
1909
|
+
lastTickRef.current += 1;
|
|
1910
|
+
setMapTick(lastTickRef.current);
|
|
1911
|
+
});
|
|
1912
|
+
const sortedMap = React.useMemo(() => {
|
|
1913
|
+
const newMap = new Map();
|
|
1914
|
+
// Filter out disconnected elements before sorting to avoid inconsistent
|
|
1915
|
+
// compareDocumentPosition results when elements are detached from the DOM.
|
|
1916
|
+
const sortedNodes = Array.from(map.keys()).filter(node => node.isConnected).sort(sortByDocumentPosition);
|
|
1917
|
+
sortedNodes.forEach((node, index) => {
|
|
1918
|
+
const metadata = map.get(node) ?? {};
|
|
1919
|
+
newMap.set(node, {
|
|
1920
|
+
...metadata,
|
|
1921
|
+
index
|
|
2149
1922
|
});
|
|
2150
|
-
}
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
1923
|
+
});
|
|
1924
|
+
return newMap;
|
|
1925
|
+
}, [map, mapTick]);
|
|
1926
|
+
useIsoLayoutEffect(() => {
|
|
1927
|
+
if (typeof MutationObserver !== 'function' || sortedMap.size === 0) {
|
|
1928
|
+
return undefined;
|
|
1929
|
+
}
|
|
1930
|
+
const mutationObserver = new MutationObserver(entries => {
|
|
1931
|
+
const diff = new Set();
|
|
1932
|
+
const updateDiff = node => diff.has(node) ? diff.delete(node) : diff.add(node);
|
|
1933
|
+
entries.forEach(entry => {
|
|
1934
|
+
entry.removedNodes.forEach(updateDiff);
|
|
1935
|
+
entry.addedNodes.forEach(updateDiff);
|
|
1936
|
+
});
|
|
1937
|
+
if (diff.size === 0) {
|
|
1938
|
+
lastTickRef.current += 1;
|
|
1939
|
+
setMapTick(lastTickRef.current);
|
|
1940
|
+
}
|
|
1941
|
+
});
|
|
1942
|
+
sortedMap.forEach((_, node) => {
|
|
1943
|
+
if (node.parentElement) {
|
|
1944
|
+
mutationObserver.observe(node.parentElement, {
|
|
1945
|
+
childList: true
|
|
2156
1946
|
});
|
|
2157
1947
|
}
|
|
2158
|
-
}
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
// that opened the context menu, so it must not activate an item.
|
|
2169
|
-
if (isContextMenu && !isMac && event.button === 2) {
|
|
2170
|
-
return;
|
|
2171
|
-
}
|
|
1948
|
+
});
|
|
1949
|
+
return () => {
|
|
1950
|
+
mutationObserver.disconnect();
|
|
1951
|
+
};
|
|
1952
|
+
}, [sortedMap]);
|
|
1953
|
+
useIsoLayoutEffect(() => {
|
|
1954
|
+
const shouldUpdateLengths = lastTickRef.current === mapTick;
|
|
1955
|
+
if (shouldUpdateLengths) {
|
|
1956
|
+
if (elementsRef.current.length !== sortedMap.size) {
|
|
1957
|
+
elementsRef.current.length = sortedMap.size;
|
|
2172
1958
|
}
|
|
2173
|
-
if (
|
|
2174
|
-
|
|
2175
|
-
// We trigger the click and override the `closeOnClick` preference to always close the menu.
|
|
2176
|
-
if (!itemMetadata || itemMetadata.type === 'regular-item') {
|
|
2177
|
-
itemRef.current.click();
|
|
2178
|
-
}
|
|
1959
|
+
if (labelsRef && labelsRef.current.length !== sortedMap.size) {
|
|
1960
|
+
labelsRef.current.length = sortedMap.size;
|
|
2179
1961
|
}
|
|
1962
|
+
nextIndexRef.current = sortedMap.size;
|
|
2180
1963
|
}
|
|
2181
|
-
|
|
2182
|
-
}
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
};
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
const {
|
|
2201
|
-
getButtonProps,
|
|
2202
|
-
buttonRef
|
|
2203
|
-
} = useButton({
|
|
2204
|
-
disabled,
|
|
2205
|
-
focusableWhenDisabled: true,
|
|
2206
|
-
native: nativeButton,
|
|
2207
|
-
composite: true
|
|
1964
|
+
onMapChange(sortedMap);
|
|
1965
|
+
}, [onMapChange, sortedMap, elementsRef, labelsRef, mapTick]);
|
|
1966
|
+
useIsoLayoutEffect(() => {
|
|
1967
|
+
return () => {
|
|
1968
|
+
elementsRef.current = [];
|
|
1969
|
+
};
|
|
1970
|
+
}, [elementsRef]);
|
|
1971
|
+
useIsoLayoutEffect(() => {
|
|
1972
|
+
return () => {
|
|
1973
|
+
if (labelsRef) {
|
|
1974
|
+
labelsRef.current = [];
|
|
1975
|
+
}
|
|
1976
|
+
};
|
|
1977
|
+
}, [labelsRef]);
|
|
1978
|
+
const subscribeMapChange = useStableCallback(fn => {
|
|
1979
|
+
listeners.add(fn);
|
|
1980
|
+
return () => {
|
|
1981
|
+
listeners.delete(fn);
|
|
1982
|
+
};
|
|
2208
1983
|
});
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
1984
|
+
useIsoLayoutEffect(() => {
|
|
1985
|
+
listeners.forEach(l => l(sortedMap));
|
|
1986
|
+
}, [listeners, sortedMap]);
|
|
1987
|
+
const contextValue = React.useMemo(() => ({
|
|
1988
|
+
register,
|
|
1989
|
+
unregister,
|
|
1990
|
+
subscribeMapChange,
|
|
1991
|
+
elementsRef,
|
|
1992
|
+
labelsRef,
|
|
1993
|
+
nextIndexRef
|
|
1994
|
+
}), [register, unregister, subscribeMapChange, elementsRef, labelsRef, nextIndexRef]);
|
|
1995
|
+
return /*#__PURE__*/jsx(CompositeListContext.Provider, {
|
|
1996
|
+
value: contextValue,
|
|
1997
|
+
children: children
|
|
2218
1998
|
});
|
|
2219
|
-
const getItemProps = React.useCallback(externalProps => {
|
|
2220
|
-
return mergeProps(commonProps, {
|
|
2221
|
-
onMouseEnter() {
|
|
2222
|
-
if (itemMetadata.type !== 'submenu-trigger') {
|
|
2223
|
-
return;
|
|
2224
|
-
}
|
|
2225
|
-
itemMetadata.setActive();
|
|
2226
|
-
}
|
|
2227
|
-
}, externalProps, getButtonProps);
|
|
2228
|
-
}, [commonProps, getButtonProps, itemMetadata]);
|
|
2229
|
-
const mergedRef = useMergedRefs(itemRef, buttonRef);
|
|
2230
|
-
return React.useMemo(() => ({
|
|
2231
|
-
getItemProps,
|
|
2232
|
-
itemRef: mergedRef
|
|
2233
|
-
}), [getItemProps, mergedRef]);
|
|
2234
1999
|
}
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
nextIndexRef: {
|
|
2246
|
-
current: 0
|
|
2000
|
+
function createMap() {
|
|
2001
|
+
return new Map();
|
|
2002
|
+
}
|
|
2003
|
+
function createListeners() {
|
|
2004
|
+
return new Set();
|
|
2005
|
+
}
|
|
2006
|
+
function sortByDocumentPosition(a, b) {
|
|
2007
|
+
const position = a.compareDocumentPosition(b);
|
|
2008
|
+
if (position & Node.DOCUMENT_POSITION_FOLLOWING || position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
|
|
2009
|
+
return -1;
|
|
2247
2010
|
}
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
return
|
|
2011
|
+
if (position & Node.DOCUMENT_POSITION_PRECEDING || position & Node.DOCUMENT_POSITION_CONTAINS) {
|
|
2012
|
+
return 1;
|
|
2013
|
+
}
|
|
2014
|
+
return 0;
|
|
2252
2015
|
}
|
|
2253
2016
|
|
|
2254
|
-
let IndexGuessBehavior = /*#__PURE__*/function (IndexGuessBehavior) {
|
|
2255
|
-
IndexGuessBehavior[IndexGuessBehavior["None"] = 0] = "None";
|
|
2256
|
-
IndexGuessBehavior[IndexGuessBehavior["GuessFromOrder"] = 1] = "GuessFromOrder";
|
|
2257
|
-
return IndexGuessBehavior;
|
|
2258
|
-
}({});
|
|
2259
|
-
|
|
2260
2017
|
/**
|
|
2261
|
-
*
|
|
2018
|
+
* Fork of the original `arrow` middleware from Floating UI that allows
|
|
2019
|
+
* configuring the offset parent.
|
|
2262
2020
|
*/
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
indexRef.current = newIndex;
|
|
2285
|
-
}
|
|
2286
|
-
return indexRef.current;
|
|
2287
|
-
} : -1));
|
|
2288
|
-
const componentRef = React.useRef(null);
|
|
2289
|
-
const ref = React.useCallback(node => {
|
|
2290
|
-
componentRef.current = node;
|
|
2291
|
-
if (index !== -1 && node !== null) {
|
|
2292
|
-
elementsRef.current[index] = node;
|
|
2293
|
-
if (labelsRef) {
|
|
2294
|
-
const isLabelDefined = label !== undefined;
|
|
2295
|
-
labelsRef.current[index] = isLabelDefined ? label : textRef?.current?.textContent ?? node.textContent;
|
|
2296
|
-
}
|
|
2297
|
-
}
|
|
2298
|
-
}, [index, elementsRef, labelsRef, label, textRef]);
|
|
2299
|
-
useIsoLayoutEffect(() => {
|
|
2300
|
-
if (externalIndex != null) {
|
|
2301
|
-
return undefined;
|
|
2302
|
-
}
|
|
2303
|
-
const node = componentRef.current;
|
|
2304
|
-
if (node) {
|
|
2305
|
-
register(node, metadata);
|
|
2306
|
-
return () => {
|
|
2307
|
-
unregister(node);
|
|
2308
|
-
};
|
|
2021
|
+
const baseArrow = options => ({
|
|
2022
|
+
name: 'arrow',
|
|
2023
|
+
options,
|
|
2024
|
+
async fn(state) {
|
|
2025
|
+
const {
|
|
2026
|
+
x,
|
|
2027
|
+
y,
|
|
2028
|
+
placement,
|
|
2029
|
+
rects,
|
|
2030
|
+
platform,
|
|
2031
|
+
elements,
|
|
2032
|
+
middlewareData
|
|
2033
|
+
} = state;
|
|
2034
|
+
// Since `element` is required, we don't Partial<> the type.
|
|
2035
|
+
const {
|
|
2036
|
+
element,
|
|
2037
|
+
padding = 0,
|
|
2038
|
+
offsetParent = 'real'
|
|
2039
|
+
} = evaluate(options, state) || {};
|
|
2040
|
+
if (element == null) {
|
|
2041
|
+
return {};
|
|
2309
2042
|
}
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2043
|
+
const paddingObject = getPaddingObject(padding);
|
|
2044
|
+
const coords = {
|
|
2045
|
+
x,
|
|
2046
|
+
y
|
|
2047
|
+
};
|
|
2048
|
+
const axis = getAlignmentAxis(placement);
|
|
2049
|
+
const length = getAxisLength(axis);
|
|
2050
|
+
const arrowDimensions = await platform.getDimensions(element);
|
|
2051
|
+
const isYAxis = axis === 'y';
|
|
2052
|
+
const minProp = isYAxis ? 'top' : 'left';
|
|
2053
|
+
const maxProp = isYAxis ? 'bottom' : 'right';
|
|
2054
|
+
const clientProp = isYAxis ? 'clientHeight' : 'clientWidth';
|
|
2055
|
+
const endDiff = rects.reference[length] + rects.reference[axis] - coords[axis] - rects.floating[length];
|
|
2056
|
+
const startDiff = coords[axis] - rects.reference[axis];
|
|
2057
|
+
const arrowOffsetParent = offsetParent === 'real' ? await platform.getOffsetParent?.(element) : elements.floating;
|
|
2058
|
+
let clientSize = elements.floating[clientProp] || rects.floating[length];
|
|
2059
|
+
|
|
2060
|
+
// DOM platform can return `window` as the `offsetParent`.
|
|
2061
|
+
if (!clientSize || !(await platform.isElement?.(arrowOffsetParent))) {
|
|
2062
|
+
clientSize = elements.floating[clientProp] || rects.floating[length];
|
|
2315
2063
|
}
|
|
2316
|
-
|
|
2317
|
-
const i = componentRef.current ? map.get(componentRef.current)?.index : null;
|
|
2318
|
-
if (i != null) {
|
|
2319
|
-
setIndex(i);
|
|
2320
|
-
}
|
|
2321
|
-
});
|
|
2322
|
-
}, [externalIndex, subscribeMapChange, setIndex]);
|
|
2323
|
-
return React.useMemo(() => ({
|
|
2324
|
-
ref,
|
|
2325
|
-
index
|
|
2326
|
-
}), [index, ref]);
|
|
2327
|
-
}
|
|
2064
|
+
const centerToReference = endDiff / 2 - startDiff / 2;
|
|
2328
2065
|
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
/**
|
|
2335
|
-
* Present when the menu checkbox item is not checked.
|
|
2336
|
-
*/
|
|
2337
|
-
MenuCheckboxItemDataAttributes["unchecked"] = "data-unchecked";
|
|
2338
|
-
/**
|
|
2339
|
-
* Present when the menu checkbox item is disabled.
|
|
2340
|
-
*/
|
|
2341
|
-
MenuCheckboxItemDataAttributes["disabled"] = "data-disabled";
|
|
2342
|
-
/**
|
|
2343
|
-
* Present when the menu checkbox item is highlighted.
|
|
2344
|
-
*/
|
|
2345
|
-
MenuCheckboxItemDataAttributes["highlighted"] = "data-highlighted";
|
|
2346
|
-
return MenuCheckboxItemDataAttributes;
|
|
2347
|
-
}({});
|
|
2066
|
+
// If the padding is large enough that it causes the arrow to no longer be
|
|
2067
|
+
// centered, modify the padding so that it is centered.
|
|
2068
|
+
const largestPossiblePadding = clientSize / 2 - arrowDimensions[length] / 2 - 1;
|
|
2069
|
+
const minPadding = Math.min(paddingObject[minProp], largestPossiblePadding);
|
|
2070
|
+
const maxPadding = Math.min(paddingObject[maxProp], largestPossiblePadding);
|
|
2348
2071
|
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2072
|
+
// Make sure the arrow doesn't overflow the floating element if the center
|
|
2073
|
+
// point is outside the floating element's bounds.
|
|
2074
|
+
const min = minPadding;
|
|
2075
|
+
const max = clientSize - arrowDimensions[length] - maxPadding;
|
|
2076
|
+
const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference;
|
|
2077
|
+
const offset = clamp(min, center, max);
|
|
2078
|
+
|
|
2079
|
+
// If the reference is small enough that the arrow's padding causes it to
|
|
2080
|
+
// to point to nothing for an aligned placement, adjust the offset of the
|
|
2081
|
+
// floating element itself. To ensure `shift()` continues to take action,
|
|
2082
|
+
// a single reset is performed when this is true.
|
|
2083
|
+
const shouldAddOffset = !middlewareData.arrow && getAlignment(placement) != null && center !== offset && rects.reference[length] / 2 - (center < min ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0;
|
|
2084
|
+
// eslint-disable-next-line no-nested-ternary
|
|
2085
|
+
const alignmentOffset = shouldAddOffset ? center < min ? center - min : center - max : 0;
|
|
2356
2086
|
return {
|
|
2357
|
-
[
|
|
2087
|
+
[axis]: coords[axis] + alignmentOffset,
|
|
2088
|
+
data: {
|
|
2089
|
+
[axis]: offset,
|
|
2090
|
+
centerOffset: center - offset - alignmentOffset,
|
|
2091
|
+
...(shouldAddOffset && {
|
|
2092
|
+
alignmentOffset
|
|
2093
|
+
})
|
|
2094
|
+
},
|
|
2095
|
+
reset: shouldAddOffset
|
|
2358
2096
|
};
|
|
2359
|
-
},
|
|
2360
|
-
...transitionStatusMapping
|
|
2361
|
-
};
|
|
2362
|
-
|
|
2363
|
-
const MenuGroupContext = /*#__PURE__*/React.createContext(undefined);
|
|
2364
|
-
if (process.env.NODE_ENV !== "production") MenuGroupContext.displayName = "MenuGroupContext";
|
|
2365
|
-
function useMenuGroupRootContext() {
|
|
2366
|
-
const context = React.useContext(MenuGroupContext);
|
|
2367
|
-
if (context === undefined) {
|
|
2368
|
-
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: MenuGroupRootContext is missing. Menu group parts must be used within <Menu.Group>.' : formatErrorMessage(31));
|
|
2369
2097
|
}
|
|
2370
|
-
return context;
|
|
2371
|
-
}
|
|
2372
|
-
|
|
2373
|
-
const MenuGroup = /*#__PURE__*/React.forwardRef(function MenuGroup(componentProps, forwardedRef) {
|
|
2374
|
-
const {
|
|
2375
|
-
render,
|
|
2376
|
-
className,
|
|
2377
|
-
...elementProps
|
|
2378
|
-
} = componentProps;
|
|
2379
|
-
const [labelId, setLabelId] = React.useState(undefined);
|
|
2380
|
-
const context = React.useMemo(() => ({
|
|
2381
|
-
setLabelId
|
|
2382
|
-
}), [setLabelId]);
|
|
2383
|
-
const element = useRenderElement('div', componentProps, {
|
|
2384
|
-
ref: forwardedRef,
|
|
2385
|
-
props: {
|
|
2386
|
-
role: 'group',
|
|
2387
|
-
'aria-labelledby': labelId,
|
|
2388
|
-
...elementProps
|
|
2389
|
-
}
|
|
2390
|
-
});
|
|
2391
|
-
return /*#__PURE__*/jsx(MenuGroupContext.Provider, {
|
|
2392
|
-
value: context,
|
|
2393
|
-
children: element
|
|
2394
|
-
});
|
|
2395
2098
|
});
|
|
2396
|
-
if (process.env.NODE_ENV !== "production") MenuGroup.displayName = "MenuGroup";
|
|
2397
2099
|
|
|
2398
2100
|
/**
|
|
2399
|
-
*
|
|
2400
|
-
*
|
|
2401
|
-
*
|
|
2402
|
-
*
|
|
2101
|
+
* Provides data to position an inner element of the floating element so that it
|
|
2102
|
+
* appears centered to the reference element.
|
|
2103
|
+
* This wraps the core `arrow` middleware to allow React refs as the element.
|
|
2104
|
+
* @see https://floating-ui.com/docs/arrow
|
|
2403
2105
|
*/
|
|
2404
|
-
const
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
render,
|
|
2408
|
-
id: idProp,
|
|
2409
|
-
...elementProps
|
|
2410
|
-
} = componentProps;
|
|
2411
|
-
const id = useBaseUiId(idProp);
|
|
2412
|
-
const {
|
|
2413
|
-
setLabelId
|
|
2414
|
-
} = useMenuGroupRootContext();
|
|
2415
|
-
useIsoLayoutEffect(() => {
|
|
2416
|
-
setLabelId(id);
|
|
2417
|
-
return () => {
|
|
2418
|
-
setLabelId(undefined);
|
|
2419
|
-
};
|
|
2420
|
-
}, [setLabelId, id]);
|
|
2421
|
-
return useRenderElement('div', componentProps, {
|
|
2422
|
-
ref: forwardedRef,
|
|
2423
|
-
props: {
|
|
2424
|
-
id,
|
|
2425
|
-
role: 'presentation',
|
|
2426
|
-
...elementProps
|
|
2427
|
-
}
|
|
2428
|
-
});
|
|
2106
|
+
const arrow = (options, deps) => ({
|
|
2107
|
+
...baseArrow(options),
|
|
2108
|
+
options: [options, deps]
|
|
2429
2109
|
});
|
|
2430
|
-
if (process.env.NODE_ENV !== "production") MenuGroupLabel.displayName = "MenuGroupLabel";
|
|
2431
2110
|
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
}
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2111
|
+
const hide = {
|
|
2112
|
+
name: 'hide',
|
|
2113
|
+
async fn(state) {
|
|
2114
|
+
const {
|
|
2115
|
+
width,
|
|
2116
|
+
height,
|
|
2117
|
+
x,
|
|
2118
|
+
y
|
|
2119
|
+
} = state.rects.reference;
|
|
2120
|
+
const anchorHidden = width === 0 && height === 0 && x === 0 && y === 0;
|
|
2121
|
+
const nativeHideResult = await hide$1().fn(state);
|
|
2122
|
+
return {
|
|
2123
|
+
data: {
|
|
2124
|
+
referenceHidden: nativeHideResult.data?.referenceHidden || anchorHidden
|
|
2125
|
+
}
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2128
|
+
};
|
|
2129
|
+
|
|
2130
|
+
const DEFAULT_SIDES = {
|
|
2131
|
+
sideX: 'left',
|
|
2132
|
+
sideY: 'top'
|
|
2133
|
+
};
|
|
2134
|
+
const adaptiveOrigin = {
|
|
2135
|
+
name: 'adaptiveOrigin',
|
|
2136
|
+
async fn(state) {
|
|
2137
|
+
const {
|
|
2138
|
+
x: rawX,
|
|
2139
|
+
y: rawY,
|
|
2140
|
+
rects: {
|
|
2141
|
+
floating: floatRect
|
|
2142
|
+
},
|
|
2143
|
+
elements: {
|
|
2144
|
+
floating
|
|
2145
|
+
},
|
|
2146
|
+
platform,
|
|
2147
|
+
strategy,
|
|
2148
|
+
placement
|
|
2149
|
+
} = state;
|
|
2150
|
+
const win = getWindow(floating);
|
|
2151
|
+
const styles = win.getComputedStyle(floating);
|
|
2152
|
+
const hasTransition = styles.transitionDuration !== '0s' && styles.transitionDuration !== '';
|
|
2153
|
+
if (!hasTransition) {
|
|
2154
|
+
return {
|
|
2155
|
+
x: rawX,
|
|
2156
|
+
y: rawY,
|
|
2157
|
+
data: DEFAULT_SIDES
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
const offsetParent = await platform.getOffsetParent?.(floating);
|
|
2161
|
+
let offsetDimensions = {
|
|
2162
|
+
width: 0,
|
|
2163
|
+
height: 0
|
|
2164
|
+
};
|
|
2165
|
+
|
|
2166
|
+
// For fixed strategy, prefer visualViewport if available
|
|
2167
|
+
if (strategy === 'fixed' && win?.visualViewport) {
|
|
2168
|
+
offsetDimensions = {
|
|
2169
|
+
width: win.visualViewport.width,
|
|
2170
|
+
height: win.visualViewport.height
|
|
2171
|
+
};
|
|
2172
|
+
} else if (offsetParent === win) {
|
|
2173
|
+
const doc = ownerDocument(floating);
|
|
2174
|
+
offsetDimensions = {
|
|
2175
|
+
width: doc.documentElement.clientWidth,
|
|
2176
|
+
height: doc.documentElement.clientHeight
|
|
2177
|
+
};
|
|
2178
|
+
} else if (await platform.isElement?.(offsetParent)) {
|
|
2179
|
+
offsetDimensions = await platform.getDimensions(offsetParent);
|
|
2180
|
+
}
|
|
2181
|
+
const currentSide = getSide(placement);
|
|
2182
|
+
let x = rawX;
|
|
2183
|
+
let y = rawY;
|
|
2184
|
+
if (currentSide === 'left') {
|
|
2185
|
+
x = offsetDimensions.width - (rawX + floatRect.width);
|
|
2186
|
+
}
|
|
2187
|
+
if (currentSide === 'top') {
|
|
2188
|
+
y = offsetDimensions.height - (rawY + floatRect.height);
|
|
2189
|
+
}
|
|
2190
|
+
const sideX = currentSide === 'left' ? 'right' : DEFAULT_SIDES.sideX;
|
|
2191
|
+
const sideY = currentSide === 'top' ? 'bottom' : DEFAULT_SIDES.sideY;
|
|
2192
|
+
return {
|
|
2193
|
+
x,
|
|
2194
|
+
y,
|
|
2195
|
+
data: {
|
|
2196
|
+
sideX,
|
|
2197
|
+
sideY
|
|
2198
|
+
}
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
};
|
|
2483
2202
|
|
|
2203
|
+
function getLogicalSide(sideParam, renderedSide, isRtl) {
|
|
2204
|
+
const isLogicalSideParam = sideParam === 'inline-start' || sideParam === 'inline-end';
|
|
2205
|
+
const logicalRight = isRtl ? 'inline-start' : 'inline-end';
|
|
2206
|
+
const logicalLeft = isRtl ? 'inline-end' : 'inline-start';
|
|
2207
|
+
return {
|
|
2208
|
+
top: 'top',
|
|
2209
|
+
right: isLogicalSideParam ? logicalRight : 'right',
|
|
2210
|
+
bottom: 'bottom',
|
|
2211
|
+
left: isLogicalSideParam ? logicalLeft : 'left'
|
|
2212
|
+
}[renderedSide];
|
|
2213
|
+
}
|
|
2214
|
+
function getOffsetData(state, sideParam, isRtl) {
|
|
2215
|
+
const {
|
|
2216
|
+
rects,
|
|
2217
|
+
placement
|
|
2218
|
+
} = state;
|
|
2219
|
+
const data = {
|
|
2220
|
+
side: getLogicalSide(sideParam, getSide(placement), isRtl),
|
|
2221
|
+
align: getAlignment(placement) || 'center',
|
|
2222
|
+
anchor: {
|
|
2223
|
+
width: rects.reference.width,
|
|
2224
|
+
height: rects.reference.height
|
|
2225
|
+
},
|
|
2226
|
+
positioner: {
|
|
2227
|
+
width: rects.floating.width,
|
|
2228
|
+
height: rects.floating.height
|
|
2229
|
+
}
|
|
2230
|
+
};
|
|
2231
|
+
return data;
|
|
2232
|
+
}
|
|
2484
2233
|
/**
|
|
2485
|
-
*
|
|
2486
|
-
*
|
|
2487
|
-
*
|
|
2488
|
-
* Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
|
|
2234
|
+
* Provides standardized anchor positioning behavior for floating elements. Wraps Floating UI's
|
|
2235
|
+
* `useFloating` hook.
|
|
2489
2236
|
*/
|
|
2490
|
-
|
|
2491
|
-
const {
|
|
2492
|
-
render,
|
|
2493
|
-
className,
|
|
2494
|
-
id: idProp,
|
|
2495
|
-
label,
|
|
2496
|
-
closeOnClick = false,
|
|
2497
|
-
...elementProps
|
|
2498
|
-
} = componentProps;
|
|
2499
|
-
const linkRef = React.useRef(null);
|
|
2500
|
-
const listItem = useCompositeListItem({
|
|
2501
|
-
label
|
|
2502
|
-
});
|
|
2503
|
-
const menuPositionerContext = useMenuPositionerContext(true);
|
|
2504
|
-
const nodeId = menuPositionerContext?.nodeId;
|
|
2505
|
-
const id = useBaseUiId(idProp);
|
|
2506
|
-
const {
|
|
2507
|
-
store
|
|
2508
|
-
} = useMenuRootContext();
|
|
2509
|
-
const highlighted = store.useState('isActive', listItem.index);
|
|
2510
|
-
const itemProps = store.useState('itemProps');
|
|
2511
|
-
const typingRef = store.context.typingRef;
|
|
2237
|
+
function useAnchorPositioning(params) {
|
|
2512
2238
|
const {
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2239
|
+
// Public parameters
|
|
2240
|
+
anchor,
|
|
2241
|
+
positionMethod = 'absolute',
|
|
2242
|
+
side: sideParam = 'bottom',
|
|
2243
|
+
sideOffset = 0,
|
|
2244
|
+
align = 'center',
|
|
2245
|
+
alignOffset = 0,
|
|
2246
|
+
collisionBoundary,
|
|
2247
|
+
collisionPadding: collisionPaddingParam = 5,
|
|
2248
|
+
sticky = false,
|
|
2249
|
+
arrowPadding = 5,
|
|
2250
|
+
disableAnchorTracking = false,
|
|
2251
|
+
// Private parameters
|
|
2252
|
+
keepMounted = false,
|
|
2253
|
+
floatingRootContext,
|
|
2254
|
+
mounted,
|
|
2255
|
+
collisionAvoidance,
|
|
2256
|
+
shiftCrossAxis = false,
|
|
2523
2257
|
nodeId,
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2258
|
+
adaptiveOrigin,
|
|
2259
|
+
lazyFlip = false,
|
|
2260
|
+
externalTree
|
|
2261
|
+
} = params;
|
|
2262
|
+
const [mountSide, setMountSide] = React.useState(null);
|
|
2263
|
+
if (!mounted && mountSide !== null) {
|
|
2264
|
+
setMountSide(null);
|
|
2265
|
+
}
|
|
2266
|
+
const collisionAvoidanceSide = collisionAvoidance.side || 'flip';
|
|
2267
|
+
const collisionAvoidanceAlign = collisionAvoidance.align || 'flip';
|
|
2268
|
+
const collisionAvoidanceFallbackAxisSide = collisionAvoidance.fallbackAxisSide || 'end';
|
|
2269
|
+
const anchorFn = typeof anchor === 'function' ? anchor : undefined;
|
|
2270
|
+
const anchorFnCallback = useStableCallback(anchorFn);
|
|
2271
|
+
const anchorDep = anchorFn ? anchorFnCallback : anchor;
|
|
2272
|
+
const anchorValueRef = useValueAsRef(anchor);
|
|
2273
|
+
const direction = useDirection();
|
|
2274
|
+
const isRtl = direction === 'rtl';
|
|
2275
|
+
const side = mountSide || {
|
|
2276
|
+
top: 'top',
|
|
2277
|
+
right: 'right',
|
|
2278
|
+
bottom: 'bottom',
|
|
2279
|
+
left: 'left',
|
|
2280
|
+
'inline-end': isRtl ? 'left' : 'right',
|
|
2281
|
+
'inline-start': isRtl ? 'right' : 'left'
|
|
2282
|
+
}[sideParam];
|
|
2283
|
+
const placement = align === 'center' ? side : `${side}-${align}`;
|
|
2284
|
+
let collisionPadding = collisionPaddingParam;
|
|
2285
|
+
|
|
2286
|
+
// Create a bias to the preferred side.
|
|
2287
|
+
// On iOS, when the mobile software keyboard opens, the input is exactly centered
|
|
2288
|
+
// in the viewport, but this can cause it to flip to the top undesirably.
|
|
2289
|
+
const bias = 1;
|
|
2290
|
+
const biasTop = sideParam === 'bottom' ? bias : 0;
|
|
2291
|
+
const biasBottom = sideParam === 'top' ? bias : 0;
|
|
2292
|
+
const biasLeft = sideParam === 'right' ? bias : 0;
|
|
2293
|
+
const biasRight = sideParam === 'left' ? bias : 0;
|
|
2294
|
+
if (typeof collisionPadding === 'number') {
|
|
2295
|
+
collisionPadding = {
|
|
2296
|
+
top: collisionPadding + biasTop,
|
|
2297
|
+
right: collisionPadding + biasRight,
|
|
2298
|
+
bottom: collisionPadding + biasBottom,
|
|
2299
|
+
left: collisionPadding + biasLeft
|
|
2300
|
+
};
|
|
2301
|
+
} else if (collisionPadding) {
|
|
2302
|
+
collisionPadding = {
|
|
2303
|
+
top: (collisionPadding.top || 0) + biasTop,
|
|
2304
|
+
right: (collisionPadding.right || 0) + biasRight,
|
|
2305
|
+
bottom: (collisionPadding.bottom || 0) + biasBottom,
|
|
2306
|
+
left: (collisionPadding.left || 0) + biasLeft
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
const commonCollisionProps = {
|
|
2310
|
+
boundary: collisionBoundary === 'clipping-ancestors' ? 'clippingAncestors' : collisionBoundary,
|
|
2311
|
+
padding: collisionPadding
|
|
2312
|
+
};
|
|
2313
|
+
|
|
2314
|
+
// Using a ref assumes that the arrow element is always present in the DOM for the lifetime of the
|
|
2315
|
+
// popup. If this assumption ends up being false, we can switch to state to manage the arrow's
|
|
2316
|
+
// presence.
|
|
2317
|
+
const arrowRef = React.useRef(null);
|
|
2318
|
+
|
|
2319
|
+
// Keep these reactive if they're not functions
|
|
2320
|
+
const sideOffsetRef = useValueAsRef(sideOffset);
|
|
2321
|
+
const alignOffsetRef = useValueAsRef(alignOffset);
|
|
2322
|
+
const sideOffsetDep = typeof sideOffset !== 'function' ? sideOffset : 0;
|
|
2323
|
+
const alignOffsetDep = typeof alignOffset !== 'function' ? alignOffset : 0;
|
|
2324
|
+
const middleware = [offset(state => {
|
|
2325
|
+
const data = getOffsetData(state, sideParam, isRtl);
|
|
2326
|
+
const sideAxis = typeof sideOffsetRef.current === 'function' ? sideOffsetRef.current(data) : sideOffsetRef.current;
|
|
2327
|
+
const alignAxis = typeof alignOffsetRef.current === 'function' ? alignOffsetRef.current(data) : alignOffsetRef.current;
|
|
2328
|
+
return {
|
|
2329
|
+
mainAxis: sideAxis,
|
|
2330
|
+
crossAxis: alignAxis,
|
|
2331
|
+
alignmentAxis: alignAxis
|
|
2332
|
+
};
|
|
2333
|
+
}, [sideOffsetDep, alignOffsetDep, isRtl, sideParam])];
|
|
2334
|
+
const shiftDisabled = collisionAvoidanceAlign === 'none' && collisionAvoidanceSide !== 'shift';
|
|
2335
|
+
const crossAxisShiftEnabled = !shiftDisabled && (sticky || shiftCrossAxis || collisionAvoidanceSide === 'shift');
|
|
2336
|
+
const flipMiddleware = collisionAvoidanceSide === 'none' ? null : flip({
|
|
2337
|
+
...commonCollisionProps,
|
|
2338
|
+
// Ensure the popup flips if it's been limited by its --available-height and it resizes.
|
|
2339
|
+
// Since the size() padding is smaller than the flip() padding, flip() will take precedence.
|
|
2340
|
+
padding: {
|
|
2341
|
+
top: collisionPadding.top + bias,
|
|
2342
|
+
right: collisionPadding.right + bias,
|
|
2343
|
+
bottom: collisionPadding.bottom + bias,
|
|
2344
|
+
left: collisionPadding.left + bias
|
|
2345
|
+
},
|
|
2346
|
+
mainAxis: !shiftCrossAxis && collisionAvoidanceSide === 'flip',
|
|
2347
|
+
crossAxis: collisionAvoidanceAlign === 'flip' ? 'alignment' : false,
|
|
2348
|
+
fallbackAxisSideDirection: collisionAvoidanceFallbackAxisSide
|
|
2527
2349
|
});
|
|
2528
|
-
|
|
2529
|
-
|
|
2350
|
+
const shiftMiddleware = shiftDisabled ? null : shift(data => {
|
|
2351
|
+
const html = ownerDocument(data.elements.floating).documentElement;
|
|
2352
|
+
return {
|
|
2353
|
+
...commonCollisionProps,
|
|
2354
|
+
// Use the Layout Viewport to avoid shifting around when pinch-zooming
|
|
2355
|
+
// for context menus.
|
|
2356
|
+
rootBoundary: shiftCrossAxis ? {
|
|
2357
|
+
x: 0,
|
|
2358
|
+
y: 0,
|
|
2359
|
+
width: html.clientWidth,
|
|
2360
|
+
height: html.clientHeight
|
|
2361
|
+
} : undefined,
|
|
2362
|
+
mainAxis: collisionAvoidanceAlign !== 'none',
|
|
2363
|
+
crossAxis: crossAxisShiftEnabled,
|
|
2364
|
+
limiter: sticky || shiftCrossAxis ? undefined : limitShift(limitData => {
|
|
2365
|
+
if (!arrowRef.current) {
|
|
2366
|
+
return {};
|
|
2367
|
+
}
|
|
2368
|
+
const {
|
|
2369
|
+
width,
|
|
2370
|
+
height
|
|
2371
|
+
} = arrowRef.current.getBoundingClientRect();
|
|
2372
|
+
const sideAxis = getSideAxis(getSide(limitData.placement));
|
|
2373
|
+
const arrowSize = sideAxis === 'y' ? width : height;
|
|
2374
|
+
const offsetAmount = sideAxis === 'y' ? collisionPadding.left + collisionPadding.right : collisionPadding.top + collisionPadding.bottom;
|
|
2375
|
+
return {
|
|
2376
|
+
offset: arrowSize / 2 + offsetAmount / 2
|
|
2377
|
+
};
|
|
2378
|
+
})
|
|
2379
|
+
};
|
|
2380
|
+
}, [commonCollisionProps, sticky, shiftCrossAxis, collisionPadding, collisionAvoidanceAlign]);
|
|
2381
|
+
|
|
2382
|
+
// https://floating-ui.com/docs/flip#combining-with-shift
|
|
2383
|
+
if (collisionAvoidanceSide === 'shift' || collisionAvoidanceAlign === 'shift' || align === 'center') {
|
|
2384
|
+
middleware.push(shiftMiddleware, flipMiddleware);
|
|
2385
|
+
} else {
|
|
2386
|
+
middleware.push(flipMiddleware, shiftMiddleware);
|
|
2530
2387
|
}
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2388
|
+
middleware.push(size({
|
|
2389
|
+
...commonCollisionProps,
|
|
2390
|
+
apply({
|
|
2391
|
+
elements: {
|
|
2392
|
+
floating
|
|
2393
|
+
},
|
|
2394
|
+
availableWidth,
|
|
2395
|
+
availableHeight,
|
|
2396
|
+
rects
|
|
2397
|
+
}) {
|
|
2398
|
+
const floatingStyle = floating.style;
|
|
2399
|
+
floatingStyle.setProperty('--available-width', `${availableWidth}px`);
|
|
2400
|
+
floatingStyle.setProperty('--available-height', `${availableHeight}px`);
|
|
2401
|
+
|
|
2402
|
+
// Snap anchor dimensions to device pixels to ensure the popup's visual width matches the anchor's one.
|
|
2403
|
+
const dpr = window.devicePixelRatio || 1;
|
|
2404
|
+
const {
|
|
2405
|
+
x,
|
|
2406
|
+
y,
|
|
2407
|
+
width,
|
|
2408
|
+
height
|
|
2409
|
+
} = rects.reference;
|
|
2410
|
+
const anchorWidth = (Math.round((x + width) * dpr) - Math.round(x * dpr)) / dpr;
|
|
2411
|
+
const anchorHeight = (Math.round((y + height) * dpr) - Math.round(y * dpr)) / dpr;
|
|
2412
|
+
floatingStyle.setProperty('--anchor-width', `${anchorWidth}px`);
|
|
2413
|
+
floatingStyle.setProperty('--anchor-height', `${anchorHeight}px`);
|
|
2414
|
+
}
|
|
2415
|
+
}), arrow(() => ({
|
|
2416
|
+
// `transform-origin` calculations rely on an element existing. If the arrow hasn't been set,
|
|
2417
|
+
// we'll create a fake element.
|
|
2418
|
+
element: arrowRef.current || document.createElement('div'),
|
|
2419
|
+
padding: arrowPadding,
|
|
2420
|
+
offsetParent: 'floating'
|
|
2421
|
+
}), [arrowPadding]), {
|
|
2422
|
+
name: 'transformOrigin',
|
|
2423
|
+
fn(state) {
|
|
2424
|
+
const {
|
|
2425
|
+
elements,
|
|
2426
|
+
middlewareData,
|
|
2427
|
+
placement: renderedPlacement,
|
|
2428
|
+
rects,
|
|
2429
|
+
y
|
|
2430
|
+
} = state;
|
|
2431
|
+
const currentRenderedSide = getSide(renderedPlacement);
|
|
2432
|
+
const currentRenderedAxis = getSideAxis(currentRenderedSide);
|
|
2433
|
+
const arrowEl = arrowRef.current;
|
|
2434
|
+
const arrowX = middlewareData.arrow?.x || 0;
|
|
2435
|
+
const arrowY = middlewareData.arrow?.y || 0;
|
|
2436
|
+
const arrowWidth = arrowEl?.clientWidth || 0;
|
|
2437
|
+
const arrowHeight = arrowEl?.clientHeight || 0;
|
|
2438
|
+
const transformX = arrowX + arrowWidth / 2;
|
|
2439
|
+
const transformY = arrowY + arrowHeight / 2;
|
|
2440
|
+
const shiftY = Math.abs(middlewareData.shift?.y || 0);
|
|
2441
|
+
const halfAnchorHeight = rects.reference.height / 2;
|
|
2442
|
+
const sideOffsetValue = typeof sideOffset === 'function' ? sideOffset(getOffsetData(state, sideParam, isRtl)) : sideOffset;
|
|
2443
|
+
const isOverlappingAnchor = shiftY > sideOffsetValue;
|
|
2444
|
+
const adjacentTransformOrigin = {
|
|
2445
|
+
top: `${transformX}px calc(100% + ${sideOffsetValue}px)`,
|
|
2446
|
+
bottom: `${transformX}px ${-sideOffsetValue}px`,
|
|
2447
|
+
left: `calc(100% + ${sideOffsetValue}px) ${transformY}px`,
|
|
2448
|
+
right: `${-sideOffsetValue}px ${transformY}px`
|
|
2449
|
+
}[currentRenderedSide];
|
|
2450
|
+
const overlapTransformOrigin = `${transformX}px ${rects.reference.y + halfAnchorHeight - y}px`;
|
|
2451
|
+
elements.floating.style.setProperty('--transform-origin', crossAxisShiftEnabled && currentRenderedAxis === 'y' && isOverlappingAnchor ? overlapTransformOrigin : adjacentTransformOrigin);
|
|
2452
|
+
return {};
|
|
2453
|
+
}
|
|
2454
|
+
}, hide, adaptiveOrigin);
|
|
2455
|
+
useIsoLayoutEffect(() => {
|
|
2456
|
+
// Ensure positioning doesn't run initially for `keepMounted` elements that
|
|
2457
|
+
// aren't initially open.
|
|
2458
|
+
if (!mounted && floatingRootContext) {
|
|
2459
|
+
floatingRootContext.update({
|
|
2460
|
+
referenceElement: null,
|
|
2461
|
+
floatingElement: null,
|
|
2462
|
+
domReferenceElement: null
|
|
2463
|
+
});
|
|
2464
|
+
}
|
|
2465
|
+
}, [mounted, floatingRootContext]);
|
|
2466
|
+
const autoUpdateOptions = React.useMemo(() => ({
|
|
2467
|
+
elementResize: !disableAnchorTracking && typeof ResizeObserver !== 'undefined',
|
|
2468
|
+
layoutShift: !disableAnchorTracking && typeof IntersectionObserver !== 'undefined'
|
|
2469
|
+
}), [disableAnchorTracking]);
|
|
2470
|
+
const {
|
|
2471
|
+
refs,
|
|
2472
|
+
elements,
|
|
2473
|
+
x,
|
|
2474
|
+
y,
|
|
2475
|
+
middlewareData,
|
|
2476
|
+
update,
|
|
2477
|
+
placement: renderedPlacement,
|
|
2478
|
+
context,
|
|
2479
|
+
isPositioned,
|
|
2480
|
+
floatingStyles: originalFloatingStyles
|
|
2481
|
+
} = useFloating({
|
|
2482
|
+
rootContext: floatingRootContext,
|
|
2483
|
+
placement,
|
|
2484
|
+
middleware,
|
|
2485
|
+
strategy: positionMethod,
|
|
2486
|
+
whileElementsMounted: keepMounted ? undefined : (...args) => autoUpdate(...args, autoUpdateOptions),
|
|
2487
|
+
nodeId,
|
|
2488
|
+
externalTree
|
|
2538
2489
|
});
|
|
2539
|
-
|
|
2540
|
-
|
|
2490
|
+
const {
|
|
2491
|
+
sideX,
|
|
2492
|
+
sideY
|
|
2493
|
+
} = middlewareData.adaptiveOrigin || DEFAULT_SIDES;
|
|
2494
|
+
|
|
2495
|
+
// Default to `fixed` when not positioned to prevent `autoFocus` scroll jumps.
|
|
2496
|
+
// This ensures the popup is inside the viewport initially before it gets positioned.
|
|
2497
|
+
const resolvedPosition = isPositioned ? positionMethod : 'fixed';
|
|
2498
|
+
const floatingStyles = React.useMemo(() => {
|
|
2499
|
+
const base = adaptiveOrigin ? {
|
|
2500
|
+
position: resolvedPosition,
|
|
2501
|
+
[sideX]: x,
|
|
2502
|
+
[sideY]: y
|
|
2503
|
+
} : {
|
|
2504
|
+
position: resolvedPosition,
|
|
2505
|
+
...originalFloatingStyles
|
|
2506
|
+
};
|
|
2507
|
+
if (!isPositioned) {
|
|
2508
|
+
base.opacity = 0;
|
|
2509
|
+
}
|
|
2510
|
+
return base;
|
|
2511
|
+
}, [adaptiveOrigin, resolvedPosition, sideX, x, sideY, y, originalFloatingStyles, isPositioned]);
|
|
2512
|
+
const registeredPositionReferenceRef = React.useRef(null);
|
|
2513
|
+
useIsoLayoutEffect(() => {
|
|
2514
|
+
if (!mounted) {
|
|
2515
|
+
return;
|
|
2516
|
+
}
|
|
2517
|
+
const anchorValue = anchorValueRef.current;
|
|
2518
|
+
const resolvedAnchor = typeof anchorValue === 'function' ? anchorValue() : anchorValue;
|
|
2519
|
+
const unwrappedElement = (isRef(resolvedAnchor) ? resolvedAnchor.current : resolvedAnchor) || null;
|
|
2520
|
+
const finalAnchor = unwrappedElement || null;
|
|
2521
|
+
if (finalAnchor !== registeredPositionReferenceRef.current) {
|
|
2522
|
+
refs.setPositionReference(finalAnchor);
|
|
2523
|
+
registeredPositionReferenceRef.current = finalAnchor;
|
|
2524
|
+
}
|
|
2525
|
+
}, [mounted, refs, anchorDep, anchorValueRef]);
|
|
2526
|
+
React.useEffect(() => {
|
|
2527
|
+
if (!mounted) {
|
|
2528
|
+
return;
|
|
2529
|
+
}
|
|
2530
|
+
const anchorValue = anchorValueRef.current;
|
|
2541
2531
|
|
|
2542
|
-
|
|
2543
|
-
if
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2532
|
+
// Refs from parent components are set after useLayoutEffect runs and are available in useEffect.
|
|
2533
|
+
// Therefore, if the anchor is a ref, we need to update the position reference in useEffect.
|
|
2534
|
+
if (typeof anchorValue === 'function') {
|
|
2535
|
+
return;
|
|
2536
|
+
}
|
|
2537
|
+
if (isRef(anchorValue) && anchorValue.current !== registeredPositionReferenceRef.current) {
|
|
2538
|
+
refs.setPositionReference(anchorValue.current);
|
|
2539
|
+
registeredPositionReferenceRef.current = anchorValue.current;
|
|
2540
|
+
}
|
|
2541
|
+
}, [mounted, refs, anchorDep, anchorValueRef]);
|
|
2542
|
+
React.useEffect(() => {
|
|
2543
|
+
if (keepMounted && mounted && elements.domReference && elements.floating) {
|
|
2544
|
+
return autoUpdate(elements.domReference, elements.floating, update, autoUpdateOptions);
|
|
2545
|
+
}
|
|
2546
|
+
return undefined;
|
|
2547
|
+
}, [keepMounted, mounted, elements, update, autoUpdateOptions]);
|
|
2548
|
+
const renderedSide = getSide(renderedPlacement);
|
|
2549
|
+
const logicalRenderedSide = getLogicalSide(sideParam, renderedSide, isRtl);
|
|
2550
|
+
const renderedAlign = getAlignment(renderedPlacement) || 'center';
|
|
2551
|
+
const anchorHidden = Boolean(middlewareData.hide?.referenceHidden);
|
|
2552
|
+
|
|
2553
|
+
/**
|
|
2554
|
+
* Locks the flip (makes it "sticky") so it doesn't prefer a given placement
|
|
2555
|
+
* and flips back lazily, not eagerly. Ideal for filtered lists that change
|
|
2556
|
+
* the size of the popup dynamically to avoid unwanted flipping when typing.
|
|
2557
|
+
*/
|
|
2558
|
+
useIsoLayoutEffect(() => {
|
|
2559
|
+
if (lazyFlip && mounted && isPositioned) {
|
|
2560
|
+
setMountSide(renderedSide);
|
|
2561
|
+
}
|
|
2562
|
+
}, [lazyFlip, mounted, isPositioned, renderedSide]);
|
|
2563
|
+
const arrowStyles = React.useMemo(() => ({
|
|
2564
|
+
position: 'absolute',
|
|
2565
|
+
top: middlewareData.arrow?.y,
|
|
2566
|
+
left: middlewareData.arrow?.x
|
|
2567
|
+
}), [middlewareData.arrow]);
|
|
2568
|
+
const arrowUncentered = middlewareData.arrow?.centerOffset !== 0;
|
|
2569
|
+
return React.useMemo(() => ({
|
|
2570
|
+
positionerStyles: floatingStyles,
|
|
2571
|
+
arrowStyles,
|
|
2572
|
+
arrowRef,
|
|
2573
|
+
arrowUncentered,
|
|
2574
|
+
side: logicalRenderedSide,
|
|
2575
|
+
align: renderedAlign,
|
|
2576
|
+
physicalSide: renderedSide,
|
|
2577
|
+
anchorHidden,
|
|
2578
|
+
refs,
|
|
2579
|
+
context,
|
|
2580
|
+
isPositioned,
|
|
2581
|
+
update
|
|
2582
|
+
}), [floatingStyles, arrowStyles, arrowRef, arrowUncentered, logicalRenderedSide, renderedAlign, renderedSide, anchorHidden, refs, context, isPositioned, update]);
|
|
2583
|
+
}
|
|
2584
|
+
function isRef(param) {
|
|
2585
|
+
return param != null && 'current' in param;
|
|
2550
2586
|
}
|
|
2551
2587
|
|
|
2552
2588
|
function getDisabledMountTransitionStyles(transitionStatus) {
|
|
2553
2589
|
return transitionStatus === 'starting' ? DISABLED_TRANSITIONS_STYLE : EMPTY_OBJECT;
|
|
2554
2590
|
}
|
|
2555
2591
|
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2592
|
+
let IndexGuessBehavior = /*#__PURE__*/function (IndexGuessBehavior) {
|
|
2593
|
+
IndexGuessBehavior[IndexGuessBehavior["None"] = 0] = "None";
|
|
2594
|
+
IndexGuessBehavior[IndexGuessBehavior["GuessFromOrder"] = 1] = "GuessFromOrder";
|
|
2595
|
+
return IndexGuessBehavior;
|
|
2596
|
+
}({});
|
|
2560
2597
|
|
|
2561
2598
|
/**
|
|
2562
|
-
*
|
|
2563
|
-
* Renders a `<div>` element.
|
|
2564
|
-
*
|
|
2565
|
-
* Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
|
|
2599
|
+
* Used to register a list item and its index (DOM position) in the `CompositeList`.
|
|
2566
2600
|
*/
|
|
2567
|
-
|
|
2568
|
-
const {
|
|
2569
|
-
render,
|
|
2570
|
-
className,
|
|
2571
|
-
finalFocus,
|
|
2572
|
-
...elementProps
|
|
2573
|
-
} = componentProps;
|
|
2601
|
+
function useCompositeListItem(params = {}) {
|
|
2574
2602
|
const {
|
|
2575
|
-
|
|
2576
|
-
|
|
2603
|
+
label,
|
|
2604
|
+
metadata,
|
|
2605
|
+
textRef,
|
|
2606
|
+
indexGuessBehavior,
|
|
2607
|
+
index: externalIndex
|
|
2608
|
+
} = params;
|
|
2577
2609
|
const {
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
const
|
|
2586
|
-
const
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
const
|
|
2595
|
-
const
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2610
|
+
register,
|
|
2611
|
+
unregister,
|
|
2612
|
+
subscribeMapChange,
|
|
2613
|
+
elementsRef,
|
|
2614
|
+
labelsRef,
|
|
2615
|
+
nextIndexRef
|
|
2616
|
+
} = useCompositeListContext();
|
|
2617
|
+
const indexRef = React.useRef(-1);
|
|
2618
|
+
const [index, setIndex] = React.useState(externalIndex ?? (indexGuessBehavior === IndexGuessBehavior.GuessFromOrder ? () => {
|
|
2619
|
+
if (indexRef.current === -1) {
|
|
2620
|
+
const newIndex = nextIndexRef.current;
|
|
2621
|
+
nextIndexRef.current += 1;
|
|
2622
|
+
indexRef.current = newIndex;
|
|
2623
|
+
}
|
|
2624
|
+
return indexRef.current;
|
|
2625
|
+
} : -1));
|
|
2626
|
+
const componentRef = React.useRef(null);
|
|
2627
|
+
const ref = React.useCallback(node => {
|
|
2628
|
+
componentRef.current = node;
|
|
2629
|
+
if (index !== -1 && node !== null) {
|
|
2630
|
+
elementsRef.current[index] = node;
|
|
2631
|
+
if (labelsRef) {
|
|
2632
|
+
const isLabelDefined = label !== undefined;
|
|
2633
|
+
labelsRef.current[index] = isLabelDefined ? label : textRef?.current?.textContent ?? node.textContent;
|
|
2602
2634
|
}
|
|
2603
2635
|
}
|
|
2604
|
-
});
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2636
|
+
}, [index, elementsRef, labelsRef, label, textRef]);
|
|
2637
|
+
useIsoLayoutEffect(() => {
|
|
2638
|
+
if (externalIndex != null) {
|
|
2639
|
+
return undefined;
|
|
2608
2640
|
}
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
instant: instantType
|
|
2627
|
-
};
|
|
2628
|
-
const setPopupElement = React.useCallback(element => {
|
|
2629
|
-
store.set('popupElement', element);
|
|
2630
|
-
}, [store]);
|
|
2631
|
-
const element = useRenderElement('div', componentProps, {
|
|
2632
|
-
state,
|
|
2633
|
-
ref: [forwardedRef, store.context.popupRef, setPopupElement],
|
|
2634
|
-
stateAttributesMapping,
|
|
2635
|
-
props: [popupProps, {
|
|
2636
|
-
onKeyDown(event) {
|
|
2637
|
-
if (insideToolbar && COMPOSITE_KEYS.has(event.key)) {
|
|
2638
|
-
event.stopPropagation();
|
|
2639
|
-
}
|
|
2641
|
+
const node = componentRef.current;
|
|
2642
|
+
if (node) {
|
|
2643
|
+
register(node, metadata);
|
|
2644
|
+
return () => {
|
|
2645
|
+
unregister(node);
|
|
2646
|
+
};
|
|
2647
|
+
}
|
|
2648
|
+
return undefined;
|
|
2649
|
+
}, [externalIndex, register, unregister, metadata]);
|
|
2650
|
+
useIsoLayoutEffect(() => {
|
|
2651
|
+
if (externalIndex != null) {
|
|
2652
|
+
return undefined;
|
|
2653
|
+
}
|
|
2654
|
+
return subscribeMapChange(map => {
|
|
2655
|
+
const i = componentRef.current ? map.get(componentRef.current)?.index : null;
|
|
2656
|
+
if (i != null) {
|
|
2657
|
+
setIndex(i);
|
|
2640
2658
|
}
|
|
2641
|
-
}
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
returnFocus = true;
|
|
2648
|
-
}
|
|
2649
|
-
return /*#__PURE__*/jsx(FloatingFocusManager, {
|
|
2650
|
-
context: floatingContext,
|
|
2651
|
-
modal: isContextMenu,
|
|
2652
|
-
disabled: !mounted,
|
|
2653
|
-
returnFocus: finalFocus === undefined ? returnFocus : finalFocus,
|
|
2654
|
-
initialFocus: parent.type !== 'menu',
|
|
2655
|
-
restoreFocus: true,
|
|
2656
|
-
externalTree: parent.type !== 'menubar' ? floatingTreeRoot : undefined,
|
|
2657
|
-
previousFocusableElement: activeTriggerElement,
|
|
2658
|
-
nextFocusableElement: parent.type === undefined ? store.context.triggerFocusTargetRef : undefined,
|
|
2659
|
-
beforeContentFocusGuardRef: parent.type === undefined ? store.context.beforeContentFocusGuardRef : undefined,
|
|
2660
|
-
children: element
|
|
2661
|
-
});
|
|
2662
|
-
});
|
|
2663
|
-
if (process.env.NODE_ENV !== "production") MenuPopup.displayName = "MenuPopup";
|
|
2664
|
-
|
|
2665
|
-
const MenuPortalContext = /*#__PURE__*/React.createContext(undefined);
|
|
2666
|
-
if (process.env.NODE_ENV !== "production") MenuPortalContext.displayName = "MenuPortalContext";
|
|
2667
|
-
function useMenuPortalContext() {
|
|
2668
|
-
const value = React.useContext(MenuPortalContext);
|
|
2669
|
-
if (value === undefined) {
|
|
2670
|
-
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: <Menu.Portal> is missing.' : formatErrorMessage(32));
|
|
2671
|
-
}
|
|
2672
|
-
return value;
|
|
2659
|
+
});
|
|
2660
|
+
}, [externalIndex, subscribeMapChange, setIndex]);
|
|
2661
|
+
return React.useMemo(() => ({
|
|
2662
|
+
ref,
|
|
2663
|
+
index
|
|
2664
|
+
}), [index, ref]);
|
|
2673
2665
|
}
|
|
2674
2666
|
|
|
2675
|
-
|
|
2667
|
+
/**
|
|
2668
|
+
* A button that closes the dialog.
|
|
2669
|
+
* Renders a `<button>` element.
|
|
2670
|
+
*
|
|
2671
|
+
* Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)
|
|
2672
|
+
*/
|
|
2673
|
+
const DialogClose = /*#__PURE__*/React.forwardRef(function DialogClose(componentProps, forwardedRef) {
|
|
2676
2674
|
const {
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2675
|
+
render,
|
|
2676
|
+
className,
|
|
2677
|
+
disabled = false,
|
|
2678
|
+
nativeButton = true,
|
|
2679
|
+
...elementProps
|
|
2680
|
+
} = componentProps;
|
|
2680
2681
|
const {
|
|
2681
2682
|
store
|
|
2682
|
-
} =
|
|
2683
|
-
const
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2683
|
+
} = useDialogRootContext();
|
|
2684
|
+
const open = store.useState('open');
|
|
2685
|
+
function handleClick(event) {
|
|
2686
|
+
if (open) {
|
|
2687
|
+
store.setOpen(false, createChangeEventDetails(closePress, event.nativeEvent));
|
|
2688
|
+
}
|
|
2687
2689
|
}
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2690
|
+
const {
|
|
2691
|
+
getButtonProps,
|
|
2692
|
+
buttonRef
|
|
2693
|
+
} = useButton({
|
|
2694
|
+
disabled,
|
|
2695
|
+
native: nativeButton
|
|
2696
|
+
});
|
|
2697
|
+
const state = {
|
|
2698
|
+
disabled
|
|
2699
|
+
};
|
|
2700
|
+
return useRenderElement('button', componentProps, {
|
|
2701
|
+
state,
|
|
2702
|
+
ref: [forwardedRef, buttonRef],
|
|
2703
|
+
props: [{
|
|
2704
|
+
onClick: handleClick
|
|
2705
|
+
}, elementProps, getButtonProps]
|
|
2694
2706
|
});
|
|
2695
2707
|
});
|
|
2696
|
-
if (process.env.NODE_ENV !== "production")
|
|
2697
|
-
|
|
2698
|
-
/**
|
|
2699
|
-
* @internal
|
|
2700
|
-
*/
|
|
2701
|
-
const DirectionContext = /*#__PURE__*/React.createContext(undefined);
|
|
2702
|
-
if (process.env.NODE_ENV !== "production") DirectionContext.displayName = "DirectionContext";
|
|
2703
|
-
function useDirection() {
|
|
2704
|
-
const context = React.useContext(DirectionContext);
|
|
2705
|
-
return context?.direction ?? 'ltr';
|
|
2706
|
-
}
|
|
2708
|
+
if (process.env.NODE_ENV !== "production") DialogClose.displayName = "DialogClose";
|
|
2707
2709
|
|
|
2708
2710
|
/**
|
|
2709
|
-
*
|
|
2710
|
-
*
|
|
2711
|
+
* A button that closes the drawer.
|
|
2712
|
+
* Renders a `<button>` element.
|
|
2713
|
+
*
|
|
2714
|
+
* Documentation: [Base UI Drawer](https://base-ui.com/react/components/drawer)
|
|
2711
2715
|
*/
|
|
2712
|
-
const
|
|
2713
|
-
name: 'arrow',
|
|
2714
|
-
options,
|
|
2715
|
-
async fn(state) {
|
|
2716
|
-
const {
|
|
2717
|
-
x,
|
|
2718
|
-
y,
|
|
2719
|
-
placement,
|
|
2720
|
-
rects,
|
|
2721
|
-
platform,
|
|
2722
|
-
elements,
|
|
2723
|
-
middlewareData
|
|
2724
|
-
} = state;
|
|
2725
|
-
// Since `element` is required, we don't Partial<> the type.
|
|
2726
|
-
const {
|
|
2727
|
-
element,
|
|
2728
|
-
padding = 0,
|
|
2729
|
-
offsetParent = 'real'
|
|
2730
|
-
} = evaluate(options, state) || {};
|
|
2731
|
-
if (element == null) {
|
|
2732
|
-
return {};
|
|
2733
|
-
}
|
|
2734
|
-
const paddingObject = getPaddingObject(padding);
|
|
2735
|
-
const coords = {
|
|
2736
|
-
x,
|
|
2737
|
-
y
|
|
2738
|
-
};
|
|
2739
|
-
const axis = getAlignmentAxis(placement);
|
|
2740
|
-
const length = getAxisLength(axis);
|
|
2741
|
-
const arrowDimensions = await platform.getDimensions(element);
|
|
2742
|
-
const isYAxis = axis === 'y';
|
|
2743
|
-
const minProp = isYAxis ? 'top' : 'left';
|
|
2744
|
-
const maxProp = isYAxis ? 'bottom' : 'right';
|
|
2745
|
-
const clientProp = isYAxis ? 'clientHeight' : 'clientWidth';
|
|
2746
|
-
const endDiff = rects.reference[length] + rects.reference[axis] - coords[axis] - rects.floating[length];
|
|
2747
|
-
const startDiff = coords[axis] - rects.reference[axis];
|
|
2748
|
-
const arrowOffsetParent = offsetParent === 'real' ? await platform.getOffsetParent?.(element) : elements.floating;
|
|
2749
|
-
let clientSize = elements.floating[clientProp] || rects.floating[length];
|
|
2750
|
-
|
|
2751
|
-
// DOM platform can return `window` as the `offsetParent`.
|
|
2752
|
-
if (!clientSize || !(await platform.isElement?.(arrowOffsetParent))) {
|
|
2753
|
-
clientSize = elements.floating[clientProp] || rects.floating[length];
|
|
2754
|
-
}
|
|
2755
|
-
const centerToReference = endDiff / 2 - startDiff / 2;
|
|
2756
|
-
|
|
2757
|
-
// If the padding is large enough that it causes the arrow to no longer be
|
|
2758
|
-
// centered, modify the padding so that it is centered.
|
|
2759
|
-
const largestPossiblePadding = clientSize / 2 - arrowDimensions[length] / 2 - 1;
|
|
2760
|
-
const minPadding = Math.min(paddingObject[minProp], largestPossiblePadding);
|
|
2761
|
-
const maxPadding = Math.min(paddingObject[maxProp], largestPossiblePadding);
|
|
2762
|
-
|
|
2763
|
-
// Make sure the arrow doesn't overflow the floating element if the center
|
|
2764
|
-
// point is outside the floating element's bounds.
|
|
2765
|
-
const min = minPadding;
|
|
2766
|
-
const max = clientSize - arrowDimensions[length] - maxPadding;
|
|
2767
|
-
const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference;
|
|
2768
|
-
const offset = clamp(min, center, max);
|
|
2769
|
-
|
|
2770
|
-
// If the reference is small enough that the arrow's padding causes it to
|
|
2771
|
-
// to point to nothing for an aligned placement, adjust the offset of the
|
|
2772
|
-
// floating element itself. To ensure `shift()` continues to take action,
|
|
2773
|
-
// a single reset is performed when this is true.
|
|
2774
|
-
const shouldAddOffset = !middlewareData.arrow && getAlignment(placement) != null && center !== offset && rects.reference[length] / 2 - (center < min ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0;
|
|
2775
|
-
// eslint-disable-next-line no-nested-ternary
|
|
2776
|
-
const alignmentOffset = shouldAddOffset ? center < min ? center - min : center - max : 0;
|
|
2777
|
-
return {
|
|
2778
|
-
[axis]: coords[axis] + alignmentOffset,
|
|
2779
|
-
data: {
|
|
2780
|
-
[axis]: offset,
|
|
2781
|
-
centerOffset: center - offset - alignmentOffset,
|
|
2782
|
-
...(shouldAddOffset && {
|
|
2783
|
-
alignmentOffset
|
|
2784
|
-
})
|
|
2785
|
-
},
|
|
2786
|
-
reset: shouldAddOffset
|
|
2787
|
-
};
|
|
2788
|
-
}
|
|
2789
|
-
});
|
|
2716
|
+
const DrawerClose = DialogClose;
|
|
2790
2717
|
|
|
2791
2718
|
/**
|
|
2792
|
-
*
|
|
2793
|
-
*
|
|
2794
|
-
*
|
|
2795
|
-
*
|
|
2719
|
+
* A heading that labels the dialog.
|
|
2720
|
+
* Renders an `<h2>` element.
|
|
2721
|
+
*
|
|
2722
|
+
* Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)
|
|
2796
2723
|
*/
|
|
2797
|
-
const
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
floating: floatRect
|
|
2833
|
-
},
|
|
2834
|
-
elements: {
|
|
2835
|
-
floating
|
|
2836
|
-
},
|
|
2837
|
-
platform,
|
|
2838
|
-
strategy,
|
|
2839
|
-
placement
|
|
2840
|
-
} = state;
|
|
2841
|
-
const win = getWindow(floating);
|
|
2842
|
-
const styles = win.getComputedStyle(floating);
|
|
2843
|
-
const hasTransition = styles.transitionDuration !== '0s' && styles.transitionDuration !== '';
|
|
2844
|
-
if (!hasTransition) {
|
|
2845
|
-
return {
|
|
2846
|
-
x: rawX,
|
|
2847
|
-
y: rawY,
|
|
2848
|
-
data: DEFAULT_SIDES
|
|
2849
|
-
};
|
|
2850
|
-
}
|
|
2851
|
-
const offsetParent = await platform.getOffsetParent?.(floating);
|
|
2852
|
-
let offsetDimensions = {
|
|
2853
|
-
width: 0,
|
|
2854
|
-
height: 0
|
|
2855
|
-
};
|
|
2856
|
-
|
|
2857
|
-
// For fixed strategy, prefer visualViewport if available
|
|
2858
|
-
if (strategy === 'fixed' && win?.visualViewport) {
|
|
2859
|
-
offsetDimensions = {
|
|
2860
|
-
width: win.visualViewport.width,
|
|
2861
|
-
height: win.visualViewport.height
|
|
2862
|
-
};
|
|
2863
|
-
} else if (offsetParent === win) {
|
|
2864
|
-
const doc = ownerDocument(floating);
|
|
2865
|
-
offsetDimensions = {
|
|
2866
|
-
width: doc.documentElement.clientWidth,
|
|
2867
|
-
height: doc.documentElement.clientHeight
|
|
2868
|
-
};
|
|
2869
|
-
} else if (await platform.isElement?.(offsetParent)) {
|
|
2870
|
-
offsetDimensions = await platform.getDimensions(offsetParent);
|
|
2871
|
-
}
|
|
2872
|
-
const currentSide = getSide(placement);
|
|
2873
|
-
let x = rawX;
|
|
2874
|
-
let y = rawY;
|
|
2875
|
-
if (currentSide === 'left') {
|
|
2876
|
-
x = offsetDimensions.width - (rawX + floatRect.width);
|
|
2877
|
-
}
|
|
2878
|
-
if (currentSide === 'top') {
|
|
2879
|
-
y = offsetDimensions.height - (rawY + floatRect.height);
|
|
2880
|
-
}
|
|
2881
|
-
const sideX = currentSide === 'left' ? 'right' : DEFAULT_SIDES.sideX;
|
|
2882
|
-
const sideY = currentSide === 'top' ? 'bottom' : DEFAULT_SIDES.sideY;
|
|
2883
|
-
return {
|
|
2884
|
-
x,
|
|
2885
|
-
y,
|
|
2886
|
-
data: {
|
|
2887
|
-
sideX,
|
|
2888
|
-
sideY
|
|
2889
|
-
}
|
|
2890
|
-
};
|
|
2724
|
+
const DialogTitle = /*#__PURE__*/React.forwardRef(function DialogTitle(componentProps, forwardedRef) {
|
|
2725
|
+
const {
|
|
2726
|
+
render,
|
|
2727
|
+
className,
|
|
2728
|
+
id: idProp,
|
|
2729
|
+
...elementProps
|
|
2730
|
+
} = componentProps;
|
|
2731
|
+
const {
|
|
2732
|
+
store
|
|
2733
|
+
} = useDialogRootContext();
|
|
2734
|
+
const id = useBaseUiId(idProp);
|
|
2735
|
+
store.useSyncedValueWithCleanup('titleElementId', id);
|
|
2736
|
+
return useRenderElement('h2', componentProps, {
|
|
2737
|
+
ref: forwardedRef,
|
|
2738
|
+
props: [{
|
|
2739
|
+
id
|
|
2740
|
+
}, elementProps]
|
|
2741
|
+
});
|
|
2742
|
+
});
|
|
2743
|
+
if (process.env.NODE_ENV !== "production") DialogTitle.displayName = "DialogTitle";
|
|
2744
|
+
|
|
2745
|
+
/**
|
|
2746
|
+
* A heading that labels the drawer.
|
|
2747
|
+
* Renders an `<h2>` element.
|
|
2748
|
+
*
|
|
2749
|
+
* Documentation: [Base UI Drawer](https://base-ui.com/react/components/drawer)
|
|
2750
|
+
*/
|
|
2751
|
+
const DrawerTitle = DialogTitle;
|
|
2752
|
+
|
|
2753
|
+
const MenuPositionerContext = /*#__PURE__*/React.createContext(undefined);
|
|
2754
|
+
if (process.env.NODE_ENV !== "production") MenuPositionerContext.displayName = "MenuPositionerContext";
|
|
2755
|
+
function useMenuPositionerContext(optional) {
|
|
2756
|
+
const context = React.useContext(MenuPositionerContext);
|
|
2757
|
+
if (context === undefined && !optional) {
|
|
2758
|
+
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: MenuPositionerContext is missing. MenuPositioner parts must be placed within <Menu.Positioner>.' : formatErrorMessage(33));
|
|
2891
2759
|
}
|
|
2892
|
-
|
|
2760
|
+
return context;
|
|
2761
|
+
}
|
|
2893
2762
|
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
const
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
left: isLogicalSideParam ? logicalLeft : 'left'
|
|
2903
|
-
}[renderedSide];
|
|
2763
|
+
const MenuRootContext = /*#__PURE__*/React.createContext(undefined);
|
|
2764
|
+
if (process.env.NODE_ENV !== "production") MenuRootContext.displayName = "MenuRootContext";
|
|
2765
|
+
function useMenuRootContext(optional) {
|
|
2766
|
+
const context = React.useContext(MenuRootContext);
|
|
2767
|
+
if (context === undefined && !optional) {
|
|
2768
|
+
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: MenuRootContext is missing. Menu parts must be placed within <Menu.Root>.' : formatErrorMessage(36));
|
|
2769
|
+
}
|
|
2770
|
+
return context;
|
|
2904
2771
|
}
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
width: rects.reference.width,
|
|
2915
|
-
height: rects.reference.height
|
|
2916
|
-
},
|
|
2917
|
-
positioner: {
|
|
2918
|
-
width: rects.floating.width,
|
|
2919
|
-
height: rects.floating.height
|
|
2920
|
-
}
|
|
2921
|
-
};
|
|
2922
|
-
return data;
|
|
2772
|
+
|
|
2773
|
+
const ContextMenuRootContext = /*#__PURE__*/React.createContext(undefined);
|
|
2774
|
+
if (process.env.NODE_ENV !== "production") ContextMenuRootContext.displayName = "ContextMenuRootContext";
|
|
2775
|
+
function useContextMenuRootContext(optional = true) {
|
|
2776
|
+
const context = React.useContext(ContextMenuRootContext);
|
|
2777
|
+
if (context === undefined && !optional) {
|
|
2778
|
+
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: ContextMenuRootContext is missing. ContextMenu parts must be placed within <ContextMenu.Root>.' : formatErrorMessage(25));
|
|
2779
|
+
}
|
|
2780
|
+
return context;
|
|
2923
2781
|
}
|
|
2782
|
+
|
|
2924
2783
|
/**
|
|
2925
|
-
*
|
|
2926
|
-
*
|
|
2784
|
+
* Returns common props shared by all menu item types.
|
|
2785
|
+
* This hook extracts the shared logic for id, role, tabIndex, onKeyDown,
|
|
2786
|
+
* onMouseMove, onClick, and onMouseUp handlers.
|
|
2927
2787
|
*/
|
|
2928
|
-
function
|
|
2788
|
+
function useMenuItemCommonProps(params) {
|
|
2929
2789
|
const {
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
side: sideParam = 'bottom',
|
|
2934
|
-
sideOffset = 0,
|
|
2935
|
-
align = 'center',
|
|
2936
|
-
alignOffset = 0,
|
|
2937
|
-
collisionBoundary,
|
|
2938
|
-
collisionPadding: collisionPaddingParam = 5,
|
|
2939
|
-
sticky = false,
|
|
2940
|
-
arrowPadding = 5,
|
|
2941
|
-
disableAnchorTracking = false,
|
|
2942
|
-
// Private parameters
|
|
2943
|
-
keepMounted = false,
|
|
2944
|
-
floatingRootContext,
|
|
2945
|
-
mounted,
|
|
2946
|
-
collisionAvoidance,
|
|
2947
|
-
shiftCrossAxis = false,
|
|
2790
|
+
closeOnClick,
|
|
2791
|
+
highlighted,
|
|
2792
|
+
id,
|
|
2948
2793
|
nodeId,
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2794
|
+
store,
|
|
2795
|
+
typingRef,
|
|
2796
|
+
itemRef,
|
|
2797
|
+
itemMetadata
|
|
2952
2798
|
} = params;
|
|
2953
|
-
const
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
const
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
const side = mountSide || {
|
|
2967
|
-
top: 'top',
|
|
2968
|
-
right: 'right',
|
|
2969
|
-
bottom: 'bottom',
|
|
2970
|
-
left: 'left',
|
|
2971
|
-
'inline-end': isRtl ? 'left' : 'right',
|
|
2972
|
-
'inline-start': isRtl ? 'right' : 'left'
|
|
2973
|
-
}[sideParam];
|
|
2974
|
-
const placement = align === 'center' ? side : `${side}-${align}`;
|
|
2975
|
-
let collisionPadding = collisionPaddingParam;
|
|
2976
|
-
|
|
2977
|
-
// Create a bias to the preferred side.
|
|
2978
|
-
// On iOS, when the mobile software keyboard opens, the input is exactly centered
|
|
2979
|
-
// in the viewport, but this can cause it to flip to the top undesirably.
|
|
2980
|
-
const bias = 1;
|
|
2981
|
-
const biasTop = sideParam === 'bottom' ? bias : 0;
|
|
2982
|
-
const biasBottom = sideParam === 'top' ? bias : 0;
|
|
2983
|
-
const biasLeft = sideParam === 'right' ? bias : 0;
|
|
2984
|
-
const biasRight = sideParam === 'left' ? bias : 0;
|
|
2985
|
-
if (typeof collisionPadding === 'number') {
|
|
2986
|
-
collisionPadding = {
|
|
2987
|
-
top: collisionPadding + biasTop,
|
|
2988
|
-
right: collisionPadding + biasRight,
|
|
2989
|
-
bottom: collisionPadding + biasBottom,
|
|
2990
|
-
left: collisionPadding + biasLeft
|
|
2991
|
-
};
|
|
2992
|
-
} else if (collisionPadding) {
|
|
2993
|
-
collisionPadding = {
|
|
2994
|
-
top: (collisionPadding.top || 0) + biasTop,
|
|
2995
|
-
right: (collisionPadding.right || 0) + biasRight,
|
|
2996
|
-
bottom: (collisionPadding.bottom || 0) + biasBottom,
|
|
2997
|
-
left: (collisionPadding.left || 0) + biasLeft
|
|
2998
|
-
};
|
|
2999
|
-
}
|
|
3000
|
-
const commonCollisionProps = {
|
|
3001
|
-
boundary: collisionBoundary === 'clipping-ancestors' ? 'clippingAncestors' : collisionBoundary,
|
|
3002
|
-
padding: collisionPadding
|
|
3003
|
-
};
|
|
3004
|
-
|
|
3005
|
-
// Using a ref assumes that the arrow element is always present in the DOM for the lifetime of the
|
|
3006
|
-
// popup. If this assumption ends up being false, we can switch to state to manage the arrow's
|
|
3007
|
-
// presence.
|
|
3008
|
-
const arrowRef = React.useRef(null);
|
|
3009
|
-
|
|
3010
|
-
// Keep these reactive if they're not functions
|
|
3011
|
-
const sideOffsetRef = useValueAsRef(sideOffset);
|
|
3012
|
-
const alignOffsetRef = useValueAsRef(alignOffset);
|
|
3013
|
-
const sideOffsetDep = typeof sideOffset !== 'function' ? sideOffset : 0;
|
|
3014
|
-
const alignOffsetDep = typeof alignOffset !== 'function' ? alignOffset : 0;
|
|
3015
|
-
const middleware = [offset(state => {
|
|
3016
|
-
const data = getOffsetData(state, sideParam, isRtl);
|
|
3017
|
-
const sideAxis = typeof sideOffsetRef.current === 'function' ? sideOffsetRef.current(data) : sideOffsetRef.current;
|
|
3018
|
-
const alignAxis = typeof alignOffsetRef.current === 'function' ? alignOffsetRef.current(data) : alignOffsetRef.current;
|
|
3019
|
-
return {
|
|
3020
|
-
mainAxis: sideAxis,
|
|
3021
|
-
crossAxis: alignAxis,
|
|
3022
|
-
alignmentAxis: alignAxis
|
|
3023
|
-
};
|
|
3024
|
-
}, [sideOffsetDep, alignOffsetDep, isRtl, sideParam])];
|
|
3025
|
-
const shiftDisabled = collisionAvoidanceAlign === 'none' && collisionAvoidanceSide !== 'shift';
|
|
3026
|
-
const crossAxisShiftEnabled = !shiftDisabled && (sticky || shiftCrossAxis || collisionAvoidanceSide === 'shift');
|
|
3027
|
-
const flipMiddleware = collisionAvoidanceSide === 'none' ? null : flip({
|
|
3028
|
-
...commonCollisionProps,
|
|
3029
|
-
// Ensure the popup flips if it's been limited by its --available-height and it resizes.
|
|
3030
|
-
// Since the size() padding is smaller than the flip() padding, flip() will take precedence.
|
|
3031
|
-
padding: {
|
|
3032
|
-
top: collisionPadding.top + bias,
|
|
3033
|
-
right: collisionPadding.right + bias,
|
|
3034
|
-
bottom: collisionPadding.bottom + bias,
|
|
3035
|
-
left: collisionPadding.left + bias
|
|
2799
|
+
const {
|
|
2800
|
+
events: menuEvents
|
|
2801
|
+
} = store.useState('floatingTreeRoot');
|
|
2802
|
+
const contextMenuContext = useContextMenuRootContext(true);
|
|
2803
|
+
const isContextMenu = contextMenuContext !== undefined;
|
|
2804
|
+
return React.useMemo(() => ({
|
|
2805
|
+
id,
|
|
2806
|
+
role: 'menuitem',
|
|
2807
|
+
tabIndex: highlighted ? 0 : -1,
|
|
2808
|
+
onKeyDown(event) {
|
|
2809
|
+
if (event.key === ' ' && typingRef?.current) {
|
|
2810
|
+
event.preventDefault();
|
|
2811
|
+
}
|
|
3036
2812
|
},
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
const shiftMiddleware = shiftDisabled ? null : shift(data => {
|
|
3042
|
-
const html = ownerDocument(data.elements.floating).documentElement;
|
|
3043
|
-
return {
|
|
3044
|
-
...commonCollisionProps,
|
|
3045
|
-
// Use the Layout Viewport to avoid shifting around when pinch-zooming
|
|
3046
|
-
// for context menus.
|
|
3047
|
-
rootBoundary: shiftCrossAxis ? {
|
|
3048
|
-
x: 0,
|
|
3049
|
-
y: 0,
|
|
3050
|
-
width: html.clientWidth,
|
|
3051
|
-
height: html.clientHeight
|
|
3052
|
-
} : undefined,
|
|
3053
|
-
mainAxis: collisionAvoidanceAlign !== 'none',
|
|
3054
|
-
crossAxis: crossAxisShiftEnabled,
|
|
3055
|
-
limiter: sticky || shiftCrossAxis ? undefined : limitShift(limitData => {
|
|
3056
|
-
if (!arrowRef.current) {
|
|
3057
|
-
return {};
|
|
3058
|
-
}
|
|
3059
|
-
const {
|
|
3060
|
-
width,
|
|
3061
|
-
height
|
|
3062
|
-
} = arrowRef.current.getBoundingClientRect();
|
|
3063
|
-
const sideAxis = getSideAxis(getSide(limitData.placement));
|
|
3064
|
-
const arrowSize = sideAxis === 'y' ? width : height;
|
|
3065
|
-
const offsetAmount = sideAxis === 'y' ? collisionPadding.left + collisionPadding.right : collisionPadding.top + collisionPadding.bottom;
|
|
3066
|
-
return {
|
|
3067
|
-
offset: arrowSize / 2 + offsetAmount / 2
|
|
3068
|
-
};
|
|
3069
|
-
})
|
|
3070
|
-
};
|
|
3071
|
-
}, [commonCollisionProps, sticky, shiftCrossAxis, collisionPadding, collisionAvoidanceAlign]);
|
|
3072
|
-
|
|
3073
|
-
// https://floating-ui.com/docs/flip#combining-with-shift
|
|
3074
|
-
if (collisionAvoidanceSide === 'shift' || collisionAvoidanceAlign === 'shift' || align === 'center') {
|
|
3075
|
-
middleware.push(shiftMiddleware, flipMiddleware);
|
|
3076
|
-
} else {
|
|
3077
|
-
middleware.push(flipMiddleware, shiftMiddleware);
|
|
3078
|
-
}
|
|
3079
|
-
middleware.push(size({
|
|
3080
|
-
...commonCollisionProps,
|
|
3081
|
-
apply({
|
|
3082
|
-
elements: {
|
|
3083
|
-
floating
|
|
3084
|
-
},
|
|
3085
|
-
availableWidth,
|
|
3086
|
-
availableHeight,
|
|
3087
|
-
rects
|
|
3088
|
-
}) {
|
|
3089
|
-
const floatingStyle = floating.style;
|
|
3090
|
-
floatingStyle.setProperty('--available-width', `${availableWidth}px`);
|
|
3091
|
-
floatingStyle.setProperty('--available-height', `${availableHeight}px`);
|
|
2813
|
+
onMouseMove(event) {
|
|
2814
|
+
if (!nodeId) {
|
|
2815
|
+
return;
|
|
2816
|
+
}
|
|
3092
2817
|
|
|
3093
|
-
//
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
width,
|
|
3099
|
-
height
|
|
3100
|
-
} = rects.reference;
|
|
3101
|
-
const anchorWidth = (Math.round((x + width) * dpr) - Math.round(x * dpr)) / dpr;
|
|
3102
|
-
const anchorHeight = (Math.round((y + height) * dpr) - Math.round(y * dpr)) / dpr;
|
|
3103
|
-
floatingStyle.setProperty('--anchor-width', `${anchorWidth}px`);
|
|
3104
|
-
floatingStyle.setProperty('--anchor-height', `${anchorHeight}px`);
|
|
3105
|
-
}
|
|
3106
|
-
}), arrow(() => ({
|
|
3107
|
-
// `transform-origin` calculations rely on an element existing. If the arrow hasn't been set,
|
|
3108
|
-
// we'll create a fake element.
|
|
3109
|
-
element: arrowRef.current || document.createElement('div'),
|
|
3110
|
-
padding: arrowPadding,
|
|
3111
|
-
offsetParent: 'floating'
|
|
3112
|
-
}), [arrowPadding]), {
|
|
3113
|
-
name: 'transformOrigin',
|
|
3114
|
-
fn(state) {
|
|
3115
|
-
const {
|
|
3116
|
-
elements,
|
|
3117
|
-
middlewareData,
|
|
3118
|
-
placement: renderedPlacement,
|
|
3119
|
-
rects,
|
|
3120
|
-
y
|
|
3121
|
-
} = state;
|
|
3122
|
-
const currentRenderedSide = getSide(renderedPlacement);
|
|
3123
|
-
const currentRenderedAxis = getSideAxis(currentRenderedSide);
|
|
3124
|
-
const arrowEl = arrowRef.current;
|
|
3125
|
-
const arrowX = middlewareData.arrow?.x || 0;
|
|
3126
|
-
const arrowY = middlewareData.arrow?.y || 0;
|
|
3127
|
-
const arrowWidth = arrowEl?.clientWidth || 0;
|
|
3128
|
-
const arrowHeight = arrowEl?.clientHeight || 0;
|
|
3129
|
-
const transformX = arrowX + arrowWidth / 2;
|
|
3130
|
-
const transformY = arrowY + arrowHeight / 2;
|
|
3131
|
-
const shiftY = Math.abs(middlewareData.shift?.y || 0);
|
|
3132
|
-
const halfAnchorHeight = rects.reference.height / 2;
|
|
3133
|
-
const sideOffsetValue = typeof sideOffset === 'function' ? sideOffset(getOffsetData(state, sideParam, isRtl)) : sideOffset;
|
|
3134
|
-
const isOverlappingAnchor = shiftY > sideOffsetValue;
|
|
3135
|
-
const adjacentTransformOrigin = {
|
|
3136
|
-
top: `${transformX}px calc(100% + ${sideOffsetValue}px)`,
|
|
3137
|
-
bottom: `${transformX}px ${-sideOffsetValue}px`,
|
|
3138
|
-
left: `calc(100% + ${sideOffsetValue}px) ${transformY}px`,
|
|
3139
|
-
right: `${-sideOffsetValue}px ${transformY}px`
|
|
3140
|
-
}[currentRenderedSide];
|
|
3141
|
-
const overlapTransformOrigin = `${transformX}px ${rects.reference.y + halfAnchorHeight - y}px`;
|
|
3142
|
-
elements.floating.style.setProperty('--transform-origin', crossAxisShiftEnabled && currentRenderedAxis === 'y' && isOverlappingAnchor ? overlapTransformOrigin : adjacentTransformOrigin);
|
|
3143
|
-
return {};
|
|
3144
|
-
}
|
|
3145
|
-
}, hide, adaptiveOrigin);
|
|
3146
|
-
useIsoLayoutEffect(() => {
|
|
3147
|
-
// Ensure positioning doesn't run initially for `keepMounted` elements that
|
|
3148
|
-
// aren't initially open.
|
|
3149
|
-
if (!mounted && floatingRootContext) {
|
|
3150
|
-
floatingRootContext.update({
|
|
3151
|
-
referenceElement: null,
|
|
3152
|
-
floatingElement: null,
|
|
3153
|
-
domReferenceElement: null
|
|
2818
|
+
// Inform the floating tree that a menu item within this menu was hovered/moved over
|
|
2819
|
+
// so unrelated descendant submenus can be closed.
|
|
2820
|
+
menuEvents.emit('itemhover', {
|
|
2821
|
+
nodeId,
|
|
2822
|
+
target: event.currentTarget
|
|
3154
2823
|
});
|
|
2824
|
+
},
|
|
2825
|
+
onClick(event) {
|
|
2826
|
+
if (closeOnClick) {
|
|
2827
|
+
menuEvents.emit('close', {
|
|
2828
|
+
domEvent: event,
|
|
2829
|
+
reason: itemPress
|
|
2830
|
+
});
|
|
2831
|
+
}
|
|
2832
|
+
},
|
|
2833
|
+
onMouseUp(event) {
|
|
2834
|
+
if (contextMenuContext) {
|
|
2835
|
+
const initialCursorPoint = contextMenuContext.initialCursorPointRef.current;
|
|
2836
|
+
contextMenuContext.initialCursorPointRef.current = null;
|
|
2837
|
+
if (isContextMenu && initialCursorPoint && Math.abs(event.clientX - initialCursorPoint.x) <= 1 && Math.abs(event.clientY - initialCursorPoint.y) <= 1) {
|
|
2838
|
+
return;
|
|
2839
|
+
}
|
|
2840
|
+
|
|
2841
|
+
// On non-macOS platforms, this mouseup belongs to the right-click gesture
|
|
2842
|
+
// that opened the context menu, so it must not activate an item.
|
|
2843
|
+
if (isContextMenu && !isMac && event.button === 2) {
|
|
2844
|
+
return;
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
if (itemRef.current && store.context.allowMouseUpTriggerRef.current && (!isContextMenu || event.button === 2)) {
|
|
2848
|
+
// This fires whenever the user clicks on the trigger, moves the cursor, and releases it over the item.
|
|
2849
|
+
// We trigger the click and override the `closeOnClick` preference to always close the menu.
|
|
2850
|
+
if (!itemMetadata || itemMetadata.type === 'regular-item') {
|
|
2851
|
+
itemRef.current.click();
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
3155
2854
|
}
|
|
3156
|
-
}, [
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
2855
|
+
}), [closeOnClick, highlighted, id, menuEvents, nodeId, store, typingRef, itemRef, contextMenuContext, isContextMenu, itemMetadata]);
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2858
|
+
const REGULAR_ITEM = {
|
|
2859
|
+
type: 'regular-item'
|
|
2860
|
+
};
|
|
2861
|
+
function useMenuItem(params) {
|
|
3161
2862
|
const {
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
2863
|
+
closeOnClick,
|
|
2864
|
+
disabled = false,
|
|
2865
|
+
highlighted,
|
|
2866
|
+
id,
|
|
2867
|
+
store,
|
|
2868
|
+
typingRef = store.context.typingRef,
|
|
2869
|
+
nativeButton,
|
|
2870
|
+
itemMetadata,
|
|
2871
|
+
nodeId
|
|
2872
|
+
} = params;
|
|
2873
|
+
const itemRef = React.useRef(null);
|
|
2874
|
+
const {
|
|
2875
|
+
getButtonProps,
|
|
2876
|
+
buttonRef
|
|
2877
|
+
} = useButton({
|
|
2878
|
+
disabled,
|
|
2879
|
+
focusableWhenDisabled: true,
|
|
2880
|
+
native: nativeButton,
|
|
2881
|
+
composite: true
|
|
2882
|
+
});
|
|
2883
|
+
const commonProps = useMenuItemCommonProps({
|
|
2884
|
+
closeOnClick,
|
|
2885
|
+
highlighted,
|
|
2886
|
+
id,
|
|
3178
2887
|
nodeId,
|
|
3179
|
-
|
|
2888
|
+
store,
|
|
2889
|
+
typingRef,
|
|
2890
|
+
itemRef,
|
|
2891
|
+
itemMetadata
|
|
3180
2892
|
});
|
|
3181
|
-
const {
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
2893
|
+
const getItemProps = React.useCallback(externalProps => {
|
|
2894
|
+
return mergeProps(commonProps, {
|
|
2895
|
+
onMouseEnter() {
|
|
2896
|
+
if (itemMetadata.type !== 'submenu-trigger') {
|
|
2897
|
+
return;
|
|
2898
|
+
}
|
|
2899
|
+
itemMetadata.setActive();
|
|
2900
|
+
}
|
|
2901
|
+
}, externalProps, getButtonProps);
|
|
2902
|
+
}, [commonProps, getButtonProps, itemMetadata]);
|
|
2903
|
+
const mergedRef = useMergedRefs(itemRef, buttonRef);
|
|
2904
|
+
return React.useMemo(() => ({
|
|
2905
|
+
getItemProps,
|
|
2906
|
+
itemRef: mergedRef
|
|
2907
|
+
}), [getItemProps, mergedRef]);
|
|
2908
|
+
}
|
|
3185
2909
|
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
if (!mounted) {
|
|
3206
|
-
return;
|
|
3207
|
-
}
|
|
3208
|
-
const anchorValue = anchorValueRef.current;
|
|
3209
|
-
const resolvedAnchor = typeof anchorValue === 'function' ? anchorValue() : anchorValue;
|
|
3210
|
-
const unwrappedElement = (isRef(resolvedAnchor) ? resolvedAnchor.current : resolvedAnchor) || null;
|
|
3211
|
-
const finalAnchor = unwrappedElement || null;
|
|
3212
|
-
if (finalAnchor !== registeredPositionReferenceRef.current) {
|
|
3213
|
-
refs.setPositionReference(finalAnchor);
|
|
3214
|
-
registeredPositionReferenceRef.current = finalAnchor;
|
|
3215
|
-
}
|
|
3216
|
-
}, [mounted, refs, anchorDep, anchorValueRef]);
|
|
3217
|
-
React.useEffect(() => {
|
|
3218
|
-
if (!mounted) {
|
|
3219
|
-
return;
|
|
3220
|
-
}
|
|
3221
|
-
const anchorValue = anchorValueRef.current;
|
|
2910
|
+
let MenuCheckboxItemDataAttributes = /*#__PURE__*/function (MenuCheckboxItemDataAttributes) {
|
|
2911
|
+
/**
|
|
2912
|
+
* Present when the menu checkbox item is checked.
|
|
2913
|
+
*/
|
|
2914
|
+
MenuCheckboxItemDataAttributes["checked"] = "data-checked";
|
|
2915
|
+
/**
|
|
2916
|
+
* Present when the menu checkbox item is not checked.
|
|
2917
|
+
*/
|
|
2918
|
+
MenuCheckboxItemDataAttributes["unchecked"] = "data-unchecked";
|
|
2919
|
+
/**
|
|
2920
|
+
* Present when the menu checkbox item is disabled.
|
|
2921
|
+
*/
|
|
2922
|
+
MenuCheckboxItemDataAttributes["disabled"] = "data-disabled";
|
|
2923
|
+
/**
|
|
2924
|
+
* Present when the menu checkbox item is highlighted.
|
|
2925
|
+
*/
|
|
2926
|
+
MenuCheckboxItemDataAttributes["highlighted"] = "data-highlighted";
|
|
2927
|
+
return MenuCheckboxItemDataAttributes;
|
|
2928
|
+
}({});
|
|
3222
2929
|
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
if (
|
|
3226
|
-
return
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
refs.setPositionReference(anchorValue.current);
|
|
3230
|
-
registeredPositionReferenceRef.current = anchorValue.current;
|
|
2930
|
+
const itemMapping = {
|
|
2931
|
+
checked(value) {
|
|
2932
|
+
if (value) {
|
|
2933
|
+
return {
|
|
2934
|
+
[MenuCheckboxItemDataAttributes.checked]: ''
|
|
2935
|
+
};
|
|
3231
2936
|
}
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
2937
|
+
return {
|
|
2938
|
+
[MenuCheckboxItemDataAttributes.unchecked]: ''
|
|
2939
|
+
};
|
|
2940
|
+
},
|
|
2941
|
+
...transitionStatusMapping
|
|
2942
|
+
};
|
|
2943
|
+
|
|
2944
|
+
const MenuGroupContext = /*#__PURE__*/React.createContext(undefined);
|
|
2945
|
+
if (process.env.NODE_ENV !== "production") MenuGroupContext.displayName = "MenuGroupContext";
|
|
2946
|
+
function useMenuGroupRootContext() {
|
|
2947
|
+
const context = React.useContext(MenuGroupContext);
|
|
2948
|
+
if (context === undefined) {
|
|
2949
|
+
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: MenuGroupRootContext is missing. Menu group parts must be used within <Menu.Group>.' : formatErrorMessage(31));
|
|
2950
|
+
}
|
|
2951
|
+
return context;
|
|
2952
|
+
}
|
|
2953
|
+
|
|
2954
|
+
const MenuGroup = /*#__PURE__*/React.forwardRef(function MenuGroup(componentProps, forwardedRef) {
|
|
2955
|
+
const {
|
|
2956
|
+
render,
|
|
2957
|
+
className,
|
|
2958
|
+
...elementProps
|
|
2959
|
+
} = componentProps;
|
|
2960
|
+
const [labelId, setLabelId] = React.useState(undefined);
|
|
2961
|
+
const context = React.useMemo(() => ({
|
|
2962
|
+
setLabelId
|
|
2963
|
+
}), [setLabelId]);
|
|
2964
|
+
const element = useRenderElement('div', componentProps, {
|
|
2965
|
+
ref: forwardedRef,
|
|
2966
|
+
props: {
|
|
2967
|
+
role: 'group',
|
|
2968
|
+
'aria-labelledby': labelId,
|
|
2969
|
+
...elementProps
|
|
3236
2970
|
}
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
2971
|
+
});
|
|
2972
|
+
return /*#__PURE__*/jsx(MenuGroupContext.Provider, {
|
|
2973
|
+
value: context,
|
|
2974
|
+
children: element
|
|
2975
|
+
});
|
|
2976
|
+
});
|
|
2977
|
+
if (process.env.NODE_ENV !== "production") MenuGroup.displayName = "MenuGroup";
|
|
3243
2978
|
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
2979
|
+
/**
|
|
2980
|
+
* An accessible label that is automatically associated with its parent group.
|
|
2981
|
+
* Renders a `<div>` element.
|
|
2982
|
+
*
|
|
2983
|
+
* Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
|
|
2984
|
+
*/
|
|
2985
|
+
const MenuGroupLabel = /*#__PURE__*/React.forwardRef(function MenuGroupLabelComponent(componentProps, forwardedRef) {
|
|
2986
|
+
const {
|
|
2987
|
+
className,
|
|
2988
|
+
render,
|
|
2989
|
+
id: idProp,
|
|
2990
|
+
...elementProps
|
|
2991
|
+
} = componentProps;
|
|
2992
|
+
const id = useBaseUiId(idProp);
|
|
2993
|
+
const {
|
|
2994
|
+
setLabelId
|
|
2995
|
+
} = useMenuGroupRootContext();
|
|
3249
2996
|
useIsoLayoutEffect(() => {
|
|
3250
|
-
|
|
3251
|
-
|
|
2997
|
+
setLabelId(id);
|
|
2998
|
+
return () => {
|
|
2999
|
+
setLabelId(undefined);
|
|
3000
|
+
};
|
|
3001
|
+
}, [setLabelId, id]);
|
|
3002
|
+
return useRenderElement('div', componentProps, {
|
|
3003
|
+
ref: forwardedRef,
|
|
3004
|
+
props: {
|
|
3005
|
+
id,
|
|
3006
|
+
role: 'presentation',
|
|
3007
|
+
...elementProps
|
|
3252
3008
|
}
|
|
3253
|
-
}
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
top: middlewareData.arrow?.y,
|
|
3257
|
-
left: middlewareData.arrow?.x
|
|
3258
|
-
}), [middlewareData.arrow]);
|
|
3259
|
-
const arrowUncentered = middlewareData.arrow?.centerOffset !== 0;
|
|
3260
|
-
return React.useMemo(() => ({
|
|
3261
|
-
positionerStyles: floatingStyles,
|
|
3262
|
-
arrowStyles,
|
|
3263
|
-
arrowRef,
|
|
3264
|
-
arrowUncentered,
|
|
3265
|
-
side: logicalRenderedSide,
|
|
3266
|
-
align: renderedAlign,
|
|
3267
|
-
physicalSide: renderedSide,
|
|
3268
|
-
anchorHidden,
|
|
3269
|
-
refs,
|
|
3270
|
-
context,
|
|
3271
|
-
isPositioned,
|
|
3272
|
-
update
|
|
3273
|
-
}), [floatingStyles, arrowStyles, arrowRef, arrowUncentered, logicalRenderedSide, renderedAlign, renderedSide, anchorHidden, refs, context, isPositioned, update]);
|
|
3274
|
-
}
|
|
3275
|
-
function isRef(param) {
|
|
3276
|
-
return param != null && 'current' in param;
|
|
3277
|
-
}
|
|
3009
|
+
});
|
|
3010
|
+
});
|
|
3011
|
+
if (process.env.NODE_ENV !== "production") MenuGroupLabel.displayName = "MenuGroupLabel";
|
|
3278
3012
|
|
|
3279
|
-
/* eslint-disable no-bitwise */
|
|
3280
3013
|
/**
|
|
3281
|
-
*
|
|
3282
|
-
*
|
|
3014
|
+
* An individual interactive item in the menu.
|
|
3015
|
+
* Renders a `<div>` element.
|
|
3016
|
+
*
|
|
3017
|
+
* Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
|
|
3283
3018
|
*/
|
|
3284
|
-
function
|
|
3019
|
+
const MenuItem = /*#__PURE__*/React.forwardRef(function MenuItem(componentProps, forwardedRef) {
|
|
3285
3020
|
const {
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
// elements and label refs, but there's a lot of async work going on and sometimes
|
|
3298
|
-
// the effect that handles `onMapChange` gets called after those refs have been
|
|
3299
|
-
// filled, and we don't want to lose those values by setting their lengths to `0`.
|
|
3300
|
-
// We also need to have them at the proper length because floating-ui uses that
|
|
3301
|
-
// information for list navigation.
|
|
3302
|
-
|
|
3303
|
-
const map = useRefWithInit(createMap).current;
|
|
3304
|
-
// `mapTick` uses a counter rather than objects for low precision-loss risk and better memory efficiency
|
|
3305
|
-
const [mapTick, setMapTick] = React.useState(0);
|
|
3306
|
-
const lastTickRef = React.useRef(mapTick);
|
|
3307
|
-
const register = useStableCallback((node, metadata) => {
|
|
3308
|
-
map.set(node, metadata ?? null);
|
|
3309
|
-
lastTickRef.current += 1;
|
|
3310
|
-
setMapTick(lastTickRef.current);
|
|
3021
|
+
render,
|
|
3022
|
+
className,
|
|
3023
|
+
id: idProp,
|
|
3024
|
+
label,
|
|
3025
|
+
nativeButton = false,
|
|
3026
|
+
disabled = false,
|
|
3027
|
+
closeOnClick = true,
|
|
3028
|
+
...elementProps
|
|
3029
|
+
} = componentProps;
|
|
3030
|
+
const listItem = useCompositeListItem({
|
|
3031
|
+
label
|
|
3311
3032
|
});
|
|
3312
|
-
const
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3033
|
+
const menuPositionerContext = useMenuPositionerContext(true);
|
|
3034
|
+
const id = useBaseUiId(idProp);
|
|
3035
|
+
const {
|
|
3036
|
+
store
|
|
3037
|
+
} = useMenuRootContext();
|
|
3038
|
+
const highlighted = store.useState('isActive', listItem.index);
|
|
3039
|
+
const itemProps = store.useState('itemProps');
|
|
3040
|
+
const {
|
|
3041
|
+
getItemProps,
|
|
3042
|
+
itemRef
|
|
3043
|
+
} = useMenuItem({
|
|
3044
|
+
closeOnClick,
|
|
3045
|
+
disabled,
|
|
3046
|
+
highlighted,
|
|
3047
|
+
id,
|
|
3048
|
+
store,
|
|
3049
|
+
nativeButton,
|
|
3050
|
+
nodeId: menuPositionerContext?.nodeId,
|
|
3051
|
+
itemMetadata: REGULAR_ITEM
|
|
3316
3052
|
});
|
|
3317
|
-
const
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3053
|
+
const state = {
|
|
3054
|
+
disabled,
|
|
3055
|
+
highlighted
|
|
3056
|
+
};
|
|
3057
|
+
return useRenderElement('div', componentProps, {
|
|
3058
|
+
state,
|
|
3059
|
+
props: [itemProps, elementProps, getItemProps],
|
|
3060
|
+
ref: [itemRef, forwardedRef, listItem.ref]
|
|
3061
|
+
});
|
|
3062
|
+
});
|
|
3063
|
+
if (process.env.NODE_ENV !== "production") MenuItem.displayName = "MenuItem";
|
|
3064
|
+
|
|
3065
|
+
/**
|
|
3066
|
+
* A link in the menu that can be used to navigate to a different page or section.
|
|
3067
|
+
* Renders an `<a>` element.
|
|
3068
|
+
*
|
|
3069
|
+
* Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
|
|
3070
|
+
*/
|
|
3071
|
+
const MenuLinkItem = /*#__PURE__*/React.forwardRef(function MenuLinkItem(componentProps, forwardedRef) {
|
|
3072
|
+
const {
|
|
3073
|
+
render,
|
|
3074
|
+
className,
|
|
3075
|
+
id: idProp,
|
|
3076
|
+
label,
|
|
3077
|
+
closeOnClick = false,
|
|
3078
|
+
...elementProps
|
|
3079
|
+
} = componentProps;
|
|
3080
|
+
const linkRef = React.useRef(null);
|
|
3081
|
+
const listItem = useCompositeListItem({
|
|
3082
|
+
label
|
|
3083
|
+
});
|
|
3084
|
+
const menuPositionerContext = useMenuPositionerContext(true);
|
|
3085
|
+
const nodeId = menuPositionerContext?.nodeId;
|
|
3086
|
+
const id = useBaseUiId(idProp);
|
|
3087
|
+
const {
|
|
3088
|
+
store
|
|
3089
|
+
} = useMenuRootContext();
|
|
3090
|
+
const highlighted = store.useState('isActive', listItem.index);
|
|
3091
|
+
const itemProps = store.useState('itemProps');
|
|
3092
|
+
const typingRef = store.context.typingRef;
|
|
3093
|
+
const {
|
|
3094
|
+
getButtonProps,
|
|
3095
|
+
buttonRef
|
|
3096
|
+
} = useButton({
|
|
3097
|
+
native: false,
|
|
3098
|
+
composite: true
|
|
3099
|
+
});
|
|
3100
|
+
const commonProps = useMenuItemCommonProps({
|
|
3101
|
+
closeOnClick,
|
|
3102
|
+
highlighted,
|
|
3103
|
+
id,
|
|
3104
|
+
nodeId,
|
|
3105
|
+
store,
|
|
3106
|
+
typingRef,
|
|
3107
|
+
itemRef: linkRef
|
|
3108
|
+
});
|
|
3109
|
+
function getItemProps(externalProps) {
|
|
3110
|
+
return mergeProps(commonProps, externalProps, getButtonProps);
|
|
3111
|
+
}
|
|
3112
|
+
const state = React.useMemo(() => ({
|
|
3113
|
+
highlighted
|
|
3114
|
+
}), [highlighted]);
|
|
3115
|
+
return useRenderElement('a', componentProps, {
|
|
3116
|
+
state,
|
|
3117
|
+
props: [itemProps, elementProps, getItemProps],
|
|
3118
|
+
ref: [linkRef, buttonRef, forwardedRef, listItem.ref]
|
|
3119
|
+
});
|
|
3120
|
+
});
|
|
3121
|
+
if (process.env.NODE_ENV !== "production") MenuLinkItem.displayName = "MenuLinkItem";
|
|
3122
|
+
|
|
3123
|
+
const ToolbarRootContext = /*#__PURE__*/React.createContext(undefined);
|
|
3124
|
+
if (process.env.NODE_ENV !== "production") ToolbarRootContext.displayName = "ToolbarRootContext";
|
|
3125
|
+
function useToolbarRootContext(optional) {
|
|
3126
|
+
const context = React.useContext(ToolbarRootContext);
|
|
3127
|
+
if (context === undefined && !optional) {
|
|
3128
|
+
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: ToolbarRootContext is missing. Toolbar parts must be placed within <Toolbar.Root>.' : formatErrorMessage(69));
|
|
3129
|
+
}
|
|
3130
|
+
return context;
|
|
3131
|
+
}
|
|
3132
|
+
|
|
3133
|
+
const stateAttributesMapping = {
|
|
3134
|
+
...popupStateMapping,
|
|
3135
|
+
...transitionStatusMapping
|
|
3136
|
+
};
|
|
3137
|
+
|
|
3138
|
+
/**
|
|
3139
|
+
* A container for the menu items.
|
|
3140
|
+
* Renders a `<div>` element.
|
|
3141
|
+
*
|
|
3142
|
+
* Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
|
|
3143
|
+
*/
|
|
3144
|
+
const MenuPopup = /*#__PURE__*/React.forwardRef(function MenuPopup(componentProps, forwardedRef) {
|
|
3145
|
+
const {
|
|
3146
|
+
render,
|
|
3147
|
+
className,
|
|
3148
|
+
finalFocus,
|
|
3149
|
+
...elementProps
|
|
3150
|
+
} = componentProps;
|
|
3151
|
+
const {
|
|
3152
|
+
store
|
|
3153
|
+
} = useMenuRootContext();
|
|
3154
|
+
const {
|
|
3155
|
+
side,
|
|
3156
|
+
align
|
|
3157
|
+
} = useMenuPositionerContext();
|
|
3158
|
+
const insideToolbar = useToolbarRootContext(true) != null;
|
|
3159
|
+
const open = store.useState('open');
|
|
3160
|
+
const transitionStatus = store.useState('transitionStatus');
|
|
3161
|
+
const popupProps = store.useState('popupProps');
|
|
3162
|
+
const mounted = store.useState('mounted');
|
|
3163
|
+
const instantType = store.useState('instantType');
|
|
3164
|
+
const triggerElement = store.useState('activeTriggerElement');
|
|
3165
|
+
const parent = store.useState('parent');
|
|
3166
|
+
const lastOpenChangeReason = store.useState('lastOpenChangeReason');
|
|
3167
|
+
const rootId = store.useState('rootId');
|
|
3168
|
+
const floatingContext = store.useState('floatingRootContext');
|
|
3169
|
+
const floatingTreeRoot = store.useState('floatingTreeRoot');
|
|
3170
|
+
const closeDelay = store.useState('closeDelay');
|
|
3171
|
+
const activeTriggerElement = store.useState('activeTriggerElement');
|
|
3172
|
+
const isContextMenu = parent.type === 'context-menu';
|
|
3173
|
+
useOpenChangeComplete({
|
|
3174
|
+
open,
|
|
3175
|
+
ref: store.context.popupRef,
|
|
3176
|
+
onComplete() {
|
|
3177
|
+
if (open) {
|
|
3178
|
+
store.context.onOpenChangeComplete?.(true);
|
|
3366
3179
|
}
|
|
3367
|
-
nextIndexRef.current = sortedMap.size;
|
|
3368
3180
|
}
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
}, [elementsRef]);
|
|
3376
|
-
useIsoLayoutEffect(() => {
|
|
3377
|
-
return () => {
|
|
3378
|
-
if (labelsRef) {
|
|
3379
|
-
labelsRef.current = [];
|
|
3380
|
-
}
|
|
3381
|
-
};
|
|
3382
|
-
}, [labelsRef]);
|
|
3383
|
-
const subscribeMapChange = useStableCallback(fn => {
|
|
3384
|
-
listeners.add(fn);
|
|
3181
|
+
});
|
|
3182
|
+
React.useEffect(() => {
|
|
3183
|
+
function handleClose(event) {
|
|
3184
|
+
store.setOpen(false, createChangeEventDetails(event.reason, event.domEvent));
|
|
3185
|
+
}
|
|
3186
|
+
floatingTreeRoot.events.on('close', handleClose);
|
|
3385
3187
|
return () => {
|
|
3386
|
-
|
|
3188
|
+
floatingTreeRoot.events.off('close', handleClose);
|
|
3387
3189
|
};
|
|
3190
|
+
}, [floatingTreeRoot.events, store]);
|
|
3191
|
+
const hoverEnabled = store.useState('hoverEnabled');
|
|
3192
|
+
const disabled = store.useState('disabled');
|
|
3193
|
+
useHoverFloatingInteraction(floatingContext, {
|
|
3194
|
+
enabled: hoverEnabled && !disabled && !isContextMenu && parent.type !== 'menubar',
|
|
3195
|
+
closeDelay
|
|
3388
3196
|
});
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
}
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3197
|
+
const state = {
|
|
3198
|
+
transitionStatus,
|
|
3199
|
+
side,
|
|
3200
|
+
align,
|
|
3201
|
+
open,
|
|
3202
|
+
nested: parent.type === 'menu',
|
|
3203
|
+
instant: instantType
|
|
3204
|
+
};
|
|
3205
|
+
const setPopupElement = React.useCallback(element => {
|
|
3206
|
+
store.set('popupElement', element);
|
|
3207
|
+
}, [store]);
|
|
3208
|
+
const element = useRenderElement('div', componentProps, {
|
|
3209
|
+
state,
|
|
3210
|
+
ref: [forwardedRef, store.context.popupRef, setPopupElement],
|
|
3211
|
+
stateAttributesMapping,
|
|
3212
|
+
props: [popupProps, {
|
|
3213
|
+
onKeyDown(event) {
|
|
3214
|
+
if (insideToolbar && COMPOSITE_KEYS.has(event.key)) {
|
|
3215
|
+
event.stopPropagation();
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
}, getDisabledMountTransitionStyles(transitionStatus), elementProps, {
|
|
3219
|
+
'data-rootownerid': rootId
|
|
3220
|
+
}]
|
|
3403
3221
|
});
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
}
|
|
3408
|
-
function createListeners() {
|
|
3409
|
-
return new Set();
|
|
3410
|
-
}
|
|
3411
|
-
function sortByDocumentPosition(a, b) {
|
|
3412
|
-
const position = a.compareDocumentPosition(b);
|
|
3413
|
-
if (position & Node.DOCUMENT_POSITION_FOLLOWING || position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
|
|
3414
|
-
return -1;
|
|
3222
|
+
let returnFocus = parent.type === undefined || isContextMenu;
|
|
3223
|
+
if (triggerElement || parent.type === 'menubar' && lastOpenChangeReason !== outsidePress) {
|
|
3224
|
+
returnFocus = true;
|
|
3415
3225
|
}
|
|
3416
|
-
|
|
3417
|
-
|
|
3226
|
+
return /*#__PURE__*/jsx(FloatingFocusManager, {
|
|
3227
|
+
context: floatingContext,
|
|
3228
|
+
modal: isContextMenu,
|
|
3229
|
+
disabled: !mounted,
|
|
3230
|
+
returnFocus: finalFocus === undefined ? returnFocus : finalFocus,
|
|
3231
|
+
initialFocus: parent.type !== 'menu',
|
|
3232
|
+
restoreFocus: true,
|
|
3233
|
+
externalTree: parent.type !== 'menubar' ? floatingTreeRoot : undefined,
|
|
3234
|
+
previousFocusableElement: activeTriggerElement,
|
|
3235
|
+
nextFocusableElement: parent.type === undefined ? store.context.triggerFocusTargetRef : undefined,
|
|
3236
|
+
beforeContentFocusGuardRef: parent.type === undefined ? store.context.beforeContentFocusGuardRef : undefined,
|
|
3237
|
+
children: element
|
|
3238
|
+
});
|
|
3239
|
+
});
|
|
3240
|
+
if (process.env.NODE_ENV !== "production") MenuPopup.displayName = "MenuPopup";
|
|
3241
|
+
|
|
3242
|
+
const MenuPortalContext = /*#__PURE__*/React.createContext(undefined);
|
|
3243
|
+
if (process.env.NODE_ENV !== "production") MenuPortalContext.displayName = "MenuPortalContext";
|
|
3244
|
+
function useMenuPortalContext() {
|
|
3245
|
+
const value = React.useContext(MenuPortalContext);
|
|
3246
|
+
if (value === undefined) {
|
|
3247
|
+
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: <Menu.Portal> is missing.' : formatErrorMessage(32));
|
|
3418
3248
|
}
|
|
3419
|
-
return
|
|
3249
|
+
return value;
|
|
3420
3250
|
}
|
|
3421
3251
|
|
|
3252
|
+
const MenuPortal = /*#__PURE__*/React.forwardRef(function MenuPortal(props, forwardedRef) {
|
|
3253
|
+
const {
|
|
3254
|
+
keepMounted = false,
|
|
3255
|
+
...portalProps
|
|
3256
|
+
} = props;
|
|
3257
|
+
const {
|
|
3258
|
+
store
|
|
3259
|
+
} = useMenuRootContext();
|
|
3260
|
+
const mounted = store.useState('mounted');
|
|
3261
|
+
const shouldRender = mounted || keepMounted;
|
|
3262
|
+
if (!shouldRender) {
|
|
3263
|
+
return null;
|
|
3264
|
+
}
|
|
3265
|
+
return /*#__PURE__*/jsx(MenuPortalContext.Provider, {
|
|
3266
|
+
value: keepMounted,
|
|
3267
|
+
children: /*#__PURE__*/jsx(FloatingPortal, {
|
|
3268
|
+
ref: forwardedRef,
|
|
3269
|
+
...portalProps
|
|
3270
|
+
})
|
|
3271
|
+
});
|
|
3272
|
+
});
|
|
3273
|
+
if (process.env.NODE_ENV !== "production") MenuPortal.displayName = "MenuPortal";
|
|
3274
|
+
|
|
3422
3275
|
const MenuPositioner = /*#__PURE__*/React.forwardRef(function MenuPositioner(componentProps, forwardedRef) {
|
|
3423
3276
|
const {
|
|
3424
3277
|
anchor: anchorProp,
|
|
@@ -4429,41 +4282,6 @@ function MenuSubmenuRoot(props) {
|
|
|
4429
4282
|
});
|
|
4430
4283
|
}
|
|
4431
4284
|
|
|
4432
|
-
function getPseudoElementBounds(element) {
|
|
4433
|
-
const elementRect = element.getBoundingClientRect();
|
|
4434
|
-
|
|
4435
|
-
// Avoid "Not implemented: window.getComputedStyle(elt, pseudoElt)"
|
|
4436
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
4437
|
-
return elementRect;
|
|
4438
|
-
}
|
|
4439
|
-
const beforeStyles = window.getComputedStyle(element, '::before');
|
|
4440
|
-
const afterStyles = window.getComputedStyle(element, '::after');
|
|
4441
|
-
const hasPseudoElements = beforeStyles.content !== 'none' || afterStyles.content !== 'none';
|
|
4442
|
-
if (!hasPseudoElements) {
|
|
4443
|
-
return elementRect;
|
|
4444
|
-
}
|
|
4445
|
-
|
|
4446
|
-
// Get dimensions of pseudo-elements
|
|
4447
|
-
const beforeWidth = parseFloat(beforeStyles.width) || 0;
|
|
4448
|
-
const beforeHeight = parseFloat(beforeStyles.height) || 0;
|
|
4449
|
-
const afterWidth = parseFloat(afterStyles.width) || 0;
|
|
4450
|
-
const afterHeight = parseFloat(afterStyles.height) || 0;
|
|
4451
|
-
|
|
4452
|
-
// Calculate max dimensions including pseudo-elements
|
|
4453
|
-
const totalWidth = Math.max(elementRect.width, beforeWidth, afterWidth);
|
|
4454
|
-
const totalHeight = Math.max(elementRect.height, beforeHeight, afterHeight);
|
|
4455
|
-
|
|
4456
|
-
// Calculate the differences to extend the bounds
|
|
4457
|
-
const widthDiff = totalWidth - elementRect.width;
|
|
4458
|
-
const heightDiff = totalHeight - elementRect.height;
|
|
4459
|
-
return {
|
|
4460
|
-
left: elementRect.left - widthDiff / 2,
|
|
4461
|
-
right: elementRect.right + widthDiff / 2,
|
|
4462
|
-
top: elementRect.top - heightDiff / 2,
|
|
4463
|
-
bottom: elementRect.bottom + heightDiff / 2
|
|
4464
|
-
};
|
|
4465
|
-
}
|
|
4466
|
-
|
|
4467
4285
|
function useCompositeItem(params = {}) {
|
|
4468
4286
|
const {
|
|
4469
4287
|
highlightItemOnHover,
|
|
@@ -4979,4 +4797,4 @@ const MenuSubmenuTrigger = /*#__PURE__*/React.forwardRef(function SubmenuTrigger
|
|
|
4979
4797
|
});
|
|
4980
4798
|
if (process.env.NODE_ENV !== "production") MenuSubmenuTrigger.displayName = "MenuSubmenuTrigger";
|
|
4981
4799
|
|
|
4982
|
-
export {
|
|
4800
|
+
export { MenuStore as A, CompositeList as C, DrawerTitle as D, IndexGuessBehavior as I, MenuTrigger as M, REGULAR_ITEM as R, DrawerClose as a, MenuLinkItem as b, MenuItem as c, MenuRadioItem as d, MenuRadioItemIndicator as e, MenuSubmenuTrigger as f, MenuRoot as g, MenuPortal as h, MenuPositioner as i, MenuPopup as j, MenuRadioGroup as k, MenuGroup as l, MenuSubmenuRoot as m, MenuGroupLabel as n, useListNavigation as o, useTypeahead as p, getPseudoElementBounds as q, useAnchorPositioning as r, getDisabledMountTransitionStyles as s, useCompositeListItem as t, useFloatingRootContext as u, useMenuRootContext as v, useMenuPositionerContext as w, useContextMenuRootContext as x, useMenuItem as y, itemMapping as z };
|