@jobber/components 9.6.0 → 9.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/dist/ActivityIndicator-cjs.js +1 -0
  2. package/dist/ActivityIndicator-es.js +1 -0
  3. package/dist/Autocomplete/index.cjs +2 -0
  4. package/dist/Autocomplete/index.mjs +2 -0
  5. package/dist/Autocomplete-cjs.js +6 -4
  6. package/dist/Autocomplete-es.js +3 -1
  7. package/dist/Banner-cjs.js +1 -0
  8. package/dist/Banner-es.js +1 -0
  9. package/dist/Card/index.cjs +2 -1
  10. package/dist/Card/index.mjs +2 -1
  11. package/dist/Chip/index.cjs +2 -0
  12. package/dist/Chip/index.mjs +2 -0
  13. package/dist/Chip-cjs.js +1 -0
  14. package/dist/Chip-es.js +1 -0
  15. package/dist/Chips/InternalChipDismissible/hooks/index.cjs +2 -0
  16. package/dist/Chips/InternalChipDismissible/hooks/index.mjs +2 -0
  17. package/dist/Chips/InternalChipDismissible/index.cjs +2 -0
  18. package/dist/Chips/InternalChipDismissible/index.mjs +2 -0
  19. package/dist/Chips/index.cjs +2 -0
  20. package/dist/Chips/index.mjs +2 -0
  21. package/dist/Combobox/index.cjs +4 -2
  22. package/dist/Combobox/index.mjs +4 -2
  23. package/dist/{ComboboxSeparator-cjs.js → ComboboxChipRemove-cjs.js} +455 -501
  24. package/dist/{ComboboxSeparator-es.js → ComboboxChipRemove-es.js} +456 -500
  25. package/dist/ComboboxPrimitive-cjs.js +30 -29
  26. package/dist/ComboboxPrimitive-es.js +3 -2
  27. package/dist/ConfirmationModal/index.cjs +2 -0
  28. package/dist/ConfirmationModal/index.mjs +2 -0
  29. package/dist/ConfirmationModal-cjs.js +1 -0
  30. package/dist/ConfirmationModal-es.js +1 -0
  31. package/dist/DataDump/index.cjs +2 -1
  32. package/dist/DataDump/index.mjs +2 -1
  33. package/dist/DataDump-cjs.js +1 -0
  34. package/dist/DataDump-es.js +1 -0
  35. package/dist/DataList/components/DataListActions/index.cjs +2 -0
  36. package/dist/DataList/components/DataListActions/index.mjs +2 -0
  37. package/dist/DataList/components/DataListBulkActions/index.cjs +2 -0
  38. package/dist/DataList/components/DataListBulkActions/index.mjs +2 -0
  39. package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +2 -0
  40. package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +2 -0
  41. package/dist/DataList/components/DataListFilters/index.cjs +2 -0
  42. package/dist/DataList/components/DataListFilters/index.mjs +2 -0
  43. package/dist/DataList/components/DataListHeader/index.cjs +2 -0
  44. package/dist/DataList/components/DataListHeader/index.mjs +2 -0
  45. package/dist/DataList/components/DataListItem/index.cjs +2 -0
  46. package/dist/DataList/components/DataListItem/index.mjs +2 -0
  47. package/dist/DataList/components/DataListItemActions/index.cjs +2 -0
  48. package/dist/DataList/components/DataListItemActions/index.mjs +2 -0
  49. package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +2 -0
  50. package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +2 -0
  51. package/dist/DataList/components/DataListItems/index.cjs +2 -0
  52. package/dist/DataList/components/DataListItems/index.mjs +2 -0
  53. package/dist/DataList/components/DataListLayout/index.cjs +2 -0
  54. package/dist/DataList/components/DataListLayout/index.mjs +2 -0
  55. package/dist/DataList/components/DataListLayoutActions/index.cjs +2 -0
  56. package/dist/DataList/components/DataListLayoutActions/index.mjs +2 -0
  57. package/dist/DataList/index.cjs +2 -0
  58. package/dist/DataList/index.mjs +2 -0
  59. package/dist/DataTable-cjs.js +1 -0
  60. package/dist/DataTable-es.js +1 -0
  61. package/dist/DatePicker/index.cjs +2 -0
  62. package/dist/DatePicker/index.mjs +2 -0
  63. package/dist/DatePicker-cjs.js +8 -7
  64. package/dist/DatePicker-es.js +6 -5
  65. package/dist/Dialog/index.cjs +2 -1
  66. package/dist/Dialog/index.mjs +2 -1
  67. package/dist/DrawerDescription-cjs.js +99 -268
  68. package/dist/DrawerDescription-es.js +103 -270
  69. package/dist/DrawerGrid-cjs.js +1 -0
  70. package/dist/DrawerGrid-es.js +1 -0
  71. package/dist/DrawerTitle-cjs.js +6 -2
  72. package/dist/DrawerTitle-es.js +7 -3
  73. package/dist/FeatureSwitch-cjs.js +1 -0
  74. package/dist/FeatureSwitch-es.js +1 -0
  75. package/dist/FieldDescription-cjs.js +181 -184
  76. package/dist/FieldDescription-es.js +184 -187
  77. package/dist/FilterPicker/components/FilterPickerActivator/index.cjs +2 -0
  78. package/dist/FilterPicker/components/FilterPickerActivator/index.mjs +2 -0
  79. package/dist/FilterPicker/components/FilterPickerContent/index.cjs +2 -0
  80. package/dist/FilterPicker/components/FilterPickerContent/index.mjs +2 -0
  81. package/dist/FilterPicker/components/FilterPickerTrigger/index.cjs +2 -0
  82. package/dist/FilterPicker/components/FilterPickerTrigger/index.mjs +2 -0
  83. package/dist/FilterPicker/index.cjs +2 -0
  84. package/dist/FilterPicker/index.mjs +2 -0
  85. package/dist/FilterPickerContent-cjs.js +5 -4
  86. package/dist/FilterPickerContent-es.js +2 -1
  87. package/dist/FormFieldWrapper-cjs.js +1 -0
  88. package/dist/FormFieldWrapper-es.js +1 -0
  89. package/dist/FormatFile/index.cjs +2 -0
  90. package/dist/FormatFile/index.mjs +2 -0
  91. package/dist/FormatFile-cjs.js +1 -0
  92. package/dist/FormatFile-es.js +1 -0
  93. package/dist/Gallery/index.cjs +2 -0
  94. package/dist/Gallery/index.mjs +2 -0
  95. package/dist/InputAvatar-cjs.js +1 -0
  96. package/dist/InputAvatar-es.js +1 -0
  97. package/dist/InputDate/index.cjs +2 -0
  98. package/dist/InputDate/index.mjs +2 -0
  99. package/dist/InputDate-cjs.js +1 -0
  100. package/dist/InputDate-es.js +1 -0
  101. package/dist/InputNumber/index.cjs +2 -1
  102. package/dist/InputNumber/index.mjs +2 -1
  103. package/dist/InternalChipDismissible-cjs.js +1 -0
  104. package/dist/InternalChipDismissible-es.js +1 -0
  105. package/dist/LightBox/index.cjs +2 -0
  106. package/dist/LightBox/index.mjs +2 -0
  107. package/dist/LightBox-cjs.js +1 -0
  108. package/dist/LightBox-es.js +1 -0
  109. package/dist/Menu/index.cjs +2 -1
  110. package/dist/Menu/index.mjs +2 -1
  111. package/dist/Menu-cjs.js +6 -4
  112. package/dist/Menu-es.js +3 -1
  113. package/dist/MenuSubmenuTrigger-cjs.js +164 -206
  114. package/dist/MenuSubmenuTrigger-es.js +152 -194
  115. package/dist/Modal/index.cjs +3 -1
  116. package/dist/Modal/index.mjs +4 -2
  117. package/dist/NumberFieldInput-cjs.js +308 -187
  118. package/dist/NumberFieldInput-es.js +308 -187
  119. package/dist/Page/index.cjs +2 -1
  120. package/dist/Page/index.mjs +2 -1
  121. package/dist/Page-cjs.js +1 -0
  122. package/dist/Page-es.js +1 -0
  123. package/dist/Popover/index.cjs +2 -0
  124. package/dist/Popover/index.mjs +2 -0
  125. package/dist/Popover-cjs.js +9 -7
  126. package/dist/Popover-es.js +3 -1
  127. package/dist/ProgressBar-cjs.js +1 -0
  128. package/dist/ProgressBar-es.js +1 -0
  129. package/dist/ScrollAreaViewport-cjs.js +1093 -747
  130. package/dist/ScrollAreaViewport-es.js +1081 -734
  131. package/dist/Select/index.cjs +2 -1
  132. package/dist/Select/index.mjs +2 -1
  133. package/dist/SelectGroupLabel-cjs.js +306 -207
  134. package/dist/SelectGroupLabel-es.js +302 -203
  135. package/dist/SideDrawer-cjs.js +1 -0
  136. package/dist/SideDrawer-es.js +1 -0
  137. package/dist/Spinner-cjs.js +1 -0
  138. package/dist/Spinner-es.js +1 -0
  139. package/dist/Tooltip/index.cjs +2 -0
  140. package/dist/Tooltip/index.mjs +2 -0
  141. package/dist/Tooltip-cjs.js +7 -6
  142. package/dist/Tooltip-es.js +2 -1
  143. package/dist/dialogReturnFocus-cjs.js +1 -1
  144. package/dist/dialogReturnFocus-es.js +2 -2
  145. package/dist/floating-ui.react-cjs.js +192 -2564
  146. package/dist/floating-ui.react-dom-cjs.js +2103 -0
  147. package/dist/floating-ui.react-dom-es.js +2072 -0
  148. package/dist/floating-ui.react-es.js +156 -2519
  149. package/dist/floating-ui.utils-cjs.js +342 -0
  150. package/dist/floating-ui.utils-es.js +300 -0
  151. package/dist/index.cjs +4 -3
  152. package/dist/index.mjs +4 -3
  153. package/dist/primitives/BottomSheet/index.cjs +1 -1
  154. package/dist/primitives/BottomSheet/index.mjs +1 -1
  155. package/dist/primitives/ComboboxPrimitive/index.cjs +4 -2
  156. package/dist/primitives/ComboboxPrimitive/index.mjs +4 -2
  157. package/dist/primitives/SelectPrimitive/index.cjs +2 -0
  158. package/dist/primitives/SelectPrimitive/index.mjs +2 -0
  159. package/dist/primitives/index.cjs +3 -2
  160. package/dist/primitives/index.mjs +3 -2
  161. package/dist/scrollEdges-cjs.js +11 -1
  162. package/dist/scrollEdges-es.js +11 -1
  163. package/dist/sharedHelpers/i18n/index.cjs +1 -0
  164. package/dist/sharedHelpers/i18n/index.d.ts +1 -0
  165. package/dist/sharedHelpers/i18n/index.mjs +1 -1
  166. package/dist/sharedHelpers/i18n/releasedLanguages.d.ts +12 -0
  167. package/dist/sharedHelpers/i18n/useReleasedLocale.d.ts +6 -0
  168. package/dist/showToast-cjs.js +1 -1
  169. package/dist/showToast-es.js +1 -1
  170. package/dist/testUtils/releasedLanguages.d.ts +16 -0
  171. package/dist/unstyledPrimitives/index.cjs +478 -720
  172. package/dist/unstyledPrimitives/index.mjs +421 -663
  173. package/dist/useAtlantisI18n-cjs.js +44 -11
  174. package/dist/useAtlantisI18n-es.js +44 -12
  175. package/dist/useCompositeListItem-cjs.js +361 -2108
  176. package/dist/useCompositeListItem-es.js +331 -2076
  177. package/dist/useLabel-cjs.js +3 -2
  178. package/dist/useLabel-es.js +3 -2
  179. package/dist/useRenderElement-cjs.js +7 -9
  180. package/dist/useRenderElement-es.js +7 -9
  181. package/dist/useScrollLock-cjs.js +344 -819
  182. package/dist/useScrollLock-es.js +296 -732
  183. package/dist/useScrollToActive-cjs.js +5 -4
  184. package/dist/useScrollToActive-es.js +2 -1
  185. package/package.json +3 -3
  186. package/dist/scrollable-cjs.js +0 -58
  187. package/dist/scrollable-es.js +0 -54
