@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
@@ -1,6 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import React__default from 'react';
3
- import { a as useRefWithInit, i as isReactVersionAtLeast, N as NOOP, c as EMPTY_ARRAY, E as EMPTY_OBJECT, u as useRenderElement, b as useMergedRefs, f as formatErrorMessage, m as mergeProps, d as makeEventPreventable } from './useRenderElement-es.js';
3
+ import { a as isShadowRoot, i as isHTMLElement, b as isElement, h as getComputedStyle, f as floor, g as getNodeName, c as isNode, K as getWindow, d as isLastTraversableNode, e as getParentNode, I as isOverflowElement } from './floating-ui.utils-es.js';
4
+ import { a as useRefWithInit, f as formatErrorMessage, i as isReactVersionAtLeast, N as NOOP, E as EMPTY_OBJECT, u as useRenderElement, b as useMergedRefs, m as mergeProps, d as makeEventPreventable } from './useRenderElement-es.js';
4
5
  import { jsx, jsxs } from 'react/jsx-runtime';
5
6
  import * as ReactDOM from 'react-dom';
6
7
 
@@ -82,6 +83,14 @@ function serializeToDevModeString(input) {
82
83
  const noop = () => {};
83
84
  const useIsoLayoutEffect = typeof document !== 'undefined' ? React.useLayoutEffect : noop;
84
85
 
86
+ function useOnFirstRender(fn) {
87
+ const ref = React.useRef(true);
88
+ if (ref.current) {
89
+ ref.current = false;
90
+ fn();
91
+ }
92
+ }
93
+
85
94
  /**
86
95
  * A clone of the React namespace for reading APIs that may be missing in older
87
96
  * supported React versions. Bundlers can rewrite direct `React.someNewApi`
@@ -180,6 +189,87 @@ const visuallyHiddenInput = {
180
189
  position: 'absolute'
181
190
  };
182
191
 
192
+ /**
193
+ * The NoOptionalParams type is a utility type that checks if a function has optional or default parameters.
194
+ * If the function has optional or default parameters, it returns a string literal type with an error message.
195
+ * Otherwise, it returns the original function type.
196
+ *
197
+ * This is used to enforce that the combiner function passed to createSelector does not have optional or default parameters,
198
+ * as memoization relies on the Function.length property, which does not account for optional or default parameters.
199
+ */
200
+
201
+ /**
202
+ * Creates a selector function that can be used to derive values from the store's state.
203
+ *
204
+ * The combiner function can have up to three additional parameters, but it **cannot have optional or default parameters**.
205
+ *
206
+ * This function accepts up to six functions and combines them into a single selector function.
207
+ * The resulting selector will take the state from the combined selectors and any additional parameters required by the combiner.
208
+ *
209
+ * The return type of the resulting selector is determined by the return type of the combiner function.
210
+ *
211
+ * @example
212
+ * const selector = createSelector(
213
+ * (state) => state.disabled
214
+ * );
215
+ *
216
+ * @example
217
+ * const selector = createSelector(
218
+ * (state) => state.disabled,
219
+ * (state) => state.open,
220
+ * (disabled, open) => ({ disabled, open })
221
+ * );
222
+ */
223
+ /* eslint-disable id-denylist */
224
+ const createSelector = (a, b, c, d, e, f, ...other) => {
225
+ if (other.length > 0) {
226
+ throw new Error(process.env.NODE_ENV !== "production" ? 'Unsupported number of selectors' : formatErrorMessage(1));
227
+ }
228
+ let selector;
229
+ if (a && b && c && d && e && f) {
230
+ selector = (state, a1, a2, a3) => {
231
+ const va = a(state, a1, a2, a3);
232
+ const vb = b(state, a1, a2, a3);
233
+ const vc = c(state, a1, a2, a3);
234
+ const vd = d(state, a1, a2, a3);
235
+ const ve = e(state, a1, a2, a3);
236
+ return f(va, vb, vc, vd, ve, a1, a2, a3);
237
+ };
238
+ } else if (a && b && c && d && e) {
239
+ selector = (state, a1, a2, a3) => {
240
+ const va = a(state, a1, a2, a3);
241
+ const vb = b(state, a1, a2, a3);
242
+ const vc = c(state, a1, a2, a3);
243
+ const vd = d(state, a1, a2, a3);
244
+ return e(va, vb, vc, vd, a1, a2, a3);
245
+ };
246
+ } else if (a && b && c && d) {
247
+ selector = (state, a1, a2, a3) => {
248
+ const va = a(state, a1, a2, a3);
249
+ const vb = b(state, a1, a2, a3);
250
+ const vc = c(state, a1, a2, a3);
251
+ return d(va, vb, vc, a1, a2, a3);
252
+ };
253
+ } else if (a && b && c) {
254
+ selector = (state, a1, a2, a3) => {
255
+ const va = a(state, a1, a2, a3);
256
+ const vb = b(state, a1, a2, a3);
257
+ return c(va, vb, a1, a2, a3);
258
+ };
259
+ } else if (a && b) {
260
+ selector = (state, a1, a2, a3) => {
261
+ const va = a(state, a1, a2, a3);
262
+ return b(va, a1, a2, a3);
263
+ };
264
+ } else if (a) {
265
+ selector = a;
266
+ } else {
267
+ throw /* minify-error-disabled */new Error('Missing arguments');
268
+ }
269
+ return selector;
270
+ };
271
+ /* eslint-enable id-denylist */
272
+
183
273
  var shim = {exports: {}};
184
274
 
185
275
  var useSyncExternalStoreShim_production = {};
@@ -587,41 +677,6 @@ function getInstance() {
587
677
  function register(hook) {
588
678
  hooks.push(hook);
589
679
  }
590
-
591
- /**
592
- * Wraps a component function to enable performance optimizations for internal hooks.
593
- *
594
- * **Performance Optimization:**
595
- * Components wrapped with `fastComponent` have access to a shared "instance" context that enables
596
- * specialized hook implementations to batch operations and reduce overhead. The wrapper creates a
597
- * stable instance object that persists across renders, sets it as the current context, calls
598
- * registered hooks before and after rendering, then clears the context. The primary benefit is
599
- * with `useStore`, where multiple store subscriptions within the same component are collapsed into
600
- * a single `useSyncExternalStore` subscription per store, significantly reducing re-render overhead.
601
- * This optimization is only active on React 19+; on earlier versions `useStore` falls back to a
602
- * separate subscription per call.
603
- *
604
- * **Requirements:**
605
- * - The component function should follow standard React component patterns
606
- * - `useStore` calls must keep a stable order and count across renders, as batched hooks are
607
- * matched by call index
608
- * - Do not rely on the instance context outside of specialized hooks
609
- *
610
- * @param fn - The component function to wrap
611
- * @returns A wrapped component with the same signature as the input function
612
- *
613
- * @example
614
- * ```tsx
615
- * // Wrapping a component to enable optimized useStore batching
616
- * export const TooltipRoot = fastComponent(function TooltipRoot(props) {
617
- * // These useStore calls share a single subscription
618
- * const open = useStore(store, (state) => state.open);
619
- * const disabled = useStore(store, (state) => state.disabled);
620
- * const value = useStore(store, (state) => state.value);
621
- * // ...
622
- * });
623
- * ```
624
- */
625
680
  function fastComponent(fn) {
626
681
  const FastComponent = (props, forwardedRef) => {
627
682
  const instance = useRefWithInit(createInstance).current;
@@ -644,31 +699,6 @@ function fastComponent(fn) {
644
699
  FastComponent.displayName = fn.displayName || fn.name;
645
700
  return FastComponent;
646
701
  }
647
-
648
- /**
649
- * Wraps a component function with ref forwarding to enable performance optimizations for internal hooks.
650
- *
651
- * This is a convenience wrapper that combines `fastComponent` with `React.forwardRef`, enabling
652
- * both performance optimizations and proper ref forwarding. See `fastComponent` for details on
653
- * the performance benefits.
654
- *
655
- * @param fn - The component function that accepts props and a forwarded ref
656
- * @returns A wrapped component with ref forwarding enabled
657
- *
658
- * @example
659
- * ```tsx
660
- * // Wrapping a component with ref forwarding and optimized hooks
661
- * export const TooltipTrigger = fastComponentRef(function TooltipTrigger(
662
- * props,
663
- * forwardedRef
664
- * ) {
665
- * const store = useContext(TooltipContext);
666
- * const open = useStore(store, (state) => state.open);
667
- * // ... component logic with ref
668
- * return <button ref={forwardedRef} {...props} />;
669
- * });
670
- * ```
671
- */
672
702
  function fastComponentRef(fn) {
673
703
  return /*#__PURE__*/React.forwardRef(fastComponent(fn));
674
704
  }
@@ -1084,170 +1114,6 @@ class ReactStore extends Store {
1084
1114
  }
1085
1115
  }
1086
1116
 
1087
- function hasWindow() {
1088
- return typeof window !== 'undefined';
1089
- }
1090
- function getNodeName(node) {
1091
- if (isNode(node)) {
1092
- return (node.nodeName || '').toLowerCase();
1093
- }
1094
- // Mocked nodes in testing environments may not be instances of Node. By
1095
- // returning `#document` an infinite loop won't occur.
1096
- // https://github.com/floating-ui/floating-ui/issues/2317
1097
- return '#document';
1098
- }
1099
- function getWindow(node) {
1100
- var _node$ownerDocument;
1101
- return (node == null || (_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window;
1102
- }
1103
- function getDocumentElement(node) {
1104
- var _ref;
1105
- return (_ref = (isNode(node) ? node.ownerDocument : node.document) || window.document) == null ? void 0 : _ref.documentElement;
1106
- }
1107
- function isNode(value) {
1108
- if (!hasWindow()) {
1109
- return false;
1110
- }
1111
- return value instanceof Node || value instanceof getWindow(value).Node;
1112
- }
1113
- function isElement(value) {
1114
- if (!hasWindow()) {
1115
- return false;
1116
- }
1117
- return value instanceof Element || value instanceof getWindow(value).Element;
1118
- }
1119
- function isHTMLElement(value) {
1120
- if (!hasWindow()) {
1121
- return false;
1122
- }
1123
- return value instanceof HTMLElement || value instanceof getWindow(value).HTMLElement;
1124
- }
1125
- function isShadowRoot(value) {
1126
- if (!hasWindow() || typeof ShadowRoot === 'undefined') {
1127
- return false;
1128
- }
1129
- return value instanceof ShadowRoot || value instanceof getWindow(value).ShadowRoot;
1130
- }
1131
- function isOverflowElement(element) {
1132
- const {
1133
- overflow,
1134
- overflowX,
1135
- overflowY,
1136
- display
1137
- } = getComputedStyle(element);
1138
- return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && display !== 'inline' && display !== 'contents';
1139
- }
1140
- function isTableElement(element) {
1141
- return /^(table|td|th)$/.test(getNodeName(element));
1142
- }
1143
- function isTopLayer(element) {
1144
- try {
1145
- if (element.matches(':popover-open')) {
1146
- return true;
1147
- }
1148
- } catch (_e) {
1149
- // no-op
1150
- }
1151
- try {
1152
- return element.matches(':modal');
1153
- } catch (_e) {
1154
- return false;
1155
- }
1156
- }
1157
- const willChangeRe = /transform|translate|scale|rotate|perspective|filter/;
1158
- const containRe = /paint|layout|strict|content/;
1159
- const isNotNone = value => !!value && value !== 'none';
1160
- let isWebKitValue;
1161
- function isContainingBlock(elementOrCss) {
1162
- const css = isElement(elementOrCss) ? getComputedStyle(elementOrCss) : elementOrCss;
1163
-
1164
- // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
1165
- // https://drafts.csswg.org/css-transforms-2/#individual-transforms
1166
- return isNotNone(css.transform) || isNotNone(css.translate) || isNotNone(css.scale) || isNotNone(css.rotate) || isNotNone(css.perspective) || !isWebKit() && (isNotNone(css.backdropFilter) || isNotNone(css.filter)) || willChangeRe.test(css.willChange || '') || containRe.test(css.contain || '');
1167
- }
1168
- function getContainingBlock(element) {
1169
- let currentNode = getParentNode(element);
1170
- while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) {
1171
- if (isContainingBlock(currentNode)) {
1172
- return currentNode;
1173
- } else if (isTopLayer(currentNode)) {
1174
- return null;
1175
- }
1176
- currentNode = getParentNode(currentNode);
1177
- }
1178
- return null;
1179
- }
1180
- function isWebKit() {
1181
- if (isWebKitValue == null) {
1182
- isWebKitValue = typeof CSS !== 'undefined' && CSS.supports && CSS.supports('-webkit-backdrop-filter', 'none');
1183
- }
1184
- return isWebKitValue;
1185
- }
1186
- function isLastTraversableNode(node) {
1187
- return /^(html|body|#document)$/.test(getNodeName(node));
1188
- }
1189
- function getComputedStyle(element) {
1190
- return getWindow(element).getComputedStyle(element);
1191
- }
1192
- function getNodeScroll(element) {
1193
- if (isElement(element)) {
1194
- return {
1195
- scrollLeft: element.scrollLeft,
1196
- scrollTop: element.scrollTop
1197
- };
1198
- }
1199
- return {
1200
- scrollLeft: element.scrollX,
1201
- scrollTop: element.scrollY
1202
- };
1203
- }
1204
- function getParentNode(node) {
1205
- if (getNodeName(node) === 'html') {
1206
- return node;
1207
- }
1208
- const result =
1209
- // Step into the shadow DOM of the parent of a slotted node.
1210
- node.assignedSlot ||
1211
- // DOM Element detected.
1212
- node.parentNode ||
1213
- // ShadowRoot detected.
1214
- isShadowRoot(node) && node.host ||
1215
- // Fallback.
1216
- getDocumentElement(node);
1217
- return isShadowRoot(result) ? result.host : result;
1218
- }
1219
- function getNearestOverflowAncestor(node) {
1220
- const parentNode = getParentNode(node);
1221
- if (isLastTraversableNode(parentNode)) {
1222
- return (node.ownerDocument || node).body;
1223
- }
1224
- if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) {
1225
- return parentNode;
1226
- }
1227
- return getNearestOverflowAncestor(parentNode);
1228
- }
1229
- function getOverflowAncestors(node, list, traverseIframes) {
1230
- var _node$ownerDocument2;
1231
- if (list === void 0) {
1232
- list = [];
1233
- }
1234
- if (traverseIframes === void 0) {
1235
- traverseIframes = true;
1236
- }
1237
- const scrollableAncestor = getNearestOverflowAncestor(node);
1238
- const isBody = scrollableAncestor === ((_node$ownerDocument2 = node.ownerDocument) == null ? void 0 : _node$ownerDocument2.body);
1239
- const win = getWindow(scrollableAncestor);
1240
- if (isBody) {
1241
- const frameElement = getFrameElement(win);
1242
- return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : [], frameElement && traverseIframes ? getOverflowAncestors(frameElement) : []);
1243
- } else {
1244
- return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor, [], traverseIframes));
1245
- }
1246
- }
1247
- function getFrameElement(win) {
1248
- return win.parent && Object.getPrototypeOf(win.parent) ? win.frameElement : null;
1249
- }
1250
-
1251
1117
  /**
1252
1118
  * Combines multiple cleanup functions into a single cleanup function.
1253
1119
  */
@@ -1277,13 +1143,15 @@ function addEventListener(target, type, listener, options) {
1277
1143
  };
1278
1144
  }
