@jobber/components 9.5.3 → 9.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/Combobox.d.ts +1 -0
  2. package/Combobox.js +17 -0
  3. package/dist/Autocomplete/index.cjs +0 -2
  4. package/dist/Autocomplete/index.mjs +0 -2
  5. package/dist/Autocomplete-cjs.js +4 -5
  6. package/dist/Autocomplete-es.js +1 -2
  7. package/dist/Card/index.cjs +2 -2
  8. package/dist/Card/index.mjs +2 -2
  9. package/dist/Chip/index.cjs +0 -2
  10. package/dist/Chip/index.mjs +0 -2
  11. package/dist/Chips/InternalChipDismissible/hooks/index.cjs +0 -2
  12. package/dist/Chips/InternalChipDismissible/hooks/index.mjs +0 -2
  13. package/dist/Chips/InternalChipDismissible/index.cjs +0 -2
  14. package/dist/Chips/InternalChipDismissible/index.mjs +0 -2
  15. package/dist/Chips/index.cjs +0 -2
  16. package/dist/Chips/index.mjs +0 -2
  17. package/dist/Combobox/Combobox.contentSlots.d.ts +5 -0
  18. package/dist/Combobox/Combobox.context.d.ts +34 -0
  19. package/dist/Combobox/Combobox.d.ts +69 -0
  20. package/dist/Combobox/Combobox.footer.d.ts +7 -0
  21. package/dist/Combobox/Combobox.items.d.ts +25 -0
  22. package/dist/Combobox/Combobox.responsive.d.ts +54 -0
  23. package/dist/Combobox/Combobox.selectAll.d.ts +4 -0
  24. package/dist/Combobox/Combobox.slotFactory.d.ts +7 -0
  25. package/dist/Combobox/Combobox.testUtils.d.ts +12 -0
  26. package/dist/Combobox/Combobox.types.d.ts +166 -0
  27. package/dist/Combobox/index.cjs +35 -0
  28. package/dist/Combobox/index.d.ts +2 -0
  29. package/dist/Combobox/index.mjs +29 -0
  30. package/dist/Combobox-cjs.js +675 -0
  31. package/dist/Combobox-es.js +673 -0
  32. package/dist/ComboboxPrimitive-cjs.js +88 -47
  33. package/dist/ComboboxPrimitive-es.js +39 -23
  34. package/dist/{ComboboxChipRemove-cjs.js → ComboboxSeparator-cjs.js} +501 -455
  35. package/dist/{ComboboxChipRemove-es.js → ComboboxSeparator-es.js} +500 -456
  36. package/dist/ConfirmationModal/index.cjs +0 -2
  37. package/dist/ConfirmationModal/index.mjs +0 -2
  38. package/dist/ContentBlock/index.cjs +1 -0
  39. package/dist/ContentBlock/index.mjs +1 -0
  40. package/dist/ContentBlock-cjs.js +2 -18
  41. package/dist/ContentBlock-es.js +1 -17
  42. package/dist/DataDump/index.cjs +2 -2
  43. package/dist/DataDump/index.mjs +2 -2
  44. package/dist/DataList/components/DataListActions/index.cjs +0 -2
  45. package/dist/DataList/components/DataListActions/index.mjs +0 -2
  46. package/dist/DataList/components/DataListBulkActions/index.cjs +0 -2
  47. package/dist/DataList/components/DataListBulkActions/index.mjs +0 -2
  48. package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +0 -2
  49. package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +0 -2
  50. package/dist/DataList/components/DataListFilters/index.cjs +0 -2
  51. package/dist/DataList/components/DataListFilters/index.mjs +0 -2
  52. package/dist/DataList/components/DataListHeader/index.cjs +0 -2
  53. package/dist/DataList/components/DataListHeader/index.mjs +0 -2
  54. package/dist/DataList/components/DataListItem/index.cjs +0 -2
  55. package/dist/DataList/components/DataListItem/index.mjs +0 -2
  56. package/dist/DataList/components/DataListItemActions/index.cjs +0 -2
  57. package/dist/DataList/components/DataListItemActions/index.mjs +0 -2
  58. package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +0 -2
  59. package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +0 -2
  60. package/dist/DataList/components/DataListItems/index.cjs +0 -2
  61. package/dist/DataList/components/DataListItems/index.mjs +0 -2
  62. package/dist/DataList/components/DataListLayout/index.cjs +0 -2
  63. package/dist/DataList/components/DataListLayout/index.mjs +0 -2
  64. package/dist/DataList/components/DataListLayoutActions/index.cjs +0 -2
  65. package/dist/DataList/components/DataListLayoutActions/index.mjs +0 -2
  66. package/dist/DataList/index.cjs +0 -2
  67. package/dist/DataList/index.mjs +0 -2
  68. package/dist/DatePicker/index.cjs +0 -2
  69. package/dist/DatePicker/index.mjs +0 -2
  70. package/dist/DatePicker-cjs.js +4 -5
  71. package/dist/DatePicker-es.js +1 -2
  72. package/dist/Dialog/index.cjs +2 -2
  73. package/dist/Dialog/index.mjs +2 -2
  74. package/dist/DrawerDescription-cjs.js +268 -99
  75. package/dist/DrawerDescription-es.js +270 -103
  76. package/dist/DrawerTitle-cjs.js +2 -6
  77. package/dist/DrawerTitle-es.js +3 -7
  78. package/dist/FieldDescription-cjs.js +184 -181
  79. package/dist/FieldDescription-es.js +187 -184
  80. package/dist/FilterPicker/components/FilterPickerActivator/index.cjs +0 -2
  81. package/dist/FilterPicker/components/FilterPickerActivator/index.mjs +0 -2
  82. package/dist/FilterPicker/components/FilterPickerContent/index.cjs +0 -2
  83. package/dist/FilterPicker/components/FilterPickerContent/index.mjs +0 -2
  84. package/dist/FilterPicker/components/FilterPickerTrigger/index.cjs +0 -2
  85. package/dist/FilterPicker/components/FilterPickerTrigger/index.mjs +0 -2
  86. package/dist/FilterPicker/index.cjs +0 -2
  87. package/dist/FilterPicker/index.mjs +0 -2
  88. package/dist/FilterPickerContent-cjs.js +4 -5
  89. package/dist/FilterPickerContent-es.js +1 -2
  90. package/dist/FormatFile/index.cjs +0 -2
  91. package/dist/FormatFile/index.mjs +0 -2
  92. package/dist/Gallery/index.cjs +0 -2
  93. package/dist/Gallery/index.mjs +0 -2
  94. package/dist/InputDate/index.cjs +0 -2
  95. package/dist/InputDate/index.mjs +0 -2
  96. package/dist/InputNumber/index.cjs +2 -2
  97. package/dist/InputNumber/index.mjs +2 -2
  98. package/dist/InputNumber/types.d.ts +5 -0
  99. package/dist/InputNumber-cjs.js +2 -2
  100. package/dist/InputNumber-es.js +2 -2
  101. package/dist/LightBox/index.cjs +0 -2
  102. package/dist/LightBox/index.mjs +0 -2
  103. package/dist/Menu/index.cjs +2 -2
  104. package/dist/Menu/index.mjs +2 -2
  105. package/dist/Menu-cjs.js +4 -5
  106. package/dist/Menu-es.js +1 -2
  107. package/dist/MenuSubmenuTrigger-cjs.js +206 -164
  108. package/dist/MenuSubmenuTrigger-es.js +194 -152
  109. package/dist/Modal/index.cjs +0 -2
  110. package/dist/Modal/index.mjs +1 -3
  111. package/dist/NumberFieldInput-cjs.js +187 -308
  112. package/dist/NumberFieldInput-es.js +187 -308
  113. package/dist/Page/index.cjs +2 -2
  114. package/dist/Page/index.mjs +2 -2
  115. package/dist/Popover/index.cjs +0 -2
  116. package/dist/Popover/index.mjs +0 -2
  117. package/dist/Popover-cjs.js +7 -8
  118. package/dist/Popover-es.js +1 -2
  119. package/dist/ScrollAreaViewport-cjs.js +747 -1093
  120. package/dist/ScrollAreaViewport-es.js +734 -1081
  121. package/dist/Select/index.cjs +2 -2
  122. package/dist/Select/index.mjs +2 -2
  123. package/dist/SelectGroupLabel-cjs.js +207 -306
  124. package/dist/SelectGroupLabel-es.js +203 -302
  125. package/dist/SelectPrimitive-cjs.js +11 -17
  126. package/dist/SelectPrimitive-es.js +1 -7
  127. package/dist/Tooltip/index.cjs +0 -2
  128. package/dist/Tooltip/index.mjs +0 -2
  129. package/dist/Tooltip-cjs.js +6 -7
  130. package/dist/Tooltip-es.js +1 -2
  131. package/dist/buttonRenderAdapter-cjs.js +2 -1
  132. package/dist/buttonRenderAdapter-es.js +2 -1
  133. package/dist/dialogReturnFocus-cjs.js +1 -1
  134. package/dist/dialogReturnFocus-es.js +2 -2
  135. package/dist/docs/InputNumber/InputNumber.md +2 -2
  136. package/dist/docs/SelectPrimitive/SelectPrimitive.md +1 -1
  137. package/dist/floating-ui.react-cjs.js +2557 -185
  138. package/dist/floating-ui.react-es.js +2512 -149
  139. package/dist/getMappedBreakpointWidth-cjs.js +21 -0
  140. package/dist/getMappedBreakpointWidth-es.js +18 -0
  141. package/dist/index.cjs +15 -10
  142. package/dist/index.d.mts +1 -0
  143. package/dist/index.d.ts +1 -0
  144. package/dist/index.mjs +14 -10
  145. package/dist/mergeClassName-cjs.js +12 -0
  146. package/dist/mergeClassName-es.js +10 -0
  147. package/dist/primitives/BottomSheet/index.cjs +1 -1
  148. package/dist/primitives/BottomSheet/index.mjs +1 -1
  149. package/dist/primitives/ComboboxPrimitive/ComboboxPrimitive.d.ts +2 -2
  150. package/dist/primitives/ComboboxPrimitive/index.cjs +4 -5
  151. package/dist/primitives/ComboboxPrimitive/index.mjs +4 -5
  152. package/dist/primitives/SelectPrimitive/index.cjs +1 -2
  153. package/dist/primitives/SelectPrimitive/index.mjs +1 -2
  154. package/dist/primitives/index.cjs +3 -3
  155. package/dist/primitives/index.mjs +3 -3
  156. package/dist/scrollEdges-cjs.js +1 -11
  157. package/dist/scrollEdges-es.js +1 -11
  158. package/dist/scrollable-cjs.js +58 -0
  159. package/dist/scrollable-es.js +54 -0
  160. package/dist/styles.css +759 -42
  161. package/dist/unstyledPrimitives/index.cjs +720 -478
  162. package/dist/unstyledPrimitives/index.mjs +663 -421
  163. package/dist/useCompositeListItem-cjs.js +2108 -361
  164. package/dist/useCompositeListItem-es.js +2076 -331
  165. package/dist/useLabel-cjs.js +2 -3
  166. package/dist/useLabel-es.js +2 -3
  167. package/dist/useRenderElement-cjs.js +9 -7
  168. package/dist/useRenderElement-es.js +9 -7
  169. package/dist/useScrollLock-cjs.js +819 -344
  170. package/dist/useScrollLock-es.js +732 -296
  171. package/dist/useScrollToActive-cjs.js +4 -5
  172. package/dist/useScrollToActive-es.js +1 -2
  173. package/dist/utils/meta/meta.json +43 -1
  174. package/package.json +8 -3
  175. package/dist/floating-ui.react-dom-cjs.js +0 -2103
  176. package/dist/floating-ui.react-dom-es.js +0 -2072
  177. package/dist/floating-ui.utils-cjs.js +0 -342
  178. package/dist/floating-ui.utils-es.js +0 -300
@@ -1,9 +1,1720 @@
1
1
  import * as React from 'react';
2
- import { aa as useId, ab as useFloatingParentNodeId, ac as FloatingRootStore, ad as PopupTriggerMap, a as useIsoLayoutEffect, ae as useFloatingTree, Z as getFloatingFocusElement, af as isTypeableCombobox, q as useValueAsRef, b as useStableCallback, a5 as useAnimationFrame, ag as isIndexOutOfListBounds, ah as getMinListIndex, ai as getMaxListIndex, D as activeElement, o as ownerDocument, _ as contains, L as stopEvent, d as createChangeEventDetails, aj as listNavigation, ak as findNonDisabledListIndex, g as getTarget, W as focusOut, al as enqueueFocus, a8 as isVirtualClick, am as isVirtualPointerEvent, an as ARROW_UP, ao as ARROW_DOWN, ap as ARROW_LEFT, aq as ARROW_RIGHT, k as useTimeout, ar as isListIndexDisabled, as as isElementVisible, u as useBaseUiId, at as jsdom, a4 as useDirection, au as DISABLED_TRANSITIONS_STYLE, p as popupStateMapping, av as useScrollLock } from './useScrollLock-es.js';
3
- import { b as isElement, i as isHTMLElement, K as getWindow, p as evaluate, q as getPaddingObject, m as getAlignmentAxis, o as getAlignment, z as getAxisLength, w as clamp, n as getSide, l as getSideAxis } from './floating-ui.utils-es.js';
2
+ import { useLayoutEffect } from 'react';
3
+ import { ae as evaluate, af as getSide, ag as getSideAxis, ah as getOppositePlacement, ai as getExpandedPlacements, aj as getOppositeAxisPlacements, ak as getAlignmentSides, al as getAlignment, am as min, an as max, ao as getAlignmentAxis, ap as getPaddingObject, aa as rectToClientRect, aq as getOppositeAxis, ar as clamp, as as getAxisLength, at as getDocumentElement, au as isElement, i as isHTMLElement, av as createCoords, aw as round, ax as isTopLayer, ay as getNodeName, az as isOverflowElement, aA as getNodeScroll, I as getWindow, b as getParentNode, a as isLastTraversableNode, aB as isTableElement, aC as isContainingBlock, aD as getContainingBlock, g as getComputedStyle$1, aE as getOverflowAncestors, aF as getFrameElement, aG as isWebKit, aH as floor, aI as useId, aJ as useFloatingParentNodeId, aK as FloatingRootStore, aL as PopupTriggerMap, c as useIsoLayoutEffect, aM as useFloatingTree, $ as getFloatingFocusElement, aN as isTypeableCombobox, w as useValueAsRef, d as useStableCallback, a8 as useAnimationFrame, aO as isIndexOutOfListBounds, aP as getMinListIndex, aQ as getMaxListIndex, D as activeElement, o as ownerDocument, a0 as contains, aR as stopEvent, h as createChangeEventDetails, aS as listNavigation, aT as findNonDisabledListIndex, e as getTarget, Y as focusOut, a9 as webkit, aU as enqueueFocus, ac as isVirtualClick, aV as isVirtualPointerEvent, aW as ARROW_UP, aX as ARROW_DOWN, aY as ARROW_LEFT, aZ as ARROW_RIGHT, q as useTimeout, a_ as isElementVisible, a$ as isListIndexDisabled, u as useBaseUiId, b0 as jsdom, a7 as useDirection, b1 as DISABLED_TRANSITIONS_STYLE, a6 as popupStateMapping, b2 as useScrollLock } from './useScrollLock-es.js';
4
4
  import { a as useRefWithInit, c as EMPTY_ARRAY, N as NOOP, E as EMPTY_OBJECT, f as formatErrorMessage, u as useRenderElement } from './useRenderElement-es.js';
5
5
  import { jsx } from 'react/jsx-runtime';
