@jobber/components 9.6.1 → 9.6.2

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.
Files changed (141) hide show
  1. package/dist/Autocomplete/index.cjs +2 -0
  2. package/dist/Autocomplete/index.mjs +2 -0
  3. package/dist/Autocomplete-cjs.js +5 -4
  4. package/dist/Autocomplete-es.js +2 -1
  5. package/dist/Card/index.cjs +2 -1
  6. package/dist/Card/index.mjs +2 -1
  7. package/dist/Chip/index.cjs +2 -0
  8. package/dist/Chip/index.mjs +2 -0
  9. package/dist/Chips/InternalChipDismissible/hooks/index.cjs +2 -0
  10. package/dist/Chips/InternalChipDismissible/hooks/index.mjs +2 -0
  11. package/dist/Chips/InternalChipDismissible/index.cjs +2 -0
  12. package/dist/Chips/InternalChipDismissible/index.mjs +2 -0
  13. package/dist/Chips/index.cjs +2 -0
  14. package/dist/Chips/index.mjs +2 -0
  15. package/dist/Combobox/index.cjs +4 -2
  16. package/dist/Combobox/index.mjs +4 -2
  17. package/dist/{ComboboxSeparator-cjs.js → ComboboxChipRemove-cjs.js} +455 -501
  18. package/dist/{ComboboxSeparator-es.js → ComboboxChipRemove-es.js} +456 -500
  19. package/dist/ComboboxPrimitive-cjs.js +30 -29
  20. package/dist/ComboboxPrimitive-es.js +3 -2
  21. package/dist/ConfirmationModal/index.cjs +2 -0
  22. package/dist/ConfirmationModal/index.mjs +2 -0
  23. package/dist/DataDump/index.cjs +2 -1
  24. package/dist/DataDump/index.mjs +2 -1
  25. package/dist/DataList/components/DataListActions/index.cjs +2 -0
  26. package/dist/DataList/components/DataListActions/index.mjs +2 -0
  27. package/dist/DataList/components/DataListBulkActions/index.cjs +2 -0
  28. package/dist/DataList/components/DataListBulkActions/index.mjs +2 -0
  29. package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +2 -0
  30. package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +2 -0
  31. package/dist/DataList/components/DataListFilters/index.cjs +2 -0
  32. package/dist/DataList/components/DataListFilters/index.mjs +2 -0
  33. package/dist/DataList/components/DataListHeader/index.cjs +2 -0
  34. package/dist/DataList/components/DataListHeader/index.mjs +2 -0
  35. package/dist/DataList/components/DataListItem/index.cjs +2 -0
  36. package/dist/DataList/components/DataListItem/index.mjs +2 -0
  37. package/dist/DataList/components/DataListItemActions/index.cjs +2 -0
  38. package/dist/DataList/components/DataListItemActions/index.mjs +2 -0
  39. package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +2 -0
  40. package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +2 -0
  41. package/dist/DataList/components/DataListItems/index.cjs +2 -0
  42. package/dist/DataList/components/DataListItems/index.mjs +2 -0
  43. package/dist/DataList/components/DataListLayout/index.cjs +2 -0
  44. package/dist/DataList/components/DataListLayout/index.mjs +2 -0
  45. package/dist/DataList/components/DataListLayoutActions/index.cjs +2 -0
  46. package/dist/DataList/components/DataListLayoutActions/index.mjs +2 -0
  47. package/dist/DataList/index.cjs +2 -0
  48. package/dist/DataList/index.mjs +2 -0
  49. package/dist/DatePicker/index.cjs +2 -0
  50. package/dist/DatePicker/index.mjs +2 -0
  51. package/dist/DatePicker-cjs.js +5 -4
  52. package/dist/DatePicker-es.js +2 -1
  53. package/dist/Dialog/index.cjs +2 -1
  54. package/dist/Dialog/index.mjs +2 -1
  55. package/dist/DrawerDescription-cjs.js +99 -268
  56. package/dist/DrawerDescription-es.js +103 -270
  57. package/dist/DrawerTitle-cjs.js +6 -2
  58. package/dist/DrawerTitle-es.js +7 -3
  59. package/dist/FieldDescription-cjs.js +181 -184
  60. package/dist/FieldDescription-es.js +184 -187
  61. package/dist/FilterPicker/components/FilterPickerActivator/index.cjs +2 -0
  62. package/dist/FilterPicker/components/FilterPickerActivator/index.mjs +2 -0
  63. package/dist/FilterPicker/components/FilterPickerContent/index.cjs +2 -0
  64. package/dist/FilterPicker/components/FilterPickerContent/index.mjs +2 -0
  65. package/dist/FilterPicker/components/FilterPickerTrigger/index.cjs +2 -0
  66. package/dist/FilterPicker/components/FilterPickerTrigger/index.mjs +2 -0
  67. package/dist/FilterPicker/index.cjs +2 -0
  68. package/dist/FilterPicker/index.mjs +2 -0
  69. package/dist/FilterPickerContent-cjs.js +5 -4
  70. package/dist/FilterPickerContent-es.js +2 -1
  71. package/dist/FormatFile/index.cjs +2 -0
  72. package/dist/FormatFile/index.mjs +2 -0
  73. package/dist/Gallery/index.cjs +2 -0
  74. package/dist/Gallery/index.mjs +2 -0
  75. package/dist/InputDate/index.cjs +2 -0
  76. package/dist/InputDate/index.mjs +2 -0
  77. package/dist/InputNumber/index.cjs +2 -1
  78. package/dist/InputNumber/index.mjs +2 -1
  79. package/dist/LightBox/index.cjs +2 -0
  80. package/dist/LightBox/index.mjs +2 -0
  81. package/dist/Menu/index.cjs +2 -1
  82. package/dist/Menu/index.mjs +2 -1
  83. package/dist/Menu-cjs.js +5 -4
  84. package/dist/Menu-es.js +2 -1
  85. package/dist/MenuSubmenuTrigger-cjs.js +164 -206
  86. package/dist/MenuSubmenuTrigger-es.js +152 -194
  87. package/dist/Modal/index.cjs +2 -0
  88. package/dist/Modal/index.mjs +3 -1
  89. package/dist/NumberFieldInput-cjs.js +308 -187
  90. package/dist/NumberFieldInput-es.js +308 -187
  91. package/dist/Page/index.cjs +2 -1
  92. package/dist/Page/index.mjs +2 -1
  93. package/dist/Popover/index.cjs +2 -0
  94. package/dist/Popover/index.mjs +2 -0
  95. package/dist/Popover-cjs.js +8 -7
  96. package/dist/Popover-es.js +2 -1
  97. package/dist/ScrollAreaViewport-cjs.js +1093 -747
  98. package/dist/ScrollAreaViewport-es.js +1081 -734
  99. package/dist/Select/index.cjs +2 -1
  100. package/dist/Select/index.mjs +2 -1
  101. package/dist/SelectGroupLabel-cjs.js +306 -207
  102. package/dist/SelectGroupLabel-es.js +302 -203
  103. package/dist/Tooltip/index.cjs +2 -0
  104. package/dist/Tooltip/index.mjs +2 -0
  105. package/dist/Tooltip-cjs.js +7 -6
  106. package/dist/Tooltip-es.js +2 -1
  107. package/dist/dialogReturnFocus-cjs.js +1 -1
  108. package/dist/dialogReturnFocus-es.js +2 -2
  109. package/dist/floating-ui.react-cjs.js +192 -2564
  110. package/dist/floating-ui.react-dom-cjs.js +2103 -0
  111. package/dist/floating-ui.react-dom-es.js +2072 -0
  112. package/dist/floating-ui.react-es.js +156 -2519
  113. package/dist/floating-ui.utils-cjs.js +342 -0
  114. package/dist/floating-ui.utils-es.js +300 -0
  115. package/dist/index.cjs +4 -3
  116. package/dist/index.mjs +4 -3
  117. package/dist/primitives/BottomSheet/index.cjs +1 -1
  118. package/dist/primitives/BottomSheet/index.mjs +1 -1
  119. package/dist/primitives/ComboboxPrimitive/index.cjs +4 -2
  120. package/dist/primitives/ComboboxPrimitive/index.mjs +4 -2
  121. package/dist/primitives/SelectPrimitive/index.cjs +2 -0
  122. package/dist/primitives/SelectPrimitive/index.mjs +2 -0
  123. package/dist/primitives/index.cjs +3 -2
  124. package/dist/primitives/index.mjs +3 -2
  125. package/dist/scrollEdges-cjs.js +11 -1
  126. package/dist/scrollEdges-es.js +11 -1
  127. package/dist/unstyledPrimitives/index.cjs +478 -720
  128. package/dist/unstyledPrimitives/index.mjs +421 -663
  129. package/dist/useCompositeListItem-cjs.js +361 -2108
  130. package/dist/useCompositeListItem-es.js +331 -2076
  131. package/dist/useLabel-cjs.js +3 -2
  132. package/dist/useLabel-es.js +3 -2
  133. package/dist/useRenderElement-cjs.js +7 -9
  134. package/dist/useRenderElement-es.js +7 -9
  135. package/dist/useScrollLock-cjs.js +344 -819
  136. package/dist/useScrollLock-es.js +296 -732
  137. package/dist/useScrollToActive-cjs.js +5 -4
  138. package/dist/useScrollToActive-es.js +2 -1
  139. package/package.json +3 -3
  140. package/dist/scrollable-cjs.js +0 -58
  141. package/dist/scrollable-es.js +0 -54
