@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
|
@@ -35,77 +35,6 @@ function useFieldItemContext() {
|
|
|
35
35
|
return context;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
const FieldsetRootContext = /*#__PURE__*/React__namespace.createContext(undefined);
|
|
39
|
-
if (process.env.NODE_ENV !== "production") FieldsetRootContext.displayName = "FieldsetRootContext";
|
|
40
|
-
function useFieldsetRootContext(optional = false) {
|
|
41
|
-
const context = React__namespace.useContext(FieldsetRootContext);
|
|
42
|
-
if (!context && !optional) {
|
|
43
|
-
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: FieldsetRootContext is missing. Fieldset parts must be placed within <Fieldset.Root>.' : useRenderElement.formatErrorMessage(86));
|
|
44
|
-
}
|
|
45
|
-
return context;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const LabelableProvider = function LabelableProvider(props) {
|
|
49
|
-
const defaultId = useScrollLock.useBaseUiId();
|
|
50
|
-
const initialControlId = props.controlId === undefined ? defaultId : props.controlId;
|
|
51
|
-
const [controlId, setControlIdState] = React__namespace.useState(initialControlId);
|
|
52
|
-
const [labelId, setLabelId] = React__namespace.useState(props.labelId);
|
|
53
|
-
const [messageIds, setMessageIds] = React__namespace.useState([]);
|
|
54
|
-
const registrationsRef = useRenderElement.useRefWithInit(() => new Map());
|
|
55
|
-
const {
|
|
56
|
-
messageIds: parentMessageIds
|
|
57
|
-
} = useCompositeListItem.useLabelableContext();
|
|
58
|
-
const registerControlId = useScrollLock.useStableCallback((source, nextId) => {
|
|
59
|
-
const registrations = registrationsRef.current;
|
|
60
|
-
if (nextId === undefined) {
|
|
61
|
-
registrations.delete(source);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
registrations.set(source, nextId);
|
|
65
|
-
|
|
66
|
-
// Only flush when registering, not when unregistering.
|
|
67
|
-
// This prevents loops during rapid unmount/remount cycles (e.g. React Activity).
|
|
68
|
-
// The next registration will pick up the correct state.
|
|
69
|
-
setControlIdState(prev => {
|
|
70
|
-
if (registrations.size === 0) {
|
|
71
|
-
return undefined;
|
|
72
|
-
}
|
|
73
|
-
let nextControlId;
|
|
74
|
-
for (const id of registrations.values()) {
|
|
75
|
-
if (prev !== undefined && id === prev) {
|
|
76
|
-
return prev;
|
|
77
|
-
}
|
|
78
|
-
if (nextControlId === undefined) {
|
|
79
|
-
nextControlId = id;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return nextControlId;
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
const getDescriptionProps = React__namespace.useCallback(externalProps => {
|
|
86
|
-
const ids = externalProps['aria-describedby'] ? externalProps['aria-describedby'].split(' ') : [];
|
|
87
|
-
ids.push(...parentMessageIds, ...messageIds);
|
|
88
|
-
return {
|
|
89
|
-
...externalProps,
|
|
90
|
-
'aria-describedby': Array.from(new Set(ids)).join(' ') || undefined
|
|
91
|
-
};
|
|
92
|
-
}, [parentMessageIds, messageIds]);
|
|
93
|
-
const contextValue = React__namespace.useMemo(() => ({
|
|
94
|
-
controlId,
|
|
95
|
-
registerControlId,
|
|
96
|
-
labelId,
|
|
97
|
-
setLabelId,
|
|
98
|
-
messageIds,
|
|
99
|
-
setMessageIds,
|
|
100
|
-
getDescriptionProps
|
|
101
|
-
}), [controlId, registerControlId, labelId, setLabelId, messageIds, setMessageIds, getDescriptionProps]);
|
|
102
|
-
return /*#__PURE__*/jsxRuntime.jsx(useCompositeListItem.LabelableContext.Provider, {
|
|
103
|
-
value: contextValue,
|
|
104
|
-
children: props.children
|
|
105
|
-
});
|
|
106
|
-
};
|
|
107
|
-
if (process.env.NODE_ENV !== "production") LabelableProvider.displayName = "LabelableProvider";
|
|
108
|
-
|
|
109
38
|
/**
|
|
110
39
|
* Combines the field's client-side, stateful validity data with the external invalid state to
|
|
111
40
|
* determine the field's true validity.
|
|
@@ -121,34 +50,35 @@ function getCombinedFieldValidityData(validityData, invalid) {
|
|
|
121
50
|
}
|
|
122
51
|
|
|
123
52
|
const validityKeys = Object.keys(useCompositeListItem.DEFAULT_VALIDITY_STATE);
|
|
124
|
-
|
|
125
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Whether an input participates in the surrounding Base UI Form. Inputs that are effectively
|
|
55
|
+
* disabled, or whose `form` attribute explicitly associates them with another form, are excluded.
|
|
56
|
+
* DOM position only matters when it associates the input with a different form. Otherwise, field
|
|
57
|
+
* registration is context-driven, so portaled inputs (for example inside a dialog) still belong to
|
|
58
|
+
* the form for both validation and values projected into `onFormSubmit`.
|
|
59
|
+
*/
|
|
60
|
+
function isEligibleInput(input, formElement) {
|
|
61
|
+
if (input.matches(':disabled')) {
|
|
126
62
|
return false;
|
|
127
63
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (key === 'valid') {
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
if (key === 'valueMissing') {
|
|
134
|
-
onlyValueMissing = state[key];
|
|
135
|
-
} else if (state[key]) {
|
|
136
|
-
onlyValueMissing = false;
|
|
137
|
-
}
|
|
64
|
+
if (!formElement || input.form === formElement) {
|
|
65
|
+
return true;
|
|
138
66
|
}
|
|
139
|
-
|
|
67
|
+
|
|
68
|
+
// React context crosses portal boundaries. An unassociated portaled input still participates in
|
|
69
|
+
// contextual validation, unless an explicit `form` attribute opts it out of the surrounding Form.
|
|
70
|
+
return input.form === null && !input.hasAttribute('form');
|
|
140
71
|
}
|
|
141
72
|
|
|
142
73
|
/**
|
|
143
74
|
* Picks the input whose native validity should represent a field that owns several inputs (such as a
|
|
144
|
-
* checkbox group). Prefers the first
|
|
145
|
-
*
|
|
146
|
-
* skipped because they don't participate in native constraint validation.
|
|
75
|
+
* checkbox or radio group). Prefers the first eligible currently-invalid input, where "first" follows
|
|
76
|
+
* registration order (mount order), and otherwise returns the first eligible input.
|
|
147
77
|
*/
|
|
148
|
-
function findRepresentativeInput(inputs) {
|
|
78
|
+
function findRepresentativeInput(inputs, formElement) {
|
|
149
79
|
let fallback = null;
|
|
150
|
-
for (const input of inputs) {
|
|
151
|
-
if (input
|
|
80
|
+
for (const input of inputs.keys()) {
|
|
81
|
+
if (!isEligibleInput(input, formElement)) {
|
|
152
82
|
continue;
|
|
153
83
|
}
|
|
154
84
|
if (!input.validity.valid) {
|
|
@@ -159,17 +89,14 @@ function findRepresentativeInput(inputs) {
|
|
|
159
89
|
return fallback;
|
|
160
90
|
}
|
|
161
91
|
function clearCustomValidity(element, inputs) {
|
|
162
|
-
|
|
163
|
-
for (const input of inputs) {
|
|
92
|
+
for (const input of inputs.keys()) {
|
|
164
93
|
input.setCustomValidity('');
|
|
165
|
-
didClearElement ||= input === element;
|
|
166
|
-
}
|
|
167
|
-
if (!didClearElement) {
|
|
168
|
-
element.setCustomValidity('');
|
|
169
94
|
}
|
|
95
|
+
element?.setCustomValidity('');
|
|
170
96
|
}
|
|
171
97
|
function useFieldValidation(params) {
|
|
172
98
|
const {
|
|
99
|
+
elementRef,
|
|
173
100
|
formRef
|
|
174
101
|
} = useCompositeListItem.useFormContext();
|
|
175
102
|
const {
|
|
@@ -181,7 +108,7 @@ function useFieldValidation(params) {
|
|
|
181
108
|
markedDirtyRef,
|
|
182
109
|
state,
|
|
183
110
|
shouldValidateOnChange,
|
|
184
|
-
|
|
111
|
+
registeredFieldIdRef
|
|
185
112
|
} = params;
|
|
186
113
|
const {
|
|
187
114
|
controlId,
|
|
@@ -189,32 +116,27 @@ function useFieldValidation(params) {
|
|
|
189
116
|
} = useCompositeListItem.useLabelableContext();
|
|
190
117
|
const timeout = useScrollLock.useTimeout();
|
|
191
118
|
const inputRef = React__namespace.useRef(null);
|
|
192
|
-
const registeredInputs = useRenderElement.useRefWithInit(() => new
|
|
119
|
+
const registeredInputs = useRenderElement.useRefWithInit(() => new Map()).current;
|
|
193
120
|
const validationCommitIdRef = React__namespace.useRef(0);
|
|
194
121
|
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
registeredInputs.add(element);
|
|
122
|
+
// Groups register several inputs against a single field so focus, validation, and form-value
|
|
123
|
+
// projection can use the same live controls. This also ensures a `required` checkbox can't be
|
|
124
|
+
// satisfied by another input in the group, matching native per-checkbox behavior.
|
|
125
|
+
const registerInput = React__namespace.useCallback((element, registration) => {
|
|
126
|
+
registeredInputs.set(element, registration);
|
|
202
127
|
return () => {
|
|
203
128
|
registeredInputs.delete(element);
|
|
204
129
|
};
|
|
205
130
|
}, [registeredInputs]);
|
|
131
|
+
const getInputControl = useScrollLock.useStableCallback(() => {
|
|
132
|
+
const element = findRepresentativeInput(registeredInputs, elementRef.current);
|
|
133
|
+
return element && registeredInputs.get(element)?.controlRef.current || null;
|
|
134
|
+
});
|
|
206
135
|
const commit = useScrollLock.useStableCallback(async (value, revalidate = false) => {
|
|
207
|
-
// A field can own several inputs (a checkbox group), but only the last-mounted one wins the shared
|
|
208
|
-
// `inputRef`. Validate against the registry instead so every input counts; `inputRef` is the
|
|
209
|
-
// fallback only when no registered input applies (none registered, or all of them disabled).
|
|
210
|
-
const element = findRepresentativeInput(registeredInputs) ?? inputRef.current;
|
|
211
|
-
if (!element) {
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
136
|
validationCommitIdRef.current += 1;
|
|
215
137
|
const validationCommitId = validationCommitIdRef.current;
|
|
216
138
|
function updateRegisteredFieldValidity(nextValidityData, externalInvalid = invalid) {
|
|
217
|
-
const fieldId =
|
|
139
|
+
const fieldId = registeredFieldIdRef.current ?? controlId;
|
|
218
140
|
if (fieldId == null) {
|
|
219
141
|
return;
|
|
220
142
|
}
|
|
@@ -228,8 +150,31 @@ function useFieldValidation(params) {
|
|
|
228
150
|
validityData: validityDataWithFormErrors
|
|
229
151
|
});
|
|
230
152
|
}
|
|
153
|
+
function publishAllValid(input, externalInvalid) {
|
|
154
|
+
const nextValidityData = {
|
|
155
|
+
value,
|
|
156
|
+
state: {
|
|
157
|
+
...useCompositeListItem.DEFAULT_VALIDITY_STATE,
|
|
158
|
+
valid: true
|
|
159
|
+
},
|
|
160
|
+
error: '',
|
|
161
|
+
errors: [],
|
|
162
|
+
initialValue: validityData.initialValue
|
|
163
|
+
};
|
|
164
|
+
clearCustomValidity(input, registeredInputs);
|
|
165
|
+
updateRegisteredFieldValidity(nextValidityData, externalInvalid);
|
|
166
|
+
setValidityData(nextValidityData);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// A field can own several inputs (such as a checkbox or radio group), but only the last-mounted
|
|
170
|
+
// one wins the shared `inputRef`. Validate against the registry instead so every input counts;
|
|
171
|
+
// `inputRef` is the fallback only when no inputs are registered.
|
|
172
|
+
const element = registeredInputs.size > 0 ? findRepresentativeInput(registeredInputs, elementRef.current) : inputRef.current;
|
|
173
|
+
// A field with no eligible input has no native constraint, but its custom validator still
|
|
174
|
+
// applies to the logical value at the configured validation boundary.
|
|
175
|
+
|
|
231
176
|
if (revalidate) {
|
|
232
|
-
if (state.valid !== false) {
|
|
177
|
+
if (state.valid !== false || !element) {
|
|
233
178
|
return;
|
|
234
179
|
}
|
|
235
180
|
const currentNativeValidity = element.validity;
|
|
@@ -237,39 +182,20 @@ function useFieldValidation(params) {
|
|
|
237
182
|
// The 'valueMissing' (required) condition has been resolved by the user typing.
|
|
238
183
|
// Temporarily mark the field as valid for this onChange event.
|
|
239
184
|
// Other native errors (e.g., typeMismatch) will be caught by full validation on blur or submit.
|
|
240
|
-
const nextValidityData = {
|
|
241
|
-
value,
|
|
242
|
-
state: {
|
|
243
|
-
...useCompositeListItem.DEFAULT_VALIDITY_STATE,
|
|
244
|
-
valid: true
|
|
245
|
-
},
|
|
246
|
-
error: '',
|
|
247
|
-
errors: [],
|
|
248
|
-
initialValue: validityData.initialValue
|
|
249
|
-
};
|
|
250
|
-
clearCustomValidity(element, registeredInputs);
|
|
251
|
-
|
|
252
185
|
// The required value is now present; ignore stale external invalid state for this pass.
|
|
253
|
-
|
|
254
|
-
setValidityData(nextValidityData);
|
|
186
|
+
publishAllValid(element, false);
|
|
255
187
|
return;
|
|
256
188
|
}
|
|
257
189
|
|
|
258
|
-
//
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}, {});
|
|
264
|
-
|
|
265
|
-
// If it's (still) natively invalid due to something other than just valueMissing,
|
|
266
|
-
// then bail from this revalidation on change to avoid "scolding" for other errors.
|
|
267
|
-
if (!currentNativeValidityObject.valid && !isOnlyValueMissing(currentNativeValidityObject)) {
|
|
268
|
-
return;
|
|
190
|
+
// A stale custom error can coexist with valueMissing, but defer any other native errors.
|
|
191
|
+
for (const key of validityKeys) {
|
|
192
|
+
if (key !== 'valid' && key !== 'valueMissing' && key !== 'customError' && currentNativeValidity[key]) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
269
195
|
}
|
|
270
196
|
|
|
271
|
-
//
|
|
272
|
-
//
|
|
197
|
+
// Value is still missing: publish the current native state so valueMissing and the changed
|
|
198
|
+
// value are observable immediately. Full custom validation still waits for its boundary.
|
|
273
199
|
}
|
|
274
200
|
function getState(el) {
|
|
275
201
|
const computedState = validityKeys.reduce((acc, key) => {
|
|
@@ -299,10 +225,16 @@ function useFieldValidation(params) {
|
|
|
299
225
|
timeout.clear();
|
|
300
226
|
let result = null;
|
|
301
227
|
let validationErrors = [];
|
|
302
|
-
|
|
228
|
+
|
|
229
|
+
// With no representative input the field carries no native constraint, so start from an
|
|
230
|
+
// all-valid native state and let the custom `validate` result below decide the outcome.
|
|
231
|
+
const nextState = element ? getState(element) : {
|
|
232
|
+
...useCompositeListItem.DEFAULT_VALIDITY_STATE,
|
|
233
|
+
valid: true
|
|
234
|
+
};
|
|
303
235
|
let defaultValidationMessage;
|
|
304
236
|
const isValidatingOnChange = shouldValidateOnChange();
|
|
305
|
-
if (element.validationMessage && !isValidatingOnChange) {
|
|
237
|
+
if (element && element.validationMessage && !isValidatingOnChange) {
|
|
306
238
|
// not validating on change, if there is a `validationMessage` from
|
|
307
239
|
// native validity, set errors and skip calling the custom validate fn
|
|
308
240
|
defaultValidationMessage = element.validationMessage;
|
|
@@ -331,20 +263,20 @@ function useFieldValidation(params) {
|
|
|
331
263
|
nextState.customError = true;
|
|
332
264
|
if (Array.isArray(result)) {
|
|
333
265
|
validationErrors = result;
|
|
334
|
-
element
|
|
266
|
+
element?.setCustomValidity(result.join('\n'));
|
|
335
267
|
} else if (result) {
|
|
336
268
|
validationErrors = [result];
|
|
337
|
-
element
|
|
269
|
+
element?.setCustomValidity(result);
|
|
338
270
|
}
|
|
339
271
|
} else if (isValidatingOnChange) {
|
|
340
272
|
// validate function returned no errors, if validating on change
|
|
341
273
|
// we need to clear the custom validity state
|
|
342
274
|
clearCustomValidity(element, registeredInputs);
|
|
343
275
|
nextState.customError = false;
|
|
344
|
-
if (element.validationMessage) {
|
|
276
|
+
if (element && element.validationMessage) {
|
|
345
277
|
defaultValidationMessage = element.validationMessage;
|
|
346
278
|
validationErrors = [element.validationMessage];
|
|
347
|
-
} else if (element.validity.valid && !nextState.valid) {
|
|
279
|
+
} else if ((!element || element.validity.valid) && !nextState.valid) {
|
|
348
280
|
nextState.valid = true;
|
|
349
281
|
}
|
|
350
282
|
}
|
|
@@ -379,12 +311,85 @@ function useFieldValidation(params) {
|
|
|
379
311
|
return React__namespace.useMemo(() => ({
|
|
380
312
|
getValidationProps,
|
|
381
313
|
inputRef,
|
|
314
|
+
registeredInputs,
|
|
382
315
|
registerInput,
|
|
316
|
+
getInputControl,
|
|
383
317
|
commit,
|
|
384
318
|
change
|
|
385
|
-
}), [getValidationProps, registerInput, commit, change]);
|
|
319
|
+
}), [getValidationProps, registeredInputs, registerInput, getInputControl, commit, change]);
|
|
386
320
|
}
|
|
387
321
|
|
|
322
|
+
const FieldsetRootContext = /*#__PURE__*/React__namespace.createContext(undefined);
|
|
323
|
+
if (process.env.NODE_ENV !== "production") FieldsetRootContext.displayName = "FieldsetRootContext";
|
|
324
|
+
function useFieldsetRootContext(optional = false) {
|
|
325
|
+
const context = React__namespace.useContext(FieldsetRootContext);
|
|
326
|
+
if (!context && !optional) {
|
|
327
|
+
throw new Error(process.env.NODE_ENV !== "production" ? 'Base UI: FieldsetRootContext is missing. Fieldset parts must be placed within <Fieldset.Root>.' : useRenderElement.formatErrorMessage(86));
|
|
328
|
+
}
|
|
329
|
+
return context;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const LabelableProvider = function LabelableProvider(props) {
|
|
333
|
+
const defaultId = useScrollLock.useBaseUiId();
|
|
334
|
+
const initialControlId = props.controlId === undefined ? defaultId : props.controlId;
|
|
335
|
+
const [controlId, setControlIdState] = React__namespace.useState(initialControlId);
|
|
336
|
+
const [labelId, setLabelId] = React__namespace.useState(props.labelId);
|
|
337
|
+
const [messageIds, setMessageIds] = React__namespace.useState([]);
|
|
338
|
+
const registrationsRef = useRenderElement.useRefWithInit(() => new Map());
|
|
339
|
+
const {
|
|
340
|
+
messageIds: parentMessageIds
|
|
341
|
+
} = useCompositeListItem.useLabelableContext();
|
|
342
|
+
const registerControlId = useScrollLock.useStableCallback((source, nextId) => {
|
|
343
|
+
const registrations = registrationsRef.current;
|
|
344
|
+
if (nextId === undefined) {
|
|
345
|
+
registrations.delete(source);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
registrations.set(source, nextId);
|
|
349
|
+
|
|
350
|
+
// Only flush when registering, not when unregistering.
|
|
351
|
+
// This prevents loops during rapid unmount/remount cycles (e.g. React Activity).
|
|
352
|
+
// The next registration will pick up the correct state.
|
|
353
|
+
setControlIdState(prev => {
|
|
354
|
+
if (registrations.size === 0) {
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
let nextControlId;
|
|
358
|
+
for (const id of registrations.values()) {
|
|
359
|
+
if (prev !== undefined && id === prev) {
|
|
360
|
+
return prev;
|
|
361
|
+
}
|
|
362
|
+
if (nextControlId === undefined) {
|
|
363
|
+
nextControlId = id;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return nextControlId;
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
const getDescriptionProps = React__namespace.useCallback(externalProps => {
|
|
370
|
+
const ids = externalProps['aria-describedby'] ? externalProps['aria-describedby'].split(' ') : [];
|
|
371
|
+
ids.push(...parentMessageIds, ...messageIds);
|
|
372
|
+
return {
|
|
373
|
+
...externalProps,
|
|
374
|
+
'aria-describedby': Array.from(new Set(ids)).join(' ') || undefined
|
|
375
|
+
};
|
|
376
|
+
}, [parentMessageIds, messageIds]);
|
|
377
|
+
const contextValue = React__namespace.useMemo(() => ({
|
|
378
|
+
controlId,
|
|
379
|
+
registerControlId,
|
|
380
|
+
labelId,
|
|
381
|
+
setLabelId,
|
|
382
|
+
messageIds,
|
|
383
|
+
setMessageIds,
|
|
384
|
+
getDescriptionProps
|
|
385
|
+
}), [controlId, registerControlId, labelId, setLabelId, messageIds, setMessageIds, getDescriptionProps]);
|
|
386
|
+
return /*#__PURE__*/jsxRuntime.jsx(useCompositeListItem.LabelableContext.Provider, {
|
|
387
|
+
value: contextValue,
|
|
388
|
+
children: props.children
|
|
389
|
+
});
|
|
390
|
+
};
|
|
391
|
+
if (process.env.NODE_ENV !== "production") LabelableProvider.displayName = "LabelableProvider";
|
|
392
|
+
|
|
388
393
|
function useFieldControlRegistration(params) {
|
|
389
394
|
const {
|
|
390
395
|
commit,
|
|
@@ -392,7 +397,7 @@ function useFieldControlRegistration(params) {
|
|
|
392
397
|
markedDirtyRef,
|
|
393
398
|
name,
|
|
394
399
|
setRegisteredFieldName,
|
|
395
|
-
|
|
400
|
+
registeredFieldIdRef,
|
|
396
401
|
setValidityData,
|
|
397
402
|
validityData
|
|
398
403
|
} = params;
|
|
@@ -401,7 +406,7 @@ function useFieldControlRegistration(params) {
|
|
|
401
406
|
} = useCompositeListItem.useFormContext();
|
|
402
407
|
const activeFieldControlSourceRef = React__namespace.useRef(null);
|
|
403
408
|
const registrationRef = React__namespace.useRef(null);
|
|
404
|
-
const
|
|
409
|
+
const initialValueCapturedRef = React__namespace.useRef(false);
|
|
405
410
|
const getValueForForm = useScrollLock.useStableCallback(() => {
|
|
406
411
|
const registration = registrationRef.current;
|
|
407
412
|
if (!registration) {
|
|
@@ -432,7 +437,7 @@ function useFieldControlRegistration(params) {
|
|
|
432
437
|
formRef.current.fields.set(registration.id, {
|
|
433
438
|
getValue: getValueForForm,
|
|
434
439
|
name: name ?? registration.name,
|
|
435
|
-
controlRef: registration.controlRef
|
|
440
|
+
controlRef: registration.controlRef,
|
|
436
441
|
validityData: getCombinedFieldValidityData(validityData, invalid),
|
|
437
442
|
validate
|
|
438
443
|
});
|
|
@@ -442,18 +447,23 @@ function useFieldControlRegistration(params) {
|
|
|
442
447
|
formRef.current.fields.delete(id);
|
|
443
448
|
}
|
|
444
449
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
450
|
+
|
|
451
|
+
// The baseline belongs to the field, not to a control instance: registration re-runs on every
|
|
452
|
+
// value change, and a control that unmounts and remounts (or is swapped for another one) comes
|
|
453
|
+
// back as a brand new registration. Capturing more than once would turn whichever value the
|
|
454
|
+
// control happens to hold at that point into the initial value, so a modified field would read
|
|
455
|
+
// pristine and its real initial value would read dirty. Consumers that want a fresh baseline
|
|
456
|
+
// remount or key `<Field.Root>` itself.
|
|
457
|
+
function captureInitialValue(registration) {
|
|
458
|
+
if (initialValueCapturedRef.current) {
|
|
448
459
|
return;
|
|
449
460
|
}
|
|
461
|
+
initialValueCapturedRef.current = true;
|
|
450
462
|
const initialValue = getRegistrationValue(registration);
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
}));
|
|
456
|
-
}
|
|
463
|
+
setValidityData(prev => prev.initialValue === initialValue ? prev : {
|
|
464
|
+
...prev,
|
|
465
|
+
initialValue
|
|
466
|
+
});
|
|
457
467
|
}
|
|
458
468
|
useScrollLock.useIsoLayoutEffect(() => {
|
|
459
469
|
const registration = registrationRef.current;
|
|
@@ -464,7 +474,7 @@ function useFieldControlRegistration(params) {
|
|
|
464
474
|
formRef.current.fields.set(registration.id, {
|
|
465
475
|
getValue: getValueForForm,
|
|
466
476
|
name: name ?? registration.name,
|
|
467
|
-
controlRef: registration.controlRef
|
|
477
|
+
controlRef: registration.controlRef,
|
|
468
478
|
validityData: getCombinedFieldValidityData(validityData, invalid),
|
|
469
479
|
validate
|
|
470
480
|
});
|
|
@@ -485,7 +495,7 @@ function useFieldControlRegistration(params) {
|
|
|
485
495
|
deleteRegistration();
|
|
486
496
|
registrationRef.current = null;
|
|
487
497
|
setRegisteredFieldName(undefined);
|
|
488
|
-
|
|
498
|
+
registeredFieldIdRef.current = undefined;
|
|
489
499
|
}
|
|
490
500
|
return;
|
|
491
501
|
}
|
|
@@ -495,11 +505,11 @@ function useFieldControlRegistration(params) {
|
|
|
495
505
|
if (!name) {
|
|
496
506
|
setRegisteredFieldName(registration.name);
|
|
497
507
|
}
|
|
498
|
-
|
|
508
|
+
registeredFieldIdRef.current = registration.id;
|
|
499
509
|
if (previousId && previousId !== registration.id) {
|
|
500
510
|
deleteRegistration(previousId);
|
|
501
511
|
}
|
|
502
|
-
|
|
512
|
+
captureInitialValue(registration);
|
|
503
513
|
refreshRegistration();
|
|
504
514
|
});
|
|
505
515
|
return [validate, register];
|
|
@@ -544,10 +554,6 @@ const FieldRootInner = /*#__PURE__*/React__namespace.forwardRef(function FieldRo
|
|
|
544
554
|
markedDirtyRef.current = dirtyProp;
|
|
545
555
|
}
|
|
546
556
|
}, [dirtyProp]);
|
|
547
|
-
const getRegisteredFieldId = React__namespace.useCallback(() => registeredFieldIdRef.current, []);
|
|
548
|
-
const setRegisteredFieldId = React__namespace.useCallback(id => {
|
|
549
|
-
registeredFieldIdRef.current = id;
|
|
550
|
-
}, []);
|
|
551
557
|
const setDirty = useScrollLock.useStableCallback(value => {
|
|
552
558
|
if (dirtyProp !== undefined) {
|
|
553
559
|
return;
|
|
@@ -574,7 +580,11 @@ const FieldRootInner = /*#__PURE__*/React__namespace.forwardRef(function FieldRo
|
|
|
574
580
|
value: null,
|
|
575
581
|
initialValue: null
|
|
576
582
|
});
|
|
577
|
-
|
|
583
|
+
|
|
584
|
+
// App-controlled invalidity (the `invalid` prop and `<Form>` errors) keeps the field marked
|
|
585
|
+
// invalid even while disabled. Only computed validity (native constraints and `validate`)
|
|
586
|
+
// is suppressed when disabled, matching `:disabled` not participating in constraint validation.
|
|
587
|
+
const valid = !invalid && (disabled ? null : validityData.state.valid);
|
|
578
588
|
const state = React__namespace.useMemo(() => ({
|
|
579
589
|
disabled,
|
|
580
590
|
touched,
|
|
@@ -592,7 +602,7 @@ const FieldRootInner = /*#__PURE__*/React__namespace.forwardRef(function FieldRo
|
|
|
592
602
|
markedDirtyRef,
|
|
593
603
|
state,
|
|
594
604
|
shouldValidateOnChange,
|
|
595
|
-
|
|
605
|
+
registeredFieldIdRef
|
|
596
606
|
});
|
|
597
607
|
const [validateFieldControl, registerFieldControl] = useFieldControlRegistration({
|
|
598
608
|
commit: validation.commit,
|
|
@@ -600,7 +610,7 @@ const FieldRootInner = /*#__PURE__*/React__namespace.forwardRef(function FieldRo
|
|
|
600
610
|
markedDirtyRef,
|
|
601
611
|
name,
|
|
602
612
|
setRegisteredFieldName,
|
|
603
|
-
|
|
613
|
+
registeredFieldIdRef,
|
|
604
614
|
setValidityData,
|
|
605
615
|
validityData
|
|
606
616
|
});
|
|
@@ -613,23 +623,16 @@ const FieldRootInner = /*#__PURE__*/React__namespace.forwardRef(function FieldRo
|
|
|
613
623
|
validityData,
|
|
614
624
|
setValidityData,
|
|
615
625
|
disabled,
|
|
616
|
-
touched,
|
|
617
626
|
setTouched,
|
|
618
|
-
dirty,
|
|
619
627
|
setDirty,
|
|
620
|
-
filled,
|
|
621
628
|
setFilled,
|
|
622
|
-
focused,
|
|
623
629
|
setFocused,
|
|
624
|
-
validate,
|
|
625
630
|
validationMode,
|
|
626
|
-
validationDebounceTime,
|
|
627
631
|
shouldValidateOnChange,
|
|
628
632
|
state,
|
|
629
|
-
markedDirtyRef,
|
|
630
633
|
registerFieldControl,
|
|
631
634
|
validation
|
|
632
|
-
}), [invalid, effectiveName, validityData, disabled,
|
|
635
|
+
}), [invalid, effectiveName, validityData, disabled, setTouched, setDirty, setFilled, setFocused, validationMode, shouldValidateOnChange, state, registerFieldControl, validation]);
|
|
633
636
|
const element = useRenderElement.useRenderElement('div', componentProps, {
|
|
634
637
|
ref: forwardedRef,
|
|
635
638
|
state,
|
|
@@ -786,13 +789,13 @@ const FieldError = /*#__PURE__*/React__namespace.forwardRef(function FieldError(
|
|
|
786
789
|
} else if (validityData.errors.length > 1) {
|
|
787
790
|
error = validityData.errors;
|
|
788
791
|
}
|
|
789
|
-
let errorMessage = error
|
|
792
|
+
let errorMessage = error;
|
|
790
793
|
if (Array.isArray(error)) {
|
|
791
794
|
errorMessage = error.length > 1 ? /*#__PURE__*/jsxRuntime.jsx("ul", {
|
|
792
795
|
children: error.map(message => /*#__PURE__*/jsxRuntime.jsx("li", {
|
|
793
796
|
children: message
|
|
794
797
|
}, message))
|
|
795
|
-
}) : error[0]
|
|
798
|
+
}) : error[0];
|
|
796
799
|
}
|
|
797
800
|
const errorKey = Array.isArray(error) ? JSON.stringify(error) : error;
|
|
798
801
|
if (rendered && errorKey !== lastRenderedMessageKey) {
|