6
- import { u as useFloating$1, h as hide$1, f as flip, d as size, b as autoUpdate, o as offset, s as shift, l as limitShift } from './floating-ui.react-dom-es.js';
6
+ import * as ReactDOM from 'react-dom';
7
+
8
+ function useOnFirstRender(fn) {
9
+ const ref = React.useRef(true);
10
+ if (ref.current) {
11
+ ref.current = false;
12
+ fn();
13
+ }
14
+ }
15
+
16
+ function computeCoordsFromPlacement(_ref, placement, rtl) {
17
+ let {
18
+ reference,
19
+ floating
20
+ } = _ref;
21
+ const sideAxis = getSideAxis(placement);
22
+ const alignmentAxis = getAlignmentAxis(placement);
23
+ const alignLength = getAxisLength(alignmentAxis);
24
+ const side = getSide(placement);
25
+ const isVertical = sideAxis === 'y';
26
+ const commonX = reference.x + reference.width / 2 - floating.width / 2;
27
+ const commonY = reference.y + reference.height / 2 - floating.height / 2;
28
+ const commonAlign = reference[alignLength] / 2 - floating[alignLength] / 2;
29
+ let coords;
30
+ switch (side) {
31
+ case 'top':
32
+ coords = {
33
+ x: commonX,
34
+ y: reference.y - floating.height
35
+ };
36
+ break;
37
+ case 'bottom':
38
+ coords = {
39
+ x: commonX,
40
+ y: reference.y + reference.height
41
+ };
42
+ break;
43
+ case 'right':
44
+ coords = {
45
+ x: reference.x + reference.width,
46
+ y: commonY
47
+ };
48
+ break;
49
+ case 'left':
50
+ coords = {
51
+ x: reference.x - floating.width,
52
+ y: commonY
53
+ };
54
+ break;
55
+ default:
56
+ coords = {
57
+ x: reference.x,
58
+ y: reference.y
59
+ };
60
+ }
61
+ const alignment = getAlignment(placement);
62
+ if (alignment) {
63
+ coords[alignmentAxis] += commonAlign * (alignment === 'end' ? 1 : -1) * (rtl && isVertical ? -1 : 1);
64
+ }
65
+ return coords;
66
+ }
67
+
68
+ /**
69
+ * Resolves with an object of overflow side offsets that determine how much the
70
+ * element is overflowing a given clipping boundary on each side.
71
+ * - positive = overflowing the boundary by that number of pixels
72
+ * - negative = how many pixels left before it will overflow
73
+ * - 0 = lies flush with the boundary
74
+ * @see https://floating-ui.com/docs/detectOverflow
75
+ */
76
+ async function detectOverflow(state, options) {
77
+ var _await$platform$isEle;
78
+ if (options === void 0) {
79
+ options = {};
80
+ }
81
+ const {
82
+ x,
83
+ y,
84
+ platform,
85
+ rects,
86
+ elements,
87
+ strategy
88
+ } = state;
89
+ const {
90
+ boundary = 'clippingAncestors',
91
+ rootBoundary = 'viewport',
92
+ elementContext = 'floating',
93
+ altBoundary = false,
94
+ padding = 0
95
+ } = evaluate(options, state);
96
+ const paddingObject = getPaddingObject(padding);
97
+ const altContext = elementContext === 'floating' ? 'reference' : 'floating';
98
+ const element = elements[altBoundary ? altContext : elementContext];
99
+ const clippingClientRect = rectToClientRect(await platform.getClippingRect({
100
+ element: ((_await$platform$isEle = await (platform.isElement == null ? void 0 : platform.isElement(element))) != null ? _await$platform$isEle : true) ? element : element.contextElement || (await (platform.getDocumentElement == null ? void 0 : platform.getDocumentElement(elements.floating))),
101
+ boundary,
102
+ rootBoundary,
103
+ strategy
104
+ }));
105
+ const rect = elementContext === 'floating' ? {
106
+ x,
107
+ y,
108
+ width: rects.floating.width,
109
+ height: rects.floating.height
110
+ } : rects.reference;
111
+ const offsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(elements.floating));
112
+ const offsetScale = (await (platform.isElement == null ? void 0 : platform.isElement(offsetParent))) && (await (platform.getScale == null ? void 0 : platform.getScale(offsetParent))) || {
113
+ x: 1,
114
+ y: 1
115
+ };
116
+ const elementClientRect = rectToClientRect(platform.convertOffsetParentRelativeRectToViewportRelativeRect ? await platform.convertOffsetParentRelativeRectToViewportRelativeRect({
117
+ elements,
118
+ rect,
119
+ offsetParent,
120
+ strategy
121
+ }) : rect);
122
+ return {
123
+ top: (clippingClientRect.top - elementClientRect.top + paddingObject.top) / offsetScale.y,
124
+ bottom: (elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom) / offsetScale.y,
125
+ left: (clippingClientRect.left - elementClientRect.left + paddingObject.left) / offsetScale.x,
126
+ right: (elementClientRect.right - clippingClientRect.right + paddingObject.right) / offsetScale.x
127
+ };
128
+ }
129
+
130
+ // Maximum number of resets that can occur before bailing to avoid infinite reset loops.
131
+ const MAX_RESET_COUNT = 50;
132
+
133
+ /**
134
+ * Computes the `x` and `y` coordinates that will place the floating element
135
+ * next to a given reference element.
136
+ *
137
+ * This export does not have any `platform` interface logic. You will need to
138
+ * write one for the platform you are using Floating UI with.
139
+ */
140
+ const computePosition$1 = async (reference, floating, config) => {
141
+ const {
142
+ placement = 'bottom',
143
+ strategy = 'absolute',
144
+ middleware = [],
145
+ platform
146
+ } = config;
147
+ const platformWithDetectOverflow = platform.detectOverflow ? platform : {
148
+ ...platform,
149
+ detectOverflow
150
+ };
151
+ const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(floating));
152
+ let rects = await platform.getElementRects({
153
+ reference,
154
+ floating,
155
+ strategy
156
+ });
157
+ let {
158
+ x,
159
+ y
160
+ } = computeCoordsFromPlacement(rects, placement, rtl);
161
+ let statefulPlacement = placement;
162
+ let resetCount = 0;
163
+ const middlewareData = {};
164
+ for (let i = 0; i < middleware.length; i++) {
165
+ const currentMiddleware = middleware[i];
166
+ if (!currentMiddleware) {
167
+ continue;
168
+ }
169
+ const {
170
+ name,
171
+ fn
172
+ } = currentMiddleware;
173
+ const {
174
+ x: nextX,
175
+ y: nextY,
176
+ data,
177
+ reset
178
+ } = await fn({
179
+ x,
180
+ y,
181
+ initialPlacement: placement,
182
+ placement: statefulPlacement,
183
+ strategy,
184
+ middlewareData,
185
+ rects,
186
+ platform: platformWithDetectOverflow,
187
+ elements: {
188
+ reference,
189
+ floating
190
+ }
191
+ });
192
+ x = nextX != null ? nextX : x;
193
+ y = nextY != null ? nextY : y;
194
+ middlewareData[name] = {
195
+ ...middlewareData[name],
196
+ ...data
197
+ };
198
+ if (reset && resetCount < MAX_RESET_COUNT) {
199
+ resetCount++;
200
+ if (typeof reset === 'object') {
201
+ if (reset.placement) {
202
+ statefulPlacement = reset.placement;
203
+ }
204
+ if (reset.rects) {
205
+ rects = reset.rects === true ? await platform.getElementRects({
206
+ reference,
207
+ floating,
208
+ strategy
209
+ }) : reset.rects;
210
+ }
211
+ ({
212
+ x,
213
+ y
214
+ } = computeCoordsFromPlacement(rects, statefulPlacement, rtl));
215
+ }
216
+ i = -1;
217
+ }
218
+ }
219
+ return {
220
+ x,
221
+ y,
222
+ placement: statefulPlacement,
223
+ strategy,
224
+ middlewareData
225
+ };
226
+ };
227
+
228
+ /**
229
+ * Optimizes the visibility of the floating element by flipping the `placement`
230
+ * in order to keep it in view when the preferred placement(s) will overflow the
231
+ * clipping boundary. Alternative to `autoPlacement`.
232
+ * @see https://floating-ui.com/docs/flip
233
+ */
234
+ const flip$2 = function (options) {
235
+ if (options === void 0) {
236
+ options = {};
237
+ }
238
+ return {
239
+ name: 'flip',
240
+ options,
241
+ async fn(state) {
242
+ var _middlewareData$arrow, _middlewareData$flip;
243
+ const {
244
+ placement,
245
+ middlewareData,
246
+ rects,
247
+ initialPlacement,
248
+ platform,
249
+ elements
250
+ } = state;
251
+ const {
252
+ mainAxis: checkMainAxis = true,
253
+ crossAxis: checkCrossAxis = true,
254
+ fallbackPlacements: specifiedFallbackPlacements,
255
+ fallbackStrategy = 'bestFit',
256
+ fallbackAxisSideDirection = 'none',
257
+ flipAlignment = true,
258
+ ...detectOverflowOptions
259
+ } = evaluate(options, state);
260
+
261
+ // If a reset by the arrow was caused due to an alignment offset being
262
+ // added, we should skip any logic now since `flip()` has already done its
263
+ // work.
264
+ // https://github.com/floating-ui/floating-ui/issues/2549#issuecomment-1719601643
265
+ if ((_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {
266
+ return {};
267
+ }
268
+ const side = getSide(placement);
269
+ const initialSideAxis = getSideAxis(initialPlacement);
270
+ const isBasePlacement = getSide(initialPlacement) === initialPlacement;
271
+ const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));
272
+ const fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipAlignment ? [getOppositePlacement(initialPlacement)] : getExpandedPlacements(initialPlacement));
273
+ const hasFallbackAxisSideDirection = fallbackAxisSideDirection !== 'none';
274
+ if (!specifiedFallbackPlacements && hasFallbackAxisSideDirection) {
275
+ fallbackPlacements.push(...getOppositeAxisPlacements(initialPlacement, flipAlignment, fallbackAxisSideDirection, rtl));
276
+ }
277
+ const placements = [initialPlacement, ...fallbackPlacements];
278
+ const overflow = await platform.detectOverflow(state, detectOverflowOptions);
279
+ const overflows = [];
280
+ let overflowsData = ((_middlewareData$flip = middlewareData.flip) == null ? void 0 : _middlewareData$flip.overflows) || [];
281
+ if (checkMainAxis) {
282
+ overflows.push(overflow[side]);
283
+ }
284
+ if (checkCrossAxis) {
285
+ const sides = getAlignmentSides(placement, rects, rtl);
286
+ overflows.push(overflow[sides[0]], overflow[sides[1]]);
287
+ }
288
+ overflowsData = [...overflowsData, {
289
+ placement,
290
+ overflows
291
+ }];
292
+
293
+ // One or more sides is overflowing.
294
+ if (!overflows.every(side => side <= 0)) {
295
+ var _middlewareData$flip2, _overflowsData$filter;
296
+ const nextIndex = (((_middlewareData$flip2 = middlewareData.flip) == null ? void 0 : _middlewareData$flip2.index) || 0) + 1;
297
+ const nextPlacement = placements[nextIndex];
298
+ if (nextPlacement) {
299
+ const ignoreCrossAxisOverflow = checkCrossAxis === 'alignment' ? initialSideAxis !== getSideAxis(nextPlacement) : false;
300
+ if (!ignoreCrossAxisOverflow ||
301
+ // We leave the current main axis only if every placement on that axis
302
+ // overflows the main axis.
303
+ overflowsData.every(d => getSideAxis(d.placement) === initialSideAxis ? d.overflows[0] > 0 : true)) {
304
+ // Try next placement and re-run the lifecycle.
305
+ return {
306
+ data: {
307
+ index: nextIndex,
308
+ overflows: overflowsData
309
+ },
310
+ reset: {
311
+ placement: nextPlacement
312
+ }
313
+ };
314
+ }
315
+ }
316
+
317
+ // First, find the candidates that fit on the mainAxis side of overflow,
318
+ // then find the placement that fits the best on the main crossAxis side.
319
+ let resetPlacement = (_overflowsData$filter = overflowsData.filter(d => d.overflows[0] <= 0).sort((a, b) => a.overflows[1] - b.overflows[1])[0]) == null ? void 0 : _overflowsData$filter.placement;
320
+
321
+ // Otherwise fallback.
322
+ if (!resetPlacement) {
323
+ switch (fallbackStrategy) {
324
+ case 'bestFit':
325
+ {
326
+ var _overflowsData$filter2;
327
+ const placement = (_overflowsData$filter2 = overflowsData.filter(d => {
328
+ if (hasFallbackAxisSideDirection) {
329
+ const currentSideAxis = getSideAxis(d.placement);
330
+ return currentSideAxis === initialSideAxis ||
331
+ // Create a bias to the `y` side axis due to horizontal
332
+ // reading directions favoring greater width.
333
+ currentSideAxis === 'y';
334
+ }
335
+ return true;
336
+ }).map(d => [d.placement, d.overflows.filter(overflow => overflow > 0).reduce((acc, overflow) => acc + overflow, 0)]).sort((a, b) => a[1] - b[1])[0]) == null ? void 0 : _overflowsData$filter2[0];
337
+ if (placement) {
338
+ resetPlacement = placement;
339
+ }
340
+ break;
341
+ }
342
+ case 'initialPlacement':
343
+ resetPlacement = initialPlacement;
344
+ break;
345
+ }
346
+ }
347
+ if (placement !== resetPlacement) {
348
+ return {
349
+ reset: {
350
+ placement: resetPlacement
351
+ }
352
+ };
353
+ }
354
+ }
355
+ return {};
356
+ }
357
+ };
358
+ };
359
+
360
+ const originSides = /*#__PURE__*/new Set(['left', 'top']);
361
+
362
+ // For type backwards-compatibility, the `OffsetOptions` type was also
363
+ // Derivable.
364
+
365
+ async function convertValueToCoords(state, options) {
366
+ const {
367
+ placement,
368
+ platform,
369
+ elements
370
+ } = state;
371
+ const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));
372
+ const side = getSide(placement);
373
+ const alignment = getAlignment(placement);
374
+ const isVertical = getSideAxis(placement) === 'y';
375
+ const mainAxisMulti = originSides.has(side) ? -1 : 1;
376
+ const crossAxisMulti = rtl && isVertical ? -1 : 1;
377
+ const rawValue = evaluate(options, state);
378
+
379
+ // eslint-disable-next-line prefer-const
380
+ let {
381
+ mainAxis,
382
+ crossAxis,
383
+ alignmentAxis
384
+ } = typeof rawValue === 'number' ? {
385
+ mainAxis: rawValue,
386
+ crossAxis: 0,
387
+ alignmentAxis: null
388
+ } : {
389
+ mainAxis: rawValue.mainAxis || 0,
390
+ crossAxis: rawValue.crossAxis || 0,
391
+ alignmentAxis: rawValue.alignmentAxis
392
+ };
393
+ if (alignment && typeof alignmentAxis === 'number') {
394
+ crossAxis = alignment === 'end' ? alignmentAxis * -1 : alignmentAxis;
395
+ }
396
+ return isVertical ? {
397
+ x: crossAxis * crossAxisMulti,
398
+ y: mainAxis * mainAxisMulti
399
+ } : {
400
+ x: mainAxis * mainAxisMulti,
401
+ y: crossAxis * crossAxisMulti
402
+ };
403
+ }
404
+
405
+ /**
406
+ * Modifies the placement by translating the floating element along the
407
+ * specified axes.
408
+ * A number (shorthand for `mainAxis` or distance), or an axes configuration
409
+ * object may be passed.
410
+ * @see https://floating-ui.com/docs/offset
411
+ */
412
+ const offset$2 = function (options) {
413
+ if (options === void 0) {
414
+ options = 0;
415
+ }
416
+ return {
417
+ name: 'offset',
418
+ options,
419
+ async fn(state) {
420
+ var _middlewareData$offse, _middlewareData$arrow;
421
+ const {
422
+ x,
423
+ y,
424
+ placement,
425
+ middlewareData
426
+ } = state;
427
+ const diffCoords = await convertValueToCoords(state, options);
428
+
429
+ // If the placement is the same and the arrow caused an alignment offset
430
+ // then we don't need to change the positioning coordinates.
431
+ if (placement === ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse.placement) && (_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {
432
+ return {};
433
+ }
434
+ return {
435
+ x: x + diffCoords.x,
436
+ y: y + diffCoords.y,
437
+ data: {
438
+ ...diffCoords,
439
+ placement
440
+ }
441
+ };
442
+ }
443
+ };
444
+ };
445
+
446
+ /**
447
+ * Optimizes the visibility of the floating element by shifting it in order to
448
+ * keep it in view when it will overflow the clipping boundary.
449
+ * @see https://floating-ui.com/docs/shift
450
+ */
451
+ const shift$2 = function (options) {
452
+ if (options === void 0) {
453
+ options = {};
454
+ }
455
+ return {
456
+ name: 'shift',
457
+ options,
458
+ async fn(state) {
459
+ const {
460
+ x,
461
+ y,
462
+ placement,
463
+ platform
464
+ } = state;
465
+ const {
466
+ mainAxis: checkMainAxis = true,
467
+ crossAxis: checkCrossAxis = false,
468
+ limiter = {
469
+ fn: _ref => {
470
+ let {
471
+ x,
472
+ y
473
+ } = _ref;
474
+ return {
475
+ x,
476
+ y
477
+ };
478
+ }
479
+ },
480
+ ...detectOverflowOptions
481
+ } = evaluate(options, state);
482
+ const coords = {
483
+ x,
484
+ y
485
+ };
486
+ const overflow = await platform.detectOverflow(state, detectOverflowOptions);
487
+ const crossAxis = getSideAxis(placement);
488
+ const mainAxis = getOppositeAxis(crossAxis);
489
+ let mainAxisCoord = coords[mainAxis];
490
+ let crossAxisCoord = coords[crossAxis];
491
+ const clampCoord = (axis, coord) => clamp(coord + overflow[axis === 'y' ? 'top' : 'left'], coord, coord - overflow[axis === 'y' ? 'bottom' : 'right']);
492
+ if (checkMainAxis) {
493
+ mainAxisCoord = clampCoord(mainAxis, mainAxisCoord);
494
+ }
495
+ if (checkCrossAxis) {
496
+ crossAxisCoord = clampCoord(crossAxis, crossAxisCoord);
497
+ }
498
+ const limitedCoords = limiter.fn({
499
+ ...state,
500
+ [mainAxis]: mainAxisCoord,
501
+ [crossAxis]: crossAxisCoord
502
+ });
503
+ return {
504
+ ...limitedCoords,
505
+ data: {
506
+ x: limitedCoords.x - x,
507
+ y: limitedCoords.y - y,
508
+ enabled: {
509
+ [mainAxis]: checkMainAxis,
510
+ [crossAxis]: checkCrossAxis
511
+ }
512
+ }
513
+ };
514
+ }
515
+ };
516
+ };
517
+ /**
518
+ * Built-in `limiter` that will stop `shift()` at a certain point.
519
+ */
520
+ const limitShift$2 = function (options) {
521
+ if (options === void 0) {
522
+ options = {};
523
+ }
524
+ return {
525
+ options,
526
+ fn(state) {
527
+ var _rawOffset$mainAxis, _rawOffset$crossAxis;
528
+ const {
529
+ x,
530
+ y,
531
+ placement,
532
+ rects,
533
+ middlewareData
534
+ } = state;
535
+ const {
536
+ offset = 0,
537
+ mainAxis: checkMainAxis = true,
538
+ crossAxis: checkCrossAxis = true
539
+ } = evaluate(options, state);
540
+ const coords = {
541
+ x,
542
+ y
543
+ };
544
+ const crossAxis = getSideAxis(placement);
545
+ const mainAxis = getOppositeAxis(crossAxis);
546
+ let mainAxisCoord = coords[mainAxis];
547
+ let crossAxisCoord = coords[crossAxis];
548
+ const rawOffset = evaluate(offset, state);
549
+ const computedOffset = typeof rawOffset === 'number' ? {
550
+ mainAxis: rawOffset,
551
+ crossAxis: 0
552
+ } : {
553
+ mainAxis: (_rawOffset$mainAxis = rawOffset.mainAxis) != null ? _rawOffset$mainAxis : 0,
554
+ crossAxis: (_rawOffset$crossAxis = rawOffset.crossAxis) != null ? _rawOffset$crossAxis : 0
555
+ };
556
+ if (checkMainAxis) {
557
+ const len = mainAxis === 'y' ? 'height' : 'width';
558
+ const limitMin = rects.reference[mainAxis] - rects.floating[len] + computedOffset.mainAxis;
559
+ const limitMax = rects.reference[mainAxis] + rects.reference[len] - computedOffset.mainAxis;
560
+ if (mainAxisCoord < limitMin) {
561
+ mainAxisCoord = limitMin;
562
+ } else if (mainAxisCoord > limitMax) {
563
+ mainAxisCoord = limitMax;
564
+ }
565
+ }
566
+ if (checkCrossAxis) {
567
+ var _middlewareData$offse, _middlewareData$offse2;
568
+ const len = mainAxis === 'y' ? 'width' : 'height';
569
+ const isOriginSide = originSides.has(getSide(placement));
570
+ const limitMin = rects.reference[crossAxis] - rects.floating[len] + (isOriginSide ? ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse[crossAxis]) || 0 : 0) + (isOriginSide ? 0 : computedOffset.crossAxis);
571
+ const limitMax = rects.reference[crossAxis] + rects.reference[len] + (isOriginSide ? 0 : ((_middlewareData$offse2 = middlewareData.offset) == null ? void 0 : _middlewareData$offse2[crossAxis]) || 0) - (isOriginSide ? computedOffset.crossAxis : 0);
572
+ if (crossAxisCoord < limitMin) {
573
+ crossAxisCoord = limitMin;
574
+ } else if (crossAxisCoord > limitMax) {
575
+ crossAxisCoord = limitMax;
576
+ }
577
+ }
578
+ return {
579
+ [mainAxis]: mainAxisCoord,
580
+ [crossAxis]: crossAxisCoord
581
+ };
582
+ }
583
+ };
584
+ };
585
+
586
+ // Method syntax keeps callback parameters bivariant, but expressing the
587
+ // explicit `| undefined` required by `exactOptionalPropertyTypes` needs
588
+ // property syntax, which is contravariant under `strictFunctionTypes`.
589
+ // Extracting the function from a method position restores that bivariance so
590
+ // consumers can still assign callbacks with narrower parameter types.
591
+
592
+ /**
593
+ * Provides data that allows you to change the size of the floating element —
594
+ * for instance, prevent it from overflowing the clipping boundary or match the
595
+ * width of the reference element.
596
+ * @see https://floating-ui.com/docs/size
597
+ */
598
+ const size$2 = function (options) {
599
+ if (options === void 0) {
600
+ options = {};
601
+ }
602
+ return {
603
+ name: 'size',
604
+ options,
605
+ async fn(state) {
606
+ const {
607
+ placement,
608
+ rects,
609
+ platform,
610
+ elements
611
+ } = state;
612
+ const {
613
+ apply = () => {},
614
+ ...detectOverflowOptions
615
+ } = evaluate(options, state);
616
+ const overflow = await platform.detectOverflow(state, detectOverflowOptions);
617
+ const side = getSide(placement);
618
+ const alignment = getAlignment(placement);
619
+ const isYAxis = getSideAxis(placement) === 'y';
620
+ const {
621
+ width,
622
+ height
623
+ } = rects.floating;
624
+ let heightSide;
625
+ let widthSide;
626
+ if (side === 'top' || side === 'bottom') {
627
+ heightSide = side;
628
+ widthSide = alignment === ((await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating))) ? 'start' : 'end') ? 'left' : 'right';
629
+ } else {
630
+ widthSide = side;
631
+ heightSide = alignment === 'end' ? 'top' : 'bottom';
632
+ }
633
+ const maximumClippingHeight = height - overflow.top - overflow.bottom;
634
+ const maximumClippingWidth = width - overflow.left - overflow.right;
635
+ const overflowAvailableHeight = min(height - overflow[heightSide], maximumClippingHeight);
636
+ const overflowAvailableWidth = min(width - overflow[widthSide], maximumClippingWidth);
637
+ const shiftData = state.middlewareData.shift;
638
+ const noShift = !shiftData;
639
+ let availableHeight = overflowAvailableHeight;
640
+ let availableWidth = overflowAvailableWidth;
641
+ if (shiftData != null && shiftData.enabled.x) {
642
+ availableWidth = maximumClippingWidth;
643
+ }
644
+ if (shiftData != null && shiftData.enabled.y) {
645
+ availableHeight = maximumClippingHeight;
646
+ }
647
+ if (noShift && !alignment) {
648
+ if (isYAxis) {
649
+ availableWidth = width - 2 * max(overflow.left, overflow.right);
650
+ } else {
651
+ availableHeight = height - 2 * max(overflow.top, overflow.bottom);
652
+ }
653
+ }
654
+ await apply({
655
+ ...state,
656
+ availableWidth,
657
+ availableHeight
658
+ });
659
+ const nextDimensions = await platform.getDimensions(elements.floating);
660
+ if (width !== nextDimensions.width || height !== nextDimensions.height) {
661
+ return {
662
+ reset: {
663
+ rects: true
664
+ }
665
+ };
666
+ }
667
+ return {};
668
+ }
669
+ };
670
+ };
671
+
672
+ function getCssDimensions(element) {
673
+ const css = getComputedStyle$1(element);
674
+ // In testing environments, the `width` and `height` properties are empty
675
+ // strings for SVG elements, returning NaN. Fallback to `0` in this case.
676
+ let width = parseFloat(css.width) || 0;
677
+ let height = parseFloat(css.height) || 0;
678
+ const hasOffset = isHTMLElement(element);
679
+ const offsetWidth = hasOffset ? element.offsetWidth : width;
680
+ const offsetHeight = hasOffset ? element.offsetHeight : height;
681
+ const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;
682
+ if (shouldFallback) {
683
+ width = offsetWidth;
684
+ height = offsetHeight;
685
+ }
686
+ return {
687
+ width,
688
+ height,
689
+ $: shouldFallback
690
+ };
691
+ }
692
+
693
+ function unwrapElement(element) {
694
+ return !isElement(element) ? element.contextElement : element;
695
+ }
696
+
697
+ function getScale(element) {
698
+ const domElement = unwrapElement(element);
699
+ if (!isHTMLElement(domElement)) {
700
+ return createCoords(1);
701
+ }
702
+ const rect = domElement.getBoundingClientRect();
703
+ const {
704
+ width,
705
+ height,
706
+ $
707
+ } = getCssDimensions(domElement);
708
+ let x = ($ ? round(rect.width) : rect.width) / width;
709
+ let y = ($ ? round(rect.height) : rect.height) / height;
710
+
711
+ // 0, NaN, or Infinity should always fallback to 1.
712
+
713
+ if (!x || !Number.isFinite(x)) {
714
+ x = 1;
715
+ }
716
+ if (!y || !Number.isFinite(y)) {
717
+ y = 1;
718
+ }
719
+ return {
720
+ x,
721
+ y
722
+ };
723
+ }
724
+
725
+ const noOffsets = /*#__PURE__*/createCoords(0);
726
+ function getVisualOffsets(element) {
727
+ const win = getWindow(element);
728
+ if (!isWebKit() || !win.visualViewport) {
729
+ return noOffsets;
730
+ }
731
+ return {
732
+ x: win.visualViewport.offsetLeft,
733
+ y: win.visualViewport.offsetTop
734
+ };
735
+ }
736
+ function shouldAddVisualOffsets(element, isFixed, floatingOffsetParent) {
737
+ if (isFixed === void 0) {
738
+ isFixed = false;
739
+ }
740
+ return !!floatingOffsetParent && isFixed && floatingOffsetParent === getWindow(element);
741
+ }
742
+
743
+ function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) {
744
+ if (includeScale === void 0) {
745
+ includeScale = false;
746
+ }
747
+ if (isFixedStrategy === void 0) {
748
+ isFixedStrategy = false;
749
+ }
750
+ const clientRect = element.getBoundingClientRect();
751
+ const domElement = unwrapElement(element);
752
+ let scale = createCoords(1);
753
+ if (includeScale) {
754
+ if (offsetParent) {
755
+ if (isElement(offsetParent)) {
756
+ scale = getScale(offsetParent);
757
+ }
758
+ } else {
759
+ scale = getScale(element);
760
+ }
761
+ }
762
+ const visualOffsets = shouldAddVisualOffsets(domElement, isFixedStrategy, offsetParent) ? getVisualOffsets(domElement) : createCoords(0);
763
+ let x = (clientRect.left + visualOffsets.x) / scale.x;
764
+ let y = (clientRect.top + visualOffsets.y) / scale.y;
765
+ let width = clientRect.width / scale.x;
766
+ let height = clientRect.height / scale.y;
767
+ if (domElement && offsetParent) {
768
+ const win = getWindow(domElement);
769
+ const offsetWin = isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;
770
+ let currentWin = win;
771
+ let currentIFrame = getFrameElement(currentWin);
772
+ while (currentIFrame && offsetWin !== currentWin) {
773
+ const iframeScale = getScale(currentIFrame);
774
+ const iframeRect = currentIFrame.getBoundingClientRect();
775
+ const css = getComputedStyle$1(currentIFrame);
776
+ const left = iframeRect.left + (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;
777
+ const top = iframeRect.top + (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;
778
+ x *= iframeScale.x;
779
+ y *= iframeScale.y;
780
+ width *= iframeScale.x;
781
+ height *= iframeScale.y;
782
+ x += left;
783
+ y += top;
784
+ currentWin = getWindow(currentIFrame);
785
+ currentIFrame = getFrameElement(currentWin);
786
+ }
787
+ }
788
+ return rectToClientRect({
789
+ width,
790
+ height,
791
+ x,
792
+ y
793
+ });
794
+ }
795
+
796
+ // If <html> has a CSS width greater than the viewport, then this will be
797
+ // incorrect for RTL.
798
+ function getWindowScrollBarX(element, rect) {
799
+ const leftScroll = getNodeScroll(element).scrollLeft;
800
+ if (!rect) {
801
+ return getBoundingClientRect(getDocumentElement(element)).left + leftScroll;
802
+ }
803
+ return rect.left + leftScroll;
804
+ }
805
+
806
+ function getHTMLOffset(documentElement, scroll) {
807
+ const htmlRect = documentElement.getBoundingClientRect();
808
+ const x = htmlRect.left + scroll.scrollLeft - getWindowScrollBarX(documentElement, htmlRect);
809
+ const y = htmlRect.top + scroll.scrollTop;
810
+ return {
811
+ x,
812
+ y
813
+ };
814
+ }
815
+
816
+ function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {
817
+ let {
818
+ elements,
819
+ rect,
820
+ offsetParent,
821
+ strategy
822
+ } = _ref;
823
+ const isFixed = strategy === 'fixed';
824
+ const documentElement = getDocumentElement(offsetParent);
825
+ const topLayer = elements ? isTopLayer(elements.floating) : false;
826
+ if (offsetParent === documentElement || topLayer && isFixed) {
827
+ return rect;
828
+ }
829
+ let scroll = {
830
+ scrollLeft: 0,
831
+ scrollTop: 0
832
+ };
833
+ let scale = createCoords(1);
834
+ const offsets = createCoords(0);
835
+ const isOffsetParentAnElement = isHTMLElement(offsetParent);
836
+ if (isOffsetParentAnElement || !isFixed) {
837
+ if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
838
+ scroll = getNodeScroll(offsetParent);
839
+ }
840
+ if (isOffsetParentAnElement) {
841
+ const offsetRect = getBoundingClientRect(offsetParent);
842
+ scale = getScale(offsetParent);
843
+ offsets.x = offsetRect.x + offsetParent.clientLeft;
844
+ offsets.y = offsetRect.y + offsetParent.clientTop;
845
+ }
846
+ }
847
+ const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);
848
+ return {
849
+ width: rect.width * scale.x,
850
+ height: rect.height * scale.y,
851
+ x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x + htmlOffset.x,
852
+ y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y + htmlOffset.y
853
+ };
854
+ }
855
+
856
+ function getClientRects(element) {
857
+ return element.getClientRects ? Array.from(element.getClientRects()) : [];
858
+ }
859
+
860
+ // Gets the entire size of the scrollable document area, even extending outside
861
+ // of the `<html>` and `<body>` rect bounds if horizontally scrollable.
862
+ function getDocumentRect(html) {
863
+ const scroll = getNodeScroll(html);
864
+ const body = html.ownerDocument.body;
865
+ const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth);
866
+ const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight);
867
+ let x = -scroll.scrollLeft + getWindowScrollBarX(html);
868
+ const y = -scroll.scrollTop;
869
+ if (getComputedStyle$1(body).direction === 'rtl') {
870
+ x += max(html.clientWidth, body.clientWidth) - width;
871
+ }
872
+ return {
873
+ width,
874
+ height,
875
+ x,
876
+ y
877
+ };
878
+ }
879
+
880
+ // Safety check: ensure the scrollbar space is reasonable in case this
881
+ // calculation is affected by unusual styles.
882
+ // Most scrollbars leave 15-18px of space.
883
+ const SCROLLBAR_MAX = 25;
884
+ function getViewportRect(element, strategy, rootBoundary) {
885
+ if (rootBoundary === void 0) {
886
+ rootBoundary = 'viewport';
887
+ }
888
+ const isLayoutViewport = rootBoundary === 'layoutViewport';
889
+ const win = getWindow(element);
890
+ const html = getDocumentElement(element);
891
+ const visualViewport = win.visualViewport;
892
+ let width = html.clientWidth;
893
+ let height = html.clientHeight;
894
+ let x = 0;
895
+ let y = 0;
896
+ if (visualViewport) {
897
+ // Client coordinates are relative to the layout viewport, except in
898
+ // WebKit with an `absolute` strategy, where they are relative to the
899
+ // visual viewport.
900
+ const layoutRelativeClientCoords = !isWebKit() || strategy === 'fixed';
901
+ if (isLayoutViewport) {
902
+ if (!layoutRelativeClientCoords) {
903
+ x = -visualViewport.offsetLeft;
904
+ y = -visualViewport.offsetTop;
905
+ }
906
+ } else {
907
+ width = visualViewport.width;
908
+ height = visualViewport.height;
909
+ if (layoutRelativeClientCoords) {
910
+ x = visualViewport.offsetLeft;
911
+ y = visualViewport.offsetTop;
912
+ }
913
+ }
914
+ }
915
+ const windowScrollbarX = getWindowScrollBarX(html);
916
+ // `scrollbar-gutter: stable` on the <html> reserves gutter space that shrinks
917
+ // the visual width but isn't reflected in `html.clientWidth`, so subtract it.
918
+ // Only the inline-end (right) gutter can hold the scrollbar; `both-edges` also
919
+ // reserves an empty inline-start gutter that clips nothing, so exclude just
920
+ // the one scrollbar-side gutter — halve the measured (two-gutter) total. A
921
+ // left-side scrollbar (`windowScrollbarX > 0`) is already handled by
922
+ // `getHTMLOffset`/`visualViewport.width`; skip it here.
923
+ if (windowScrollbarX <= 0) {
924
+ const doc = html.ownerDocument;
925
+ const body = doc.body;
926
+ const bodyStyles = getComputedStyle(body);
927
+ const bodyMarginInline = doc.compatMode === 'CSS1Compat' ? parseFloat(bodyStyles.marginLeft) + parseFloat(bodyStyles.marginRight) || 0 : 0;
928
+ const reservedWidth = Math.abs(html.clientWidth - body.clientWidth - bodyMarginInline);
929
+ const gutter = getComputedStyle(html).scrollbarGutter === 'stable both-edges' ? reservedWidth / 2 : reservedWidth;
930
+ if (gutter <= SCROLLBAR_MAX) {
931
+ width -= gutter;
932
+ }
933
+ }
934
+ return {
935
+ width,
936
+ height,
937
+ x,
938
+ y
939
+ };
940
+ }
941
+
942
+ // Returns the inner client rect, subtracting scrollbars if present.
943
+ function getInnerBoundingClientRect(element, strategy) {
944
+ const clientRect = getBoundingClientRect(element, true, strategy === 'fixed');
945
+ const top = clientRect.top + element.clientTop;
946
+ const left = clientRect.left + element.clientLeft;
947
+ const scale = getScale(element);
948
+ const width = element.clientWidth * scale.x;
949
+ const height = element.clientHeight * scale.y;
950
+ const x = left * scale.x;
951
+ const y = top * scale.y;
952
+ return {
953
+ width,
954
+ height,
955
+ x,
956
+ y
957
+ };
958
+ }
959
+ function getClientRectFromClippingAncestor(element, clippingAncestor, strategy) {
960
+ let rect;
961
+ if (clippingAncestor === 'viewport' || clippingAncestor === 'layoutViewport') {
962
+ rect = getViewportRect(element, strategy, clippingAncestor);
963
+ } else if (clippingAncestor === 'document') {
964
+ rect = getDocumentRect(getDocumentElement(element));
965
+ } else if (isElement(clippingAncestor)) {
966
+ rect = getInnerBoundingClientRect(clippingAncestor, strategy);
967
+ } else {
968
+ const visualOffsets = getVisualOffsets(element);
969
+ rect = {
970
+ x: clippingAncestor.x - visualOffsets.x,
971
+ y: clippingAncestor.y - visualOffsets.y,
972
+ width: clippingAncestor.width,
973
+ height: clippingAncestor.height
974
+ };
975
+ }
976
+ return rectToClientRect(rect);
977
+ }
978
+
979
+ // A "clipping ancestor" is an `overflow` element with the characteristic of
980
+ // clipping (or hiding) child elements. This returns all clipping ancestors
981
+ // of the given element up the tree.
982
+ function getClippingElementAncestors(element, cache) {
983
+ const cachedResult = cache.get(element);
984
+ if (cachedResult) {
985
+ return cachedResult;
986
+ }
987
+ let result = getOverflowAncestors(element, [], false).filter(el => isElement(el) && getNodeName(el) !== 'body');
988
+ let lastKeptComputedStyle = null;
989
+ const elementIsFixed = getComputedStyle$1(element).position === 'fixed';
990
+ let currentNode = elementIsFixed ? getParentNode(element) : element;
991
+
992
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
993
+ while (isElement(currentNode) && !isLastTraversableNode(currentNode)) {
994
+ const computedStyle = getComputedStyle$1(currentNode);
995
+ const currentNodeIsContaining = isContainingBlock(currentNode);
996
+ // Position of the containing block chain below the current node. A fixed
997
+ // element whose containing block hasn't been found yet is a fixed chain.
998
+ const lastPosition = lastKeptComputedStyle ? lastKeptComputedStyle.position : elementIsFixed ? 'fixed' : '';
999
+
1000
+ // A non-containing ancestor does not clip the element when the chain
1001
+ // below it escapes it: a fixed chain escapes all ancestors up to the
1002
+ // next containing block, an absolute chain escapes static ancestors.
1003
+ const shouldDropCurrentNode = !currentNodeIsContaining && (lastPosition === 'fixed' || lastPosition === 'absolute' && computedStyle.position === 'static');
1004
+ if (shouldDropCurrentNode) {
1005
+ // Drop non-containing blocks.
1006
+ result = result.filter(ancestor => ancestor !== currentNode);
1007
+ } else {
1008
+ // The kept node carries the chain position for the next iteration.
1009
+ lastKeptComputedStyle = computedStyle;
1010
+ }
1011
+ currentNode = getParentNode(currentNode);
1012
+ }
1013
+ cache.set(element, result);
1014
+ return result;
1015
+ }
1016
+
1017
+ // Gets the maximum area that the element is visible in due to any number of
1018
+ // clipping ancestors.
1019
+ function getClippingRect(_ref) {
1020
+ let {
1021
+ element,
1022
+ boundary,
1023
+ rootBoundary,
1024
+ strategy
1025
+ } = _ref;
1026
+ const elementClippingAncestors = boundary === 'clippingAncestors' ? isTopLayer(element) ? [] : getClippingElementAncestors(element, this._c) : [].concat(boundary);
1027
+ const clippingAncestors = [...elementClippingAncestors, rootBoundary];
1028
+ const firstRect = getClientRectFromClippingAncestor(element, clippingAncestors[0], strategy);
1029
+ let top = firstRect.top;
1030
+ let right = firstRect.right;
1031
+ let bottom = firstRect.bottom;
1032
+ let left = firstRect.left;
1033
+ for (let i = 1; i < clippingAncestors.length; i++) {
1034
+ const rect = getClientRectFromClippingAncestor(element, clippingAncestors[i], strategy);
1035
+ top = max(rect.top, top);
1036
+ right = min(rect.right, right);
1037
+ bottom = min(rect.bottom, bottom);
1038
+ left = max(rect.left, left);
1039
+ }
1040
+ return {
1041
+ width: right - left,
1042
+ height: bottom - top,
1043
+ x: left,
1044
+ y: top
1045
+ };
1046
+ }
1047
+
1048
+ function getDimensions(element) {
1049
+ const {
1050
+ width,
1051
+ height
1052
+ } = getCssDimensions(element);
1053
+ return {
1054
+ width,
1055
+ height
1056
+ };
1057
+ }
1058
+
1059
+ function getRectRelativeToOffsetParent(element, offsetParent, strategy) {
1060
+ const isOffsetParentAnElement = isHTMLElement(offsetParent);
1061
+ const documentElement = getDocumentElement(offsetParent);
1062
+ const isFixed = strategy === 'fixed';
1063
+ const rect = getBoundingClientRect(element, true, isFixed, offsetParent);
1064
+ let scroll = {
1065
+ scrollLeft: 0,
1066
+ scrollTop: 0
1067
+ };
1068
+ const offsets = createCoords(0);
1069
+ if (isOffsetParentAnElement || !isFixed) {
1070
+ if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
1071
+ scroll = getNodeScroll(offsetParent);
1072
+ }
1073
+ if (isOffsetParentAnElement) {
1074
+ const offsetRect = getBoundingClientRect(offsetParent, true, isFixed, offsetParent);
1075
+ offsets.x = offsetRect.x + offsetParent.clientLeft;
1076
+ offsets.y = offsetRect.y + offsetParent.clientTop;
1077
+ }
1078
+ }
1079
+
1080
+ // If the <body> scrollbar appears on the left (e.g. RTL systems). Use
1081
+ // Firefox with layout.scrollbar.side = 3 in about:config to test this.
1082
+ if (!isOffsetParentAnElement && documentElement) {
1083
+ offsets.x = getWindowScrollBarX(documentElement);
1084
+ }
1085
+ const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);
1086
+ const x = rect.left + scroll.scrollLeft - offsets.x - htmlOffset.x;
1087
+ const y = rect.top + scroll.scrollTop - offsets.y - htmlOffset.y;
1088
+ return {
1089
+ x,
1090
+ y,
1091
+ width: rect.width,
1092
+ height: rect.height
1093
+ };
1094
+ }
1095
+
1096
+ function isStaticPositioned(element) {
1097
+ return getComputedStyle$1(element).position === 'static';
1098
+ }
1099
+
1100
+ function getTrueOffsetParent(element, polyfill) {
1101
+ if (!isHTMLElement(element) || getComputedStyle$1(element).position === 'fixed') {
1102
+ return null;
1103
+ }
1104
+ if (polyfill) {
1105
+ return polyfill(element);
1106
+ }
1107
+ let rawOffsetParent = element.offsetParent;
1108
+
1109
+ // Firefox returns the <html> element as the offsetParent if it's non-static,
1110
+ // while Chrome and Safari return the <body> element. The <body> element must
1111
+ // be used to perform the correct calculations even if the <html> element is
1112
+ // non-static.
1113
+ if (getDocumentElement(element) === rawOffsetParent) {
1114
+ rawOffsetParent = rawOffsetParent.ownerDocument.body;
1115
+ }
1116
+ return rawOffsetParent;
1117
+ }
1118
+
1119
+ // Gets the closest ancestor positioned element. Handles some edge cases,
1120
+ // such as table ancestors and cross browser bugs.
1121
+ function getOffsetParent(element, polyfill) {
1122
+ const win = getWindow(element);
1123
+ if (isTopLayer(element)) {
1124
+ return win;
1125
+ }
1126
+ if (!isHTMLElement(element)) {
1127
+ let svgOffsetParent = getParentNode(element);
1128
+ while (svgOffsetParent && !isLastTraversableNode(svgOffsetParent)) {
1129
+ if (isElement(svgOffsetParent) && !isStaticPositioned(svgOffsetParent)) {
1130
+ return svgOffsetParent;
1131
+ }
1132
+ svgOffsetParent = getParentNode(svgOffsetParent);
1133
+ }
1134
+ return win;
1135
+ }
1136
+ let offsetParent = getTrueOffsetParent(element, polyfill);
1137
+ while (offsetParent && isTableElement(offsetParent) && isStaticPositioned(offsetParent)) {
1138
+ offsetParent = getTrueOffsetParent(offsetParent, polyfill);
1139
+ }
1140
+ if (offsetParent && isLastTraversableNode(offsetParent) && isStaticPositioned(offsetParent) && !isContainingBlock(offsetParent)) {
1141
+ return win;
1142
+ }
1143
+ return offsetParent || getContainingBlock(element) || win;
1144
+ }
1145
+
1146
+ const getElementRects = async function (data) {
1147
+ const getOffsetParentFn = this.getOffsetParent || getOffsetParent;
1148
+ const getDimensionsFn = this.getDimensions;
1149
+ const floatingDimensions = await getDimensionsFn(data.floating);
1150
+ return {
1151
+ reference: getRectRelativeToOffsetParent(data.reference, await getOffsetParentFn(data.floating), data.strategy),
1152
+ floating: {
1153
+ x: 0,
1154
+ y: 0,
1155
+ width: floatingDimensions.width,
1156
+ height: floatingDimensions.height
1157
+ }
1158
+ };
1159
+ };
1160
+
1161
+ function isRTL(element) {
1162
+ return getComputedStyle$1(element).direction === 'rtl';
1163
+ }
1164
+
1165
+ const platform = {
1166
+ convertOffsetParentRelativeRectToViewportRelativeRect,
1167
+ getDocumentElement,
1168
+ getClippingRect,
1169
+ getOffsetParent,
1170
+ getElementRects,
1171
+ getClientRects,
1172
+ getDimensions,
1173
+ getScale,
1174
+ isElement,
1175
+ isRTL
1176
+ };
1177
+
1178
+ function rectsAreEqual(a, b) {
1179
+ return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
1180
+ }
1181
+
1182
+ // https://samthor.au/2021/observing-dom/
1183
+ function observeMove(element, onMove, ancestorResize) {
1184
+ let io = null;
1185
+ let timeoutId;
1186
+ const root = getDocumentElement(element);
1187
+ function cleanup() {
1188
+ var _io;
1189
+ clearTimeout(timeoutId);
1190
+ (_io = io) == null || _io.disconnect();
1191
+ io = null;
1192
+ }
1193
+ function refresh(skip, threshold) {
1194
+ if (skip === void 0) {
1195
+ skip = false;
1196
+ }
1197
+ if (threshold === void 0) {
1198
+ threshold = 1;
1199
+ }
1200
+ cleanup();
1201
+ const elementRectForRootMargin = element.getBoundingClientRect();
1202
+ const {
1203
+ left,
1204
+ top,
1205
+ width,
1206
+ height
1207
+ } = elementRectForRootMargin;
1208
+ if (!skip) {
1209
+ onMove();
1210
+ }
1211
+ if (!width || !height) {
1212
+ return;
1213
+ }
1214
+ const insetTop = floor(top);
1215
+ const insetRight = floor(root.clientWidth - (left + width));
1216
+ const insetBottom = floor(root.clientHeight - (top + height));
1217
+ const insetLeft = floor(left);
1218
+ const rootMargin = -insetTop + "px " + -insetRight + "px " + -insetBottom + "px " + -insetLeft + "px";
1219
+ const options = {
1220
+ rootMargin,
1221
+ threshold: max(0, min(1, threshold)) || 1
1222
+ };
1223
+ let isFirstUpdate = true;
1224
+ function handleObserve(entries) {
1225
+ const ratio = entries[0].intersectionRatio;
1226
+
1227
+ // The entry is a snapshot, so the reference may have moved since the
1228
+ // intersection was computed (under performance constraints, or between
1229
+ // consecutive frames of a multi-frame layout shift). The reported ratio
1230
+ // and the observed area are stale in that case and cannot be trusted to
1231
+ // detect subsequent movement, so refresh regardless of the ratio.
1232
+ if (!rectsAreEqual(elementRectForRootMargin, element.getBoundingClientRect())) {
1233
+ return refresh();
1234
+ }
1235
+ if (ratio !== threshold) {
1236
+ if (!isFirstUpdate) {
1237
+ return refresh();
1238
+ }
1239
+ if (!ratio) {
1240
+ // If the reference is clipped in place, the ratio is 0. Throttle
1241
+ // the refresh to prevent an infinite loop of updates.
1242
+ timeoutId = setTimeout(() => {
1243
+ refresh(false, 1e-7);
1244
+ }, 1000);
1245
+ } else {
1246
+ refresh(false, ratio);
1247
+ }
1248
+ }
1249
+ isFirstUpdate = false;
1250
+ }
1251
+
1252
+ // Older browsers don't support a `document` as the root and will throw an
1253
+ // error.
1254
+ try {
1255
+ io = new IntersectionObserver(handleObserve, {
1256
+ ...options,
1257
+ // Handle <iframe>s
1258
+ root: root.ownerDocument
1259
+ });
1260
+ } catch (_e) {
1261
+ io = new IntersectionObserver(handleObserve, options);
1262
+ }
1263
+ io.observe(element);
1264
+ }
1265
+ const win = getWindow(element);
1266
+ // The window is a resize ancestor, so when `ancestorResize` is enabled its
1267
+ // listener already runs the update on resize. Here we only need to rebuild
1268
+ // the `IntersectionObserver` for the new root size, skipping a redundant
1269
+ // update. When `ancestorResize` is disabled, this becomes the sole update.
1270
+ const handleResize = () => refresh(ancestorResize);
1271
+ win.addEventListener('resize', handleResize);
1272
+ refresh(true);
1273
+ return () => {
1274
+ win.removeEventListener('resize', handleResize);
1275
+ cleanup();
1276
+ };
1277
+ }
1278
+
1279
+ /**
1280
+ * Automatically updates the position of the floating element when necessary.
1281
+ * Should only be called when the floating element is mounted on the DOM or
1282
+ * visible on the screen.
1283
+ * @returns cleanup function that should be invoked when the floating element is
1284
+ * removed from the DOM or hidden from the screen.
1285
+ * @see https://floating-ui.com/docs/autoUpdate
1286
+ */
1287
+ function autoUpdate(reference, floating, update, options) {
1288
+ if (options === void 0) {
1289
+ options = {};
1290
+ }
1291
+ const {
1292
+ ancestorScroll = true,
1293
+ ancestorResize = true,
1294
+ elementResize = typeof ResizeObserver === 'function',
1295
+ layoutShift = typeof IntersectionObserver === 'function',
1296
+ animationFrame = false
1297
+ } = options;
1298
+ const referenceEl = unwrapElement(reference);
1299
+ const ancestors = ancestorScroll || ancestorResize ? [...(referenceEl ? getOverflowAncestors(referenceEl) : []), ...(floating ? getOverflowAncestors(floating) : [])] : [];
1300
+ ancestors.forEach(ancestor => {
1301
+ ancestorScroll && ancestor.addEventListener('scroll', update);
1302
+ ancestorResize && ancestor.addEventListener('resize', update);
1303
+ });
1304
+ const cleanupIo = referenceEl && layoutShift ? observeMove(referenceEl, update, ancestorResize) : null;
1305
+ let reobserveFrame = -1;
1306
+ let resizeObserver = null;
1307
+ if (elementResize) {
1308
+ resizeObserver = new ResizeObserver(_ref => {
1309
+ let [firstEntry] = _ref;
1310
+ if (firstEntry && firstEntry.target === referenceEl && resizeObserver && floating) {
1311
+ // Prevent update loops when using the `size` middleware.
1312
+ // https://github.com/floating-ui/floating-ui/issues/1740
1313
+ resizeObserver.unobserve(floating);
1314
+ cancelAnimationFrame(reobserveFrame);
1315
+ reobserveFrame = requestAnimationFrame(() => {
1316
+ var _resizeObserver;
1317
+ (_resizeObserver = resizeObserver) == null || _resizeObserver.observe(floating);
1318
+ });
1319
+ }
1320
+ update();
1321
+ });
1322
+ if (referenceEl && !animationFrame) {
1323
+ resizeObserver.observe(referenceEl);
1324
+ }
1325
+ if (floating) {
1326
+ resizeObserver.observe(floating);
1327
+ }
1328
+ }
1329
+ let frameId;
1330
+ let prevRefRect = animationFrame ? getBoundingClientRect(reference) : null;
1331
+ if (animationFrame) {
1332
+ frameLoop();
1333
+ }
1334
+ function frameLoop() {
1335
+ const nextRefRect = getBoundingClientRect(reference);
1336
+ if (prevRefRect && !rectsAreEqual(prevRefRect, nextRefRect)) {
1337
+ update();
1338
+ }
1339
+ prevRefRect = nextRefRect;
1340
+ frameId = requestAnimationFrame(frameLoop);
1341
+ }
1342
+ update();
1343
+ return () => {
1344
+ var _resizeObserver2;
1345
+ ancestors.forEach(ancestor => {
1346
+ ancestorScroll && ancestor.removeEventListener('scroll', update);
1347
+ ancestorResize && ancestor.removeEventListener('resize', update);
1348
+ });
1349
+ cleanupIo == null || cleanupIo();
1350
+ (_resizeObserver2 = resizeObserver) == null || _resizeObserver2.disconnect();
1351
+ resizeObserver = null;
1352
+ if (animationFrame) {
1353
+ cancelAnimationFrame(frameId);
1354
+ }
1355
+ };
1356
+ }
1357
+
1358
+ /**
1359
+ * Modifies the placement by translating the floating element along the
1360
+ * specified axes.
1361
+ * A number (shorthand for `mainAxis` or distance), or an axes configuration
1362
+ * object may be passed.
1363
+ * @see https://floating-ui.com/docs/offset
1364
+ */
1365
+ const offset$1 = offset$2;
1366
+
1367
+ /**
1368
+ * Optimizes the visibility of the floating element by shifting it in order to
1369
+ * keep it in view when it will overflow the clipping boundary.
1370
+ * @see https://floating-ui.com/docs/shift
1371
+ */
1372
+ const shift$1 = shift$2;
1373
+
1374
+ /**
1375
+ * Optimizes the visibility of the floating element by flipping the `placement`
1376
+ * in order to keep it in view when the preferred placement(s) will overflow the
1377
+ * clipping boundary. Alternative to `autoPlacement`.
1378
+ * @see https://floating-ui.com/docs/flip
1379
+ */
1380
+ const flip$1 = flip$2;
1381
+
1382
+ /**
1383
+ * Provides data that allows you to change the size of the floating element —
1384
+ * for instance, prevent it from overflowing the clipping boundary or match the
1385
+ * width of the reference element.
1386
+ * @see https://floating-ui.com/docs/size
1387
+ */
1388
+ const size$1 = size$2;
1389
+
1390
+ /**
1391
+ * Built-in `limiter` that will stop `shift()` at a certain point.
1392
+ */
1393
+ const limitShift$1 = limitShift$2;
1394
+
1395
+ /**
1396
+ * Computes the `x` and `y` coordinates that will place the floating element
1397
+ * next to a given reference element.
1398
+ */
1399
+ const computePosition = (reference, floating, options) => {
1400
+ // This caches the expensive `getClippingElementAncestors` function so that
1401
+ // multiple lifecycle resets re-use the same result. It only lives for a
1402
+ // single call. If other functions become expensive, we can add them as well.
1403
+ const cache = new Map();
1404
+ const mergedOptions = options != null ? options : {};
1405
+ const platformWithCache = {
1406
+ ...platform,
1407
+ ...mergedOptions.platform,
1408
+ _c: cache
1409
+ };
1410
+ return computePosition$1(reference, floating, {
1411
+ ...mergedOptions,
1412
+ platform: platformWithCache
1413
+ });
1414
+ };
1415
+
1416
+ var isClient = typeof document !== 'undefined';
1417
+
1418
+ var noop = function noop() {};
1419
+ var index = isClient ? useLayoutEffect : noop;
1420
+
1421
+ // Fork of `fast-deep-equal` that only does the comparisons we need and compares
1422
+ // functions
1423
+ function deepEqual(a, b) {
1424
+ if (a === b) {
1425
+ return true;
1426
+ }
1427
+ if (typeof a !== typeof b) {
1428
+ return false;
1429
+ }
1430
+ if (typeof a === 'function' && a.toString() === b.toString()) {
1431
+ return true;
1432
+ }
1433
+ let length;
1434
+ let i;
1435
+ let keys;
1436
+ if (a && b && typeof a === 'object') {
1437
+ if (Array.isArray(a)) {
1438
+ length = a.length;
1439
+ if (length !== b.length) return false;
1440
+ for (i = length; i-- !== 0;) {
1441
+ if (!deepEqual(a[i], b[i])) {
1442
+ return false;
1443
+ }
1444
+ }
1445
+ return true;
1446
+ }
1447
+ keys = Object.keys(a);
1448
+ length = keys.length;
1449
+ if (length !== Object.keys(b).length) {
1450
+ return false;
1451
+ }
1452
+ for (i = length; i-- !== 0;) {
1453
+ if (!{}.hasOwnProperty.call(b, keys[i])) {
1454
+ return false;
1455
+ }
1456
+ }
1457
+ for (i = length; i-- !== 0;) {
1458
+ const key = keys[i];
1459
+ if (key === '_owner' && a.$$typeof) {
1460
+ continue;
1461
+ }
1462
+ if (!deepEqual(a[key], b[key])) {
1463
+ return false;
1464
+ }
1465
+ }
1466
+ return true;
1467
+ }
1468
+ return a !== a && b !== b;
1469
+ }
1470
+
1471
+ function getDPR(element) {
1472
+ if (typeof window === 'undefined') {
1473
+ return 1;
1474
+ }
1475
+ const win = element.ownerDocument.defaultView || window;
1476
+ return win.devicePixelRatio || 1;
1477
+ }
1478
+
1479
+ function roundByDPR(element, value) {
1480
+ const dpr = getDPR(element);
1481
+ return Math.round(value * dpr) / dpr;
1482
+ }
1483
+
1484
+ function useLatestRef(value) {
1485
+ const ref = React.useRef(value);
1486
+ index(() => {
1487
+ ref.current = value;
1488
+ });
1489
+ return ref;
1490
+ }
1491
+
1492
+ /**
1493
+ * Provides data to position a floating element.
1494
+ * @see https://floating-ui.com/docs/useFloating
1495
+ */
1496
+ function useFloating(options) {
1497
+ if (options === void 0) {
1498
+ options = {};
1499
+ }
1500
+ const {
1501
+ placement = 'bottom',
1502
+ strategy = 'absolute',
1503
+ middleware = [],
1504
+ platform,
1505
+ elements: {
1506
+ reference: externalReference,
1507
+ floating: externalFloating
1508
+ } = {},
1509
+ transform = true,
1510
+ whileElementsMounted,
1511
+ open
1512
+ } = options;
1513
+ const [data, setData] = React.useState({
1514
+ x: 0,
1515
+ y: 0,
1516
+ strategy,
1517
+ placement,
1518
+ middlewareData: {},
1519
+ isPositioned: false
1520
+ });
1521
+ const [latestMiddleware, setLatestMiddleware] = React.useState(middleware);
1522
+ if (!deepEqual(latestMiddleware, middleware)) {
1523
+ setLatestMiddleware(middleware);
1524
+ }
1525
+ const [_reference, _setReference] = React.useState(null);
1526
+ const [_floating, _setFloating] = React.useState(null);
1527
+ const setReference = React.useCallback(node => {
1528
+ if (node !== referenceRef.current) {
1529
+ referenceRef.current = node;
1530
+ _setReference(node);
1531
+ }
1532
+ }, []);
1533
+ const setFloating = React.useCallback(node => {
1534
+ if (node !== floatingRef.current) {
1535
+ floatingRef.current = node;
1536
+ _setFloating(node);
1537
+ }
1538
+ }, []);
1539
+ const referenceEl = externalReference || _reference;
1540
+ const floatingEl = externalFloating || _floating;
1541
+ const referenceRef = React.useRef(null);
1542
+ const floatingRef = React.useRef(null);
1543
+ const dataRef = React.useRef(data);
1544
+ const hasWhileElementsMounted = whileElementsMounted != null;
1545
+ const whileElementsMountedRef = useLatestRef(whileElementsMounted);
1546
+ const platformRef = useLatestRef(platform);
1547
+ const openRef = useLatestRef(open);
1548
+ const update = React.useCallback(() => {
1549
+ if (!referenceRef.current || !floatingRef.current) {
1550
+ return;
1551
+ }
1552
+ const config = {
1553
+ placement,
1554
+ strategy,
1555
+ middleware: latestMiddleware
1556
+ };
1557
+ if (platformRef.current) {
1558
+ config.platform = platformRef.current;
1559
+ }
1560
+ computePosition(referenceRef.current, floatingRef.current, config).then(data => {
1561
+ const fullData = {
1562
+ ...data,
1563
+ // The floating element's position may be recomputed while it's closed
1564
+ // but still mounted (such as when transitioning out). To ensure
1565
+ // `isPositioned` will be `false` initially on the next open, avoid
1566
+ // setting it to `true` when `open === false` (must be specified).
1567
+ isPositioned: openRef.current !== false
1568
+ };
1569
+ if (isMountedRef.current && !deepEqual(dataRef.current, fullData)) {
1570
+ dataRef.current = fullData;
1571
+ ReactDOM.flushSync(() => {
1572
+ setData(fullData);
1573
+ });
1574
+ }
1575
+ });
1576
+ }, [latestMiddleware, placement, strategy, platformRef, openRef]);
1577
+ index(() => {
1578
+ if (open === false && dataRef.current.isPositioned) {
1579
+ dataRef.current.isPositioned = false;
1580
+ setData(data => ({
1581
+ ...data,
1582
+ isPositioned: false
1583
+ }));
1584
+ }
1585
+ }, [open]);
1586
+ const isMountedRef = React.useRef(false);
1587
+ index(() => {
1588
+ isMountedRef.current = true;
1589
+ return () => {
1590
+ isMountedRef.current = false;
1591
+ };
1592
+ }, []);
1593
+ index(() => {
1594
+ if (referenceEl) referenceRef.current = referenceEl;
1595
+ if (floatingEl) floatingRef.current = floatingEl;
1596
+ if (referenceEl && floatingEl) {
1597
+ if (whileElementsMountedRef.current) {
1598
+ return whileElementsMountedRef.current(referenceEl, floatingEl, update);
1599
+ }
1600
+ update();
1601
+ }
1602
+ }, [referenceEl, floatingEl, update, whileElementsMountedRef, hasWhileElementsMounted]);
1603
+ const refs = React.useMemo(() => ({
1604
+ reference: referenceRef,
1605
+ floating: floatingRef,
1606
+ setReference,
1607
+ setFloating
1608
+ }), [setReference, setFloating]);
1609
+ const elements = React.useMemo(() => ({
1610
+ reference: referenceEl,
1611
+ floating: floatingEl
1612
+ }), [referenceEl, floatingEl]);
1613
+ const floatingStyles = React.useMemo(() => {
1614
+ const initialStyles = {
1615
+ position: strategy,
1616
+ left: 0,
1617
+ top: 0
1618
+ };
1619
+ if (!elements.floating) {
1620
+ return initialStyles;
1621
+ }
1622
+ const x = roundByDPR(elements.floating, data.x);
1623
+ const y = roundByDPR(elements.floating, data.y);
1624
+ if (transform) {
1625
+ return {
1626
+ ...initialStyles,
1627
+ transform: "translate(" + x + "px, " + y + "px)",
1628
+ ...(getDPR(elements.floating) >= 1.5 && {
1629
+ willChange: 'transform'
1630
+ })
1631
+ };
1632
+ }
1633
+ return {
1634
+ position: strategy,
1635
+ left: x,
1636
+ top: y
1637
+ };
1638
+ }, [strategy, transform, elements.floating, data.x, data.y]);
1639
+ return React.useMemo(() => ({
1640
+ ...data,
1641
+ update,
1642
+ refs,
1643
+ elements,
1644
+ floatingStyles
1645
+ }), [data, update, refs, elements, floatingStyles]);
1646
+ }
1647
+
1648
+ /**
1649
+ * Modifies the placement by translating the floating element along the
1650
+ * specified axes.
1651
+ * A number (shorthand for `mainAxis` or distance), or an axes configuration
1652
+ * object may be passed.
1653
+ * @see https://floating-ui.com/docs/offset
1654
+ */
1655
+ const offset = (options, deps) => {
1656
+ const result = offset$1(options);
1657
+ return {
1658
+ name: result.name,
1659
+ fn: result.fn,
1660
+ options: [options, deps]
1661
+ };
1662
+ };
1663
+
1664
+ /**
1665
+ * Optimizes the visibility of the floating element by shifting it in order to
1666
+ * keep it in view when it will overflow the clipping boundary.
1667
+ * @see https://floating-ui.com/docs/shift
1668
+ */
1669
+ const shift = (options, deps) => {
1670
+ const result = shift$1(options);
1671
+ return {
1672
+ name: result.name,
1673
+ fn: result.fn,
1674
+ options: [options, deps]
1675
+ };
1676
+ };
1677
+
1678
+ /**
1679
+ * Built-in `limiter` that will stop `shift()` at a certain point.
1680
+ */
1681
+ const limitShift = (options, deps) => {
1682
+ const result = limitShift$1(options);
1683
+ return {
1684
+ fn: result.fn,
1685
+ options: [options, deps]
1686
+ };
1687
+ };
1688
+
1689
+ /**
1690
+ * Optimizes the visibility of the floating element by flipping the `placement`
1691
+ * in order to keep it in view when the preferred placement(s) will overflow the
1692
+ * clipping boundary. Alternative to `autoPlacement`.
1693
+ * @see https://floating-ui.com/docs/flip
1694
+ */
1695
+ const flip = (options, deps) => {
1696
+ const result = flip$1(options);
1697
+ return {
1698
+ name: result.name,
1699
+ fn: result.fn,
1700
+ options: [options, deps]
1701
+ };
1702
+ };
1703
+
1704
+ /**
1705
+ * Provides data that allows you to change the size of the floating element —
1706
+ * for instance, prevent it from overflowing the clipping boundary or match the
1707
+ * width of the reference element.
1708
+ * @see https://floating-ui.com/docs/size
1709
+ */
1710
+ const size = (options, deps) => {
1711
+ const result = size$1(options);
1712
+ return {
1713
+ name: result.name,
1714
+ fn: result.fn,
1715
+ options: [options, deps]
1716
+ };
1717
+ };
7
1718
 
