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