1279
1145
 
1146
+ const EMPTY$2 = [];
1147
+
1280
1148
  /**
1281
1149
  * A React.useEffect equivalent that runs once, when the component is mounted.
1282
1150
  */
1283
1151
  function useOnMount(fn) {
1284
1152
  // TODO: uncomment once we enable eslint-plugin-react-compiler // eslint-disable-next-line react-compiler/react-compiler -- no need to put `fn` in the dependency array
1285
1153
  /* eslint-disable react-hooks/exhaustive-deps */
1286
- React.useEffect(fn, EMPTY_ARRAY);
1154
+ React.useEffect(fn, EMPTY$2);
1287
1155
  /* eslint-enable react-hooks/exhaustive-deps */
1288
1156
  }
1289
1157
 
@@ -1347,7 +1215,7 @@ class Scheduler {
1347
1215
  this.callbacksCount -= 1;
1348
1216
  }
1349
1217
  }
1350
- let scheduler = new Scheduler();
1218
+ const scheduler = new Scheduler();
1351
1219
  class AnimationFrame {
1352
1220
  static create() {
1353
1221
  return new AnimationFrame();
@@ -1787,138 +1655,6 @@ const FocusGuard = /*#__PURE__*/React.forwardRef(function FocusGuard(props, ref)
1787
1655
  });
1788
1656
  if (process.env.NODE_ENV !== "production") FocusGuard.displayName = "FocusGuard";
1789
1657
 
1790
- /**
1791
- * Custom positioning reference element.
1792
- * @see https://floating-ui.com/docs/virtual-elements
1793
- */
1794
-
1795
- const min = Math.min;
1796
- const max = Math.max;
1797
- const round = Math.round;
1798
- const floor = Math.floor;
1799
- const createCoords = v => ({
1800
- x: v,
1801
- y: v
1802
- });
1803
- const oppositeSideMap = {
1804
- left: 'right',
1805
- right: 'left',
1806
- bottom: 'top',
1807
- top: 'bottom'
1808
- };
1809
- function clamp(start, value, end) {
1810
- return max(start, min(value, end));
1811
- }
1812
- function evaluate(value, param) {
1813
- return typeof value === 'function' ? value(param) : value;
1814
- }
1815
- function getSide(placement) {
1816
- return placement.split('-')[0];
1817
- }
1818
- function getAlignment(placement) {
1819
- return placement.split('-')[1];
1820
- }
1821
- function getOppositeAxis(axis) {
1822
- return axis === 'x' ? 'y' : 'x';
1823
- }
1824
- function getAxisLength(axis) {
1825
- return axis === 'y' ? 'height' : 'width';
1826
- }
1827
- function getSideAxis(placement) {
1828
- const firstChar = placement[0];
1829
- return firstChar === 't' || firstChar === 'b' ? 'y' : 'x';
1830
- }
1831
- function getAlignmentAxis(placement) {
1832
- return getOppositeAxis(getSideAxis(placement));
1833
- }
1834
- function getAlignmentSides(placement, rects, rtl) {
1835
- if (rtl === void 0) {
1836
- rtl = false;
1837
- }
1838
- const alignment = getAlignment(placement);
1839
- const alignmentAxis = getAlignmentAxis(placement);
1840
- const length = getAxisLength(alignmentAxis);
1841
- let mainAlignmentSide = alignmentAxis === 'x' ? alignment === (rtl ? 'end' : 'start') ? 'right' : 'left' : alignment === 'start' ? 'bottom' : 'top';
1842
- if (rects.reference[length] > rects.floating[length]) {
1843
- mainAlignmentSide = getOppositePlacement(mainAlignmentSide);
1844
- }
1845
- return [mainAlignmentSide, getOppositePlacement(mainAlignmentSide)];
1846
- }
1847
- function getExpandedPlacements(placement) {
1848
- const oppositePlacement = getOppositePlacement(placement);
1849
- return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)];
1850
- }
1851
- function getOppositeAlignmentPlacement(placement) {
1852
- return placement.includes('start') ? placement.replace('start', 'end') : placement.replace('end', 'start');
1853
- }
1854
- const lrPlacement = ['left', 'right'];
1855
- const rlPlacement = ['right', 'left'];
1856
- const tbPlacement = ['top', 'bottom'];
1857
- const btPlacement = ['bottom', 'top'];
1858
- function getSideList(side, isStart, rtl) {
1859
- switch (side) {
1860
- case 'top':
1861
- case 'bottom':
1862
- if (rtl) return isStart ? rlPlacement : lrPlacement;
1863
- return isStart ? lrPlacement : rlPlacement;
1864
- case 'left':
1865
- case 'right':
1866
- return isStart ? tbPlacement : btPlacement;
1867
- default:
1868
- return [];
1869
- }
1870
- }
1871
- function getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) {
1872
- const alignment = getAlignment(placement);
1873
- let list = getSideList(getSide(placement), direction === 'start', rtl);
1874
- if (alignment) {
1875
- list = list.map(side => side + "-" + alignment);
1876
- if (flipAlignment) {
1877
- list = list.concat(list.map(getOppositeAlignmentPlacement));
1878
- }
1879
- }
1880
- return list;
1881
- }
1882
- function getOppositePlacement(placement) {
1883
- const side = getSide(placement);
1884
- return oppositeSideMap[side] + placement.slice(side.length);
1885
- }
1886
- function expandPaddingObject(padding) {
1887
- var _padding$top, _padding$right, _padding$bottom, _padding$left;
1888
- return {
1889
- top: (_padding$top = padding.top) != null ? _padding$top : 0,
1890
- right: (_padding$right = padding.right) != null ? _padding$right : 0,
1891
- bottom: (_padding$bottom = padding.bottom) != null ? _padding$bottom : 0,
1892
- left: (_padding$left = padding.left) != null ? _padding$left : 0
1893
- };
1894
- }
1895
- function getPaddingObject(padding) {
1896
- return typeof padding !== 'number' ? expandPaddingObject(padding) : {
1897
- top: padding,
1898
- right: padding,
1899
- bottom: padding,
1900
- left: padding
1901
- };
1902
- }
1903
- function rectToClientRect(rect) {
1904
- const {
1905
- x,
1906
- y,
1907
- width,
1908
- height
1909
- } = rect;
1910
- return {
1911
- width,
1912
- height,
1913
- top: y,
1914
- left: x,
1915
- right: x + width,
1916
- bottom: y + height,
1917
- x,
1918
- y
1919
- };
1920
- }
1921
-
1922
1658
  function isDifferentGridRow(index, cols, prevRow) {
1923
1659
  return Math.floor(index / cols) !== prevRow;
1924
1660
  }
