@jobber/components 9.6.1 → 9.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Autocomplete/index.cjs +2 -0
- package/dist/Autocomplete/index.mjs +2 -0
- package/dist/Autocomplete-cjs.js +5 -4
- package/dist/Autocomplete-es.js +2 -1
- package/dist/Card/index.cjs +2 -1
- package/dist/Card/index.mjs +2 -1
- package/dist/Chip/index.cjs +2 -0
- package/dist/Chip/index.mjs +2 -0
- package/dist/Chips/InternalChipDismissible/hooks/index.cjs +2 -0
- package/dist/Chips/InternalChipDismissible/hooks/index.mjs +2 -0
- package/dist/Chips/InternalChipDismissible/index.cjs +2 -0
- package/dist/Chips/InternalChipDismissible/index.mjs +2 -0
- package/dist/Chips/index.cjs +2 -0
- package/dist/Chips/index.mjs +2 -0
- package/dist/Combobox/index.cjs +4 -2
- package/dist/Combobox/index.mjs +4 -2
- package/dist/{ComboboxSeparator-cjs.js → ComboboxChipRemove-cjs.js} +455 -501
- package/dist/{ComboboxSeparator-es.js → ComboboxChipRemove-es.js} +456 -500
- package/dist/ComboboxPrimitive-cjs.js +30 -29
- package/dist/ComboboxPrimitive-es.js +3 -2
- package/dist/ConfirmationModal/index.cjs +2 -0
- package/dist/ConfirmationModal/index.mjs +2 -0
- package/dist/DataDump/index.cjs +2 -1
- package/dist/DataDump/index.mjs +2 -1
- package/dist/DataList/components/DataListActions/index.cjs +2 -0
- package/dist/DataList/components/DataListActions/index.mjs +2 -0
- package/dist/DataList/components/DataListBulkActions/index.cjs +2 -0
- package/dist/DataList/components/DataListBulkActions/index.mjs +2 -0
- package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +2 -0
- package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +2 -0
- package/dist/DataList/components/DataListFilters/index.cjs +2 -0
- package/dist/DataList/components/DataListFilters/index.mjs +2 -0
- package/dist/DataList/components/DataListHeader/index.cjs +2 -0
- package/dist/DataList/components/DataListHeader/index.mjs +2 -0
- package/dist/DataList/components/DataListItem/index.cjs +2 -0
- package/dist/DataList/components/DataListItem/index.mjs +2 -0
- package/dist/DataList/components/DataListItemActions/index.cjs +2 -0
- package/dist/DataList/components/DataListItemActions/index.mjs +2 -0
- package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +2 -0
- package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +2 -0
- package/dist/DataList/components/DataListItems/index.cjs +2 -0
- package/dist/DataList/components/DataListItems/index.mjs +2 -0
- package/dist/DataList/components/DataListLayout/index.cjs +2 -0
- package/dist/DataList/components/DataListLayout/index.mjs +2 -0
- package/dist/DataList/components/DataListLayoutActions/index.cjs +2 -0
- package/dist/DataList/components/DataListLayoutActions/index.mjs +2 -0
- package/dist/DataList/index.cjs +2 -0
- package/dist/DataList/index.mjs +2 -0
- package/dist/DatePicker/index.cjs +2 -0
- package/dist/DatePicker/index.mjs +2 -0
- package/dist/DatePicker-cjs.js +5 -4
- package/dist/DatePicker-es.js +2 -1
- package/dist/Dialog/index.cjs +2 -1
- package/dist/Dialog/index.mjs +2 -1
- package/dist/DrawerDescription-cjs.js +99 -268
- package/dist/DrawerDescription-es.js +103 -270
- package/dist/DrawerTitle-cjs.js +6 -2
- package/dist/DrawerTitle-es.js +7 -3
- package/dist/FieldDescription-cjs.js +181 -184
- package/dist/FieldDescription-es.js +184 -187
- package/dist/FilterPicker/components/FilterPickerActivator/index.cjs +2 -0
- package/dist/FilterPicker/components/FilterPickerActivator/index.mjs +2 -0
- package/dist/FilterPicker/components/FilterPickerContent/index.cjs +2 -0
- package/dist/FilterPicker/components/FilterPickerContent/index.mjs +2 -0
- package/dist/FilterPicker/components/FilterPickerTrigger/index.cjs +2 -0
- package/dist/FilterPicker/components/FilterPickerTrigger/index.mjs +2 -0
- package/dist/FilterPicker/index.cjs +2 -0
- package/dist/FilterPicker/index.mjs +2 -0
- package/dist/FilterPickerContent-cjs.js +5 -4
- package/dist/FilterPickerContent-es.js +2 -1
- package/dist/FormatFile/index.cjs +2 -0
- package/dist/FormatFile/index.mjs +2 -0
- package/dist/Gallery/index.cjs +2 -0
- package/dist/Gallery/index.mjs +2 -0
- package/dist/InputDate/index.cjs +2 -0
- package/dist/InputDate/index.mjs +2 -0
- package/dist/InputNumber/index.cjs +2 -1
- package/dist/InputNumber/index.mjs +2 -1
- package/dist/LightBox/index.cjs +2 -0
- package/dist/LightBox/index.mjs +2 -0
- package/dist/Menu/index.cjs +2 -1
- package/dist/Menu/index.mjs +2 -1
- package/dist/Menu-cjs.js +5 -4
- package/dist/Menu-es.js +2 -1
- package/dist/MenuSubmenuTrigger-cjs.js +164 -206
- package/dist/MenuSubmenuTrigger-es.js +152 -194
- package/dist/Modal/index.cjs +2 -0
- package/dist/Modal/index.mjs +3 -1
- package/dist/NumberFieldInput-cjs.js +308 -187
- package/dist/NumberFieldInput-es.js +308 -187
- package/dist/Page/index.cjs +2 -1
- package/dist/Page/index.mjs +2 -1
- package/dist/Popover/index.cjs +2 -0
- package/dist/Popover/index.mjs +2 -0
- package/dist/Popover-cjs.js +8 -7
- package/dist/Popover-es.js +2 -1
- package/dist/ScrollAreaViewport-cjs.js +1093 -747
- package/dist/ScrollAreaViewport-es.js +1081 -734
- package/dist/Select/index.cjs +2 -1
- package/dist/Select/index.mjs +2 -1
- package/dist/SelectGroupLabel-cjs.js +306 -207
- package/dist/SelectGroupLabel-es.js +302 -203
- package/dist/Tooltip/index.cjs +2 -0
- package/dist/Tooltip/index.mjs +2 -0
- package/dist/Tooltip-cjs.js +7 -6
- package/dist/Tooltip-es.js +2 -1
- package/dist/dialogReturnFocus-cjs.js +1 -1
- package/dist/dialogReturnFocus-es.js +2 -2
- package/dist/floating-ui.react-cjs.js +192 -2564
- package/dist/floating-ui.react-dom-cjs.js +2103 -0
- package/dist/floating-ui.react-dom-es.js +2072 -0
- package/dist/floating-ui.react-es.js +156 -2519
- package/dist/floating-ui.utils-cjs.js +342 -0
- package/dist/floating-ui.utils-es.js +300 -0
- package/dist/index.cjs +4 -3
- package/dist/index.mjs +4 -3
- package/dist/primitives/BottomSheet/index.cjs +1 -1
- package/dist/primitives/BottomSheet/index.mjs +1 -1
- package/dist/primitives/ComboboxPrimitive/index.cjs +4 -2
- package/dist/primitives/ComboboxPrimitive/index.mjs +4 -2
- package/dist/primitives/SelectPrimitive/index.cjs +2 -0
- package/dist/primitives/SelectPrimitive/index.mjs +2 -0
- package/dist/primitives/index.cjs +3 -2
- package/dist/primitives/index.mjs +3 -2
- package/dist/scrollEdges-cjs.js +11 -1
- package/dist/scrollEdges-es.js +11 -1
- package/dist/unstyledPrimitives/index.cjs +478 -720
- package/dist/unstyledPrimitives/index.mjs +421 -663
- package/dist/useCompositeListItem-cjs.js +361 -2108
- package/dist/useCompositeListItem-es.js +331 -2076
- package/dist/useLabel-cjs.js +3 -2
- package/dist/useLabel-es.js +3 -2
- package/dist/useRenderElement-cjs.js +7 -9
- package/dist/useRenderElement-es.js +7 -9
- package/dist/useScrollLock-cjs.js +344 -819
- package/dist/useScrollLock-es.js +296 -732
- package/dist/useScrollToActive-cjs.js +5 -4
- package/dist/useScrollToActive-es.js +2 -1
- package/package.json +3 -3
- package/dist/scrollable-cjs.js +0 -58
- package/dist/scrollable-es.js +0 -54
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { N as createSelector, n as useControlled, m as useTransitionStatus, O as useOpenInteractionType, P as useStore, q as useValueAsRef, b as useStableCallback, a as useIsoLayoutEffect, K as useValueChanged, f as useOpenChangeComplete, Q as useClick, R as useDismiss, F as FOCUSABLE_POPUP_PROPS, U as useOnFirstRender, d as createChangeEventDetails, v as none, w as visuallyHiddenInput, x as visuallyHidden, V as Store, W as focusOut, X as outsidePress, Y as pressableTriggerOpenStateMapping, c as useButton, k as useTimeout, Z as getFloatingFocusElement, _ as contains, o as ownerDocument, $ as cancelOpen, a0 as FloatingPortal, a1 as InternalBackdrop, a2 as inertValue, a3 as DROPDOWN_COLLISION_AVOIDANCE, p as popupStateMapping, t as transitionStatusMapping, a4 as useDirection, a5 as useAnimationFrame, a6 as webkit, s as addEventListener, i as FloatingFocusManager, a7 as windowResize, a8 as isVirtualClick, a9 as itemPress, u as useBaseUiId } from './useScrollLock-es.js';
|
|
3
3
|
import { f as formatErrorMessage, a as useRefWithInit, m as mergeProps, E as EMPTY_OBJECT, b as useMergedRefs, c as EMPTY_ARRAY, u as useRenderElement } from './useRenderElement-es.js';
|
|
4
|
-
import { s as stringifyAsValue, h as hasNullItemLabel, e as compareItemEquality, a as useFormContext, b as useFieldRootContext, c as useLabelableId, d as useRegisterFieldControl, g as findItemIndex, i as useFloatingRootContext, j as useListNavigation, k as useTypeahead, l as
|
|
4
|
+
import { s as stringifyAsValue, h as hasNullItemLabel, e as compareItemEquality, a as useFormContext, b as useFieldRootContext, c as useLabelableId, d as useRegisterFieldControl, g as findItemIndex, i as useFloatingRootContext, j as useListNavigation, k as useTypeahead, l as defaultItemEquality, m as stringifyAsLabel, n as areArraysEqual, f as fieldValidityMapping, u as useLabelableContext, r as resolveAriaLabelledBy, o as getPseudoElementBounds, p as resolveMultipleLabels, q as resolveSelectedLabel, t as useAnchoredPopupScrollLock, v as useAnchorPositioning, w as usePositioner, C as CompositeList, x as selectedValueIncludes, y as getDisabledMountTransitionStyles, z as useCompositeListItem, I as IndexGuessBehavior, A as removeItem } from './useCompositeListItem-es.js';
|
|
5
5
|
import { g as getMaxScrollOffset, n as normalizeScrollOffset, u as useCSPContext, c as clamp, S as SCROLL_EDGE_TOLERANCE_PX, C as COMPOSITE_KEYS, s as styleDisableScrollbar } from './scrollEdges-es.js';
|
|
6
6
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
7
|
+
import { K as getWindow, r as rectToClientRect } from './floating-ui.utils-es.js';
|
|
8
|
+
import { p as platform } from './floating-ui.react-dom-es.js';
|
|
7
9
|
|
|
8
10
|
const ToolbarRootContext = /*#__PURE__*/React.createContext(undefined);
|
|
9
11
|
if (process.env.NODE_ENV !== "production") ToolbarRootContext.displayName = "ToolbarRootContext";
|
|
@@ -25,7 +27,7 @@ function usePreviousValue(value) {
|
|
|
25
27
|
current: value,
|
|
26
28
|
previous: null
|
|
27
29
|
});
|
|
28
|
-
if (
|
|
30
|
+
if (value !== state.current) {
|
|
29
31
|
setState({
|
|
30
32
|
current: value,
|
|
31
33
|
previous: state.current
|
|
@@ -40,6 +42,8 @@ function isElementDisabled(element) {
|
|
|
40
42
|
|
|
41
43
|
const SelectRootContext = /*#__PURE__*/React.createContext(null);
|
|
42
44
|
if (process.env.NODE_ENV !== "production") SelectRootContext.displayName = "SelectRootContext";
|
|
45
|
+
const SelectFloatingContext = /*#__PURE__*/React.createContext(null);
|
|
46
|
+
if (process.env.NODE_ENV !== "production") SelectFloatingContext.displayName = "SelectFloatingContext";
|
|
43
47
|
function useSelectRootContext() {
|
|
44
48
|
const context = React.useContext(SelectRootContext);
|
|
45
49
|
if (context === null) {
|
|
@@ -47,16 +51,25 @@ function useSelectRootContext() {
|
|
|
47
51
|
}
|
|
48
52
|
return context;
|
|
49
53
|
}
|
|
54
|
+
function useSelectFloatingContext() {
|
|
55
|
+
const context = React.useContext(SelectFloatingContext);
|
|
56
|
+
if (context === null) {
|
|
57
|
+
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: SelectFloatingContext is missing. Select parts must be placed within <Select.Root>.' : formatErrorMessage(61));
|
|
58
|
+
}
|
|
59
|
+
return context;
|
|
60
|
+
}
|
|
50
61
|
|
|
51
62
|
const selectors = {
|
|
52
|
-
id: state => state.id,
|
|
53
|
-
labelId: state => state.labelId,
|
|
54
|
-
modal: state => state.modal,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
id: createSelector(state => state.id),
|
|
64
|
+
labelId: createSelector(state => state.labelId),
|
|
65
|
+
modal: createSelector(state => state.modal),
|
|
66
|
+
multiple: createSelector(state => state.multiple),
|
|
67
|
+
items: createSelector(state => state.items),
|
|
68
|
+
itemToStringLabel: createSelector(state => state.itemToStringLabel),
|
|
69
|
+
itemToStringValue: createSelector(state => state.itemToStringValue),
|
|
70
|
+
isItemEqualToValue: createSelector(state => state.isItemEqualToValue),
|
|
71
|
+
value: createSelector(state => state.value),
|
|
72
|
+
hasSelectedValue: createSelector(state => {
|
|
60
73
|
const {
|
|
61
74
|
value,
|
|
62
75
|
multiple,
|
|
@@ -69,19 +82,19 @@ const selectors = {
|
|
|
69
82
|
return value.length > 0;
|
|
70
83
|
}
|
|
71
84
|
return stringifyAsValue(value, itemToStringValue) !== '';
|
|
72
|
-
},
|
|
73
|
-
hasNullItemLabel: (state, enabled) => {
|
|
85
|
+
}),
|
|
86
|
+
hasNullItemLabel: createSelector((state, enabled) => {
|
|
74
87
|
return enabled ? hasNullItemLabel(state.items) : false;
|
|
75
|
-
},
|
|
76
|
-
open: state => state.open,
|
|
77
|
-
mounted: state => state.mounted,
|
|
78
|
-
forceMount: state => state.forceMount,
|
|
79
|
-
transitionStatus: state => state.transitionStatus,
|
|
80
|
-
openMethod: state => state.openMethod,
|
|
81
|
-
activeIndex: state => state.activeIndex,
|
|
82
|
-
selectedIndex: state => state.selectedIndex,
|
|
83
|
-
isActive: (state, index) => state.activeIndex === index,
|
|
84
|
-
isSelected: (state, itemValue) => {
|
|
88
|
+
}),
|
|
89
|
+
open: createSelector(state => state.open),
|
|
90
|
+
mounted: createSelector(state => state.mounted),
|
|
91
|
+
forceMount: createSelector(state => state.forceMount),
|
|
92
|
+
transitionStatus: createSelector(state => state.transitionStatus),
|
|
93
|
+
openMethod: createSelector(state => state.openMethod),
|
|
94
|
+
activeIndex: createSelector(state => state.activeIndex),
|
|
95
|
+
selectedIndex: createSelector(state => state.selectedIndex),
|
|
96
|
+
isActive: createSelector((state, index) => state.activeIndex === index),
|
|
97
|
+
isSelected: createSelector((state, itemValue) => {
|
|
85
98
|
const comparer = state.isItemEqualToValue;
|
|
86
99
|
const storeValue = state.value;
|
|
87
100
|
if (state.multiple) {
|
|
@@ -92,19 +105,19 @@ const selectors = {
|
|
|
92
105
|
// value changes while the popup is open, where the index sync is deferred) must not
|
|
93
106
|
// keep a previously selected item marked as selected.
|
|
94
107
|
return compareItemEquality(itemValue, storeValue, comparer);
|
|
95
|
-
},
|
|
96
|
-
isSelectedByFocus: (state, index) => {
|
|
108
|
+
}),
|
|
109
|
+
isSelectedByFocus: createSelector((state, index) => {
|
|
97
110
|
return state.selectedIndex === index;
|
|
98
|
-
},
|
|
99
|
-
popupProps: state => state.popupProps,
|
|
100
|
-
triggerProps: state => state.triggerProps,
|
|
101
|
-
triggerElement: state => state.triggerElement,
|
|
102
|
-
positionerElement: state => state.positionerElement,
|
|
103
|
-
listElement: state => state.listElement,
|
|
104
|
-
popupSide: state => state.popupSide,
|
|
105
|
-
scrollUpArrowVisible: state => state.scrollUpArrowVisible,
|
|
106
|
-
scrollDownArrowVisible: state => state.scrollDownArrowVisible,
|
|
107
|
-
hasScrollArrows: state => state.hasScrollArrows
|
|
111
|
+
}),
|
|
112
|
+
popupProps: createSelector(state => state.popupProps),
|
|
113
|
+
triggerProps: createSelector(state => state.triggerProps),
|
|
114
|
+
triggerElement: createSelector(state => state.triggerElement),
|
|
115
|
+
positionerElement: createSelector(state => state.positionerElement),
|
|
116
|
+
listElement: createSelector(state => state.listElement),
|
|
117
|
+
popupSide: createSelector(state => state.popupSide),
|
|
118
|
+
scrollUpArrowVisible: createSelector(state => state.scrollUpArrowVisible),
|
|
119
|
+
scrollDownArrowVisible: createSelector(state => state.scrollDownArrowVisible),
|
|
120
|
+
hasScrollArrows: createSelector(state => state.hasScrollArrows)
|
|
108
121
|
};
|
|
109
122
|
|
|
110
123
|
function SelectRoot(props) {
|
|
@@ -190,7 +203,7 @@ function SelectRoot(props) {
|
|
|
190
203
|
openMethod,
|
|
191
204
|
triggerProps: interactionTypeProps
|
|
192
205
|
} = useOpenInteractionType(open);
|
|
193
|
-
const store = useRefWithInit(() => new
|
|
206
|
+
const store = useRefWithInit(() => new Store({
|
|
194
207
|
id: generatedId,
|
|
195
208
|
labelId: undefined,
|
|
196
209
|
modal,
|
|
@@ -222,7 +235,7 @@ function SelectRoot(props) {
|
|
|
222
235
|
const triggerElement = useStore(store, selectors.triggerElement);
|
|
223
236
|
const positionerElement = useStore(store, selectors.positionerElement);
|
|
224
237
|
const previousOpenMethod = usePreviousValue(openMethod);
|
|
225
|
-
const renderedOpenMethod = openMethod ?? previousOpenMethod;
|
|
238
|
+
const renderedOpenMethod = openMethod ?? previousOpenMethod ?? null;
|
|
226
239
|
const serializedValue = React.useMemo(() => {
|
|
227
240
|
// In multiple mode the shared input is nameless; per-value entries are submitted via
|
|
228
241
|
// `hiddenInputs`. Its value is therefore irrelevant, and passing the whole array to
|
|
@@ -238,26 +251,38 @@ function SelectRoot(props) {
|
|
|
238
251
|
}
|
|
239
252
|
return stringifyAsValue(value, itemToStringValue);
|
|
240
253
|
}, [multiple, value, itemToStringValue]);
|
|
241
|
-
const controlRef = useValueAsRef(triggerElement);
|
|
254
|
+
const controlRef = useValueAsRef(store.state.triggerElement);
|
|
242
255
|
const getStringifiedValueForForm = useStableCallback(() => fieldStringValue);
|
|
243
256
|
useRegisterFieldControl(controlRef, generatedId, value, getStringifiedValueForForm, !disabled, nameProp);
|
|
244
257
|
const initialValueRef = React.useRef(value);
|
|
245
258
|
// Mirror the `hasSelectedValue` store selector so the Field's filled state agrees with the
|
|
246
259
|
// trigger/value placeholder semantics (a value serializing to `''` counts as empty).
|
|
247
|
-
const hasSelectedValue = multiple ? Array.isArray(value) && value.length > 0 : value != null &&
|
|
260
|
+
const hasSelectedValue = multiple ? Array.isArray(value) && value.length > 0 : value != null && stringifyAsValue(value, itemToStringValue) !== '';
|
|
261
|
+
useIsoLayoutEffect(() => {
|
|
262
|
+
// Ensure the values and labels are registered for programmatic value changes.
|
|
263
|
+
if (value !== initialValueRef.current) {
|
|
264
|
+
store.set('forceMount', true);
|
|
265
|
+
}
|
|
266
|
+
}, [store, value]);
|
|
248
267
|
useIsoLayoutEffect(() => {
|
|
249
268
|
setFilled(hasSelectedValue);
|
|
250
269
|
}, [hasSelectedValue, setFilled]);
|
|
251
270
|
useIsoLayoutEffect(function syncSelectedIndex() {
|
|
252
|
-
|
|
253
|
-
let
|
|
271
|
+
const registry = valuesRef.current;
|
|
272
|
+
let nextIndex;
|
|
254
273
|
if (multiple) {
|
|
255
274
|
const currentValue = Array.isArray(value) ? value : [];
|
|
256
|
-
|
|
257
|
-
|
|
275
|
+
if (currentValue.length === 0) {
|
|
276
|
+
nextIndex = null;
|
|
277
|
+
} else {
|
|
278
|
+
const lastValue = currentValue[currentValue.length - 1];
|
|
279
|
+
const lastIndex = findItemIndex(registry, lastValue, isItemEqualToValue);
|
|
280
|
+
nextIndex = lastIndex === -1 ? null : lastIndex;
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
const index = findItemIndex(registry, value, isItemEqualToValue);
|
|
284
|
+
nextIndex = index === -1 ? null : index;
|
|
258
285
|
}
|
|
259
|
-
const index = empty ? -1 : findItemIndex(valuesRef.current, target, isItemEqualToValue);
|
|
260
|
-
const nextIndex = index === -1 ? null : index;
|
|
261
286
|
if (nextIndex === null) {
|
|
262
287
|
selectedItemTextRef.current = null;
|
|
263
288
|
}
|
|
@@ -265,7 +290,7 @@ function SelectRoot(props) {
|
|
|
265
290
|
return;
|
|
266
291
|
}
|
|
267
292
|
store.set('selectedIndex', nextIndex);
|
|
268
|
-
}, [multiple, open, value, isItemEqualToValue, store]);
|
|
293
|
+
}, [hasSelectedValue, multiple, open, value, valuesRef, isItemEqualToValue, store, selectedItemTextRef]);
|
|
269
294
|
function isSelectedValueDirty(currentValue) {
|
|
270
295
|
const initialValue = validityData.initialValue;
|
|
271
296
|
if (Array.isArray(currentValue) && Array.isArray(initialValue)) {
|
|
@@ -296,9 +321,7 @@ function SelectRoot(props) {
|
|
|
296
321
|
setMounted(false);
|
|
297
322
|
store.update({
|
|
298
323
|
activeIndex: null,
|
|
299
|
-
openMethod: null
|
|
300
|
-
scrollUpArrowVisible: false,
|
|
301
|
-
scrollDownArrowVisible: false
|
|
324
|
+
openMethod: null
|
|
302
325
|
});
|
|
303
326
|
onOpenChangeComplete?.(false);
|
|
304
327
|
});
|
|
@@ -322,13 +345,21 @@ function SelectRoot(props) {
|
|
|
322
345
|
}
|
|
323
346
|
setValueUnwrapped(nextValue);
|
|
324
347
|
});
|
|
325
|
-
const handleScrollArrowVisibility = useStableCallback(
|
|
348
|
+
const handleScrollArrowVisibility = useStableCallback(() => {
|
|
349
|
+
const scroller = store.state.listElement || popupRef.current;
|
|
350
|
+
if (!scroller) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
326
353
|
const maxScrollTop = getMaxScrollOffset(scroller.scrollHeight, scroller.clientHeight);
|
|
327
354
|
const scrollTop = normalizeScrollOffset(scroller.scrollTop, maxScrollTop);
|
|
328
355
|
const shouldShowUp = scrollTop > 0;
|
|
329
356
|
const shouldShowDown = scrollTop < maxScrollTop;
|
|
330
|
-
store.
|
|
331
|
-
|
|
357
|
+
if (store.state.scrollUpArrowVisible !== shouldShowUp) {
|
|
358
|
+
store.set('scrollUpArrowVisible', shouldShowUp);
|
|
359
|
+
}
|
|
360
|
+
if (store.state.scrollDownArrowVisible !== shouldShowDown) {
|
|
361
|
+
store.set('scrollDownArrowVisible', shouldShowDown);
|
|
362
|
+
}
|
|
332
363
|
});
|
|
333
364
|
const floatingContext = useFloatingRootContext({
|
|
334
365
|
open,
|
|
@@ -373,16 +404,20 @@ function SelectRoot(props) {
|
|
|
373
404
|
if (open) {
|
|
374
405
|
store.set('activeIndex', index);
|
|
375
406
|
} else {
|
|
376
|
-
setValue(valuesRef.current[index], createChangeEventDetails(none));
|
|
407
|
+
setValue(valuesRef.current[index], createChangeEventDetails('none'));
|
|
377
408
|
}
|
|
378
409
|
},
|
|
379
410
|
onTyping(typing) {
|
|
380
411
|
typingRef.current = typing;
|
|
381
412
|
}
|
|
382
413
|
});
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
414
|
+
const mergedTriggerProps = React.useMemo(() => {
|
|
415
|
+
const triggerInteractionProps = mergeProps(typeahead.reference, listNavigation.reference, dismiss.reference, click.reference, interactionTypeProps);
|
|
416
|
+
if (generatedId) {
|
|
417
|
+
triggerInteractionProps.id = generatedId;
|
|
418
|
+
}
|
|
419
|
+
return triggerInteractionProps;
|
|
420
|
+
}, [click.reference, typeahead.reference, listNavigation.reference, dismiss.reference, interactionTypeProps, generatedId]);
|
|
386
421
|
const popupProps = React.useMemo(() => mergeProps(FOCUSABLE_POPUP_PROPS, typeahead.floating, listNavigation.floating, dismiss.floating), [typeahead.floating, listNavigation.floating, dismiss.floating]);
|
|
387
422
|
const itemProps = listNavigation.item ?? EMPTY_OBJECT;
|
|
388
423
|
useOnFirstRender(() => {
|
|
@@ -391,25 +426,27 @@ function SelectRoot(props) {
|
|
|
391
426
|
triggerProps: mergedTriggerProps
|
|
392
427
|
});
|
|
393
428
|
});
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
429
|
+
useIsoLayoutEffect(() => {
|
|
430
|
+
store.update({
|
|
431
|
+
id: generatedId,
|
|
432
|
+
modal,
|
|
433
|
+
multiple,
|
|
434
|
+
value,
|
|
435
|
+
open,
|
|
436
|
+
mounted,
|
|
437
|
+
transitionStatus,
|
|
438
|
+
popupProps,
|
|
439
|
+
triggerProps: mergedTriggerProps,
|
|
440
|
+
items,
|
|
441
|
+
itemToStringLabel,
|
|
442
|
+
itemToStringValue,
|
|
443
|
+
isItemEqualToValue,
|
|
444
|
+
openMethod: renderedOpenMethod
|
|
445
|
+
});
|
|
446
|
+
}, [store, generatedId, modal, multiple, value, open, mounted, transitionStatus, popupProps, mergedTriggerProps, items, itemToStringLabel, itemToStringValue, isItemEqualToValue, renderedOpenMethod]);
|
|
410
447
|
const contextValue = React.useMemo(() => ({
|
|
411
448
|
store,
|
|
412
|
-
|
|
449
|
+
name,
|
|
413
450
|
required,
|
|
414
451
|
disabled,
|
|
415
452
|
readOnly,
|
|
@@ -434,8 +471,9 @@ function SelectRoot(props) {
|
|
|
434
471
|
onOpenChangeComplete,
|
|
435
472
|
alignItemWithTriggerActiveRef,
|
|
436
473
|
initialValueRef
|
|
437
|
-
}), [store,
|
|
474
|
+
}), [store, name, required, disabled, readOnly, multiple, highlightItemOnHover, setValue, setOpen, itemProps, validation, onOpenChangeComplete, handleScrollArrowVisibility]);
|
|
438
475
|
const ref = useMergedRefs(inputRef, validation.inputRef);
|
|
476
|
+
const hasMultipleSelection = multiple && Array.isArray(value) && value.length > 0;
|
|
439
477
|
const hiddenInputName = multiple ? undefined : name;
|
|
440
478
|
const hiddenInputs = React.useMemo(() => {
|
|
441
479
|
if (!multiple || !Array.isArray(value) || !name) {
|
|
@@ -452,69 +490,73 @@ function SelectRoot(props) {
|
|
|
452
490
|
}, currentSerializedValue);
|
|
453
491
|
});
|
|
454
492
|
}, [multiple, value, form, name, itemToStringValue, disabled]);
|
|
455
|
-
return /*#__PURE__*/
|
|
493
|
+
return /*#__PURE__*/jsx(SelectRootContext.Provider, {
|
|
456
494
|
value: contextValue,
|
|
457
|
-
children:
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
//
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
//
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
const nextValue = event.currentTarget.value;
|
|
473
|
-
const details = createChangeEventDetails(none, event.nativeEvent);
|
|
474
|
-
function handleChange() {
|
|
475
|
-
if (multiple) {
|
|
476
|
-
// Browser autofill only writes a single scalar value.
|
|
495
|
+
children: /*#__PURE__*/jsxs(SelectFloatingContext.Provider, {
|
|
496
|
+
value: floatingContext,
|
|
497
|
+
children: [children, /*#__PURE__*/jsx("input", {
|
|
498
|
+
...validation.getValidationProps(disabled, {
|
|
499
|
+
onFocus() {
|
|
500
|
+
// Move focus to the trigger element when the hidden input is focused.
|
|
501
|
+
store.state.triggerElement?.focus({
|
|
502
|
+
// Supported in Chrome from 144 (January 2026)
|
|
503
|
+
focusVisible: true
|
|
504
|
+
});
|
|
505
|
+
},
|
|
506
|
+
// Handle browser autofill.
|
|
507
|
+
onChange(event) {
|
|
508
|
+
// Workaround for https://github.com/facebook/react/issues/9023
|
|
509
|
+
if (event.nativeEvent.defaultPrevented || disabled || readOnly) {
|
|
477
510
|
return;
|
|
478
511
|
}
|
|
512
|
+
const nextValue = event.currentTarget.value;
|
|
513
|
+
const details = createChangeEventDetails(none, event.nativeEvent);
|
|
514
|
+
function handleChange() {
|
|
515
|
+
if (multiple) {
|
|
516
|
+
// Browser autofill only writes a single scalar value.
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
479
519
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
520
|
+
// Preserve the original serialized matching, then fall back to rendered text,
|
|
521
|
+
// which browsers can autofill for primitive values like `value="US">United States`.
|
|
522
|
+
const nextValueLower = nextValue.toLowerCase();
|
|
523
|
+
let matchingIndex = valuesRef.current.findIndex(candidate => stringifyAsValue(candidate, itemToStringValue).toLowerCase() === nextValueLower || stringifyAsLabel(candidate, itemToStringLabel).toLowerCase() === nextValueLower);
|
|
524
|
+
if (matchingIndex === -1) {
|
|
525
|
+
matchingIndex = valuesRef.current.findIndex((_, index) => {
|
|
526
|
+
const renderedLabel = labelsRef.current[index];
|
|
527
|
+
return renderedLabel != null && renderedLabel.toLowerCase() === nextValueLower;
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
const matchingValue = matchingIndex === -1 ? undefined : valuesRef.current[matchingIndex];
|
|
531
|
+
if (matchingValue != null) {
|
|
532
|
+
// `setValue` may be canceled by `onValueChange`; rely on `useValueChanged` to
|
|
533
|
+
// mark the field dirty and run validation only when the value actually changes.
|
|
534
|
+
setValue(matchingValue, details);
|
|
535
|
+
}
|
|
495
536
|
}
|
|
537
|
+
store.set('forceMount', true);
|
|
538
|
+
queueMicrotask(handleChange);
|
|
496
539
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
suppressHydrationWarning: true
|
|
514
|
-
}), hiddenInputs]
|
|
540
|
+
}),
|
|
541
|
+
id: generatedId && hiddenInputName == null ? `${generatedId}-hidden-input` : undefined,
|
|
542
|
+
form: form,
|
|
543
|
+
name: hiddenInputName,
|
|
544
|
+
autoComplete: autoComplete,
|
|
545
|
+
value: serializedValue,
|
|
546
|
+
disabled: disabled,
|
|
547
|
+
required: required && !hasMultipleSelection,
|
|
548
|
+
readOnly: readOnly,
|
|
549
|
+
ref: ref,
|
|
550
|
+
style: name ? visuallyHiddenInput : visuallyHidden,
|
|
551
|
+
tabIndex: -1,
|
|
552
|
+
"aria-hidden": true,
|
|
553
|
+
suppressHydrationWarning: true
|
|
554
|
+
}), hiddenInputs]
|
|
555
|
+
})
|
|
515
556
|
});
|
|
516
557
|
}
|
|
517
558
|
|
|
559
|
+
const BOUNDARY_OFFSET = 2;
|
|
518
560
|
const SELECTED_DELAY = 400;
|
|
519
561
|
const stateAttributesMapping$2 = {
|
|
520
562
|
...pressableTriggerOpenStateMapping,
|
|
@@ -587,7 +629,9 @@ const SelectTrigger = /*#__PURE__*/React.forwardRef(function SelectTrigger(compo
|
|
|
587
629
|
disabled,
|
|
588
630
|
native: nativeButton
|
|
589
631
|
});
|
|
590
|
-
const setTriggerElement =
|
|
632
|
+
const setTriggerElement = useStableCallback(element => {
|
|
633
|
+
store.set('triggerElement', element);
|
|
634
|
+
});
|
|
591
635
|
const timeoutFocus = useTimeout();
|
|
592
636
|
const timeoutMouseDown = useTimeout();
|
|
593
637
|
const selectedDelayTimeout = useTimeout();
|
|
@@ -616,7 +660,7 @@ const SelectTrigger = /*#__PURE__*/React.forwardRef(function SelectTrigger(compo
|
|
|
616
660
|
const mergedProps = mergeProps(triggerProps, {
|
|
617
661
|
id,
|
|
618
662
|
role: 'combobox',
|
|
619
|
-
'aria-expanded': open,
|
|
663
|
+
'aria-expanded': open ? 'true' : 'false',
|
|
620
664
|
'aria-haspopup': 'listbox',
|
|
621
665
|
'aria-controls': open ? listElement?.id ?? getFloatingFocusElement(positionerElement)?.id : undefined,
|
|
622
666
|
'aria-labelledby': ariaLabelledBy,
|
|
@@ -665,7 +709,8 @@ const SelectTrigger = /*#__PURE__*/React.forwardRef(function SelectTrigger(compo
|
|
|
665
709
|
if (contains(triggerRef.current, mouseUpTarget) || contains(positionerRef.current, mouseUpTarget)) {
|
|
666
710
|
return;
|
|
667
711
|
}
|
|
668
|
-
|
|
712
|
+
const bounds = getPseudoElementBounds(triggerRef.current);
|
|
713
|
+
if (mouseEvent.clientX >= bounds.left - BOUNDARY_OFFSET && mouseEvent.clientX <= bounds.right + BOUNDARY_OFFSET && mouseEvent.clientY >= bounds.top - BOUNDARY_OFFSET && mouseEvent.clientY <= bounds.bottom + BOUNDARY_OFFSET) {
|
|
669
714
|
return;
|
|
670
715
|
}
|
|
671
716
|
setOpen(false, createChangeEventDetails(cancelOpen, mouseEvent));
|
|
@@ -740,7 +785,7 @@ const SelectValue = /*#__PURE__*/React.forwardRef(function SelectValue(component
|
|
|
740
785
|
children = childrenProp(value);
|
|
741
786
|
} else if (childrenProp != null) {
|
|
742
787
|
children = childrenProp;
|
|
743
|
-
} else if (
|
|
788
|
+
} else if (!hasSelectedValue && placeholder != null && !hasNullLabel) {
|
|
744
789
|
children = placeholder;
|
|
745
790
|
} else if (Array.isArray(value)) {
|
|
746
791
|
children = resolveMultipleLabels(value, items, itemToStringLabel);
|
|
@@ -759,6 +804,9 @@ const SelectValue = /*#__PURE__*/React.forwardRef(function SelectValue(component
|
|
|
759
804
|
});
|
|
760
805
|
if (process.env.NODE_ENV !== "production") SelectValue.displayName = "SelectValue";
|
|
761
806
|
|
|
807
|
+
const SelectPortalContext = /*#__PURE__*/React.createContext(undefined);
|
|
808
|
+
if (process.env.NODE_ENV !== "production") SelectPortalContext.displayName = "SelectPortalContext";
|
|
809
|
+
|
|
762
810
|
const SelectPortal = /*#__PURE__*/React.forwardRef(function SelectPortal(portalProps, forwardedRef) {
|
|
763
811
|
const {
|
|
764
812
|
store
|
|
@@ -769,9 +817,12 @@ const SelectPortal = /*#__PURE__*/React.forwardRef(function SelectPortal(portalP
|
|
|
769
817
|
if (!shouldRender) {
|
|
770
818
|
return null;
|
|
771
819
|
}
|
|
772
|
-
return /*#__PURE__*/jsx(
|
|
773
|
-
|
|
774
|
-
|
|
820
|
+
return /*#__PURE__*/jsx(SelectPortalContext.Provider, {
|
|
821
|
+
value: true,
|
|
822
|
+
children: /*#__PURE__*/jsx(FloatingPortal, {
|
|
823
|
+
ref: forwardedRef,
|
|
824
|
+
...portalProps
|
|
825
|
+
})
|
|
775
826
|
});
|
|
776
827
|
});
|
|
777
828
|
if (process.env.NODE_ENV !== "production") SelectPortal.displayName = "SelectPortal";
|
|
@@ -811,19 +862,17 @@ const FIXED = {
|
|
|
811
862
|
const SelectPositioner = /*#__PURE__*/React.forwardRef(function SelectPositioner(componentProps, forwardedRef) {
|
|
812
863
|
const {
|
|
813
864
|
anchor,
|
|
865
|
+
positionMethod = 'absolute',
|
|
814
866
|
className,
|
|
815
867
|
render,
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
align,
|
|
821
|
-
sideOffset,
|
|
822
|
-
alignOffset,
|
|
868
|
+
side = 'bottom',
|
|
869
|
+
align = 'center',
|
|
870
|
+
sideOffset = 0,
|
|
871
|
+
alignOffset = 0,
|
|
823
872
|
collisionBoundary = 'clipping-ancestors',
|
|
824
873
|
collisionPadding,
|
|
825
|
-
arrowPadding,
|
|
826
|
-
sticky,
|
|
874
|
+
arrowPadding = 5,
|
|
875
|
+
sticky = false,
|
|
827
876
|
disableAnchorTracking,
|
|
828
877
|
alignItemWithTrigger = true,
|
|
829
878
|
collisionAvoidance = DROPDOWN_COLLISION_AVOIDANCE,
|
|
@@ -839,9 +888,9 @@ const SelectPositioner = /*#__PURE__*/React.forwardRef(function SelectPositioner
|
|
|
839
888
|
valuesRef,
|
|
840
889
|
initialValueRef,
|
|
841
890
|
popupRef,
|
|
842
|
-
setValue
|
|
843
|
-
floatingContext: floatingRootContext
|
|
891
|
+
setValue
|
|
844
892
|
} = useSelectRootContext();
|
|
893
|
+
const floatingRootContext = useSelectFloatingContext();
|
|
845
894
|
const open = useStore(store, selectors.open);
|
|
846
895
|
const mounted = useStore(store, selectors.mounted);
|
|
847
896
|
const modal = useStore(store, selectors.modal);
|
|
@@ -858,6 +907,16 @@ const SelectPositioner = /*#__PURE__*/React.forwardRef(function SelectPositioner
|
|
|
858
907
|
if (!mounted && controlledAlignItemWithTrigger !== alignItemWithTrigger) {
|
|
859
908
|
setControlledAlignItemWithTrigger(alignItemWithTrigger);
|
|
860
909
|
}
|
|
910
|
+
useIsoLayoutEffect(() => {
|
|
911
|
+
if (!mounted) {
|
|
912
|
+
if (selectors.scrollUpArrowVisible(store.state)) {
|
|
913
|
+
store.set('scrollUpArrowVisible', false);
|
|
914
|
+
}
|
|
915
|
+
if (selectors.scrollDownArrowVisible(store.state)) {
|
|
916
|
+
store.set('scrollDownArrowVisible', false);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}, [store, mounted]);
|
|
861
920
|
React.useImperativeHandle(alignItemWithTriggerActiveRef, () => alignItemWithTriggerActive);
|
|
862
921
|
useAnchoredPopupScrollLock((alignItemWithTriggerActive || modal) && open, openMethod === 'touch', positionerElement, triggerElement);
|
|
863
922
|
const positioning = useAnchorPositioning({
|
|
@@ -888,7 +947,9 @@ const SelectPositioner = /*#__PURE__*/React.forwardRef(function SelectPositioner
|
|
|
888
947
|
useIsoLayoutEffect(() => {
|
|
889
948
|
store.set('popupSide', positioning.side);
|
|
890
949
|
}, [store, positioning.side]);
|
|
891
|
-
const setPositionerElement =
|
|
950
|
+
const setPositionerElement = useStableCallback(element => {
|
|
951
|
+
store.set('positionerElement', element);
|
|
952
|
+
});
|
|
892
953
|
const element = usePositioner(componentProps, state, {
|
|
893
954
|
styles: positionerStyles,
|
|
894
955
|
transitionStatus,
|
|
@@ -899,6 +960,9 @@ const SelectPositioner = /*#__PURE__*/React.forwardRef(function SelectPositioner
|
|
|
899
960
|
});
|
|
900
961
|
const prevMapSizeRef = React.useRef(0);
|
|
901
962
|
const onMapChange = useStableCallback(map => {
|
|
963
|
+
if (map.size === 0 && prevMapSizeRef.current === 0) {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
902
966
|
if (valuesRef.current.length === 0) {
|
|
903
967
|
return;
|
|
904
968
|
}
|
|
@@ -922,8 +986,9 @@ const SelectPositioner = /*#__PURE__*/React.forwardRef(function SelectPositioner
|
|
|
922
986
|
}
|
|
923
987
|
}
|
|
924
988
|
if (prevSize !== 0 && store.state.multiple && Array.isArray(value)) {
|
|
925
|
-
const
|
|
926
|
-
|
|
989
|
+
const hasVisibleItem = selectedItemValue => findItemIndex(valuesRef.current, selectedItemValue, isItemEqualToValue) !== -1;
|
|
990
|
+
const nextValue = value.filter(selectedItemValue => hasVisibleItem(selectedItemValue));
|
|
991
|
+
if (nextValue.length !== value.length || nextValue.some(selectedItemValue => !selectedValueIncludes(value, selectedItemValue, isItemEqualToValue))) {
|
|
927
992
|
setValue(nextValue, eventDetails);
|
|
928
993
|
if (nextValue.length === 0) {
|
|
929
994
|
store.set('selectedIndex', null);
|
|
@@ -997,8 +1062,7 @@ const SelectPopup = /*#__PURE__*/React.forwardRef(function SelectPopup(component
|
|
|
997
1062
|
handleScrollArrowVisibility,
|
|
998
1063
|
scrollHandlerRef,
|
|
999
1064
|
listRef,
|
|
1000
|
-
highlightItemOnHover
|
|
1001
|
-
floatingContext: floatingRootContext
|
|
1065
|
+
highlightItemOnHover
|
|
1002
1066
|
} = useSelectRootContext();
|
|
1003
1067
|
const {
|
|
1004
1068
|
side,
|
|
@@ -1008,6 +1072,7 @@ const SelectPopup = /*#__PURE__*/React.forwardRef(function SelectPopup(component
|
|
|
1008
1072
|
setControlledAlignItemWithTrigger
|
|
1009
1073
|
} = useSelectPositionerContext();
|
|
1010
1074
|
const insideToolbar = useToolbarRootContext(true) != null;
|
|
1075
|
+
const floatingRootContext = useSelectFloatingContext();
|
|
1011
1076
|
const direction = useDirection();
|
|
1012
1077
|
const {
|
|
1013
1078
|
nonce,
|
|
@@ -1030,10 +1095,14 @@ const SelectPopup = /*#__PURE__*/React.forwardRef(function SelectPopup(component
|
|
|
1030
1095
|
if (!positionerElement || !popupRef.current || !initialPlacedRef.current) {
|
|
1031
1096
|
return;
|
|
1032
1097
|
}
|
|
1098
|
+
if (reachedMaxHeightRef.current || !alignItemWithTriggerActive) {
|
|
1099
|
+
handleScrollArrowVisibility();
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1033
1102
|
const isTopPositioned = positionerElement.style.top === '0px';
|
|
1034
1103
|
const isBottomPositioned = positionerElement.style.bottom === '0px';
|
|
1035
|
-
if (
|
|
1036
|
-
handleScrollArrowVisibility(
|
|
1104
|
+
if (!isTopPositioned && !isBottomPositioned) {
|
|
1105
|
+
handleScrollArrowVisibility();
|
|
1037
1106
|
return;
|
|
1038
1107
|
}
|
|
1039
1108
|
const scale = getScale(positionerElement);
|
|
@@ -1047,50 +1116,63 @@ const SelectPopup = /*#__PURE__*/React.forwardRef(function SelectPopup(component
|
|
|
1047
1116
|
const maxAvailableHeight = Math.min(doc.documentElement.clientHeight - marginTop - marginBottom, maxPopupHeight);
|
|
1048
1117
|
const scrollTop = scroller.scrollTop;
|
|
1049
1118
|
const maxScrollTop = getMaxScrollTop(scroller);
|
|
1050
|
-
|
|
1051
|
-
// `Infinity` requests a scroll to the recomputed maximum offset.
|
|
1119
|
+
let nextPositionerHeight = 0;
|
|
1052
1120
|
let nextScrollTop = null;
|
|
1121
|
+
let setReachedMax = false;
|
|
1122
|
+
let scrollToMax = false;
|
|
1053
1123
|
const setHeight = height => {
|
|
1054
1124
|
positionerElement.style.height = `${height}px`;
|
|
1055
1125
|
};
|
|
1056
|
-
const
|
|
1057
|
-
const nextHeight = Math.min(currentHeight + diff, maxAvailableHeight);
|
|
1058
|
-
if (diff <= SCROLL_EDGE_TOLERANCE_PX) {
|
|
1126
|
+
const handleSmallDiff = (diff, targetScrollTop) => {
|
|
1059
1127
|
const heightDelta = clamp(diff, 0, maxAvailableHeight - currentHeight);
|
|
1060
1128
|
if (heightDelta > 0) {
|
|
1061
1129
|
// Consume the remaining scroll in height.
|
|
1062
1130
|
setHeight(currentHeight + heightDelta);
|
|
1063
1131
|
}
|
|
1064
|
-
scroller.scrollTop =
|
|
1132
|
+
scroller.scrollTop = targetScrollTop;
|
|
1065
1133
|
if (maxAvailableHeight - (currentHeight + heightDelta) <= SCROLL_EDGE_TOLERANCE_PX) {
|
|
1066
1134
|
reachedMaxHeightRef.current = true;
|
|
1067
1135
|
}
|
|
1068
|
-
handleScrollArrowVisibility(
|
|
1136
|
+
handleScrollArrowVisibility();
|
|
1137
|
+
};
|
|
1138
|
+
const diff = isTopPositioned ? maxScrollTop - scrollTop : scrollTop;
|
|
1139
|
+
const nextHeight = Math.min(currentHeight + diff, maxAvailableHeight);
|
|
1140
|
+
nextPositionerHeight = nextHeight;
|
|
1141
|
+
if (diff <= SCROLL_EDGE_TOLERANCE_PX) {
|
|
1142
|
+
handleSmallDiff(diff, isTopPositioned ? maxScrollTop : 0);
|
|
1069
1143
|
return;
|
|
1070
1144
|
}
|
|
1071
1145
|
if (maxAvailableHeight - nextHeight > SCROLL_EDGE_TOLERANCE_PX) {
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1146
|
+
if (isTopPositioned) {
|
|
1147
|
+
scrollToMax = true;
|
|
1148
|
+
} else {
|
|
1149
|
+
nextScrollTop = 0;
|
|
1150
|
+
}
|
|
1151
|
+
} else {
|
|
1152
|
+
setReachedMax = true;
|
|
1153
|
+
if (isBottomPositioned && scrollTop < maxScrollTop) {
|
|
1154
|
+
const overshoot = currentHeight + diff - maxAvailableHeight;
|
|
1155
|
+
nextScrollTop = scrollTop - (diff - overshoot);
|
|
1156
|
+
}
|
|
1076
1157
|
}
|
|
1077
|
-
|
|
1158
|
+
nextPositionerHeight = Math.ceil(nextPositionerHeight);
|
|
1078
1159
|
if (nextPositionerHeight !== 0) {
|
|
1079
1160
|
setHeight(nextPositionerHeight);
|
|
1080
1161
|
}
|
|
1081
|
-
if (nextScrollTop != null) {
|
|
1162
|
+
if (scrollToMax || nextScrollTop != null) {
|
|
1082
1163
|
// Recompute bounds after resizing (clientHeight likely changed).
|
|
1083
|
-
const
|
|
1164
|
+
const nextMaxScrollTop = getMaxScrollTop(scroller);
|
|
1165
|
+
const target = scrollToMax ? nextMaxScrollTop : clamp(nextScrollTop, 0, nextMaxScrollTop);
|
|
1084
1166
|
|
|
1085
1167
|
// Avoid adjustments that re-trigger scroll events forever.
|
|
1086
1168
|
if (Math.abs(scroller.scrollTop - target) > SCROLL_EDGE_TOLERANCE_PX) {
|
|
1087
1169
|
scroller.scrollTop = target;
|
|
1088
1170
|
}
|
|
1089
1171
|
}
|
|
1090
|
-
if (nextPositionerHeight >= maxAvailableHeight - SCROLL_EDGE_TOLERANCE_PX) {
|
|
1172
|
+
if (setReachedMax || nextPositionerHeight >= maxAvailableHeight - SCROLL_EDGE_TOLERANCE_PX) {
|
|
1091
1173
|
reachedMaxHeightRef.current = true;
|
|
1092
1174
|
}
|
|
1093
|
-
handleScrollArrowVisibility(
|
|
1175
|
+
handleScrollArrowVisibility();
|
|
1094
1176
|
});
|
|
1095
1177
|
React.useImperativeHandle(scrollHandlerRef, () => handleScroll, [handleScroll]);
|
|
1096
1178
|
useOpenChangeComplete({
|
|
@@ -1141,18 +1223,17 @@ const SelectPopup = /*#__PURE__*/React.forwardRef(function SelectPopup(component
|
|
|
1141
1223
|
if (!open || !triggerElement || !positionerElement || !popupElement || alignItemWithTriggerActive && !isPositioned || store.state.transitionStatus === 'ending') {
|
|
1142
1224
|
return;
|
|
1143
1225
|
}
|
|
1144
|
-
initialPlacedRef.current = true;
|
|
1145
|
-
popupElement.style.removeProperty('--transform-origin');
|
|
1146
1226
|
if (!alignItemWithTriggerActive) {
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1227
|
+
initialPlacedRef.current = true;
|
|
1228
|
+
scrollArrowFrame.request(handleScrollArrowVisibility);
|
|
1229
|
+
popupElement.style.removeProperty('--transform-origin');
|
|
1150
1230
|
return;
|
|
1151
1231
|
}
|
|
1152
1232
|
|
|
1153
1233
|
// Ensure we remove any transforms that can affect the location of the popup
|
|
1154
1234
|
// and therefore the calculations.
|
|
1155
1235
|
const restoreTransformStyles = unsetTransformStyles(popupElement);
|
|
1236
|
+
popupElement.style.removeProperty('--transform-origin');
|
|
1156
1237
|
try {
|
|
1157
1238
|
let textElement = selectedItemTextRef.current;
|
|
1158
1239
|
if (!textElement?.isConnected) {
|
|
@@ -1221,6 +1302,7 @@ const SelectPopup = /*#__PURE__*/React.forwardRef(function SelectPopup(component
|
|
|
1221
1302
|
// Safari doesn't position the popup correctly when pinch-zoomed.
|
|
1222
1303
|
const isPinchZoomed = (win.visualViewport?.scale ?? 1) !== 1 && webkit;
|
|
1223
1304
|
if (fallbackToAlignPopupToTrigger || isPinchZoomed) {
|
|
1305
|
+
initialPlacedRef.current = true;
|
|
1224
1306
|
clearStyles(positionerElement, originalPositionerStylesRef.current);
|
|
1225
1307
|
setControlledAlignItemWithTrigger(false);
|
|
1226
1308
|
return;
|
|
@@ -1239,16 +1321,18 @@ const SelectPopup = /*#__PURE__*/React.forwardRef(function SelectPopup(component
|
|
|
1239
1321
|
const popupTop = positionerRect.top;
|
|
1240
1322
|
const popupHeight = positionerRect.height;
|
|
1241
1323
|
const textCenterY = textRect.top + textRect.height / 2;
|
|
1242
|
-
const
|
|
1324
|
+
const transformOriginY = popupHeight > 0 ? (textCenterY - popupTop) / popupHeight * 100 : 50;
|
|
1325
|
+
const clampedY = clamp(transformOriginY, 0, 100);
|
|
1243
1326
|
popupElement.style.setProperty('--transform-origin', `50% ${clampedY}%`);
|
|
1244
1327
|
}
|
|
1245
1328
|
if (initialHeight === viewportHeight || height >= maxPopupHeight) {
|
|
1246
1329
|
reachedMaxHeightRef.current = true;
|
|
1247
1330
|
}
|
|
1248
|
-
handleScrollArrowVisibility(
|
|
1331
|
+
handleScrollArrowVisibility();
|
|
1249
1332
|
if (highlightItemOnHover && store.state.selectedIndex === null && store.state.activeIndex === null && listRef.current[0] != null) {
|
|
1250
1333
|
store.set('activeIndex', 0);
|
|
1251
1334
|
}
|
|
1335
|
+
initialPlacedRef.current = true;
|
|
1252
1336
|
} finally {
|
|
1253
1337
|
restoreTransformStyles();
|
|
1254
1338
|
}
|
|
@@ -1287,14 +1371,15 @@ const SelectPopup = /*#__PURE__*/React.forwardRef(function SelectPopup(component
|
|
|
1287
1371
|
style: listElement ? {
|
|
1288
1372
|
height: '100%'
|
|
1289
1373
|
} : LIST_FUNCTIONAL_STYLES
|
|
1290
|
-
})
|
|
1291
|
-
className: !listElement && alignItemWithTriggerActive ? styleDisableScrollbar.className : undefined
|
|
1374
|
+
})
|
|
1292
1375
|
};
|
|
1293
1376
|
const element = useRenderElement('div', componentProps, {
|
|
1294
1377
|
ref: [forwardedRef, popupRef],
|
|
1295
1378
|
state,
|
|
1296
1379
|
stateAttributesMapping,
|
|
1297
|
-
props: [popupProps, defaultProps, getDisabledMountTransitionStyles(transitionStatus),
|
|
1380
|
+
props: [popupProps, defaultProps, getDisabledMountTransitionStyles(transitionStatus), {
|
|
1381
|
+
className: !listElement && alignItemWithTriggerActive ? styleDisableScrollbar.className : undefined
|
|
1382
|
+
}, elementProps]
|
|
1298
1383
|
});
|
|
1299
1384
|
return /*#__PURE__*/jsxs(React.Fragment, {
|
|
1300
1385
|
children: [!disableStyleElements && styleDisableScrollbar.getElement(nonce), /*#__PURE__*/jsx(FloatingFocusManager, {
|
|
@@ -1310,7 +1395,7 @@ const SelectPopup = /*#__PURE__*/React.forwardRef(function SelectPopup(component
|
|
|
1310
1395
|
});
|
|
1311
1396
|
if (process.env.NODE_ENV !== "production") SelectPopup.displayName = "SelectPopup";
|
|
1312
1397
|
function getMaxPopupHeight(popupStyles) {
|
|
1313
|
-
const maxHeightStyle = popupStyles.maxHeight;
|
|
1398
|
+
const maxHeightStyle = popupStyles.maxHeight || '';
|
|
1314
1399
|
return maxHeightStyle.endsWith('px') ? parseFloat(maxHeightStyle) || Infinity : Infinity;
|
|
1315
1400
|
}
|
|
1316
1401
|
function getMaxScrollTop(scroller) {
|
|
@@ -1368,14 +1453,14 @@ const SelectList = /*#__PURE__*/React.forwardRef(function SelectList(componentPr
|
|
|
1368
1453
|
} = componentProps;
|
|
1369
1454
|
const {
|
|
1370
1455
|
store,
|
|
1371
|
-
scrollHandlerRef
|
|
1372
|
-
multiple
|
|
1456
|
+
scrollHandlerRef
|
|
1373
1457
|
} = useSelectRootContext();
|
|
1374
1458
|
const {
|
|
1375
1459
|
alignItemWithTriggerActive
|
|
1376
1460
|
} = useSelectPositionerContext();
|
|
1377
1461
|
const hasScrollArrows = useStore(store, selectors.hasScrollArrows);
|
|
1378
1462
|
const openMethod = useStore(store, selectors.openMethod);
|
|
1463
|
+
const multiple = useStore(store, selectors.multiple);
|
|
1379
1464
|
const id = useStore(store, selectors.id);
|
|
1380
1465
|
const defaultProps = {
|
|
1381
1466
|
id: `${id}-list`,
|
|
@@ -1389,7 +1474,9 @@ const SelectList = /*#__PURE__*/React.forwardRef(function SelectList(componentPr
|
|
|
1389
1474
|
}),
|
|
1390
1475
|
className: hasScrollArrows && openMethod !== 'touch' ? styleDisableScrollbar.className : undefined
|
|
1391
1476
|
};
|
|
1392
|
-
const setListElement =
|
|
1477
|
+
const setListElement = useStableCallback(element => {
|
|
1478
|
+
store.set('listElement', element);
|
|
1479
|
+
});
|
|
1393
1480
|
return useRenderElement('div', componentProps, {
|
|
1394
1481
|
ref: [forwardedRef, setListElement],
|
|
1395
1482
|
props: [defaultProps, elementProps]
|
|
@@ -1414,15 +1501,15 @@ const SelectItem = /*#__PURE__*/React.memo(/*#__PURE__*/React.forwardRef(functio
|
|
|
1414
1501
|
style,
|
|
1415
1502
|
value: itemValue = null,
|
|
1416
1503
|
label,
|
|
1417
|
-
disabled
|
|
1504
|
+
disabled = false,
|
|
1418
1505
|
nativeButton = false,
|
|
1419
1506
|
...elementProps
|
|
1420
1507
|
} = componentProps;
|
|
1421
1508
|
const textRef = React.useRef(null);
|
|
1422
1509
|
const listItem = useCompositeListItem({
|
|
1423
|
-
guess: true,
|
|
1424
1510
|
label,
|
|
1425
|
-
textRef
|
|
1511
|
+
textRef,
|
|
1512
|
+
indexGuessBehavior: IndexGuessBehavior.GuessFromOrder
|
|
1426
1513
|
});
|
|
1427
1514
|
const {
|
|
1428
1515
|
store,
|
|
@@ -1437,22 +1524,28 @@ const SelectItem = /*#__PURE__*/React.memo(/*#__PURE__*/React.forwardRef(functio
|
|
|
1437
1524
|
disabled: selectDisabled,
|
|
1438
1525
|
readOnly
|
|
1439
1526
|
} = useSelectRootContext();
|
|
1440
|
-
const disabled = selectDisabled || disabledProp;
|
|
1441
1527
|
const highlighted = useStore(store, selectors.isActive, listItem.index);
|
|
1442
1528
|
const open = useStore(store, selectors.open);
|
|
1443
1529
|
const selected = useStore(store, selectors.isSelected, itemValue);
|
|
1444
1530
|
const selectedByFocus = useStore(store, selectors.isSelectedByFocus, listItem.index);
|
|
1445
1531
|
const isItemEqualToValue = useStore(store, selectors.isItemEqualToValue);
|
|
1446
1532
|
const index = listItem.index;
|
|
1533
|
+
const hasRegistered = index !== -1;
|
|
1447
1534
|
const itemRef = React.useRef(null);
|
|
1448
1535
|
useIsoLayoutEffect(() => {
|
|
1536
|
+
if (!hasRegistered) {
|
|
1537
|
+
return undefined;
|
|
1538
|
+
}
|
|
1449
1539
|
const values = valuesRef.current;
|
|
1450
1540
|
values[index] = itemValue;
|
|
1451
1541
|
return () => {
|
|
1452
1542
|
delete values[index];
|
|
1453
1543
|
};
|
|
1454
|
-
}, [index, itemValue, valuesRef]);
|
|
1544
|
+
}, [hasRegistered, index, itemValue, valuesRef]);
|
|
1455
1545
|
useIsoLayoutEffect(() => {
|
|
1546
|
+
if (!hasRegistered) {
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1456
1549
|
const selectedValue = store.state.value;
|
|
1457
1550
|
let selectedCandidate = selectedValue;
|
|
1458
1551
|
if (multiple && Array.isArray(selectedValue)) {
|
|
@@ -1468,7 +1561,8 @@ const SelectItem = /*#__PURE__*/React.memo(/*#__PURE__*/React.forwardRef(functio
|
|
|
1468
1561
|
selectedItemTextRef.current = textRef.current;
|
|
1469
1562
|
}
|
|
1470
1563
|
}
|
|
1471
|
-
}, [index, multiple, isItemEqualToValue, store, itemValue, selectedItemTextRef]);
|
|
1564
|
+
}, [hasRegistered, index, multiple, isItemEqualToValue, store, itemValue, selectedItemTextRef]);
|
|
1565
|
+
const lastKeyRef = React.useRef(null);
|
|
1472
1566
|
const pointerTypeRef = React.useRef('mouse');
|
|
1473
1567
|
const allowMouseSelectionRef = React.useRef(false);
|
|
1474
1568
|
const {
|
|
@@ -1509,15 +1603,14 @@ const SelectItem = /*#__PURE__*/React.memo(/*#__PURE__*/React.forwardRef(functio
|
|
|
1509
1603
|
'aria-selected': selected,
|
|
1510
1604
|
tabIndex: open && highlighted ? 0 : -1,
|
|
1511
1605
|
onKeyDown(event) {
|
|
1606
|
+
lastKeyRef.current = event.key;
|
|
1512
1607
|
store.set('activeIndex', index);
|
|
1513
1608
|
if (event.key === ' ' && typingRef.current) {
|
|
1514
|
-
// `useButton` skips Space activation for `role="option"` items when the keydown
|
|
1515
|
-
// is `defaultPrevented`, keeping typeahead spaces from committing a selection.
|
|
1516
1609
|
event.preventDefault();
|
|
1517
1610
|
}
|
|
1518
1611
|
},
|
|
1519
1612
|
onClick(event) {
|
|
1520
|
-
const isMouseClick = pointerTypeRef.current !== 'touch';
|
|
1613
|
+
const isMouseClick = event.type === 'click' && pointerTypeRef.current !== 'touch';
|
|
1521
1614
|
const clickPointerType = event.nativeEvent.pointerType;
|
|
1522
1615
|
const isVirtualMouseClick = isMouseClick && isVirtualClick(event.nativeEvent) && (
|
|
1523
1616
|
// Generic no-pointer `detail === 0` clicks stay tied to highlight state. Virtual
|
|
@@ -1529,9 +1622,15 @@ const SelectItem = /*#__PURE__*/React.memo(/*#__PURE__*/React.forwardRef(functio
|
|
|
1529
1622
|
// assistive technology activation.
|
|
1530
1623
|
const isInvalidMouseClick = isMouseClick && !isVirtualMouseClick && !allowMouseSelectionRef.current;
|
|
1531
1624
|
allowMouseSelectionRef.current = false;
|
|
1532
|
-
|
|
1625
|
+
|
|
1626
|
+
// Prevent double commit on {Enter}
|
|
1627
|
+
if (event.type === 'keydown' && lastKeyRef.current === null) {
|
|
1628
|
+
return;
|
|
1629
|
+
}
|
|
1630
|
+
if (disabled || event.type === 'keydown' && lastKeyRef.current === ' ' && typingRef.current || isInvalidMouseClick) {
|
|
1533
1631
|
return;
|
|
1534
1632
|
}
|
|
1633
|
+
lastKeyRef.current = null;
|
|
1535
1634
|
commitSelection(event.nativeEvent);
|
|
1536
1635
|
},
|
|
1537
1636
|
onPointerEnter(event) {
|
|
@@ -1580,8 +1679,9 @@ const SelectItem = /*#__PURE__*/React.memo(/*#__PURE__*/React.forwardRef(functio
|
|
|
1580
1679
|
selected,
|
|
1581
1680
|
index,
|
|
1582
1681
|
textRef,
|
|
1583
|
-
selectedByFocus
|
|
1584
|
-
|
|
1682
|
+
selectedByFocus,
|
|
1683
|
+
hasRegistered
|
|
1684
|
+
}), [selected, index, textRef, selectedByFocus, hasRegistered]);
|
|
1585
1685
|
return /*#__PURE__*/jsx(SelectItemContext.Provider, {
|
|
1586
1686
|
value: contextValue,
|
|
1587
1687
|
children: element
|
|
@@ -1590,10 +1690,11 @@ const SelectItem = /*#__PURE__*/React.memo(/*#__PURE__*/React.forwardRef(functio
|
|
|
1590
1690
|
if (process.env.NODE_ENV !== "production") SelectItem.displayName = "SelectItem";
|
|
1591
1691
|
|
|
1592
1692
|
const SelectItemIndicator = /*#__PURE__*/React.forwardRef(function SelectItemIndicator(componentProps, forwardedRef) {
|
|
1693
|
+
const keepMounted = componentProps.keepMounted ?? false;
|
|
1593
1694
|
const {
|
|
1594
1695
|
selected
|
|
1595
1696
|
} = useSelectItemContext();
|
|
1596
|
-
const shouldRender =
|
|
1697
|
+
const shouldRender = keepMounted || selected;
|
|
1597
1698
|
if (!shouldRender) {
|
|
1598
1699
|
return null;
|
|
1599
1700
|
}
|
|
@@ -1659,7 +1760,8 @@ const SelectItemText = /*#__PURE__*/React.memo(/*#__PURE__*/React.forwardRef(fun
|
|
|
1659
1760
|
const {
|
|
1660
1761
|
index,
|
|
1661
1762
|
textRef,
|
|
1662
|
-
selectedByFocus
|
|
1763
|
+
selectedByFocus,
|
|
1764
|
+
hasRegistered
|
|
1663
1765
|
} = useSelectItemContext();
|
|
1664
1766
|
const {
|
|
1665
1767
|
firstItemTextRef,
|
|
@@ -1675,13 +1777,13 @@ const SelectItemText = /*#__PURE__*/React.memo(/*#__PURE__*/React.forwardRef(fun
|
|
|
1675
1777
|
if (!node) {
|
|
1676
1778
|
return;
|
|
1677
1779
|
}
|
|
1678
|
-
if (index === 0) {
|
|
1780
|
+
if (hasRegistered && index === 0) {
|
|
1679
1781
|
firstItemTextRef.current = node;
|
|
1680
1782
|
}
|
|
1681
|
-
if (selectedByFocus) {
|
|
1783
|
+
if (hasRegistered && selectedByFocus) {
|
|
1682
1784
|
selectedItemTextRef.current = node;
|
|
1683
1785
|
}
|
|
1684
|
-
}, [firstItemTextRef, selectedItemTextRef, index, selectedByFocus]);
|
|
1786
|
+
}, [firstItemTextRef, selectedItemTextRef, index, selectedByFocus, hasRegistered]);
|
|
1685
1787
|
const element = useRenderElement('div', componentProps, {
|
|
1686
1788
|
ref: [localRef, forwardedRef, textRef],
|
|
1687
1789
|
props: elementProps
|
|
@@ -1746,9 +1848,6 @@ const SelectGroupLabel = /*#__PURE__*/React.forwardRef(function SelectGroupLabel
|
|
|
1746
1848
|
const id = useBaseUiId(idProp);
|
|
1747
1849
|
useIsoLayoutEffect(() => {
|
|
1748
1850
|
setLabelId(id);
|
|
1749
|
-
return () => {
|
|
1750
|
-
setLabelId(currentId => currentId === id ? undefined : currentId);
|
|
1751
|
-
};
|
|
1752
1851
|
}, [id, setLabelId]);
|
|
1753
1852
|
const element = useRenderElement('div', componentProps, {
|
|
1754
1853
|
ref: forwardedRef,
|