8
1719
  function useFloatingRootContext(options) {
9
1720
  const {
@@ -52,16 +1763,17 @@ function useFloatingRootContext(options) {
52
1763
  }
53
1764
 
54
1765
  /**
55
- * Provides data to position a floating element and context to add interactions.
56
- * @see https://floating-ui.com/docs/useFloating
1766
+ * Base UI's private `useFloating` path. The caller must supply the root store, so this skips the
1767
+ * internal root-context hook used by the public Floating UI-compatible API.
57
1768
  */
58
- function useFloating(options = {}) {
1769
+ function useBaseUIFloating(options) {
1770
+ return useFloatingWithStore(options, options.rootContext);
1771
+ }
1772
+ function useFloatingWithStore(options, store) {
59
1773
  const {
60
1774
  nodeId,
61
1775
  externalTree
62
1776
  } = options;
63
- const internalStore = useFloatingRootContext(options);
64
- const store = options.rootContext || internalStore;
65
1777
  const referenceElement = store.useState('referenceElement');
66
1778
  const floatingElement = store.useState('floatingElement');
67
1779
  const domReferenceElement = store.useState('domReferenceElement');
@@ -77,7 +1789,7 @@ function useFloating(options = {}) {
77
1789
  floating: floatingElement,
78
1790
  domReference: domReferenceElement
79
1791
  }), [referenceElement, floatingElement, domReferenceElement]);
80
- const position = useFloating$1({
1792
+ const position = useFloating({
81
1793
  ...options,
82
1794
  elements: {
83
1795
  ...storeElements,
@@ -167,6 +1879,13 @@ function useFloating(options = {}) {
167
1879
  }
168
1880
 
169
1881
  const ESCAPE = 'Escape';
1882
+
1883
+ // WebKit fires zero-delta `mousemove`/`pointermove` events when the list scrolls
1884
+ // beneath a stationary pointer, moving the highlight during keyboard navigation.
1885
+ // https://github.com/mui/base-ui/issues/4002
1886
+ function isStationaryWebKitPointer(event) {
1887
+ return webkit && event.movementX === 0 && event.movementY === 0;
1888
+ }
170
1889
  function doSwitch(orientation, vertical, horizontal) {
171
1890
  switch (orientation) {
172
1891
  case 'vertical':
@@ -573,6 +2292,9 @@ function useListNavigation(context, props) {
573
2292
  }),
574
2293
  // Safari
575
2294
  onMouseMove(event) {
2295
+ if (isStationaryWebKitPointer(event)) {
2296
+ return;
2297
+ }
576
2298
  forceSyncFocusRef.current = true;
577
2299
  forceScrollIntoViewRef.current = false;
578
2300
  if (focusItemOnHover) {
@@ -636,7 +2358,10 @@ function useListNavigation(context, props) {
636
2358
  }
637
2359
  commonOnKeyDown(event);
638
2360
  },
639
- onPointerMove() {
2361
+ onPointerMove(event) {
2362
+ if (isStationaryWebKitPointer(event)) {
2363
+ return;
2364
+ }
640
2365
  isPointerModalityRef.current = true;
641
2366
  }
642
2367
  };
@@ -757,18 +2482,19 @@ function useTypeahead(context, props) {
757
2482
  const prevIndexRef = React.useRef(selectedIndex ?? activeIndex ?? -1);
758
2483
  const matchIndexRef = React.useRef(null);
759
2484
  const onKeyDown = useStableCallback(event => {
760
- function isVisible(index) {
761
- const element = elementsRef?.current[index];
762
- return !element || isElementVisible(element);
2485
+ function getElement(index) {
2486
+ return elementsRef?.current[index];
763
2487
  }
764
2488
  function isItemAvailable(index) {
765
- if (!isVisible(index)) {
2489
+ const element = getElement(index);
2490
+ if (element && !isElementVisible(element) || element?.matches(':disabled')) {
766
2491
  return false;
767
2492
  }
768
- // Visibility is handled above; pass an empty element list so `isListIndexDisabled`
769
- // resolves only the explicit `disabledIndices` (array/predicate) and skips its own
770
- // visibility/attribute fallbacks. Consumers that don't opt in keep matching every
771
- // visible item.
2493
+ // Visibility and native disabled state are handled above; pass an empty
2494
+ // element list so `isListIndexDisabled` resolves only the explicit
2495
+ // `disabledIndices` (array/predicate) and skips its own fallbacks.
2496
+ // Consumers that don't pass `disabledIndices` keep matching every visible
2497
+ // item except native disabled elements provided through `elementsRef`.
772
2498
  return disabledIndices == null || !isListIndexDisabled(EMPTY_ARRAY, index, disabledIndices);
773
2499
  }
774
2500
  function getMatchingIndex(list, string, startIndex = 0) {
@@ -876,12 +2602,6 @@ function useTypeahead(context, props) {
876
2602
  stringRef.current = '';
877
2603
  }
878
2604
  }, [open, selectedIndex, timeout]);
879
- useIsoLayoutEffect(() => {
880
- // Sync arrow key navigation but not typeahead navigation.
881
- if (open && stringRef.current === '') {
882
- prevIndexRef.current = selectedIndex ?? activeIndex ?? -1;
883
- }
884
- }, [open, selectedIndex, activeIndex]);
885
2605
  const sharedProps = React.useMemo(() => ({
886
2606
  onKeyDown,
887
2607
  onBlur
@@ -921,6 +2641,12 @@ function findItemIndex(itemValues, selectedValue, comparer) {
921
2641
  return compareItemEquality(itemValue, selectedValue, comparer);
922
2642
  });
923
2643
  }
2644
+ function findSelectionIndex(itemValues, selectedValue, comparer, multiple) {
2645
+ // Only unwrap in multiple mode: an array can itself be a valid single-select value.
2646
+ const lastValue = multiple && Array.isArray(selectedValue) ? selectedValue[selectedValue.length - 1] : selectedValue;
2647
+ const index = findItemIndex(itemValues, lastValue, comparer);
2648
+ return index === -1 ? null : index;
2649
+ }
924
2650
  function removeItem(selectedValues, itemValue, comparer) {
925
2651
  return selectedValues.filter(selectedValue => !compareItemEquality(itemValue, selectedValue, comparer));
926
2652
  }
@@ -1043,38 +2769,6 @@ function resolveMultipleLabels(values, items, itemToStringLabel) {
1043
2769
  }, []);
1044
2770
  }
1045
2771
 
1046
- let FieldControlDataAttributes = /*#__PURE__*/function (FieldControlDataAttributes) {
1047
- /**
1048
- * Present when the field is disabled.
1049
- */
1050
- FieldControlDataAttributes["disabled"] = "data-disabled";
1051
- /**
1052
- * Present when the field is in a valid state.
1053
- */
1054
- FieldControlDataAttributes["valid"] = "data-valid";
1055
- /**
1056
- * Present when the field is in an invalid state.
1057
- */
1058
- FieldControlDataAttributes["invalid"] = "data-invalid";
1059
- /**
1060
- * Present when the field has been touched.
1061
- */
1062
- FieldControlDataAttributes["touched"] = "data-touched";
1063
- /**
1064
- * Present when the field's value has changed.
1065
- */
1066
- FieldControlDataAttributes["dirty"] = "data-dirty";
1067
- /**
1068
- * Present when the field is filled.
1069
- */
1070
- FieldControlDataAttributes["filled"] = "data-filled";
1071
- /**
1072
- * Present when the field control is focused.
1073
- */
1074
- FieldControlDataAttributes["focused"] = "data-focused";
1075
- return FieldControlDataAttributes;
1076
- }({});
1077
-
1078
2772
  const DEFAULT_VALIDITY_STATE = {
1079
2773
  badInput: false,
1080
2774
  customError: false,
@@ -1106,11 +2800,11 @@ const fieldValidityMapping = {
1106
2800
  }
1107
2801
  if (value) {
1108
2802
  return {
1109
- [FieldControlDataAttributes.valid]: ''
2803
+ 'data-valid': ''
1110
2804
  };
1111
2805
  }
1112
2806
  return {
1113
- [FieldControlDataAttributes.invalid]: ''
2807
+ 'data-invalid': ''
1114
2808
  };
1115
2809
  }
1116
2810
  };
@@ -1127,29 +2821,22 @@ const DEFAULT_FIELD_ROOT_CONTEXT = {
1127
2821
  },
1128
2822
  setValidityData: NOOP,
1129
2823
  disabled: undefined,
1130
- touched: DEFAULT_FIELD_STATE_ATTRIBUTES.touched,
1131
2824
  setTouched: NOOP,
1132
- dirty: DEFAULT_FIELD_STATE_ATTRIBUTES.dirty,
1133
2825
  setDirty: NOOP,
1134
- filled: DEFAULT_FIELD_STATE_ATTRIBUTES.filled,
1135
2826
  setFilled: NOOP,
1136
- focused: DEFAULT_FIELD_STATE_ATTRIBUTES.focused,
1137
2827
  setFocused: NOOP,
1138
- validate: () => null,
1139
2828
  validationMode: 'onSubmit',
1140
- validationDebounceTime: 0,
1141
2829
  shouldValidateOnChange: () => false,
1142
2830
  state: DEFAULT_FIELD_ROOT_STATE,
1143
- markedDirtyRef: {
1144
- current: false
1145
- },
1146
2831
  registerFieldControl: NOOP,
1147
2832
  validation: {
1148
2833
  getValidationProps: (_disabled, props = EMPTY_OBJECT) => props,
1149
2834
  inputRef: {
1150
2835
  current: null
1151
2836
  },
2837
+ registeredInputs: new Map(),
1152
2838
  registerInput: NOOP,
2839
+ getInputControl: () => null,
1153
2840
  commit: async () => {},
1154
2841
  change: NOOP
1155
2842
  }
@@ -1168,14 +2855,16 @@ function useRegisterFieldControl(controlRef, id, value, getFormValueOverride, en
1168
2855
  const {
1169
2856
  registerFieldControl
1170
2857
  } = useFieldRootContext();
1171
- const sourceRef = React.useRef(null);
1172
- if (!sourceRef.current) {
1173
- sourceRef.current = Symbol();
1174
- }
2858
+ const sourceRef = useRefWithInit(() => Symbol());
2859
+
2860
+ // Re-register without unregistering first: re-registration with the same id updates the
2861
+ // form's fields Map entry in place, while a delete + re-add would move the field to the
2862
+ // end of the Map every time its value changes.
1175
2863
  useIsoLayoutEffect(() => {
1176
2864
  const source = sourceRef.current;
1177
- if (!source || !enabled) {
1178
- return undefined;
2865
+ if (!enabled) {
2866
+ registerFieldControl(source, undefined);
2867
+ return;
1179
2868
  }
1180
2869
  const registration = {
1181
2870
  controlRef,
@@ -1185,13 +2874,19 @@ function useRegisterFieldControl(controlRef, id, value, getFormValueOverride, en
1185
2874
  value
1186
2875
  };
1187
2876
  registerFieldControl(source, registration);
2877
+ }, [controlRef, enabled, getFormValueOverride, id, name, registerFieldControl, sourceRef, value]);
2878
+ useIsoLayoutEffect(() => {
2879
+ const source = sourceRef.current;
1188
2880
  return () => {
1189
2881
  registerFieldControl(source, undefined);
1190
2882
  };
1191
- }, [controlRef, enabled, getFormValueOverride, id, name, registerFieldControl, value]);
2883
+ }, [registerFieldControl, sourceRef]);
1192
2884
  }
1193
2885
 
1194
2886
  const FormContext = /*#__PURE__*/React.createContext({
2887
+ elementRef: {
2888
+ current: null
2889
+ },
1195
2890
  formRef: {
1196
2891
  current: {
1197
2892
  fields: new Map()
@@ -1239,7 +2934,7 @@ function useLabelableId(params = {}) {
1239
2934
  } = useLabelableContext();
1240
2935
  const defaultId = useBaseUiId(id);
1241
2936
  const controlIdForEffect = implicit ? controlId : undefined;
1242
- const controlSourceRef = useRefWithInit(() => Symbol('labelable-control'));
2937
+ const controlSourceRef = useRefWithInit(() => Symbol());
1243
2938
  const hasRegisteredRef = React.useRef(false);
1244
2939
  const hadExplicitIdRef = React.useRef(id != null);
1245
2940
  const unregisterControlId = useStableCallback(() => {
@@ -1288,6 +2983,19 @@ function areArraysEqual(array1, array2, itemComparer = (a, b) => a === b) {
1288
2983
  return array1.length === array2.length && array1.every((value, index) => itemComparer(value, array2[index]));
1289
2984
  }
1290
2985
 
2986
+ // Tolerance around the element bounds so a fast click whose pointer drifts slightly
2987
+ // during press-release isn't mistaken for a drag-off-and-release cancellation.
2988
+ // Matches typical OS/browser drag-initiation thresholds.
2989
+ const BOUNDARY_OFFSET = 5;
2990
+
2991
+ /**
2992
+ * Determines if a mouse event occurred within the bounds of an element
2993
+ * (including its pseudo-elements), with a small tolerance for pointer drift.
2994
+ */
2995
+ function isMouseWithinBounds(event, element) {
2996
+ const bounds = getPseudoElementBounds(element);
2997
+ return event.clientX >= bounds.left - BOUNDARY_OFFSET && event.clientX <= bounds.right + BOUNDARY_OFFSET && event.clientY >= bounds.top - BOUNDARY_OFFSET && event.clientY <= bounds.bottom + BOUNDARY_OFFSET;
2998
+ }
1291
2999
  function getPseudoElementBounds(element) {
1292
3000
  const elementRect = element.getBoundingClientRect();
1293
3001
  const win = getWindow(element);
@@ -1334,12 +3042,7 @@ function resolveAriaLabelledBy(fieldLabelId, localLabelId) {
1334
3042
  const CompositeListContext = /*#__PURE__*/React.createContext({
1335
3043
  register: () => {},
1336
3044
  unregister: () => {},
1337
- subscribeMapChange: () => {
1338
- return () => {};
1339
- },
1340
- elementsRef: {
1341
- current: []
1342
- },
3045
+ subscribeMapChange: () => () => {},
1343
3046
  nextIndexRef: {
1344
3047
  current: 0
1345
3048
  }
@@ -1352,7 +3055,6 @@ function useCompositeListContext() {
1352
3055
  /* eslint-disable no-bitwise */
1353
3056
  /**
1354
3057
  * Provides context for a list of items in a composite component.
1355
- * @internal
1356
3058
  */
1357
3059
  function CompositeList(props) {
1358
3060
  const {
@@ -1362,114 +3064,146 @@ function CompositeList(props) {
1362
3064
  onMapChange: onMapChangeProp
1363
3065
  } = props;
1364
3066
  const onMapChange = useStableCallback(onMapChangeProp);
1365
- const nextIndexRef = React.useRef(0);
3067
+ const [, setMapTick] = React.useState(false);
1366
3068
  const listeners = useRefWithInit(createListeners).current;
1367
-
1368
- // We use a stable `map` to avoid O(n^2) re-allocation costs for large lists.
1369
- // `mapTick` is our re-render trigger mechanism. We also need to update the
1370
- // elements and label refs, but there's a lot of async work going on and sometimes
1371
- // the effect that handles `onMapChange` gets called after those refs have been
1372
- // filled, and we don't want to lose those values by setting their lengths to `0`.
1373
- // We also need to have them at the proper length because floating-ui uses that
1374
- // information for list navigation.
1375
-
1376
3069
  const map = useRefWithInit(createMap).current;
1377
- // `mapTick` uses a counter rather than objects for low precision-loss risk and better memory efficiency
1378
- const [mapTick, setMapTick] = React.useState(0);
1379
- const lastTickRef = React.useRef(mapTick);
1380
- const register = useStableCallback((node, metadata) => {
1381
- map.set(node, metadata ?? null);
1382
- lastTickRef.current += 1;
1383
- setMapTick(lastTickRef.current);
3070
+ const nextIndexRef = React.useRef(0);
3071
+ const isDirtyRef = React.useRef(true);
3072
+ const itemsRef = React.useRef([]);
3073
+ const mutationObserverRef = React.useRef(null);
3074
+
3075
+ // Item effects can run without their parent rendering. Schedule one synchronous
3076
+ // parent update for the whole commit so refs are rebuilt before paint and while
3077
+ // the originating React event is still inside `act()` in tests.
3078
+ const scheduleMapUpdate = useStableCallback(() => {
3079
+ if (isDirtyRef.current) {
3080
+ return;
3081
+ }
3082
+ isDirtyRef.current = true;
3083
+ setMapTick(tick => !tick);
3084
+ });
3085
+ const register = useStableCallback((node, registration) => {
3086
+ map.set(node, registration);
3087
+ scheduleMapUpdate();
1384
3088
  });
1385
3089
  const unregister = useStableCallback(node => {
1386
3090
  map.delete(node);
1387
- lastTickRef.current += 1;
1388
- setMapTick(lastTickRef.current);
3091
+ scheduleMapUpdate();
1389
3092
  });
1390
- const sortedMap = React.useMemo(() => {
1391
- const newMap = new Map();
1392
- // Filter out disconnected elements before sorting to avoid inconsistent
1393
- // compareDocumentPosition results when elements are detached from the DOM.
1394
- const sortedNodes = Array.from(map.keys()).filter(node => node.isConnected).sort(sortByDocumentPosition);
1395
- sortedNodes.forEach((node, index) => {
1396
- const metadata = map.get(node) ?? {};
1397
- newMap.set(node, {
1398
- ...metadata,
1399
- index
3093
+ const syncRefs = useStableCallback(items => {
3094
+ const nextMap = new Map();
3095
+ elementsRef.current.length = 0;
3096
+ if (labelsRef) {
3097
+ labelsRef.current.length = 0;
3098
+ }
3099
+ items.forEach(item => {
3100
+ nextMap.set(item.element, {
3101
+ ...(item.registration.metadata ?? {}),
3102
+ index: item.index
1400
3103
  });
3104
+ elementsRef.current[item.index] = item.element;
3105
+ if (labelsRef) {
3106
+ labelsRef.current[item.index] = item.registration.label !== undefined ? item.registration.label : item.registration.textRef?.current?.textContent ?? item.element.textContent;
3107
+ }
1401
3108
  });
1402
- return newMap;
1403
- }, [map, mapTick]);
1404
- useIsoLayoutEffect(() => {
1405
- if (typeof MutationObserver !== 'function' || sortedMap.size === 0) {
1406
- return undefined;
3109
+ nextIndexRef.current = elementsRef.current.length;
3110
+ return nextMap;
3111
+ });
3112
+ function observe(sortedNodes) {
3113
+ mutationObserverRef.current?.disconnect();
3114
+ mutationObserverRef.current = null;
3115
+
3116
+ // A single item can't reorder.
3117
+ if (typeof MutationObserver !== 'function' || sortedNodes.length < 2) {
3118
+ return;
1407
3119
  }
1408
3120
  const mutationObserver = new MutationObserver(entries => {
1409
- const diff = new Set();
1410
- const updateDiff = node => diff.has(node) ? diff.delete(node) : diff.add(node);
1411
- entries.forEach(entry => {
1412
- entry.removedNodes.forEach(updateDiff);
1413
- entry.addedNodes.forEach(updateDiff);
1414
- });
1415
- if (diff.size === 0) {
1416
- lastTickRef.current += 1;
1417
- setMapTick(lastTickRef.current);
3121
+ // Only verify the order after a move: a node that was removed and later
3122
+ // re-added within the same batch. Additions and removals alone can't
3123
+ // change the relative order of the remaining items, and items that mount
3124
+ // or unmount re-sort through `register`/`unregister`.
3125
+ if (!hasMovedNode(entries)) {
3126
+ return;
1418
3127
  }
1419
- });
1420
- sortedMap.forEach((_, node) => {
1421
- if (node.parentElement) {
1422
- mutationObserver.observe(node.parentElement, {
1423
- childList: true
1424
- });
3128
+ let previousConnectedNode = null;
3129
+
3130
+ // If any connected node now appears before the previous connected node,
3131
+ // wrappers/items moved and the index map needs to be rebuilt.
3132
+ for (const node of sortedNodes) {
3133
+ if (!node.isConnected) {
3134
+ continue;
3135
+ }
3136
+ if (previousConnectedNode && sortByDocumentPosition(previousConnectedNode, node) > 0) {
3137
+ mutationObserver.disconnect();
3138
+ scheduleMapUpdate();
3139
+ return;
3140
+ }
3141
+ previousConnectedNode = node;
1425
3142
  }
1426
3143
  });
1427
- return () => {
1428
- mutationObserver.disconnect();
1429
- };
1430
- }, [sortedMap]);
1431
- useIsoLayoutEffect(() => {
1432
- const shouldUpdateLengths = lastTickRef.current === mapTick;
1433
- if (shouldUpdateLengths) {
1434
- if (elementsRef.current.length !== sortedMap.size) {
1435
- elementsRef.current.length = sortedMap.size;
3144
+ mutationObserverRef.current = mutationObserver;
3145
+
3146
+ // A reorder that changes item indexes must invert at least one adjacent pair
3147
+ // from the previous sorted order. Observing each pair's common parent catches
3148
+ // both direct item moves and ancestor wrapper moves at the boundary.
3149
+ const roots = new Set();
3150
+ for (let i = 1; i < sortedNodes.length; i += 1) {
3151
+ const root = getCommonAncestor(sortedNodes[i - 1], sortedNodes[i]);
3152
+ if (root) {
3153
+ roots.add(root);
1436
3154
  }
1437
- if (labelsRef && labelsRef.current.length !== sortedMap.size) {
1438
- labelsRef.current.length = sortedMap.size;
1439
- }
1440
- nextIndexRef.current = sortedMap.size;
1441
3155
  }
1442
- onMapChange(sortedMap);
1443
- }, [onMapChange, sortedMap, elementsRef, labelsRef, mapTick]);
3156
+ roots.forEach(root => mutationObserver.observe(root, {
3157
+ childList: true
3158
+ }));
3159
+ }
3160
+ const flush = useStableCallback(() => {
3161
+ const [items, automaticNodes] = getCompositeListSnapshot(map);
3162
+ const nextMap = syncRefs(items);
3163
+ observe(automaticNodes);
3164
+ itemsRef.current = items;
3165
+ isDirtyRef.current = false;
3166
+ listeners.forEach(listener => listener(nextMap));
3167
+ onMapChange(nextMap);
3168
+ });
1444
3169
  useIsoLayoutEffect(() => {
3170
+ // Re-copy the last committed snapshot when the ref objects change or Strict Mode replays
3171
+ // effects without reattaching callback refs.
3172
+ if (!isDirtyRef.current) {
3173
+ syncRefs(itemsRef.current);
3174
+ }
1445
3175
  return () => {
1446
3176
  elementsRef.current = [];
1447
- };
1448
- }, [elementsRef]);
1449
- useIsoLayoutEffect(() => {
1450
- return () => {
1451
3177
  if (labelsRef) {
1452
3178
  labelsRef.current = [];
1453
3179
  }
1454
3180
  };
1455
- }, [labelsRef]);
3181
+ }, [elementsRef, labelsRef, syncRefs]);
3182
+ useIsoLayoutEffect(() => {
3183
+ if (isDirtyRef.current) {
3184
+ flush();
3185
+ }
3186
+ });
3187
+ useIsoLayoutEffect(() => {
3188
+ return () => {
3189
+ mutationObserverRef.current?.disconnect();
3190
+ // React 18 Strict Mode replays effects without replaying callback refs.
3191
+ // Mark the retained map dirty so the replay rebuilds refs and observation.
3192
+ isDirtyRef.current = true;
3193
+ };
3194
+ }, []);
1456
3195
  const subscribeMapChange = useStableCallback(fn => {
1457
3196
  listeners.add(fn);
1458
3197
  return () => {
1459
3198
  listeners.delete(fn);
1460
3199
  };
1461
3200
  });
1462
- useIsoLayoutEffect(() => {
1463
- listeners.forEach(l => l(sortedMap));
1464
- }, [listeners, sortedMap]);
1465
3201
  const contextValue = React.useMemo(() => ({
1466
3202
  register,
1467
3203
  unregister,
1468
3204
  subscribeMapChange,
1469
- elementsRef,
1470
- labelsRef,
1471
3205
  nextIndexRef
1472
- }), [register, unregister, subscribeMapChange, elementsRef, labelsRef, nextIndexRef]);
3206
+ }), [register, unregister, subscribeMapChange, nextIndexRef]);
1473
3207
  return /*#__PURE__*/jsx(CompositeListContext.Provider, {
1474
3208
  value: contextValue,
1475
3209
  children: children
@@ -1481,15 +3215,66 @@ function createMap() {
1481
3215
  function createListeners() {
1482
3216
  return new Set();
1483
3217
  }
1484
- function sortByDocumentPosition(a, b) {
1485
- const position = a.compareDocumentPosition(b);
1486
- if (position & Node.DOCUMENT_POSITION_FOLLOWING || position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
1487
- return -1;
3218
+ function getCompositeListSnapshot(map) {
3219
+ const reservedIndices = new Set();
3220
+ const items = [];
3221
+ const automaticItems = [];
3222
+ map.forEach((registration, node) => {
3223
+ if (!node.isConnected) {
3224
+ return;
3225
+ }
3226
+ const index = registration.index;
3227
+ const item = {
3228
+ index: index ?? -1,
3229
+ element: node,
3230
+ registration
3231
+ };
3232
+ if (index === null) {
3233
+ automaticItems.push(item);
3234
+ } else if (index >= 0) {
3235
+ reservedIndices.add(index);
3236
+ items.push(item);
3237
+ }
3238
+ });
3239
+ let nextAutomaticIndex = 0;
3240
+ automaticItems.sort((a, b) => sortByDocumentPosition(a.element, b.element));
3241
+ automaticItems.forEach(item => {
3242
+ while (reservedIndices.has(nextAutomaticIndex)) {
3243
+ nextAutomaticIndex += 1;
3244
+ }
3245
+ item.index = nextAutomaticIndex;
3246
+ items.push(item);
3247
+ nextAutomaticIndex += 1;
3248
+ });
3249
+ if (reservedIndices.size > 0) {
3250
+ items.sort((a, b) => a.index - b.index);
1488
3251
  }
1489
- if (position & Node.DOCUMENT_POSITION_PRECEDING || position & Node.DOCUMENT_POSITION_CONTAINS) {
1490
- return 1;
3252
+ return [items, automaticItems.map(item => item.element)];
3253
+ }
3254
+ function getCommonAncestor(firstNode, lastNode) {
3255
+ let ancestor = firstNode.parentElement;
3256
+
3257
+ // The `parentElement` walk cannot cross shadow boundaries, so the native
3258
+ // `contains` is sufficient here.
3259
+ while (ancestor && !ancestor.contains(lastNode)) {
3260
+ ancestor = ancestor.parentElement;
3261
+ }
3262
+ return ancestor;
3263
+ }
3264
+ function hasMovedNode(entries) {
3265
+ for (const entry of entries) {
3266
+ for (let i = 0; i < entry.removedNodes.length; i += 1) {
3267
+ if (entry.removedNodes[i].isConnected) {
3268
+ return true;
3269
+ }
3270
+ }
1491
3271
  }
1492
- return 0;
3272
+ return false;
3273
+ }
3274
+ function sortByDocumentPosition(a, b) {
3275
+ // `DOCUMENT_POSITION_CONTAINED_BY` is always reported alongside `FOLLOWING`, and `CONTAINS`
3276
+ // alongside `PRECEDING`, so testing `FOLLOWING` alone orders siblings and nested items alike.
3277
+ return a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
1493
3278
  }
1494
3279
 
1495
3280
  /**
@@ -1586,7 +3371,6 @@ const arrow = (options, deps) => ({
1586
3371
  options: [options, deps]
1587
3372
  });
1588
3373
 
1589
- const nativeHideFn = hide$1().fn;
1590
3374
  const hide = {
1591
3375
  name: 'hide',
1592
3376
  async fn(state) {
@@ -1597,10 +3381,15 @@ const hide = {
1597
3381
  y
1598
3382
  } = state.rects.reference;
1599
3383
  const anchorHidden = width === 0 && height === 0 && x === 0 && y === 0;
1600
- const nativeHideResult = await nativeHideFn(state);
3384
+ // Mirrors Floating UI's `hide()` referenceHidden strategy. Floating UI injects
3385
+ // `detectOverflow` into the middleware platform before invoking middleware.
3386
+ const overflow = await state.platform.detectOverflow(state, {
3387
+ elementContext: 'reference'
3388
+ });
3389
+ const referenceHidden = overflow.top - height >= 0 || overflow.right - width >= 0 || overflow.bottom - height >= 0 || overflow.left - width >= 0;
1601
3390
  return {
1602
3391
  data: {
1603
- referenceHidden: nativeHideResult.data?.referenceHidden || anchorHidden
3392
+ referenceHidden: referenceHidden || anchorHidden
1604
3393
  }
1605
3394
  };
1606
3395
  }
@@ -1610,75 +3399,13 @@ const DEFAULT_SIDES = {
1610
3399
  sideX: 'left',
1611
3400
  sideY: 'top'
1612
3401
  };
1613
- const adaptiveOrigin = {
1614
- name: 'adaptiveOrigin',
1615
- async fn(state) {
1616
- const {
1617
- x: rawX,
1618
- y: rawY,
1619
- rects: {
1620
- floating: floatRect
1621
- },
1622
- elements: {
1623
- floating
1624
- },
1625
- platform,
1626
- strategy,
1627
- placement
1628
- } = state;
1629
- const win = getWindow(floating);
1630
- const styles = win.getComputedStyle(floating);
1631
- const hasTransition = styles.transitionDuration !== '0s' && styles.transitionDuration !== '';
1632
- if (!hasTransition) {
1633
- return {
1634
- x: rawX,
1635
- y: rawY,
1636
- data: DEFAULT_SIDES
1637
- };
1638
- }
1639
- const offsetParent = await platform.getOffsetParent?.(floating);
1640
- let offsetDimensions = {
1641
- width: 0,
1642
- height: 0
1643
- };
1644
3402
 
1645
- // For fixed strategy, prefer visualViewport if available
1646
- if (strategy === 'fixed' && win?.visualViewport) {
1647
- offsetDimensions = {
1648
- width: win.visualViewport.width,
1649
- height: win.visualViewport.height
1650
- };
1651
- } else if (offsetParent === win) {
1652
- const doc = ownerDocument(floating);
1653
- offsetDimensions = {
1654
- width: doc.documentElement.clientWidth,
1655
- height: doc.documentElement.clientHeight
1656
- };
1657
- } else if (await platform.isElement?.(offsetParent)) {
1658
- offsetDimensions = await platform.getDimensions(offsetParent);
1659
- }
1660
- const currentSide = getSide(placement);
1661
- let x = rawX;
1662
- let y = rawY;
1663
- if (currentSide === 'left') {
1664
- x = offsetDimensions.width - (rawX + floatRect.width);
1665
- }
1666
- if (currentSide === 'top') {
1667
- y = offsetDimensions.height - (rawY + floatRect.height);
1668
- }
1669
- const sideX = currentSide === 'left' ? 'right' : DEFAULT_SIDES.sideX;
1670
- const sideY = currentSide === 'top' ? 'bottom' : DEFAULT_SIDES.sideY;
1671
- return {
1672
- x,
1673
- y,
1674
- data: {
1675
- sideX,
1676
- sideY
1677
- }
1678
- };
1679
- }
1680
- };
3403
+ // Self-contained stand-in for Floating UI's `Middleware` type. Referencing the real type in
3404
+ // store state makes `tsc` declaration emit reference `Platform`/`detectOverflow` from
3405
+ // `@floating-ui/core` internals, which fails as non-portable (TS2883) in consuming builds.
1681
3406
 
3407
+ const AVAILABLE_WIDTH_VAR = '--available-width';
3408
+ const AVAILABLE_HEIGHT_VAR = '--available-height';
1682
3409
  function getLogicalSide(sideParam, renderedSide, isRtl) {
1683
3410
  const isLogicalSideParam = sideParam === 'inline-start' || sideParam === 'inline-end';
1684
3411
  const logicalRight = isRtl ? 'inline-start' : 'inline-end';
@@ -1714,6 +3441,9 @@ function getOffsetData(state, sideParam, isRtl) {
1714
3441
  * `useFloating` hook.
1715
3442
  */
1716
3443
  function useAnchorPositioning(params) {
3444
+ return useAnchorPositioningWithHook(params, useBaseUIFloating);
3445
+ }
3446
+ function useAnchorPositioningWithHook(params, useFloatingHook) {
1717
3447
  const {
1718
3448
  // Public parameters
1719
3449
  anchor,
@@ -1733,7 +3463,7 @@ function useAnchorPositioning(params) {
1733
3463
  floatingRootContext,
1734
3464
  mounted,
1735
3465
  collisionAvoidance,
1736
- shiftCrossAxis = false,
3466
+ shift: shift$1,
1737
3467
  nodeId,
1738
3468
  adaptiveOrigin,
1739
3469
  lazyFlip = false,
@@ -1746,6 +3476,8 @@ function useAnchorPositioning(params) {
1746
3476
  const collisionAvoidanceSide = collisionAvoidance.side || 'flip';
1747
3477
  const collisionAvoidanceAlign = collisionAvoidance.align || 'flip';
1748
3478
  const collisionAvoidanceFallbackAxisSide = collisionAvoidance.fallbackAxisSide || 'end';
3479
+ const shiftCrossAxis = shift$1?.crossAxis ?? false;
3480
+ const shiftRootBoundary = shift$1?.rootBoundary;
1749
3481
  const anchorFn = typeof anchor === 'function' ? anchor : undefined;
1750
3482
  const anchorFnCallback = useStableCallback(anchorFn);
1751
3483
  const anchorDep = anchorFn ? anchorFnCallback : anchor;
@@ -1763,30 +3495,32 @@ function useAnchorPositioning(params) {
1763
3495
  }[sideParam];
1764
3496
  const placement = align === 'center' ? side : `${side}-${align}`;
1765
3497
  let collisionPadding = collisionPaddingParam;
3498
+ if (typeof collisionPadding === 'number') {
3499
+ collisionPadding = {
3500
+ top: collisionPadding,
3501
+ right: collisionPadding,
3502
+ bottom: collisionPadding,
3503
+ left: collisionPadding
3504
+ };
3505
+ } else if (collisionPadding) {
3506
+ collisionPadding = {
3507
+ top: collisionPadding.top || 0,
3508
+ right: collisionPadding.right || 0,
3509
+ bottom: collisionPadding.bottom || 0,
3510
+ left: collisionPadding.left || 0
3511
+ };
3512
+ }
1766
3513
 
1767
3514
  // Create a bias to the preferred side.
1768
3515
  // On iOS, when the mobile software keyboard opens, the input is exactly centered
1769
3516
  // in the viewport, but this can cause it to flip to the top undesirably.
3517
+ // The bias is only applied to `flip()` so it doesn't shift the resting position
3518
+ // computed by `shift()` and `size()` away from the requested `collisionPadding`.
1770
3519
  const bias = 1;
1771
3520
  const biasTop = sideParam === 'bottom' ? bias : 0;
1772
3521
  const biasBottom = sideParam === 'top' ? bias : 0;
1773
3522
  const biasLeft = sideParam === 'right' ? bias : 0;
1774
3523
  const biasRight = sideParam === 'left' ? bias : 0;
1775
- if (typeof collisionPadding === 'number') {
1776
- collisionPadding = {
1777
- top: collisionPadding + biasTop,
1778
- right: collisionPadding + biasRight,
1779
- bottom: collisionPadding + biasBottom,
1780
- left: collisionPadding + biasLeft
1781
- };
1782
- } else if (collisionPadding) {
1783
- collisionPadding = {
1784
- top: (collisionPadding.top || 0) + biasTop,
1785
- right: (collisionPadding.right || 0) + biasRight,
1786
- bottom: (collisionPadding.bottom || 0) + biasBottom,
1787
- left: (collisionPadding.left || 0) + biasLeft
1788
- };
1789
- }
1790
3524
  const commonCollisionProps = {
1791
3525
  boundary: collisionBoundary === 'clipping-ancestors' ? 'clippingAncestors' : collisionBoundary,
1792
3526
  padding: collisionPadding
@@ -1823,46 +3557,37 @@ function useAnchorPositioning(params) {
1823
3557
  // Ensure the popup flips if it's been limited by its --available-height and it resizes.
1824
3558
  // Since the size() padding is smaller than the flip() padding, flip() will take precedence.
1825
3559
  padding: {
1826
- top: collisionPadding.top + bias,
1827
- right: collisionPadding.right + bias,
1828
- bottom: collisionPadding.bottom + bias,
1829
- left: collisionPadding.left + bias
3560
+ top: collisionPadding.top + bias + biasTop,
3561
+ right: collisionPadding.right + bias + biasRight,
3562
+ bottom: collisionPadding.bottom + bias + biasBottom,
3563
+ left: collisionPadding.left + bias + biasLeft
1830
3564
  },
1831
3565
  mainAxis: !shiftCrossAxis && collisionAvoidanceSide === 'flip',
1832
3566
  crossAxis: collisionAvoidanceAlign === 'flip' ? 'alignment' : false,
1833
3567
  fallbackAxisSideDirection: collisionAvoidanceFallbackAxisSide
1834
3568
  });
1835
- const shiftMiddleware = shiftDisabled ? null : shift(data => {
1836
- const html = ownerDocument(data.elements.floating).documentElement;
1837
- return {
1838
- ...commonCollisionProps,
1839
- // Use the Layout Viewport to avoid shifting around when pinch-zooming
1840
- // for context menus.
1841
- rootBoundary: shiftCrossAxis ? {
1842
- x: 0,
1843
- y: 0,
1844
- width: html.clientWidth,
1845
- height: html.clientHeight
1846
- } : undefined,
1847
- mainAxis: collisionAvoidanceAlign !== 'none',
1848
- crossAxis: crossAxisShiftEnabled,
1849
- limiter: sticky || shiftCrossAxis ? undefined : limitShift(limitData => {
1850
- if (!arrowRef.current) {
1851
- return {};
1852
- }
1853
- const {
1854
- width,
1855
- height
1856
- } = arrowRef.current.getBoundingClientRect();
1857
- const sideAxis = getSideAxis(getSide(limitData.placement));
1858
- const arrowSize = sideAxis === 'y' ? width : height;
1859
- const offsetAmount = sideAxis === 'y' ? collisionPadding.left + collisionPadding.right : collisionPadding.top + collisionPadding.bottom;
1860
- return {
1861
- offset: arrowSize / 2 + offsetAmount / 2
1862
- };
1863
- })
1864
- };
1865
- }, [commonCollisionProps, sticky, shiftCrossAxis, collisionPadding, collisionAvoidanceAlign]);
3569
+ const shiftMiddleware = shiftDisabled ? null : shift({
3570
+ ...commonCollisionProps,
3571
+ // Use the Layout Viewport to avoid shifting around when pinch-zooming.
3572
+ rootBoundary: shiftRootBoundary,
3573
+ mainAxis: collisionAvoidanceAlign !== 'none',
3574
+ crossAxis: crossAxisShiftEnabled,
3575
+ limiter: sticky || shiftCrossAxis ? undefined : limitShift(limitData => {
3576
+ if (!arrowRef.current) {
3577
+ return {};
3578
+ }
3579
+ const {
3580
+ width,
3581
+ height
3582
+ } = arrowRef.current.getBoundingClientRect();
3583
+ const sideAxis = getSideAxis(getSide(limitData.placement));
3584
+ const arrowSize = sideAxis === 'y' ? width : height;
3585
+ const offsetAmount = sideAxis === 'y' ? collisionPadding.left + collisionPadding.right : collisionPadding.top + collisionPadding.bottom;
3586
+ return {
3587
+ offset: arrowSize / 2 + offsetAmount / 2
3588
+ };
3589
+ })
3590
+ }, [commonCollisionProps, sticky, shiftCrossAxis, shiftRootBoundary, collisionPadding, collisionAvoidanceAlign]);
1866
3591
 
1867
3592
  // https://floating-ui.com/docs/flip#combining-with-shift
1868
3593
  if (collisionAvoidanceSide === 'shift' || collisionAvoidanceAlign === 'shift' || align === 'center') {
@@ -1884,8 +3609,8 @@ function useAnchorPositioning(params) {
1884
3609
  return;
1885
3610
  }
1886
3611
  const floatingStyle = floating.style;
1887
- floatingStyle.setProperty('--available-width', `${availableWidth}px`);
1888
- floatingStyle.setProperty('--available-height', `${availableHeight}px`);
3612
+ floatingStyle.setProperty(AVAILABLE_WIDTH_VAR, `${availableWidth}px`);
3613
+ floatingStyle.setProperty(AVAILABLE_HEIGHT_VAR, `${availableHeight}px`);
1889
3614
 
1890
3615
  // Snap anchor dimensions to device pixels to ensure the popup's visual width matches the anchor's one.
1891
3616
  const dpr = getWindow(floating).devicePixelRatio || 1;
@@ -1967,7 +3692,7 @@ function useAnchorPositioning(params) {
1967
3692
  context,
1968
3693
  isPositioned,
1969
3694
  floatingStyles: originalFloatingStyles
1970
- } = useFloating({
3695
+ } = useFloatingHook({
1971
3696
  rootContext: floatingRootContext,
1972
3697
  open: keepMounted ? mounted : undefined,
1973
3698
  placement,
@@ -1986,14 +3711,41 @@ function useAnchorPositioning(params) {
1986
3711
  // This ensures the popup is inside the viewport initially before it gets positioned.
1987
3712
  const resolvedPosition = isPositioned ? positionMethod : 'fixed';
1988
3713
  const floatingStyles = React.useMemo(() => {
1989
- const base = adaptiveOrigin ? {
1990
- position: resolvedPosition,
1991
- [sideX]: x,
1992
- [sideY]: y
1993
- } : {
1994
- position: resolvedPosition,
1995
- ...originalFloatingStyles
1996
- };
3714
+ let base;
3715
+ if (!isPositioned) {
3716
+ // Until a position for the current open is computed, ignore any coordinates retained from a
3717
+ // previous open (or from a pass that measured the hidden popup as 0x0). Rendering the
3718
+ // full-size popup at such stale coordinates can overflow the layout viewport, which makes
3719
+ // mobile Chrome zoom the page out and reflow everything the popup is anchored to.
3720
+ base = {
3721
+ position: resolvedPosition,
3722
+ top: 0,
3723
+ left: 0
3724
+ };
3725
+ } else if (adaptiveOrigin) {
3726
+ base = {
3727
+ position: resolvedPosition,
3728
+ [sideX]: x,
3729
+ [sideY]: y
3730
+ };
3731
+ } else {
3732
+ base = {
3733
+ ...originalFloatingStyles,
3734
+ position: resolvedPosition
3735
+ };
3736
+ }
3737
+
3738
+ // Seed the available size vars so consumer `max-height: min(x, var(--available-height))` rules
3739
+ // resolve to a valid length on the first positioning pass, before `size()` writes the real
3740
+ // values. Without a fallback the unresolved `var()` invalidates the whole declaration, so the
3741
+ // popup is measured unconstrained while `flip()` picks its side, against the full content
3742
+ // height rather than the capped one. Seeded unconditionally (not only while `!isPositioned`):
3743
+ // the keys must stay present with a constant value so React's per-property style diff never
3744
+ // rewrites them after mount, preserving the px values `size()` sets imperatively. Moving them
3745
+ // into the `!isPositioned` branch makes React remove them once positioned, wiping `size()`'s
3746
+ // values and leaving the popup unconstrained.
3747
+ base[AVAILABLE_WIDTH_VAR] = '100vw';
3748
+ base[AVAILABLE_HEIGHT_VAR] = '100vh';
1997
3749
  if (!isPositioned) {
1998
3750
  base.opacity = 0;
1999
3751
  }
@@ -2044,10 +3796,10 @@ function useAnchorPositioning(params) {
2044
3796
  // and flips back lazily, not eagerly. Ideal for filtered lists that change
2045
3797
  // the size of the popup dynamically to avoid unwanted flipping when typing.
2046
3798
  useIsoLayoutEffect(() => {
2047
- if (lazyFlip && mounted && isPositioned) {
3799
+ if (lazyFlip && mounted && isPositioned && renderedSide !== side) {
2048
3800
  setMountSide(renderedSide);
2049
3801
  }
2050
- }, [lazyFlip, mounted, isPositioned, renderedSide]);
3802
+ }, [lazyFlip, mounted, isPositioned, renderedSide, side]);
2051
3803
  const arrowStyles = React.useMemo(() => ({
2052
3804
  position: 'absolute',
2053
3805
  top: middlewareData.arrow?.y,
@@ -2132,64 +3884,57 @@ function useAnchoredPopupScrollLock(enabled, touchOpen, positionerElement, refer
2132
3884
  useScrollLock(enabled && (!touchOpen || touchOpenShouldLockScroll), referenceElement);
2133
3885
  }
2134
3886
 
2135
- let IndexGuessBehavior = /*#__PURE__*/function (IndexGuessBehavior) {
2136
- IndexGuessBehavior[IndexGuessBehavior["None"] = 0] = "None";
2137
- IndexGuessBehavior[IndexGuessBehavior["GuessFromOrder"] = 1] = "GuessFromOrder";
2138
- return IndexGuessBehavior;
2139
- }({});
2140
-
2141
3887
  /**
2142
3888
  * Used to register a list item and its index (DOM position) in the `CompositeList`.
2143
3889
  */
2144
3890
  function useCompositeListItem(params = {}) {
2145
3891
  const {
3892
+ guess,
2146
3893
  label,
2147
3894
  metadata,
2148
3895
  textRef,
2149
- indexGuessBehavior,
2150
3896
  index: externalIndex
2151
3897
  } = params;
2152
3898
  const {
2153
3899
  register,
2154
3900
  unregister,
2155
3901
  subscribeMapChange,
2156
- elementsRef,
2157
- labelsRef,
2158
3902
  nextIndexRef
2159
3903
  } = useCompositeListContext();
3904
+
3905
+ // Guess the index from the render order. This avoids a re-render after mount for
3906
+ // flat lists rendered in DOM order; when the guess is wrong (grouped or out-of-order
3907
+ // rendering), the commit flush corrects it before paint.
2160
3908
  const indexRef = React.useRef(-1);
2161
- const [index, setIndex] = React.useState(externalIndex ?? (indexGuessBehavior === IndexGuessBehavior.GuessFromOrder ? () => {
3909
+ const [internalIndex, setInternalIndex] = React.useState(externalIndex == null && guess ? () => {
2162
3910
  if (indexRef.current === -1) {
2163
3911
  const newIndex = nextIndexRef.current;
2164
3912
  nextIndexRef.current += 1;
2165
3913
  indexRef.current = newIndex;
2166
3914
  }
2167
3915
  return indexRef.current;
2168
- } : -1));
3916
+ } : -1);
3917
+ const index = externalIndex ?? internalIndex;
2169
3918
  const componentRef = React.useRef(null);
3919
+
3920
+ // Deliberately identity-sensitive: nested items sharing one DOM node rely on ref attachment
3921
+ // order to decide which registration wins, and republishing from an effect instead would let
3922
+ // an inner item's later update silently take ownership from the outer one.
2170
3923
  const ref = React.useCallback(node => {
2171
- componentRef.current = node;
2172
- if (index !== -1 && node !== null) {
2173
- elementsRef.current[index] = node;
2174
- if (labelsRef) {
2175
- const isLabelDefined = label !== undefined;
2176
- labelsRef.current[index] = isLabelDefined ? label : textRef?.current?.textContent ?? node.textContent;
2177
- }
3924
+ const previousNode = componentRef.current;
3925
+ if (previousNode) {
3926
+ unregister(previousNode);
2178
3927
  }
2179
- }, [index, elementsRef, labelsRef, label, textRef]);
2180
- useIsoLayoutEffect(() => {
2181
- if (externalIndex != null) {
2182
- return undefined;
2183
- }
2184
- const node = componentRef.current;
3928
+ componentRef.current = node;
2185
3929
  if (node) {
2186
- register(node, metadata);
2187
- return () => {
2188
- unregister(node);
2189
- };
3930
+ register(node, {
3931
+ metadata: metadata ?? null,
3932
+ index: externalIndex ?? null,
3933
+ label,
3934
+ textRef
3935
+ });
2190
3936
  }
2191
- return undefined;
2192
- }, [externalIndex, register, unregister, metadata]);
3937
+ }, [externalIndex, register, unregister, metadata, label, textRef]);
2193
3938
  useIsoLayoutEffect(() => {
2194
3939
  if (externalIndex != null) {
2195
3940
  return undefined;
@@ -2197,14 +3942,14 @@ function useCompositeListItem(params = {}) {
2197
3942
  return subscribeMapChange(map => {
2198
3943
  const i = componentRef.current ? map.get(componentRef.current)?.index : null;
2199
3944
  if (i != null) {
2200
- setIndex(i);
3945
+ setInternalIndex(i);
2201
3946
  }
2202
3947
  });
2203
- }, [externalIndex, subscribeMapChange, setIndex]);
3948
+ }, [externalIndex, subscribeMapChange]);
2204
3949
  return {
2205
3950
  ref,
2206
3951
  index
2207
3952
  };
2208
3953
  }
2209
3954
 
2210
- export { removeItem as A, adaptiveOrigin as B, CompositeList as C, DEFAULT_VALIDITY_STATE as D, isGroupedItems as E, FieldRootContext as F, DEFAULT_FIELD_ROOT_CONTEXT as G, DEFAULT_FIELD_STATE_ATTRIBUTES as H, IndexGuessBehavior as I, getDefaultLabelId as J, LabelableContext as L, useFormContext as a, useFieldRootContext as b, useLabelableId as c, useRegisterFieldControl as d, compareItemEquality as e, fieldValidityMapping as f, findItemIndex as g, hasNullItemLabel as h, useFloatingRootContext as i, useListNavigation as j, useTypeahead as k, defaultItemEquality as l, stringifyAsLabel as m, areArraysEqual as n, getPseudoElementBounds as o, resolveMultipleLabels as p, resolveSelectedLabel as q, resolveAriaLabelledBy as r, stringifyAsValue as s, useAnchoredPopupScrollLock as t, useLabelableContext as u, useAnchorPositioning as v, usePositioner as w, selectedValueIncludes as x, getDisabledMountTransitionStyles as y, useCompositeListItem as z };
3955
+ export { useCompositeListItem as A, removeItem as B, CompositeList as C, DEFAULT_VALIDITY_STATE as D, isGroupedItems as E, FieldRootContext as F, findSelectionIndex as G, selectedValueIncludes as H, DEFAULT_FIELD_ROOT_CONTEXT as I, DEFAULT_FIELD_STATE_ATTRIBUTES as J, getDefaultLabelId as K, LabelableContext as L, DEFAULT_SIDES as M, useFormContext as a, useFieldRootContext as b, useLabelableId as c, useRegisterFieldControl as d, compareItemEquality as e, fieldValidityMapping as f, findItemIndex as g, hasNullItemLabel as h, useFloatingRootContext as i, useListNavigation as j, useTypeahead as k, useOnFirstRender as l, defaultItemEquality as m, stringifyAsLabel as n, areArraysEqual as o, isMouseWithinBounds as p, resolveMultipleLabels as q, resolveAriaLabelledBy as r, stringifyAsValue as s, resolveSelectedLabel as t, useLabelableContext as u, useAnchoredPopupScrollLock as v, useAnchorPositioning as w, usePositioner as x, platform as y, getDisabledMountTransitionStyles as z };