@@ -2214,13 +1950,6 @@ function isListIndexDisabled(list, index, disabledIndices) {
2214
1950
  if (!isElementVisible(element)) {
2215
1951
  return true;
2216
1952
  }
2217
-
2218
- // A natively disabled element can never receive focus, so it must always be
2219
- // skipped, even when `disabledIndices` marks it as enabled. Only
2220
- // `aria-disabled` items can be focusable-while-disabled.
2221
- if (element.matches(':disabled')) {
2222
- return true;
2223
- }
2224
1953
  return !disabledIndices && (element.hasAttribute('disabled') || element.getAttribute('aria-disabled') === 'true');
2225
1954
  }
2226
1955
  function isHiddenByStyles(styles) {
@@ -2751,6 +2480,11 @@ function useFloatingPortalNode(props = {}) {
2751
2480
  }
2752
2481
  return;
2753
2482
  }
2483
+
2484
+ // React 17 does not use React.useId().
2485
+ if (uniqueId == null) {
2486
+ return;
2487
+ }
2754
2488
  const resolvedContainer = (containerProp && (isNode(containerProp) ? containerProp : containerProp.current)) ?? parentPortalNode ?? document.body;
2755
2489
  if (resolvedContainer == null) {
2756
2490
  if (containerRef.current) {
@@ -2765,7 +2499,7 @@ function useFloatingPortalNode(props = {}) {
2765
2499
  setPortalNode(null);
2766
2500
  setContainerElement(resolvedContainer);
2767
2501
  }
2768
- }, [containerProp, parentPortalNode]);
2502
+ }, [containerProp, parentPortalNode, uniqueId]);
2769
2503
  const portalElement = useRenderElement('div', componentProps, {
2770
2504
  ref: [ref, setPortalNodeRef],
2771
2505
  props: [{
@@ -2778,11 +2512,8 @@ function useFloatingPortalNode(props = {}) {
2778
2512
  // Another call inside `FloatingPortal`/`FloatingPortalLite` then injects the children into `portalElement`.
2779
2513
  const portalSubtree = containerElement && portalElement ? /*#__PURE__*/ReactDOM.createPortal(portalElement, containerElement) : null;
2780
2514
  return {
2781
- node: portalNode,
2782
- // `id` and `render` props can override or remove the generated ID. Use the exact
2783
- // rendered value so `aria-owns` never points at an ID absent from the DOM.
2784
- nodeId: /*#__PURE__*/React.isValidElement(portalElement) ? portalElement.props.id : undefined,
2785
- subtree: portalSubtree
2515
+ portalNode,
2516
+ portalSubtree
2786
2517
  };
2787
2518
  }
2788
2519
 
@@ -2802,12 +2533,12 @@ const FloatingPortal = /*#__PURE__*/React.forwardRef(function FloatingPortal(com
2802
2533
  style,
2803
2534
  children,
2804
2535
  container,
2536
+ renderGuards,
2805
2537
  ...elementProps
2806
2538
  } = componentProps;
2807
2539
  const {
2808
- node: portalNode,
2809
- nodeId: portalNodeId,
2810
- subtree: portalSubtree
2540
+ portalNode,
2541
+ portalSubtree
2811
2542
  } = useFloatingPortalNode({
2812
2543
  container,
2813
2544
  ref: forwardedRef,
@@ -2822,7 +2553,7 @@ const FloatingPortal = /*#__PURE__*/React.forwardRef(function FloatingPortal(com
2822
2553
  const focusInsideDisabledRef = React.useRef(false);
2823
2554
  const modal = focusManagerState?.modal;
2824
2555
  const open = focusManagerState?.open;
2825
- const shouldRenderGuards = !!focusManagerState && !focusManagerState.modal && focusManagerState.open && !!portalNode;
2556
+ const shouldRenderGuards = typeof renderGuards === 'boolean' ? renderGuards : !!focusManagerState && !focusManagerState.modal && focusManagerState.open && !!portalNode;
2826
2557
 
2827
2558
  // https://codesandbox.io/s/tabbable-portal-f4tng?file=/src/TabbablePortal.tsx
2828
2559
  React.useEffect(() => {
@@ -2884,7 +2615,7 @@ const FloatingPortal = /*#__PURE__*/React.forwardRef(function FloatingPortal(com
2884
2615
  }
2885
2616
  }
2886
2617
  }), shouldRenderGuards && portalNode && /*#__PURE__*/jsx("span", {
2887
- "aria-owns": portalNodeId,
2618
+ "aria-owns": portalNode.id,
2888
2619
  style: ownerVisuallyHidden
2889
2620
  }), portalNode && /*#__PURE__*/ReactDOM.createPortal(children, portalNode), shouldRenderGuards && portalNode && /*#__PURE__*/jsx(FocusGuard, {
2890
2621
  "data-type": "outside",
@@ -3148,10 +2879,11 @@ function FloatingFocusManager(props) {
3148
2879
  } = store.context;
3149
2880
  const getNodeId = useStableCallback(() => dataRef.current.floatingContext?.nodeId);
3150
2881
  const ignoreInitialFocus = initialFocus === false;
3151
- // A typeable combobox reference (e.g. input/textarea) with `initialFocus={false}`
3152
- // has different focus semantics: focus is not trapped inside the floating element,
3153
- // so in the modal case the guards are not rendered, but `aria-hidden` is still
3154
- // applied to the outside nodes.
2882
+ // If the reference is a combobox and is typeable (e.g. input/textarea),
2883
+ // there are different focus semantics. The guards should not be rendered, but
2884
+ // aria-hidden should be applied to all nodes still. Further, the visually
2885
+ // hidden dismiss button should only appear at the end of the list, not the
2886
+ // start.
3155
2887
  const isUntrappedTypeableCombobox = isTypeableCombobox(domReference) && ignoreInitialFocus;
3156
2888
  const initialFocusRef = useValueAsRef(initialFocus);
3157
2889
  const returnFocusRef = useValueAsRef(returnFocus);
@@ -3265,12 +2997,12 @@ function FloatingFocusManager(props) {
3265
2997
  const triggers = store.context.triggerElements;
3266
2998
  const insideElements = getResolvedInsideElements();
3267
2999
  const isRelatedFocusGuard = relatedTarget?.hasAttribute(createAttribute('focus-guard')) && [beforeGuardRef.current, afterGuardRef.current, portalContext?.beforeInsideRef.current, portalContext?.afterInsideRef.current, portalContext?.beforeOutsideRef.current, portalContext?.afterOutsideRef.current, resolveRef(previousFocusableElement), resolveRef(nextFocusableElement)].includes(relatedTarget);
3268
- const movedToUnrelatedNode = !(contains(domReference, relatedTarget) || contains(floating, relatedTarget) || contains(relatedTarget, floating) || contains(portalContext?.portalNode, relatedTarget) || insideElements.some(element => element === relatedTarget || contains(element, relatedTarget)) || triggers.hasMatchingElement(trigger => contains(trigger, relatedTarget)) || isRelatedFocusGuard || tree && (getNodeChildren(tree.nodesRef.current, nodeId).find(node => contains(node.context?.elements.floating, relatedTarget) || contains(node.context?.elements.domReference, relatedTarget)) || getNodeAncestors(tree.nodesRef.current, nodeId).find(node => [node.context?.elements.floating, getFloatingFocusElement(node.context?.elements.floating)].includes(relatedTarget) || node.context?.elements.domReference === relatedTarget)));
3000
+ const movedToUnrelatedNode = !(contains(domReference, relatedTarget) || contains(floating, relatedTarget) || contains(relatedTarget, floating) || contains(portalContext?.portalNode, relatedTarget) || insideElements.some(element => element === relatedTarget || contains(element, relatedTarget)) || relatedTarget != null && triggers.hasElement(relatedTarget) || triggers.hasMatchingElement(trigger => contains(trigger, relatedTarget)) || isRelatedFocusGuard || tree && (getNodeChildren(tree.nodesRef.current, nodeId).find(node => contains(node.context?.elements.floating, relatedTarget) || contains(node.context?.elements.domReference, relatedTarget)) || getNodeAncestors(tree.nodesRef.current, nodeId).find(node => [node.context?.elements.floating, getFloatingFocusElement(node.context?.elements.floating)].includes(relatedTarget) || node.context?.elements.domReference === relatedTarget)));
3269
3001
  if (currentTarget === domReference && floatingFocusElement) {
3270
3002
  handleTabIndex(floatingFocusElement);
3271
3003
  }
3272
3004
 
3273
- // Restore focus to the previously focused tabbable element to prevent
3005
+ // Restore focus to the previous tabbable element index to prevent
3274
3006
  // focus from being lost outside the floating tree.
3275
3007
  if (restoreFocus && currentTarget !== domReference && !isElementVisible(target) && activeElement(doc) === doc.body) {
3276
3008
  // Let `FloatingPortal` effect knows that focus is still inside the
@@ -3280,10 +3012,10 @@ function FloatingFocusManager(props) {
3280
3012
  // If explicitly requested to restore focus to the popup container, do not search
3281
3013
  // for the next/previous tabbable element.
3282
3014
  if (restoreFocus === 'popup') {
3283
- // If the focused element is removed on pointerdown, the browser
3284
- // tries to move focus to it right after the `.focus()` call above,
3285
- // but because it's removed in the same tick, focus is lost instead.
3286
- // Re-focusing asynchronously (next frame) wins that race.
3015
+ // If the element is removed on pointerdown, focus tries to move it,
3016
+ // but since it's removed at the same time, focus gets lost as it
3017
+ // happens after the .focus() call above.
3018
+ // In this case, focus needs to be moved asynchronously.
3287
3019
  restoreFocusFrame.request(() => {
3288
3020
  floatingFocusElement.focus();
3289
3021
  });
@@ -3365,8 +3097,6 @@ function FloatingFocusManager(props) {
3365
3097
  if (!open || disabled || !isHTMLElement(floatingFocusElement)) {
3366
3098
  return;
3367
3099
  }
3368
- closeTypeRef.current = '';
3369
- lastInteractionTypeRef.current = '';
3370
3100
  const doc = ownerDocument(floatingFocusElement);
3371
3101
  const previouslyFocusedElement = activeElement(doc);
3372
3102
 
@@ -3398,9 +3128,7 @@ function FloatingFocusManager(props) {
3398
3128
  }
3399
3129
  elToFocus = elToFocus || getDefaultFocusElement();
3400
3130
  const hadFocusInside = contains(floatingFocusElement, activeElement(doc));
3401
-
3402
- // enqueueFocus returns a rAF-cancel function; we intentionally don't cancel this focus.
3403
- void enqueueFocus(elToFocus, {
3131
+ enqueueFocus(elToFocus, {
3404
3132
  preventScroll: elToFocus === floatingFocusElement,
3405
3133
  shouldFocus() {
3406
3134
  // This focus is queued on the next animation frame. If the floating element has closed
@@ -3427,11 +3155,13 @@ function FloatingFocusManager(props) {
3427
3155
  }
3428
3156
  const doc = ownerDocument(floatingFocusElement);
3429
3157
  const elementFocusedBeforeOpen = activeElement(doc);
3430
- // Only an explicit `null` interaction type represents a programmatic open.
3431
- // `undefined` is normalized to `''` by the prop default, so it never reaches
3432
- // here as nullish and is intentionally not treated as programmatic.
3158
+ // Only nullish interaction types represent programmatic opens. The empty
3159
+ // string default is intentionally not treated as programmatic.
3433
3160
  const preferPreviousFocus = openInteractionTypeRef.current == null;
3434
3161
  addPreviouslyFocusedElement(elementFocusedBeforeOpen);
3162
+
3163
+ // Dismissing via outside press should always ignore `returnFocus` to
3164
+ // prevent unwanted scrolling.
3435
3165
  function onOpenChangeLocal(details) {
3436
3166
  if (!details.open) {
3437
3167
  closeTypeRef.current = getEventType(details.nativeEvent, lastInteractionTypeRef.current);
@@ -3447,11 +3177,6 @@ function FloatingFocusManager(props) {
3447
3177
  } else if (isVirtualClick(details.nativeEvent) || isVirtualPointerEvent(details.nativeEvent)) {
3448
3178
  preventReturnFocusRef.current = false;
3449
3179
  } else {
3450
- // On outside press, only return focus to the reference when the browser supports the
3451
- // `focus({ preventScroll })` option; without it, restoring focus scrolls the page.
3452
- // Chrome on Android and Samsung Internet still don't support `preventScroll`
3453
- // (https://issues.chromium.org/issues/41453122), so the runtime check keeps return
3454
- // focus disabled there to avoid the scroll jump.
3455
3180
  let isPreventScrollSupported = false;
3456
3181
  ownerDocument(floatingFocusElement).createElement('div').focus({
3457
3182
  get preventScroll() {
@@ -3467,9 +3192,9 @@ function FloatingFocusManager(props) {
3467
3192
  }
3468
3193
  }
3469
3194
  events.on('openchange', onOpenChangeLocal);
3470
- function getReturnElement(closeType) {
3195
+ function getReturnElement() {
3471
3196
  const returnFocusValueOrFn = returnFocusRef.current;
3472
- let resolvedReturnFocusValue = typeof returnFocusValueOrFn === 'function' ? returnFocusValueOrFn(closeType) : returnFocusValueOrFn;
3197
+ let resolvedReturnFocusValue = typeof returnFocusValueOrFn === 'function' ? returnFocusValueOrFn(closeTypeRef.current) : returnFocusValueOrFn;
3473
3198
 
3474
3199
  // `null` should fallback to default behavior in case of an empty ref.
3475
3200
  if (resolvedReturnFocusValue === undefined || resolvedReturnFocusValue === false) {
@@ -3497,11 +3222,9 @@ function FloatingFocusManager(props) {
3497
3222
 
3498
3223
  // eslint-disable-next-line react-hooks/exhaustive-deps
3499
3224
  const returnFocusValueOrFn = returnFocusRef.current;
3500
- const closeType = closeTypeRef.current;
3501
- const returnElement = getReturnElement(closeType);
3225
+ const returnElement = getReturnElement();
3502
3226
  queueMicrotask(() => {
3503
- // `returnElement` if it is tabbable, otherwise its first tabbable child,
3504
- // otherwise `returnElement` itself (which may not be tabbable at all).
3227
+ // This is `returnElement`, if it's tabbable, or its first tabbable child.
3505
3228
  const tabbableReturnElement = getFirstTabbableElement(returnElement);
3506
3229
  const hasExplicitReturnFocus = typeof returnFocusValueOrFn !== 'boolean';
3507
3230
  if (returnFocusValueOrFn && !preventReturnFocusRef.current && isHTMLElement(tabbableReturnElement) && (
@@ -3509,13 +3232,9 @@ function FloatingFocusManager(props) {
3509
3232
  // since it likely entered a different element which should be
3510
3233
  // respected: https://github.com/floating-ui/floating-ui/issues/2607
3511
3234
  !hasExplicitReturnFocus && tabbableReturnElement !== activeEl && activeEl !== doc.body ? isFocusInsideFloatingTree : true)) {
3512
- const focusOptions = {
3235
+ tabbableReturnElement.focus({
3513
3236
  preventScroll: true
3514
- };
3515
- if (closeType === 'keyboard') {
3516
- focusOptions.focusVisible = true;
3517
- }
3518
- tabbableReturnElement.focus(focusOptions);
3237
+ });
3519
3238
  }
3520
3239
  preventReturnFocusRef.current = false;
3521
3240
  });
@@ -3538,8 +3257,8 @@ function FloatingFocusManager(props) {
3538
3257
  }
3539
3258
  }, [open, floating]);
3540
3259
 
3541
- // Synchronize the focus manager state (modal, closeOnFocusOut, open, etc.) to the
3542
- // FloatingPortal context, which uses it to decide whether to render its own guards.
3260
+ // Synchronize the `context` & `modal` value to the FloatingPortal context.
3261
+ // It will decide whether or not it needs to render its own guards.
3543
3262
  useIsoLayoutEffect(() => {
3544
3263
  if (disabled || !portalContext) {
3545
3264
  return undefined;
@@ -3574,8 +3293,7 @@ function FloatingFocusManager(props) {
3574
3293
  onFocus: event => {
3575
3294
  if (modal) {
3576
3295
  const els = getTabbableContent();
3577
- // enqueueFocus returns a rAF-cancel function we don't need here.
3578
- void enqueueFocus(els[els.length - 1]);
3296
+ enqueueFocus(els[els.length - 1]);
3579
3297
  } else if (portalContext?.portalNode) {
3580
3298
  preventReturnFocusRef.current = false;
3581
3299
  if (isOutsideEvent(event, portalContext.portalNode)) {
@@ -3591,8 +3309,7 @@ function FloatingFocusManager(props) {
3591
3309
  ref: mergedAfterGuardRef,
3592
3310
  onFocus: event => {
3593
3311
  if (modal) {
3594
- // enqueueFocus returns a rAF-cancel function we don't need here.
3595
- void enqueueFocus(getTabbableContent()[0]);
3312
+ enqueueFocus(getTabbableContent()[0]);
3596
3313
  } else if (portalContext?.portalNode) {
3597
3314
  if (closeOnFocusOut) {
3598
3315
  preventReturnFocusRef.current = true;
@@ -3664,11 +3381,7 @@ function useClick(context, props = {}) {
3664
3381
  }
3665
3382
  return {
3666
3383
  onPointerDown(event) {
3667
- // Screen reader activations (Android TalkBack, desktop screen readers) report a
3668
- // mouse-like `pointerType`, but `ignoreMouse` must not drop them: hover logic cannot
3669
- // open for a virtual press since there is no real pointer movement to wait for.
3670
- // Virtual `touch` presses (iOS VoiceOver) keep their type so `touchOpenDelay` applies.
3671
- pointerTypeRef.current = isMouseLikePointerType(event.pointerType, true) && isVirtualPointerEvent(event.nativeEvent) ? 'virtual' : event.pointerType;
3384
+ pointerTypeRef.current = event.pointerType;
3672
3385
  },
3673
3386
  onMouseDown(event) {
3674
3387
  const pointerType = pointerTypeRef.current;
@@ -3848,7 +3561,7 @@ function useDismiss(context, props = {}) {
3848
3561
  });
3849
3562
  React.useEffect(() => {
3850
3563
  if (!open || !enabled) {
3851
- return clearInsideReactTree;
3564
+ return undefined;
3852
3565
  }
3853
3566
  dataRef.current.__escapeKeyBubbles = escapeKeyBubbles;
3854
3567
  dataRef.current.__outsidePressBubbles = outsidePressBubbles;
@@ -4126,9 +3839,9 @@ function useDismiss(context, props = {}) {
4126
3839
  preventedPressSuppressionTimeout.clear();
4127
3840
  resetPressStartState();
4128
3841
  suppressNextOutsideClickRef.current = false;
4129
- clearInsideReactTree();
4130
3842
  };
4131
3843
  }, [dataRef, floatingElement, escapeKey$1, outsidePressEnabled, outsidePress$1, open, enabled, escapeKeyBubbles, outsidePressBubbles, closeOnEscapeKeyDown, clearInsideReactTree, getOutsidePressEventProp, hasBlockingChild, isEventWithinOwnElements, tree, store, cancelDismissOnEndTimeout]);
3844
+ React.useEffect(clearInsideReactTree, [outsidePress$1, clearInsideReactTree]);
4132
3845
  const reference = React.useMemo(() => ({
4133
3846
  onKeyDown: closeOnEscapeKeyDown,
4134
3847
  onPointerDown: closeOnReferencePress,
@@ -4162,12 +3875,12 @@ function useDismiss(context, props = {}) {
4162
3875
  }
4163
3876
 
4164
3877
  const selectors = {
4165
- open: state => state.open,
4166
- transitionStatus: state => state.transitionStatus,
4167
- domReferenceElement: state => state.domReferenceElement,
4168
- referenceElement: state => state.positionReference ?? state.referenceElement,
4169
- floatingElement: state => state.floatingElement,
4170
- floatingId: state => state.floatingId
3878
+ open: createSelector(state => state.open),
3879
+ transitionStatus: createSelector(state => state.transitionStatus),
3880
+ domReferenceElement: createSelector(state => state.domReferenceElement),
3881
+ referenceElement: createSelector(state => state.positionReference ?? state.referenceElement),
3882
+ floatingElement: createSelector(state => state.floatingElement),
3883
+ floatingId: createSelector(state => state.floatingId)
4171
3884
  };
4172
3885
  class FloatingRootStore extends ReactStore {
4173
3886
  constructor(options) {
@@ -4356,14 +4069,44 @@ function useTransitionStatus(open, enableIdleState = false, deferEndingState = f
4356
4069
  };
4357
4070
  }
4358
4071
 
4072
+ let TransitionStatusDataAttributes = /*#__PURE__*/function (TransitionStatusDataAttributes) {
4073
+ /**
4074
+ * Present when the component is animating in.
4075
+ */
4076
+ TransitionStatusDataAttributes["startingStyle"] = "data-starting-style";
4077
+ /**
4078
+ * Present when the component is animating out.
4079
+ */
4080
+ TransitionStatusDataAttributes["endingStyle"] = "data-ending-style";
4081
+ return TransitionStatusDataAttributes;
4082
+ }({});
4083
+ const STARTING_HOOK = {
4084
+ [TransitionStatusDataAttributes.startingStyle]: ''
4085
+ };
4086
+ const ENDING_HOOK = {
4087
+ [TransitionStatusDataAttributes.endingStyle]: ''
4088
+ };
4089
+ const transitionStatusMapping = {
4090
+ transitionStatus(value) {
4091
+ if (value === 'starting') {
4092
+ return STARTING_HOOK;
4093
+ }
4094
+ if (value === 'ending') {
4095
+ return ENDING_HOOK;
4096
+ }
4097
+ return null;
4098
+ }
4099
+ };
4100
+
4359
4101
  /**
4360
4102
  * Executes a function once all animations have finished on the provided element.
4361
- * If an animation is canceled, waits for any replacement animations before executing.
4362
4103
  * @param elementOrRef - The element to watch for animations.
4363
4104
  * @param waitForStartingStyleRemoved - Whether to wait for [data-starting-style] to be removed before checking for animations.
4105
+ * @param treatAbortedAsFinished - Whether to treat aborted animations as finished. If `false`, and there are aborted animations,
4106
+ * the function will check again if any new animations have started and wait for them to finish.
4364
4107
  * @returns A function that takes a callback to execute once all animations have finished, and an optional AbortSignal to abort the callback
4365
4108
  */
4366
- function useAnimationsFinished(elementOrRef, waitForStartingStyleRemoved = false) {
4109
+ function useAnimationsFinished(elementOrRef, waitForStartingStyleRemoved = false, treatAbortedAsFinished = true) {
4367
4110
  const frame = useAnimationFrame();
4368
4111
  return useStableCallback((fnToExecute,
4369
4112
  /**
@@ -4392,22 +4135,23 @@ function useAnimationsFinished(elementOrRef, waitForStartingStyleRemoved = false
4392
4135
  if (!signal?.aborted) {
4393
4136
  done();
4394
4137
  }
4395
- }, () => {
4396
- if (signal?.aborted) {
4138
+ }).catch(() => {
4139
+ if (treatAbortedAsFinished) {
4140
+ if (!signal?.aborted) {
4141
+ done();
4142
+ }
4397
4143
  return;
4398
4144
  }
4399
4145
  const currentAnimations = resolvedElement.getAnimations();
4400
- if (currentAnimations.some(animation => animation.pending || animation.playState !== 'finished')) {
4146
+ if (!signal?.aborted && currentAnimations.length > 0 && currentAnimations.some(animation => animation.pending || animation.playState !== 'finished')) {
4401
4147
  // Sometimes animations can be aborted because a property they depend on changes while the animation plays.
4402
4148
  // In such cases, we need to re-check if any new animations have started.
4403
4149
  exec();
4404
- return;
4405
4150
  }
4406
- done();
4407
4151
  });
4408
4152
  }
4409
4153
  if (waitForStartingStyleRemoved) {
4410
- const startingStyleAttribute = 'data-starting-style';
4154
+ const startingStyleAttribute = TransitionStatusDataAttributes.startingStyle;
4411
4155
 
4412
4156
  // If `[data-starting-style]` isn't present, fall back to waiting one more frame
4413
4157
  // to give "open" animations a chance to be registered.
@@ -4447,7 +4191,7 @@ function useOpenChangeComplete(parameters) {
4447
4191
  onComplete: onCompleteParam
4448
4192
  } = parameters;
4449
4193
  const onComplete = useStableCallback(onCompleteParam);
4450
- const runOnceAnimationsFinish = useAnimationsFinished(ref, open);
4194
+ const runOnceAnimationsFinish = useAnimationsFinished(ref, open, false);
4451
4195
  React.useEffect(() => {
4452
4196
  if (!enabled) {
4453
4197
  return undefined;
@@ -4473,26 +4217,14 @@ const FOCUSABLE_POPUP_PROPS = {
4473
4217
  function createDefaultInitialFocus(popupRef) {
4474
4218
  return interactionType => interactionType === 'touch' ? popupRef.current : true;
4475
4219
  }
4476
-
4477
- /**
4478
- * The subset of a popup handle that a Root needs to bind its store to. Both the real handle classes
4479
- * and any test double satisfy it.
4480
- */
4481
-
4482
- /**
4483
- * Creates and owns a popup store on behalf of a Root part. The store is created exactly once, with
4484
- * controlled props and root state synced separately after creation. Sets up the synced floating
4485
- * root context and returns the store.
4486
- *
4487
- * @param createStore Factory that builds the store. Called exactly once, receiving the floating id
4488
- * and whether the popup is nested inside another floating element, both resolved on the first render.
4489
- * @param treatPopupAsFloatingElement Whether the popup element is passed to Floating UI as the
4490
- * floating element instead of the default positioner.
4491
- */
4492
- function usePopupRootStore(createStore, treatPopupAsFloatingElement = false) {
4220
+ function usePopupStore(externalStore, createStore, treatPopupAsFloatingElement = false) {
4493
4221
  const floatingId = useId();
4494
4222
  const nested = useFloatingParentNodeId() != null;
4495
- const store = useRefWithInit(() => createStore(floatingId, nested)).current;
4223
+ const internalStoreRef = React.useRef(null);
4224
+ if (externalStore === undefined && internalStoreRef.current === null) {
4225
+ internalStoreRef.current = createStore(floatingId, nested);
4226
+ }
4227
+ const store = externalStore ?? internalStoreRef.current;
4496
4228
  useSyncedFloatingRootContext({
4497
4229
  popupStore: store,
4498
4230
  treatPopupAsFloatingElement,
@@ -4501,27 +4233,10 @@ function usePopupRootStore(createStore, treatPopupAsFloatingElement = false) {
4501
4233
  nested,
4502
4234
  onOpenChange: store.setOpen
4503
4235
  });
4504
- return store;
4505
- }
4506
-
4507
- /**
4508
- * Attaches a Root's store to a handle for this component's committed lifetime. Popup Roots render
4509
- * it before their interactions and user children so its layout effect runs before descendant layout
4510
- * effects. This lets descendants call the handle during the Root's initial commit without attaching
4511
- * during render, which would leak suspended or abandoned stores. Store subscribers are notified by
4512
- * `attachStore` in this ordinary layout phase, where React permits synchronous updates.
4513
- *
4514
- * Popup Roots must render this component only when a handle is present so handle-less Roots avoid
4515
- * mounting an extra fiber and layout effect.
4516
- */
4517
- function PopupHandleAttachment({
4518
- handle,
4519
- store
4520
- }) {
4521
- useIsoLayoutEffect(() => {
4522
- return handle.attachStore(store);
4523
- }, [handle, store]);
4524
- return null;
4236
+ return {
4237
+ store,
4238
+ internalStore: internalStoreRef.current
4239
+ };
4525
4240
  }
4526
4241
 
4527
4242
  /**
@@ -4586,6 +4301,19 @@ function attachPreventUnmountOnClose(eventDetails) {
4586
4301
  };
4587
4302
  return () => preventUnmountOnClose;
4588
4303
  }
4304
+ function useInitialOpenSync(store, openProp, defaultOpen, defaultTriggerId) {
4305
+ useOnFirstRender(() => {
4306
+ if (openProp === undefined && store.state.open === false && defaultOpen) {
4307
+ // Avoid notifying detached trigger subscribers while the Root is rendering.
4308
+ store.state = {
4309
+ ...store.state,
4310
+ open: true,
4311
+ activeTriggerId: defaultTriggerId,
4312
+ preventUnmountingOnClose: false
4313
+ };
4314
+ }
4315
+ });
4316
+ }
4589
4317
 
4590
4318
  /**
4591
4319
  * Sets up trigger data forwarding to the store.
@@ -4598,11 +4326,11 @@ function attachPreventUnmountOnClose(eventDetails) {
4598
4326
  function useTriggerDataForwarding(triggerId, triggerElementRef, store, stateUpdates) {
4599
4327
  const isMountedByThisTrigger = store.useState('isMountedByTrigger', triggerId);
4600
4328
  const baseRegisterTrigger = useTriggerRegistration(triggerId, store);
4601
-
4602
- // Applies trigger-owned state (active-trigger ownership and payload) when the trigger registers.
4603
- // Stable so payload/`stateUpdates` changes do not change the ref identity (which would needlessly
4604
- // churn registration); it reads the latest closure values when invoked.
4605
- const applyTriggerData = useStableCallback(element => {
4329
+ const registerTrigger = useStableCallback(element => {
4330
+ baseRegisterTrigger(element);
4331
+ if (!element) {
4332
+ return;
4333
+ }
4606
4334
  const open = store.select('open');
4607
4335
  const activeTriggerId = store.select('activeTriggerId');
4608
4336
  if (activeTriggerId === triggerId) {
@@ -4623,18 +4351,6 @@ function useTriggerDataForwarding(triggerId, triggerElementRef, store, stateUpda
4623
4351
  });
4624
4352
  }
4625
4353
  });
4626
-
4627
- // Intentionally NOT stable. Its identity is derived from `baseRegisterTrigger`, which is keyed on
4628
- // `[store, id]`, so when a handle-backed trigger's store pointer swaps the merged ref re-fires —
4629
- // unregistering from the previous store and registering into the new one. This lets a detached
4630
- // trigger follow its handle's currently-attached store across attach/detach/remount. (A stable
4631
- // callback would keep its identity and never re-fire on a store swap.)
4632
- const registerTrigger = React.useCallback(element => {
4633
- baseRegisterTrigger(element);
4634
- if (element) {
4635
- applyTriggerData(element);
4636
- }
4637
- }, [baseRegisterTrigger, applyTriggerData]);
4638
4354
  useIsoLayoutEffect(() => {
4639
4355
  if (isMountedByThisTrigger) {
4640
4356
  store.update({
@@ -4654,17 +4370,12 @@ function useTriggerDataForwarding(triggerId, triggerElementRef, store, stateUpda
4654
4370
  *
4655
4371
  * When a popup opens without an explicit trigger id and exactly one trigger is registered, that
4656
4372
  * trigger is claimed as the active trigger. When the active trigger id is still registered but its
4657
- * element changed, the active element is refreshed. When the active trigger id is missing from the
4658
- * registry but the same element is still registered under a different id (e.g. the rendered trigger
4659
- * carries its own DOM `id` that differs from Base UI's internal trigger id), the active id is
4660
- * reassociated to the registered id instead of being treated as lost. When the active trigger
4661
- * unregisters, the default path preserves existing ownership so non-closing popup families do not
4662
- * silently claim a different trigger while staying open.
4373
+ * element changed, the active element is refreshed. When the active trigger unregisters, the
4374
+ * default path preserves existing ownership so non-closing popup families do not silently claim a
4375
+ * different trigger while staying open.
4663
4376
  *
4664
- * If `closeOnActiveTriggerUnmount` is enabled, unregistering a previously resolved active trigger
4665
- * requests a close after a microtask so a same-tick replacement trigger with the same id can
4666
- * register first. An active trigger id that has not matched a registered trigger yet is treated as
4667
- * pending and does not request a close.
4377
+ * If `closeOnActiveTriggerUnmount` is enabled, unregistering the active trigger requests a close
4378
+ * after a microtask so a same-tick replacement trigger with the same id can register first.
4668
4379
  *
4669
4380
  * This should be called on the Root part.
4670
4381
  *
@@ -4675,22 +4386,10 @@ function useImplicitActiveTrigger(store, options = {}) {
4675
4386
  const {
4676
4387
  closeOnActiveTriggerUnmount = false
4677
4388
  } = options;
4678
- // Distinguishes a trigger that unmounted from a new active trigger that has not hydrated yet.
4679
- const resolvedActiveTriggerIdRef = React.useRef(null);
4680
4389
  const open = store.useState('open');
4681
4390
  const reactiveTriggerCount = store.useState('triggerCount');
4682
- // Subscribe to the active trigger id so the reconciliation below reruns when ownership moves to
4683
- // another trigger while the popup stays open (e.g. a focus/hover handoff between triggers).
4684
- const activeTriggerId = store.useState('activeTriggerId');
4685
- // Subscribe to the active trigger element so the reconciliation reruns when a pending active
4686
- // trigger registers in a commit where the trigger count nets out unchanged (registration
4687
- // forwards the element to the store when the registering trigger matches the active id).
4688
- // Without this, the id would never be marked resolved and a later genuine unmount would be
4689
- // misclassified as pending, disabling `closeOnActiveTriggerUnmount`.
4690
- const reactiveActiveTriggerElement = store.useState('activeTriggerElement');
4691
4391
  useIsoLayoutEffect(() => {
4692
4392
  if (!open) {
4693
- resolvedActiveTriggerIdRef.current = null;
4694
4393
  if (store.state.triggerCount !== 0) {
4695
4394
  store.set('triggerCount', 0);
4696
4395
  }
@@ -4701,42 +4400,22 @@ function useImplicitActiveTrigger(store, options = {}) {
4701
4400
  if (store.state.triggerCount !== triggerCount) {
4702
4401
  stateUpdates.triggerCount = triggerCount;
4703
4402
  }
4704
- const currentActiveTriggerId = store.select('activeTriggerId');
4403
+ const activeTriggerId = store.select('activeTriggerId');
4705
4404
  let lostActiveTriggerId = null;
4706
- if (currentActiveTriggerId) {
4707
- const activeTriggerElement = store.context.triggerElements.getById(currentActiveTriggerId);
4405
+ if (activeTriggerId) {
4406
+ const activeTriggerElement = store.context.triggerElements.getById(activeTriggerId);
4708
4407
  if (!activeTriggerElement) {
4709
- for (const [triggerId, triggerElement] of store.context.triggerElements.entries()) {
4710
- if (triggerElement === store.state.activeTriggerElement) {
4711
- stateUpdates.activeTriggerId = triggerId;
4712
- stateUpdates.activeTriggerElement = triggerElement;
4713
- resolvedActiveTriggerIdRef.current = triggerId;
4714
- break;
4715
- }
4716
- }
4717
- if (stateUpdates.activeTriggerId === undefined) {
4718
- if (resolvedActiveTriggerIdRef.current === currentActiveTriggerId) {
4719
- lostActiveTriggerId = currentActiveTriggerId;
4720
- } else {
4721
- resolvedActiveTriggerIdRef.current = null;
4722
- }
4723
- }
4724
- } else {
4725
- resolvedActiveTriggerIdRef.current = currentActiveTriggerId;
4726
- if (activeTriggerElement !== store.state.activeTriggerElement) {
4727
- stateUpdates.activeTriggerElement = activeTriggerElement;
4728
- }
4408
+ lostActiveTriggerId = activeTriggerId;
4409
+ } else if (activeTriggerElement !== store.state.activeTriggerElement) {
4410
+ stateUpdates.activeTriggerElement = activeTriggerElement;
4729
4411
  }
4730
- } else {
4731
- resolvedActiveTriggerIdRef.current = null;
4732
4412
  }
4733
- if (!lostActiveTriggerId && !currentActiveTriggerId && triggerCount === 1) {
4413
+ if (!lostActiveTriggerId && !activeTriggerId && triggerCount === 1) {
4734
4414
  const iteratorResult = store.context.triggerElements.entries().next();
4735
4415
  if (!iteratorResult.done) {
4736
4416
  const [implicitTriggerId, implicitTriggerElement] = iteratorResult.value;
4737
4417
  stateUpdates.activeTriggerId = implicitTriggerId;
4738
4418
  stateUpdates.activeTriggerElement = implicitTriggerElement;
4739
- resolvedActiveTriggerIdRef.current = implicitTriggerId;
4740
4419
  }
4741
4420
  }
4742
4421
  if (stateUpdates.triggerCount !== undefined || stateUpdates.activeTriggerId !== undefined || stateUpdates.activeTriggerElement !== undefined) {
@@ -4761,7 +4440,7 @@ function useImplicitActiveTrigger(store, options = {}) {
4761
4440
  });
4762
4441
  }
4763
4442
  }
4764
- }, [open, store, reactiveTriggerCount, activeTriggerId, reactiveActiveTriggerElement, closeOnActiveTriggerUnmount]);
4443
+ }, [open, store, reactiveTriggerCount, closeOnActiveTriggerUnmount]);
4765
4444
  }
4766
4445
 
4767
4446
  /**
@@ -4839,33 +4518,13 @@ function usePopupRootSync(store, open) {
4839
4518
  }, [store]);
4840
4519
  }
4841
4520
 
4842
- /**
4843
- * Development-only reverse index of element to registered id, keyed by the owning map.
4844
- *
4845
- * Registration would otherwise have to scan every entry to detect an element claimed by two ids,
4846
- * making the mount of many triggers sharing one handle quadratic. Kept module-scoped, lazily
4847
- * initialized, and read only from `process.env.NODE_ENV` guards so production builds drop it along
4848
- * with the checks.
4849
- */
4850
- let devElementIdsByMap;
4851
- function getDevElementIds(map) {
4852
- devElementIdsByMap ??= new WeakMap();
4853
- let elementIds = devElementIdsByMap.get(map);
4854
- if (!elementIds) {
4855
- elementIds = new WeakMap();
4856
- devElementIdsByMap.set(map, elementIds);
4857
- }
4858
- return elementIds;
4859
- }
4860
-
4861
4521
  /**
4862
4522
  * Data structure to keep track of popup trigger elements by their IDs.
4863
- *
4864
- * Element lookups iterate the id map rather than maintaining a parallel Set. Registration is O(1),
4865
- * while `hasElement` and `hasMatchingElement` are linear in the number of triggers.
4523
+ * Uses both a set of Elements and a map of IDs to Elements for efficient lookups.
4866
4524
  */
4867
4525
  class PopupTriggerMap {
4868
4526
  constructor() {
4527
+ this.elementsSet = new Set();
4869
4528
  this.idMap = new Map();
4870
4529
  }
4871
4530
 
@@ -4875,56 +4534,49 @@ class PopupTriggerMap {
4875
4534
  * Note: The provided element is assumed to not be registered under multiple IDs.
4876
4535
  */
4877
4536
  add(id, element) {
4537
+ const existingElement = this.idMap.get(id);
4538
+ if (existingElement === element) {
4539
+ return;
4540
+ }
4541
+ if (existingElement !== undefined) {
4542
+ // We assume that the same element won't be registered under multiple ids.
4543
+ // This is safe considering how useTriggerRegistration is implemented.
4544
+ this.elementsSet.delete(existingElement);
4545
+ }
4546
+ this.elementsSet.add(element);
4547
+ this.idMap.set(id, element);
4878
4548
  if (process.env.NODE_ENV !== 'production') {
4879
- const elementIds = getDevElementIds(this);
4880
- const existingId = elementIds.get(element);
4881
- if (existingId !== undefined && existingId !== id) {
4549
+ if (this.elementsSet.size !== this.idMap.size) {
4882
4550
  // TODO: fix mui/no-guarded-throw
4883
4551
  // eslint-disable-next-line mui/no-guarded-throw
4884
4552
  throw new Error('Base UI: A trigger element cannot be registered under multiple IDs in PopupTriggerMap.');
4885
4553
  }
4886
-
4887
- // Reusing an id for a different element evicts the previous one, so it must lose its claim
4888
- // on the id or a later registration under a different id would be reported as a duplicate.
4889
- const previousElement = this.idMap.get(id);
4890
- if (previousElement !== undefined && previousElement !== element) {
4891
- elementIds.delete(previousElement);
4892
- }
4893
- elementIds.set(element, id);
4894
4554
  }
4895
- this.idMap.set(id, element);
4896
4555
  }
4897
4556
 
4898
4557
  /**
4899
4558
  * Removes the trigger element with the given ID.
4900
4559
  */
4901
4560
  delete(id) {
4902
- if (process.env.NODE_ENV !== 'production') {
4903
- const element = this.idMap.get(id);
4904
- if (element !== undefined) {
4905
- devElementIdsByMap?.get(this)?.delete(element);
4906
- }
4561
+ const element = this.idMap.get(id);
4562
+ if (element) {
4563
+ this.elementsSet.delete(element);
4564
+ this.idMap.delete(id);
4907
4565
  }
4908
- this.idMap.delete(id);
4909
4566
  }
4910
4567
 
4911
4568
  /**
4912
4569
  * Whether the given element is registered as a trigger.
4913
4570
  */
4914
4571
  hasElement(element) {
4915
- for (const registered of this.idMap.values()) {
4916
- if (registered === element) {
4917
- return true;
4918
- }
4919
- }
4920
- return false;
4572
+ return this.elementsSet.has(element);
4921
4573
  }
4922
4574
 
4923
4575
  /**
4924
4576
  * Whether there is a registered trigger element matching the given predicate.
4925
4577
  */
4926
4578
  hasMatchingElement(predicate) {
4927
- for (const element of this.idMap.values()) {
4579
+ for (const element of this.elementsSet) {
4928
4580
  if (predicate(element)) {
4929
4581
  return true;
4930
4582
  }
@@ -4950,7 +4602,7 @@ class PopupTriggerMap {
4950
4602
  * Returns an iterable of all registered trigger elements.
4951
4603
  */
4952
4604
  elements() {
4953
- return this.idMap.values();
4605
+ return this.elementsSet.values();
4954
4606
  }
4955
4607
 
4956
4608
  /**
@@ -5010,11 +4662,14 @@ function useValueChanged(value, onChange) {
5010
4662
  const valueRef = React.useRef(value);
5011
4663
  const onChangeCallback = useStableCallback(onChange);
5012
4664
  useIsoLayoutEffect(() => {
5013
- if (valueRef.current !== value) {
5014
- onChangeCallback(valueRef.current);
4665
+ if (valueRef.current === value) {
4666
+ return;
5015
4667
  }
5016
- valueRef.current = value;
4668
+ onChangeCallback(valueRef.current);
5017
4669
  }, [value, onChangeCallback]);
4670
+ useIsoLayoutEffect(() => {
4671
+ valueRef.current = value;
4672
+ }, [value]);
5018
4673
  }
5019
4674
 
5020
4675
  function useOpenMethodTriggerProps(open, setOpenMethod) {
@@ -5057,6 +4712,9 @@ function useOpenInteractionType(open) {
5057
4712
  }), [openMethod, triggerProps]);
5058
4713
  }
5059
4714
 
4715
+ /**
4716
+ * @internal
4717
+ */
5060
4718
  const DirectionContext = /*#__PURE__*/React.createContext(undefined);
5061
4719
  if (process.env.NODE_ENV !== "production") DirectionContext.displayName = "DirectionContext";
5062
4720
  function useDirection() {
@@ -5115,30 +4773,6 @@ function useFocusableWhenDisabled(parameters) {
5115
4773
  };
5116
4774
  }
5117
4775
 
5118
- /**
5119
- * Dispatches a constructed click on the target so it carries the source event's
5120
- * modifier state, which `click()` always reports as unpressed. Like `click()`,
5121
- * the untrusted click still runs native activation behavior (form submission,
5122
- * link navigation).
5123
- * `detail` defaults to 0 (the native convention for keyboard-generated clicks);
5124
- * pass `detail: 1` when the click represents a mouse gesture so consumers keying
5125
- * off `detail === 0` don't classify it as a keyboard activation.
5126
- */
5127
- function dispatchClickWithModifiers(target, sourceEvent, {
5128
- detail = 0
5129
- } = {}) {
5130
- target.dispatchEvent(new (getWindow(target).PointerEvent)('click', {
5131
- bubbles: true,
5132
- cancelable: true,
5133
- composed: true,
5134
- detail,
5135
- shiftKey: sourceEvent.shiftKey,
5136
- ctrlKey: sourceEvent.ctrlKey,
5137
- altKey: sourceEvent.altKey,
5138
- metaKey: sourceEvent.metaKey
5139
- }));
5140
- }
5141
-
5142
4776
  function useButton(parameters = {}) {
5143
4777
  const {
5144
4778
  disabled = false,
@@ -5239,34 +4873,24 @@ function useButton(parameters = {}) {
5239
4873
  return;
5240
4874
  }
5241
4875
  event.preventDefault();
5242
-
5243
- // Only a native-mode item that isn't a real <button> is excluded.
5244
- if (!isNativeButton || isButton) {
4876
+ if (isLink || isNativeButton && isButton) {
4877
+ currentTarget.click();
4878
+ event.preventBaseUIHandler();
4879
+ } else if (shouldClick) {
4880
+ externalOnClick?.(event);
5245
4881
  event.preventBaseUIHandler();
5246
- dispatchClickWithModifiers(currentTarget, event);
5247
4882
  }
5248
4883
  return;
5249
4884
  }
5250
4885
 
5251
4886
  // Keyboard accessibility for native and non-native elements.
5252
- if (!shouldClick || isNativeButton || !isSpaceKey && !isEnterKey) {
5253
- // Space activates links on keyup (`role="button"` semantics, matching the
5254
- // composite path); prevent the page scroll Space would otherwise trigger.
5255
- // Enter is left to the browser's native link activation.
5256
- if (isCurrentTarget && isLink && isSpaceKey) {
4887
+ if (shouldClick) {
4888
+ if (!isNativeButton && (isSpaceKey || isEnterKey)) {
5257
4889
  event.preventDefault();
5258
4890
  }
5259
- return;
5260
- }
5261
-
5262
- // Match native buttons: preventing the keydown's default cancels activation.
5263
- if (event.defaultPrevented) {
5264
- return;
5265
- }
5266
- event.preventDefault();
5267
- if (isEnterKey) {
5268
- event.preventBaseUIHandler();
5269
- dispatchClickWithModifiers(currentTarget, event);
4891
+ if (!isNativeButton && isEnterKey) {
4892
+ externalOnClick?.(event);
4893
+ }
5270
4894
  }
5271
4895
  },
5272
4896
  onKeyUp(event) {
@@ -5286,14 +4910,9 @@ function useButton(parameters = {}) {
5286
4910
  return;
5287
4911
  }
5288
4912
 
5289
- // Keyboard accessibility for non interactive elements.
5290
- // Match native buttons: preventing the keyup's default cancels Space activation.
5291
- // Limitation: unlike a native <button>, a prevented *keydown* cannot cancel the
5292
- // activation — no state is kept between keydown and keyup, so we can't tell
5293
- // whether the keydown was prevented or even happened on this element.
5294
- if (event.target === event.currentTarget && !isNativeButton && !isCompositeItem && !event.defaultPrevented && event.key === ' ') {
5295
- event.preventBaseUIHandler();
5296
- dispatchClickWithModifiers(event.currentTarget, event);
4913
+ // Keyboard accessibility for non interactive elements
4914
+ if (event.target === event.currentTarget && !isNativeButton && !isCompositeItem && event.key === ' ') {
4915
+ externalOnClick?.(event);
5297
4916
  }
5298
4917
  },
5299
4918
  onPointerDown(event) {
@@ -5322,38 +4941,9 @@ function isButtonElement(elem) {
5322
4941
  return isHTMLElement(elem) && elem.tagName === 'BUTTON';
5323
4942
  }
5324
4943
  function isValidLinkElement(elem) {
5325
- return isHTMLElement(elem) && elem.tagName === 'A' && Boolean(elem.href);
4944
+ return Boolean(elem?.tagName === 'A' && elem?.href);
5326
4945
  }
5327
4946
 
5328
- let TransitionStatusDataAttributes = /*#__PURE__*/function (TransitionStatusDataAttributes) {
5329
- /**
5330
- * Present when the component begins animating in.
5331
- */
5332
- TransitionStatusDataAttributes["startingStyle"] = "data-starting-style";
5333
- /**
5334
- * Present when the component is animating out.
5335
- */
5336
- TransitionStatusDataAttributes["endingStyle"] = "data-ending-style";
5337
- return TransitionStatusDataAttributes;
5338
- }({});
5339
- const STARTING_HOOK = {
5340
- 'data-starting-style': ''
5341
- };
5342
- const ENDING_HOOK = {
5343
- 'data-ending-style': ''
5344
- };
5345
- const transitionStatusMapping = {
5346
- transitionStatus(value) {
5347
- if (value === 'starting') {
5348
- return STARTING_HOOK;
5349
- }
5350
- if (value === 'ending') {
5351
- return ENDING_HOOK;
5352
- }
5353
- return null;
5354
- }
5355
- };
5356
-
5357
4947
  let CommonPopupDataAttributes = function (CommonPopupDataAttributes) {
5358
4948
  /**
5359
4949
  * Present when the popup is open.
@@ -5364,7 +4954,7 @@ let CommonPopupDataAttributes = function (CommonPopupDataAttributes) {
5364
4954
  */
5365
4955
  CommonPopupDataAttributes["closed"] = "data-closed";
5366
4956
  /**
5367
- * Present when the popup begins animating in.
4957
+ * Present when the popup is animating in.
5368
4958
  */
5369
4959
  CommonPopupDataAttributes[CommonPopupDataAttributes["startingStyle"] = TransitionStatusDataAttributes.startingStyle] = "startingStyle";
5370
4960
  /**
@@ -5387,24 +4977,32 @@ let CommonPopupDataAttributes = function (CommonPopupDataAttributes) {
5387
4977
  CommonPopupDataAttributes["align"] = "data-align";
5388
4978
  return CommonPopupDataAttributes;
5389
4979
  }({});
5390
-
5391
- // Literal keys (instead of enum member references) keep the docs-only enums above
5392
- // tree-shakeable: a runtime reference would retain the whole enum IIFE in every bundle.
4980
+ let CommonTriggerDataAttributes = /*#__PURE__*/function (CommonTriggerDataAttributes) {
4981
+ /**
4982
+ * Present when the popup is open.
4983
+ */
4984
+ CommonTriggerDataAttributes["popupOpen"] = "data-popup-open";
4985
+ /**
4986
+ * Present when a pressable trigger is pressed.
4987
+ */
4988
+ CommonTriggerDataAttributes["pressed"] = "data-pressed";
4989
+ return CommonTriggerDataAttributes;
4990
+ }({});
5393
4991
  const TRIGGER_HOOK = {
5394
- 'data-popup-open': ''
4992
+ [CommonTriggerDataAttributes.popupOpen]: ''
5395
4993
  };
5396
4994
  const PRESSABLE_TRIGGER_HOOK = {
5397
- 'data-popup-open': '',
5398
- 'data-pressed': ''
4995
+ [CommonTriggerDataAttributes.popupOpen]: '',
4996
+ [CommonTriggerDataAttributes.pressed]: ''
5399
4997
  };
5400
4998
  const POPUP_OPEN_HOOK = {
5401
- 'data-open': ''
4999
+ [CommonPopupDataAttributes.open]: ''
5402
5000
  };
5403
5001
  const POPUP_CLOSED_HOOK = {
5404
- 'data-closed': ''
5002
+ [CommonPopupDataAttributes.closed]: ''
5405
5003
  };
5406
5004
  const ANCHOR_HIDDEN_HOOK = {
5407
- 'data-anchor-hidden': ''
5005
+ [CommonPopupDataAttributes.anchorHidden]: ''
5408
5006
  };
5409
5007
  const triggerOpenStateMapping = {
5410
5008
  open(value) {
@@ -5436,10 +5034,6 @@ const popupStateMapping = {
5436
5034
  return null;
5437
5035
  }
5438
5036
  };
5439
- const popupTransitionStateMapping = {
5440
- ...popupStateMapping,
5441
- ...transitionStatusMapping
5442
- };
5443
5037
 
5444
5038
  function inertValue(value) {
5445
5039
  if (isReactVersionAtLeast(19)) {
@@ -5481,15 +5075,6 @@ if (process.env.NODE_ENV !== "production") InternalBackdrop.displayName = "Inter
5481
5075
  let originalHtmlStyles = {};
5482
5076
  let originalBodyStyles = {};
5483
5077
  let originalHtmlScrollBehavior = '';
5484
-
5485
- // The viewport's overflow comes from <html> when it establishes its own scroll container, and
5486
- // propagates from <body> otherwise. An `overflow` style on the other element doesn't lock the page.
5487
- function getViewportScroller(html, body) {
5488
- return isOverflowElement(html) ? html : body;
5489
- }
5490
- function isPageScrollLocked(win, html, body) {
5491
- return /hidden|clip/.test(win.getComputedStyle(getViewportScroller(html, body)).overflowY);
5492
- }
5493
5078
  function hasInsetScrollbars(referenceElement) {
5494
5079
  if (typeof document === 'undefined') {
5495
5080
  return false;
@@ -5506,7 +5091,7 @@ function supportsStableScrollbarGutter(referenceElement) {
5506
5091
  const doc = ownerDocument(referenceElement);
5507
5092
  const html = doc.documentElement;
5508
5093
  const body = doc.body;
5509
- const scrollContainer = getViewportScroller(html, body);
5094
+ const scrollContainer = isOverflowElement(html) ? html : body;
5510
5095
  const originalScrollContainerOverflowY = scrollContainer.style.overflowY;
5511
5096
  const originalHtmlStyleGutter = html.style.scrollbarGutter;
5512
5097
  html.style.scrollbarGutter = 'stable';
@@ -5527,7 +5112,7 @@ function preventScrollOverlayScrollbars(referenceElement) {
5527
5112
  // won't have any effect.
5528
5113
  // But if <body> has an `overflow` style (like `overflow-x: hidden`), we need to lock it
5529
5114
  // instead, as sticky elements shift otherwise.
5530
- const elementToLock = getViewportScroller(html, body);
5115
+ const elementToLock = isOverflowElement(html) ? html : body;
5531
5116
  const originalElementToLockStyles = {
5532
5117
  overflowY: elementToLock.style.overflowY,
5533
5118
  overflowX: elementToLock.style.overflowX
@@ -5592,7 +5177,7 @@ function preventScrollInsetScrollbars(referenceElement) {
5592
5177
  // with whitespace. Warn if <body> has margins?
5593
5178
  const marginY = parseFloat(bodyStyles.marginTop) + parseFloat(bodyStyles.marginBottom);
5594
5179
  const marginX = parseFloat(bodyStyles.marginLeft) + parseFloat(bodyStyles.marginRight);
5595
- const elementToLock = getViewportScroller(html, body);
5180
+ const elementToLock = isOverflowElement(html) ? html : body;
5596
5181
  updateGutterOnly = supportsStableScrollbarGutter(referenceElement);
5597
5182
 
5598
5183
  /*
@@ -5622,9 +5207,7 @@ function preventScrollInsetScrollbars(referenceElement) {
5622
5207
  height: marginY || scrollbarHeight ? `calc(100dvh - ${marginY + scrollbarHeight}px)` : '100dvh',
5623
5208
  width: marginX || scrollbarWidth ? `calc(100vw - ${marginX + scrollbarWidth}px)` : '100vw',
5624
5209
  boxSizing: 'border-box',
5625
- // Assign the longhands that `cleanup` restores, so nothing is left behind.
5626
- overflowY: 'hidden',
5627
- overflowX: 'hidden',
5210
+ overflow: 'hidden',
5628
5211
  scrollBehavior: 'unset'
5629
5212
  });
5630
5213
  body.scrollTop = scrollTop;
@@ -5689,30 +5272,11 @@ class ScrollLocker {
5689
5272
  }
5690
5273
  const doc = ownerDocument(referenceElement);
5691
5274
  const html = doc.documentElement;
5692
- const body = doc.body;
5693
- const win = getWindow(html);
5694
-
5695
- // The page is already locked, either by the site author or by a non-Base UI overlay that
5696
- // hasn't cleaned up yet. Leave it alone and wait for the lock to clear before taking over,
5697
- // otherwise we'd snapshot the locked state and restore it after our own lock is released.
5698
- if (isPageScrollLocked(win, html, body)) {
5699
- const observer = new win.MutationObserver(() => {
5700
- if (isPageScrollLocked(win, html, body)) {
5701
- return;
5702
- }
5703
- observer.disconnect();
5704
- this.restore = null;
5705
- this.lock(referenceElement);
5706
- });
5275
+ const htmlOverflowY = getWindow(html).getComputedStyle(html).overflowY;
5707
5276
 
5708
- // Watch every attribute: locks are applied through inline styles, classes, or attributes
5709
- // paired with a stylesheet (`data-scroll-locked` in react-remove-scroll, for example).
5710
- const options = {
5711
- attributes: true
5712
- };
5713
- observer.observe(html, options);
5714
- observer.observe(body, options);
5715
- this.restore = () => observer.disconnect();
5277
+ // If the site author already hid overflow on <html>, respect it and bail out.
5278
+ if (htmlOverflowY === 'hidden' || htmlOverflowY === 'clip') {
5279
+ this.restore = NOOP;
5716
5280
  return;
5717
5281
  }
5718
5282
  const hasOverlayScrollbars = ios || !hasInsetScrollbars(referenceElement);
@@ -5743,4 +5307,4 @@ function useScrollLock(enabled = true, referenceElement = null) {
5743
5307
  }, [enabled, referenceElement]);
5744
5308
  }
5745
5309
 
5746
- export { getFloatingFocusElement as $, AnimationFrame as A, visuallyHiddenInput as B, visuallyHidden as C, activeElement as D, wheel as E, FOCUSABLE_POPUP_PROPS as F, createGenericEventDetails as G, useOnMount as H, getWindow as I, incrementPress as J, decrementPress as K, useValueChanged as L, inputClear as M, inputBlur as N, inputChange as O, keyboard as P, inputPaste as Q, useOpenInteractionType as R, SafeReact as S, Timeout as T, useStore as U, useClick as V, useDismiss as W, ReactStore as X, focusOut as Y, outsidePress as Z, pressableTriggerOpenStateMapping as _, isLastTraversableNode as a, isListIndexDisabled as a$, contains as a0, cancelOpen as a1, FloatingPortal as a2, InternalBackdrop as a3, inertValue as a4, DROPDOWN_COLLISION_AVOIDANCE as a5, popupStateMapping as a6, useDirection as a7, useAnimationFrame as a8, webkit as a9, getNodeScroll as aA, isTableElement as aB, isContainingBlock as aC, getContainingBlock as aD, getOverflowAncestors as aE, getFrameElement as aF, isWebKit as aG, floor as aH, useId as aI, useFloatingParentNodeId as aJ, FloatingRootStore as aK, PopupTriggerMap as aL, useFloatingTree as aM, isTypeableCombobox as aN, isIndexOutOfListBounds as aO, getMinListIndex as aP, getMaxListIndex as aQ, stopEvent as aR, listNavigation as aS, findNonDisabledListIndex as aT, enqueueFocus as aU, isVirtualPointerEvent as aV, ARROW_UP as aW, ARROW_DOWN as aX, ARROW_LEFT as aY, ARROW_RIGHT as aZ, isElementVisible as a_, rectToClientRect as aa, windowResize as ab, isVirtualClick as ac, itemPress as ad, evaluate as ae, getSide as af, getSideAxis as ag, getOppositePlacement as ah, getExpandedPlacements as ai, getOppositeAxisPlacements as aj, getAlignmentSides as ak, getAlignment as al, min as am, max as an, getAlignmentAxis as ao, getPaddingObject as ap, getOppositeAxis as aq, clamp as ar, getAxisLength as as, getDocumentElement as at, isElement as au, createCoords as av, round as aw, isTopLayer as ax, getNodeName as ay, isOverflowElement as az, getParentNode as b, scrub as b$, jsdom as b0, DISABLED_TRANSITIONS_STYLE as b1, useScrollLock as b2, shimExports as b3, usePopupInteractionProps as b4, setPopupOpenState as b5, usePopupRootStore as b6, usePopupRootSync as b7, useImplicitActiveTrigger as b8, useOpenStateTransitions as b9, POPUP_COLLISION_AVOIDANCE as bA, FloatingNode as bB, siblingOpen as bC, FloatingTreeStore as bD, fastComponent as bE, useFloatingNodeId as bF, attachPreventUnmountOnClose as bG, useSyncedFloatingRootContext as bH, TYPEAHEAD_RESET_MS as bI, FloatingTree as bJ, useCompositeRootContext as bK, getTabbableBeforeElement as bL, isOutsideEvent as bM, getTabbableAfterElement as bN, getNextTabbable as bO, fastComponentRef as bP, FocusGuard as bQ, PATIENT_CLICK_THRESHOLD as bR, useTriggerRegistration as bS, voiceOver as bT, getGridNavigatedIndex as bU, Store as bV, inputPress as bW, pointer as bX, gecko as bY, clearPress as bZ, chipRemovePress as b_, PopupHandleAttachment as ba, useTriggerDataForwarding as bb, useOpenMethodTriggerProps as bc, CLICK_TRIGGER_IDENTIFIER as bd, triggerOpenStateMapping as be, CommonPopupDataAttributes as bf, android as bg, closeWatcher as bh, TransitionStatusDataAttributes as bi, swipe as bj, BASE_UI_SWIPE_IGNORE_SELECTOR as bk, isMouseLikePointerType as bl, mergeCleanups as bm, isTypeableElement as bn, matchesFocusVisible as bo, isTargetInsideEnabledTrigger as bp, triggerFocus as bq, createAttribute as br, mac as bs, triggerPress as bt, escapeKey as bu, isInteractiveElement as bv, getNodeChildren as bw, triggerHover as bx, dispatchClickWithModifiers as by, useAnimationsFinished as bz, useIsoLayoutEffect as c, useStableCallback as d, getTarget as e, useButton as f, getComputedStyle as g, createChangeEventDetails as h, isHTMLElement as i, closePress as j, imperativeAction as k, useOpenChangeComplete as l, createDefaultInitialFocus as m, FloatingFocusManager as n, ownerDocument as o, popupTransitionStateMapping as p, useTimeout as q, error as r, useTransitionStatus as s, transitionStatusMapping as t, useBaseUiId as u, useControlled as v, useValueAsRef as w, ios as x, addEventListener as y, none as z };
5310
+ export { cancelOpen as $, inputBlur as A, inputPaste as B, CommonPopupDataAttributes as C, activeElement as D, createGenericEventDetails as E, FOCUSABLE_POPUP_PROPS as F, wheel as G, useOnMount as H, incrementPress as I, decrementPress as J, useValueChanged as K, stopEvent as L, keyboard as M, createSelector as N, useOpenInteractionType as O, useStore as P, useClick as Q, useDismiss as R, SafeReact as S, Timeout as T, useOnFirstRender as U, Store as V, focusOut as W, outsidePress as X, pressableTriggerOpenStateMapping as Y, getFloatingFocusElement as Z, contains as _, useIsoLayoutEffect as a, getTabbableBeforeElement as a$, FloatingPortal as a0, InternalBackdrop as a1, inertValue as a2, DROPDOWN_COLLISION_AVOIDANCE as a3, useDirection as a4, useAnimationFrame as a5, webkit as a6, windowResize as a7, isVirtualClick as a8, itemPress as a9, isTargetInsideEnabledTrigger as aA, triggerFocus as aB, createAttribute as aC, mac as aD, triggerPress as aE, escapeKey as aF, isInteractiveElement as aG, getNodeChildren as aH, triggerHover as aI, useAnimationsFinished as aJ, POPUP_COLLISION_AVOIDANCE as aK, FloatingNode as aL, siblingOpen as aM, ReactStore as aN, FloatingTreeStore as aO, fastComponent as aP, useInitialOpenSync as aQ, useFloatingNodeId as aR, useImplicitActiveTrigger as aS, useOpenStateTransitions as aT, attachPreventUnmountOnClose as aU, setPopupOpenState as aV, useSyncedFloatingRootContext as aW, TYPEAHEAD_RESET_MS as aX, usePopupInteractionProps as aY, FloatingTree as aZ, useCompositeRootContext as a_, useId as aa, useFloatingParentNodeId as ab, FloatingRootStore as ac, PopupTriggerMap as ad, useFloatingTree as ae, isTypeableCombobox as af, isIndexOutOfListBounds as ag, getMinListIndex as ah, getMaxListIndex as ai, listNavigation as aj, findNonDisabledListIndex as ak, enqueueFocus as al, isVirtualPointerEvent as am, ARROW_UP as an, ARROW_DOWN as ao, ARROW_LEFT as ap, ARROW_RIGHT as aq, isListIndexDisabled as ar, isElementVisible as as, jsdom as at, DISABLED_TRANSITIONS_STYLE as au, useScrollLock as av, isMouseLikePointerType as aw, mergeCleanups as ax, isTypeableElement as ay, matchesFocusVisible as az, useStableCallback as b, isOutsideEvent as b0, getTabbableAfterElement as b1, getNextTabbable as b2, fastComponentRef as b3, useTriggerDataForwarding as b4, FocusGuard as b5, PATIENT_CLICK_THRESHOLD as b6, useTriggerRegistration as b7, triggerOpenStateMapping as b8, usePopupRootSync as b9, usePopupStore as ba, useOpenMethodTriggerProps as bb, CLICK_TRIGGER_IDENTIFIER as bc, android as bd, closeWatcher as be, BASE_UI_SWIPE_IGNORE_SELECTOR as bf, TransitionStatusDataAttributes as bg, swipe as bh, getGridNavigatedIndex as bi, inputPress as bj, gecko as bk, clearPress as bl, chipRemovePress as bm, pointer as bn, scrub as bo, useButton as c, createChangeEventDetails as d, closePress as e, useOpenChangeComplete as f, getTarget as g, createDefaultInitialFocus as h, FloatingFocusManager as i, imperativeAction as j, useTimeout as k, error as l, useTransitionStatus as m, useControlled as n, ownerDocument as o, popupStateMapping as p, useValueAsRef as q, ios as r, addEventListener as s, transitionStatusMapping as t, useBaseUiId as u, none as v, visuallyHiddenInput as w, visuallyHidden as x, inputChange as y, inputClear as z };