@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
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { a as useRefWithInit, f as formatErrorMessage, u as useRenderElement, m as mergeProps,
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { a as useRefWithInit, f as formatErrorMessage, u as useRenderElement, m as mergeProps, c as useMergedRefs, E as EMPTY_OBJECT, b as EMPTY_ARRAY, w as warn } from './useRenderElement-es.js';
|
|
3
|
+
import { X as mergeCleanups, Y as isTypeableElement, Z as matchesFocusVisible, _ as isTargetInsideEnabledTrigger, $ as createAttribute, J as webkit, d as useFloatingTree, u as useFloatingParentNodeId, a0 as isInteractiveElement, a1 as getNodeChildren, p as popupStateMapping, a as FloatingFocusManager, E as FloatingPortal, a2 as POPUP_COLLISION_AVOIDANCE, I as InternalBackdrop, G as inertValue, a3 as FloatingNode, H as DROPDOWN_COLLISION_AVOIDANCE, t as createSelector, R as ReactStore, P as PopupTriggerMap, a4 as FloatingTreeStore, a5 as fastComponent, a6 as useInitialOpenSync, a7 as useFloatingNodeId, v as useOpenInteractionType, L as useImplicitActiveTrigger, M as useOpenStateTransitions, a8 as attachPreventUnmountOnClose, O as setPopupOpenState, a9 as useSyncedFloatingRootContext, y as useDismiss, r as useDirection, aa as TYPEAHEAD_RESET_MS, F as FOCUSABLE_POPUP_PROPS, N as usePopupInteractionProps, ab as FloatingTree, ac as getTabbableBeforeElement, ad as isOutsideEvent, ae as getTabbableAfterElement, af as getNextTabbable, ag as fastComponentRef, T as useTriggerDataForwarding, x as useClick, B as pressableTriggerOpenStateMapping, ah as FocusGuard, ai as PATIENT_CLICK_THRESHOLD, aj as useTriggerRegistration, W as triggerOpenStateMapping } from './useScrollLock-es.js';
|
|
4
|
+
import { Y as isMouseLikePointerType, h as useTimeout, B as addEventListener, g as getTarget, d as createChangeEventDetails, Z as triggerFocus, p as activeElement, o as ownerDocument, q as contains, _ as mac, $ as triggerPress, a0 as escapeKey, L as useOnMount, T as Timeout, b as useStableCallback, a as useIsoLayoutEffect, a1 as triggerHover, m as useValueAsRef, t as transitionStatusMapping, V as itemPress, c as useButton, u as useBaseUiId, f as useOpenChangeComplete, Q as outsidePress, a2 as useAnimationsFinished, a3 as siblingOpen, z as useControlled, k as useTransitionStatus, l as useId, v as focusOut, r as listNavigation, i as imperativeAction, a4 as useCompositeRootContext, R as cancelOpen, S as SafeReact } from './useButton-es.js';
|
|
5
5
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
-
import {
|
|
7
|
-
import { u as useToolbarRootContext } from './
|
|
8
|
-
import { C as COMPOSITE_KEYS } from './
|
|
6
|
+
import { h as useCompositeListItem, f as getDisabledMountTransitionStyles, d as useAnchorPositioning, i as adaptiveOrigin, c as useAnchoredPopupScrollLock, e as usePositioner, C as CompositeList, a as useListNavigation, b as useTypeahead, g as getPseudoElementBounds } from './useCompositeListItem-es.js';
|
|
7
|
+
import { u as useToolbarRootContext, i as isElementDisabled } from './isElementDisabled-es.js';
|
|
8
|
+
import { C as COMPOSITE_KEYS } from './scrollEdges-es.js';
|
|
9
9
|
import { o as getWindow, b as isElement, a as isHTMLElement, d as isLastTraversableNode, e as getParentNode } from './floating-ui.utils.dom-es.js';
|
|
10
|
-
import { p as popupStoreSelectors, l as createInitialPopupStoreState
|
|
10
|
+
import { p as popupStoreSelectors, l as createInitialPopupStoreState } from './ScrollAreaViewport-es.js';
|
|
11
11
|
import * as ReactDOM from 'react-dom';
|
|
12
12
|
|
|
13
13
|
function resolveValue(value, pointerType) {
|
|
@@ -35,28 +35,31 @@ function getRestMs(value) {
|
|
|
35
35
|
function isClickLikeOpenEvent(openEventType, interactedInside) {
|
|
36
36
|
return interactedInside || openEventType === 'click' || openEventType === 'mousedown';
|
|
37
37
|
}
|
|
38
|
+
function isHoverOpenEvent(openEventType) {
|
|
39
|
+
return openEventType?.includes('mouse') && openEventType !== 'mousedown';
|
|
40
|
+
}
|
|
38
41
|
|
|
39
|
-
const isMacSafari =
|
|
42
|
+
const isMacSafari = mac && webkit;
|
|
40
43
|
/**
|
|
41
44
|
* Opens the floating element while the reference element has focus, like CSS
|
|
42
45
|
* `:focus`.
|
|
43
46
|
* @see https://floating-ui.com/docs/useFocus
|
|
44
47
|
*/
|
|
45
48
|
function useFocus(context, props = {}) {
|
|
49
|
+
const {
|
|
50
|
+
enabled = true,
|
|
51
|
+
delay
|
|
52
|
+
} = props;
|
|
46
53
|
const store = 'rootStore' in context ? context.rootStore : context;
|
|
47
54
|
const {
|
|
48
55
|
events,
|
|
49
56
|
dataRef
|
|
50
57
|
} = store.context;
|
|
51
|
-
const {
|
|
52
|
-
enabled = true,
|
|
53
|
-
delay
|
|
54
|
-
} = props;
|
|
55
58
|
const blockFocusRef = React.useRef(false);
|
|
56
59
|
// Track which reference should be blocked from re-opening after Escape/press dismissal.
|
|
57
60
|
const blockedReferenceRef = React.useRef(null);
|
|
58
|
-
const timeout = useTimeout();
|
|
59
61
|
const keyboardModalityRef = React.useRef(true);
|
|
62
|
+
const timeout = useTimeout();
|
|
60
63
|
React.useEffect(() => {
|
|
61
64
|
const domReference = store.select('domReferenceElement');
|
|
62
65
|
if (!enabled) {
|
|
@@ -79,18 +82,7 @@ function useFocus(context, props = {}) {
|
|
|
79
82
|
function onPointerDown() {
|
|
80
83
|
keyboardModalityRef.current = false;
|
|
81
84
|
}
|
|
82
|
-
|
|
83
|
-
if (isMacSafari) {
|
|
84
|
-
win.addEventListener('keydown', onKeyDown, true);
|
|
85
|
-
win.addEventListener('pointerdown', onPointerDown, true);
|
|
86
|
-
}
|
|
87
|
-
return () => {
|
|
88
|
-
win.removeEventListener('blur', onBlur);
|
|
89
|
-
if (isMacSafari) {
|
|
90
|
-
win.removeEventListener('keydown', onKeyDown, true);
|
|
91
|
-
win.removeEventListener('pointerdown', onPointerDown, true);
|
|
92
|
-
}
|
|
93
|
-
};
|
|
85
|
+
return mergeCleanups(addEventListener(win, 'blur', onBlur), isMacSafari && addEventListener(win, 'keydown', onKeyDown, true), isMacSafari && addEventListener(win, 'pointerdown', onPointerDown, true));
|
|
94
86
|
}, [store, enabled]);
|
|
95
87
|
React.useEffect(() => {
|
|
96
88
|
if (!enabled) {
|
|
@@ -110,101 +102,100 @@ function useFocus(context, props = {}) {
|
|
|
110
102
|
events.off('openchange', onOpenChangeLocal);
|
|
111
103
|
};
|
|
112
104
|
}, [events, enabled, store]);
|
|
113
|
-
const reference = React.useMemo(() =>
|
|
114
|
-
|
|
105
|
+
const reference = React.useMemo(() => {
|
|
106
|
+
function resetBlockedFocus() {
|
|
115
107
|
blockFocusRef.current = false;
|
|
116
108
|
blockedReferenceRef.current = null;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
blockFocusRef.current
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
const target = getTarget(event.nativeEvent);
|
|
128
|
-
if (isElement(target)) {
|
|
129
|
-
// Safari fails to match `:focus-visible` if focus was initially
|
|
130
|
-
// outside the document.
|
|
131
|
-
if (isMacSafari && !event.relatedTarget) {
|
|
132
|
-
if (!keyboardModalityRef.current && !isTypeableElement(target)) {
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
onMouseLeave() {
|
|
112
|
+
resetBlockedFocus();
|
|
113
|
+
},
|
|
114
|
+
onFocus(event) {
|
|
115
|
+
const focusTarget = event.currentTarget;
|
|
116
|
+
if (blockFocusRef.current) {
|
|
117
|
+
if (blockedReferenceRef.current === focusTarget) {
|
|
133
118
|
return;
|
|
134
119
|
}
|
|
135
|
-
|
|
136
|
-
return;
|
|
120
|
+
resetBlockedFocus();
|
|
137
121
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
timeout.start(delayValue, () => {
|
|
150
|
-
if (blockFocusRef.current) {
|
|
151
|
-
return;
|
|
122
|
+
const target = getTarget(event.nativeEvent);
|
|
123
|
+
if (isElement(target)) {
|
|
124
|
+
// Safari fails to match `:focus-visible` if focus was initially
|
|
125
|
+
// outside the document.
|
|
126
|
+
if (isMacSafari && !event.relatedTarget) {
|
|
127
|
+
if (!keyboardModalityRef.current && !isTypeableElement(target)) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
} else if (!matchesFocusVisible(target)) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
152
133
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
// Hit the non-modal focus management portal guard. Focus will be
|
|
163
|
-
// moved into the floating element immediately after.
|
|
164
|
-
const movedToFocusGuard = isElement(relatedTarget) && relatedTarget.hasAttribute(createAttribute('focus-guard')) && relatedTarget.getAttribute('data-type') === 'outside';
|
|
165
|
-
|
|
166
|
-
// Wait for the window blur listener to fire.
|
|
167
|
-
timeout.start(0, () => {
|
|
168
|
-
const domReference = store.select('domReferenceElement');
|
|
169
|
-
const activeEl = activeElement(domReference ? domReference.ownerDocument : document);
|
|
170
|
-
|
|
171
|
-
// Focus left the page, keep it open.
|
|
172
|
-
if (!relatedTarget && activeEl === domReference) {
|
|
134
|
+
const movedFromOtherEnabledTrigger = isTargetInsideEnabledTrigger(event.relatedTarget, store.context.triggerElements);
|
|
135
|
+
const {
|
|
136
|
+
nativeEvent,
|
|
137
|
+
currentTarget
|
|
138
|
+
} = event;
|
|
139
|
+
const delayValue = typeof delay === 'function' ? delay() : delay;
|
|
140
|
+
if (store.select('open') && movedFromOtherEnabledTrigger || delayValue === 0 || delayValue === undefined) {
|
|
141
|
+
store.setOpen(true, createChangeEventDetails(triggerFocus, nativeEvent, currentTarget));
|
|
173
142
|
return;
|
|
174
143
|
}
|
|
144
|
+
timeout.start(delayValue, () => {
|
|
145
|
+
if (blockFocusRef.current) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
store.setOpen(true, createChangeEventDetails(triggerFocus, nativeEvent, currentTarget));
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
onBlur(event) {
|
|
152
|
+
resetBlockedFocus();
|
|
153
|
+
const relatedTarget = event.relatedTarget;
|
|
154
|
+
const nativeEvent = event.nativeEvent;
|
|
155
|
+
|
|
156
|
+
// Hit the non-modal focus management portal guard. Focus will be
|
|
157
|
+
// moved into the floating element immediately after.
|
|
158
|
+
const movedToFocusGuard = isElement(relatedTarget) && relatedTarget.hasAttribute(createAttribute('focus-guard')) && relatedTarget.getAttribute('data-type') === 'outside';
|
|
159
|
+
|
|
160
|
+
// Wait for the window blur listener to fire.
|
|
161
|
+
timeout.start(0, () => {
|
|
162
|
+
const domReference = store.select('domReferenceElement');
|
|
163
|
+
const activeEl = activeElement(ownerDocument(domReference));
|
|
164
|
+
|
|
165
|
+
// Focus left the page, keep it open.
|
|
166
|
+
if (!relatedTarget && activeEl === domReference) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
175
169
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
170
|
+
// When focusing the reference element (e.g. regular click), then
|
|
171
|
+
// clicking into the floating element, prevent it from hiding.
|
|
172
|
+
// Note: it must be focusable, e.g. `tabindex="-1"`.
|
|
173
|
+
// We can not rely on relatedTarget to point to the correct element
|
|
174
|
+
// as it will only point to the shadow host of the newly focused element
|
|
175
|
+
// and not the element that actually has received focus if it is located
|
|
176
|
+
// inside a shadow root.
|
|
177
|
+
if (contains(dataRef.current.floatingContext?.refs.floating.current, activeEl) || contains(domReference, activeEl) || movedToFocusGuard) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
186
180
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
181
|
+
// If the next focused element is one of the triggers, do not close
|
|
182
|
+
// the floating element. The focus handler of that trigger will
|
|
183
|
+
// handle the open state.
|
|
184
|
+
const nextFocusedElement = relatedTarget ?? activeEl;
|
|
185
|
+
if (isTargetInsideEnabledTrigger(nextFocusedElement, store.context.triggerElements)) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
store.setOpen(false, createChangeEventDetails(triggerFocus, nativeEvent));
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
}, [dataRef, delay, store, timeout]);
|
|
198
193
|
return React.useMemo(() => enabled ? {
|
|
199
194
|
reference,
|
|
200
195
|
trigger: reference
|
|
201
196
|
} : {}, [enabled, reference]);
|
|
202
197
|
}
|
|
203
198
|
|
|
204
|
-
const interactiveSelector = `button,a,[role="button"],select,[tabindex]:not([tabindex="-1"]),${TYPEABLE_SELECTOR}`;
|
|
205
|
-
function isInteractiveElement(element) {
|
|
206
|
-
return element ? Boolean(element.closest(interactiveSelector)) : false;
|
|
207
|
-
}
|
|
208
199
|
class HoverInteraction {
|
|
209
200
|
constructor() {
|
|
210
201
|
this.pointerType = undefined;
|
|
@@ -231,13 +222,18 @@ class HoverInteraction {
|
|
|
231
222
|
return this.dispose;
|
|
232
223
|
};
|
|
233
224
|
}
|
|
225
|
+
const pointerEventsMutationOwnerByScopeElement = new WeakMap();
|
|
234
226
|
function clearSafePolygonPointerEventsMutation(instance) {
|
|
235
227
|
if (!instance.performedPointerEventsMutation) {
|
|
236
228
|
return;
|
|
237
229
|
}
|
|
238
|
-
instance.pointerEventsScopeElement
|
|
239
|
-
|
|
240
|
-
|
|
230
|
+
const scopeElement = instance.pointerEventsScopeElement;
|
|
231
|
+
if (scopeElement && pointerEventsMutationOwnerByScopeElement.get(scopeElement) === instance) {
|
|
232
|
+
instance.pointerEventsScopeElement?.style.removeProperty('pointer-events');
|
|
233
|
+
instance.pointerEventsReferenceElement?.style.removeProperty('pointer-events');
|
|
234
|
+
instance.pointerEventsFloatingElement?.style.removeProperty('pointer-events');
|
|
235
|
+
pointerEventsMutationOwnerByScopeElement.delete(scopeElement);
|
|
236
|
+
}
|
|
241
237
|
instance.performedPointerEventsMutation = false;
|
|
242
238
|
instance.pointerEventsScopeElement = null;
|
|
243
239
|
instance.pointerEventsReferenceElement = null;
|
|
@@ -249,18 +245,23 @@ function applySafePolygonPointerEventsMutation(instance, options) {
|
|
|
249
245
|
referenceElement,
|
|
250
246
|
floatingElement
|
|
251
247
|
} = options;
|
|
248
|
+
const existingOwner = pointerEventsMutationOwnerByScopeElement.get(scopeElement);
|
|
249
|
+
if (existingOwner && existingOwner !== instance) {
|
|
250
|
+
clearSafePolygonPointerEventsMutation(existingOwner);
|
|
251
|
+
}
|
|
252
252
|
clearSafePolygonPointerEventsMutation(instance);
|
|
253
253
|
instance.performedPointerEventsMutation = true;
|
|
254
254
|
instance.pointerEventsScopeElement = scopeElement;
|
|
255
255
|
instance.pointerEventsReferenceElement = referenceElement;
|
|
256
256
|
instance.pointerEventsFloatingElement = floatingElement;
|
|
257
|
+
pointerEventsMutationOwnerByScopeElement.set(scopeElement, instance);
|
|
257
258
|
scopeElement.style.pointerEvents = 'none';
|
|
258
259
|
referenceElement.style.pointerEvents = 'auto';
|
|
259
260
|
floatingElement.style.pointerEvents = 'auto';
|
|
260
261
|
}
|
|
261
262
|
function useHoverInteractionSharedState(store) {
|
|
262
|
-
const instance = useRefWithInit(HoverInteraction.create).current;
|
|
263
263
|
const data = store.context.dataRef.current;
|
|
264
|
+
const instance = useRefWithInit(() => data.hoverInteractionState ?? HoverInteraction.create()).current;
|
|
264
265
|
if (!data.hoverInteractionState) {
|
|
265
266
|
data.hoverInteractionState = instance;
|
|
266
267
|
}
|
|
@@ -272,6 +273,11 @@ function useHoverInteractionSharedState(store) {
|
|
|
272
273
|
* Provides hover interactions that should be attached to the floating element.
|
|
273
274
|
*/
|
|
274
275
|
function useHoverFloatingInteraction(context, parameters = {}) {
|
|
276
|
+
const {
|
|
277
|
+
enabled = true,
|
|
278
|
+
closeDelay: closeDelayProp = 0,
|
|
279
|
+
nodeId: nodeIdProp
|
|
280
|
+
} = parameters;
|
|
275
281
|
const store = 'rootStore' in context ? context.rootStore : context;
|
|
276
282
|
const open = store.useState('open');
|
|
277
283
|
const floatingElement = store.useState('floatingElement');
|
|
@@ -279,47 +285,19 @@ function useHoverFloatingInteraction(context, parameters = {}) {
|
|
|
279
285
|
const {
|
|
280
286
|
dataRef
|
|
281
287
|
} = store.context;
|
|
282
|
-
const {
|
|
283
|
-
enabled = true,
|
|
284
|
-
closeDelay: closeDelayProp = 0
|
|
285
|
-
} = parameters;
|
|
286
|
-
const instance = useHoverInteractionSharedState(store);
|
|
287
288
|
const tree = useFloatingTree();
|
|
288
289
|
const parentId = useFloatingParentNodeId();
|
|
290
|
+
const instance = useHoverInteractionSharedState(store);
|
|
291
|
+
const childClosedTimeout = useTimeout();
|
|
289
292
|
const isClickLikeOpenEvent$1 = useStableCallback(() => {
|
|
290
293
|
return isClickLikeOpenEvent(dataRef.current.openEvent?.type, instance.interactedInside);
|
|
291
294
|
});
|
|
292
295
|
const isHoverOpen = useStableCallback(() => {
|
|
293
|
-
|
|
294
|
-
return type?.includes('mouse') && type !== 'mousedown';
|
|
295
|
-
});
|
|
296
|
-
const isRelatedTargetInsideEnabledTrigger = useStableCallback(target => {
|
|
297
|
-
return isTargetInsideEnabledTrigger(target, store.context.triggerElements);
|
|
296
|
+
return isHoverOpenEvent(dataRef.current.openEvent?.type);
|
|
298
297
|
});
|
|
299
|
-
const closeWithDelay = React.useCallback(event => {
|
|
300
|
-
const closeDelay = getDelay(closeDelayProp, 'close', instance.pointerType);
|
|
301
|
-
const close = () => {
|
|
302
|
-
store.setOpen(false, createChangeEventDetails(triggerHover, event));
|
|
303
|
-
tree?.events.emit('floating.closed', event);
|
|
304
|
-
};
|
|
305
|
-
if (closeDelay) {
|
|
306
|
-
instance.openChangeTimeout.start(closeDelay, close);
|
|
307
|
-
} else {
|
|
308
|
-
instance.openChangeTimeout.clear();
|
|
309
|
-
close();
|
|
310
|
-
}
|
|
311
|
-
}, [closeDelayProp, store, instance, tree]);
|
|
312
298
|
const clearPointerEvents = useStableCallback(() => {
|
|
313
299
|
clearSafePolygonPointerEventsMutation(instance);
|
|
314
300
|
});
|
|
315
|
-
const handleInteractInside = useStableCallback(event => {
|
|
316
|
-
const target = getTarget(event);
|
|
317
|
-
if (!isInteractiveElement(target)) {
|
|
318
|
-
instance.interactedInside = false;
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
instance.interactedInside = target?.closest('[aria-haspopup]') != null;
|
|
322
|
-
});
|
|
323
301
|
useIsoLayoutEffect(() => {
|
|
324
302
|
if (!open) {
|
|
325
303
|
instance.pointerType = undefined;
|
|
@@ -343,7 +321,13 @@ function useHoverFloatingInteraction(context, parameters = {}) {
|
|
|
343
321
|
if (parentFloating) {
|
|
344
322
|
parentFloating.style.pointerEvents = '';
|
|
345
323
|
}
|
|
346
|
-
|
|
324
|
+
|
|
325
|
+
// A keep-mounted submenu can appear in the tree before it opens, so a
|
|
326
|
+
// cached scope or parent lookup may resolve to the submenu itself. That
|
|
327
|
+
// would not shield sibling items in the parent menu.
|
|
328
|
+
const cachedScopeElement = instance.pointerEventsScopeElement !== floatingEl ? instance.pointerEventsScopeElement : null;
|
|
329
|
+
const parentScopeElement = parentFloating !== floatingEl ? parentFloating : null;
|
|
330
|
+
const scopeElement = instance.handleCloseOptions?.getScope?.() ?? cachedScopeElement ?? parentScopeElement ?? ref.closest('[data-rootownerid]') ?? doc.body;
|
|
347
331
|
applySafePolygonPointerEventsMutation(instance, {
|
|
348
332
|
scopeElement,
|
|
349
333
|
referenceElement: ref,
|
|
@@ -355,11 +339,34 @@ function useHoverFloatingInteraction(context, parameters = {}) {
|
|
|
355
339
|
}
|
|
356
340
|
return undefined;
|
|
357
341
|
}, [enabled, open, domReferenceElement, floatingElement, instance, isHoverOpen, tree, parentId, clearPointerEvents]);
|
|
358
|
-
const childClosedTimeout = useTimeout();
|
|
359
342
|
React.useEffect(() => {
|
|
360
343
|
if (!enabled) {
|
|
361
344
|
return undefined;
|
|
362
345
|
}
|
|
346
|
+
function hasParentChildren() {
|
|
347
|
+
return !!(tree && parentId && getNodeChildren(tree.nodesRef.current, parentId).length > 0);
|
|
348
|
+
}
|
|
349
|
+
function closeWithDelay(event) {
|
|
350
|
+
const closeDelay = getDelay(closeDelayProp, 'close', instance.pointerType);
|
|
351
|
+
const close = () => {
|
|
352
|
+
store.setOpen(false, createChangeEventDetails(triggerHover, event));
|
|
353
|
+
tree?.events.emit('floating.closed', event);
|
|
354
|
+
};
|
|
355
|
+
if (closeDelay) {
|
|
356
|
+
instance.openChangeTimeout.start(closeDelay, close);
|
|
357
|
+
} else {
|
|
358
|
+
instance.openChangeTimeout.clear();
|
|
359
|
+
close();
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function handleInteractInside(event) {
|
|
363
|
+
const target = getTarget(event);
|
|
364
|
+
if (!isInteractiveElement(target)) {
|
|
365
|
+
instance.interactedInside = false;
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
instance.interactedInside = target?.closest('[aria-haspopup]') != null;
|
|
369
|
+
}
|
|
363
370
|
function onFloatingMouseEnter() {
|
|
364
371
|
instance.openChangeTimeout.clear();
|
|
365
372
|
childClosedTimeout.clear();
|
|
@@ -367,15 +374,21 @@ function useHoverFloatingInteraction(context, parameters = {}) {
|
|
|
367
374
|
clearPointerEvents();
|
|
368
375
|
}
|
|
369
376
|
function onFloatingMouseLeave(event) {
|
|
370
|
-
if (
|
|
377
|
+
if (hasParentChildren() && tree) {
|
|
371
378
|
tree.events.on('floating.closed', onNodeClosed);
|
|
372
379
|
return;
|
|
373
380
|
}
|
|
374
|
-
if (
|
|
381
|
+
if (isTargetInsideEnabledTrigger(event.relatedTarget, store.context.triggerElements)) {
|
|
375
382
|
// If the mouse is leaving the reference element to another trigger, don't explicitly close the popup
|
|
376
383
|
// as it will be moved.
|
|
377
384
|
return;
|
|
378
385
|
}
|
|
386
|
+
const currentNodeId = dataRef.current.floatingContext?.nodeId ?? nodeIdProp;
|
|
387
|
+
const relatedTarget = event.relatedTarget;
|
|
388
|
+
const isMovingIntoDescendantFloating = tree && currentNodeId && isElement(relatedTarget) && getNodeChildren(tree.nodesRef.current, currentNodeId, false).some(node => contains(node.context?.elements.floating, relatedTarget));
|
|
389
|
+
if (isMovingIntoDescendantFloating) {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
379
392
|
|
|
380
393
|
// If the safePolygon handler is active, let it handle the close logic.
|
|
381
394
|
if (instance.handler) {
|
|
@@ -383,12 +396,12 @@ function useHoverFloatingInteraction(context, parameters = {}) {
|
|
|
383
396
|
return;
|
|
384
397
|
}
|
|
385
398
|
clearPointerEvents();
|
|
386
|
-
if (!isClickLikeOpenEvent$1()) {
|
|
399
|
+
if (isHoverOpen() && !isClickLikeOpenEvent$1()) {
|
|
387
400
|
closeWithDelay(event);
|
|
388
401
|
}
|
|
389
402
|
}
|
|
390
403
|
function onNodeClosed(event) {
|
|
391
|
-
if (!tree || !parentId ||
|
|
404
|
+
if (!tree || !parentId || hasParentChildren()) {
|
|
392
405
|
return;
|
|
393
406
|
}
|
|
394
407
|
// Allow the mouseenter event to fire in case child was closed because mouse moved into parent.
|
|
@@ -399,20 +412,10 @@ function useHoverFloatingInteraction(context, parameters = {}) {
|
|
|
399
412
|
});
|
|
400
413
|
}
|
|
401
414
|
const floating = floatingElement;
|
|
402
|
-
|
|
403
|
-
floating.addEventListener('mouseenter', onFloatingMouseEnter);
|
|
404
|
-
floating.addEventListener('mouseleave', onFloatingMouseLeave);
|
|
405
|
-
floating.addEventListener('pointerdown', handleInteractInside, true);
|
|
406
|
-
}
|
|
407
|
-
return () => {
|
|
408
|
-
if (floating) {
|
|
409
|
-
floating.removeEventListener('mouseenter', onFloatingMouseEnter);
|
|
410
|
-
floating.removeEventListener('mouseleave', onFloatingMouseLeave);
|
|
411
|
-
floating.removeEventListener('pointerdown', handleInteractInside, true);
|
|
412
|
-
}
|
|
415
|
+
return mergeCleanups(floating && addEventListener(floating, 'mouseenter', onFloatingMouseEnter), floating && addEventListener(floating, 'mouseleave', onFloatingMouseLeave), floating && addEventListener(floating, 'pointerdown', handleInteractInside, true), () => {
|
|
413
416
|
tree?.events.off('floating.closed', onNodeClosed);
|
|
414
|
-
};
|
|
415
|
-
}, [enabled, floatingElement, store, dataRef,
|
|
417
|
+
});
|
|
418
|
+
}, [enabled, floatingElement, store, dataRef, closeDelayProp, nodeIdProp, isHoverOpen, isClickLikeOpenEvent$1, clearPointerEvents, instance, tree, parentId, childClosedTimeout]);
|
|
416
419
|
}
|
|
417
420
|
|
|
418
421
|
const EMPTY_REF = {
|
|
@@ -424,11 +427,6 @@ const EMPTY_REF = {
|
|
|
424
427
|
* elements.
|
|
425
428
|
*/
|
|
426
429
|
function useHoverReferenceInteraction(context, props = {}) {
|
|
427
|
-
const store = 'rootStore' in context ? context.rootStore : context;
|
|
428
|
-
const {
|
|
429
|
-
dataRef,
|
|
430
|
-
events
|
|
431
|
-
} = store.context;
|
|
432
430
|
const {
|
|
433
431
|
enabled = true,
|
|
434
432
|
delay = 0,
|
|
@@ -439,23 +437,29 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
439
437
|
triggerElementRef = EMPTY_REF,
|
|
440
438
|
externalTree,
|
|
441
439
|
isActiveTrigger = true,
|
|
442
|
-
getHandleCloseContext
|
|
440
|
+
getHandleCloseContext,
|
|
441
|
+
isClosing,
|
|
442
|
+
shouldOpen: shouldOpenProp
|
|
443
443
|
} = props;
|
|
444
|
+
const store = 'rootStore' in context ? context.rootStore : context;
|
|
445
|
+
const {
|
|
446
|
+
dataRef,
|
|
447
|
+
events
|
|
448
|
+
} = store.context;
|
|
444
449
|
const tree = useFloatingTree(externalTree);
|
|
445
450
|
const instance = useHoverInteractionSharedState(store);
|
|
451
|
+
const isHoverCloseActiveRef = React.useRef(false);
|
|
446
452
|
const handleCloseRef = useValueAsRef(handleClose);
|
|
447
453
|
const delayRef = useValueAsRef(delay);
|
|
448
454
|
const restMsRef = useValueAsRef(restMs);
|
|
449
455
|
const enabledRef = useValueAsRef(enabled);
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
instance.handleCloseOptions = handleCloseRef.current?.__options;
|
|
453
|
-
}
|
|
456
|
+
const shouldOpenRef = useValueAsRef(shouldOpenProp);
|
|
457
|
+
const isClosingRef = useValueAsRef(isClosing);
|
|
454
458
|
const isClickLikeOpenEvent$1 = useStableCallback(() => {
|
|
455
459
|
return isClickLikeOpenEvent(dataRef.current.openEvent?.type, instance.interactedInside);
|
|
456
460
|
});
|
|
457
|
-
const
|
|
458
|
-
return
|
|
461
|
+
const checkShouldOpen = useStableCallback(() => {
|
|
462
|
+
return shouldOpenRef.current?.() !== false;
|
|
459
463
|
});
|
|
460
464
|
const isOverInactiveTrigger = useStableCallback((currentDomReference, currentTarget, target) => {
|
|
461
465
|
const allTriggers = store.context.triggerElements;
|
|
@@ -472,19 +476,6 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
472
476
|
const targetElement = target;
|
|
473
477
|
return allTriggers.hasMatchingElement(trigger => contains(trigger, targetElement)) && (!currentDomReference || !contains(currentDomReference, targetElement));
|
|
474
478
|
});
|
|
475
|
-
const closeWithDelay = React.useCallback((event, runElseBranch = true) => {
|
|
476
|
-
const closeDelay = getDelay(delayRef.current, 'close', instance.pointerType);
|
|
477
|
-
if (closeDelay) {
|
|
478
|
-
instance.openChangeTimeout.start(closeDelay, () => {
|
|
479
|
-
store.setOpen(false, createChangeEventDetails(triggerHover, event));
|
|
480
|
-
tree?.events.emit('floating.closed', event);
|
|
481
|
-
});
|
|
482
|
-
} else if (runElseBranch) {
|
|
483
|
-
instance.openChangeTimeout.clear();
|
|
484
|
-
store.setOpen(false, createChangeEventDetails(triggerHover, event));
|
|
485
|
-
tree?.events.emit('floating.closed', event);
|
|
486
|
-
}
|
|
487
|
-
}, [delayRef, store, instance, tree]);
|
|
488
479
|
const cleanupMouseMoveHandler = useStableCallback(() => {
|
|
489
480
|
if (!instance.handler) {
|
|
490
481
|
return;
|
|
@@ -493,10 +484,14 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
493
484
|
doc.removeEventListener('mousemove', instance.handler);
|
|
494
485
|
instance.handler = undefined;
|
|
495
486
|
});
|
|
496
|
-
React.useEffect(() => cleanupMouseMoveHandler, [cleanupMouseMoveHandler]);
|
|
497
487
|
const clearPointerEvents = useStableCallback(() => {
|
|
498
488
|
clearSafePolygonPointerEventsMutation(instance);
|
|
499
489
|
});
|
|
490
|
+
if (isActiveTrigger) {
|
|
491
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
492
|
+
instance.handleCloseOptions = handleCloseRef.current?.__options;
|
|
493
|
+
}
|
|
494
|
+
React.useEffect(() => cleanupMouseMoveHandler, [cleanupMouseMoveHandler]);
|
|
500
495
|
|
|
501
496
|
// When closing before opening, clear the delay timeouts to cancel it
|
|
502
497
|
// from showing.
|
|
@@ -506,11 +501,14 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
506
501
|
}
|
|
507
502
|
function onOpenChangeLocal(details) {
|
|
508
503
|
if (!details.open) {
|
|
504
|
+
isHoverCloseActiveRef.current = details.reason === triggerHover;
|
|
509
505
|
cleanupMouseMoveHandler();
|
|
510
506
|
instance.openChangeTimeout.clear();
|
|
511
507
|
instance.restTimeout.clear();
|
|
512
508
|
instance.blockMouseMove = true;
|
|
513
509
|
instance.restTimeoutPending = false;
|
|
510
|
+
} else {
|
|
511
|
+
isHoverCloseActiveRef.current = false;
|
|
514
512
|
}
|
|
515
513
|
}
|
|
516
514
|
events.on('openchange', onOpenChangeLocal);
|
|
@@ -522,6 +520,19 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
522
520
|
if (!enabled) {
|
|
523
521
|
return undefined;
|
|
524
522
|
}
|
|
523
|
+
function closeWithDelay(event, runElseBranch = true) {
|
|
524
|
+
const closeDelay = getDelay(delayRef.current, 'close', instance.pointerType);
|
|
525
|
+
if (closeDelay) {
|
|
526
|
+
instance.openChangeTimeout.start(closeDelay, () => {
|
|
527
|
+
store.setOpen(false, createChangeEventDetails(triggerHover, event));
|
|
528
|
+
tree?.events.emit('floating.closed', event);
|
|
529
|
+
});
|
|
530
|
+
} else if (runElseBranch) {
|
|
531
|
+
instance.openChangeTimeout.clear();
|
|
532
|
+
store.setOpen(false, createChangeEventDetails(triggerHover, event));
|
|
533
|
+
tree?.events.emit('floating.closed', event);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
525
536
|
const trigger = triggerElementRef.current ?? (isActiveTrigger ? store.select('domReferenceElement') : null);
|
|
526
537
|
if (!isElement(trigger)) {
|
|
527
538
|
return undefined;
|
|
@@ -536,27 +547,57 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
536
547
|
// Only rest delay is set; there's no fallback delay.
|
|
537
548
|
// This will be handled by `onMouseMove`.
|
|
538
549
|
const restMsValue = getRestMs(restMsRef.current);
|
|
539
|
-
if (restMsValue > 0 && !getDelay(delayRef.current, 'open')) {
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
550
|
const openDelay = getDelay(delayRef.current, 'open', instance.pointerType);
|
|
543
|
-
const
|
|
551
|
+
const eventTarget = getTarget(event);
|
|
552
|
+
const currentTarget = event.currentTarget ?? null;
|
|
544
553
|
const currentDomReference = store.select('domReferenceElement');
|
|
545
|
-
|
|
554
|
+
let triggerNode = currentTarget;
|
|
555
|
+
|
|
556
|
+
// Wrapper/delegated mode: resolve the actual trigger from the event target.
|
|
557
|
+
if (isElement(eventTarget) && !store.context.triggerElements.hasElement(eventTarget)) {
|
|
558
|
+
for (const triggerElement of store.context.triggerElements.elements()) {
|
|
559
|
+
if (contains(triggerElement, eventTarget)) {
|
|
560
|
+
triggerNode = triggerElement;
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Wrapper/delegated mode fallback: if the wrapper contains the active trigger,
|
|
567
|
+
// treat this as re-entering that active trigger.
|
|
568
|
+
if (isElement(currentTarget) && isElement(currentDomReference) && !store.context.triggerElements.hasElement(currentTarget) && contains(currentTarget, currentDomReference)) {
|
|
569
|
+
triggerNode = currentDomReference;
|
|
570
|
+
}
|
|
571
|
+
const isOverInactive = triggerNode == null ? false : isOverInactiveTrigger(currentDomReference, triggerNode, eventTarget);
|
|
546
572
|
const isOpen = store.select('open');
|
|
573
|
+
const isInClosingTransition = isClosingRef.current?.() ?? store.select('transitionStatus') === 'ending';
|
|
574
|
+
const isHoverCloseTransition = !isOpen && isInClosingTransition && isHoverCloseActiveRef.current;
|
|
575
|
+
const isReenteringSameTriggerDuringCloseTransition = !isOverInactive && isElement(triggerNode) && isElement(currentDomReference) && contains(currentDomReference, triggerNode) && isHoverCloseTransition;
|
|
576
|
+
const isRestOnlyDelay = restMsValue > 0 && !openDelay;
|
|
577
|
+
const shouldOpenImmediately = isOverInactive && (isOpen || isHoverCloseTransition) || isReenteringSameTriggerDuringCloseTransition;
|
|
547
578
|
const shouldOpen = !isOpen || isOverInactive;
|
|
548
579
|
|
|
549
|
-
//
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
580
|
+
// Open immediately when moving between triggers while open, or during
|
|
581
|
+
// a hover-driven close transition (including same-trigger re-entry).
|
|
582
|
+
if (shouldOpenImmediately) {
|
|
583
|
+
if (checkShouldOpen()) {
|
|
584
|
+
store.setOpen(true, createChangeEventDetails(triggerHover, event, triggerNode));
|
|
585
|
+
}
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if (isRestOnlyDelay) {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
if (openDelay) {
|
|
553
592
|
instance.openChangeTimeout.start(openDelay, () => {
|
|
554
|
-
if (shouldOpen) {
|
|
593
|
+
if (shouldOpen && checkShouldOpen()) {
|
|
555
594
|
store.setOpen(true, createChangeEventDetails(triggerHover, event, triggerNode));
|
|
556
595
|
}
|
|
557
596
|
});
|
|
558
597
|
} else if (shouldOpen) {
|
|
559
|
-
|
|
598
|
+
if (checkShouldOpen()) {
|
|
599
|
+
store.setOpen(true, createChangeEventDetails(triggerHover, event, triggerNode));
|
|
600
|
+
}
|
|
560
601
|
}
|
|
561
602
|
}
|
|
562
603
|
function onMouseLeave(event) {
|
|
@@ -570,8 +611,7 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
570
611
|
instance.restTimeout.clear();
|
|
571
612
|
instance.restTimeoutPending = false;
|
|
572
613
|
const handleCloseContextBase = dataRef.current.floatingContext ?? getHandleCloseContext?.();
|
|
573
|
-
|
|
574
|
-
if (ignoreRelatedTargetTrigger) {
|
|
614
|
+
if (isTargetInsideEnabledTrigger(event.relatedTarget, store.context.triggerElements)) {
|
|
575
615
|
return;
|
|
576
616
|
}
|
|
577
617
|
if (handleCloseRef.current && handleCloseContextBase) {
|
|
@@ -602,20 +642,12 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
602
642
|
}
|
|
603
643
|
}
|
|
604
644
|
if (move) {
|
|
605
|
-
|
|
645
|
+
return mergeCleanups(addEventListener(trigger, 'mousemove', onMouseEnter, {
|
|
606
646
|
once: true
|
|
607
|
-
});
|
|
647
|
+
}), addEventListener(trigger, 'mouseenter', onMouseEnter), addEventListener(trigger, 'mouseleave', onMouseLeave));
|
|
608
648
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
return () => {
|
|
612
|
-
if (move) {
|
|
613
|
-
trigger.removeEventListener('mousemove', onMouseEnter);
|
|
614
|
-
}
|
|
615
|
-
trigger.removeEventListener('mouseenter', onMouseEnter);
|
|
616
|
-
trigger.removeEventListener('mouseleave', onMouseLeave);
|
|
617
|
-
};
|
|
618
|
-
}, [cleanupMouseMoveHandler, clearPointerEvents, dataRef, delayRef, closeWithDelay, store, enabled, handleCloseRef, instance, isActiveTrigger, isOverInactiveTrigger, isClickLikeOpenEvent$1, isRelatedTargetInsideEnabledTrigger, mouseOnly, move, restMsRef, triggerElementRef, tree, enabledRef, getHandleCloseContext]);
|
|
649
|
+
return mergeCleanups(addEventListener(trigger, 'mouseenter', onMouseEnter), addEventListener(trigger, 'mouseleave', onMouseLeave));
|
|
650
|
+
}, [cleanupMouseMoveHandler, clearPointerEvents, dataRef, delayRef, store, enabled, handleCloseRef, instance, isActiveTrigger, isOverInactiveTrigger, isClickLikeOpenEvent$1, mouseOnly, move, restMsRef, triggerElementRef, tree, enabledRef, getHandleCloseContext, isClosingRef, checkShouldOpen]);
|
|
619
651
|
return React.useMemo(() => {
|
|
620
652
|
if (!enabled) {
|
|
621
653
|
return undefined;
|
|
@@ -637,6 +669,17 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
637
669
|
if (mouseOnly && !isMouseLikePointerType(instance.pointerType)) {
|
|
638
670
|
return;
|
|
639
671
|
}
|
|
672
|
+
if (currentOpen && isOverInactive && instance.handleCloseOptions?.blockPointerEvents) {
|
|
673
|
+
const floatingElement = store.select('floatingElement');
|
|
674
|
+
if (floatingElement) {
|
|
675
|
+
const scopeElement = instance.handleCloseOptions?.getScope?.() ?? trigger.ownerDocument.body;
|
|
676
|
+
applySafePolygonPointerEventsMutation(instance, {
|
|
677
|
+
scopeElement,
|
|
678
|
+
referenceElement: trigger,
|
|
679
|
+
floatingElement
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
}
|
|
640
683
|
const restMsValue = getRestMs(restMsRef.current);
|
|
641
684
|
if (currentOpen && !isOverInactive || restMsValue === 0) {
|
|
642
685
|
return;
|
|
@@ -654,7 +697,7 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
654
697
|
return;
|
|
655
698
|
}
|
|
656
699
|
const latestOpen = store.select('open');
|
|
657
|
-
if (!instance.blockMouseMove && (!latestOpen || isOverInactive)) {
|
|
700
|
+
if (!instance.blockMouseMove && (!latestOpen || isOverInactive) && checkShouldOpen()) {
|
|
658
701
|
store.setOpen(true, createChangeEventDetails(triggerHover, nativeEvent, trigger));
|
|
659
702
|
}
|
|
660
703
|
}
|
|
@@ -670,7 +713,7 @@ function useHoverReferenceInteraction(context, props = {}) {
|
|
|
670
713
|
}
|
|
671
714
|
}
|
|
672
715
|
};
|
|
673
|
-
}, [enabled, instance, isClickLikeOpenEvent$1, isOverInactiveTrigger, mouseOnly, store, restMsRef]);
|
|
716
|
+
}, [enabled, instance, isClickLikeOpenEvent$1, isOverInactiveTrigger, mouseOnly, store, restMsRef, checkShouldOpen]);
|
|
674
717
|
}
|
|
675
718
|
|
|
676
719
|
/* eslint-disable no-nested-ternary */
|
|
@@ -909,6 +952,7 @@ function safePolygon(options = {}) {
|
|
|
909
952
|
|
|
910
953
|
// eslint-disable-next-line no-underscore-dangle
|
|
911
954
|
fn.__options = {
|
|
955
|
+
...options,
|
|
912
956
|
blockPointerEvents
|
|
913
957
|
};
|
|
914
958
|
return fn;
|
|
@@ -957,8 +1001,9 @@ const stateAttributesMapping$1 = {
|
|
|
957
1001
|
*/
|
|
958
1002
|
const MenuBackdrop = /*#__PURE__*/React.forwardRef(function MenuBackdrop(componentProps, forwardedRef) {
|
|
959
1003
|
const {
|
|
960
|
-
className,
|
|
961
1004
|
render,
|
|
1005
|
+
className,
|
|
1006
|
+
style,
|
|
962
1007
|
...elementProps
|
|
963
1008
|
} = componentProps;
|
|
964
1009
|
const {
|
|
@@ -1009,12 +1054,13 @@ function useMenuItemCommonProps(params) {
|
|
|
1009
1054
|
const {
|
|
1010
1055
|
events: menuEvents
|
|
1011
1056
|
} = store.useState('floatingTreeRoot');
|
|
1057
|
+
const open = store.useState('open');
|
|
1012
1058
|
const contextMenuContext = useContextMenuRootContext(true);
|
|
1013
1059
|
const isContextMenu = contextMenuContext !== undefined;
|
|
1014
1060
|
return React.useMemo(() => ({
|
|
1015
1061
|
id,
|
|
1016
1062
|
role: 'menuitem',
|
|
1017
|
-
tabIndex: highlighted ? 0 : -1,
|
|
1063
|
+
tabIndex: open && highlighted ? 0 : -1,
|
|
1018
1064
|
onKeyDown(event) {
|
|
1019
1065
|
if (event.key === ' ' && typingRef?.current) {
|
|
1020
1066
|
event.preventDefault();
|
|
@@ -1050,7 +1096,7 @@ function useMenuItemCommonProps(params) {
|
|
|
1050
1096
|
|
|
1051
1097
|
// On non-macOS platforms, this mouseup belongs to the right-click gesture
|
|
1052
1098
|
// that opened the context menu, so it must not activate an item.
|
|
1053
|
-
if (isContextMenu && !
|
|
1099
|
+
if (isContextMenu && !mac && event.button === 2) {
|
|
1054
1100
|
return;
|
|
1055
1101
|
}
|
|
1056
1102
|
}
|
|
@@ -1062,7 +1108,7 @@ function useMenuItemCommonProps(params) {
|
|
|
1062
1108
|
}
|
|
1063
1109
|
}
|
|
1064
1110
|
}
|
|
1065
|
-
}), [closeOnClick, highlighted, id, menuEvents, nodeId, store, typingRef, itemRef, contextMenuContext, isContextMenu, itemMetadata]);
|
|
1111
|
+
}), [closeOnClick, highlighted, id, menuEvents, nodeId, open, store, typingRef, itemRef, contextMenuContext, isContextMenu, itemMetadata]);
|
|
1066
1112
|
}
|
|
1067
1113
|
|
|
1068
1114
|
const REGULAR_ITEM = {
|
|
@@ -1071,7 +1117,7 @@ const REGULAR_ITEM = {
|
|
|
1071
1117
|
function useMenuItem(params) {
|
|
1072
1118
|
const {
|
|
1073
1119
|
closeOnClick,
|
|
1074
|
-
disabled = false,
|
|
1120
|
+
disabled: disabledProp = false,
|
|
1075
1121
|
highlighted,
|
|
1076
1122
|
id,
|
|
1077
1123
|
store,
|
|
@@ -1080,6 +1126,8 @@ function useMenuItem(params) {
|
|
|
1080
1126
|
itemMetadata,
|
|
1081
1127
|
nodeId
|
|
1082
1128
|
} = params;
|
|
1129
|
+
const rootDisabled = store.useState('disabled');
|
|
1130
|
+
const disabled = disabledProp || rootDisabled;
|
|
1083
1131
|
const itemRef = React.useRef(null);
|
|
1084
1132
|
const {
|
|
1085
1133
|
getButtonProps,
|
|
@@ -1156,7 +1204,7 @@ if (process.env.NODE_ENV !== "production") MenuGroupContext.displayName = "MenuG
|
|
|
1156
1204
|
function useMenuGroupRootContext() {
|
|
1157
1205
|
const context = React.useContext(MenuGroupContext);
|
|
1158
1206
|
if (context === undefined) {
|
|
1159
|
-
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI:
|
|
1207
|
+
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>.' : formatErrorMessage(31));
|
|
1160
1208
|
}
|
|
1161
1209
|
return context;
|
|
1162
1210
|
}
|
|
@@ -1165,12 +1213,10 @@ const MenuGroup = /*#__PURE__*/React.forwardRef(function MenuGroup(componentProp
|
|
|
1165
1213
|
const {
|
|
1166
1214
|
render,
|
|
1167
1215
|
className,
|
|
1216
|
+
style,
|
|
1168
1217
|
...elementProps
|
|
1169
1218
|
} = componentProps;
|
|
1170
1219
|
const [labelId, setLabelId] = React.useState(undefined);
|
|
1171
|
-
const context = React.useMemo(() => ({
|
|
1172
|
-
setLabelId
|
|
1173
|
-
}), [setLabelId]);
|
|
1174
1220
|
const element = useRenderElement('div', componentProps, {
|
|
1175
1221
|
ref: forwardedRef,
|
|
1176
1222
|
props: {
|
|
@@ -1180,7 +1226,7 @@ const MenuGroup = /*#__PURE__*/React.forwardRef(function MenuGroup(componentProp
|
|
|
1180
1226
|
}
|
|
1181
1227
|
});
|
|
1182
1228
|
return /*#__PURE__*/jsx(MenuGroupContext.Provider, {
|
|
1183
|
-
value:
|
|
1229
|
+
value: setLabelId,
|
|
1184
1230
|
children: element
|
|
1185
1231
|
});
|
|
1186
1232
|
});
|
|
@@ -1192,17 +1238,16 @@ if (process.env.NODE_ENV !== "production") MenuGroup.displayName = "MenuGroup";
|
|
|
1192
1238
|
*
|
|
1193
1239
|
* Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
|
|
1194
1240
|
*/
|
|
1195
|
-
const MenuGroupLabel = /*#__PURE__*/React.forwardRef(function
|
|
1241
|
+
const MenuGroupLabel = /*#__PURE__*/React.forwardRef(function MenuGroupLabel(componentProps, forwardedRef) {
|
|
1196
1242
|
const {
|
|
1197
|
-
className,
|
|
1198
1243
|
render,
|
|
1244
|
+
className,
|
|
1245
|
+
style,
|
|
1199
1246
|
id: idProp,
|
|
1200
1247
|
...elementProps
|
|
1201
1248
|
} = componentProps;
|
|
1202
1249
|
const id = useBaseUiId(idProp);
|
|
1203
|
-
const
|
|
1204
|
-
setLabelId
|
|
1205
|
-
} = useMenuGroupRootContext();
|
|
1250
|
+
const setLabelId = useMenuGroupRootContext();
|
|
1206
1251
|
useIsoLayoutEffect(() => {
|
|
1207
1252
|
setLabelId(id);
|
|
1208
1253
|
return () => {
|
|
@@ -1235,6 +1280,7 @@ const MenuItem = /*#__PURE__*/React.forwardRef(function MenuItem(componentProps,
|
|
|
1235
1280
|
nativeButton = false,
|
|
1236
1281
|
disabled = false,
|
|
1237
1282
|
closeOnClick = true,
|
|
1283
|
+
style,
|
|
1238
1284
|
...elementProps
|
|
1239
1285
|
} = componentProps;
|
|
1240
1286
|
const listItem = useCompositeListItem({
|
|
@@ -1257,7 +1303,7 @@ const MenuItem = /*#__PURE__*/React.forwardRef(function MenuItem(componentProps,
|
|
|
1257
1303
|
id,
|
|
1258
1304
|
store,
|
|
1259
1305
|
nativeButton,
|
|
1260
|
-
nodeId: menuPositionerContext?.nodeId,
|
|
1306
|
+
nodeId: menuPositionerContext?.context.nodeId,
|
|
1261
1307
|
itemMetadata: REGULAR_ITEM
|
|
1262
1308
|
});
|
|
1263
1309
|
const state = {
|
|
@@ -1285,6 +1331,7 @@ const MenuLinkItem = /*#__PURE__*/React.forwardRef(function MenuLinkItem(compone
|
|
|
1285
1331
|
id: idProp,
|
|
1286
1332
|
label,
|
|
1287
1333
|
closeOnClick = false,
|
|
1334
|
+
style,
|
|
1288
1335
|
...elementProps
|
|
1289
1336
|
} = componentProps;
|
|
1290
1337
|
const linkRef = React.useRef(null);
|
|
@@ -1292,7 +1339,7 @@ const MenuLinkItem = /*#__PURE__*/React.forwardRef(function MenuLinkItem(compone
|
|
|
1292
1339
|
label
|
|
1293
1340
|
});
|
|
1294
1341
|
const menuPositionerContext = useMenuPositionerContext(true);
|
|
1295
|
-
const nodeId = menuPositionerContext?.nodeId;
|
|
1342
|
+
const nodeId = menuPositionerContext?.context.nodeId;
|
|
1296
1343
|
const id = useBaseUiId(idProp);
|
|
1297
1344
|
const {
|
|
1298
1345
|
store
|
|
@@ -1319,9 +1366,9 @@ const MenuLinkItem = /*#__PURE__*/React.forwardRef(function MenuLinkItem(compone
|
|
|
1319
1366
|
function getItemProps(externalProps) {
|
|
1320
1367
|
return mergeProps(commonProps, externalProps, getButtonProps);
|
|
1321
1368
|
}
|
|
1322
|
-
const state =
|
|
1369
|
+
const state = {
|
|
1323
1370
|
highlighted
|
|
1324
|
-
}
|
|
1371
|
+
};
|
|
1325
1372
|
return useRenderElement('a', componentProps, {
|
|
1326
1373
|
state,
|
|
1327
1374
|
props: [itemProps, elementProps, getItemProps],
|
|
@@ -1345,6 +1392,7 @@ const MenuPopup = /*#__PURE__*/React.forwardRef(function MenuPopup(componentProp
|
|
|
1345
1392
|
const {
|
|
1346
1393
|
render,
|
|
1347
1394
|
className,
|
|
1395
|
+
style,
|
|
1348
1396
|
finalFocus,
|
|
1349
1397
|
...elementProps
|
|
1350
1398
|
} = componentProps;
|
|
@@ -1369,6 +1417,9 @@ const MenuPopup = /*#__PURE__*/React.forwardRef(function MenuPopup(componentProp
|
|
|
1369
1417
|
const floatingTreeRoot = store.useState('floatingTreeRoot');
|
|
1370
1418
|
const closeDelay = store.useState('closeDelay');
|
|
1371
1419
|
const activeTriggerElement = store.useState('activeTriggerElement');
|
|
1420
|
+
const hoverEnabled = store.useState('hoverEnabled');
|
|
1421
|
+
const disabled = store.useState('disabled');
|
|
1422
|
+
const openMethod = store.useState('openMethod');
|
|
1372
1423
|
const isContextMenu = parent.type === 'context-menu';
|
|
1373
1424
|
useOpenChangeComplete({
|
|
1374
1425
|
open,
|
|
@@ -1388,12 +1439,13 @@ const MenuPopup = /*#__PURE__*/React.forwardRef(function MenuPopup(componentProp
|
|
|
1388
1439
|
floatingTreeRoot.events.off('close', handleClose);
|
|
1389
1440
|
};
|
|
1390
1441
|
}, [floatingTreeRoot.events, store]);
|
|
1391
|
-
const hoverEnabled = store.useState('hoverEnabled');
|
|
1392
|
-
const disabled = store.useState('disabled');
|
|
1393
1442
|
useHoverFloatingInteraction(floatingContext, {
|
|
1394
1443
|
enabled: hoverEnabled && !disabled && !isContextMenu && parent.type !== 'menubar',
|
|
1395
1444
|
closeDelay
|
|
1396
1445
|
});
|
|
1446
|
+
const setPopupElement = React.useCallback(element => {
|
|
1447
|
+
store.set('popupElement', element);
|
|
1448
|
+
}, [store]);
|
|
1397
1449
|
const state = {
|
|
1398
1450
|
transitionStatus,
|
|
1399
1451
|
side,
|
|
@@ -1402,9 +1454,6 @@ const MenuPopup = /*#__PURE__*/React.forwardRef(function MenuPopup(componentProp
|
|
|
1402
1454
|
nested: parent.type === 'menu',
|
|
1403
1455
|
instant: instantType
|
|
1404
1456
|
};
|
|
1405
|
-
const setPopupElement = React.useCallback(element => {
|
|
1406
|
-
store.set('popupElement', element);
|
|
1407
|
-
}, [store]);
|
|
1408
1457
|
const element = useRenderElement('div', componentProps, {
|
|
1409
1458
|
state,
|
|
1410
1459
|
ref: [forwardedRef, store.context.popupRef, setPopupElement],
|
|
@@ -1425,6 +1474,7 @@ const MenuPopup = /*#__PURE__*/React.forwardRef(function MenuPopup(componentProp
|
|
|
1425
1474
|
}
|
|
1426
1475
|
return /*#__PURE__*/jsx(FloatingFocusManager, {
|
|
1427
1476
|
context: floatingContext,
|
|
1477
|
+
openInteractionType: openMethod,
|
|
1428
1478
|
modal: isContextMenu,
|
|
1429
1479
|
disabled: !mounted,
|
|
1430
1480
|
returnFocus: finalFocus === undefined ? returnFocus : finalFocus,
|
|
@@ -1488,6 +1538,7 @@ const MenuPositioner = /*#__PURE__*/React.forwardRef(function MenuPositioner(com
|
|
|
1488
1538
|
sticky = false,
|
|
1489
1539
|
disableAnchorTracking = false,
|
|
1490
1540
|
collisionAvoidance: collisionAvoidanceProp = DROPDOWN_COLLISION_AVOIDANCE,
|
|
1541
|
+
style,
|
|
1491
1542
|
...elementProps
|
|
1492
1543
|
} = componentProps;
|
|
1493
1544
|
const {
|
|
@@ -1501,6 +1552,7 @@ const MenuPositioner = /*#__PURE__*/React.forwardRef(function MenuPositioner(com
|
|
|
1501
1552
|
const mounted = store.useState('mounted');
|
|
1502
1553
|
const open = store.useState('open');
|
|
1503
1554
|
const modal = store.useState('modal');
|
|
1555
|
+
const openMethod = store.useState('openMethod');
|
|
1504
1556
|
const triggerElement = store.useState('activeTriggerElement');
|
|
1505
1557
|
const transitionStatus = store.useState('transitionStatus');
|
|
1506
1558
|
const positionerElement = store.useState('positionerElement');
|
|
@@ -1532,7 +1584,7 @@ const MenuPositioner = /*#__PURE__*/React.forwardRef(function MenuPositioner(com
|
|
|
1532
1584
|
computedAlign = computedAlign ?? 'start';
|
|
1533
1585
|
collisionAvoidance = componentProps.collisionAvoidance ?? POPUP_COLLISION_AVOIDANCE;
|
|
1534
1586
|
} else if (parent.type === 'menubar') {
|
|
1535
|
-
computedSide = computedSide ?? 'bottom';
|
|
1587
|
+
computedSide = computedSide ?? (parent.context.orientation === 'vertical' ? 'inline-end' : 'bottom');
|
|
1536
1588
|
computedAlign = computedAlign ?? 'start';
|
|
1537
1589
|
}
|
|
1538
1590
|
const contextMenu = parent.type === 'context-menu';
|
|
@@ -1557,20 +1609,6 @@ const MenuPositioner = /*#__PURE__*/React.forwardRef(function MenuPositioner(com
|
|
|
1557
1609
|
externalTree: floatingTreeRoot,
|
|
1558
1610
|
adaptiveOrigin: hasViewport ? adaptiveOrigin : undefined
|
|
1559
1611
|
});
|
|
1560
|
-
const positionerProps = React.useMemo(() => {
|
|
1561
|
-
const hiddenStyles = {};
|
|
1562
|
-
if (!open) {
|
|
1563
|
-
hiddenStyles.pointerEvents = 'none';
|
|
1564
|
-
}
|
|
1565
|
-
return {
|
|
1566
|
-
role: 'presentation',
|
|
1567
|
-
hidden: !mounted,
|
|
1568
|
-
style: {
|
|
1569
|
-
...positioner.positionerStyles,
|
|
1570
|
-
...hiddenStyles
|
|
1571
|
-
}
|
|
1572
|
-
};
|
|
1573
|
-
}, [open, mounted, positioner.positionerStyles]);
|
|
1574
1612
|
React.useEffect(() => {
|
|
1575
1613
|
function onMenuOpenChange(details) {
|
|
1576
1614
|
if (details.open) {
|
|
@@ -1678,19 +1716,16 @@ const MenuPositioner = /*#__PURE__*/React.forwardRef(function MenuPositioner(com
|
|
|
1678
1716
|
nested: parent.type === 'menu',
|
|
1679
1717
|
instant: instantType
|
|
1680
1718
|
};
|
|
1681
|
-
const
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
stateAttributesMapping: popupStateMapping,
|
|
1692
|
-
ref: [forwardedRef, store.useStateSetter('positionerElement')],
|
|
1693
|
-
props: [positionerProps, getDisabledMountTransitionStyles(transitionStatus), elementProps]
|
|
1719
|
+
const menubarModal = parent.type === 'menubar' && parent.context.modal;
|
|
1720
|
+
const popupModal = modal && lastOpenChangeReason !== triggerHover;
|
|
1721
|
+
useAnchoredPopupScrollLock(open && (menubarModal || popupModal), openMethod === 'touch', positionerElement, triggerElement);
|
|
1722
|
+
const element = usePositioner(componentProps, state, {
|
|
1723
|
+
styles: positioner.positionerStyles,
|
|
1724
|
+
transitionStatus,
|
|
1725
|
+
props: elementProps,
|
|
1726
|
+
refs: [forwardedRef, store.useStateSetter('positionerElement')],
|
|
1727
|
+
hidden: !mounted,
|
|
1728
|
+
inert: !open
|
|
1694
1729
|
});
|
|
1695
1730
|
const shouldRenderBackdrop = mounted && parent.type !== 'menu' && (parent.type !== 'menubar' && modal && lastOpenChangeReason !== triggerHover || parent.type === 'menubar' && parent.context.modal);
|
|
1696
1731
|
|
|
@@ -1702,7 +1737,7 @@ const MenuPositioner = /*#__PURE__*/React.forwardRef(function MenuPositioner(com
|
|
|
1702
1737
|
backdropCutout = triggerElement;
|
|
1703
1738
|
}
|
|
1704
1739
|
return /*#__PURE__*/jsxs(MenuPositionerContext.Provider, {
|
|
1705
|
-
value:
|
|
1740
|
+
value: positioner,
|
|
1706
1741
|
children: [shouldRenderBackdrop && /*#__PURE__*/jsx(InternalBackdrop, {
|
|
1707
1742
|
ref: parent.type === 'context-menu' || parent.type === 'nested-context-menu' ? parent.context.internalBackdropRef : null,
|
|
1708
1743
|
inert: inertValue(!open),
|
|
@@ -1743,16 +1778,18 @@ const MenuRadioGroup = /*#__PURE__*/React.memo(/*#__PURE__*/React.forwardRef(fun
|
|
|
1743
1778
|
defaultValue,
|
|
1744
1779
|
onValueChange: onValueChangeProp,
|
|
1745
1780
|
disabled = false,
|
|
1781
|
+
style,
|
|
1782
|
+
'aria-labelledby': ariaLabelledByProp,
|
|
1746
1783
|
...elementProps
|
|
1747
1784
|
} = componentProps;
|
|
1785
|
+
const [labelId, setLabelId] = React.useState(undefined);
|
|
1748
1786
|
const [value, setValueUnwrapped] = useControlled({
|
|
1749
1787
|
controlled: valueProp,
|
|
1750
1788
|
default: defaultValue,
|
|
1751
1789
|
name: 'MenuRadioGroup'
|
|
1752
1790
|
});
|
|
1753
|
-
const onValueChange = useStableCallback(onValueChangeProp);
|
|
1754
1791
|
const setValue = useStableCallback((newValue, eventDetails) => {
|
|
1755
|
-
|
|
1792
|
+
onValueChangeProp?.(newValue, eventDetails);
|
|
1756
1793
|
if (eventDetails.isCanceled) {
|
|
1757
1794
|
return;
|
|
1758
1795
|
}
|
|
@@ -1766,6 +1803,7 @@ const MenuRadioGroup = /*#__PURE__*/React.memo(/*#__PURE__*/React.forwardRef(fun
|
|
|
1766
1803
|
ref: forwardedRef,
|
|
1767
1804
|
props: {
|
|
1768
1805
|
role: 'group',
|
|
1806
|
+
'aria-labelledby': ariaLabelledByProp ?? labelId,
|
|
1769
1807
|
'aria-disabled': disabled || undefined,
|
|
1770
1808
|
...elementProps
|
|
1771
1809
|
}
|
|
@@ -1775,9 +1813,12 @@ const MenuRadioGroup = /*#__PURE__*/React.memo(/*#__PURE__*/React.forwardRef(fun
|
|
|
1775
1813
|
setValue,
|
|
1776
1814
|
disabled
|
|
1777
1815
|
}), [value, setValue, disabled]);
|
|
1778
|
-
return /*#__PURE__*/jsx(
|
|
1779
|
-
value:
|
|
1780
|
-
children:
|
|
1816
|
+
return /*#__PURE__*/jsx(MenuGroupContext.Provider, {
|
|
1817
|
+
value: setLabelId,
|
|
1818
|
+
children: /*#__PURE__*/jsx(MenuRadioGroupContext.Provider, {
|
|
1819
|
+
value: context,
|
|
1820
|
+
children: element
|
|
1821
|
+
})
|
|
1781
1822
|
});
|
|
1782
1823
|
}));
|
|
1783
1824
|
if (process.env.NODE_ENV !== "production") MenuRadioGroup.displayName = "MenuRadioGroup";
|
|
@@ -1802,6 +1843,7 @@ const MenuRadioItem = /*#__PURE__*/React.forwardRef(function MenuRadioItem(compo
|
|
|
1802
1843
|
disabled: disabledProp = false,
|
|
1803
1844
|
closeOnClick = false,
|
|
1804
1845
|
value,
|
|
1846
|
+
style,
|
|
1805
1847
|
...elementProps
|
|
1806
1848
|
} = componentProps;
|
|
1807
1849
|
const listItem = useCompositeListItem({
|
|
@@ -1831,7 +1873,7 @@ const MenuRadioItem = /*#__PURE__*/React.forwardRef(function MenuRadioItem(compo
|
|
|
1831
1873
|
id,
|
|
1832
1874
|
store,
|
|
1833
1875
|
nativeButton,
|
|
1834
|
-
nodeId: menuPositionerContext?.nodeId,
|
|
1876
|
+
nodeId: menuPositionerContext?.context.nodeId,
|
|
1835
1877
|
itemMetadata: REGULAR_ITEM
|
|
1836
1878
|
});
|
|
1837
1879
|
const state = React.useMemo(() => ({
|
|
@@ -1839,13 +1881,12 @@ const MenuRadioItem = /*#__PURE__*/React.forwardRef(function MenuRadioItem(compo
|
|
|
1839
1881
|
highlighted,
|
|
1840
1882
|
checked
|
|
1841
1883
|
}), [disabled, highlighted, checked]);
|
|
1842
|
-
|
|
1843
|
-
const details = {
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
};
|
|
1884
|
+
function handleClick(event) {
|
|
1885
|
+
const details = createChangeEventDetails(itemPress, event.nativeEvent, undefined, {
|
|
1886
|
+
preventUnmountOnClose() {}
|
|
1887
|
+
});
|
|
1847
1888
|
setSelectedValue(value, details);
|
|
1848
|
-
}
|
|
1889
|
+
}
|
|
1849
1890
|
const element = useRenderElement('div', componentProps, {
|
|
1850
1891
|
state,
|
|
1851
1892
|
stateAttributesMapping: itemMapping,
|
|
@@ -1873,6 +1914,7 @@ const MenuRadioItemIndicator = /*#__PURE__*/React.forwardRef(function MenuRadioI
|
|
|
1873
1914
|
const {
|
|
1874
1915
|
render,
|
|
1875
1916
|
className,
|
|
1917
|
+
style,
|
|
1876
1918
|
keepMounted = false,
|
|
1877
1919
|
...elementProps
|
|
1878
1920
|
} = componentProps;
|
|
@@ -1925,7 +1967,9 @@ const selectors = {
|
|
|
1925
1967
|
...popupStoreSelectors,
|
|
1926
1968
|
disabled: createSelector(state => state.parent.type === 'menubar' ? state.parent.context.disabled || state.disabled : state.disabled),
|
|
1927
1969
|
modal: createSelector(state => (state.parent.type === undefined || state.parent.type === 'context-menu') && (state.modal ?? true)),
|
|
1970
|
+
openMethod: createSelector(state => state.openMethod),
|
|
1928
1971
|
allowMouseEnter: createSelector(state => state.allowMouseEnter),
|
|
1972
|
+
highlightItemOnHover: createSelector(state => state.highlightItemOnHover),
|
|
1929
1973
|
stickIfOpen: createSelector(state => state.stickIfOpen),
|
|
1930
1974
|
parent: createSelector(state => state.parent),
|
|
1931
1975
|
rootId: createSelector(state => {
|
|
@@ -2034,7 +2078,9 @@ function createInitialState() {
|
|
|
2034
2078
|
...createInitialPopupStoreState(),
|
|
2035
2079
|
disabled: false,
|
|
2036
2080
|
modal: true,
|
|
2081
|
+
openMethod: null,
|
|
2037
2082
|
allowMouseEnter: false,
|
|
2083
|
+
highlightItemOnHover: true,
|
|
2038
2084
|
stickIfOpen: true,
|
|
2039
2085
|
parent: {
|
|
2040
2086
|
type: undefined
|
|
@@ -2116,46 +2162,19 @@ const MenuRoot = fastComponent(function MenuRoot(props) {
|
|
|
2116
2162
|
triggerIdProp,
|
|
2117
2163
|
parent: parentFromContext
|
|
2118
2164
|
});
|
|
2119
|
-
|
|
2120
|
-
// Support initially open state when uncontrolled
|
|
2121
|
-
useOnFirstRender(() => {
|
|
2122
|
-
if (openProp === undefined && store.state.open === false && defaultOpen === true) {
|
|
2123
|
-
store.update({
|
|
2124
|
-
open: true,
|
|
2125
|
-
activeTriggerId: defaultTriggerIdProp
|
|
2126
|
-
});
|
|
2127
|
-
}
|
|
2128
|
-
});
|
|
2165
|
+
useInitialOpenSync(store, openProp, defaultOpen, defaultTriggerIdProp);
|
|
2129
2166
|
store.useControlledProp('openProp', openProp);
|
|
2130
2167
|
store.useControlledProp('triggerIdProp', triggerIdProp);
|
|
2131
2168
|
store.useContextCallback('onOpenChangeComplete', onOpenChangeComplete);
|
|
2169
|
+
const rootId = useId();
|
|
2170
|
+
const floatingId = useId();
|
|
2132
2171
|
const floatingTreeRoot = store.useState('floatingTreeRoot');
|
|
2133
2172
|
const floatingNodeIdFromContext = useFloatingNodeId(floatingTreeRoot);
|
|
2134
2173
|
const floatingParentNodeIdFromContext = useFloatingParentNodeId();
|
|
2135
|
-
useIsoLayoutEffect(() => {
|
|
2136
|
-
if (contextMenuContext && !parentMenuRootContext) {
|
|
2137
|
-
// This is a context menu root.
|
|
2138
|
-
// It doesn't support detached triggers yet, so we have to sync the parent context manually.
|
|
2139
|
-
store.update({
|
|
2140
|
-
parent: {
|
|
2141
|
-
type: 'context-menu',
|
|
2142
|
-
context: contextMenuContext
|
|
2143
|
-
},
|
|
2144
|
-
floatingNodeId: floatingNodeIdFromContext,
|
|
2145
|
-
floatingParentNodeId: floatingParentNodeIdFromContext
|
|
2146
|
-
});
|
|
2147
|
-
} else if (parentMenuRootContext) {
|
|
2148
|
-
store.update({
|
|
2149
|
-
floatingNodeId: floatingNodeIdFromContext,
|
|
2150
|
-
floatingParentNodeId: floatingParentNodeIdFromContext
|
|
2151
|
-
});
|
|
2152
|
-
}
|
|
2153
|
-
}, [contextMenuContext, parentMenuRootContext, floatingNodeIdFromContext, floatingParentNodeIdFromContext, store]);
|
|
2154
2174
|
const open = store.useState('open');
|
|
2155
2175
|
const activeTriggerElement = store.useState('activeTriggerElement');
|
|
2156
2176
|
const positionerElement = store.useState('positionerElement');
|
|
2157
2177
|
const hoverEnabled = store.useState('hoverEnabled');
|
|
2158
|
-
const modal = store.useState('modal');
|
|
2159
2178
|
const disabled = store.useState('disabled');
|
|
2160
2179
|
const lastOpenChangeReason = store.useState('lastOpenChangeReason');
|
|
2161
2180
|
const parent = store.useState('parent');
|
|
@@ -2163,22 +2182,27 @@ const MenuRoot = fastComponent(function MenuRoot(props) {
|
|
|
2163
2182
|
const payload = store.useState('payload');
|
|
2164
2183
|
const floatingParentNodeId = store.useState('floatingParentNodeId');
|
|
2165
2184
|
const openEventRef = React.useRef(null);
|
|
2185
|
+
const allowOutsidePressDismissalRef = React.useRef(parent.type !== 'context-menu');
|
|
2186
|
+
const allowOutsidePressDismissalTimeout = useTimeout();
|
|
2187
|
+
const allowTouchToCloseRef = React.useRef(true);
|
|
2188
|
+
const allowTouchToCloseTimeout = useTimeout();
|
|
2166
2189
|
const nested = floatingParentNodeId != null;
|
|
2167
|
-
let floatingEvents;
|
|
2168
2190
|
if (process.env.NODE_ENV !== 'production') {
|
|
2169
2191
|
if (parent.type !== undefined && modalProp !== undefined) {
|
|
2170
2192
|
console.warn('Base UI: The `modal` prop is not supported on nested menus. It will be ignored.');
|
|
2171
2193
|
}
|
|
2172
2194
|
}
|
|
2173
|
-
store.useSyncedValues({
|
|
2174
|
-
disabled: disabledProp,
|
|
2175
|
-
modal: parent.type === undefined ? modalProp : undefined,
|
|
2176
|
-
rootId: useId()
|
|
2177
|
-
});
|
|
2178
2195
|
const {
|
|
2179
2196
|
openMethod,
|
|
2180
2197
|
triggerProps: interactionTypeProps
|
|
2181
2198
|
} = useOpenInteractionType(open);
|
|
2199
|
+
store.useSyncedValues({
|
|
2200
|
+
disabled: disabledProp,
|
|
2201
|
+
highlightItemOnHover,
|
|
2202
|
+
modal: parent.type === undefined ? modalProp : undefined,
|
|
2203
|
+
openMethod,
|
|
2204
|
+
rootId
|
|
2205
|
+
});
|
|
2182
2206
|
useImplicitActiveTrigger(store);
|
|
2183
2207
|
const {
|
|
2184
2208
|
forceUnmount
|
|
@@ -2188,8 +2212,25 @@ const MenuRoot = fastComponent(function MenuRoot(props) {
|
|
|
2188
2212
|
stickIfOpen: true
|
|
2189
2213
|
});
|
|
2190
2214
|
});
|
|
2191
|
-
|
|
2192
|
-
|
|
2215
|
+
useIsoLayoutEffect(() => {
|
|
2216
|
+
if (contextMenuContext && !parentMenuRootContext) {
|
|
2217
|
+
// This is a context menu root.
|
|
2218
|
+
// It doesn't support detached triggers yet, so we have to sync the parent context manually.
|
|
2219
|
+
store.update({
|
|
2220
|
+
parent: {
|
|
2221
|
+
type: 'context-menu',
|
|
2222
|
+
context: contextMenuContext
|
|
2223
|
+
},
|
|
2224
|
+
floatingNodeId: floatingNodeIdFromContext,
|
|
2225
|
+
floatingParentNodeId: floatingParentNodeIdFromContext
|
|
2226
|
+
});
|
|
2227
|
+
} else if (parentMenuRootContext) {
|
|
2228
|
+
store.update({
|
|
2229
|
+
floatingNodeId: floatingNodeIdFromContext,
|
|
2230
|
+
floatingParentNodeId: floatingParentNodeIdFromContext
|
|
2231
|
+
});
|
|
2232
|
+
}
|
|
2233
|
+
}, [contextMenuContext, parentMenuRootContext, floatingNodeIdFromContext, floatingParentNodeIdFromContext, store]);
|
|
2193
2234
|
React.useEffect(() => {
|
|
2194
2235
|
if (!open) {
|
|
2195
2236
|
openEventRef.current = null;
|
|
@@ -2210,22 +2251,17 @@ const MenuRoot = fastComponent(function MenuRoot(props) {
|
|
|
2210
2251
|
allowOutsidePressDismissalRef.current = true;
|
|
2211
2252
|
});
|
|
2212
2253
|
}, [allowOutsidePressDismissalTimeout, open, parent.type]);
|
|
2213
|
-
useScrollLock(open && modal && lastOpenChangeReason !== triggerHover && openMethod !== 'touch', positionerElement);
|
|
2214
2254
|
useIsoLayoutEffect(() => {
|
|
2215
2255
|
if (!open && !hoverEnabled) {
|
|
2216
2256
|
store.set('hoverEnabled', true);
|
|
2217
2257
|
}
|
|
2218
2258
|
}, [open, hoverEnabled, store]);
|
|
2219
|
-
const allowTouchToCloseRef = React.useRef(true);
|
|
2220
|
-
const allowTouchToCloseTimeout = useTimeout();
|
|
2221
2259
|
const setOpen = useStableCallback((nextOpen, eventDetails) => {
|
|
2222
2260
|
const reason = eventDetails.reason;
|
|
2223
2261
|
if (open === nextOpen && eventDetails.trigger === activeTriggerElement && lastOpenChangeReason === reason) {
|
|
2224
2262
|
return;
|
|
2225
2263
|
}
|
|
2226
|
-
|
|
2227
|
-
store.set('preventUnmountingOnClose', true);
|
|
2228
|
-
};
|
|
2264
|
+
const shouldPreventUnmountOnClose = attachPreventUnmountOnClose(eventDetails);
|
|
2229
2265
|
|
|
2230
2266
|
// Do not immediately reset the activeTriggerId to allow
|
|
2231
2267
|
// exit animations to play and focus to be returned correctly.
|
|
@@ -2236,31 +2272,12 @@ const MenuRoot = fastComponent(function MenuRoot(props) {
|
|
|
2236
2272
|
if (eventDetails.isCanceled) {
|
|
2237
2273
|
return;
|
|
2238
2274
|
}
|
|
2239
|
-
|
|
2240
|
-
open: nextOpen,
|
|
2241
|
-
nativeEvent: eventDetails.event,
|
|
2242
|
-
reason: eventDetails.reason,
|
|
2243
|
-
nested
|
|
2244
|
-
};
|
|
2245
|
-
floatingEvents?.emit('openchange', details);
|
|
2275
|
+
store.state.floatingRootContext.dispatchOpenChange(nextOpen, eventDetails);
|
|
2246
2276
|
const nativeEvent = eventDetails.event;
|
|
2247
2277
|
if (nextOpen === false && nativeEvent?.type === 'click' && nativeEvent.pointerType === 'touch' && !allowTouchToCloseRef.current) {
|
|
2248
2278
|
return;
|
|
2249
2279
|
}
|
|
2250
2280
|
|
|
2251
|
-
// Workaround `enableFocusInside` in Floating UI setting `tabindex=0` of a non-highlighted
|
|
2252
|
-
// option upon close when tabbing out due to `keepMounted=true`:
|
|
2253
|
-
// https://github.com/floating-ui/floating-ui/pull/3004/files#diff-962a7439cdeb09ea98d4b622a45d517bce07ad8c3f866e089bda05f4b0bbd875R194-R199
|
|
2254
|
-
// This otherwise causes options to retain `tabindex=0` incorrectly when the popup is closed
|
|
2255
|
-
// when tabbing outside.
|
|
2256
|
-
if (!nextOpen && activeIndex !== null) {
|
|
2257
|
-
const activeOption = store.context.itemDomElements.current[activeIndex];
|
|
2258
|
-
// Wait for Floating UI's focus effect to have fired
|
|
2259
|
-
queueMicrotask(() => {
|
|
2260
|
-
activeOption?.setAttribute('tabindex', '-1');
|
|
2261
|
-
});
|
|
2262
|
-
}
|
|
2263
|
-
|
|
2264
2281
|
// Prevent the menu from closing on mobile devices that have a delayed click event.
|
|
2265
2282
|
// In some cases the menu, when tapped, will fire the focus event first and then the click event.
|
|
2266
2283
|
// Without this guard, the menu will close immediately after opening.
|
|
@@ -2280,14 +2297,7 @@ const MenuRoot = fastComponent(function MenuRoot(props) {
|
|
|
2280
2297
|
openChangeReason: reason
|
|
2281
2298
|
};
|
|
2282
2299
|
openEventRef.current = eventDetails.event ?? null;
|
|
2283
|
-
|
|
2284
|
-
// If a popup is closing, the `trigger` may be null.
|
|
2285
|
-
// We want to keep the previous value so that exit animations are played and focus is returned correctly.
|
|
2286
|
-
const newTriggerId = eventDetails.trigger?.id ?? null;
|
|
2287
|
-
if (newTriggerId || nextOpen) {
|
|
2288
|
-
updatedState.activeTriggerId = newTriggerId;
|
|
2289
|
-
updatedState.activeTriggerElement = eventDetails.trigger ?? null;
|
|
2290
|
-
}
|
|
2300
|
+
setPopupOpenState(updatedState, nextOpen, eventDetails.trigger, shouldPreventUnmountOnClose());
|
|
2291
2301
|
store.update(updatedState);
|
|
2292
2302
|
if (parent.type === 'menubar' && (reason === triggerFocus || reason === focusOut || reason === triggerHover || reason === listNavigation || reason === siblingOpen)) {
|
|
2293
2303
|
store.set('instantType', 'group');
|
|
@@ -2297,33 +2307,13 @@ const MenuRoot = fastComponent(function MenuRoot(props) {
|
|
|
2297
2307
|
store.set('instantType', undefined);
|
|
2298
2308
|
}
|
|
2299
2309
|
});
|
|
2300
|
-
const createMenuEventDetails = React.useCallback(reason => {
|
|
2301
|
-
const details = createChangeEventDetails(reason);
|
|
2302
|
-
details.preventUnmountOnClose = () => {
|
|
2303
|
-
store.set('preventUnmountingOnClose', true);
|
|
2304
|
-
};
|
|
2305
|
-
return details;
|
|
2306
|
-
}, [store]);
|
|
2307
|
-
const handleImperativeClose = React.useCallback(() => {
|
|
2308
|
-
store.setOpen(false, createMenuEventDetails(imperativeAction));
|
|
2309
|
-
}, [store, createMenuEventDetails]);
|
|
2310
|
-
React.useImperativeHandle(actionsRef, () => ({
|
|
2311
|
-
unmount: forceUnmount,
|
|
2312
|
-
close: handleImperativeClose
|
|
2313
|
-
}), [forceUnmount, handleImperativeClose]);
|
|
2314
|
-
let ctx;
|
|
2315
|
-
if (parent.type === 'context-menu') {
|
|
2316
|
-
ctx = parent.context;
|
|
2317
|
-
}
|
|
2318
|
-
React.useImperativeHandle(ctx?.positionerRef, () => positionerElement, [positionerElement]);
|
|
2319
|
-
React.useImperativeHandle(ctx?.actionsRef, () => ({
|
|
2320
|
-
setOpen
|
|
2321
|
-
}), [setOpen]);
|
|
2322
2310
|
const floatingRootContext = useSyncedFloatingRootContext({
|
|
2323
2311
|
popupStore: store,
|
|
2312
|
+
floatingId,
|
|
2313
|
+
nested: floatingParentNodeIdFromContext != null,
|
|
2324
2314
|
onOpenChange: setOpen
|
|
2325
2315
|
});
|
|
2326
|
-
floatingEvents = floatingRootContext.context.events;
|
|
2316
|
+
const floatingEvents = floatingRootContext.context.events;
|
|
2327
2317
|
React.useEffect(() => {
|
|
2328
2318
|
const handleSetOpenEvent = ({
|
|
2329
2319
|
open: nextOpen,
|
|
@@ -2334,6 +2324,21 @@ const MenuRoot = fastComponent(function MenuRoot(props) {
|
|
|
2334
2324
|
floatingEvents?.off('setOpen', handleSetOpenEvent);
|
|
2335
2325
|
};
|
|
2336
2326
|
}, [floatingEvents, setOpen]);
|
|
2327
|
+
const handleImperativeClose = React.useCallback(() => {
|
|
2328
|
+
store.setOpen(false, createChangeEventDetails(imperativeAction));
|
|
2329
|
+
}, [store]);
|
|
2330
|
+
React.useImperativeHandle(actionsRef, () => ({
|
|
2331
|
+
unmount: forceUnmount,
|
|
2332
|
+
close: handleImperativeClose
|
|
2333
|
+
}), [forceUnmount, handleImperativeClose]);
|
|
2334
|
+
let ctx;
|
|
2335
|
+
if (parent.type === 'context-menu') {
|
|
2336
|
+
ctx = parent.context;
|
|
2337
|
+
}
|
|
2338
|
+
React.useImperativeHandle(ctx?.positionerRef, () => positionerElement, [positionerElement]);
|
|
2339
|
+
React.useImperativeHandle(ctx?.actionsRef, () => ({
|
|
2340
|
+
setOpen
|
|
2341
|
+
}), [setOpen]);
|
|
2337
2342
|
const dismiss = useDismiss(floatingRootContext, {
|
|
2338
2343
|
enabled: !disabled,
|
|
2339
2344
|
bubbles: {
|
|
@@ -2347,9 +2352,6 @@ const MenuRoot = fastComponent(function MenuRoot(props) {
|
|
|
2347
2352
|
},
|
|
2348
2353
|
externalTree: nested ? floatingTreeRoot : undefined
|
|
2349
2354
|
});
|
|
2350
|
-
const role = useRole(floatingRootContext, {
|
|
2351
|
-
role: 'menu'
|
|
2352
|
-
});
|
|
2353
2355
|
const direction = useDirection();
|
|
2354
2356
|
const setActiveIndex = React.useCallback(index => {
|
|
2355
2357
|
if (store.select('activeIndex') === index) {
|
|
@@ -2372,10 +2374,11 @@ const MenuRoot = fastComponent(function MenuRoot(props) {
|
|
|
2372
2374
|
externalTree: nested ? floatingTreeRoot : undefined,
|
|
2373
2375
|
focusItemOnHover: highlightItemOnHover
|
|
2374
2376
|
});
|
|
2375
|
-
const
|
|
2377
|
+
const onTyping = React.useCallback(nextTyping => {
|
|
2376
2378
|
store.context.typingRef.current = nextTyping;
|
|
2377
2379
|
}, [store]);
|
|
2378
2380
|
const typeahead = useTypeahead(floatingRootContext, {
|
|
2381
|
+
enabled: !disabled,
|
|
2379
2382
|
listRef: store.context.itemLabels,
|
|
2380
2383
|
elementsRef: store.context.itemDomElements,
|
|
2381
2384
|
activeIndex,
|
|
@@ -2385,34 +2388,28 @@ const MenuRoot = fastComponent(function MenuRoot(props) {
|
|
|
2385
2388
|
store.set('activeIndex', index);
|
|
2386
2389
|
}
|
|
2387
2390
|
},
|
|
2388
|
-
|
|
2391
|
+
onTyping
|
|
2389
2392
|
});
|
|
2390
|
-
const {
|
|
2391
|
-
getReferenceProps,
|
|
2392
|
-
getFloatingProps,
|
|
2393
|
-
getItemProps,
|
|
2394
|
-
getTriggerProps
|
|
2395
|
-
} = useInteractions([dismiss, role, listNavigation$1, typeahead]);
|
|
2396
2393
|
const activeTriggerProps = React.useMemo(() => {
|
|
2397
|
-
const mergedProps = mergeProps(
|
|
2394
|
+
const mergedProps = mergeProps(typeahead.reference, listNavigation$1.reference, dismiss.reference, {
|
|
2398
2395
|
onMouseMove() {
|
|
2399
2396
|
store.set('allowMouseEnter', true);
|
|
2400
2397
|
}
|
|
2401
2398
|
}, interactionTypeProps);
|
|
2402
|
-
|
|
2399
|
+
mergedProps['aria-haspopup'] = 'menu';
|
|
2400
|
+
mergedProps['aria-expanded'] = open;
|
|
2403
2401
|
return mergedProps;
|
|
2404
|
-
}, [
|
|
2402
|
+
}, [store, typeahead.reference, listNavigation$1.reference, dismiss.reference, interactionTypeProps, open]);
|
|
2405
2403
|
const inactiveTriggerProps = React.useMemo(() => {
|
|
2406
|
-
const
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
}
|
|
2410
|
-
const mergedProps = mergeProps(triggerProps, interactionTypeProps);
|
|
2411
|
-
delete mergedProps.role;
|
|
2412
|
-
delete mergedProps['aria-controls'];
|
|
2404
|
+
const mergedProps = mergeProps(listNavigation$1.trigger, dismiss.trigger, interactionTypeProps);
|
|
2405
|
+
mergedProps['aria-haspopup'] = 'menu';
|
|
2406
|
+
mergedProps['aria-expanded'] = false;
|
|
2413
2407
|
return mergedProps;
|
|
2414
|
-
}, [
|
|
2415
|
-
const popupProps = React.useMemo(() =>
|
|
2408
|
+
}, [listNavigation$1.trigger, dismiss.trigger, interactionTypeProps]);
|
|
2409
|
+
const popupProps = React.useMemo(() => mergeProps(FOCUSABLE_POPUP_PROPS, {
|
|
2410
|
+
id: floatingId,
|
|
2411
|
+
role: 'menu',
|
|
2412
|
+
'aria-labelledby': activeTriggerElement?.id,
|
|
2416
2413
|
onMouseMove() {
|
|
2417
2414
|
store.set('allowMouseEnter', true);
|
|
2418
2415
|
if (parent.type === 'menu') {
|
|
@@ -2433,9 +2430,9 @@ const MenuRoot = fastComponent(function MenuRoot(props) {
|
|
|
2433
2430
|
relay(event);
|
|
2434
2431
|
}
|
|
2435
2432
|
}
|
|
2436
|
-
}), [
|
|
2437
|
-
const itemProps =
|
|
2438
|
-
store
|
|
2433
|
+
}, typeahead.floating, listNavigation$1.floating, dismiss.floating), [activeTriggerElement, floatingId, parent.type, store, typeahead.floating, listNavigation$1.floating, dismiss.floating]);
|
|
2434
|
+
const itemProps = listNavigation$1.item ?? EMPTY_OBJECT;
|
|
2435
|
+
usePopupInteractionProps(store, {
|
|
2439
2436
|
floatingRootContext,
|
|
2440
2437
|
activeTriggerProps,
|
|
2441
2438
|
inactiveTriggerProps,
|
|
@@ -2465,7 +2462,7 @@ if (process.env.NODE_ENV !== "production") MenuRoot.displayName = "MenuRoot";
|
|
|
2465
2462
|
|
|
2466
2463
|
/**
|
|
2467
2464
|
* Groups all parts of a submenu.
|
|
2468
|
-
* Doesn
|
|
2465
|
+
* Doesn't render its own HTML element.
|
|
2469
2466
|
*
|
|
2470
2467
|
* Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
|
|
2471
2468
|
*/
|
|
@@ -2495,7 +2492,7 @@ function useCompositeItem(params = {}) {
|
|
|
2495
2492
|
const isHighlighted = highlightedIndex === index;
|
|
2496
2493
|
const itemRef = React.useRef(null);
|
|
2497
2494
|
const mergedRef = useMergedRefs(ref, itemRef);
|
|
2498
|
-
const compositeProps =
|
|
2495
|
+
const compositeProps = {
|
|
2499
2496
|
tabIndex: isHighlighted ? 0 : -1,
|
|
2500
2497
|
onFocus() {
|
|
2501
2498
|
onHighlightedIndexChange(index);
|
|
@@ -2510,7 +2507,7 @@ function useCompositeItem(params = {}) {
|
|
|
2510
2507
|
item.focus();
|
|
2511
2508
|
}
|
|
2512
2509
|
}
|
|
2513
|
-
}
|
|
2510
|
+
};
|
|
2514
2511
|
return {
|
|
2515
2512
|
compositeProps,
|
|
2516
2513
|
compositeRef: mergedRef,
|
|
@@ -2525,6 +2522,7 @@ function CompositeItem(componentProps) {
|
|
|
2525
2522
|
const {
|
|
2526
2523
|
render,
|
|
2527
2524
|
className,
|
|
2525
|
+
style,
|
|
2528
2526
|
state = EMPTY_OBJECT,
|
|
2529
2527
|
props = EMPTY_ARRAY,
|
|
2530
2528
|
refs = EMPTY_ARRAY,
|
|
@@ -2557,6 +2555,53 @@ function findRootOwnerId(node) {
|
|
|
2557
2555
|
return findRootOwnerId(getParentNode(node));
|
|
2558
2556
|
}
|
|
2559
2557
|
|
|
2558
|
+
/**
|
|
2559
|
+
* Minimal store interface required by the focus guard hook.
|
|
2560
|
+
* Both PopoverStore and MenuStore satisfy this interface.
|
|
2561
|
+
*/
|
|
2562
|
+
|
|
2563
|
+
/**
|
|
2564
|
+
* Provides focus guard handlers for popup triggers (Popover, Menu).
|
|
2565
|
+
*
|
|
2566
|
+
* When the popup is open, invisible focus guard elements are placed before and after
|
|
2567
|
+
* the trigger. These handlers close the popup and move focus to the appropriate
|
|
2568
|
+
* tabbable element when the guards receive focus (i.e. when the user tabs out).
|
|
2569
|
+
*/
|
|
2570
|
+
function useTriggerFocusGuards(store, triggerElementRef) {
|
|
2571
|
+
const preFocusGuardRef = React.useRef(null);
|
|
2572
|
+
function handlePreFocusGuardFocus(event) {
|
|
2573
|
+
ReactDOM.flushSync(() => {
|
|
2574
|
+
store.setOpen(false, createChangeEventDetails(focusOut, event.nativeEvent, event.currentTarget));
|
|
2575
|
+
});
|
|
2576
|
+
const previousTabbable = getTabbableBeforeElement(preFocusGuardRef.current);
|
|
2577
|
+
previousTabbable?.focus();
|
|
2578
|
+
}
|
|
2579
|
+
function handleFocusTargetFocus(event) {
|
|
2580
|
+
const positionerElement = store.select('positionerElement');
|
|
2581
|
+
if (positionerElement && isOutsideEvent(event, positionerElement)) {
|
|
2582
|
+
store.context.beforeContentFocusGuardRef.current?.focus();
|
|
2583
|
+
} else {
|
|
2584
|
+
ReactDOM.flushSync(() => {
|
|
2585
|
+
store.setOpen(false, createChangeEventDetails(focusOut, event.nativeEvent, event.currentTarget));
|
|
2586
|
+
});
|
|
2587
|
+
let nextTabbable = getTabbableAfterElement(store.context.triggerFocusTargetRef.current || triggerElementRef.current);
|
|
2588
|
+
while (nextTabbable !== null && contains(positionerElement, nextTabbable)) {
|
|
2589
|
+
const prevTabbable = nextTabbable;
|
|
2590
|
+
nextTabbable = getNextTabbable(nextTabbable);
|
|
2591
|
+
if (nextTabbable === prevTabbable) {
|
|
2592
|
+
break;
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
nextTabbable?.focus();
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
return {
|
|
2599
|
+
preFocusGuardRef,
|
|
2600
|
+
handlePreFocusGuardFocus,
|
|
2601
|
+
handleFocusTargetFocus
|
|
2602
|
+
};
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2560
2605
|
/**
|
|
2561
2606
|
* Returns `click` and `mousedown` handlers that fix the behavior of triggers of popups that are toggled by different events.
|
|
2562
2607
|
* For example, a button that opens a popup on mousedown and closes it on click.
|
|
@@ -2606,6 +2651,7 @@ const MenuTrigger = fastComponentRef(function MenuTrigger(componentProps, forwar
|
|
|
2606
2651
|
const {
|
|
2607
2652
|
render,
|
|
2608
2653
|
className,
|
|
2654
|
+
style,
|
|
2609
2655
|
disabled: disabledProp = false,
|
|
2610
2656
|
nativeButton = true,
|
|
2611
2657
|
id: idProp,
|
|
@@ -2625,6 +2671,7 @@ const MenuTrigger = fastComponentRef(function MenuTrigger(componentProps, forwar
|
|
|
2625
2671
|
const isTriggerActive = store.useState('isTriggerActive', thisTriggerId);
|
|
2626
2672
|
const floatingRootContext = store.useState('floatingRootContext');
|
|
2627
2673
|
const isOpenedByThisTrigger = store.useState('isOpenedByTrigger', thisTriggerId);
|
|
2674
|
+
const popupId = store.useState('triggerPopupId', thisTriggerId);
|
|
2628
2675
|
const triggerElementRef = React.useRef(null);
|
|
2629
2676
|
const parent = useMenuParent();
|
|
2630
2677
|
const compositeRootContext = useCompositeRootContext(true);
|
|
@@ -2708,7 +2755,8 @@ const MenuTrigger = fastComponentRef(function MenuTrigger(componentProps, forwar
|
|
|
2708
2755
|
},
|
|
2709
2756
|
triggerElementRef,
|
|
2710
2757
|
externalTree: floatingTreeRoot,
|
|
2711
|
-
isActiveTrigger: isTriggerActive
|
|
2758
|
+
isActiveTrigger: isTriggerActive,
|
|
2759
|
+
isClosing: () => store.select('transitionStatus') === 'ending'
|
|
2712
2760
|
});
|
|
2713
2761
|
|
|
2714
2762
|
// Whether to ignore clicks to open the menu.
|
|
@@ -2730,15 +2778,21 @@ const MenuTrigger = fastComponentRef(function MenuTrigger(componentProps, forwar
|
|
|
2730
2778
|
enabled: isInMenubar,
|
|
2731
2779
|
mouseDownAction: 'open'
|
|
2732
2780
|
});
|
|
2733
|
-
const localInteractionProps =
|
|
2781
|
+
const localInteractionProps = React.useMemo(() => mergeProps(focus.reference, click.reference), [focus.reference, click.reference]);
|
|
2782
|
+
const rootTriggerProps = store.useState('triggerProps', isMountedByThisTrigger);
|
|
2783
|
+
const {
|
|
2784
|
+
preFocusGuardRef,
|
|
2785
|
+
handlePreFocusGuardFocus,
|
|
2786
|
+
handleFocusTargetFocus
|
|
2787
|
+
} = useTriggerFocusGuards(store, triggerElementRef);
|
|
2734
2788
|
const state = {
|
|
2735
2789
|
disabled,
|
|
2736
2790
|
open: isOpenedByThisTrigger
|
|
2737
2791
|
};
|
|
2738
|
-
const rootTriggerProps = store.useState('triggerProps', isMountedByThisTrigger);
|
|
2739
2792
|
const ref = [triggerRef, forwardedRef, buttonRef, registerTrigger, triggerElementRef];
|
|
2740
|
-
const props = [localInteractionProps
|
|
2793
|
+
const props = [localInteractionProps, hoverProps ?? EMPTY_OBJECT, rootTriggerProps, {
|
|
2741
2794
|
'aria-haspopup': 'menu',
|
|
2795
|
+
'aria-controls': popupId,
|
|
2742
2796
|
id: thisTriggerId,
|
|
2743
2797
|
onMouseDown: event => {
|
|
2744
2798
|
if (store.select('open')) {
|
|
@@ -2757,33 +2811,6 @@ const MenuTrigger = fastComponentRef(function MenuTrigger(componentProps, forwar
|
|
|
2757
2811
|
}, isInMenubar ? {
|
|
2758
2812
|
role: 'menuitem'
|
|
2759
2813
|
} : {}, mixedToggleHandlers, elementProps, getButtonProps];
|
|
2760
|
-
const preFocusGuardRef = React.useRef(null);
|
|
2761
|
-
const handlePreFocusGuardFocus = useStableCallback(event => {
|
|
2762
|
-
ReactDOM.flushSync(() => {
|
|
2763
|
-
store.setOpen(false, createChangeEventDetails(focusOut, event.nativeEvent, event.currentTarget));
|
|
2764
|
-
});
|
|
2765
|
-
const previousTabbable = getTabbableBeforeElement(preFocusGuardRef.current);
|
|
2766
|
-
previousTabbable?.focus();
|
|
2767
|
-
});
|
|
2768
|
-
const handleFocusTargetFocus = useStableCallback(event => {
|
|
2769
|
-
const currentPositionerElement = store.select('positionerElement');
|
|
2770
|
-
if (currentPositionerElement && isOutsideEvent(event, currentPositionerElement)) {
|
|
2771
|
-
store.context.beforeContentFocusGuardRef.current?.focus();
|
|
2772
|
-
} else {
|
|
2773
|
-
ReactDOM.flushSync(() => {
|
|
2774
|
-
store.setOpen(false, createChangeEventDetails(focusOut, event.nativeEvent, event.currentTarget));
|
|
2775
|
-
});
|
|
2776
|
-
let nextTabbable = getTabbableAfterElement(store.context.triggerFocusTargetRef.current || triggerElementRef.current);
|
|
2777
|
-
while (nextTabbable !== null && contains(currentPositionerElement, nextTabbable)) {
|
|
2778
|
-
const prevTabbable = nextTabbable;
|
|
2779
|
-
nextTabbable = getNextTabbable(nextTabbable);
|
|
2780
|
-
if (nextTabbable === prevTabbable) {
|
|
2781
|
-
break;
|
|
2782
|
-
}
|
|
2783
|
-
}
|
|
2784
|
-
nextTabbable?.focus();
|
|
2785
|
-
}
|
|
2786
|
-
});
|
|
2787
2814
|
const element = useRenderElement('button', componentProps, {
|
|
2788
2815
|
enabled: !isInMenubar,
|
|
2789
2816
|
stateAttributesMapping: pressableTriggerOpenStateMapping,
|
|
@@ -2796,6 +2823,7 @@ const MenuTrigger = fastComponentRef(function MenuTrigger(componentProps, forwar
|
|
|
2796
2823
|
tag: "button",
|
|
2797
2824
|
render: render,
|
|
2798
2825
|
className: className,
|
|
2826
|
+
style: style,
|
|
2799
2827
|
state: state,
|
|
2800
2828
|
refs: ref,
|
|
2801
2829
|
props: props,
|
|
@@ -2879,10 +2907,11 @@ function useMenuParent() {
|
|
|
2879
2907
|
*
|
|
2880
2908
|
* Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
|
|
2881
2909
|
*/
|
|
2882
|
-
const MenuSubmenuTrigger = /*#__PURE__*/React.forwardRef(function
|
|
2910
|
+
const MenuSubmenuTrigger = /*#__PURE__*/React.forwardRef(function MenuSubmenuTrigger(componentProps, forwardedRef) {
|
|
2883
2911
|
const {
|
|
2884
2912
|
render,
|
|
2885
2913
|
className,
|
|
2914
|
+
style,
|
|
2886
2915
|
label,
|
|
2887
2916
|
id: idProp,
|
|
2888
2917
|
nativeButton = false,
|
|
@@ -2892,7 +2921,9 @@ const MenuSubmenuTrigger = /*#__PURE__*/React.forwardRef(function SubmenuTrigger
|
|
|
2892
2921
|
disabled: disabledProp = false,
|
|
2893
2922
|
...elementProps
|
|
2894
2923
|
} = componentProps;
|
|
2895
|
-
const listItem = useCompositeListItem(
|
|
2924
|
+
const listItem = useCompositeListItem({
|
|
2925
|
+
label
|
|
2926
|
+
});
|
|
2896
2927
|
const menuPositionerContext = useMenuPositionerContext();
|
|
2897
2928
|
const {
|
|
2898
2929
|
store
|
|
@@ -2901,6 +2932,7 @@ const MenuSubmenuTrigger = /*#__PURE__*/React.forwardRef(function SubmenuTrigger
|
|
|
2901
2932
|
const open = store.useState('open');
|
|
2902
2933
|
const floatingRootContext = store.useState('floatingRootContext');
|
|
2903
2934
|
const floatingTreeRoot = store.useState('floatingTreeRoot');
|
|
2935
|
+
const popupId = store.useState('triggerPopupId', thisTriggerId);
|
|
2904
2936
|
const baseRegisterTrigger = useTriggerRegistration(thisTriggerId, store);
|
|
2905
2937
|
const registerTrigger = React.useCallback(element => {
|
|
2906
2938
|
const cleanup = baseRegisterTrigger(element);
|
|
@@ -2924,16 +2956,29 @@ const MenuSubmenuTrigger = /*#__PURE__*/React.forwardRef(function SubmenuTrigger
|
|
|
2924
2956
|
}
|
|
2925
2957
|
store.useSyncedValue('closeDelay', closeDelay);
|
|
2926
2958
|
const parentMenuStore = submenuRootContext.parentMenu;
|
|
2959
|
+
const rootDisabled = store.useState('disabled');
|
|
2960
|
+
const parentDisabled = parentMenuStore.useState('disabled');
|
|
2961
|
+
const disabled = disabledProp || rootDisabled || parentDisabled;
|
|
2962
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
2963
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
2964
|
+
useIsoLayoutEffect(() => {
|
|
2965
|
+
const element = triggerElementRef.current;
|
|
2966
|
+
if (element && isElementDisabled(element) && !disabled) {
|
|
2967
|
+
const ownerStackMessage = SafeReact.captureOwnerStack?.() || '';
|
|
2968
|
+
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}`);
|
|
2969
|
+
}
|
|
2970
|
+
});
|
|
2971
|
+
}
|
|
2927
2972
|
const itemProps = parentMenuStore.useState('itemProps');
|
|
2928
2973
|
const highlighted = parentMenuStore.useState('isActive', listItem.index);
|
|
2929
2974
|
const itemMetadata = React.useMemo(() => ({
|
|
2930
2975
|
type: 'submenu-trigger',
|
|
2931
2976
|
setActive() {
|
|
2932
|
-
parentMenuStore.
|
|
2977
|
+
if (parentMenuStore.select('highlightItemOnHover')) {
|
|
2978
|
+
parentMenuStore.set('activeIndex', listItem.index);
|
|
2979
|
+
}
|
|
2933
2980
|
}
|
|
2934
2981
|
}), [parentMenuStore, listItem.index]);
|
|
2935
|
-
const rootDisabled = store.useState('disabled');
|
|
2936
|
-
const disabled = disabledProp || rootDisabled;
|
|
2937
2982
|
const {
|
|
2938
2983
|
getItemProps,
|
|
2939
2984
|
itemRef
|
|
@@ -2946,10 +2991,9 @@ const MenuSubmenuTrigger = /*#__PURE__*/React.forwardRef(function SubmenuTrigger
|
|
|
2946
2991
|
typingRef: parentMenuStore.context.typingRef,
|
|
2947
2992
|
nativeButton,
|
|
2948
2993
|
itemMetadata,
|
|
2949
|
-
nodeId: menuPositionerContext?.nodeId
|
|
2994
|
+
nodeId: menuPositionerContext?.context.nodeId
|
|
2950
2995
|
});
|
|
2951
2996
|
const hoverEnabled = store.useState('hoverEnabled');
|
|
2952
|
-
const allowMouseEnter = parentMenuStore.useState('allowMouseEnter');
|
|
2953
2997
|
const hoverProps = useHoverReferenceInteraction(floatingRootContext, {
|
|
2954
2998
|
enabled: hoverEnabled && openOnHover && !disabled,
|
|
2955
2999
|
handleClose: safePolygon({
|
|
@@ -2958,12 +3002,14 @@ const MenuSubmenuTrigger = /*#__PURE__*/React.forwardRef(function SubmenuTrigger
|
|
|
2958
3002
|
mouseOnly: true,
|
|
2959
3003
|
move: true,
|
|
2960
3004
|
restMs: delay,
|
|
2961
|
-
delay:
|
|
3005
|
+
delay: {
|
|
2962
3006
|
open: delay,
|
|
2963
3007
|
close: closeDelay
|
|
2964
|
-
}
|
|
3008
|
+
},
|
|
3009
|
+
shouldOpen: delay > 0 ? () => parentMenuStore.select('allowMouseEnter') : undefined,
|
|
2965
3010
|
triggerElementRef,
|
|
2966
|
-
externalTree: floatingTreeRoot
|
|
3011
|
+
externalTree: floatingTreeRoot,
|
|
3012
|
+
isClosing: () => store.select('transitionStatus') === 'ending'
|
|
2967
3013
|
});
|
|
2968
3014
|
const click = useClick(floatingRootContext, {
|
|
2969
3015
|
enabled: !disabled,
|
|
@@ -2972,7 +3018,7 @@ const MenuSubmenuTrigger = /*#__PURE__*/React.forwardRef(function SubmenuTrigger
|
|
|
2972
3018
|
ignoreMouse: openOnHover,
|
|
2973
3019
|
stickIfOpen: false
|
|
2974
3020
|
});
|
|
2975
|
-
const localInteractionProps =
|
|
3021
|
+
const localInteractionProps = click.reference ?? EMPTY_OBJECT;
|
|
2976
3022
|
const rootTriggerProps = store.useState('triggerProps', true);
|
|
2977
3023
|
delete rootTriggerProps.id;
|
|
2978
3024
|
const state = {
|
|
@@ -2983,7 +3029,8 @@ const MenuSubmenuTrigger = /*#__PURE__*/React.forwardRef(function SubmenuTrigger
|
|
|
2983
3029
|
const element = useRenderElement('div', componentProps, {
|
|
2984
3030
|
state,
|
|
2985
3031
|
stateAttributesMapping: triggerOpenStateMapping,
|
|
2986
|
-
props: [localInteractionProps
|
|
3032
|
+
props: [localInteractionProps, hoverProps, rootTriggerProps, itemProps, {
|
|
3033
|
+
'aria-controls': popupId,
|
|
2987
3034
|
tabIndex: open || highlighted ? 0 : -1,
|
|
2988
3035
|
onBlur() {
|
|
2989
3036
|
if (highlighted) {
|