@jobber/components 8.23.0 → 8.23.1
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/index.cjs +1 -1
- package/dist/Autocomplete/index.mjs +1 -1
- package/dist/Card/index.cjs +4 -4
- package/dist/Card/index.mjs +4 -4
- package/dist/Chip/index.cjs +1 -1
- package/dist/Chip/index.mjs +1 -1
- package/dist/Chips/InternalChipDismissible/hooks/index.cjs +1 -1
- package/dist/Chips/InternalChipDismissible/hooks/index.mjs +1 -1
- package/dist/Chips/InternalChipDismissible/index.cjs +1 -1
- package/dist/Chips/InternalChipDismissible/index.mjs +1 -1
- package/dist/Chips/index.cjs +1 -1
- package/dist/Chips/index.mjs +1 -1
- package/dist/ComboboxChipRemove-cjs.js +668 -379
- package/dist/ComboboxChipRemove-es.js +508 -219
- package/dist/ConfirmationModal/index.cjs +1 -1
- package/dist/ConfirmationModal/index.mjs +1 -1
- package/dist/DataDump/index.cjs +4 -4
- package/dist/DataDump/index.mjs +4 -4
- package/dist/DataList/components/DataListActions/index.cjs +1 -1
- package/dist/DataList/components/DataListActions/index.mjs +1 -1
- package/dist/DataList/components/DataListBulkActions/index.cjs +1 -1
- package/dist/DataList/components/DataListBulkActions/index.mjs +1 -1
- package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +1 -1
- package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +1 -1
- package/dist/DataList/components/DataListFilters/index.cjs +1 -1
- package/dist/DataList/components/DataListFilters/index.mjs +1 -1
- package/dist/DataList/components/DataListHeader/index.cjs +1 -1
- package/dist/DataList/components/DataListHeader/index.mjs +1 -1
- package/dist/DataList/components/DataListItem/index.cjs +1 -1
- package/dist/DataList/components/DataListItem/index.mjs +1 -1
- package/dist/DataList/components/DataListItemActions/index.cjs +1 -1
- package/dist/DataList/components/DataListItemActions/index.mjs +1 -1
- package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +1 -1
- package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +1 -1
- package/dist/DataList/components/DataListItems/index.cjs +1 -1
- package/dist/DataList/components/DataListItems/index.mjs +1 -1
- package/dist/DataList/components/DataListLayout/index.cjs +1 -1
- package/dist/DataList/components/DataListLayout/index.mjs +1 -1
- package/dist/DataList/components/DataListLayoutActions/index.cjs +1 -1
- package/dist/DataList/components/DataListLayoutActions/index.mjs +1 -1
- package/dist/DataList/index.cjs +1 -1
- package/dist/DataList/index.mjs +1 -1
- package/dist/DatePicker/index.cjs +1 -1
- package/dist/DatePicker/index.mjs +1 -1
- package/dist/Dialog/index.cjs +3 -3
- package/dist/Dialog/index.mjs +3 -3
- package/dist/DrawerDescription-cjs.js +32 -33
- package/dist/DrawerDescription-es.js +19 -20
- package/dist/DrawerTitle-cjs.js +7 -5
- package/dist/DrawerTitle-es.js +7 -5
- package/dist/FilterPicker/components/FilterPickerActivator/index.cjs +1 -1
- package/dist/FilterPicker/components/FilterPickerActivator/index.mjs +1 -1
- package/dist/FilterPicker/components/FilterPickerContent/index.cjs +1 -1
- package/dist/FilterPicker/components/FilterPickerContent/index.mjs +1 -1
- package/dist/FilterPicker/components/FilterPickerTrigger/index.cjs +1 -1
- package/dist/FilterPicker/components/FilterPickerTrigger/index.mjs +1 -1
- package/dist/FilterPicker/index.cjs +1 -1
- package/dist/FilterPicker/index.mjs +1 -1
- package/dist/FormFieldWrapper-cjs.js +2 -2
- package/dist/FormFieldWrapper-es.js +2 -2
- package/dist/FormatFile/index.cjs +1 -1
- package/dist/FormatFile/index.mjs +1 -1
- package/dist/Gallery/index.cjs +1 -1
- package/dist/Gallery/index.mjs +1 -1
- package/dist/InputDate/index.cjs +1 -1
- package/dist/InputDate/index.mjs +1 -1
- package/dist/InputNumberExperimental-cjs.js +319 -102
- package/dist/InputNumberExperimental-es.js +321 -104
- package/dist/LightBox/index.cjs +1 -1
- package/dist/LightBox/index.mjs +1 -1
- package/dist/Menu/index.cjs +4 -4
- package/dist/Menu/index.mjs +4 -4
- package/dist/MenuSubmenuTrigger-cjs.js +545 -498
- package/dist/MenuSubmenuTrigger-es.js +490 -443
- package/dist/Modal/index.cjs +1 -1
- package/dist/Modal/index.mjs +1 -1
- package/dist/NumberFieldInput-cjs.js +609 -496
- package/dist/NumberFieldInput-es.js +612 -498
- package/dist/Page/index.cjs +4 -4
- package/dist/Page/index.mjs +4 -4
- package/dist/Popover/index.cjs +1 -1
- package/dist/Popover/index.mjs +1 -1
- package/dist/ScrollAreaViewport-cjs.js +326 -601
- package/dist/ScrollAreaViewport-es.js +278 -548
- package/dist/SelectGroupLabel-cjs.js +494 -528
- package/dist/SelectGroupLabel-es.js +411 -445
- package/dist/Separator-cjs.js +1 -0
- package/dist/Separator-es.js +1 -0
- package/dist/Tooltip/index.cjs +1 -1
- package/dist/Tooltip/index.mjs +1 -1
- package/dist/docs/InputTime/InputTime.md +8 -0
- package/dist/docs/SelectPrimitive/SelectPrimitive.md +38 -25
- package/dist/floating-ui.react-cjs.js +586 -10
- package/dist/floating-ui.react-dom-cjs.js +92 -92
- package/dist/floating-ui.react-dom-es.js +1 -1
- package/dist/floating-ui.react-es.js +580 -4
- package/dist/floating-ui.utils-cjs.js +159 -0
- package/dist/floating-ui.utils-es.js +136 -0
- package/dist/index.cjs +4 -4
- package/dist/index.mjs +4 -4
- package/dist/{ToolbarRootContext-cjs.js → isElementDisabled-cjs.js} +5 -0
- package/dist/{ToolbarRootContext-es.js → isElementDisabled-es.js} +5 -1
- package/dist/primitives/BottomSheet/index.cjs +3 -3
- package/dist/primitives/BottomSheet/index.mjs +3 -3
- package/dist/primitives/ComboboxPrimitive/index.cjs +2 -2
- package/dist/primitives/ComboboxPrimitive/index.mjs +2 -2
- package/dist/primitives/InputNumberExperimental/index.cjs +1 -1
- package/dist/primitives/InputNumberExperimental/index.mjs +1 -1
- package/dist/primitives/SelectPrimitive/index.cjs +5 -5
- package/dist/primitives/SelectPrimitive/index.mjs +5 -5
- package/dist/primitives/index.cjs +4 -4
- package/dist/primitives/index.mjs +4 -4
- package/dist/resolveAriaLabelledBy-cjs.js +5 -0
- package/dist/resolveAriaLabelledBy-es.js +5 -1
- package/dist/{CSPContext-cjs.js → scrollEdges-cjs.js} +30 -2
- package/dist/{CSPContext-es.js → scrollEdges-es.js} +28 -3
- package/dist/styles.css +9 -3
- package/dist/unstyledPrimitives/index.cjs +1217 -639
- package/dist/unstyledPrimitives/index.mjs +1163 -585
- package/dist/useButton-cjs.js +283 -317
- package/dist/useButton-es.js +277 -298
- package/dist/useCompositeListItem-cjs.js +328 -279
- package/dist/useCompositeListItem-es.js +293 -246
- package/dist/useLabel-cjs.js +11 -1
- package/dist/useLabel-es.js +11 -2
- package/dist/useLabelableId-cjs.js +37 -91
- package/dist/useLabelableId-es.js +37 -90
- package/dist/useRenderElement-cjs.js +81 -26
- package/dist/useRenderElement-es.js +81 -27
- package/dist/{InternalBackdrop-cjs.js → useScrollLock-cjs.js} +1286 -1220
- package/dist/{InternalBackdrop-es.js → useScrollLock-es.js} +1250 -1207
- package/package.json +3 -3
- package/dist/index.esm-cjs.js +0 -738
- package/dist/index.esm-es.js +0 -712
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
var React = require('react');
|
|
4
4
|
var useRenderElement = require('./useRenderElement-cjs.js');
|
|
5
|
-
var
|
|
5
|
+
var useScrollLock = require('./useScrollLock-cjs.js');
|
|
6
6
|
var useButton = require('./useButton-cjs.js');
|
|
7
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
8
8
|
var useCompositeListItem = require('./useCompositeListItem-cjs.js');
|
|
9
|
-
var
|
|
10
|
-
var
|
|
9
|
+
var isElementDisabled = require('./isElementDisabled-cjs.js');
|
|
10
|
+
var scrollEdges = require('./scrollEdges-cjs.js');
|
|
11
11
|
var floatingUi_utils_dom = require('./floating-ui.utils.dom-cjs.js');
|
|
12
12
|
var ScrollAreaViewport = require('./ScrollAreaViewport-cjs.js');
|
|
13
13
|
var ReactDOM = require('react-dom');
|
|
@@ -57,28 +57,31 @@ function getRestMs(value) {
|
|
|
57
57
|
function isClickLikeOpenEvent(openEventType, interactedInside) {
|
|
58
58
|
return interactedInside || openEventType === 'click' || openEventType === 'mousedown';
|
|
59
59
|
}
|
|
60
|
+
function isHoverOpenEvent(openEventType) {
|
|
61
|
+
return openEventType?.includes('mouse') && openEventType !== 'mousedown';
|
|
62
|
+
}
|
|
60
63
|
|
|
61
|
-
const isMacSafari = useButton.
|
|
64
|
+
const isMacSafari = useButton.mac && useScrollLock.webkit;
|
|
62
65
|
/**
|
|
63
66
|
* Opens the floating element while the reference element has focus, like CSS
|
|
64
67
|
* `:focus`.
|
|
65
68
|
* @see https://floating-ui.com/docs/useFocus
|
|
66
69
|
*/
|
|
67
70
|
function useFocus(context, props = {}) {
|
|
71
|
+
const {
|
|
72
|
+
enabled = true,
|
|
73
|
+
delay
|
|
74
|
+
} = props;
|
|
68
75
|
const store = 'rootStore' in context ? context.rootStore : context;
|
|
69
76
|
const {
|
|
70
77
|
events,
|
|
71
78
|
dataRef
|
|
72
79
|
} = store.context;
|
|
73
|
-
const {
|
|
74
|
-
enabled = true,
|
|
75
|
-
delay
|
|
76
|
-
} = props;
|
|
77
80
|
const blockFocusRef = React__namespace.useRef(false);
|
|
78
81
|
// Track which reference should be blocked from re-opening after Escape/press dismissal.
|
|
79
82
|
const blockedReferenceRef = React__namespace.useRef(null);
|
|
80
|
-
const timeout = useButton.useTimeout();
|
|
81
83
|
const keyboardModalityRef = React__namespace.useRef(true);
|
|
84
|
+
const timeout = useButton.useTimeout();
|
|
82
85
|
React__namespace.useEffect(() => {
|
|
83
86
|
const domReference = store.select('domReferenceElement');
|
|
84
87
|
if (!enabled) {
|
|
@@ -101,18 +104,7 @@ function useFocus(context, props = {}) {
|
|
|
101
104
|
function onPointerDown() {
|
|
102
105
|
keyboardModalityRef.current = false;
|
|
103
106
|
}
|
|
104
|
-
|
|
105
|
-
if (isMacSafari) {
|
|
106
|
-
win.addEventListener('keydown', onKeyDown, true);
|
|
107
|
-
win.addEventListener('pointerdown', onPointerDown, true);
|
|
108
|
-
}
|
|
109
|
-
return () => {
|
|
110
|
-
win.removeEventListener('blur', onBlur);
|
|
111
|
-
if (isMacSafari) {
|
|
112
|
-
win.removeEventListener('keydown', onKeyDown, true);
|
|
113
|
-
win.removeEventListener('pointerdown', onPointerDown, true);
|
|
114
|
-
}
|
|
115
|
-
};
|
|
107
|
+
return useScrollLock.mergeCleanups(useButton.addEventListener(win, 'blur', onBlur), isMacSafari && useButton.addEventListener(win, 'keydown', onKeyDown, true), isMacSafari && useButton.addEventListener(win, 'pointerdown', onPointerDown, true));
|
|
116
108
|
}, [store, enabled]);
|
|
117
109
|
React__namespace.useEffect(() => {
|
|
118
110
|
if (!enabled) {
|
|
@@ -132,101 +124,100 @@ function useFocus(context, props = {}) {
|
|
|
132
124
|
events.off('openchange', onOpenChangeLocal);
|
|
133
125
|
};
|
|
134
126
|
}, [events, enabled, store]);
|
|
135
|
-
const reference = React__namespace.useMemo(() =>
|
|
136
|
-
|
|
127
|
+
const reference = React__namespace.useMemo(() => {
|
|
128
|
+
function resetBlockedFocus() {
|
|
137
129
|
blockFocusRef.current = false;
|
|
138
130
|
blockedReferenceRef.current = null;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
blockFocusRef.current
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
const target = useButton.getTarget(event.nativeEvent);
|
|
150
|
-
if (floatingUi_utils_dom.isElement(target)) {
|
|
151
|
-
// Safari fails to match `:focus-visible` if focus was initially
|
|
152
|
-
// outside the document.
|
|
153
|
-
if (isMacSafari && !event.relatedTarget) {
|
|
154
|
-
if (!keyboardModalityRef.current && !useButton.isTypeableElement(target)) {
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
onMouseLeave() {
|
|
134
|
+
resetBlockedFocus();
|
|
135
|
+
},
|
|
136
|
+
onFocus(event) {
|
|
137
|
+
const focusTarget = event.currentTarget;
|
|
138
|
+
if (blockFocusRef.current) {
|
|
139
|
+
if (blockedReferenceRef.current === focusTarget) {
|
|
155
140
|
return;
|
|
156
141
|
}
|
|
157
|
-
|
|
158
|
-
return;
|
|
142
|
+
resetBlockedFocus();
|
|
159
143
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
timeout.start(delayValue, () => {
|
|
172
|
-
if (blockFocusRef.current) {
|
|
173
|
-
return;
|
|
144
|
+
const target = useButton.getTarget(event.nativeEvent);
|
|
145
|
+
if (floatingUi_utils_dom.isElement(target)) {
|
|
146
|
+
// Safari fails to match `:focus-visible` if focus was initially
|
|
147
|
+
// outside the document.
|
|
148
|
+
if (isMacSafari && !event.relatedTarget) {
|
|
149
|
+
if (!keyboardModalityRef.current && !useScrollLock.isTypeableElement(target)) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
} else if (!useScrollLock.matchesFocusVisible(target)) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
174
155
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
// Hit the non-modal focus management portal guard. Focus will be
|
|
185
|
-
// moved into the floating element immediately after.
|
|
186
|
-
const movedToFocusGuard = floatingUi_utils_dom.isElement(relatedTarget) && relatedTarget.hasAttribute(InternalBackdrop.createAttribute('focus-guard')) && relatedTarget.getAttribute('data-type') === 'outside';
|
|
187
|
-
|
|
188
|
-
// Wait for the window blur listener to fire.
|
|
189
|
-
timeout.start(0, () => {
|
|
190
|
-
const domReference = store.select('domReferenceElement');
|
|
191
|
-
const activeEl = useButton.activeElement(domReference ? domReference.ownerDocument : document);
|
|
192
|
-
|
|
193
|
-
// Focus left the page, keep it open.
|
|
194
|
-
if (!relatedTarget && activeEl === domReference) {
|
|
156
|
+
const movedFromOtherEnabledTrigger = useScrollLock.isTargetInsideEnabledTrigger(event.relatedTarget, store.context.triggerElements);
|
|
157
|
+
const {
|
|
158
|
+
nativeEvent,
|
|
159
|
+
currentTarget
|
|
160
|
+
} = event;
|
|
161
|
+
const delayValue = typeof delay === 'function' ? delay() : delay;
|
|
162
|
+
if (store.select('open') && movedFromOtherEnabledTrigger || delayValue === 0 || delayValue === undefined) {
|
|
163
|
+
store.setOpen(true, useButton.createChangeEventDetails(useButton.triggerFocus, nativeEvent, currentTarget));
|
|
195
164
|
return;
|
|
196
165
|
}
|
|
166
|
+
timeout.start(delayValue, () => {
|
|
167
|
+
if (blockFocusRef.current) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
store.setOpen(true, useButton.createChangeEventDetails(useButton.triggerFocus, nativeEvent, currentTarget));
|
|
171
|
+
});
|
|
172
|
+
},
|
|
173
|
+
onBlur(event) {
|
|
174
|
+
resetBlockedFocus();
|
|
175
|
+
const relatedTarget = event.relatedTarget;
|
|
176
|
+
const nativeEvent = event.nativeEvent;
|
|
177
|
+
|
|
178
|
+
// Hit the non-modal focus management portal guard. Focus will be
|
|
179
|
+
// moved into the floating element immediately after.
|
|
180
|
+
const movedToFocusGuard = floatingUi_utils_dom.isElement(relatedTarget) && relatedTarget.hasAttribute(useScrollLock.createAttribute('focus-guard')) && relatedTarget.getAttribute('data-type') === 'outside';
|
|
181
|
+
|
|
182
|
+
// Wait for the window blur listener to fire.
|
|
183
|
+
timeout.start(0, () => {
|
|
184
|
+
const domReference = store.select('domReferenceElement');
|
|
185
|
+
const activeEl = useButton.activeElement(useButton.ownerDocument(domReference));
|
|
186
|
+
|
|
187
|
+
// Focus left the page, keep it open.
|
|
188
|
+
if (!relatedTarget && activeEl === domReference) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
197
191
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
192
|
+
// When focusing the reference element (e.g. regular click), then
|
|
193
|
+
// clicking into the floating element, prevent it from hiding.
|
|
194
|
+
// Note: it must be focusable, e.g. `tabindex="-1"`.
|
|
195
|
+
// We can not rely on relatedTarget to point to the correct element
|
|
196
|
+
// as it will only point to the shadow host of the newly focused element
|
|
197
|
+
// and not the element that actually has received focus if it is located
|
|
198
|
+
// inside a shadow root.
|
|
199
|
+
if (useButton.contains(dataRef.current.floatingContext?.refs.floating.current, activeEl) || useButton.contains(domReference, activeEl) || movedToFocusGuard) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
208
202
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
203
|
+
// If the next focused element is one of the triggers, do not close
|
|
204
|
+
// the floating element. The focus handler of that trigger will
|
|
205
|
+
// handle the open state.
|
|
206
|
+
const nextFocusedElement = relatedTarget ?? activeEl;
|
|
207
|
+
if (useScrollLock.isTargetInsideEnabledTrigger(nextFocusedElement, store.context.triggerElements)) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
store.setOpen(false, useButton.createChangeEventDetails(useButton.triggerFocus, nativeEvent));
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}, [dataRef, delay, store, timeout]);
|
|
220
215
|
return React__namespace.useMemo(() => enabled ? {
|
|
221
216
|
reference,
|
|
222
217
|
trigger: reference
|
|
223
218
|
} : {}, [enabled, reference]);
|
|
224
219
|
}
|
|
225
220
|
|
|
226
|
-
const interactiveSelector = `button,a,[role="button"],select,[tabindex]:not([tabindex="-1"]),${useButton.TYPEABLE_SELECTOR}`;
|
|
227
|
-
function isInteractiveElement(element) {
|
|
228
|
-
return element ? Boolean(element.closest(interactiveSelector)) : false;
|
|
229
|
-
}
|
|
230
221
|
class HoverInteraction {
|
|
231
222
|
constructor() {
|
|
232
223
|
this.pointerType = undefined;
|
|
@@ -253,13 +244,18 @@ class HoverInteraction {
|
|
|
253
244
|
return this.dispose;
|
|
254
245
|
};
|
|
255
246
|
}
|
|
247
|
+
const pointerEventsMutationOwnerByScopeElement = new WeakMap();
|
|
256
248
|
function clearSafePolygonPointerEventsMutation(instance) {
|
|
257
249
|
if (!instance.performedPointerEventsMutation) {
|
|
258
250
|
return;
|
|
259
251
|
}
|
|
260
|
-
instance.pointerEventsScopeElement
|
|
261
|
-
|
|
262
|
-
|
|
252
|
+
const scopeElement = instance.pointerEventsScopeElement;
|
|
253
|
+
if (scopeElement && pointerEventsMutationOwnerByScopeElement.get(scopeElement) === instance) {
|
|
254
|
+
instance.pointerEventsScopeElement?.style.removeProperty('pointer-events');
|
|
255
|
+
instance.pointerEventsReferenceElement?.style.removeProperty('pointer-events');
|
|
256
|
+
instance.pointerEventsFloatingElement?.style.removeProperty('pointer-events');
|
|
257
|
+
pointerEventsMutationOwnerByScopeElement.delete(scopeElement);
|
|
258
|
+
}
|
|
263
259
|
instance.performedPointerEventsMutation = false;
|
|
264
260
|
instance.pointerEventsScopeElement = null;
|
|
265
261
|
instance.pointerEventsReferenceElement = null;
|
|
@@ -271,18 +267,23 @@ function applySafePolygonPointerEventsMutation(instance, options) {
|
|
|
271
267
|
referenceElement,
|
|
272
268
|
floatingElement
|
|
273
269
|
} = options;
|
|
270
|
+
const existingOwner = pointerEventsMutationOwnerByScopeElement.get(scopeElement);
|
|
271
|
+
if (existingOwner && existingOwner !== instance) {
|
|
272
|
+
clearSafePolygonPointerEventsMutation(existingOwner);
|
|
273
|
+
}
|
|
274
274
|
clearSafePolygonPointerEventsMutation(instance);
|
|
275
275
|
instance.performedPointerEventsMutation = true;
|
|
276
276
|
instance.pointerEventsScopeElement = scopeElement;
|
|
277
277
|
instance.pointerEventsReferenceElement = referenceElement;
|
|
278
278
|
instance.pointerEventsFloatingElement = floatingElement;
|
|
279
|
+
pointerEventsMutationOwnerByScopeElement.set(scopeElement, instance);
|
|
279
280
|
scopeElement.style.pointerEvents = 'none';
|
|
280
281
|
referenceElement.style.pointerEvents = 'auto';
|
|
281
282
|
floatingElement.style.pointerEvents = 'auto';
|
|
282
283
|
}
|
|
283
284
|
function useHoverInteractionSharedState(store) {
|
|
284
|
-
const instance = useRenderElement.useRefWithInit(HoverInteraction.create).current;
|
|
285
285
|
const data = store.context.dataRef.current;
|
|
286
|
+
const instance = useRenderElement.useRefWithInit(() => data.hoverInteractionState ?? HoverInteraction.create()).current;
|
|
286
287
|
if (!data.hoverInteractionState) {
|
|
287
288
|
data.hoverInteractionState = instance;
|
|
288
289
|
}
|
|
@@ -294,6 +295,11 @@ function useHoverInteractionSharedState(store) {
|
|
|
294
295
|
* Provides hover interactions that should be attached to the floating element.
|
|
295
296
|
*/
|
|
296
297
|
function useHoverFloatingInteraction(context, parameters = {}) {
|
|
298
|
+
const {
|
|
299
|
+
enabled = true,
|
|
300
|
+
closeDelay: closeDelayProp = 0,
|
|
301
|
+
nodeId: nodeIdProp
|
|
302
|
+
} = parameters;
|
|
297
303
|
const store = 'rootStore' in context ? context.rootStore : context;
|
|
298
304
|
const open = store.useState('open');
|
|
299
305
|
const floatingElement = store.useState('floatingElement');
|
|
@@ -301,47 +307,19 @@ function useHoverFloatingInteraction(context, parameters = {}) {
|
|
|
301
307
|
const {
|
|
302
308
|
dataRef
|
|
303
309
|
} = store.context;
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
closeDelay: closeDelayProp = 0
|
|
307
|
-
} = parameters;
|
|
310
|
+
const tree = useScrollLock.useFloatingTree();
|
|
311
|
+
const parentId = useScrollLock.useFloatingParentNodeId();
|
|
308
312
|
const instance = useHoverInteractionSharedState(store);
|
|
309
|
-
const
|
|
310
|
-
const parentId = InternalBackdrop.useFloatingParentNodeId();
|
|
313
|
+
const childClosedTimeout = useButton.useTimeout();
|
|
311
314
|
const isClickLikeOpenEvent$1 = useButton.useStableCallback(() => {
|
|
312
315
|
return isClickLikeOpenEvent(dataRef.current.openEvent?.type, instance.interactedInside);
|
|
313
316
|
});
|
|
314
317
|
const isHoverOpen = useButton.useStableCallback(() => {
|
|
315
|
-
|
|
316
|
-
return type?.includes('mouse') && type !== 'mousedown';
|
|
317
|
-
});
|
|
318
|
-
const isRelatedTargetInsideEnabledTrigger = useButton.useStableCallback(target => {
|
|
319
|
-
return useButton.isTargetInsideEnabledTrigger(target, store.context.triggerElements);
|
|
318
|
+
return isHoverOpenEvent(dataRef.current.openEvent?.type);
|
|
320
319
|
});
|
|
321
|
-
const closeWithDelay = React__namespace.useCallback(event => {
|
|
322
|
-
const closeDelay = getDelay(closeDelayProp, 'close', instance.pointerType);
|
|
323
|
-
const close = () => {
|
|
324
|
-
store.setOpen(false, useButton.createChangeEventDetails(useButton.triggerHover, event));
|
|
325
|
-
tree?.events.emit('floating.closed', event);
|
|
326
|
-
};
|
|
327
|
-
if (closeDelay) {
|
|
328
|
-
instance.openChangeTimeout.start(closeDelay, close);
|
|
329
|
-
} else {
|
|
330
|
-
instance.openChangeTimeout.clear();
|
|
331
|
-
close();
|
|
332
|
-
}
|
|
333
|
-
}, [closeDelayProp, store, instance, tree]);
|
|
334
320
|
const clearPointerEvents = useButton.useStableCallback(() => {
|
|
335
321
|
clearSafePolygonPointerEventsMutation(instance);
|
|
336
322
|
});
|
|
337
|
-
const handleInteractInside = useButton.useStableCallback(event => {
|
|
338
|
-
const target = useButton.getTarget(event);
|
|
339
|
-
if (!isInteractiveElement(target)) {
|
|
340
|
-
instance.interactedInside = false;
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
instance.interactedInside = target?.closest('[aria-haspopup]') != null;
|
|
344
|
-
});
|
|
345
323
|
useButton.useIsoLayoutEffect(() => {
|
|
346
324
|
if (!open) {
|
|
347
325
|
instance.pointerType = undefined;
|
|
@@ -365,7 +343,13 @@ function useHoverFloatingInteraction(context, parameters = {}) {
|
|
|
365
343
|
if (parentFloating) {
|
|
366
344
|
parentFloating.style.pointerEvents = '';
|
|
367
345
|
}
|
|
368
|
-
|
|
346
|
+
|
|
347
|
+
// A keep-mounted submenu can appear in the tree before it opens, so a
|
|
348
|
+
// cached scope or parent lookup may resolve to the submenu itself. That
|
|
349
|
+
// would not shield sibling items in the parent menu.
|
|
350
|
+
const cachedScopeElement = instance.pointerEventsScopeElement !== floatingEl ? instance.pointerEventsScopeElement : null;
|
|
351
|
+
const parentScopeElement = parentFloating !== floatingEl ? parentFloating : null;
|
|
352
|
+
const scopeElement = instance.handleCloseOptions?.getScope?.() ?? cachedScopeElement ?? parentScopeElement ?? ref.closest('[data-rootownerid]') ?? doc.body;
|
|
369
353
|
applySafePolygonPointerEventsMutation(instance, {
|
|
370
354
|
scopeElement,
|
|
371
355
|
referenceElement: ref,
|
|
@@ -377,11 +361,34 @@ function useHoverFloatingInteraction(context, parameters = {}) {
|
|
|
377
361
|
}
|
|
378
362
|
return undefined;
|
|
379
363
|
}, [enabled, open, domReferenceElement, floatingElement, instance, isHoverOpen, tree, parentId, clearPointerEvents]);
|
|
380
|
-
const childClosedTimeout = useButton.useTimeout();
|
|
381
364
|
React__namespace.useEffect(() => {
|
|
382
365
|
if (!enabled) {
|
|
383
366
|
return undefined;
|
|
384
367
|
}
|
|
368
|
+
function hasParentChildren() {
|
|
369
|
+
return !!(tree && parentId && useScrollLock.getNodeChildren(tree.nodesRef.current, parentId).length > 0);
|
|
370
|
+
}
|
|
371
|
+
function closeWithDelay(event) {
|
|
372
|
+
const closeDelay = getDelay(closeDelayProp, 'close', instance.pointerType);
|
|
373
|
+
const close = () => {
|
|
374
|
+
store.setOpen(false, useButton.createChangeEventDetails(useButton.triggerHover, event));
|
|
375
|
+
tree?.events.emit('floating.closed', event);
|
|
376
|
+
};
|
|
377
|
+
if (closeDelay) {
|
|
378
|
+
instance.openChangeTimeout.start(closeDelay, close);
|
|
379
|
+
} else {
|
|
380
|
+
instance.openChangeTimeout.clear();
|
|
381
|
+
close();
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function handleInteractInside(event) {
|
|
385
|
+
const target = useButton.getTarget(event);
|
|
386
|
+
if (!useScrollLock.isInteractiveElement(target)) {
|
|
387
|
+
instance.interactedInside = false;
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
instance.interactedInside = target?.closest('[aria-haspopup]') != null;
|
|
391
|
+
}
|
|
385
392
|
function onFloatingMouseEnter() {
|
|
386
393
|
instance.openChangeTimeout.clear();
|
|
387
394
|
childClosedTimeout.clear();
|
|
@@ -389,15 +396,21 @@ function useHoverFloatingInteraction(context, parameters = {}) {
|
|
|
389
396
|
clearPointerEvents();
|
|
390
397
|
}
|
|
391
398
|
function onFloatingMouseLeave(event) {
|
|
392
|
-
if (
|
|
399
|
+
if (hasParentChildren() && tree) {
|
|
393
400
|
tree.events.on('floating.closed', onNodeClosed);
|
|
394
401
|
return;
|
|
395
402
|
}
|
|
396
|
-
if (
|
|
403
|
+
if (useScrollLock.isTargetInsideEnabledTrigger(event.relatedTarget, store.context.triggerElements)) {
|
|
397
404
|
// If the mouse is leaving the reference element to another trigger, don't explicitly close the popup
|
|
398
405
|
// as it will be moved.
|
|
399
406
|
return;
|
|
400
407
|
}
|
|
408
|
+
const currentNodeId = dataRef.current.floatingContext?.nodeId ?? nodeIdProp;
|
|
409
|
+
const relatedTarget = event.relatedTarget;
|
|
410
|
+
const isMovingIntoDescendantFloating = tree && currentNodeId && floatingUi_utils_dom.isElement(relatedTarget) && useScrollLock.getNodeChildren(tree.nodesRef.current, currentNodeId, false).some(node => useButton.contains(node.context?.elements.floating, relatedTarget));
|
|
411
|
+
if (isMovingIntoDescendantFloating) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
401
414
|
|
|
402
415
|
// If the safePolygon handler is active, let it handle the close logic.
|
|
403
416
|
if (instance.handler) {
|
|
@@ -405,12 +418,12 @@ function useHoverFloatingInteraction(context, parameters = {}) {
|
|
|
405
418
|
return;
|
|
406
419
|
}
|
|
407
420
|
clearPointerEvents();
|
|
408
|
-
if (!isClickLikeOpenEvent$1()) {
|
|
421
|
+
if (isHoverOpen() && !isClickLikeOpenEvent$1()) {
|
|
409
422
|
closeWithDelay(event);
|
|
410
423
|
}
|
|
411
424
|
}
|
|
412
425
|
function onNodeClosed(event) {
|
|
413
|
-
if (!tree || !parentId ||
|
|
426
|
+
if (!tree || !parentId || hasParentChildren()) {
|
|
414
427
|
return;
|
|
415
428
|
}
|
|
416
429
|
// Allow the mouseenter event to fire in case child was closed because mouse moved into parent.
|
|
@@ -421,20 +434,10 @@ function useHoverFloatingInteraction(context, parameters = {}) {
|
|
|
421
434
|
});
|
|
422
435
|
}
|
|
423
436
|
const floating = floatingElement;
|
|
424
|
-
|
|
425
|
-
floating.addEventListener('mouseenter', onFloatingMouseEnter);
|
|
426
|
-
floating.addEventListener('mouseleave', onFloatingMouseLeave);
|
|
427
|
-
floating.addEventListener('pointerdown', handleInteractInside, true);
|
|
428
|
-
}
|
|
429
|
-
return () => {
|
|
430
|
-
if (floating) {
|
|
431
|
-
floating.removeEventListener('mouseenter', onFloatingMouseEnter);
|
|
432
|
-
floating.removeEventListener('mouseleave', onFloatingMouseLeave);
|
|
433
|
-
floating.removeEventListener('pointerdown', handleInteractInside, true);
|
|
434
|
-
}
|
|
437
|
+
return useScrollLock.mergeCleanups(floating && useButton.addEventListener(floating, 'mouseenter', onFloatingMouseEnter), floating && useButton.addEventListener(floating, 'mouseleave', onFloatingMouseLeave), floating && useButton.addEventListener(floating, 'pointerdown', handleInteractInside, true), () => {
|
|
435
438
|
tree?.events.off('floating.closed', onNodeClosed);
|
|
436
|
-
};
|
|
437
|
-
}, [enabled, floatingElement, store, dataRef,
|
|
439
|
+
});
|
|
440
|
+
}, [enabled, floatingElement, store, dataRef, closeDelayProp, nodeIdProp, isHoverOpen, isClickLikeOpenEvent$1, clearPointerEvents, instance, tree, parentId, childClosedTimeout]);
|
|
438
441
|
}
|
|
439
442
|
|
|
440
443
|
const EMPTY_REF = {
|
|
@@ -446,11 +449,6 @@ const EMPTY_REF = {
|
|
|
446
449
|
* elements.
|
|
447
450
|
*/
|
|
448
451
|
function useHoverReferenceInteraction(context, props = {}) {
|
|
449
|
-
const store = 'rootStore' in context ? context.rootStore : context;
|
|
450
|
-
const {
|
|
451
|
-
dataRef,
|
|
452
|
-
events
|
|
453
|
-
} = store.context;
|
|
454
452
|
const {
|
|
455
453
|
enabled = true,
|
|
456
454
|
delay = 0,
|
|
@@ -461,23 +459,29 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
461
459
|
triggerElementRef = EMPTY_REF,
|
|
462
460
|
externalTree,
|
|
463
461
|
isActiveTrigger = true,
|
|
464
|
-
getHandleCloseContext
|
|
462
|
+
getHandleCloseContext,
|
|
463
|
+
isClosing,
|
|
464
|
+
shouldOpen: shouldOpenProp
|
|
465
465
|
} = props;
|
|
466
|
-
const
|
|
466
|
+
const store = 'rootStore' in context ? context.rootStore : context;
|
|
467
|
+
const {
|
|
468
|
+
dataRef,
|
|
469
|
+
events
|
|
470
|
+
} = store.context;
|
|
471
|
+
const tree = useScrollLock.useFloatingTree(externalTree);
|
|
467
472
|
const instance = useHoverInteractionSharedState(store);
|
|
473
|
+
const isHoverCloseActiveRef = React__namespace.useRef(false);
|
|
468
474
|
const handleCloseRef = useButton.useValueAsRef(handleClose);
|
|
469
475
|
const delayRef = useButton.useValueAsRef(delay);
|
|
470
476
|
const restMsRef = useButton.useValueAsRef(restMs);
|
|
471
477
|
const enabledRef = useButton.useValueAsRef(enabled);
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
instance.handleCloseOptions = handleCloseRef.current?.__options;
|
|
475
|
-
}
|
|
478
|
+
const shouldOpenRef = useButton.useValueAsRef(shouldOpenProp);
|
|
479
|
+
const isClosingRef = useButton.useValueAsRef(isClosing);
|
|
476
480
|
const isClickLikeOpenEvent$1 = useButton.useStableCallback(() => {
|
|
477
481
|
return isClickLikeOpenEvent(dataRef.current.openEvent?.type, instance.interactedInside);
|
|
478
482
|
});
|
|
479
|
-
const
|
|
480
|
-
return
|
|
483
|
+
const checkShouldOpen = useButton.useStableCallback(() => {
|
|
484
|
+
return shouldOpenRef.current?.() !== false;
|
|
481
485
|
});
|
|
482
486
|
const isOverInactiveTrigger = useButton.useStableCallback((currentDomReference, currentTarget, target) => {
|
|
483
487
|
const allTriggers = store.context.triggerElements;
|
|
@@ -494,19 +498,6 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
494
498
|
const targetElement = target;
|
|
495
499
|
return allTriggers.hasMatchingElement(trigger => useButton.contains(trigger, targetElement)) && (!currentDomReference || !useButton.contains(currentDomReference, targetElement));
|
|
496
500
|
});
|
|
497
|
-
const closeWithDelay = React__namespace.useCallback((event, runElseBranch = true) => {
|
|
498
|
-
const closeDelay = getDelay(delayRef.current, 'close', instance.pointerType);
|
|
499
|
-
if (closeDelay) {
|
|
500
|
-
instance.openChangeTimeout.start(closeDelay, () => {
|
|
501
|
-
store.setOpen(false, useButton.createChangeEventDetails(useButton.triggerHover, event));
|
|
502
|
-
tree?.events.emit('floating.closed', event);
|
|
503
|
-
});
|
|
504
|
-
} else if (runElseBranch) {
|
|
505
|
-
instance.openChangeTimeout.clear();
|
|
506
|
-
store.setOpen(false, useButton.createChangeEventDetails(useButton.triggerHover, event));
|
|
507
|
-
tree?.events.emit('floating.closed', event);
|
|
508
|
-
}
|
|
509
|
-
}, [delayRef, store, instance, tree]);
|
|
510
501
|
const cleanupMouseMoveHandler = useButton.useStableCallback(() => {
|
|
511
502
|
if (!instance.handler) {
|
|
512
503
|
return;
|
|
@@ -515,10 +506,14 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
515
506
|
doc.removeEventListener('mousemove', instance.handler);
|
|
516
507
|
instance.handler = undefined;
|
|
517
508
|
});
|
|
518
|
-
React__namespace.useEffect(() => cleanupMouseMoveHandler, [cleanupMouseMoveHandler]);
|
|
519
509
|
const clearPointerEvents = useButton.useStableCallback(() => {
|
|
520
510
|
clearSafePolygonPointerEventsMutation(instance);
|
|
521
511
|
});
|
|
512
|
+
if (isActiveTrigger) {
|
|
513
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
514
|
+
instance.handleCloseOptions = handleCloseRef.current?.__options;
|
|
515
|
+
}
|
|
516
|
+
React__namespace.useEffect(() => cleanupMouseMoveHandler, [cleanupMouseMoveHandler]);
|
|
522
517
|
|
|
523
518
|
// When closing before opening, clear the delay timeouts to cancel it
|
|
524
519
|
// from showing.
|
|
@@ -528,11 +523,14 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
528
523
|
}
|
|
529
524
|
function onOpenChangeLocal(details) {
|
|
530
525
|
if (!details.open) {
|
|
526
|
+
isHoverCloseActiveRef.current = details.reason === useButton.triggerHover;
|
|
531
527
|
cleanupMouseMoveHandler();
|
|
532
528
|
instance.openChangeTimeout.clear();
|
|
533
529
|
instance.restTimeout.clear();
|
|
534
530
|
instance.blockMouseMove = true;
|
|
535
531
|
instance.restTimeoutPending = false;
|
|
532
|
+
} else {
|
|
533
|
+
isHoverCloseActiveRef.current = false;
|
|
536
534
|
}
|
|
537
535
|
}
|
|
538
536
|
events.on('openchange', onOpenChangeLocal);
|
|
@@ -544,6 +542,19 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
544
542
|
if (!enabled) {
|
|
545
543
|
return undefined;
|
|
546
544
|
}
|
|
545
|
+
function closeWithDelay(event, runElseBranch = true) {
|
|
546
|
+
const closeDelay = getDelay(delayRef.current, 'close', instance.pointerType);
|
|
547
|
+
if (closeDelay) {
|
|
548
|
+
instance.openChangeTimeout.start(closeDelay, () => {
|
|
549
|
+
store.setOpen(false, useButton.createChangeEventDetails(useButton.triggerHover, event));
|
|
550
|
+
tree?.events.emit('floating.closed', event);
|
|
551
|
+
});
|
|
552
|
+
} else if (runElseBranch) {
|
|
553
|
+
instance.openChangeTimeout.clear();
|
|
554
|
+
store.setOpen(false, useButton.createChangeEventDetails(useButton.triggerHover, event));
|
|
555
|
+
tree?.events.emit('floating.closed', event);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
547
558
|
const trigger = triggerElementRef.current ?? (isActiveTrigger ? store.select('domReferenceElement') : null);
|
|
548
559
|
if (!floatingUi_utils_dom.isElement(trigger)) {
|
|
549
560
|
return undefined;
|
|
@@ -558,27 +569,57 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
558
569
|
// Only rest delay is set; there's no fallback delay.
|
|
559
570
|
// This will be handled by `onMouseMove`.
|
|
560
571
|
const restMsValue = getRestMs(restMsRef.current);
|
|
561
|
-
if (restMsValue > 0 && !getDelay(delayRef.current, 'open')) {
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
572
|
const openDelay = getDelay(delayRef.current, 'open', instance.pointerType);
|
|
565
|
-
const
|
|
573
|
+
const eventTarget = useButton.getTarget(event);
|
|
574
|
+
const currentTarget = event.currentTarget ?? null;
|
|
566
575
|
const currentDomReference = store.select('domReferenceElement');
|
|
567
|
-
|
|
576
|
+
let triggerNode = currentTarget;
|
|
577
|
+
|
|
578
|
+
// Wrapper/delegated mode: resolve the actual trigger from the event target.
|
|
579
|
+
if (floatingUi_utils_dom.isElement(eventTarget) && !store.context.triggerElements.hasElement(eventTarget)) {
|
|
580
|
+
for (const triggerElement of store.context.triggerElements.elements()) {
|
|
581
|
+
if (useButton.contains(triggerElement, eventTarget)) {
|
|
582
|
+
triggerNode = triggerElement;
|
|
583
|
+
break;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Wrapper/delegated mode fallback: if the wrapper contains the active trigger,
|
|
589
|
+
// treat this as re-entering that active trigger.
|
|
590
|
+
if (floatingUi_utils_dom.isElement(currentTarget) && floatingUi_utils_dom.isElement(currentDomReference) && !store.context.triggerElements.hasElement(currentTarget) && useButton.contains(currentTarget, currentDomReference)) {
|
|
591
|
+
triggerNode = currentDomReference;
|
|
592
|
+
}
|
|
593
|
+
const isOverInactive = triggerNode == null ? false : isOverInactiveTrigger(currentDomReference, triggerNode, eventTarget);
|
|
568
594
|
const isOpen = store.select('open');
|
|
595
|
+
const isInClosingTransition = isClosingRef.current?.() ?? store.select('transitionStatus') === 'ending';
|
|
596
|
+
const isHoverCloseTransition = !isOpen && isInClosingTransition && isHoverCloseActiveRef.current;
|
|
597
|
+
const isReenteringSameTriggerDuringCloseTransition = !isOverInactive && floatingUi_utils_dom.isElement(triggerNode) && floatingUi_utils_dom.isElement(currentDomReference) && useButton.contains(currentDomReference, triggerNode) && isHoverCloseTransition;
|
|
598
|
+
const isRestOnlyDelay = restMsValue > 0 && !openDelay;
|
|
599
|
+
const shouldOpenImmediately = isOverInactive && (isOpen || isHoverCloseTransition) || isReenteringSameTriggerDuringCloseTransition;
|
|
569
600
|
const shouldOpen = !isOpen || isOverInactive;
|
|
570
601
|
|
|
571
|
-
//
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
602
|
+
// Open immediately when moving between triggers while open, or during
|
|
603
|
+
// a hover-driven close transition (including same-trigger re-entry).
|
|
604
|
+
if (shouldOpenImmediately) {
|
|
605
|
+
if (checkShouldOpen()) {
|
|
606
|
+
store.setOpen(true, useButton.createChangeEventDetails(useButton.triggerHover, event, triggerNode));
|
|
607
|
+
}
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
if (isRestOnlyDelay) {
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
if (openDelay) {
|
|
575
614
|
instance.openChangeTimeout.start(openDelay, () => {
|
|
576
|
-
if (shouldOpen) {
|
|
615
|
+
if (shouldOpen && checkShouldOpen()) {
|
|
577
616
|
store.setOpen(true, useButton.createChangeEventDetails(useButton.triggerHover, event, triggerNode));
|
|
578
617
|
}
|
|
579
618
|
});
|
|
580
619
|
} else if (shouldOpen) {
|
|
581
|
-
|
|
620
|
+
if (checkShouldOpen()) {
|
|
621
|
+
store.setOpen(true, useButton.createChangeEventDetails(useButton.triggerHover, event, triggerNode));
|
|
622
|
+
}
|
|
582
623
|
}
|
|
583
624
|
}
|
|
584
625
|
function onMouseLeave(event) {
|
|
@@ -592,8 +633,7 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
592
633
|
instance.restTimeout.clear();
|
|
593
634
|
instance.restTimeoutPending = false;
|
|
594
635
|
const handleCloseContextBase = dataRef.current.floatingContext ?? getHandleCloseContext?.();
|
|
595
|
-
|
|
596
|
-
if (ignoreRelatedTargetTrigger) {
|
|
636
|
+
if (useScrollLock.isTargetInsideEnabledTrigger(event.relatedTarget, store.context.triggerElements)) {
|
|
597
637
|
return;
|
|
598
638
|
}
|
|
599
639
|
if (handleCloseRef.current && handleCloseContextBase) {
|
|
@@ -624,20 +664,12 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
624
664
|
}
|
|
625
665
|
}
|
|
626
666
|
if (move) {
|
|
627
|
-
|
|
667
|
+
return useScrollLock.mergeCleanups(useButton.addEventListener(trigger, 'mousemove', onMouseEnter, {
|
|
628
668
|
once: true
|
|
629
|
-
});
|
|
669
|
+
}), useButton.addEventListener(trigger, 'mouseenter', onMouseEnter), useButton.addEventListener(trigger, 'mouseleave', onMouseLeave));
|
|
630
670
|
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
return () => {
|
|
634
|
-
if (move) {
|
|
635
|
-
trigger.removeEventListener('mousemove', onMouseEnter);
|
|
636
|
-
}
|
|
637
|
-
trigger.removeEventListener('mouseenter', onMouseEnter);
|
|
638
|
-
trigger.removeEventListener('mouseleave', onMouseLeave);
|
|
639
|
-
};
|
|
640
|
-
}, [cleanupMouseMoveHandler, clearPointerEvents, dataRef, delayRef, closeWithDelay, store, enabled, handleCloseRef, instance, isActiveTrigger, isOverInactiveTrigger, isClickLikeOpenEvent$1, isRelatedTargetInsideEnabledTrigger, mouseOnly, move, restMsRef, triggerElementRef, tree, enabledRef, getHandleCloseContext]);
|
|
671
|
+
return useScrollLock.mergeCleanups(useButton.addEventListener(trigger, 'mouseenter', onMouseEnter), useButton.addEventListener(trigger, 'mouseleave', onMouseLeave));
|
|
672
|
+
}, [cleanupMouseMoveHandler, clearPointerEvents, dataRef, delayRef, store, enabled, handleCloseRef, instance, isActiveTrigger, isOverInactiveTrigger, isClickLikeOpenEvent$1, mouseOnly, move, restMsRef, triggerElementRef, tree, enabledRef, getHandleCloseContext, isClosingRef, checkShouldOpen]);
|
|
641
673
|
return React__namespace.useMemo(() => {
|
|
642
674
|
if (!enabled) {
|
|
643
675
|
return undefined;
|
|
@@ -659,6 +691,17 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
659
691
|
if (mouseOnly && !useButton.isMouseLikePointerType(instance.pointerType)) {
|
|
660
692
|
return;
|
|
661
693
|
}
|
|
694
|
+
if (currentOpen && isOverInactive && instance.handleCloseOptions?.blockPointerEvents) {
|
|
695
|
+
const floatingElement = store.select('floatingElement');
|
|
696
|
+
if (floatingElement) {
|
|
697
|
+
const scopeElement = instance.handleCloseOptions?.getScope?.() ?? trigger.ownerDocument.body;
|
|
698
|
+
applySafePolygonPointerEventsMutation(instance, {
|
|
699
|
+
scopeElement,
|
|
700
|
+
referenceElement: trigger,
|
|
701
|
+
floatingElement
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
}
|
|
662
705
|
const restMsValue = getRestMs(restMsRef.current);
|
|
663
706
|
if (currentOpen && !isOverInactive || restMsValue === 0) {
|
|
664
707
|
return;
|
|
@@ -676,7 +719,7 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
676
719
|
return;
|
|
677
720
|
}
|
|
678
721
|
const latestOpen = store.select('open');
|
|
679
|
-
if (!instance.blockMouseMove && (!latestOpen || isOverInactive)) {
|
|
722
|
+
if (!instance.blockMouseMove && (!latestOpen || isOverInactive) && checkShouldOpen()) {
|
|
680
723
|
store.setOpen(true, useButton.createChangeEventDetails(useButton.triggerHover, nativeEvent, trigger));
|
|
681
724
|
}
|
|
682
725
|
}
|
|
@@ -692,7 +735,7 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
692
735
|
}
|
|
693
736
|
}
|
|
694
737
|
};
|
|
695
|
-
}, [enabled, instance, isClickLikeOpenEvent$1, isOverInactiveTrigger, mouseOnly, store, restMsRef]);
|
|
738
|
+
}, [enabled, instance, isClickLikeOpenEvent$1, isOverInactiveTrigger, mouseOnly, store, restMsRef, checkShouldOpen]);
|
|
696
739
|
}
|
|
697
740
|
|
|
698
741
|
/* eslint-disable no-nested-ternary */
|
|
@@ -810,7 +853,7 @@ function safePolygon(options = {}) {
|
|
|
810
853
|
return undefined;
|
|
811
854
|
}
|
|
812
855
|
function hasOpenChildNode() {
|
|
813
|
-
return Boolean(tree &&
|
|
856
|
+
return Boolean(tree && useScrollLock.getNodeChildren(tree.nodesRef.current, nodeId).length > 0);
|
|
814
857
|
}
|
|
815
858
|
function closeIfNoOpenChild() {
|
|
816
859
|
if (!hasOpenChildNode()) {
|
|
@@ -931,6 +974,7 @@ function safePolygon(options = {}) {
|
|
|
931
974
|
|
|
932
975
|
// eslint-disable-next-line no-underscore-dangle
|
|
933
976
|
fn.__options = {
|
|
977
|
+
...options,
|
|
934
978
|
blockPointerEvents
|
|
935
979
|
};
|
|
936
980
|
return fn;
|
|
@@ -967,7 +1011,7 @@ function useContextMenuRootContext(optional = true) {
|
|
|
967
1011
|
}
|
|
968
1012
|
|
|
969
1013
|
const stateAttributesMapping$1 = {
|
|
970
|
-
...
|
|
1014
|
+
...useScrollLock.popupStateMapping,
|
|
971
1015
|
...useButton.transitionStatusMapping
|
|
972
1016
|
};
|
|
973
1017
|
|
|
@@ -979,8 +1023,9 @@ const stateAttributesMapping$1 = {
|
|
|
979
1023
|
*/
|
|
980
1024
|
const MenuBackdrop = /*#__PURE__*/React__namespace.forwardRef(function MenuBackdrop(componentProps, forwardedRef) {
|
|
981
1025
|
const {
|
|
982
|
-
className,
|
|
983
1026
|
render,
|
|
1027
|
+
className,
|
|
1028
|
+
style,
|
|
984
1029
|
...elementProps
|
|
985
1030
|
} = componentProps;
|
|
986
1031
|
const {
|
|
@@ -1031,12 +1076,13 @@ function useMenuItemCommonProps(params) {
|
|
|
1031
1076
|
const {
|
|
1032
1077
|
events: menuEvents
|
|
1033
1078
|
} = store.useState('floatingTreeRoot');
|
|
1079
|
+
const open = store.useState('open');
|
|
1034
1080
|
const contextMenuContext = useContextMenuRootContext(true);
|
|
1035
1081
|
const isContextMenu = contextMenuContext !== undefined;
|
|
1036
1082
|
return React__namespace.useMemo(() => ({
|
|
1037
1083
|
id,
|
|
1038
1084
|
role: 'menuitem',
|
|
1039
|
-
tabIndex: highlighted ? 0 : -1,
|
|
1085
|
+
tabIndex: open && highlighted ? 0 : -1,
|
|
1040
1086
|
onKeyDown(event) {
|
|
1041
1087
|
if (event.key === ' ' && typingRef?.current) {
|
|
1042
1088
|
event.preventDefault();
|
|
@@ -1072,7 +1118,7 @@ function useMenuItemCommonProps(params) {
|
|
|
1072
1118
|
|
|
1073
1119
|
// On non-macOS platforms, this mouseup belongs to the right-click gesture
|
|
1074
1120
|
// that opened the context menu, so it must not activate an item.
|
|
1075
|
-
if (isContextMenu && !useButton.
|
|
1121
|
+
if (isContextMenu && !useButton.mac && event.button === 2) {
|
|
1076
1122
|
return;
|
|
1077
1123
|
}
|
|
1078
1124
|
}
|
|
@@ -1084,7 +1130,7 @@ function useMenuItemCommonProps(params) {
|
|
|
1084
1130
|
}
|
|
1085
1131
|
}
|
|
1086
1132
|
}
|
|
1087
|
-
}), [closeOnClick, highlighted, id, menuEvents, nodeId, store, typingRef, itemRef, contextMenuContext, isContextMenu, itemMetadata]);
|
|
1133
|
+
}), [closeOnClick, highlighted, id, menuEvents, nodeId, open, store, typingRef, itemRef, contextMenuContext, isContextMenu, itemMetadata]);
|
|
1088
1134
|
}
|
|
1089
1135
|
|
|
1090
1136
|
const REGULAR_ITEM = {
|
|
@@ -1093,7 +1139,7 @@ const REGULAR_ITEM = {
|
|
|
1093
1139
|
function useMenuItem(params) {
|
|
1094
1140
|
const {
|
|
1095
1141
|
closeOnClick,
|
|
1096
|
-
disabled = false,
|
|
1142
|
+
disabled: disabledProp = false,
|
|
1097
1143
|
highlighted,
|
|
1098
1144
|
id,
|
|
1099
1145
|
store,
|
|
@@ -1102,6 +1148,8 @@ function useMenuItem(params) {
|
|
|
1102
1148
|
itemMetadata,
|
|
1103
1149
|
nodeId
|
|
1104
1150
|
} = params;
|
|
1151
|
+
const rootDisabled = store.useState('disabled');
|
|
1152
|
+
const disabled = disabledProp || rootDisabled;
|
|
1105
1153
|
const itemRef = React__namespace.useRef(null);
|
|
1106
1154
|
const {
|
|
1107
1155
|
getButtonProps,
|
|
@@ -1178,7 +1226,7 @@ if (process.env.NODE_ENV !== "production") MenuGroupContext.displayName = "MenuG
|
|
|
1178
1226
|
function useMenuGroupRootContext() {
|
|
1179
1227
|
const context = React__namespace.useContext(MenuGroupContext);
|
|
1180
1228
|
if (context === undefined) {
|
|
1181
|
-
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI:
|
|
1229
|
+
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: MenuGroupContext is missing. Menu group parts must be used within <Menu.Group> or <Menu.RadioGroup>.' : useRenderElement.formatErrorMessage(31));
|
|
1182
1230
|
}
|
|
1183
1231
|
return context;
|
|
1184
1232
|
}
|
|
@@ -1187,12 +1235,10 @@ const MenuGroup = /*#__PURE__*/React__namespace.forwardRef(function MenuGroup(co
|
|
|
1187
1235
|
const {
|
|
1188
1236
|
render,
|
|
1189
1237
|
className,
|
|
1238
|
+
style,
|
|
1190
1239
|
...elementProps
|
|
1191
1240
|
} = componentProps;
|
|
1192
1241
|
const [labelId, setLabelId] = React__namespace.useState(undefined);
|
|
1193
|
-
const context = React__namespace.useMemo(() => ({
|
|
1194
|
-
setLabelId
|
|
1195
|
-
}), [setLabelId]);
|
|
1196
1242
|
const element = useRenderElement.useRenderElement('div', componentProps, {
|
|
1197
1243
|
ref: forwardedRef,
|
|
1198
1244
|
props: {
|
|
@@ -1202,7 +1248,7 @@ const MenuGroup = /*#__PURE__*/React__namespace.forwardRef(function MenuGroup(co
|
|
|
1202
1248
|
}
|
|
1203
1249
|
});
|
|
1204
1250
|
return /*#__PURE__*/jsxRuntime.jsx(MenuGroupContext.Provider, {
|
|
1205
|
-
value:
|
|
1251
|
+
value: setLabelId,
|
|
1206
1252
|
children: element
|
|
1207
1253
|
});
|
|
1208
1254
|
});
|
|
@@ -1214,17 +1260,16 @@ if (process.env.NODE_ENV !== "production") MenuGroup.displayName = "MenuGroup";
|
|
|
1214
1260
|
*
|
|
1215
1261
|
* Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
|
|
1216
1262
|
*/
|
|
1217
|
-
const MenuGroupLabel = /*#__PURE__*/React__namespace.forwardRef(function
|
|
1263
|
+
const MenuGroupLabel = /*#__PURE__*/React__namespace.forwardRef(function MenuGroupLabel(componentProps, forwardedRef) {
|
|
1218
1264
|
const {
|
|
1219
|
-
className,
|
|
1220
1265
|
render,
|
|
1266
|
+
className,
|
|
1267
|
+
style,
|
|
1221
1268
|
id: idProp,
|
|
1222
1269
|
...elementProps
|
|
1223
1270
|
} = componentProps;
|
|
1224
1271
|
const id = useButton.useBaseUiId(idProp);
|
|
1225
|
-
const
|
|
1226
|
-
setLabelId
|
|
1227
|
-
} = useMenuGroupRootContext();
|
|
1272
|
+
const setLabelId = useMenuGroupRootContext();
|
|
1228
1273
|
useButton.useIsoLayoutEffect(() => {
|
|
1229
1274
|
setLabelId(id);
|
|
1230
1275
|
return () => {
|
|
@@ -1257,6 +1302,7 @@ const MenuItem = /*#__PURE__*/React__namespace.forwardRef(function MenuItem(comp
|
|
|
1257
1302
|
nativeButton = false,
|
|
1258
1303
|
disabled = false,
|
|
1259
1304
|
closeOnClick = true,
|
|
1305
|
+
style,
|
|
1260
1306
|
...elementProps
|
|
1261
1307
|
} = componentProps;
|
|
1262
1308
|
const listItem = useCompositeListItem.useCompositeListItem({
|
|
@@ -1279,7 +1325,7 @@ const MenuItem = /*#__PURE__*/React__namespace.forwardRef(function MenuItem(comp
|
|
|
1279
1325
|
id,
|
|
1280
1326
|
store,
|
|
1281
1327
|
nativeButton,
|
|
1282
|
-
nodeId: menuPositionerContext?.nodeId,
|
|
1328
|
+
nodeId: menuPositionerContext?.context.nodeId,
|
|
1283
1329
|
itemMetadata: REGULAR_ITEM
|
|
1284
1330
|
});
|
|
1285
1331
|
const state = {
|
|
@@ -1307,6 +1353,7 @@ const MenuLinkItem = /*#__PURE__*/React__namespace.forwardRef(function MenuLinkI
|
|
|
1307
1353
|
id: idProp,
|
|
1308
1354
|
label,
|
|
1309
1355
|
closeOnClick = false,
|
|
1356
|
+
style,
|
|
1310
1357
|
...elementProps
|
|
1311
1358
|
} = componentProps;
|
|
1312
1359
|
const linkRef = React__namespace.useRef(null);
|
|
@@ -1314,7 +1361,7 @@ const MenuLinkItem = /*#__PURE__*/React__namespace.forwardRef(function MenuLinkI
|
|
|
1314
1361
|
label
|
|
1315
1362
|
});
|
|
1316
1363
|
const menuPositionerContext = useMenuPositionerContext(true);
|
|
1317
|
-
const nodeId = menuPositionerContext?.nodeId;
|
|
1364
|
+
const nodeId = menuPositionerContext?.context.nodeId;
|
|
1318
1365
|
const id = useButton.useBaseUiId(idProp);
|
|
1319
1366
|
const {
|
|
1320
1367
|
store
|
|
@@ -1341,9 +1388,9 @@ const MenuLinkItem = /*#__PURE__*/React__namespace.forwardRef(function MenuLinkI
|
|
|
1341
1388
|
function getItemProps(externalProps) {
|
|
1342
1389
|
return useRenderElement.mergeProps(commonProps, externalProps, getButtonProps);
|
|
1343
1390
|
}
|
|
1344
|
-
const state =
|
|
1391
|
+
const state = {
|
|
1345
1392
|
highlighted
|
|
1346
|
-
}
|
|
1393
|
+
};
|
|
1347
1394
|
return useRenderElement.useRenderElement('a', componentProps, {
|
|
1348
1395
|
state,
|
|
1349
1396
|
props: [itemProps, elementProps, getItemProps],
|
|
@@ -1353,7 +1400,7 @@ const MenuLinkItem = /*#__PURE__*/React__namespace.forwardRef(function MenuLinkI
|
|
|
1353
1400
|
if (process.env.NODE_ENV !== "production") MenuLinkItem.displayName = "MenuLinkItem";
|
|
1354
1401
|
|
|
1355
1402
|
const stateAttributesMapping = {
|
|
1356
|
-
...
|
|
1403
|
+
...useScrollLock.popupStateMapping,
|
|
1357
1404
|
...useButton.transitionStatusMapping
|
|
1358
1405
|
};
|
|
1359
1406
|
|
|
@@ -1367,6 +1414,7 @@ const MenuPopup = /*#__PURE__*/React__namespace.forwardRef(function MenuPopup(co
|
|
|
1367
1414
|
const {
|
|
1368
1415
|
render,
|
|
1369
1416
|
className,
|
|
1417
|
+
style,
|
|
1370
1418
|
finalFocus,
|
|
1371
1419
|
...elementProps
|
|
1372
1420
|
} = componentProps;
|
|
@@ -1377,7 +1425,7 @@ const MenuPopup = /*#__PURE__*/React__namespace.forwardRef(function MenuPopup(co
|
|
|
1377
1425
|
side,
|
|
1378
1426
|
align
|
|
1379
1427
|
} = useMenuPositionerContext();
|
|
1380
|
-
const insideToolbar =
|
|
1428
|
+
const insideToolbar = isElementDisabled.useToolbarRootContext(true) != null;
|
|
1381
1429
|
const open = store.useState('open');
|
|
1382
1430
|
const transitionStatus = store.useState('transitionStatus');
|
|
1383
1431
|
const popupProps = store.useState('popupProps');
|
|
@@ -1391,6 +1439,9 @@ const MenuPopup = /*#__PURE__*/React__namespace.forwardRef(function MenuPopup(co
|
|
|
1391
1439
|
const floatingTreeRoot = store.useState('floatingTreeRoot');
|
|
1392
1440
|
const closeDelay = store.useState('closeDelay');
|
|
1393
1441
|
const activeTriggerElement = store.useState('activeTriggerElement');
|
|
1442
|
+
const hoverEnabled = store.useState('hoverEnabled');
|
|
1443
|
+
const disabled = store.useState('disabled');
|
|
1444
|
+
const openMethod = store.useState('openMethod');
|
|
1394
1445
|
const isContextMenu = parent.type === 'context-menu';
|
|
1395
1446
|
useButton.useOpenChangeComplete({
|
|
1396
1447
|
open,
|
|
@@ -1410,12 +1461,13 @@ const MenuPopup = /*#__PURE__*/React__namespace.forwardRef(function MenuPopup(co
|
|
|
1410
1461
|
floatingTreeRoot.events.off('close', handleClose);
|
|
1411
1462
|
};
|
|
1412
1463
|
}, [floatingTreeRoot.events, store]);
|
|
1413
|
-
const hoverEnabled = store.useState('hoverEnabled');
|
|
1414
|
-
const disabled = store.useState('disabled');
|
|
1415
1464
|
useHoverFloatingInteraction(floatingContext, {
|
|
1416
1465
|
enabled: hoverEnabled && !disabled && !isContextMenu && parent.type !== 'menubar',
|
|
1417
1466
|
closeDelay
|
|
1418
1467
|
});
|
|
1468
|
+
const setPopupElement = React__namespace.useCallback(element => {
|
|
1469
|
+
store.set('popupElement', element);
|
|
1470
|
+
}, [store]);
|
|
1419
1471
|
const state = {
|
|
1420
1472
|
transitionStatus,
|
|
1421
1473
|
side,
|
|
@@ -1424,16 +1476,13 @@ const MenuPopup = /*#__PURE__*/React__namespace.forwardRef(function MenuPopup(co
|
|
|
1424
1476
|
nested: parent.type === 'menu',
|
|
1425
1477
|
instant: instantType
|
|
1426
1478
|
};
|
|
1427
|
-
const setPopupElement = React__namespace.useCallback(element => {
|
|
1428
|
-
store.set('popupElement', element);
|
|
1429
|
-
}, [store]);
|
|
1430
1479
|
const element = useRenderElement.useRenderElement('div', componentProps, {
|
|
1431
1480
|
state,
|
|
1432
1481
|
ref: [forwardedRef, store.context.popupRef, setPopupElement],
|
|
1433
1482
|
stateAttributesMapping,
|
|
1434
1483
|
props: [popupProps, {
|
|
1435
1484
|
onKeyDown(event) {
|
|
1436
|
-
if (insideToolbar &&
|
|
1485
|
+
if (insideToolbar && scrollEdges.COMPOSITE_KEYS.has(event.key)) {
|
|
1437
1486
|
event.stopPropagation();
|
|
1438
1487
|
}
|
|
1439
1488
|
}
|
|
@@ -1445,8 +1494,9 @@ const MenuPopup = /*#__PURE__*/React__namespace.forwardRef(function MenuPopup(co
|
|
|
1445
1494
|
if (triggerElement || parent.type === 'menubar' && lastOpenChangeReason !== useButton.outsidePress) {
|
|
1446
1495
|
returnFocus = true;
|
|
1447
1496
|
}
|
|
1448
|
-
return /*#__PURE__*/jsxRuntime.jsx(
|
|
1497
|
+
return /*#__PURE__*/jsxRuntime.jsx(useScrollLock.FloatingFocusManager, {
|
|
1449
1498
|
context: floatingContext,
|
|
1499
|
+
openInteractionType: openMethod,
|
|
1450
1500
|
modal: isContextMenu,
|
|
1451
1501
|
disabled: !mounted,
|
|
1452
1502
|
returnFocus: finalFocus === undefined ? returnFocus : finalFocus,
|
|
@@ -1486,7 +1536,7 @@ const MenuPortal = /*#__PURE__*/React__namespace.forwardRef(function MenuPortal(
|
|
|
1486
1536
|
}
|
|
1487
1537
|
return /*#__PURE__*/jsxRuntime.jsx(MenuPortalContext.Provider, {
|
|
1488
1538
|
value: keepMounted,
|
|
1489
|
-
children: /*#__PURE__*/jsxRuntime.jsx(
|
|
1539
|
+
children: /*#__PURE__*/jsxRuntime.jsx(useScrollLock.FloatingPortal, {
|
|
1490
1540
|
ref: forwardedRef,
|
|
1491
1541
|
...portalProps
|
|
1492
1542
|
})
|
|
@@ -1509,7 +1559,8 @@ const MenuPositioner = /*#__PURE__*/React__namespace.forwardRef(function MenuPos
|
|
|
1509
1559
|
arrowPadding = 5,
|
|
1510
1560
|
sticky = false,
|
|
1511
1561
|
disableAnchorTracking = false,
|
|
1512
|
-
collisionAvoidance: collisionAvoidanceProp =
|
|
1562
|
+
collisionAvoidance: collisionAvoidanceProp = useScrollLock.DROPDOWN_COLLISION_AVOIDANCE,
|
|
1563
|
+
style,
|
|
1513
1564
|
...elementProps
|
|
1514
1565
|
} = componentProps;
|
|
1515
1566
|
const {
|
|
@@ -1523,6 +1574,7 @@ const MenuPositioner = /*#__PURE__*/React__namespace.forwardRef(function MenuPos
|
|
|
1523
1574
|
const mounted = store.useState('mounted');
|
|
1524
1575
|
const open = store.useState('open');
|
|
1525
1576
|
const modal = store.useState('modal');
|
|
1577
|
+
const openMethod = store.useState('openMethod');
|
|
1526
1578
|
const triggerElement = store.useState('activeTriggerElement');
|
|
1527
1579
|
const transitionStatus = store.useState('transitionStatus');
|
|
1528
1580
|
const positionerElement = store.useState('positionerElement');
|
|
@@ -1552,9 +1604,9 @@ const MenuPositioner = /*#__PURE__*/React__namespace.forwardRef(function MenuPos
|
|
|
1552
1604
|
if (parent.type === 'menu') {
|
|
1553
1605
|
computedSide = computedSide ?? 'inline-end';
|
|
1554
1606
|
computedAlign = computedAlign ?? 'start';
|
|
1555
|
-
collisionAvoidance = componentProps.collisionAvoidance ??
|
|
1607
|
+
collisionAvoidance = componentProps.collisionAvoidance ?? useScrollLock.POPUP_COLLISION_AVOIDANCE;
|
|
1556
1608
|
} else if (parent.type === 'menubar') {
|
|
1557
|
-
computedSide = computedSide ?? 'bottom';
|
|
1609
|
+
computedSide = computedSide ?? (parent.context.orientation === 'vertical' ? 'inline-end' : 'bottom');
|
|
1558
1610
|
computedAlign = computedAlign ?? 'start';
|
|
1559
1611
|
}
|
|
1560
1612
|
const contextMenu = parent.type === 'context-menu';
|
|
@@ -1579,20 +1631,6 @@ const MenuPositioner = /*#__PURE__*/React__namespace.forwardRef(function MenuPos
|
|
|
1579
1631
|
externalTree: floatingTreeRoot,
|
|
1580
1632
|
adaptiveOrigin: hasViewport ? useCompositeListItem.adaptiveOrigin : undefined
|
|
1581
1633
|
});
|
|
1582
|
-
const positionerProps = React__namespace.useMemo(() => {
|
|
1583
|
-
const hiddenStyles = {};
|
|
1584
|
-
if (!open) {
|
|
1585
|
-
hiddenStyles.pointerEvents = 'none';
|
|
1586
|
-
}
|
|
1587
|
-
return {
|
|
1588
|
-
role: 'presentation',
|
|
1589
|
-
hidden: !mounted,
|
|
1590
|
-
style: {
|
|
1591
|
-
...positioner.positionerStyles,
|
|
1592
|
-
...hiddenStyles
|
|
1593
|
-
}
|
|
1594
|
-
};
|
|
1595
|
-
}, [open, mounted, positioner.positionerStyles]);
|
|
1596
1634
|
React__namespace.useEffect(() => {
|
|
1597
1635
|
function onMenuOpenChange(details) {
|
|
1598
1636
|
if (details.open) {
|
|
@@ -1700,19 +1738,16 @@ const MenuPositioner = /*#__PURE__*/React__namespace.forwardRef(function MenuPos
|
|
|
1700
1738
|
nested: parent.type === 'menu',
|
|
1701
1739
|
instant: instantType
|
|
1702
1740
|
};
|
|
1703
|
-
const
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
stateAttributesMapping: InternalBackdrop.popupStateMapping,
|
|
1714
|
-
ref: [forwardedRef, store.useStateSetter('positionerElement')],
|
|
1715
|
-
props: [positionerProps, useCompositeListItem.getDisabledMountTransitionStyles(transitionStatus), elementProps]
|
|
1741
|
+
const menubarModal = parent.type === 'menubar' && parent.context.modal;
|
|
1742
|
+
const popupModal = modal && lastOpenChangeReason !== useButton.triggerHover;
|
|
1743
|
+
useCompositeListItem.useAnchoredPopupScrollLock(open && (menubarModal || popupModal), openMethod === 'touch', positionerElement, triggerElement);
|
|
1744
|
+
const element = useCompositeListItem.usePositioner(componentProps, state, {
|
|
1745
|
+
styles: positioner.positionerStyles,
|
|
1746
|
+
transitionStatus,
|
|
1747
|
+
props: elementProps,
|
|
1748
|
+
refs: [forwardedRef, store.useStateSetter('positionerElement')],
|
|
1749
|
+
hidden: !mounted,
|
|
1750
|
+
inert: !open
|
|
1716
1751
|
});
|
|
1717
1752
|
const shouldRenderBackdrop = mounted && parent.type !== 'menu' && (parent.type !== 'menubar' && modal && lastOpenChangeReason !== useButton.triggerHover || parent.type === 'menubar' && parent.context.modal);
|
|
1718
1753
|
|
|
@@ -1724,12 +1759,12 @@ const MenuPositioner = /*#__PURE__*/React__namespace.forwardRef(function MenuPos
|
|
|
1724
1759
|
backdropCutout = triggerElement;
|
|
1725
1760
|
}
|
|
1726
1761
|
return /*#__PURE__*/jsxRuntime.jsxs(MenuPositionerContext.Provider, {
|
|
1727
|
-
value:
|
|
1728
|
-
children: [shouldRenderBackdrop && /*#__PURE__*/jsxRuntime.jsx(
|
|
1762
|
+
value: positioner,
|
|
1763
|
+
children: [shouldRenderBackdrop && /*#__PURE__*/jsxRuntime.jsx(useScrollLock.InternalBackdrop, {
|
|
1729
1764
|
ref: parent.type === 'context-menu' || parent.type === 'nested-context-menu' ? parent.context.internalBackdropRef : null,
|
|
1730
|
-
inert:
|
|
1765
|
+
inert: useScrollLock.inertValue(!open),
|
|
1731
1766
|
cutout: backdropCutout
|
|
1732
|
-
}), /*#__PURE__*/jsxRuntime.jsx(
|
|
1767
|
+
}), /*#__PURE__*/jsxRuntime.jsx(useScrollLock.FloatingNode, {
|
|
1733
1768
|
id: floatingNodeId,
|
|
1734
1769
|
children: /*#__PURE__*/jsxRuntime.jsx(useCompositeListItem.CompositeList, {
|
|
1735
1770
|
elementsRef: store.context.itemDomElements,
|
|
@@ -1765,16 +1800,18 @@ const MenuRadioGroup = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__na
|
|
|
1765
1800
|
defaultValue,
|
|
1766
1801
|
onValueChange: onValueChangeProp,
|
|
1767
1802
|
disabled = false,
|
|
1803
|
+
style,
|
|
1804
|
+
'aria-labelledby': ariaLabelledByProp,
|
|
1768
1805
|
...elementProps
|
|
1769
1806
|
} = componentProps;
|
|
1807
|
+
const [labelId, setLabelId] = React__namespace.useState(undefined);
|
|
1770
1808
|
const [value, setValueUnwrapped] = useButton.useControlled({
|
|
1771
1809
|
controlled: valueProp,
|
|
1772
1810
|
default: defaultValue,
|
|
1773
1811
|
name: 'MenuRadioGroup'
|
|
1774
1812
|
});
|
|
1775
|
-
const onValueChange = useButton.useStableCallback(onValueChangeProp);
|
|
1776
1813
|
const setValue = useButton.useStableCallback((newValue, eventDetails) => {
|
|
1777
|
-
|
|
1814
|
+
onValueChangeProp?.(newValue, eventDetails);
|
|
1778
1815
|
if (eventDetails.isCanceled) {
|
|
1779
1816
|
return;
|
|
1780
1817
|
}
|
|
@@ -1788,6 +1825,7 @@ const MenuRadioGroup = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__na
|
|
|
1788
1825
|
ref: forwardedRef,
|
|
1789
1826
|
props: {
|
|
1790
1827
|
role: 'group',
|
|
1828
|
+
'aria-labelledby': ariaLabelledByProp ?? labelId,
|
|
1791
1829
|
'aria-disabled': disabled || undefined,
|
|
1792
1830
|
...elementProps
|
|
1793
1831
|
}
|
|
@@ -1797,9 +1835,12 @@ const MenuRadioGroup = /*#__PURE__*/React__namespace.memo(/*#__PURE__*/React__na
|
|
|
1797
1835
|
setValue,
|
|
1798
1836
|
disabled
|
|
1799
1837
|
}), [value, setValue, disabled]);
|
|
1800
|
-
return /*#__PURE__*/jsxRuntime.jsx(
|
|
1801
|
-
value:
|
|
1802
|
-
children:
|
|
1838
|
+
return /*#__PURE__*/jsxRuntime.jsx(MenuGroupContext.Provider, {
|
|
1839
|
+
value: setLabelId,
|
|
1840
|
+
children: /*#__PURE__*/jsxRuntime.jsx(MenuRadioGroupContext.Provider, {
|
|
1841
|
+
value: context,
|
|
1842
|
+
children: element
|
|
1843
|
+
})
|
|
1803
1844
|
});
|
|
1804
1845
|
}));
|
|
1805
1846
|
if (process.env.NODE_ENV !== "production") MenuRadioGroup.displayName = "MenuRadioGroup";
|
|
@@ -1824,6 +1865,7 @@ const MenuRadioItem = /*#__PURE__*/React__namespace.forwardRef(function MenuRadi
|
|
|
1824
1865
|
disabled: disabledProp = false,
|
|
1825
1866
|
closeOnClick = false,
|
|
1826
1867
|
value,
|
|
1868
|
+
style,
|
|
1827
1869
|
...elementProps
|
|
1828
1870
|
} = componentProps;
|
|
1829
1871
|
const listItem = useCompositeListItem.useCompositeListItem({
|
|
@@ -1853,7 +1895,7 @@ const MenuRadioItem = /*#__PURE__*/React__namespace.forwardRef(function MenuRadi
|
|
|
1853
1895
|
id,
|
|
1854
1896
|
store,
|
|
1855
1897
|
nativeButton,
|
|
1856
|
-
nodeId: menuPositionerContext?.nodeId,
|
|
1898
|
+
nodeId: menuPositionerContext?.context.nodeId,
|
|
1857
1899
|
itemMetadata: REGULAR_ITEM
|
|
1858
1900
|
});
|
|
1859
1901
|
const state = React__namespace.useMemo(() => ({
|
|
@@ -1861,13 +1903,12 @@ const MenuRadioItem = /*#__PURE__*/React__namespace.forwardRef(function MenuRadi
|
|
|
1861
1903
|
highlighted,
|
|
1862
1904
|
checked
|
|
1863
1905
|
}), [disabled, highlighted, checked]);
|
|
1864
|
-
|
|
1865
|
-
const details = {
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
};
|
|
1906
|
+
function handleClick(event) {
|
|
1907
|
+
const details = useButton.createChangeEventDetails(useButton.itemPress, event.nativeEvent, undefined, {
|
|
1908
|
+
preventUnmountOnClose() {}
|
|
1909
|
+
});
|
|
1869
1910
|
setSelectedValue(value, details);
|
|
1870
|
-
}
|
|
1911
|
+
}
|
|
1871
1912
|
const element = useRenderElement.useRenderElement('div', componentProps, {
|
|
1872
1913
|
state,
|
|
1873
1914
|
stateAttributesMapping: itemMapping,
|
|
@@ -1895,6 +1936,7 @@ const MenuRadioItemIndicator = /*#__PURE__*/React__namespace.forwardRef(function
|
|
|
1895
1936
|
const {
|
|
1896
1937
|
render,
|
|
1897
1938
|
className,
|
|
1939
|
+
style,
|
|
1898
1940
|
keepMounted = false,
|
|
1899
1941
|
...elementProps
|
|
1900
1942
|
} = componentProps;
|
|
@@ -1945,34 +1987,36 @@ function useMenubarContext(optional) {
|
|
|
1945
1987
|
|
|
1946
1988
|
const selectors = {
|
|
1947
1989
|
...ScrollAreaViewport.popupStoreSelectors,
|
|
1948
|
-
disabled:
|
|
1949
|
-
modal:
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1990
|
+
disabled: useScrollLock.createSelector(state => state.parent.type === 'menubar' ? state.parent.context.disabled || state.disabled : state.disabled),
|
|
1991
|
+
modal: useScrollLock.createSelector(state => (state.parent.type === undefined || state.parent.type === 'context-menu') && (state.modal ?? true)),
|
|
1992
|
+
openMethod: useScrollLock.createSelector(state => state.openMethod),
|
|
1993
|
+
allowMouseEnter: useScrollLock.createSelector(state => state.allowMouseEnter),
|
|
1994
|
+
highlightItemOnHover: useScrollLock.createSelector(state => state.highlightItemOnHover),
|
|
1995
|
+
stickIfOpen: useScrollLock.createSelector(state => state.stickIfOpen),
|
|
1996
|
+
parent: useScrollLock.createSelector(state => state.parent),
|
|
1997
|
+
rootId: useScrollLock.createSelector(state => {
|
|
1954
1998
|
if (state.parent.type === 'menu') {
|
|
1955
1999
|
return state.parent.store.select('rootId');
|
|
1956
2000
|
}
|
|
1957
2001
|
return state.parent.type !== undefined ? state.parent.context.rootId : state.rootId;
|
|
1958
2002
|
}),
|
|
1959
|
-
activeIndex:
|
|
1960
|
-
isActive:
|
|
1961
|
-
hoverEnabled:
|
|
1962
|
-
instantType:
|
|
1963
|
-
lastOpenChangeReason:
|
|
1964
|
-
floatingTreeRoot:
|
|
2003
|
+
activeIndex: useScrollLock.createSelector(state => state.activeIndex),
|
|
2004
|
+
isActive: useScrollLock.createSelector((state, itemIndex) => state.activeIndex === itemIndex),
|
|
2005
|
+
hoverEnabled: useScrollLock.createSelector(state => state.hoverEnabled),
|
|
2006
|
+
instantType: useScrollLock.createSelector(state => state.instantType),
|
|
2007
|
+
lastOpenChangeReason: useScrollLock.createSelector(state => state.openChangeReason),
|
|
2008
|
+
floatingTreeRoot: useScrollLock.createSelector(state => {
|
|
1965
2009
|
if (state.parent.type === 'menu') {
|
|
1966
2010
|
return state.parent.store.select('floatingTreeRoot');
|
|
1967
2011
|
}
|
|
1968
2012
|
return state.floatingTreeRoot;
|
|
1969
2013
|
}),
|
|
1970
|
-
floatingNodeId:
|
|
1971
|
-
floatingParentNodeId:
|
|
1972
|
-
itemProps:
|
|
1973
|
-
closeDelay:
|
|
1974
|
-
hasViewport:
|
|
1975
|
-
keyboardEventRelay:
|
|
2014
|
+
floatingNodeId: useScrollLock.createSelector(state => state.floatingNodeId),
|
|
2015
|
+
floatingParentNodeId: useScrollLock.createSelector(state => state.floatingParentNodeId),
|
|
2016
|
+
itemProps: useScrollLock.createSelector(state => state.itemProps),
|
|
2017
|
+
closeDelay: useScrollLock.createSelector(state => state.closeDelay),
|
|
2018
|
+
hasViewport: useScrollLock.createSelector(state => state.hasViewport),
|
|
2019
|
+
keyboardEventRelay: useScrollLock.createSelector(state => {
|
|
1976
2020
|
if (state.keyboardEventRelay) {
|
|
1977
2021
|
return state.keyboardEventRelay;
|
|
1978
2022
|
}
|
|
@@ -1982,7 +2026,7 @@ const selectors = {
|
|
|
1982
2026
|
return undefined;
|
|
1983
2027
|
})
|
|
1984
2028
|
};
|
|
1985
|
-
class MenuStore extends
|
|
2029
|
+
class MenuStore extends useScrollLock.ReactStore {
|
|
1986
2030
|
constructor(initialState) {
|
|
1987
2031
|
super({
|
|
1988
2032
|
...createInitialState(),
|
|
@@ -2005,7 +2049,7 @@ class MenuStore extends InternalBackdrop.ReactStore {
|
|
|
2005
2049
|
triggerFocusTargetRef: /*#__PURE__*/React__namespace.createRef(),
|
|
2006
2050
|
beforeContentFocusGuardRef: /*#__PURE__*/React__namespace.createRef(),
|
|
2007
2051
|
onOpenChangeComplete: undefined,
|
|
2008
|
-
triggerElements: new
|
|
2052
|
+
triggerElements: new useScrollLock.PopupTriggerMap()
|
|
2009
2053
|
}, selectors);
|
|
2010
2054
|
|
|
2011
2055
|
// Set up propagation of state from parent menu if applicable.
|
|
@@ -2056,7 +2100,9 @@ function createInitialState() {
|
|
|
2056
2100
|
...ScrollAreaViewport.createInitialPopupStoreState(),
|
|
2057
2101
|
disabled: false,
|
|
2058
2102
|
modal: true,
|
|
2103
|
+
openMethod: null,
|
|
2059
2104
|
allowMouseEnter: false,
|
|
2105
|
+
highlightItemOnHover: true,
|
|
2060
2106
|
stickIfOpen: true,
|
|
2061
2107
|
parent: {
|
|
2062
2108
|
type: undefined
|
|
@@ -2066,7 +2112,7 @@ function createInitialState() {
|
|
|
2066
2112
|
hoverEnabled: true,
|
|
2067
2113
|
instantType: undefined,
|
|
2068
2114
|
openChangeReason: null,
|
|
2069
|
-
floatingTreeRoot: new
|
|
2115
|
+
floatingTreeRoot: new useScrollLock.FloatingTreeStore(),
|
|
2070
2116
|
floatingNodeId: undefined,
|
|
2071
2117
|
floatingParentNodeId: null,
|
|
2072
2118
|
itemProps: useRenderElement.EMPTY_OBJECT,
|
|
@@ -2082,7 +2128,7 @@ function useMenuSubmenuRootContext() {
|
|
|
2082
2128
|
return React__namespace.useContext(MenuSubmenuRootContext);
|
|
2083
2129
|
}
|
|
2084
2130
|
|
|
2085
|
-
const MenuRoot =
|
|
2131
|
+
const MenuRoot = useScrollLock.fastComponent(function MenuRoot(props) {
|
|
2086
2132
|
const {
|
|
2087
2133
|
children,
|
|
2088
2134
|
open: openProp,
|
|
@@ -2138,46 +2184,19 @@ const MenuRoot = InternalBackdrop.fastComponent(function MenuRoot(props) {
|
|
|
2138
2184
|
triggerIdProp,
|
|
2139
2185
|
parent: parentFromContext
|
|
2140
2186
|
});
|
|
2141
|
-
|
|
2142
|
-
// Support initially open state when uncontrolled
|
|
2143
|
-
InternalBackdrop.useOnFirstRender(() => {
|
|
2144
|
-
if (openProp === undefined && store.state.open === false && defaultOpen === true) {
|
|
2145
|
-
store.update({
|
|
2146
|
-
open: true,
|
|
2147
|
-
activeTriggerId: defaultTriggerIdProp
|
|
2148
|
-
});
|
|
2149
|
-
}
|
|
2150
|
-
});
|
|
2187
|
+
useScrollLock.useInitialOpenSync(store, openProp, defaultOpen, defaultTriggerIdProp);
|
|
2151
2188
|
store.useControlledProp('openProp', openProp);
|
|
2152
2189
|
store.useControlledProp('triggerIdProp', triggerIdProp);
|
|
2153
2190
|
store.useContextCallback('onOpenChangeComplete', onOpenChangeComplete);
|
|
2191
|
+
const rootId = useButton.useId();
|
|
2192
|
+
const floatingId = useButton.useId();
|
|
2154
2193
|
const floatingTreeRoot = store.useState('floatingTreeRoot');
|
|
2155
|
-
const floatingNodeIdFromContext =
|
|
2156
|
-
const floatingParentNodeIdFromContext =
|
|
2157
|
-
useButton.useIsoLayoutEffect(() => {
|
|
2158
|
-
if (contextMenuContext && !parentMenuRootContext) {
|
|
2159
|
-
// This is a context menu root.
|
|
2160
|
-
// It doesn't support detached triggers yet, so we have to sync the parent context manually.
|
|
2161
|
-
store.update({
|
|
2162
|
-
parent: {
|
|
2163
|
-
type: 'context-menu',
|
|
2164
|
-
context: contextMenuContext
|
|
2165
|
-
},
|
|
2166
|
-
floatingNodeId: floatingNodeIdFromContext,
|
|
2167
|
-
floatingParentNodeId: floatingParentNodeIdFromContext
|
|
2168
|
-
});
|
|
2169
|
-
} else if (parentMenuRootContext) {
|
|
2170
|
-
store.update({
|
|
2171
|
-
floatingNodeId: floatingNodeIdFromContext,
|
|
2172
|
-
floatingParentNodeId: floatingParentNodeIdFromContext
|
|
2173
|
-
});
|
|
2174
|
-
}
|
|
2175
|
-
}, [contextMenuContext, parentMenuRootContext, floatingNodeIdFromContext, floatingParentNodeIdFromContext, store]);
|
|
2194
|
+
const floatingNodeIdFromContext = useScrollLock.useFloatingNodeId(floatingTreeRoot);
|
|
2195
|
+
const floatingParentNodeIdFromContext = useScrollLock.useFloatingParentNodeId();
|
|
2176
2196
|
const open = store.useState('open');
|
|
2177
2197
|
const activeTriggerElement = store.useState('activeTriggerElement');
|
|
2178
2198
|
const positionerElement = store.useState('positionerElement');
|
|
2179
2199
|
const hoverEnabled = store.useState('hoverEnabled');
|
|
2180
|
-
const modal = store.useState('modal');
|
|
2181
2200
|
const disabled = store.useState('disabled');
|
|
2182
2201
|
const lastOpenChangeReason = store.useState('lastOpenChangeReason');
|
|
2183
2202
|
const parent = store.useState('parent');
|
|
@@ -2185,33 +2204,55 @@ const MenuRoot = InternalBackdrop.fastComponent(function MenuRoot(props) {
|
|
|
2185
2204
|
const payload = store.useState('payload');
|
|
2186
2205
|
const floatingParentNodeId = store.useState('floatingParentNodeId');
|
|
2187
2206
|
const openEventRef = React__namespace.useRef(null);
|
|
2207
|
+
const allowOutsidePressDismissalRef = React__namespace.useRef(parent.type !== 'context-menu');
|
|
2208
|
+
const allowOutsidePressDismissalTimeout = useButton.useTimeout();
|
|
2209
|
+
const allowTouchToCloseRef = React__namespace.useRef(true);
|
|
2210
|
+
const allowTouchToCloseTimeout = useButton.useTimeout();
|
|
2188
2211
|
const nested = floatingParentNodeId != null;
|
|
2189
|
-
let floatingEvents;
|
|
2190
2212
|
if (process.env.NODE_ENV !== 'production') {
|
|
2191
2213
|
if (parent.type !== undefined && modalProp !== undefined) {
|
|
2192
2214
|
console.warn('Base UI: The `modal` prop is not supported on nested menus. It will be ignored.');
|
|
2193
2215
|
}
|
|
2194
2216
|
}
|
|
2217
|
+
const {
|
|
2218
|
+
openMethod,
|
|
2219
|
+
triggerProps: interactionTypeProps
|
|
2220
|
+
} = useScrollLock.useOpenInteractionType(open);
|
|
2195
2221
|
store.useSyncedValues({
|
|
2196
2222
|
disabled: disabledProp,
|
|
2223
|
+
highlightItemOnHover,
|
|
2197
2224
|
modal: parent.type === undefined ? modalProp : undefined,
|
|
2198
|
-
rootId: useButton.useId()
|
|
2199
|
-
});
|
|
2200
|
-
const {
|
|
2201
2225
|
openMethod,
|
|
2202
|
-
|
|
2203
|
-
}
|
|
2204
|
-
|
|
2226
|
+
rootId
|
|
2227
|
+
});
|
|
2228
|
+
useScrollLock.useImplicitActiveTrigger(store);
|
|
2205
2229
|
const {
|
|
2206
2230
|
forceUnmount
|
|
2207
|
-
} =
|
|
2231
|
+
} = useScrollLock.useOpenStateTransitions(open, store, () => {
|
|
2208
2232
|
store.update({
|
|
2209
2233
|
allowMouseEnter: false,
|
|
2210
2234
|
stickIfOpen: true
|
|
2211
2235
|
});
|
|
2212
2236
|
});
|
|
2213
|
-
|
|
2214
|
-
|
|
2237
|
+
useButton.useIsoLayoutEffect(() => {
|
|
2238
|
+
if (contextMenuContext && !parentMenuRootContext) {
|
|
2239
|
+
// This is a context menu root.
|
|
2240
|
+
// It doesn't support detached triggers yet, so we have to sync the parent context manually.
|
|
2241
|
+
store.update({
|
|
2242
|
+
parent: {
|
|
2243
|
+
type: 'context-menu',
|
|
2244
|
+
context: contextMenuContext
|
|
2245
|
+
},
|
|
2246
|
+
floatingNodeId: floatingNodeIdFromContext,
|
|
2247
|
+
floatingParentNodeId: floatingParentNodeIdFromContext
|
|
2248
|
+
});
|
|
2249
|
+
} else if (parentMenuRootContext) {
|
|
2250
|
+
store.update({
|
|
2251
|
+
floatingNodeId: floatingNodeIdFromContext,
|
|
2252
|
+
floatingParentNodeId: floatingParentNodeIdFromContext
|
|
2253
|
+
});
|
|
2254
|
+
}
|
|
2255
|
+
}, [contextMenuContext, parentMenuRootContext, floatingNodeIdFromContext, floatingParentNodeIdFromContext, store]);
|
|
2215
2256
|
React__namespace.useEffect(() => {
|
|
2216
2257
|
if (!open) {
|
|
2217
2258
|
openEventRef.current = null;
|
|
@@ -2232,22 +2273,17 @@ const MenuRoot = InternalBackdrop.fastComponent(function MenuRoot(props) {
|
|
|
2232
2273
|
allowOutsidePressDismissalRef.current = true;
|
|
2233
2274
|
});
|
|
2234
2275
|
}, [allowOutsidePressDismissalTimeout, open, parent.type]);
|
|
2235
|
-
InternalBackdrop.useScrollLock(open && modal && lastOpenChangeReason !== useButton.triggerHover && openMethod !== 'touch', positionerElement);
|
|
2236
2276
|
useButton.useIsoLayoutEffect(() => {
|
|
2237
2277
|
if (!open && !hoverEnabled) {
|
|
2238
2278
|
store.set('hoverEnabled', true);
|
|
2239
2279
|
}
|
|
2240
2280
|
}, [open, hoverEnabled, store]);
|
|
2241
|
-
const allowTouchToCloseRef = React__namespace.useRef(true);
|
|
2242
|
-
const allowTouchToCloseTimeout = useButton.useTimeout();
|
|
2243
2281
|
const setOpen = useButton.useStableCallback((nextOpen, eventDetails) => {
|
|
2244
2282
|
const reason = eventDetails.reason;
|
|
2245
2283
|
if (open === nextOpen && eventDetails.trigger === activeTriggerElement && lastOpenChangeReason === reason) {
|
|
2246
2284
|
return;
|
|
2247
2285
|
}
|
|
2248
|
-
|
|
2249
|
-
store.set('preventUnmountingOnClose', true);
|
|
2250
|
-
};
|
|
2286
|
+
const shouldPreventUnmountOnClose = useScrollLock.attachPreventUnmountOnClose(eventDetails);
|
|
2251
2287
|
|
|
2252
2288
|
// Do not immediately reset the activeTriggerId to allow
|
|
2253
2289
|
// exit animations to play and focus to be returned correctly.
|
|
@@ -2258,31 +2294,12 @@ const MenuRoot = InternalBackdrop.fastComponent(function MenuRoot(props) {
|
|
|
2258
2294
|
if (eventDetails.isCanceled) {
|
|
2259
2295
|
return;
|
|
2260
2296
|
}
|
|
2261
|
-
|
|
2262
|
-
open: nextOpen,
|
|
2263
|
-
nativeEvent: eventDetails.event,
|
|
2264
|
-
reason: eventDetails.reason,
|
|
2265
|
-
nested
|
|
2266
|
-
};
|
|
2267
|
-
floatingEvents?.emit('openchange', details);
|
|
2297
|
+
store.state.floatingRootContext.dispatchOpenChange(nextOpen, eventDetails);
|
|
2268
2298
|
const nativeEvent = eventDetails.event;
|
|
2269
2299
|
if (nextOpen === false && nativeEvent?.type === 'click' && nativeEvent.pointerType === 'touch' && !allowTouchToCloseRef.current) {
|
|
2270
2300
|
return;
|
|
2271
2301
|
}
|
|
2272
2302
|
|
|
2273
|
-
// Workaround `enableFocusInside` in Floating UI setting `tabindex=0` of a non-highlighted
|
|
2274
|
-
// option upon close when tabbing out due to `keepMounted=true`:
|
|
2275
|
-
// https://github.com/floating-ui/floating-ui/pull/3004/files#diff-962a7439cdeb09ea98d4b622a45d517bce07ad8c3f866e089bda05f4b0bbd875R194-R199
|
|
2276
|
-
// This otherwise causes options to retain `tabindex=0` incorrectly when the popup is closed
|
|
2277
|
-
// when tabbing outside.
|
|
2278
|
-
if (!nextOpen && activeIndex !== null) {
|
|
2279
|
-
const activeOption = store.context.itemDomElements.current[activeIndex];
|
|
2280
|
-
// Wait for Floating UI's focus effect to have fired
|
|
2281
|
-
queueMicrotask(() => {
|
|
2282
|
-
activeOption?.setAttribute('tabindex', '-1');
|
|
2283
|
-
});
|
|
2284
|
-
}
|
|
2285
|
-
|
|
2286
2303
|
// Prevent the menu from closing on mobile devices that have a delayed click event.
|
|
2287
2304
|
// In some cases the menu, when tapped, will fire the focus event first and then the click event.
|
|
2288
2305
|
// Without this guard, the menu will close immediately after opening.
|
|
@@ -2302,14 +2319,7 @@ const MenuRoot = InternalBackdrop.fastComponent(function MenuRoot(props) {
|
|
|
2302
2319
|
openChangeReason: reason
|
|
2303
2320
|
};
|
|
2304
2321
|
openEventRef.current = eventDetails.event ?? null;
|
|
2305
|
-
|
|
2306
|
-
// If a popup is closing, the `trigger` may be null.
|
|
2307
|
-
// We want to keep the previous value so that exit animations are played and focus is returned correctly.
|
|
2308
|
-
const newTriggerId = eventDetails.trigger?.id ?? null;
|
|
2309
|
-
if (newTriggerId || nextOpen) {
|
|
2310
|
-
updatedState.activeTriggerId = newTriggerId;
|
|
2311
|
-
updatedState.activeTriggerElement = eventDetails.trigger ?? null;
|
|
2312
|
-
}
|
|
2322
|
+
useScrollLock.setPopupOpenState(updatedState, nextOpen, eventDetails.trigger, shouldPreventUnmountOnClose());
|
|
2313
2323
|
store.update(updatedState);
|
|
2314
2324
|
if (parent.type === 'menubar' && (reason === useButton.triggerFocus || reason === useButton.focusOut || reason === useButton.triggerHover || reason === useButton.listNavigation || reason === useButton.siblingOpen)) {
|
|
2315
2325
|
store.set('instantType', 'group');
|
|
@@ -2319,16 +2329,26 @@ const MenuRoot = InternalBackdrop.fastComponent(function MenuRoot(props) {
|
|
|
2319
2329
|
store.set('instantType', undefined);
|
|
2320
2330
|
}
|
|
2321
2331
|
});
|
|
2322
|
-
const
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2332
|
+
const floatingRootContext = useScrollLock.useSyncedFloatingRootContext({
|
|
2333
|
+
popupStore: store,
|
|
2334
|
+
floatingId,
|
|
2335
|
+
nested: floatingParentNodeIdFromContext != null,
|
|
2336
|
+
onOpenChange: setOpen
|
|
2337
|
+
});
|
|
2338
|
+
const floatingEvents = floatingRootContext.context.events;
|
|
2339
|
+
React__namespace.useEffect(() => {
|
|
2340
|
+
const handleSetOpenEvent = ({
|
|
2341
|
+
open: nextOpen,
|
|
2342
|
+
eventDetails
|
|
2343
|
+
}) => setOpen(nextOpen, eventDetails);
|
|
2344
|
+
floatingEvents.on('setOpen', handleSetOpenEvent);
|
|
2345
|
+
return () => {
|
|
2346
|
+
floatingEvents?.off('setOpen', handleSetOpenEvent);
|
|
2326
2347
|
};
|
|
2327
|
-
|
|
2328
|
-
}, [store]);
|
|
2348
|
+
}, [floatingEvents, setOpen]);
|
|
2329
2349
|
const handleImperativeClose = React__namespace.useCallback(() => {
|
|
2330
|
-
store.setOpen(false,
|
|
2331
|
-
}, [store
|
|
2350
|
+
store.setOpen(false, useButton.createChangeEventDetails(useButton.imperativeAction));
|
|
2351
|
+
}, [store]);
|
|
2332
2352
|
React__namespace.useImperativeHandle(actionsRef, () => ({
|
|
2333
2353
|
unmount: forceUnmount,
|
|
2334
2354
|
close: handleImperativeClose
|
|
@@ -2341,22 +2361,7 @@ const MenuRoot = InternalBackdrop.fastComponent(function MenuRoot(props) {
|
|
|
2341
2361
|
React__namespace.useImperativeHandle(ctx?.actionsRef, () => ({
|
|
2342
2362
|
setOpen
|
|
2343
2363
|
}), [setOpen]);
|
|
2344
|
-
const
|
|
2345
|
-
popupStore: store,
|
|
2346
|
-
onOpenChange: setOpen
|
|
2347
|
-
});
|
|
2348
|
-
floatingEvents = floatingRootContext.context.events;
|
|
2349
|
-
React__namespace.useEffect(() => {
|
|
2350
|
-
const handleSetOpenEvent = ({
|
|
2351
|
-
open: nextOpen,
|
|
2352
|
-
eventDetails
|
|
2353
|
-
}) => setOpen(nextOpen, eventDetails);
|
|
2354
|
-
floatingEvents.on('setOpen', handleSetOpenEvent);
|
|
2355
|
-
return () => {
|
|
2356
|
-
floatingEvents?.off('setOpen', handleSetOpenEvent);
|
|
2357
|
-
};
|
|
2358
|
-
}, [floatingEvents, setOpen]);
|
|
2359
|
-
const dismiss = InternalBackdrop.useDismiss(floatingRootContext, {
|
|
2364
|
+
const dismiss = useScrollLock.useDismiss(floatingRootContext, {
|
|
2360
2365
|
enabled: !disabled,
|
|
2361
2366
|
bubbles: {
|
|
2362
2367
|
escapeKey: closeParentOnEsc && parent.type === 'menu'
|
|
@@ -2369,10 +2374,7 @@ const MenuRoot = InternalBackdrop.fastComponent(function MenuRoot(props) {
|
|
|
2369
2374
|
},
|
|
2370
2375
|
externalTree: nested ? floatingTreeRoot : undefined
|
|
2371
2376
|
});
|
|
2372
|
-
const
|
|
2373
|
-
role: 'menu'
|
|
2374
|
-
});
|
|
2375
|
-
const direction = InternalBackdrop.useDirection();
|
|
2377
|
+
const direction = useScrollLock.useDirection();
|
|
2376
2378
|
const setActiveIndex = React__namespace.useCallback(index => {
|
|
2377
2379
|
if (store.select('activeIndex') === index) {
|
|
2378
2380
|
return;
|
|
@@ -2394,47 +2396,42 @@ const MenuRoot = InternalBackdrop.fastComponent(function MenuRoot(props) {
|
|
|
2394
2396
|
externalTree: nested ? floatingTreeRoot : undefined,
|
|
2395
2397
|
focusItemOnHover: highlightItemOnHover
|
|
2396
2398
|
});
|
|
2397
|
-
const
|
|
2399
|
+
const onTyping = React__namespace.useCallback(nextTyping => {
|
|
2398
2400
|
store.context.typingRef.current = nextTyping;
|
|
2399
2401
|
}, [store]);
|
|
2400
2402
|
const typeahead = useCompositeListItem.useTypeahead(floatingRootContext, {
|
|
2403
|
+
enabled: !disabled,
|
|
2401
2404
|
listRef: store.context.itemLabels,
|
|
2402
2405
|
elementsRef: store.context.itemDomElements,
|
|
2403
2406
|
activeIndex,
|
|
2404
|
-
resetMs:
|
|
2407
|
+
resetMs: useScrollLock.TYPEAHEAD_RESET_MS,
|
|
2405
2408
|
onMatch: index => {
|
|
2406
2409
|
if (open && index !== activeIndex) {
|
|
2407
2410
|
store.set('activeIndex', index);
|
|
2408
2411
|
}
|
|
2409
2412
|
},
|
|
2410
|
-
|
|
2413
|
+
onTyping
|
|
2411
2414
|
});
|
|
2412
|
-
const {
|
|
2413
|
-
getReferenceProps,
|
|
2414
|
-
getFloatingProps,
|
|
2415
|
-
getItemProps,
|
|
2416
|
-
getTriggerProps
|
|
2417
|
-
} = InternalBackdrop.useInteractions([dismiss, role, listNavigation, typeahead]);
|
|
2418
2415
|
const activeTriggerProps = React__namespace.useMemo(() => {
|
|
2419
|
-
const mergedProps = useRenderElement.mergeProps(
|
|
2416
|
+
const mergedProps = useRenderElement.mergeProps(typeahead.reference, listNavigation.reference, dismiss.reference, {
|
|
2420
2417
|
onMouseMove() {
|
|
2421
2418
|
store.set('allowMouseEnter', true);
|
|
2422
2419
|
}
|
|
2423
2420
|
}, interactionTypeProps);
|
|
2424
|
-
|
|
2421
|
+
mergedProps['aria-haspopup'] = 'menu';
|
|
2422
|
+
mergedProps['aria-expanded'] = open;
|
|
2425
2423
|
return mergedProps;
|
|
2426
|
-
}, [
|
|
2424
|
+
}, [store, typeahead.reference, listNavigation.reference, dismiss.reference, interactionTypeProps, open]);
|
|
2427
2425
|
const inactiveTriggerProps = React__namespace.useMemo(() => {
|
|
2428
|
-
const
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
}
|
|
2432
|
-
const mergedProps = useRenderElement.mergeProps(triggerProps, interactionTypeProps);
|
|
2433
|
-
delete mergedProps.role;
|
|
2434
|
-
delete mergedProps['aria-controls'];
|
|
2426
|
+
const mergedProps = useRenderElement.mergeProps(listNavigation.trigger, dismiss.trigger, interactionTypeProps);
|
|
2427
|
+
mergedProps['aria-haspopup'] = 'menu';
|
|
2428
|
+
mergedProps['aria-expanded'] = false;
|
|
2435
2429
|
return mergedProps;
|
|
2436
|
-
}, [
|
|
2437
|
-
const popupProps = React__namespace.useMemo(() =>
|
|
2430
|
+
}, [listNavigation.trigger, dismiss.trigger, interactionTypeProps]);
|
|
2431
|
+
const popupProps = React__namespace.useMemo(() => useRenderElement.mergeProps(useScrollLock.FOCUSABLE_POPUP_PROPS, {
|
|
2432
|
+
id: floatingId,
|
|
2433
|
+
role: 'menu',
|
|
2434
|
+
'aria-labelledby': activeTriggerElement?.id,
|
|
2438
2435
|
onMouseMove() {
|
|
2439
2436
|
store.set('allowMouseEnter', true);
|
|
2440
2437
|
if (parent.type === 'menu') {
|
|
@@ -2455,9 +2452,9 @@ const MenuRoot = InternalBackdrop.fastComponent(function MenuRoot(props) {
|
|
|
2455
2452
|
relay(event);
|
|
2456
2453
|
}
|
|
2457
2454
|
}
|
|
2458
|
-
}), [
|
|
2459
|
-
const itemProps =
|
|
2460
|
-
|
|
2455
|
+
}, typeahead.floating, listNavigation.floating, dismiss.floating), [activeTriggerElement, floatingId, parent.type, store, typeahead.floating, listNavigation.floating, dismiss.floating]);
|
|
2456
|
+
const itemProps = listNavigation.item ?? useRenderElement.EMPTY_OBJECT;
|
|
2457
|
+
useScrollLock.usePopupInteractionProps(store, {
|
|
2461
2458
|
floatingRootContext,
|
|
2462
2459
|
activeTriggerProps,
|
|
2463
2460
|
inactiveTriggerProps,
|
|
@@ -2476,7 +2473,7 @@ const MenuRoot = InternalBackdrop.fastComponent(function MenuRoot(props) {
|
|
|
2476
2473
|
});
|
|
2477
2474
|
if (parent.type === undefined || parent.type === 'context-menu') {
|
|
2478
2475
|
// set up a FloatingTree to provide the context to nested menus
|
|
2479
|
-
return /*#__PURE__*/jsxRuntime.jsx(
|
|
2476
|
+
return /*#__PURE__*/jsxRuntime.jsx(useScrollLock.FloatingTree, {
|
|
2480
2477
|
externalTree: floatingTreeRoot,
|
|
2481
2478
|
children: content
|
|
2482
2479
|
});
|
|
@@ -2487,7 +2484,7 @@ if (process.env.NODE_ENV !== "production") MenuRoot.displayName = "MenuRoot";
|
|
|
2487
2484
|
|
|
2488
2485
|
/**
|
|
2489
2486
|
* Groups all parts of a submenu.
|
|
2490
|
-
* Doesn
|
|
2487
|
+
* Doesn't render its own HTML element.
|
|
2491
2488
|
*
|
|
2492
2489
|
* Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
|
|
2493
2490
|
*/
|
|
@@ -2517,7 +2514,7 @@ function useCompositeItem(params = {}) {
|
|
|
2517
2514
|
const isHighlighted = highlightedIndex === index;
|
|
2518
2515
|
const itemRef = React__namespace.useRef(null);
|
|
2519
2516
|
const mergedRef = useRenderElement.useMergedRefs(ref, itemRef);
|
|
2520
|
-
const compositeProps =
|
|
2517
|
+
const compositeProps = {
|
|
2521
2518
|
tabIndex: isHighlighted ? 0 : -1,
|
|
2522
2519
|
onFocus() {
|
|
2523
2520
|
onHighlightedIndexChange(index);
|
|
@@ -2532,7 +2529,7 @@ function useCompositeItem(params = {}) {
|
|
|
2532
2529
|
item.focus();
|
|
2533
2530
|
}
|
|
2534
2531
|
}
|
|
2535
|
-
}
|
|
2532
|
+
};
|
|
2536
2533
|
return {
|
|
2537
2534
|
compositeProps,
|
|
2538
2535
|
compositeRef: mergedRef,
|
|
@@ -2547,6 +2544,7 @@ function CompositeItem(componentProps) {
|
|
|
2547
2544
|
const {
|
|
2548
2545
|
render,
|
|
2549
2546
|
className,
|
|
2547
|
+
style,
|
|
2550
2548
|
state = useRenderElement.EMPTY_OBJECT,
|
|
2551
2549
|
props = useRenderElement.EMPTY_ARRAY,
|
|
2552
2550
|
refs = useRenderElement.EMPTY_ARRAY,
|
|
@@ -2579,6 +2577,53 @@ function findRootOwnerId(node) {
|
|
|
2579
2577
|
return findRootOwnerId(floatingUi_utils_dom.getParentNode(node));
|
|
2580
2578
|
}
|
|
2581
2579
|
|
|
2580
|
+
/**
|
|
2581
|
+
* Minimal store interface required by the focus guard hook.
|
|
2582
|
+
* Both PopoverStore and MenuStore satisfy this interface.
|
|
2583
|
+
*/
|
|
2584
|
+
|
|
2585
|
+
/**
|
|
2586
|
+
* Provides focus guard handlers for popup triggers (Popover, Menu).
|
|
2587
|
+
*
|
|
2588
|
+
* When the popup is open, invisible focus guard elements are placed before and after
|
|
2589
|
+
* the trigger. These handlers close the popup and move focus to the appropriate
|
|
2590
|
+
* tabbable element when the guards receive focus (i.e. when the user tabs out).
|
|
2591
|
+
*/
|
|
2592
|
+
function useTriggerFocusGuards(store, triggerElementRef) {
|
|
2593
|
+
const preFocusGuardRef = React__namespace.useRef(null);
|
|
2594
|
+
function handlePreFocusGuardFocus(event) {
|
|
2595
|
+
ReactDOM__namespace.flushSync(() => {
|
|
2596
|
+
store.setOpen(false, useButton.createChangeEventDetails(useButton.focusOut, event.nativeEvent, event.currentTarget));
|
|
2597
|
+
});
|
|
2598
|
+
const previousTabbable = useScrollLock.getTabbableBeforeElement(preFocusGuardRef.current);
|
|
2599
|
+
previousTabbable?.focus();
|
|
2600
|
+
}
|
|
2601
|
+
function handleFocusTargetFocus(event) {
|
|
2602
|
+
const positionerElement = store.select('positionerElement');
|
|
2603
|
+
if (positionerElement && useScrollLock.isOutsideEvent(event, positionerElement)) {
|
|
2604
|
+
store.context.beforeContentFocusGuardRef.current?.focus();
|
|
2605
|
+
} else {
|
|
2606
|
+
ReactDOM__namespace.flushSync(() => {
|
|
2607
|
+
store.setOpen(false, useButton.createChangeEventDetails(useButton.focusOut, event.nativeEvent, event.currentTarget));
|
|
2608
|
+
});
|
|
2609
|
+
let nextTabbable = useScrollLock.getTabbableAfterElement(store.context.triggerFocusTargetRef.current || triggerElementRef.current);
|
|
2610
|
+
while (nextTabbable !== null && useButton.contains(positionerElement, nextTabbable)) {
|
|
2611
|
+
const prevTabbable = nextTabbable;
|
|
2612
|
+
nextTabbable = useScrollLock.getNextTabbable(nextTabbable);
|
|
2613
|
+
if (nextTabbable === prevTabbable) {
|
|
2614
|
+
break;
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
nextTabbable?.focus();
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
return {
|
|
2621
|
+
preFocusGuardRef,
|
|
2622
|
+
handlePreFocusGuardFocus,
|
|
2623
|
+
handleFocusTargetFocus
|
|
2624
|
+
};
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2582
2627
|
/**
|
|
2583
2628
|
* Returns `click` and `mousedown` handlers that fix the behavior of triggers of popups that are toggled by different events.
|
|
2584
2629
|
* For example, a button that opens a popup on mousedown and closes it on click.
|
|
@@ -2624,10 +2669,11 @@ const BOUNDARY_OFFSET = 2;
|
|
|
2624
2669
|
*
|
|
2625
2670
|
* Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
|
|
2626
2671
|
*/
|
|
2627
|
-
const MenuTrigger =
|
|
2672
|
+
const MenuTrigger = useScrollLock.fastComponentRef(function MenuTrigger(componentProps, forwardedRef) {
|
|
2628
2673
|
const {
|
|
2629
2674
|
render,
|
|
2630
2675
|
className,
|
|
2676
|
+
style,
|
|
2631
2677
|
disabled: disabledProp = false,
|
|
2632
2678
|
nativeButton = true,
|
|
2633
2679
|
id: idProp,
|
|
@@ -2647,19 +2693,20 @@ const MenuTrigger = InternalBackdrop.fastComponentRef(function MenuTrigger(compo
|
|
|
2647
2693
|
const isTriggerActive = store.useState('isTriggerActive', thisTriggerId);
|
|
2648
2694
|
const floatingRootContext = store.useState('floatingRootContext');
|
|
2649
2695
|
const isOpenedByThisTrigger = store.useState('isOpenedByTrigger', thisTriggerId);
|
|
2696
|
+
const popupId = store.useState('triggerPopupId', thisTriggerId);
|
|
2650
2697
|
const triggerElementRef = React__namespace.useRef(null);
|
|
2651
2698
|
const parent = useMenuParent();
|
|
2652
2699
|
const compositeRootContext = useButton.useCompositeRootContext(true);
|
|
2653
|
-
const floatingTreeRootFromContext =
|
|
2700
|
+
const floatingTreeRootFromContext = useScrollLock.useFloatingTree();
|
|
2654
2701
|
const floatingTreeRoot = React__namespace.useMemo(() => {
|
|
2655
|
-
return floatingTreeRootFromContext ?? new
|
|
2702
|
+
return floatingTreeRootFromContext ?? new useScrollLock.FloatingTreeStore();
|
|
2656
2703
|
}, [floatingTreeRootFromContext]);
|
|
2657
|
-
const floatingNodeId =
|
|
2658
|
-
const floatingParentNodeId =
|
|
2704
|
+
const floatingNodeId = useScrollLock.useFloatingNodeId(floatingTreeRoot);
|
|
2705
|
+
const floatingParentNodeId = useScrollLock.useFloatingParentNodeId();
|
|
2659
2706
|
const {
|
|
2660
2707
|
registerTrigger,
|
|
2661
2708
|
isMountedByThisTrigger
|
|
2662
|
-
} =
|
|
2709
|
+
} = useScrollLock.useTriggerDataForwarding(thisTriggerId, triggerElementRef, store, {
|
|
2663
2710
|
payload,
|
|
2664
2711
|
closeDelay,
|
|
2665
2712
|
parent,
|
|
@@ -2730,14 +2777,15 @@ const MenuTrigger = InternalBackdrop.fastComponentRef(function MenuTrigger(compo
|
|
|
2730
2777
|
},
|
|
2731
2778
|
triggerElementRef,
|
|
2732
2779
|
externalTree: floatingTreeRoot,
|
|
2733
|
-
isActiveTrigger: isTriggerActive
|
|
2780
|
+
isActiveTrigger: isTriggerActive,
|
|
2781
|
+
isClosing: () => store.select('transitionStatus') === 'ending'
|
|
2734
2782
|
});
|
|
2735
2783
|
|
|
2736
2784
|
// Whether to ignore clicks to open the menu.
|
|
2737
2785
|
// `lastOpenChangeReason` doesn't need to be reactive here, as we need to run this
|
|
2738
2786
|
// only when `isOpenedByThisTrigger` changes.
|
|
2739
2787
|
const stickIfOpen = useStickIfOpen(isOpenedByThisTrigger, store.select('lastOpenChangeReason'));
|
|
2740
|
-
const click =
|
|
2788
|
+
const click = useScrollLock.useClick(floatingRootContext, {
|
|
2741
2789
|
enabled: !disabled && parent.type !== 'context-menu',
|
|
2742
2790
|
event: isOpenedByThisTrigger && isInMenubar ? 'click' : 'mousedown',
|
|
2743
2791
|
toggle: true,
|
|
@@ -2752,15 +2800,21 @@ const MenuTrigger = InternalBackdrop.fastComponentRef(function MenuTrigger(compo
|
|
|
2752
2800
|
enabled: isInMenubar,
|
|
2753
2801
|
mouseDownAction: 'open'
|
|
2754
2802
|
});
|
|
2755
|
-
const localInteractionProps =
|
|
2803
|
+
const localInteractionProps = React__namespace.useMemo(() => useRenderElement.mergeProps(focus.reference, click.reference), [focus.reference, click.reference]);
|
|
2804
|
+
const rootTriggerProps = store.useState('triggerProps', isMountedByThisTrigger);
|
|
2805
|
+
const {
|
|
2806
|
+
preFocusGuardRef,
|
|
2807
|
+
handlePreFocusGuardFocus,
|
|
2808
|
+
handleFocusTargetFocus
|
|
2809
|
+
} = useTriggerFocusGuards(store, triggerElementRef);
|
|
2756
2810
|
const state = {
|
|
2757
2811
|
disabled,
|
|
2758
2812
|
open: isOpenedByThisTrigger
|
|
2759
2813
|
};
|
|
2760
|
-
const rootTriggerProps = store.useState('triggerProps', isMountedByThisTrigger);
|
|
2761
2814
|
const ref = [triggerRef, forwardedRef, buttonRef, registerTrigger, triggerElementRef];
|
|
2762
|
-
const props = [localInteractionProps
|
|
2815
|
+
const props = [localInteractionProps, hoverProps ?? useRenderElement.EMPTY_OBJECT, rootTriggerProps, {
|
|
2763
2816
|
'aria-haspopup': 'menu',
|
|
2817
|
+
'aria-controls': popupId,
|
|
2764
2818
|
id: thisTriggerId,
|
|
2765
2819
|
onMouseDown: event => {
|
|
2766
2820
|
if (store.select('open')) {
|
|
@@ -2779,36 +2833,9 @@ const MenuTrigger = InternalBackdrop.fastComponentRef(function MenuTrigger(compo
|
|
|
2779
2833
|
}, isInMenubar ? {
|
|
2780
2834
|
role: 'menuitem'
|
|
2781
2835
|
} : {}, mixedToggleHandlers, elementProps, getButtonProps];
|
|
2782
|
-
const preFocusGuardRef = React__namespace.useRef(null);
|
|
2783
|
-
const handlePreFocusGuardFocus = useButton.useStableCallback(event => {
|
|
2784
|
-
ReactDOM__namespace.flushSync(() => {
|
|
2785
|
-
store.setOpen(false, useButton.createChangeEventDetails(useButton.focusOut, event.nativeEvent, event.currentTarget));
|
|
2786
|
-
});
|
|
2787
|
-
const previousTabbable = InternalBackdrop.getTabbableBeforeElement(preFocusGuardRef.current);
|
|
2788
|
-
previousTabbable?.focus();
|
|
2789
|
-
});
|
|
2790
|
-
const handleFocusTargetFocus = useButton.useStableCallback(event => {
|
|
2791
|
-
const currentPositionerElement = store.select('positionerElement');
|
|
2792
|
-
if (currentPositionerElement && InternalBackdrop.isOutsideEvent(event, currentPositionerElement)) {
|
|
2793
|
-
store.context.beforeContentFocusGuardRef.current?.focus();
|
|
2794
|
-
} else {
|
|
2795
|
-
ReactDOM__namespace.flushSync(() => {
|
|
2796
|
-
store.setOpen(false, useButton.createChangeEventDetails(useButton.focusOut, event.nativeEvent, event.currentTarget));
|
|
2797
|
-
});
|
|
2798
|
-
let nextTabbable = InternalBackdrop.getTabbableAfterElement(store.context.triggerFocusTargetRef.current || triggerElementRef.current);
|
|
2799
|
-
while (nextTabbable !== null && useButton.contains(currentPositionerElement, nextTabbable)) {
|
|
2800
|
-
const prevTabbable = nextTabbable;
|
|
2801
|
-
nextTabbable = InternalBackdrop.getNextTabbable(nextTabbable);
|
|
2802
|
-
if (nextTabbable === prevTabbable) {
|
|
2803
|
-
break;
|
|
2804
|
-
}
|
|
2805
|
-
}
|
|
2806
|
-
nextTabbable?.focus();
|
|
2807
|
-
}
|
|
2808
|
-
});
|
|
2809
2836
|
const element = useRenderElement.useRenderElement('button', componentProps, {
|
|
2810
2837
|
enabled: !isInMenubar,
|
|
2811
|
-
stateAttributesMapping:
|
|
2838
|
+
stateAttributesMapping: useScrollLock.pressableTriggerOpenStateMapping,
|
|
2812
2839
|
state,
|
|
2813
2840
|
ref,
|
|
2814
2841
|
props
|
|
@@ -2818,10 +2845,11 @@ const MenuTrigger = InternalBackdrop.fastComponentRef(function MenuTrigger(compo
|
|
|
2818
2845
|
tag: "button",
|
|
2819
2846
|
render: render,
|
|
2820
2847
|
className: className,
|
|
2848
|
+
style: style,
|
|
2821
2849
|
state: state,
|
|
2822
2850
|
refs: ref,
|
|
2823
2851
|
props: props,
|
|
2824
|
-
stateAttributesMapping:
|
|
2852
|
+
stateAttributesMapping: useScrollLock.pressableTriggerOpenStateMapping
|
|
2825
2853
|
});
|
|
2826
2854
|
}
|
|
2827
2855
|
|
|
@@ -2830,12 +2858,12 @@ const MenuTrigger = InternalBackdrop.fastComponentRef(function MenuTrigger(compo
|
|
|
2830
2858
|
|
|
2831
2859
|
if (isOpenedByThisTrigger) {
|
|
2832
2860
|
return /*#__PURE__*/jsxRuntime.jsxs(React__namespace.Fragment, {
|
|
2833
|
-
children: [/*#__PURE__*/jsxRuntime.jsx(
|
|
2861
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(useScrollLock.FocusGuard, {
|
|
2834
2862
|
ref: preFocusGuardRef,
|
|
2835
2863
|
onFocus: handlePreFocusGuardFocus
|
|
2836
2864
|
}, `${thisTriggerId}-pre-focus-guard`), /*#__PURE__*/jsxRuntime.jsx(React__namespace.Fragment, {
|
|
2837
2865
|
children: element
|
|
2838
|
-
}, thisTriggerId), /*#__PURE__*/jsxRuntime.jsx(
|
|
2866
|
+
}, thisTriggerId), /*#__PURE__*/jsxRuntime.jsx(useScrollLock.FocusGuard, {
|
|
2839
2867
|
ref: store.context.triggerFocusTargetRef,
|
|
2840
2868
|
onFocus: handleFocusTargetFocus
|
|
2841
2869
|
}, `${thisTriggerId}-post-focus-guard`)]
|
|
@@ -2857,7 +2885,7 @@ function useStickIfOpen(open, openReason) {
|
|
|
2857
2885
|
// Only allow "patient" clicks to close the menu if it's open.
|
|
2858
2886
|
// If they clicked within 500ms of the menu opening, keep it open.
|
|
2859
2887
|
setStickIfOpen(true);
|
|
2860
|
-
stickIfOpenTimeout.start(
|
|
2888
|
+
stickIfOpenTimeout.start(useScrollLock.PATIENT_CLICK_THRESHOLD, () => {
|
|
2861
2889
|
setStickIfOpen(false);
|
|
2862
2890
|
});
|
|
2863
2891
|
} else if (!open) {
|
|
@@ -2901,10 +2929,11 @@ function useMenuParent() {
|
|
|
2901
2929
|
*
|
|
2902
2930
|
* Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
|
|
2903
2931
|
*/
|
|
2904
|
-
const MenuSubmenuTrigger = /*#__PURE__*/React__namespace.forwardRef(function
|
|
2932
|
+
const MenuSubmenuTrigger = /*#__PURE__*/React__namespace.forwardRef(function MenuSubmenuTrigger(componentProps, forwardedRef) {
|
|
2905
2933
|
const {
|
|
2906
2934
|
render,
|
|
2907
2935
|
className,
|
|
2936
|
+
style,
|
|
2908
2937
|
label,
|
|
2909
2938
|
id: idProp,
|
|
2910
2939
|
nativeButton = false,
|
|
@@ -2914,7 +2943,9 @@ const MenuSubmenuTrigger = /*#__PURE__*/React__namespace.forwardRef(function Sub
|
|
|
2914
2943
|
disabled: disabledProp = false,
|
|
2915
2944
|
...elementProps
|
|
2916
2945
|
} = componentProps;
|
|
2917
|
-
const listItem = useCompositeListItem.useCompositeListItem(
|
|
2946
|
+
const listItem = useCompositeListItem.useCompositeListItem({
|
|
2947
|
+
label
|
|
2948
|
+
});
|
|
2918
2949
|
const menuPositionerContext = useMenuPositionerContext();
|
|
2919
2950
|
const {
|
|
2920
2951
|
store
|
|
@@ -2923,7 +2954,8 @@ const MenuSubmenuTrigger = /*#__PURE__*/React__namespace.forwardRef(function Sub
|
|
|
2923
2954
|
const open = store.useState('open');
|
|
2924
2955
|
const floatingRootContext = store.useState('floatingRootContext');
|
|
2925
2956
|
const floatingTreeRoot = store.useState('floatingTreeRoot');
|
|
2926
|
-
const
|
|
2957
|
+
const popupId = store.useState('triggerPopupId', thisTriggerId);
|
|
2958
|
+
const baseRegisterTrigger = useScrollLock.useTriggerRegistration(thisTriggerId, store);
|
|
2927
2959
|
const registerTrigger = React__namespace.useCallback(element => {
|
|
2928
2960
|
const cleanup = baseRegisterTrigger(element);
|
|
2929
2961
|
if (element !== null && store.select('open') && store.select('activeTriggerId') == null) {
|
|
@@ -2946,16 +2978,29 @@ const MenuSubmenuTrigger = /*#__PURE__*/React__namespace.forwardRef(function Sub
|
|
|
2946
2978
|
}
|
|
2947
2979
|
store.useSyncedValue('closeDelay', closeDelay);
|
|
2948
2980
|
const parentMenuStore = submenuRootContext.parentMenu;
|
|
2981
|
+
const rootDisabled = store.useState('disabled');
|
|
2982
|
+
const parentDisabled = parentMenuStore.useState('disabled');
|
|
2983
|
+
const disabled = disabledProp || rootDisabled || parentDisabled;
|
|
2984
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
2985
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
2986
|
+
useButton.useIsoLayoutEffect(() => {
|
|
2987
|
+
const element = triggerElementRef.current;
|
|
2988
|
+
if (element && isElementDisabled.isElementDisabled(element) && !disabled) {
|
|
2989
|
+
const ownerStackMessage = useButton.SafeReact.captureOwnerStack?.() || '';
|
|
2990
|
+
useRenderElement.warn(`A disabled element was detected on <Menu.SubmenuTrigger>. To properly disable the trigger, use the \`disabled\` prop on the component instead of setting it on the rendered element.${ownerStackMessage}`);
|
|
2991
|
+
}
|
|
2992
|
+
});
|
|
2993
|
+
}
|
|
2949
2994
|
const itemProps = parentMenuStore.useState('itemProps');
|
|
2950
2995
|
const highlighted = parentMenuStore.useState('isActive', listItem.index);
|
|
2951
2996
|
const itemMetadata = React__namespace.useMemo(() => ({
|
|
2952
2997
|
type: 'submenu-trigger',
|
|
2953
2998
|
setActive() {
|
|
2954
|
-
parentMenuStore.
|
|
2999
|
+
if (parentMenuStore.select('highlightItemOnHover')) {
|
|
3000
|
+
parentMenuStore.set('activeIndex', listItem.index);
|
|
3001
|
+
}
|
|
2955
3002
|
}
|
|
2956
3003
|
}), [parentMenuStore, listItem.index]);
|
|
2957
|
-
const rootDisabled = store.useState('disabled');
|
|
2958
|
-
const disabled = disabledProp || rootDisabled;
|
|
2959
3004
|
const {
|
|
2960
3005
|
getItemProps,
|
|
2961
3006
|
itemRef
|
|
@@ -2968,10 +3013,9 @@ const MenuSubmenuTrigger = /*#__PURE__*/React__namespace.forwardRef(function Sub
|
|
|
2968
3013
|
typingRef: parentMenuStore.context.typingRef,
|
|
2969
3014
|
nativeButton,
|
|
2970
3015
|
itemMetadata,
|
|
2971
|
-
nodeId: menuPositionerContext?.nodeId
|
|
3016
|
+
nodeId: menuPositionerContext?.context.nodeId
|
|
2972
3017
|
});
|
|
2973
3018
|
const hoverEnabled = store.useState('hoverEnabled');
|
|
2974
|
-
const allowMouseEnter = parentMenuStore.useState('allowMouseEnter');
|
|
2975
3019
|
const hoverProps = useHoverReferenceInteraction(floatingRootContext, {
|
|
2976
3020
|
enabled: hoverEnabled && openOnHover && !disabled,
|
|
2977
3021
|
handleClose: safePolygon({
|
|
@@ -2980,21 +3024,23 @@ const MenuSubmenuTrigger = /*#__PURE__*/React__namespace.forwardRef(function Sub
|
|
|
2980
3024
|
mouseOnly: true,
|
|
2981
3025
|
move: true,
|
|
2982
3026
|
restMs: delay,
|
|
2983
|
-
delay:
|
|
3027
|
+
delay: {
|
|
2984
3028
|
open: delay,
|
|
2985
3029
|
close: closeDelay
|
|
2986
|
-
}
|
|
3030
|
+
},
|
|
3031
|
+
shouldOpen: delay > 0 ? () => parentMenuStore.select('allowMouseEnter') : undefined,
|
|
2987
3032
|
triggerElementRef,
|
|
2988
|
-
externalTree: floatingTreeRoot
|
|
3033
|
+
externalTree: floatingTreeRoot,
|
|
3034
|
+
isClosing: () => store.select('transitionStatus') === 'ending'
|
|
2989
3035
|
});
|
|
2990
|
-
const click =
|
|
3036
|
+
const click = useScrollLock.useClick(floatingRootContext, {
|
|
2991
3037
|
enabled: !disabled,
|
|
2992
3038
|
event: 'mousedown',
|
|
2993
3039
|
toggle: !openOnHover,
|
|
2994
3040
|
ignoreMouse: openOnHover,
|
|
2995
3041
|
stickIfOpen: false
|
|
2996
3042
|
});
|
|
2997
|
-
const localInteractionProps =
|
|
3043
|
+
const localInteractionProps = click.reference ?? useRenderElement.EMPTY_OBJECT;
|
|
2998
3044
|
const rootTriggerProps = store.useState('triggerProps', true);
|
|
2999
3045
|
delete rootTriggerProps.id;
|
|
3000
3046
|
const state = {
|
|
@@ -3004,8 +3050,9 @@ const MenuSubmenuTrigger = /*#__PURE__*/React__namespace.forwardRef(function Sub
|
|
|
3004
3050
|
};
|
|
3005
3051
|
const element = useRenderElement.useRenderElement('div', componentProps, {
|
|
3006
3052
|
state,
|
|
3007
|
-
stateAttributesMapping:
|
|
3008
|
-
props: [localInteractionProps
|
|
3053
|
+
stateAttributesMapping: useScrollLock.triggerOpenStateMapping,
|
|
3054
|
+
props: [localInteractionProps, hoverProps, rootTriggerProps, itemProps, {
|
|
3055
|
+
'aria-controls': popupId,
|
|
3009
3056
|
tabIndex: open || highlighted ? 0 : -1,
|
|
3010
3057
|
onBlur() {
|
|
3011
3058
|
if (highlighted) {
|