@@ -1,1720 +1,9 @@
1
1
  import * as React from 'react';
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';
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';
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 * 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
- };
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';
1718
7
 
1719
8
  function useFloatingRootContext(options) {
1720
9
  const {
@@ -1763,17 +52,16 @@ function useFloatingRootContext(options) {
1763
52
  }
1764
53
 
1765
54
  /**
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.
55
+ * Provides data to position a floating element and context to add interactions.
56
+ * @see https://floating-ui.com/docs/useFloating
1768
57
  */
1769
- function useBaseUIFloating(options) {
1770
- return useFloatingWithStore(options, options.rootContext);
1771
- }
1772
- function useFloatingWithStore(options, store) {
58
+ function useFloating(options = {}) {
1773
59
  const {
1774
60
  nodeId,
1775
61
  externalTree
1776
62
  } = options;
63
+ const internalStore = useFloatingRootContext(options);
64
+ const store = options.rootContext || internalStore;
1777
65
  const referenceElement = store.useState('referenceElement');
1778
66
  const floatingElement = store.useState('floatingElement');
1779
67
  const domReferenceElement = store.useState('domReferenceElement');
@@ -1789,7 +77,7 @@ function useFloatingWithStore(options, store) {
1789
77
  floating: floatingElement,
1790
78
  domReference: domReferenceElement
1791
79
  }), [referenceElement, floatingElement, domReferenceElement]);
1792
- const position = useFloating({
80
+ const position = useFloating$1({
1793
81
  ...options,
1794
82
  elements: {
1795
83
  ...storeElements,
@@ -1879,13 +167,6 @@ function useFloatingWithStore(options, store) {
1879
167
  }
1880
168
 
1881
169
  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
- }
1889
170
  function doSwitch(orientation, vertical, horizontal) {
1890
171
  switch (orientation) {
1891
172
  case 'vertical':
@@ -2292,9 +573,6 @@ function useListNavigation(context, props) {
2292
573
  }),
2293
574
  // Safari
2294
575
  onMouseMove(event) {
2295
- if (isStationaryWebKitPointer(event)) {
2296
- return;
2297
- }
2298
576
  forceSyncFocusRef.current = true;
2299
577
  forceScrollIntoViewRef.current = false;
2300
578
  if (focusItemOnHover) {
@@ -2358,10 +636,7 @@ function useListNavigation(context, props) {
2358
636
  }
2359
637
  commonOnKeyDown(event);
2360
638
  },
2361
- onPointerMove(event) {
2362
- if (isStationaryWebKitPointer(event)) {
2363
- return;
2364
- }
639
+ onPointerMove() {
2365
640
  isPointerModalityRef.current = true;
2366
641
  }
2367
642
  };
@@ -2482,19 +757,18 @@ function useTypeahead(context, props) {
2482
757
  const prevIndexRef = React.useRef(selectedIndex ?? activeIndex ?? -1);
2483
758
  const matchIndexRef = React.useRef(null);
2484
759
  const onKeyDown = useStableCallback(event => {
2485
- function getElement(index) {
2486
- return elementsRef?.current[index];
760
+ function isVisible(index) {
761
+ const element = elementsRef?.current[index];
762
+ return !element || isElementVisible(element);
2487
763
  }
2488
764
  function isItemAvailable(index) {
2489
- const element = getElement(index);
2490
- if (element && !isElementVisible(element) || element?.matches(':disabled')) {
765
+ if (!isVisible(index)) {
2491
766
  return false;
2492
767
  }
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`.
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.
2498
772
  return disabledIndices == null || !isListIndexDisabled(EMPTY_ARRAY, index, disabledIndices);
2499
773
  }
2500
774
  function getMatchingIndex(list, string, startIndex = 0) {
@@ -2602,6 +876,12 @@ function useTypeahead(context, props) {
2602
876
  stringRef.current = '';
2603
877
  }
2604
878
  }, [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]);
2605
885
  const sharedProps = React.useMemo(() => ({
2606
886
  onKeyDown,
2607
887
  onBlur
@@ -2641,12 +921,6 @@ function findItemIndex(itemValues, selectedValue, comparer) {
2641
921
  return compareItemEquality(itemValue, selectedValue, comparer);
2642
922
  });
2643
923
  }
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
- }
2650
924
  function removeItem(selectedValues, itemValue, comparer) {
2651
925
  return selectedValues.filter(selectedValue => !compareItemEquality(itemValue, selectedValue, comparer));
2652
926
  }
@@ -2769,6 +1043,38 @@ function resolveMultipleLabels(values, items, itemToStringLabel) {
2769
1043
  }, []);
2770
1044
  }
2771
1045
 
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
+
2772
1078
  const DEFAULT_VALIDITY_STATE = {
2773
1079
  badInput: false,
2774
1080
  customError: false,
@@ -2800,11 +1106,11 @@ const fieldValidityMapping = {
2800
1106
  }
2801
1107
  if (value) {
2802
1108
  return {
2803
- 'data-valid': ''
1109
+ [FieldControlDataAttributes.valid]: ''
2804
1110
  };
2805
1111
  }
2806
1112
  return {
2807
- 'data-invalid': ''
1113
+ [FieldControlDataAttributes.invalid]: ''
2808
1114
  };
2809
1115
  }
2810
1116
  };
@@ -2821,22 +1127,29 @@ const DEFAULT_FIELD_ROOT_CONTEXT = {
2821
1127
  },
2822
1128
  setValidityData: NOOP,
2823
1129
  disabled: undefined,
1130
+ touched: DEFAULT_FIELD_STATE_ATTRIBUTES.touched,
2824
1131
  setTouched: NOOP,
1132
+ dirty: DEFAULT_FIELD_STATE_ATTRIBUTES.dirty,
2825
1133
  setDirty: NOOP,
1134
+ filled: DEFAULT_FIELD_STATE_ATTRIBUTES.filled,
2826
1135
  setFilled: NOOP,
1136
+ focused: DEFAULT_FIELD_STATE_ATTRIBUTES.focused,
2827
1137
  setFocused: NOOP,
1138
+ validate: () => null,
2828
1139
  validationMode: 'onSubmit',
1140
+ validationDebounceTime: 0,
2829
1141
  shouldValidateOnChange: () => false,
2830
1142
  state: DEFAULT_FIELD_ROOT_STATE,
1143
+ markedDirtyRef: {
1144
+ current: false
1145
+ },
2831
1146
  registerFieldControl: NOOP,
2832
1147
  validation: {
2833
1148
  getValidationProps: (_disabled, props = EMPTY_OBJECT) => props,
2834
1149
  inputRef: {
2835
1150
  current: null
2836
1151
  },
2837
- registeredInputs: new Map(),
2838
1152
  registerInput: NOOP,
2839
- getInputControl: () => null,
2840
1153
  commit: async () => {},
2841
1154
  change: NOOP
2842
1155
  }
@@ -2855,16 +1168,14 @@ function useRegisterFieldControl(controlRef, id, value, getFormValueOverride, en
2855
1168
  const {
2856
1169
  registerFieldControl
2857
1170
  } = useFieldRootContext();
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.
1171
+ const sourceRef = React.useRef(null);
1172
+ if (!sourceRef.current) {
1173
+ sourceRef.current = Symbol();
1174
+ }
2863
1175
  useIsoLayoutEffect(() => {
2864
1176
  const source = sourceRef.current;
2865
- if (!enabled) {
2866
- registerFieldControl(source, undefined);
2867
- return;
1177
+ if (!source || !enabled) {
1178
+ return undefined;
2868
1179
  }
2869
1180
  const registration = {
2870
1181
  controlRef,
@@ -2874,19 +1185,13 @@ function useRegisterFieldControl(controlRef, id, value, getFormValueOverride, en
2874
1185
  value
2875
1186
  };
2876
1187
  registerFieldControl(source, registration);
2877
- }, [controlRef, enabled, getFormValueOverride, id, name, registerFieldControl, sourceRef, value]);
2878
- useIsoLayoutEffect(() => {
2879
- const source = sourceRef.current;
2880
1188
  return () => {
2881
1189
  registerFieldControl(source, undefined);
2882
1190
  };
2883
- }, [registerFieldControl, sourceRef]);
1191
+ }, [controlRef, enabled, getFormValueOverride, id, name, registerFieldControl, value]);
2884
1192
  }
2885
1193
 
2886
1194
  const FormContext = /*#__PURE__*/React.createContext({
2887
- elementRef: {
2888
- current: null
2889
- },
2890
1195
  formRef: {
2891
1196
  current: {
2892
1197
  fields: new Map()
@@ -2934,7 +1239,7 @@ function useLabelableId(params = {}) {
2934
1239
  } = useLabelableContext();
2935
1240
  const defaultId = useBaseUiId(id);
2936
1241
  const controlIdForEffect = implicit ? controlId : undefined;
2937
- const controlSourceRef = useRefWithInit(() => Symbol());
1242
+ const controlSourceRef = useRefWithInit(() => Symbol('labelable-control'));
2938
1243
  const hasRegisteredRef = React.useRef(false);
2939
1244
  const hadExplicitIdRef = React.useRef(id != null);
2940
1245
  const unregisterControlId = useStableCallback(() => {
@@ -2983,19 +1288,6 @@ function areArraysEqual(array1, array2, itemComparer = (a, b) => a === b) {
2983
1288
  return array1.length === array2.length && array1.every((value, index) => itemComparer(value, array2[index]));
2984
1289
  }
2985
1290
 
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
- }
2999
1291
  function getPseudoElementBounds(element) {
3000
1292
  const elementRect = element.getBoundingClientRect();
3001
1293
  const win = getWindow(element);
@@ -3042,7 +1334,12 @@ function resolveAriaLabelledBy(fieldLabelId, localLabelId) {
3042
1334
  const CompositeListContext = /*#__PURE__*/React.createContext({
3043
1335
  register: () => {},
3044
1336
  unregister: () => {},
3045
- subscribeMapChange: () => () => {},
1337
+ subscribeMapChange: () => {
1338
+ return () => {};
1339
+ },
1340
+ elementsRef: {
1341
+ current: []
1342
+ },
3046
1343
  nextIndexRef: {
3047
1344
  current: 0
3048
1345
  }
@@ -3055,6 +1352,7 @@ function useCompositeListContext() {
3055
1352
  /* eslint-disable no-bitwise */
3056
1353
  /**
3057
1354
  * Provides context for a list of items in a composite component.
1355
+ * @internal
3058
1356
  */
3059
1357
  function CompositeList(props) {
3060
1358
  const {
@@ -3064,146 +1362,114 @@ function CompositeList(props) {
3064
1362
  onMapChange: onMapChangeProp
3065
1363
  } = props;
3066
1364
  const onMapChange = useStableCallback(onMapChangeProp);
3067
- const [, setMapTick] = React.useState(false);
1365
+ const nextIndexRef = React.useRef(0);
3068
1366
  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
+
3069
1376
  const map = useRefWithInit(createMap).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();
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);
3088
1384
  });
3089
1385
  const unregister = useStableCallback(node => {
3090
1386
  map.delete(node);
3091
- scheduleMapUpdate();
1387
+ lastTickRef.current += 1;
1388
+ setMapTick(lastTickRef.current);
3092
1389
  });
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
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
3103
1400
  });
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
- }
3108
1401
  });
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;
1402
+ return newMap;
1403
+ }, [map, mapTick]);
1404
+ useIsoLayoutEffect(() => {
1405
+ if (typeof MutationObserver !== 'function' || sortedMap.size === 0) {
1406
+ return undefined;
3119
1407
  }
3120
1408
  const mutationObserver = new MutationObserver(entries => {
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;
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);
3127
1418
  }
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;
1419
+ });
1420
+ sortedMap.forEach((_, node) => {
1421
+ if (node.parentElement) {
1422
+ mutationObserver.observe(node.parentElement, {
1423
+ childList: true
1424
+ });
3142
1425
  }
3143
1426
  });
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);
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;
3154
1436
  }
1437
+ if (labelsRef && labelsRef.current.length !== sortedMap.size) {
1438
+ labelsRef.current.length = sortedMap.size;
1439
+ }
1440
+ nextIndexRef.current = sortedMap.size;
3155
1441
  }
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
- });
1442
+ onMapChange(sortedMap);
1443
+ }, [onMapChange, sortedMap, elementsRef, labelsRef, mapTick]);
3169
1444
  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
- }
3175
1445
  return () => {
3176
1446
  elementsRef.current = [];
3177
- if (labelsRef) {
3178
- labelsRef.current = [];
3179
- }
3180
1447
  };
3181
- }, [elementsRef, labelsRef, syncRefs]);
3182
- useIsoLayoutEffect(() => {
3183
- if (isDirtyRef.current) {
3184
- flush();
3185
- }
3186
- });
1448
+ }, [elementsRef]);
3187
1449
  useIsoLayoutEffect(() => {
3188
1450
  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;
1451
+ if (labelsRef) {
1452
+ labelsRef.current = [];
1453
+ }
3193
1454
  };
3194
- }, []);
1455
+ }, [labelsRef]);
3195
1456
  const subscribeMapChange = useStableCallback(fn => {
3196
1457
  listeners.add(fn);
3197
1458
  return () => {
3198
1459
  listeners.delete(fn);
3199
1460
  };
3200
1461
  });
1462
+ useIsoLayoutEffect(() => {
1463
+ listeners.forEach(l => l(sortedMap));
1464
+ }, [listeners, sortedMap]);
3201
1465
  const contextValue = React.useMemo(() => ({
3202
1466
  register,
3203
1467
  unregister,
3204
1468
  subscribeMapChange,
1469
+ elementsRef,
1470
+ labelsRef,
3205
1471
  nextIndexRef
3206
- }), [register, unregister, subscribeMapChange, nextIndexRef]);
1472
+ }), [register, unregister, subscribeMapChange, elementsRef, labelsRef, nextIndexRef]);
3207
1473
  return /*#__PURE__*/jsx(CompositeListContext.Provider, {
3208
1474
  value: contextValue,
3209
1475
  children: children
@@ -3215,66 +1481,15 @@ function createMap() {
3215
1481
  function createListeners() {
3216
1482
  return new Set();
3217
1483
  }
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);
3251
- }
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;
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;
3261
1488
  }
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
- }
1489
+ if (position & Node.DOCUMENT_POSITION_PRECEDING || position & Node.DOCUMENT_POSITION_CONTAINS) {
1490
+ return 1;
3271
1491
  }
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;
1492
+ return 0;
3278
1493
  }
3279
1494
 
3280
1495
  /**
@@ -3371,6 +1586,7 @@ const arrow = (options, deps) => ({
3371
1586
  options: [options, deps]
3372
1587
  });
3373
1588
 
1589
+ const nativeHideFn = hide$1().fn;
3374
1590
  const hide = {
3375
1591
  name: 'hide',
3376
1592
  async fn(state) {
@@ -3381,15 +1597,10 @@ const hide = {
3381
1597
  y
3382
1598
  } = state.rects.reference;
3383
1599
  const anchorHidden = width === 0 && height === 0 && x === 0 && y === 0;
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;
1600
+ const nativeHideResult = await nativeHideFn(state);
3390
1601
  return {
3391
1602
  data: {
3392
- referenceHidden: referenceHidden || anchorHidden
1603
+ referenceHidden: nativeHideResult.data?.referenceHidden || anchorHidden
3393
1604
  }
3394
1605
  };
3395
1606
  }
@@ -3399,13 +1610,75 @@ const DEFAULT_SIDES = {
3399
1610
  sideX: 'left',
3400
1611
  sideY: 'top'
3401
1612
  };
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
+ };
3402
1644
 
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.
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
+ };
3406
1681
 
3407
- const AVAILABLE_WIDTH_VAR = '--available-width';
3408
- const AVAILABLE_HEIGHT_VAR = '--available-height';
3409
1682
  function getLogicalSide(sideParam, renderedSide, isRtl) {
3410
1683
  const isLogicalSideParam = sideParam === 'inline-start' || sideParam === 'inline-end';
3411
1684
  const logicalRight = isRtl ? 'inline-start' : 'inline-end';
@@ -3441,9 +1714,6 @@ function getOffsetData(state, sideParam, isRtl) {
3441
1714
  * `useFloating` hook.
3442
1715
  */
3443
1716
  function useAnchorPositioning(params) {
3444
- return useAnchorPositioningWithHook(params, useBaseUIFloating);
3445
- }
3446
- function useAnchorPositioningWithHook(params, useFloatingHook) {
3447
1717
  const {
3448
1718
  // Public parameters
3449
1719
  anchor,
@@ -3463,7 +1733,7 @@ function useAnchorPositioningWithHook(params, useFloatingHook) {
3463
1733
  floatingRootContext,
3464
1734
  mounted,
3465
1735
  collisionAvoidance,
3466
- shift: shift$1,
1736
+ shiftCrossAxis = false,
3467
1737
  nodeId,
3468
1738
  adaptiveOrigin,
3469
1739
  lazyFlip = false,
@@ -3476,8 +1746,6 @@ function useAnchorPositioningWithHook(params, useFloatingHook) {
3476
1746
  const collisionAvoidanceSide = collisionAvoidance.side || 'flip';
3477
1747
  const collisionAvoidanceAlign = collisionAvoidance.align || 'flip';
3478
1748
  const collisionAvoidanceFallbackAxisSide = collisionAvoidance.fallbackAxisSide || 'end';
3479
- const shiftCrossAxis = shift$1?.crossAxis ?? false;
3480
- const shiftRootBoundary = shift$1?.rootBoundary;
3481
1749
  const anchorFn = typeof anchor === 'function' ? anchor : undefined;
3482
1750
  const anchorFnCallback = useStableCallback(anchorFn);
3483
1751
  const anchorDep = anchorFn ? anchorFnCallback : anchor;
@@ -3495,32 +1763,30 @@ function useAnchorPositioningWithHook(params, useFloatingHook) {
3495
1763
  }[sideParam];
3496
1764
  const placement = align === 'center' ? side : `${side}-${align}`;
3497
1765
  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
- }
3513
1766
 
3514
1767
  // Create a bias to the preferred side.
3515
1768
  // On iOS, when the mobile software keyboard opens, the input is exactly centered
3516
1769
  // 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`.
3519
1770
  const bias = 1;
3520
1771
  const biasTop = sideParam === 'bottom' ? bias : 0;
3521
1772
  const biasBottom = sideParam === 'top' ? bias : 0;
3522
1773
  const biasLeft = sideParam === 'right' ? bias : 0;
3523
1774
  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
+ }
3524
1790
  const commonCollisionProps = {
3525
1791
  boundary: collisionBoundary === 'clipping-ancestors' ? 'clippingAncestors' : collisionBoundary,
3526
1792
  padding: collisionPadding
@@ -3557,37 +1823,46 @@ function useAnchorPositioningWithHook(params, useFloatingHook) {
3557
1823
  // Ensure the popup flips if it's been limited by its --available-height and it resizes.
3558
1824
  // Since the size() padding is smaller than the flip() padding, flip() will take precedence.
3559
1825
  padding: {
3560
- top: collisionPadding.top + bias + biasTop,
3561
- right: collisionPadding.right + bias + biasRight,
3562
- bottom: collisionPadding.bottom + bias + biasBottom,
3563
- left: collisionPadding.left + bias + biasLeft
1826
+ top: collisionPadding.top + bias,
1827
+ right: collisionPadding.right + bias,
1828
+ bottom: collisionPadding.bottom + bias,
1829
+ left: collisionPadding.left + bias
3564
1830
  },
3565
1831
  mainAxis: !shiftCrossAxis && collisionAvoidanceSide === 'flip',
3566
1832
  crossAxis: collisionAvoidanceAlign === 'flip' ? 'alignment' : false,
3567
1833
  fallbackAxisSideDirection: collisionAvoidanceFallbackAxisSide
3568
1834
  });
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]);
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]);
3591
1866
 
3592
1867
  // https://floating-ui.com/docs/flip#combining-with-shift
3593
1868
  if (collisionAvoidanceSide === 'shift' || collisionAvoidanceAlign === 'shift' || align === 'center') {
@@ -3609,8 +1884,8 @@ function useAnchorPositioningWithHook(params, useFloatingHook) {
3609
1884
  return;
3610
1885
  }
3611
1886
  const floatingStyle = floating.style;
3612
- floatingStyle.setProperty(AVAILABLE_WIDTH_VAR, `${availableWidth}px`);
3613
- floatingStyle.setProperty(AVAILABLE_HEIGHT_VAR, `${availableHeight}px`);
1887
+ floatingStyle.setProperty('--available-width', `${availableWidth}px`);
1888
+ floatingStyle.setProperty('--available-height', `${availableHeight}px`);
3614
1889
 
3615
1890
  // Snap anchor dimensions to device pixels to ensure the popup's visual width matches the anchor's one.
3616
1891
  const dpr = getWindow(floating).devicePixelRatio || 1;
@@ -3692,7 +1967,7 @@ function useAnchorPositioningWithHook(params, useFloatingHook) {
3692
1967
  context,
3693
1968
  isPositioned,
3694
1969
  floatingStyles: originalFloatingStyles
3695
- } = useFloatingHook({
1970
+ } = useFloating({
3696
1971
  rootContext: floatingRootContext,
3697
1972
  open: keepMounted ? mounted : undefined,
3698
1973
  placement,
@@ -3711,41 +1986,14 @@ function useAnchorPositioningWithHook(params, useFloatingHook) {
3711
1986
  // This ensures the popup is inside the viewport initially before it gets positioned.
3712
1987
  const resolvedPosition = isPositioned ? positionMethod : 'fixed';
3713
1988
  const floatingStyles = React.useMemo(() => {
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';
1989
+ const base = adaptiveOrigin ? {
1990
+ position: resolvedPosition,
1991
+ [sideX]: x,
1992
+ [sideY]: y
1993
+ } : {
1994
+ position: resolvedPosition,
1995
+ ...originalFloatingStyles
1996
+ };
3749
1997
  if (!isPositioned) {
3750
1998
  base.opacity = 0;
3751
1999
  }
@@ -3796,10 +2044,10 @@ function useAnchorPositioningWithHook(params, useFloatingHook) {
3796
2044
  // and flips back lazily, not eagerly. Ideal for filtered lists that change
3797
2045
  // the size of the popup dynamically to avoid unwanted flipping when typing.
3798
2046
  useIsoLayoutEffect(() => {
3799
- if (lazyFlip && mounted && isPositioned && renderedSide !== side) {
2047
+ if (lazyFlip && mounted && isPositioned) {
3800
2048
  setMountSide(renderedSide);
3801
2049
  }
3802
- }, [lazyFlip, mounted, isPositioned, renderedSide, side]);
2050
+ }, [lazyFlip, mounted, isPositioned, renderedSide]);
3803
2051
  const arrowStyles = React.useMemo(() => ({
3804
2052
  position: 'absolute',
3805
2053
  top: middlewareData.arrow?.y,
@@ -3884,57 +2132,64 @@ function useAnchoredPopupScrollLock(enabled, touchOpen, positionerElement, refer
3884
2132
  useScrollLock(enabled && (!touchOpen || touchOpenShouldLockScroll), referenceElement);
3885
2133
  }
3886
2134
 
2135
+ let IndexGuessBehavior = /*#__PURE__*/function (IndexGuessBehavior) {
2136
+ IndexGuessBehavior[IndexGuessBehavior["None"] = 0] = "None";
2137
+ IndexGuessBehavior[IndexGuessBehavior["GuessFromOrder"] = 1] = "GuessFromOrder";
2138
+ return IndexGuessBehavior;
2139
+ }({});
2140
+
3887
2141
  /**
3888
2142
  * Used to register a list item and its index (DOM position) in the `CompositeList`.
3889
2143
  */
3890
2144
  function useCompositeListItem(params = {}) {
3891
2145
  const {
3892
- guess,
3893
2146
  label,
3894
2147
  metadata,
3895
2148
  textRef,
2149
+ indexGuessBehavior,
3896
2150
  index: externalIndex
3897
2151
  } = params;
3898
2152
  const {
3899
2153
  register,
3900
2154
  unregister,
3901
2155
  subscribeMapChange,
2156
+ elementsRef,
2157
+ labelsRef,
3902
2158
  nextIndexRef
3903
2159
  } = 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.
3908
2160
  const indexRef = React.useRef(-1);
3909
- const [internalIndex, setInternalIndex] = React.useState(externalIndex == null && guess ? () => {
2161
+ const [index, setIndex] = React.useState(externalIndex ?? (indexGuessBehavior === IndexGuessBehavior.GuessFromOrder ? () => {
3910
2162
  if (indexRef.current === -1) {
3911
2163
  const newIndex = nextIndexRef.current;
3912
2164
  nextIndexRef.current += 1;
3913
2165
  indexRef.current = newIndex;
3914
2166
  }
3915
2167
  return indexRef.current;
3916
- } : -1);
3917
- const index = externalIndex ?? internalIndex;
2168
+ } : -1));
3918
2169
  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.
3923
2170
  const ref = React.useCallback(node => {
3924
- const previousNode = componentRef.current;
3925
- if (previousNode) {
3926
- unregister(previousNode);
3927
- }
3928
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
+ }
2178
+ }
2179
+ }, [index, elementsRef, labelsRef, label, textRef]);
2180
+ useIsoLayoutEffect(() => {
2181
+ if (externalIndex != null) {
2182
+ return undefined;
2183
+ }
2184
+ const node = componentRef.current;
3929
2185
  if (node) {
3930
- register(node, {
3931
- metadata: metadata ?? null,
3932
- index: externalIndex ?? null,
3933
- label,
3934
- textRef
3935
- });
2186
+ register(node, metadata);
2187
+ return () => {
2188
+ unregister(node);
2189
+ };
3936
2190
  }
3937
- }, [externalIndex, register, unregister, metadata, label, textRef]);
2191
+ return undefined;
2192
+ }, [externalIndex, register, unregister, metadata]);
3938
2193
  useIsoLayoutEffect(() => {
3939
2194
  if (externalIndex != null) {
3940
2195
  return undefined;
@@ -3942,14 +2197,14 @@ function useCompositeListItem(params = {}) {
3942
2197
  return subscribeMapChange(map => {
3943
2198
  const i = componentRef.current ? map.get(componentRef.current)?.index : null;
3944
2199
  if (i != null) {
3945
- setInternalIndex(i);
2200
+ setIndex(i);
3946
2201
  }
3947
2202
  });
3948
- }, [externalIndex, subscribeMapChange]);
2203
+ }, [externalIndex, subscribeMapChange, setIndex]);
3949
2204
  return {
3950
2205
  ref,
3951
2206
  index
3952
2207
  };
3953
2208
  }
3954
2209
 
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 };
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 };