@@ -7,6 +7,7 @@ var jsxRuntime = require('react/jsx-runtime');
7
7
  var useCompositeListItem = require('./useCompositeListItem-cjs.js');
8
8
  var SelectGroupLabel = require('./SelectGroupLabel-cjs.js');
9
9
  var scrollEdges = require('./scrollEdges-cjs.js');
10
+ var floatingUi_utils = require('./floating-ui.utils-cjs.js');
10
11
  var ScrollAreaViewport = require('./ScrollAreaViewport-cjs.js');
11
12
  var ReactDOM = require('react-dom');
12
13
 
@@ -85,14 +86,14 @@ function useFocus(context, props = {}) {
85
86
  if (!enabled) {
86
87
  return undefined;
87
88
  }
88
- const win = useScrollLock.getWindow(domReference);
89
+ const win = floatingUi_utils.getWindow(domReference);
89
90
 
90
91
  // If the reference was focused and the user left the tab/window, and the
91
92
  // floating element was not open, the focus should be blocked when they
92
93
  // return to the tab/window.
93
94
  function onBlur() {
94
95
  const currentDomReference = store.select('domReferenceElement');
95
- if (!store.select('open') && useScrollLock.isHTMLElement(currentDomReference) && currentDomReference === useScrollLock.activeElement(useScrollLock.ownerDocument(currentDomReference))) {
96
+ if (!store.select('open') && floatingUi_utils.isHTMLElement(currentDomReference) && currentDomReference === useScrollLock.activeElement(useScrollLock.ownerDocument(currentDomReference))) {
96
97
  blockFocusRef.current = true;
97
98
  }
98
99
  }
@@ -111,7 +112,7 @@ function useFocus(context, props = {}) {
111
112
  function onOpenChangeLocal(details) {
112
113
  if (details.reason === useScrollLock.triggerPress || details.reason === useScrollLock.escapeKey) {
113
114
  const referenceElement = store.select('domReferenceElement');
114
- if (useScrollLock.isElement(referenceElement)) {
115
+ if (floatingUi_utils.isElement(referenceElement)) {
115
116
  blockedReferenceRef.current = referenceElement;
116
117
  blockFocusRef.current = true;
117
118
  }
@@ -140,7 +141,7 @@ function useFocus(context, props = {}) {
140
141
  resetBlockedFocus();
141
142
  }
142
143
  const target = useScrollLock.getTarget(event.nativeEvent);
143
- if (useScrollLock.isElement(target)) {
144
+ if (floatingUi_utils.isElement(target)) {
144
145
  // Safari fails to match `:focus-visible` if focus was initially
145
146
  // outside the document.
146
147
  if (isMacSafari && !event.relatedTarget) {
@@ -175,7 +176,7 @@ function useFocus(context, props = {}) {
175
176
 
176
177
  // Hit the non-modal focus management portal guard. Focus will be
177
178
  // moved into the floating element immediately after.
178
- const movedToFocusGuard = useScrollLock.isElement(relatedTarget) && relatedTarget.hasAttribute(useScrollLock.createAttribute('focus-guard')) && relatedTarget.getAttribute('data-type') === 'outside';
179
+ const movedToFocusGuard = floatingUi_utils.isElement(relatedTarget) && relatedTarget.hasAttribute(useScrollLock.createAttribute('focus-guard')) && relatedTarget.getAttribute('data-type') === 'outside';
179
180
 
180
181
  // Wait for the window blur listener to fire.
181
182
  timeout.start(0, () => {
@@ -333,7 +334,7 @@ function useHoverFloatingInteraction(context, parameters = {}) {
333
334
  if (!enabled) {
334
335
  return undefined;
335
336
  }
336
- if (open && instance.handleCloseOptions?.blockPointerEvents && isHoverOpen() && useScrollLock.isElement(domReferenceElement) && floatingElement) {
337
+ if (open && instance.handleCloseOptions?.blockPointerEvents && isHoverOpen() && floatingUi_utils.isElement(domReferenceElement) && floatingElement) {
337
338
  const ref = domReferenceElement;
338
339
  const floatingEl = floatingElement;
339
340
  const doc = useScrollLock.ownerDocument(floatingElement);
@@ -405,7 +406,7 @@ function useHoverFloatingInteraction(context, parameters = {}) {
405
406
  }
406
407
  const currentNodeId = dataRef.current.floatingContext?.nodeId ?? nodeIdProp;
407
408
  const relatedTarget = event.relatedTarget;
408
- const isMovingIntoDescendantFloating = tree && currentNodeId && useScrollLock.isElement(relatedTarget) && useScrollLock.getNodeChildren(tree.nodesRef.current, currentNodeId, false).some(node => useScrollLock.contains(node.context?.elements.floating, relatedTarget));
409
+ const isMovingIntoDescendantFloating = tree && currentNodeId && floatingUi_utils.isElement(relatedTarget) && useScrollLock.getNodeChildren(tree.nodesRef.current, currentNodeId, false).some(node => useScrollLock.contains(node.context?.elements.floating, relatedTarget));
409
410
  if (isMovingIntoDescendantFloating) {
410
411
  return;
411
412
  }
@@ -459,8 +460,7 @@ function useHoverReferenceInteraction(context, props = {}) {
459
460
  isActiveTrigger = true,
460
461
  getHandleCloseContext,
461
462
  isClosing,
462
- shouldOpen: shouldOpenProp,
463
- guardStaleOpen = false
463
+ shouldOpen: shouldOpenProp
464
464
  } = props;
465
465
  const store = 'rootStore' in context ? context.rootStore : context;
466
466
  const {
@@ -491,7 +491,7 @@ function useHoverReferenceInteraction(context, props = {}) {
491
491
  }
492
492
 
493
493
  // Fallback for delegated/wrapper usage where currentTarget may be outside the trigger map.
494
- if (!useScrollLock.isElement(target)) {
494
+ if (!floatingUi_utils.isElement(target)) {
495
495
  return false;
496
496
  }
497
497
  const targetElement = target;
@@ -555,7 +555,7 @@ function useHoverReferenceInteraction(context, props = {}) {
555
555
  }
556
556
  }
557
557
  const trigger = triggerElementRef.current ?? (isActiveTrigger ? store.select('domReferenceElement') : null);
558
- if (!useScrollLock.isElement(trigger)) {
558
+ if (!floatingUi_utils.isElement(trigger)) {
559
559
  return undefined;
560
560
  }
561
561
  function onMouseEnter(event) {
@@ -575,7 +575,7 @@ function useHoverReferenceInteraction(context, props = {}) {
575
575
  let triggerNode = currentTarget;
576
576
 
577
577
  // Wrapper/delegated mode: resolve the actual trigger from the event target.
578
- if (useScrollLock.isElement(eventTarget) && !store.context.triggerElements.hasElement(eventTarget)) {
578
+ if (floatingUi_utils.isElement(eventTarget) && !store.context.triggerElements.hasElement(eventTarget)) {
579
579
  for (const triggerElement of store.context.triggerElements.elements()) {
580
580
  if (useScrollLock.contains(triggerElement, eventTarget)) {
581
581
  triggerNode = triggerElement;
@@ -586,14 +586,14 @@ function useHoverReferenceInteraction(context, props = {}) {
586
586
 
587
587
  // Wrapper/delegated mode fallback: if the wrapper contains the active trigger,
588
588
  // treat this as re-entering that active trigger.
589
- if (useScrollLock.isElement(currentTarget) && useScrollLock.isElement(currentDomReference) && !store.context.triggerElements.hasElement(currentTarget) && useScrollLock.contains(currentTarget, currentDomReference)) {
589
+ if (floatingUi_utils.isElement(currentTarget) && floatingUi_utils.isElement(currentDomReference) && !store.context.triggerElements.hasElement(currentTarget) && useScrollLock.contains(currentTarget, currentDomReference)) {
590
590
  triggerNode = currentDomReference;
591
591
  }
592
592
  const isOverInactive = triggerNode == null ? false : isOverInactiveTrigger(currentDomReference, triggerNode, eventTarget);
593
593
  const isOpen = store.select('open');
594
594
  const isInClosingTransition = isClosingRef.current?.() ?? store.select('transitionStatus') === 'ending';
595
595
  const isHoverCloseTransition = !isOpen && isInClosingTransition && isHoverCloseActiveRef.current;
596
- const isReenteringSameTriggerDuringCloseTransition = !isOverInactive && useScrollLock.isElement(triggerNode) && useScrollLock.isElement(currentDomReference) && useScrollLock.contains(currentDomReference, triggerNode) && isHoverCloseTransition;
596
+ const isReenteringSameTriggerDuringCloseTransition = !isOverInactive && floatingUi_utils.isElement(triggerNode) && floatingUi_utils.isElement(currentDomReference) && useScrollLock.contains(currentDomReference, triggerNode) && isHoverCloseTransition;
597
597
  const isRestOnlyDelay = restMsValue > 0 && !openDelay;
598
598
  const shouldOpenImmediately = isOverInactive && (isOpen || isHoverCloseTransition) || isReenteringSameTriggerDuringCloseTransition;
599
599
  const shouldOpen = !isOpen || isOverInactive;
@@ -662,24 +662,13 @@ function useHoverReferenceInteraction(context, props = {}) {
662
662
  closeWithDelay(event);
663
663
  }
664
664
  }
665
-
666
- // Backup cancellation for Chrome's dropped `mouseleave` — see `guardStaleOpen`.
667
- function onMouseOut(event) {
668
- if (useScrollLock.contains(trigger, event.relatedTarget)) {
669
- return; // moved within the trigger's own subtree
670
- }
671
- instance.openChangeTimeout.clear();
672
- instance.restTimeout.clear();
673
- instance.restTimeoutPending = false;
674
- }
675
- const staleOpenGuard = guardStaleOpen ? useScrollLock.addEventListener(trigger, 'mouseout', onMouseOut) : undefined;
676
665
  if (move) {
677
666
  return useScrollLock.mergeCleanups(useScrollLock.addEventListener(trigger, 'mousemove', onMouseEnter, {
678
667
  once: true
679
- }), useScrollLock.addEventListener(trigger, 'mouseenter', onMouseEnter), useScrollLock.addEventListener(trigger, 'mouseleave', onMouseLeave), staleOpenGuard);
668
+ }), useScrollLock.addEventListener(trigger, 'mouseenter', onMouseEnter), useScrollLock.addEventListener(trigger, 'mouseleave', onMouseLeave));
680
669
  }
681
- return useScrollLock.mergeCleanups(useScrollLock.addEventListener(trigger, 'mouseenter', onMouseEnter), useScrollLock.addEventListener(trigger, 'mouseleave', onMouseLeave), staleOpenGuard);
682
- }, [cleanupMouseMoveHandler, clearPointerEvents, dataRef, delayRef, store, enabled, handleCloseRef, instance, isActiveTrigger, isOverInactiveTrigger, isClickLikeOpenEvent$1, mouseOnly, move, restMsRef, triggerElementRef, tree, enabledRef, getHandleCloseContext, isClosingRef, checkShouldOpen, guardStaleOpen]);
670
+ return useScrollLock.mergeCleanups(useScrollLock.addEventListener(trigger, 'mouseenter', onMouseEnter), useScrollLock.addEventListener(trigger, 'mouseleave', onMouseLeave));
671
+ }, [cleanupMouseMoveHandler, clearPointerEvents, dataRef, delayRef, store, enabled, handleCloseRef, instance, isActiveTrigger, isOverInactiveTrigger, isClickLikeOpenEvent$1, mouseOnly, move, restMsRef, triggerElementRef, tree, enabledRef, getHandleCloseContext, isClosingRef, checkShouldOpen]);
683
672
  return React__namespace.useMemo(() => {
684
673
  if (!enabled) {
685
674
  return undefined;
@@ -859,7 +848,7 @@ function safePolygon(options = {}) {
859
848
 
860
849
  // Prevent overlapping floating element from being stuck in an open-close
861
850
  // loop: https://github.com/floating-ui/floating-ui/issues/1910
862
- if (isLeave && useScrollLock.isElement(event.relatedTarget) && useScrollLock.contains(floating, event.relatedTarget)) {
851
+ if (isLeave && floatingUi_utils.isElement(event.relatedTarget) && useScrollLock.contains(floating, event.relatedTarget)) {
863
852
  return undefined;
864
853
  }
865
854
  function hasOpenChildNode() {
@@ -1020,6 +1009,11 @@ function useContextMenuRootContext(optional = true) {
1020
1009
  return context;
1021
1010
  }
1022
1011
 
1012
+ const stateAttributesMapping$1 = {
1013
+ ...useScrollLock.popupStateMapping,
1014
+ ...useScrollLock.transitionStatusMapping
1015
+ };
1016
+
1023
1017
  /**
1024
1018
  * An overlay displayed beneath the menu popup.
1025
1019
  * Renders a `<div>` element.
@@ -1048,7 +1042,7 @@ const MenuBackdrop = /*#__PURE__*/React__namespace.forwardRef(function MenuBackd
1048
1042
  return useRenderElement.useRenderElement('div', componentProps, {
1049
1043
  ref: contextMenuContext?.backdropRef ? [forwardedRef, contextMenuContext.backdropRef] : forwardedRef,
1050
1044
  state,
1051
- stateAttributesMapping: useScrollLock.popupTransitionStateMapping,
1045
+ stateAttributesMapping: stateAttributesMapping$1,
1052
1046
  props: [{
1053
1047
  role: 'presentation',
1054
1048
  hidden: !mounted,
@@ -1130,12 +1124,8 @@ function useMenuItemCommonProps(params) {
1130
1124
  if (itemRef.current && store.context.allowMouseUpTriggerRef.current && (!isContextMenu || event.button === 2)) {
1131
1125
  // This fires whenever the user clicks on the trigger, moves the cursor, and releases it over the item.
1132
1126
  // We trigger the click and override the `closeOnClick` preference to always close the menu.
1133
- if (itemMetadata.type === 'regular-item') {
1134
- // `detail: 1` marks this as a mouse-gesture click so MenuRoot doesn't
1135
- // treat it as a keyboard activation (`detail === 0` → `data-instant`).
1136
- useScrollLock.dispatchClickWithModifiers(itemRef.current, event, {
1137
- detail: 1
1138
- });
1127
+ if (!itemMetadata || itemMetadata.type === 'regular-item') {
1128
+ itemRef.current.click();
1139
1129
  }
1140
1130
  }
1141
1131
  }
@@ -1148,7 +1138,7 @@ const REGULAR_ITEM = {
1148
1138
  function useMenuItem(params) {
1149
1139
  const {
1150
1140
  closeOnClick,
1151
- disabled,
1141
+ disabled: disabledProp = false,
1152
1142
  highlighted,
1153
1143
  id,
1154
1144
  store,
@@ -1157,6 +1147,8 @@ function useMenuItem(params) {
1157
1147
  itemMetadata,
1158
1148
  nodeId
1159
1149
  } = params;
1150
+ const rootDisabled = store.useState('disabled');
1151
+ const disabled = disabledProp || rootDisabled;
1160
1152
  const itemRef = React__namespace.useRef(null);
1161
1153
  const {
1162
1154
  getButtonProps,
@@ -1280,7 +1272,7 @@ const MenuGroupLabel = /*#__PURE__*/React__namespace.forwardRef(function MenuGro
1280
1272
  useScrollLock.useIsoLayoutEffect(() => {
1281
1273
  setLabelId(id);
1282
1274
  return () => {
1283
- setLabelId(currentId => currentId === id ? undefined : currentId);
1275
+ setLabelId(undefined);
1284
1276
  };
1285
1277
  }, [setLabelId, id]);
1286
1278
  return useRenderElement.useRenderElement('div', componentProps, {
@@ -1307,13 +1299,12 @@ const MenuItem = /*#__PURE__*/React__namespace.forwardRef(function MenuItem(comp
1307
1299
  id: idProp,
1308
1300
  label,
1309
1301
  nativeButton = false,
1310
- disabled: disabledProp = false,
1302
+ disabled = false,
1311
1303
  closeOnClick = true,
1312
1304
  style,
1313
1305
  ...elementProps
1314
1306
  } = componentProps;
1315
1307
  const listItem = useCompositeListItem.useCompositeListItem({
1316
- guess: true,
1317
1308
  label
1318
1309
  });
1319
1310
  const menuPositionerContext = useMenuPositionerContext(true);
@@ -1321,8 +1312,6 @@ const MenuItem = /*#__PURE__*/React__namespace.forwardRef(function MenuItem(comp
1321
1312
  const {
1322
1313
  store
1323
1314
  } = useMenuRootContext();
1324
- const rootDisabled = store.useState('disabled');
1325
- const disabled = disabledProp || rootDisabled;
1326
1315
  const highlighted = store.useState('isActive', listItem.index);
1327
1316
  const itemProps = store.useState('itemProps');
1328
1317
  const {
@@ -1368,7 +1357,6 @@ const MenuLinkItem = /*#__PURE__*/React__namespace.forwardRef(function MenuLinkI
1368
1357
  } = componentProps;
1369
1358
  const linkRef = React__namespace.useRef(null);
1370
1359
  const listItem = useCompositeListItem.useCompositeListItem({
1371
- guess: true,
1372
1360
  label
1373
1361
  });
1374
1362
  const menuPositionerContext = useMenuPositionerContext(true);
@@ -1394,8 +1382,7 @@ const MenuLinkItem = /*#__PURE__*/React__namespace.forwardRef(function MenuLinkI
1394
1382
  nodeId,
1395
1383
  store,
1396
1384
  typingRef,
1397
- itemRef: linkRef,
1398
- itemMetadata: REGULAR_ITEM
1385
+ itemRef: linkRef
1399
1386
  });
1400
1387
  function getItemProps(externalProps) {
1401
1388
  return useRenderElement.mergeProps(commonProps, externalProps, getButtonProps);
@@ -1411,6 +1398,17 @@ const MenuLinkItem = /*#__PURE__*/React__namespace.forwardRef(function MenuLinkI
1411
1398
  });
1412
1399
  if (process.env.NODE_ENV !== "production") MenuLinkItem.displayName = "MenuLinkItem";
1413
1400
 
1401
+ const stateAttributesMapping = {
1402
+ ...useScrollLock.popupStateMapping,
1403
+ ...useScrollLock.transitionStatusMapping
1404
+ };
1405
+
1406
+ /**
1407
+ * A container for the menu items.
1408
+ * Renders a `<div>` element.
1409
+ *
1410
+ * Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
1411
+ */
1414
1412
  const MenuPopup = /*#__PURE__*/React__namespace.forwardRef(function MenuPopup(componentProps, forwardedRef) {
1415
1413
  const {
1416
1414
  render,
@@ -1432,13 +1430,14 @@ const MenuPopup = /*#__PURE__*/React__namespace.forwardRef(function MenuPopup(co
1432
1430
  const popupProps = store.useState('popupProps');
1433
1431
  const mounted = store.useState('mounted');
1434
1432
  const instantType = store.useState('instantType');
1435
- const activeTriggerElement = store.useState('activeTriggerElement');
1433
+ const triggerElement = store.useState('activeTriggerElement');
1436
1434
  const parent = store.useState('parent');
1437
1435
  const lastOpenChangeReason = store.useState('lastOpenChangeReason');
1438
1436
  const rootId = store.useState('rootId');
1439
1437
  const floatingContext = store.useState('floatingRootContext');
1440
1438
  const floatingTreeRoot = store.useState('floatingTreeRoot');
1441
1439
  const closeDelay = store.useState('closeDelay');
1440
+ const activeTriggerElement = store.useState('activeTriggerElement');
1442
1441
  const hoverEnabled = store.useState('hoverEnabled');
1443
1442
  const disabled = store.useState('disabled');
1444
1443
  const openMethod = store.useState('openMethod');
@@ -1465,7 +1464,9 @@ const MenuPopup = /*#__PURE__*/React__namespace.forwardRef(function MenuPopup(co
1465
1464
  enabled: hoverEnabled && !disabled && !isContextMenu && parent.type !== 'menubar',
1466
1465
  closeDelay
1467
1466
  });
1468
- const setPopupElement = store.useStateSetter('popupElement');
1467
+ const setPopupElement = React__namespace.useCallback(element => {
1468
+ store.set('popupElement', element);
1469
+ }, [store]);
1469
1470
  const state = {
1470
1471
  transitionStatus,
1471
1472
  side,
@@ -1477,7 +1478,7 @@ const MenuPopup = /*#__PURE__*/React__namespace.forwardRef(function MenuPopup(co
1477
1478
  const element = useRenderElement.useRenderElement('div', componentProps, {
1478
1479
  state,
1479
1480
  ref: [forwardedRef, store.context.popupRef, setPopupElement],
1480
- stateAttributesMapping: useScrollLock.popupTransitionStateMapping,
1481
+ stateAttributesMapping,
1481
1482
  props: [popupProps, {
1482
1483
  onKeyDown(event) {
1483
1484
  if (insideToolbar && scrollEdges.COMPOSITE_KEYS.has(event.key)) {
@@ -1489,7 +1490,7 @@ const MenuPopup = /*#__PURE__*/React__namespace.forwardRef(function MenuPopup(co
1489
1490
  }]
1490
1491
  });
1491
1492
  let returnFocus = parent.type === undefined || isContextMenu;
1492
- if (activeTriggerElement || parent.type === 'menubar' && lastOpenChangeReason !== useScrollLock.outsidePress) {
1493
+ if (triggerElement || parent.type === 'menubar' && lastOpenChangeReason !== useScrollLock.outsidePress) {
1493
1494
  returnFocus = true;
1494
1495
  }
1495
1496
  return /*#__PURE__*/jsxRuntime.jsx(useScrollLock.FloatingFocusManager, {
@@ -1577,13 +1578,13 @@ const MenuPositioner = /*#__PURE__*/React__namespace.forwardRef(function MenuPos
1577
1578
  const transitionStatus = store.useState('transitionStatus');
1578
1579
  const positionerElement = store.useState('positionerElement');
1579
1580
  const instantType = store.useState('instantType');
1580
- const adaptiveOrigin = store.useState('adaptiveOrigin');
1581
+ const hasViewport = store.useState('hasViewport');
1581
1582
  const lastOpenChangeReason = store.useState('lastOpenChangeReason');
1582
1583
  const floatingNodeId = store.useState('floatingNodeId');
1583
1584
  const floatingParentNodeId = store.useState('floatingParentNodeId');
1584
1585
  const domReference = floatingRootContext.useState('domReferenceElement');
1585
1586
  const previousTriggerRef = React__namespace.useRef(null);
1586
- const runOnceAnimationsFinish = useScrollLock.useAnimationsFinished(positionerElement);
1587
+ const runOnceAnimationsFinish = useScrollLock.useAnimationsFinished(positionerElement, false, false);
1587
1588
  let anchor = anchorProp;
1588
1589
  let sideOffset = sideOffsetProp;
1589
1590
  let alignOffset = alignOffsetProp;
@@ -1625,12 +1626,9 @@ const MenuPositioner = /*#__PURE__*/React__namespace.forwardRef(function MenuPos
1625
1626
  keepMounted,
1626
1627
  disableAnchorTracking,
1627
1628
  collisionAvoidance,
1628
- shift: contextMenu ? {
1629
- crossAxis: !('side' in collisionAvoidance && collisionAvoidance.side === 'flip'),
1630
- rootBoundary: 'layoutViewport'
1631
- } : undefined,
1629
+ shiftCrossAxis: contextMenu && !('side' in collisionAvoidance && collisionAvoidance.side === 'flip'),
1632
1630
  externalTree: floatingTreeRoot,
1633
- adaptiveOrigin
1631
+ adaptiveOrigin: hasViewport ? useCompositeListItem.adaptiveOrigin : undefined
1634
1632
  });
1635
1633
  React__namespace.useEffect(() => {
1636
1634
  function onMenuOpenChange(details) {
@@ -1870,7 +1868,6 @@ const MenuRadioItem = /*#__PURE__*/React__namespace.forwardRef(function MenuRadi
1870
1868
  ...elementProps
1871
1869
  } = componentProps;
1872
1870
  const listItem = useCompositeListItem.useCompositeListItem({
1873
- guess: true,
1874
1871
  label
1875
1872
  });
1876
1873
  const menuPositionerContext = useMenuPositionerContext(true);
@@ -1885,8 +1882,7 @@ const MenuRadioItem = /*#__PURE__*/React__namespace.forwardRef(function MenuRadi
1885
1882
  setValue: setSelectedValue,
1886
1883
  disabled: groupDisabled
1887
1884
  } = useMenuRadioGroupContext();
1888
- const rootDisabled = store.useState('disabled');
1889
- const disabled = disabledProp || groupDisabled || rootDisabled;
1885
+ const disabled = groupDisabled || disabledProp;
1890
1886
  const checked = selectedValue === value;
1891
1887
  const {
1892
1888
  getItemProps,
@@ -1908,7 +1904,7 @@ const MenuRadioItem = /*#__PURE__*/React__namespace.forwardRef(function MenuRadi
1908
1904
  }), [disabled, highlighted, checked]);
1909
1905
  function handleClick(event) {
1910
1906
  const details = useScrollLock.createChangeEventDetails(useScrollLock.itemPress, event.nativeEvent, undefined, {
1911
- preventUnmountOnClose: useRenderElement.NOOP
1907
+ preventUnmountOnClose() {}
1912
1908
  });
1913
1909
  setSelectedValue(value, details);
1914
1910
  }
@@ -1947,7 +1943,6 @@ const MenuRadioItemIndicator = /*#__PURE__*/React__namespace.forwardRef(function
1947
1943
  const indicatorRef = React__namespace.useRef(null);
1948
1944
  const {
1949
1945
  transitionStatus,
1950
- mounted,
1951
1946
  setMounted
1952
1947
  } = useScrollLock.useTransitionStatus(item.checked);
1953
1948
  useScrollLock.useOpenChangeComplete({
@@ -1973,7 +1968,7 @@ const MenuRadioItemIndicator = /*#__PURE__*/React__namespace.forwardRef(function
1973
1968
  'aria-hidden': true,
1974
1969
  ...elementProps
1975
1970
  },
1976
- enabled: keepMounted || mounted
1971
+ enabled: keepMounted || item.checked
1977
1972
  });
1978
1973
  return element;
1979
1974
  });
@@ -1991,35 +1986,36 @@ function useMenubarContext(optional) {
1991
1986
 
1992
1987
  const selectors = {
1993
1988
  ...ScrollAreaViewport.popupStoreSelectors,
1994
- disabled: state => state.parent.type === 'menubar' ? state.parent.context.disabled || state.disabled : state.disabled,
1995
- modal: state => (state.parent.type === undefined || state.parent.type === 'context-menu') && (state.modal ?? true),
1996
- openMethod: state => state.openMethod,
1997
- allowMouseEnter: state => state.allowMouseEnter,
1998
- highlightItemOnHover: state => state.highlightItemOnHover,
1999
- parent: state => state.parent,
2000
- rootId: state => {
1989
+ disabled: useScrollLock.createSelector(state => state.parent.type === 'menubar' ? state.parent.context.disabled || state.disabled : state.disabled),
1990
+ modal: useScrollLock.createSelector(state => (state.parent.type === undefined || state.parent.type === 'context-menu') && (state.modal ?? true)),
1991
+ openMethod: useScrollLock.createSelector(state => state.openMethod),
1992
+ allowMouseEnter: useScrollLock.createSelector(state => state.allowMouseEnter),
1993
+ highlightItemOnHover: useScrollLock.createSelector(state => state.highlightItemOnHover),
1994
+ stickIfOpen: useScrollLock.createSelector(state => state.stickIfOpen),
1995
+ parent: useScrollLock.createSelector(state => state.parent),
1996
+ rootId: useScrollLock.createSelector(state => {
2001
1997
  if (state.parent.type === 'menu') {
2002
1998
  return state.parent.store.select('rootId');
2003
1999
  }
2004
2000
  return state.parent.type !== undefined ? state.parent.context.rootId : state.rootId;
2005
- },
2006
- activeIndex: state => state.activeIndex,
2007
- isActive: (state, itemIndex) => state.activeIndex === itemIndex,
2008
- hoverEnabled: state => state.hoverEnabled,
2009
- instantType: state => state.instantType,
2010
- lastOpenChangeReason: state => state.openChangeReason,
2011
- floatingTreeRoot: state => {
2001
+ }),
2002
+ activeIndex: useScrollLock.createSelector(state => state.activeIndex),
2003
+ isActive: useScrollLock.createSelector((state, itemIndex) => state.activeIndex === itemIndex),
2004
+ hoverEnabled: useScrollLock.createSelector(state => state.hoverEnabled),
2005
+ instantType: useScrollLock.createSelector(state => state.instantType),
2006
+ lastOpenChangeReason: useScrollLock.createSelector(state => state.openChangeReason),
2007
+ floatingTreeRoot: useScrollLock.createSelector(state => {
2012
2008
  if (state.parent.type === 'menu') {
2013
2009
  return state.parent.store.select('floatingTreeRoot');
2014
2010
  }
2015
2011
  return state.floatingTreeRoot;
2016
- },
2017
- floatingNodeId: state => state.floatingNodeId,
2018
- floatingParentNodeId: state => state.floatingParentNodeId,
2019
- itemProps: state => state.itemProps,
2020
- closeDelay: state => state.closeDelay,
2021
- adaptiveOrigin: state => state.adaptiveOrigin,
2022
- keyboardEventRelay: state => {
2012
+ }),
2013
+ floatingNodeId: useScrollLock.createSelector(state => state.floatingNodeId),
2014
+ floatingParentNodeId: useScrollLock.createSelector(state => state.floatingParentNodeId),
2015
+ itemProps: useScrollLock.createSelector(state => state.itemProps),
2016
+ closeDelay: useScrollLock.createSelector(state => state.closeDelay),
2017
+ hasViewport: useScrollLock.createSelector(state => state.hasViewport),
2018
+ keyboardEventRelay: useScrollLock.createSelector(state => {
2023
2019
  if (state.keyboardEventRelay) {
2024
2020
  return state.keyboardEventRelay;
2025
2021
  }
@@ -2027,23 +2023,33 @@ const selectors = {
2027
2023
  return state.parent.store.select('keyboardEventRelay');
2028
2024
  }
2029
2025
  return undefined;
2030
- }
2026
+ })
2031
2027
  };
2032
-
2033
- /**
2034
- * The store view that detached handle-backed triggers read from. Both the real `MenuStore` and the
2035
- * inert fallback store satisfy it, so a trigger can read from whichever store the handle currently
2036
- * exposes. Narrowed to the members a trigger actually uses — the trigger-data members plus `setOpen`
2037
- * (called by the focus guards) — so the exposed surface can't bypass the open-change pipeline; on
2038
- * the detached fallback store every one of these mutations is a no-op.
2039
- */
2040
-
2041
2028
  class MenuStore extends useScrollLock.ReactStore {
2042
2029
  constructor(initialState) {
2043
2030
  super({
2044
2031
  ...createInitialState(),
2045
2032
  ...initialState
2046
- }, createInitialContext(), selectors);
2033
+ }, {
2034
+ positionerRef: /*#__PURE__*/React__namespace.createRef(),
2035
+ popupRef: /*#__PURE__*/React__namespace.createRef(),
2036
+ typingRef: {
2037
+ current: false
2038
+ },
2039
+ itemDomElements: {
2040
+ current: []
2041
+ },
2042
+ itemLabels: {
2043
+ current: []
2044
+ },
2045
+ allowMouseUpTriggerRef: {
2046
+ current: false
2047
+ },
2048
+ triggerFocusTargetRef: /*#__PURE__*/React__namespace.createRef(),
2049
+ beforeContentFocusGuardRef: /*#__PURE__*/React__namespace.createRef(),
2050
+ onOpenChangeComplete: undefined,
2051
+ triggerElements: new useScrollLock.PopupTriggerMap()
2052
+ }, selectors);
2047
2053
 
2048
2054
  // Set up propagation of state from parent menu if applicable.
2049
2055
  this.unsubscribeParentListener = this.observe('parent', parent => {
@@ -2079,44 +2085,15 @@ class MenuStore extends useScrollLock.ReactStore {
2079
2085
  eventDetails
2080
2086
  });
2081
2087
  }
2088
+ static useStore(externalStore, initialState) {
2089
+ // eslint-disable-next-line react-hooks/rules-of-hooks
2090
+ const internalStore = useRenderElement.useRefWithInit(() => {
2091
+ return new MenuStore(initialState);
2092
+ }).current;
2093
+ return externalStore ?? internalStore;
2094
+ }
2082
2095
  unsubscribeParentListener = null;
2083
2096
  }
2084
-
2085
- /**
2086
- * Creates the inert fallback store used by detached handle-backed triggers while no `Menu.Root` is
2087
- * attached. It preserves a menu-specific trigger registry in context so detached triggers can
2088
- * register before migrating to the live root store. `setOpen` is a no-op (matching the inert
2089
- * reads/writes of `NullStore`), so a trigger can hand the store to focus-guard helpers that expect
2090
- * `setOpen` without it ever taking effect while detached.
2091
- */
2092
- function createNullMenuStore() {
2093
- const store = new ScrollAreaViewport.NullStore(Object.freeze(createInitialState()), Object.freeze(createInitialContext()), selectors);
2094
- return Object.assign(store, {
2095
- setOpen: useRenderElement.NOOP
2096
- });
2097
- }
2098
- function createInitialContext() {
2099
- return {
2100
- positionerRef: /*#__PURE__*/React__namespace.createRef(),
2101
- popupRef: /*#__PURE__*/React__namespace.createRef(),
2102
- typingRef: {
2103
- current: false
2104
- },
2105
- itemDomElements: {
2106
- current: []
2107
- },
2108
- itemLabels: {
2109
- current: []
2110
- },
2111
- allowMouseUpTriggerRef: {
2112
- current: false
2113
- },
2114
- triggerFocusTargetRef: /*#__PURE__*/React__namespace.createRef(),
2115
- beforeContentFocusGuardRef: /*#__PURE__*/React__namespace.createRef(),
2116
- onOpenChangeComplete: undefined,
2117
- triggerElements: new useScrollLock.PopupTriggerMap()
2118
- };
2119
- }
2120
2097
  function createInitialState() {
2121
2098
  return {
2122
2099
  ...ScrollAreaViewport.createInitialPopupStoreState(),
@@ -2125,6 +2102,7 @@ function createInitialState() {
2125
2102
  openMethod: null,
2126
2103
  allowMouseEnter: false,
2127
2104
  highlightItemOnHover: true,
2105
+ stickIfOpen: true,
2128
2106
  parent: {
2129
2107
  type: undefined
2130
2108
  },
@@ -2139,7 +2117,7 @@ function createInitialState() {
2139
2117
  itemProps: useRenderElement.EMPTY_OBJECT,
2140
2118
  keyboardEventRelay: undefined,
2141
2119
  closeDelay: 0,
2142
- adaptiveOrigin: undefined
2120
+ hasViewport: false
2143
2121
  };
2144
2122
  }
2145
2123
 
@@ -2198,13 +2176,14 @@ const MenuRoot = useScrollLock.fastComponent(function MenuRoot(props) {
2198
2176
  type: undefined
2199
2177
  };
2200
2178
  }, [contextMenuContext, parentMenuRootContext, menubarContext, isSubmenu]);
2201
- const store = useMenuRootStore({
2179
+ const store = MenuStore.useStore(handle?.store, {
2202
2180
  open: defaultOpen,
2203
2181
  openProp,
2204
2182
  activeTriggerId: defaultTriggerIdProp,
2205
2183
  triggerIdProp,
2206
2184
  parent: parentFromContext
2207
2185
  });
2186
+ useScrollLock.useInitialOpenSync(store, openProp, defaultOpen, defaultTriggerIdProp);
2208
2187
  store.useControlledProp('openProp', openProp);
2209
2188
  store.useControlledProp('triggerIdProp', triggerIdProp);
2210
2189
  store.useContextCallback('onOpenChangeComplete', onOpenChangeComplete);
@@ -2249,7 +2228,10 @@ const MenuRoot = useScrollLock.fastComponent(function MenuRoot(props) {
2249
2228
  const {
2250
2229
  forceUnmount
2251
2230
  } = useScrollLock.useOpenStateTransitions(open, store, () => {
2252
- store.set('allowMouseEnter', false);
2231
+ store.update({
2232
+ allowMouseEnter: false,
2233
+ stickIfOpen: true
2234
+ });
2253
2235
  });
2254
2236
  useScrollLock.useIsoLayoutEffect(() => {
2255
2237
  if (contextMenuContext && !parentMenuRootContext) {
@@ -2297,12 +2279,6 @@ const MenuRoot = useScrollLock.fastComponent(function MenuRoot(props) {
2297
2279
  }, [open, hoverEnabled, store]);
2298
2280
  const setOpen = useScrollLock.useStableCallback((nextOpen, eventDetails) => {
2299
2281
  const reason = eventDetails.reason;
2300
-
2301
- // Read the store directly, as relayed tree events and stale hover timers can request
2302
- // a close after the state changed but before this component re-rendered.
2303
- if (!nextOpen && !store.select('open')) {
2304
- return;
2305
- }
2306
2282
  if (open === nextOpen && eventDetails.trigger === activeTriggerElement && lastOpenChangeReason === reason) {
2307
2283
  return;
2308
2284
  }
@@ -2335,17 +2311,13 @@ const MenuRoot = useScrollLock.fastComponent(function MenuRoot(props) {
2335
2311
  allowTouchToCloseRef.current = true;
2336
2312
  allowTouchToCloseTimeout.clear();
2337
2313
  }
2338
-
2339
- // Keyboard and assistive-technology activations produce `detail === 0` clicks;
2340
- // mouse-gesture clicks (including the synthesized drag-release click from
2341
- // `useMenuItemCommonProps`) carry `detail >= 1`.
2342
- const isKeyboardClick = (reason === useScrollLock.triggerPress || reason === useScrollLock.itemPress) && nativeEvent.detail === 0;
2314
+ const isKeyboardClick = (reason === useScrollLock.triggerPress || reason === useScrollLock.itemPress) && nativeEvent.detail === 0 && nativeEvent?.isTrusted;
2343
2315
  const isDismissClose = !nextOpen && (reason === useScrollLock.escapeKey || reason == null);
2344
2316
  const updatedState = {
2345
2317
  open: nextOpen,
2346
2318
  openChangeReason: reason
2347
2319
  };
2348
- openEventRef.current = eventDetails.event;
2320
+ openEventRef.current = eventDetails.event ?? null;
2349
2321
  useScrollLock.setPopupOpenState(updatedState, nextOpen, eventDetails.trigger, shouldPreventUnmountOnClose());
2350
2322
  store.update(updatedState);
2351
2323
  if (parent.type === 'menubar' && (reason === useScrollLock.triggerFocus || reason === useScrollLock.focusOut || reason === useScrollLock.triggerHover || reason === useScrollLock.listNavigation || reason === useScrollLock.siblingOpen)) {
@@ -2363,11 +2335,7 @@ const MenuRoot = useScrollLock.fastComponent(function MenuRoot(props) {
2363
2335
  onOpenChange: setOpen
2364
2336
  });
2365
2337
  const floatingEvents = floatingRootContext.context.events;
2366
-
2367
- // Registered in a layout effect (not a passive one) so `setOpen` emits from imperative
2368
- // `MenuHandle.open()` calls made in the same commit this root mounts — e.g. from another layout
2369
- // effect during a route-transition handoff — are received instead of being silently dropped.
2370
- useScrollLock.useIsoLayoutEffect(() => {
2338
+ React__namespace.useEffect(() => {
2371
2339
  const handleSetOpenEvent = ({
2372
2340
  open: nextOpen,
2373
2341
  eventDetails
@@ -2496,14 +2464,11 @@ const MenuRoot = useScrollLock.fastComponent(function MenuRoot(props) {
2496
2464
  store,
2497
2465
  parent: parentFromContext
2498
2466
  }), [store, parentFromContext]);
2499
- const content = /*#__PURE__*/jsxRuntime.jsxs(MenuRootContext.Provider, {
2467
+ const content = /*#__PURE__*/jsxRuntime.jsx(MenuRootContext.Provider, {
2500
2468
  value: context,
2501
- children: [handle && /*#__PURE__*/jsxRuntime.jsx(useScrollLock.PopupHandleAttachment, {
2502
- handle: handle,
2503
- store: store
2504
- }), typeof children === 'function' ? children({
2469
+ children: typeof children === 'function' ? children({
2505
2470
  payload
2506
- }) : children]
2471
+ }) : children
2507
2472
  });
2508
2473
  if (parent.type === undefined || parent.type === 'context-menu') {
2509
2474
  // set up a FloatingTree to provide the context to nested menus
@@ -2515,15 +2480,6 @@ const MenuRoot = useScrollLock.fastComponent(function MenuRoot(props) {
2515
2480
  return content;
2516
2481
  });
2517
2482
  if (process.env.NODE_ENV !== "production") MenuRoot.displayName = "MenuRoot";
2518
- function useMenuRootStore(initialState) {
2519
- // The store is owned by this Root instance and created exactly once. It is not tied to the handle:
2520
- // the handle attaches to it, so swapping the handle re-attaches rather than recreating state.
2521
- // Default values are only initial values; controlled values and root state are synced after creation.
2522
- // Unlike other popups, Menu wires its floating root context separately (it relays open changes
2523
- // through an event).
2524
- const store = useRenderElement.useRefWithInit(() => new MenuStore(initialState)).current;
2525
- return store;
2526
- }
2527
2483
 
2528
2484
  /**
2529
2485
  * Groups all parts of a submenu.
@@ -2580,6 +2536,9 @@ function useCompositeItem(params = {}) {
2580
2536
  };
2581
2537
  }
2582
2538
 
2539
+ /**
2540
+ * @internal
2541
+ */
2583
2542
  function CompositeItem(componentProps) {
2584
2543
  const {
2585
2544
  render,
@@ -2601,21 +2560,20 @@ function CompositeItem(componentProps) {
2601
2560
  });
2602
2561
  return useRenderElement.useRenderElement(tag, componentProps, {
2603
2562
  state,
2604
- // The composite ref attaches first so an outer item wins when nested items share a DOM node.
2605
- ref: [compositeRef, ...refs],
2563
+ ref: [...refs, compositeRef],
2606
2564
  props: [compositeProps, ...props, elementProps],
2607
2565
  stateAttributesMapping
2608
2566
  });
2609
2567
  }
2610
2568
 
2611
2569
  function findRootOwnerId(node) {
2612
- if (useScrollLock.isHTMLElement(node) && node.hasAttribute('data-rootownerid')) {
2613
- return node.getAttribute('data-rootownerid');
2570
+ if (floatingUi_utils.isHTMLElement(node) && node.hasAttribute('data-rootownerid')) {
2571
+ return node.getAttribute('data-rootownerid') ?? undefined;
2614
2572
  }
2615
- if (useScrollLock.isLastTraversableNode(node)) {
2573
+ if (floatingUi_utils.isLastTraversableNode(node)) {
2616
2574
  return undefined;
2617
2575
  }
2618
- return findRootOwnerId(useScrollLock.getParentNode(node));
2576
+ return findRootOwnerId(floatingUi_utils.getParentNode(node));
2619
2577
  }
2620
2578
 
2621
2579
  /**
@@ -2702,6 +2660,14 @@ function useMixedToggleClickHandler(params) {
2702
2660
  }, [enabled, mouseDownAction, open]);
2703
2661
  }
2704
2662
 
2663
+ const BOUNDARY_OFFSET = 2;
2664
+
2665
+ /**
2666
+ * A button that opens the menu.
2667
+ * Renders a `<button>` element.
2668
+ *
2669
+ * Documentation: [Base UI Menu](https://base-ui.com/react/components/menu)
2670
+ */
2705
2671
  const MenuTrigger = useScrollLock.fastComponentRef(function MenuTrigger(componentProps, forwardedRef) {
2706
2672
  const {
2707
2673
  render,
@@ -2718,8 +2684,7 @@ const MenuTrigger = useScrollLock.fastComponentRef(function MenuTrigger(componen
2718
2684
  ...elementProps
2719
2685
  } = componentProps;
2720
2686
  const rootContext = useMenuRootContext(true);
2721
- const handleStore = ScrollAreaViewport.usePopupHandleStore(handle);
2722
- const store = handleStore ?? rootContext?.store;
2687
+ const store = handle?.store ?? rootContext?.store;
2723
2688
  if (!store) {
2724
2689
  throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: <Menu.Trigger> must be either used within a <Menu.Root> component or provided with a handle.' : useRenderElement.formatErrorMessage(85));
2725
2690
  }
@@ -2779,7 +2744,8 @@ const MenuTrigger = useScrollLock.fastComponentRef(function MenuTrigger(componen
2779
2744
  if (mouseUpTarget != null && findRootOwnerId(mouseUpTarget) === store.select('rootId')) {
2780
2745
  return;
2781
2746
  }
2782
- if (useCompositeListItem.isMouseWithinBounds(mouseEvent, triggerRef.current)) {
2747
+ const bounds = useCompositeListItem.getPseudoElementBounds(triggerRef.current);
2748
+ if (mouseEvent.clientX >= bounds.left - BOUNDARY_OFFSET && mouseEvent.clientX <= bounds.right + BOUNDARY_OFFSET && mouseEvent.clientY >= bounds.top - BOUNDARY_OFFSET && mouseEvent.clientY <= bounds.bottom + BOUNDARY_OFFSET) {
2783
2749
  return;
2784
2750
  }
2785
2751
  floatingTreeRoot.events.emit('close', {
@@ -2798,7 +2764,7 @@ const MenuTrigger = useScrollLock.fastComponentRef(function MenuTrigger(componen
2798
2764
  const parentMenubarHasSubmenuOpen = isInMenubar && parent.context.hasSubmenuOpen;
2799
2765
  const openOnHover = openOnHoverProp ?? parentMenubarHasSubmenuOpen;
2800
2766
  const hoverProps = useHoverReferenceInteraction(floatingRootContext, {
2801
- enabled: openOnHover && !disabled && (!isInMenubar || parentMenubarHasSubmenuOpen && !isMountedByThisTrigger),
2767
+ enabled: openOnHover && !disabled && parent.type !== 'context-menu' && (!isInMenubar || parentMenubarHasSubmenuOpen && !isMountedByThisTrigger),
2802
2768
  handleClose: safePolygon({
2803
2769
  blockPointerEvents: !isInMenubar
2804
2770
  }),
@@ -2819,7 +2785,7 @@ const MenuTrigger = useScrollLock.fastComponentRef(function MenuTrigger(componen
2819
2785
  // only when `isOpenedByThisTrigger` changes.
2820
2786
  const stickIfOpen = useStickIfOpen(isOpenedByThisTrigger, store.select('lastOpenChangeReason'));
2821
2787
  const click = useScrollLock.useClick(floatingRootContext, {
2822
- enabled: !disabled,
2788
+ enabled: !disabled && parent.type !== 'context-menu',
2823
2789
  event: isOpenedByThisTrigger && isInMenubar ? 'click' : 'mousedown',
2824
2790
  toggle: true,
2825
2791
  ignoreMouse: false,
@@ -2914,7 +2880,7 @@ function useStickIfOpen(open, openReason) {
2914
2880
  const stickIfOpenTimeout = useScrollLock.useTimeout();
2915
2881
  const [stickIfOpen, setStickIfOpen] = React__namespace.useState(false);
2916
2882
  useScrollLock.useIsoLayoutEffect(() => {
2917
- if (open && openReason === useScrollLock.triggerHover) {
2883
+ if (open && openReason === 'trigger-hover') {
2918
2884
  // Only allow "patient" clicks to close the menu if it's open.
2919
2885
  // If they clicked within 500ms of the menu opening, keep it open.
2920
2886
  setStickIfOpen(true);
@@ -2929,6 +2895,8 @@ function useStickIfOpen(open, openReason) {
2929
2895
  return stickIfOpen;
2930
2896
  }
2931
2897
  function useMenuParent() {
2898
+ const contextMenuContext = useContextMenuRootContext(true);
2899
+ const parentContext = useMenuRootContext(true);
2932
2900
  const menubarContext = useMenubarContext(true);
2933
2901
  const parent = React__namespace.useMemo(() => {
2934
2902
  if (menubarContext) {
@@ -2937,17 +2905,23 @@ function useMenuParent() {
2937
2905
  context: menubarContext
2938
2906
  };
2939
2907
  }
2908
+
2909
+ // Ensure this is not a Menu nested inside ContextMenu.Trigger.
2910
+ // ContextMenu parentContext is always undefined as ContextMenu.Root is instantiated with
2911
+ // <MenuRootContext.Provider value={undefined}>
2912
+ if (contextMenuContext && !parentContext) {
2913
+ return {
2914
+ type: 'context-menu',
2915
+ context: contextMenuContext
2916
+ };
2917
+ }
2940
2918
  return {
2941
2919
  type: undefined
2942
2920
  };
2943
- }, [menubarContext]);
2921
+ }, [contextMenuContext, parentContext, menubarContext]);
2944
2922
  return parent;
2945
2923
  }
2946
2924
 
2947
- const VOICE_OVER_EXPANDED_PROPS = {
2948
- 'aria-expanded': undefined
2949
- };
2950
-
2951
2925
  /**
2952
2926
  * A menu item that opens a submenu.
2953
2927
  * Renders a `<div>` element.
@@ -2968,12 +2942,7 @@ const MenuSubmenuTrigger = /*#__PURE__*/React__namespace.forwardRef(function Men
2968
2942
  disabled: disabledProp = false,
2969
2943
  ...elementProps
2970
2944
  } = componentProps;
2971
- const submenuRootContext = useMenuSubmenuRootContext();
2972
- if (!submenuRootContext?.parentMenu) {
2973
- throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: <Menu.SubmenuTrigger> must be placed in <Menu.SubmenuRoot>.' : useRenderElement.formatErrorMessage(37));
2974
- }
2975
2945
  const listItem = useCompositeListItem.useCompositeListItem({
2976
- guess: true,
2977
2946
  label
2978
2947
  });
2979
2948
  const menuPositionerContext = useMenuPositionerContext();
@@ -3002,6 +2971,10 @@ const MenuSubmenuTrigger = /*#__PURE__*/React__namespace.forwardRef(function Men
3002
2971
  triggerElementRef.current = el;
3003
2972
  store.set('activeTriggerElement', el);
3004
2973
  }, [store]);
2974
+ const submenuRootContext = useMenuSubmenuRootContext();
2975
+ if (!submenuRootContext?.parentMenu) {
2976
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: <Menu.SubmenuTrigger> must be placed in <Menu.SubmenuRoot>.' : useRenderElement.formatErrorMessage(37));
2977
+ }
3005
2978
  store.useSyncedValue('closeDelay', closeDelay);
3006
2979
  const parentMenuStore = submenuRootContext.parentMenu;
3007
2980
  const rootDisabled = store.useState('disabled');
@@ -3009,7 +2982,7 @@ const MenuSubmenuTrigger = /*#__PURE__*/React__namespace.forwardRef(function Men
3009
2982
  const disabled = disabledProp || rootDisabled || parentDisabled;
3010
2983
  if (process.env.NODE_ENV !== 'production') {
3011
2984
  // eslint-disable-next-line react-hooks/rules-of-hooks
3012
- React__namespace.useEffect(() => {
2985
+ useScrollLock.useIsoLayoutEffect(() => {
3013
2986
  const element = triggerElementRef.current;
3014
2987
  if (element && SelectGroupLabel.isElementDisabled(element) && !disabled) {
3015
2988
  const ownerStackMessage = useScrollLock.SafeReact.captureOwnerStack?.() || '';
@@ -3057,10 +3030,7 @@ const MenuSubmenuTrigger = /*#__PURE__*/React__namespace.forwardRef(function Men
3057
3030
  shouldOpen: delay > 0 ? () => parentMenuStore.select('allowMouseEnter') : undefined,
3058
3031
  triggerElementRef,
3059
3032
  externalTree: floatingTreeRoot,
3060
- isClosing: () => store.select('transitionStatus') === 'ending',
3061
- // Chrome can drop the trigger's `mouseleave` during a fast pointer sweep,
3062
- // leaving a stale submenu open (see #5152) — cancel from `mouseout` too.
3063
- guardStaleOpen: true
3033
+ isClosing: () => store.select('transitionStatus') === 'ending'
3064
3034
  });
3065
3035
  const click = useScrollLock.useClick(floatingRootContext, {
3066
3036
  enabled: !disabled,
@@ -3077,22 +3047,10 @@ const MenuSubmenuTrigger = /*#__PURE__*/React__namespace.forwardRef(function Men
3077
3047
  highlighted,
3078
3048
  open
3079
3049
  };
3080
- const openMethod = store.useState('openMethod');
3081
- const lastOpenChangeReason = store.useState('lastOpenChangeReason');
3082
- // Arrow keys open the submenu through list navigation without dispatching a click, so
3083
- // `openMethod` stays null there; Enter and Space do dispatch one and report `keyboard`.
3084
- const openedByKeyboard = lastOpenChangeReason === useScrollLock.listNavigation || openMethod === 'keyboard';
3085
- const shouldOmitExpanded = open && openedByKeyboard && useScrollLock.voiceOver;
3086
3050
  const element = useRenderElement.useRenderElement('div', componentProps, {
3087
3051
  state,
3088
3052
  stateAttributesMapping: useScrollLock.triggerOpenStateMapping,
3089
- props: [localInteractionProps, hoverProps, rootTriggerProps, itemProps,
3090
- // Opening a submenu changes the trigger's expanded state while the trigger still holds
3091
- // focus, and VoiceOver announces that state change instead of the submenu item that focus
3092
- // moves to a moment later, so the first item is never announced. Dropping the state while
3093
- // the submenu is open avoids the announcement without claiming the submenu is collapsed;
3094
- // `aria-haspopup` still conveys that the item opens a submenu.
3095
- shouldOmitExpanded ? VOICE_OVER_EXPANDED_PROPS : undefined, {
3053
+ props: [localInteractionProps, hoverProps, rootTriggerProps, itemProps, {
3096
3054
  'aria-controls': popupId,
3097
3055
  tabIndex: open || highlighted ? 0 : -1,
3098
3056
  onBlur() {
@@ -3119,11 +3077,11 @@ exports.MenuRadioGroup = MenuRadioGroup;
3119
3077
  exports.MenuRadioItem = MenuRadioItem;
3120
3078
  exports.MenuRadioItemIndicator = MenuRadioItemIndicator;
3121
3079
  exports.MenuRoot = MenuRoot;
3080
+ exports.MenuStore = MenuStore;
3122
3081
  exports.MenuSubmenuRoot = MenuSubmenuRoot;
3123
3082
  exports.MenuSubmenuTrigger = MenuSubmenuTrigger;
3124
3083
  exports.MenuTrigger = MenuTrigger;
3125
3084
  exports.REGULAR_ITEM = REGULAR_ITEM;
3126
- exports.createNullMenuStore = createNullMenuStore;
3127
3085
  exports.itemMapping = itemMapping;
3128
3086
  exports.useMenuItem = useMenuItem;
3129
3087
  exports.useMenuPositionerContext = useMenuPositionerContext;