@jobber/components 8.23.0 → 8.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/dist/Autocomplete/index.cjs +1 -1
  2. package/dist/Autocomplete/index.mjs +1 -1
  3. package/dist/Card/index.cjs +4 -4
  4. package/dist/Card/index.mjs +4 -4
  5. package/dist/Chip/index.cjs +1 -1
  6. package/dist/Chip/index.mjs +1 -1
  7. package/dist/Chips/InternalChipDismissible/hooks/index.cjs +1 -1
  8. package/dist/Chips/InternalChipDismissible/hooks/index.mjs +1 -1
  9. package/dist/Chips/InternalChipDismissible/index.cjs +1 -1
  10. package/dist/Chips/InternalChipDismissible/index.mjs +1 -1
  11. package/dist/Chips/index.cjs +1 -1
  12. package/dist/Chips/index.mjs +1 -1
  13. package/dist/ComboboxChipRemove-cjs.js +668 -379
  14. package/dist/ComboboxChipRemove-es.js +508 -219
  15. package/dist/ConfirmationModal/index.cjs +1 -1
  16. package/dist/ConfirmationModal/index.mjs +1 -1
  17. package/dist/DataDump/index.cjs +4 -4
  18. package/dist/DataDump/index.mjs +4 -4
  19. package/dist/DataList/components/DataListActions/index.cjs +1 -1
  20. package/dist/DataList/components/DataListActions/index.mjs +1 -1
  21. package/dist/DataList/components/DataListBulkActions/index.cjs +1 -1
  22. package/dist/DataList/components/DataListBulkActions/index.mjs +1 -1
  23. package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +1 -1
  24. package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +1 -1
  25. package/dist/DataList/components/DataListFilters/index.cjs +1 -1
  26. package/dist/DataList/components/DataListFilters/index.mjs +1 -1
  27. package/dist/DataList/components/DataListHeader/index.cjs +1 -1
  28. package/dist/DataList/components/DataListHeader/index.mjs +1 -1
  29. package/dist/DataList/components/DataListItem/index.cjs +1 -1
  30. package/dist/DataList/components/DataListItem/index.mjs +1 -1
  31. package/dist/DataList/components/DataListItemActions/index.cjs +1 -1
  32. package/dist/DataList/components/DataListItemActions/index.mjs +1 -1
  33. package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +1 -1
  34. package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +1 -1
  35. package/dist/DataList/components/DataListItems/index.cjs +1 -1
  36. package/dist/DataList/components/DataListItems/index.mjs +1 -1
  37. package/dist/DataList/components/DataListLayout/index.cjs +1 -1
  38. package/dist/DataList/components/DataListLayout/index.mjs +1 -1
  39. package/dist/DataList/components/DataListLayoutActions/index.cjs +1 -1
  40. package/dist/DataList/components/DataListLayoutActions/index.mjs +1 -1
  41. package/dist/DataList/index.cjs +1 -1
  42. package/dist/DataList/index.mjs +1 -1
  43. package/dist/DatePicker/index.cjs +1 -1
  44. package/dist/DatePicker/index.mjs +1 -1
  45. package/dist/Dialog/index.cjs +3 -3
  46. package/dist/Dialog/index.mjs +3 -3
  47. package/dist/DrawerDescription-cjs.js +32 -33
  48. package/dist/DrawerDescription-es.js +19 -20
  49. package/dist/DrawerTitle-cjs.js +7 -5
  50. package/dist/DrawerTitle-es.js +7 -5
  51. package/dist/FilterPicker/components/FilterPickerActivator/index.cjs +1 -1
  52. package/dist/FilterPicker/components/FilterPickerActivator/index.mjs +1 -1
  53. package/dist/FilterPicker/components/FilterPickerContent/index.cjs +1 -1
  54. package/dist/FilterPicker/components/FilterPickerContent/index.mjs +1 -1
  55. package/dist/FilterPicker/components/FilterPickerTrigger/index.cjs +1 -1
  56. package/dist/FilterPicker/components/FilterPickerTrigger/index.mjs +1 -1
  57. package/dist/FilterPicker/index.cjs +1 -1
  58. package/dist/FilterPicker/index.mjs +1 -1
  59. package/dist/FormFieldWrapper-cjs.js +2 -2
  60. package/dist/FormFieldWrapper-es.js +2 -2
  61. package/dist/FormatFile/index.cjs +1 -1
  62. package/dist/FormatFile/index.mjs +1 -1
  63. package/dist/Gallery/index.cjs +1 -1
  64. package/dist/Gallery/index.mjs +1 -1
  65. package/dist/InputDate/index.cjs +1 -1
  66. package/dist/InputDate/index.mjs +1 -1
  67. package/dist/InputNumberExperimental-cjs.js +319 -102
  68. package/dist/InputNumberExperimental-es.js +321 -104
  69. package/dist/LightBox/index.cjs +1 -1
  70. package/dist/LightBox/index.mjs +1 -1
  71. package/dist/Menu/index.cjs +4 -4
  72. package/dist/Menu/index.mjs +4 -4
  73. package/dist/MenuSubmenuTrigger-cjs.js +545 -498
  74. package/dist/MenuSubmenuTrigger-es.js +490 -443
  75. package/dist/Modal/index.cjs +1 -1
  76. package/dist/Modal/index.mjs +1 -1
  77. package/dist/NumberFieldInput-cjs.js +609 -496
  78. package/dist/NumberFieldInput-es.js +612 -498
  79. package/dist/Page/index.cjs +4 -4
  80. package/dist/Page/index.mjs +4 -4
  81. package/dist/Popover/index.cjs +1 -1
  82. package/dist/Popover/index.mjs +1 -1
  83. package/dist/ScrollAreaViewport-cjs.js +326 -601
  84. package/dist/ScrollAreaViewport-es.js +278 -548
  85. package/dist/SelectGroupLabel-cjs.js +494 -528
  86. package/dist/SelectGroupLabel-es.js +411 -445
  87. package/dist/Separator-cjs.js +1 -0
  88. package/dist/Separator-es.js +1 -0
  89. package/dist/Tooltip/index.cjs +1 -1
  90. package/dist/Tooltip/index.mjs +1 -1
  91. package/dist/docs/InputTime/InputTime.md +8 -0
  92. package/dist/docs/SelectPrimitive/SelectPrimitive.md +38 -25
  93. package/dist/floating-ui.react-cjs.js +586 -10
  94. package/dist/floating-ui.react-dom-cjs.js +92 -92
  95. package/dist/floating-ui.react-dom-es.js +1 -1
  96. package/dist/floating-ui.react-es.js +580 -4
  97. package/dist/floating-ui.utils-cjs.js +159 -0
  98. package/dist/floating-ui.utils-es.js +136 -0
  99. package/dist/index.cjs +4 -4
  100. package/dist/index.mjs +4 -4
  101. package/dist/{ToolbarRootContext-cjs.js → isElementDisabled-cjs.js} +5 -0
  102. package/dist/{ToolbarRootContext-es.js → isElementDisabled-es.js} +5 -1
  103. package/dist/primitives/BottomSheet/index.cjs +3 -3
  104. package/dist/primitives/BottomSheet/index.mjs +3 -3
  105. package/dist/primitives/ComboboxPrimitive/index.cjs +2 -2
  106. package/dist/primitives/ComboboxPrimitive/index.mjs +2 -2
  107. package/dist/primitives/InputNumberExperimental/index.cjs +1 -1
  108. package/dist/primitives/InputNumberExperimental/index.mjs +1 -1
  109. package/dist/primitives/SelectPrimitive/index.cjs +5 -5
  110. package/dist/primitives/SelectPrimitive/index.mjs +5 -5
  111. package/dist/primitives/index.cjs +4 -4
  112. package/dist/primitives/index.mjs +4 -4
  113. package/dist/resolveAriaLabelledBy-cjs.js +5 -0
  114. package/dist/resolveAriaLabelledBy-es.js +5 -1
  115. package/dist/{CSPContext-cjs.js → scrollEdges-cjs.js} +30 -2
  116. package/dist/{CSPContext-es.js → scrollEdges-es.js} +28 -3
  117. package/dist/styles.css +9 -3
  118. package/dist/unstyledPrimitives/index.cjs +1217 -639
  119. package/dist/unstyledPrimitives/index.mjs +1163 -585
  120. package/dist/useButton-cjs.js +283 -317
  121. package/dist/useButton-es.js +277 -298
  122. package/dist/useCompositeListItem-cjs.js +328 -279
  123. package/dist/useCompositeListItem-es.js +293 -246
  124. package/dist/useLabel-cjs.js +11 -1
  125. package/dist/useLabel-es.js +11 -2
  126. package/dist/useLabelableId-cjs.js +37 -91
  127. package/dist/useLabelableId-es.js +37 -90
  128. package/dist/useRenderElement-cjs.js +81 -26
  129. package/dist/useRenderElement-es.js +81 -27
  130. package/dist/{InternalBackdrop-cjs.js → useScrollLock-cjs.js} +1286 -1220
  131. package/dist/{InternalBackdrop-es.js → useScrollLock-es.js} +1250 -1207
  132. package/package.json +3 -3
  133. package/dist/index.esm-cjs.js +0 -738
  134. package/dist/index.esm-es.js +0 -712
@@ -32,11 +32,16 @@ function _interopNamespaceDefault(e) {
32
32
 
33
33
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
34
34
 
35
- const FieldsetRootContext = /*#__PURE__*/React__namespace.createContext({
36
- legendId: undefined,
37
- setLegendId: () => {},
38
- disabled: undefined
35
+ const FieldItemContext = /*#__PURE__*/React__namespace.createContext({
36
+ disabled: false
39
37
  });
38
+ if (process.env.NODE_ENV !== "production") FieldItemContext.displayName = "FieldItemContext";
39
+ function useFieldItemContext() {
40
+ const context = React__namespace.useContext(FieldItemContext);
41
+ return context;
42
+ }
43
+
44
+ const FieldsetRootContext = /*#__PURE__*/React__namespace.createContext(undefined);
40
45
  if (process.env.NODE_ENV !== "production") FieldsetRootContext.displayName = "FieldsetRootContext";
41
46
  function useFieldsetRootContext(optional = false) {
42
47
  const context = React__namespace.useContext(FieldsetRootContext);
@@ -84,9 +89,12 @@ const LabelableProvider = function LabelableProvider(props) {
84
89
  });
85
90
  });
86
91
  const getDescriptionProps = React__namespace.useCallback(externalProps => {
87
- return useRenderElement.mergeProps({
88
- 'aria-describedby': parentMessageIds.concat(messageIds).join(' ') || undefined
89
- }, externalProps);
92
+ const ids = externalProps['aria-describedby'] ? externalProps['aria-describedby'].split(' ') : [];
93
+ ids.push(...parentMessageIds, ...messageIds);
94
+ return {
95
+ ...externalProps,
96
+ 'aria-describedby': Array.from(new Set(ids)).join(' ') || undefined
97
+ };
90
98
  }, [parentMessageIds, messageIds]);
91
99
  const contextValue = React__namespace.useMemo(() => ({
92
100
  controlId,
@@ -104,6 +112,20 @@ const LabelableProvider = function LabelableProvider(props) {
104
112
  };
105
113
  if (process.env.NODE_ENV !== "production") LabelableProvider.displayName = "LabelableProvider";
106
114
 
115
+ /**
116
+ * Combines the field's client-side, stateful validity data with the external invalid state to
117
+ * determine the field's true validity.
118
+ */
119
+ function getCombinedFieldValidityData(validityData, invalid) {
120
+ return {
121
+ ...validityData,
122
+ state: {
123
+ ...validityData.state,
124
+ valid: !invalid && validityData.state.valid
125
+ }
126
+ };
127
+ }
128
+
107
129
  const validityKeys = Object.keys(useLabelableId.DEFAULT_VALIDITY_STATE);
108
130
  function isOnlyValueMissing(state) {
109
131
  if (!state || state.valid || !state.valueMissing) {
@@ -116,17 +138,45 @@ function isOnlyValueMissing(state) {
116
138
  }
117
139
  if (key === 'valueMissing') {
118
140
  onlyValueMissing = state[key];
119
- }
120
- if (state[key]) {
141
+ } else if (state[key]) {
121
142
  onlyValueMissing = false;
122
143
  }
123
144
  }
124
145
  return onlyValueMissing;
125
146
  }
147
+
148
+ /**
149
+ * Picks the input whose native validity should represent a field that owns several inputs (such as a
150
+ * checkbox group). Prefers the first enabled currently-invalid input, where "first" follows Set
151
+ * insertion order (mount order), and otherwise returns the first enabled input. Disabled inputs are
152
+ * skipped because they don't participate in native constraint validation.
153
+ */
154
+ function findRepresentativeInput(inputs) {
155
+ let fallback = null;
156
+ for (const input of inputs) {
157
+ if (input.disabled) {
158
+ continue;
159
+ }
160
+ if (!input.validity.valid) {
161
+ return input;
162
+ }
163
+ fallback ??= input;
164
+ }
165
+ return fallback;
166
+ }
167
+ function clearCustomValidity(element, inputs) {
168
+ let didClearElement = false;
169
+ for (const input of inputs) {
170
+ input.setCustomValidity('');
171
+ didClearElement ||= input === element;
172
+ }
173
+ if (!didClearElement) {
174
+ element.setCustomValidity('');
175
+ }
176
+ }
126
177
  function useFieldValidation(params) {
127
178
  const {
128
- formRef,
129
- clearErrors
179
+ formRef
130
180
  } = useLabelableId.useFormContext();
131
181
  const {
132
182
  setValidityData,
@@ -136,8 +186,8 @@ function useFieldValidation(params) {
136
186
  invalid,
137
187
  markedDirtyRef,
138
188
  state,
139
- name,
140
- shouldValidateOnChange
189
+ shouldValidateOnChange,
190
+ getRegisteredFieldId
141
191
  } = params;
142
192
  const {
143
193
  controlId,
@@ -145,11 +195,45 @@ function useFieldValidation(params) {
145
195
  } = useLabelableId.useLabelableContext();
146
196
  const timeout = useButton.useTimeout();
147
197
  const inputRef = React__namespace.useRef(null);
198
+ const registeredInputs = useRenderElement.useRefWithInit(() => new Set()).current;
199
+ const validationCommitIdRef = React__namespace.useRef(0);
200
+
201
+ // Checkbox groups register several inputs against a single field. Track them so a `required`
202
+ // checkbox can't be satisfied by another input in the group, matching native per-checkbox behavior.
203
+ const registerInput = React__namespace.useCallback(element => {
204
+ if (!element) {
205
+ return undefined;
206
+ }
207
+ registeredInputs.add(element);
208
+ return () => {
209
+ registeredInputs.delete(element);
210
+ };
211
+ }, [registeredInputs]);
148
212
  const commit = useButton.useStableCallback(async (value, revalidate = false) => {
149
- const element = inputRef.current;
213
+ // A field can own several inputs (a checkbox group), but only the last-mounted one wins the shared
214
+ // `inputRef`. Validate against the registry instead so every input counts; `inputRef` is the
215
+ // fallback only when no registered input applies (none registered, or all of them disabled).
216
+ const element = findRepresentativeInput(registeredInputs) ?? inputRef.current;
150
217
  if (!element) {
151
218
  return;
152
219
  }
220
+ validationCommitIdRef.current += 1;
221
+ const validationCommitId = validationCommitIdRef.current;
222
+ function updateRegisteredFieldValidity(nextValidityData, externalInvalid = invalid) {
223
+ const fieldId = getRegisteredFieldId() ?? controlId;
224
+ if (fieldId == null) {
225
+ return;
226
+ }
227
+ const currentFieldData = formRef.current.fields.get(fieldId);
228
+ if (!currentFieldData) {
229
+ return;
230
+ }
231
+ const validityDataWithFormErrors = getCombinedFieldValidityData(nextValidityData, externalInvalid);
232
+ formRef.current.fields.set(fieldId, {
233
+ ...currentFieldData,
234
+ validityData: validityDataWithFormErrors
235
+ });
236
+ }
153
237
  if (revalidate) {
154
238
  if (state.valid !== false) {
155
239
  return;
@@ -169,16 +253,10 @@ function useFieldValidation(params) {
169
253
  errors: [],
170
254
  initialValue: validityData.initialValue
171
255
  };
172
- element.setCustomValidity('');
173
- if (controlId) {
174
- const currentFieldData = formRef.current.fields.get(controlId);
175
- if (currentFieldData) {
176
- formRef.current.fields.set(controlId, {
177
- ...currentFieldData,
178
- ...useLabelableId.getCombinedFieldValidityData(nextValidityData, false) // invalid = false
179
- });
180
- }
181
- }
256
+ clearCustomValidity(element, registeredInputs);
257
+
258
+ // The required value is now present; ignore stale external invalid state for this pass.
259
+ updateRegisteredFieldValidity(nextValidityData, false);
182
260
  setValidityData(nextValidityData);
183
261
  return;
184
262
  }
@@ -229,8 +307,8 @@ function useFieldValidation(params) {
229
307
  let validationErrors = [];
230
308
  const nextState = getState(element);
231
309
  let defaultValidationMessage;
232
- const validateOnChange = shouldValidateOnChange();
233
- if (element.validationMessage && !validateOnChange) {
310
+ const isValidatingOnChange = shouldValidateOnChange();
311
+ if (element.validationMessage && !isValidatingOnChange) {
234
312
  // not validating on change, if there is a `validationMessage` from
235
313
  // native validity, set errors and skip calling the custom validate fn
236
314
  defaultValidationMessage = element.validationMessage;
@@ -248,6 +326,9 @@ function useFieldValidation(params) {
248
326
  const resultOrPromise = validate(value, formValues);
249
327
  if (typeof resultOrPromise === 'object' && resultOrPromise !== null && 'then' in resultOrPromise) {
250
328
  result = await resultOrPromise;
329
+ if (validationCommitId !== validationCommitIdRef.current) {
330
+ return;
331
+ }
251
332
  } else {
252
333
  result = resultOrPromise;
253
334
  }
@@ -261,10 +342,10 @@ function useFieldValidation(params) {
261
342
  validationErrors = [result];
262
343
  element.setCustomValidity(result);
263
344
  }
264
- } else if (validateOnChange) {
345
+ } else if (isValidatingOnChange) {
265
346
  // validate function returned no errors, if validating on change
266
347
  // we need to clear the custom validity state
267
- element.setCustomValidity('');
348
+ clearCustomValidity(element, registeredInputs);
268
349
  nextState.customError = false;
269
350
  if (element.validationMessage) {
270
351
  defaultValidationMessage = element.validationMessage;
@@ -281,57 +362,153 @@ function useFieldValidation(params) {
281
362
  errors: validationErrors,
282
363
  initialValue: validityData.initialValue
283
364
  };
284
- if (controlId) {
285
- const currentFieldData = formRef.current.fields.get(controlId);
286
- if (currentFieldData) {
287
- formRef.current.fields.set(controlId, {
288
- ...currentFieldData,
289
- // Keep Form-level errors part of overall field validity for submit blocking/focus logic.
290
- ...useLabelableId.getCombinedFieldValidityData(nextValidityData, invalid)
291
- });
292
- }
293
- }
365
+
366
+ // Keep Form-level errors part of overall field validity for submit blocking/focus logic.
367
+ updateRegisteredFieldValidity(nextValidityData);
294
368
  setValidityData(nextValidityData);
295
369
  });
296
- const getValidationProps = React__namespace.useCallback((externalProps = {}) => useRenderElement.mergeProps(getDescriptionProps, state.valid === false ? {
370
+ const change = useButton.useStableCallback(value => {
371
+ timeout.clear();
372
+ const validateOnChange = shouldValidateOnChange();
373
+ if (validateOnChange && value !== '' && validationDebounceTime) {
374
+ validationCommitIdRef.current += 1;
375
+ timeout.start(validationDebounceTime, () => {
376
+ commit(value);
377
+ });
378
+ } else {
379
+ commit(value, !validateOnChange);
380
+ }
381
+ });
382
+ const getValidationProps = React__namespace.useCallback((disabled, externalProps = {}) => useRenderElement.mergeProps(getDescriptionProps(externalProps), state.valid === false && !state.disabled && !disabled ? {
297
383
  'aria-invalid': true
298
- } : useRenderElement.EMPTY_OBJECT, externalProps), [getDescriptionProps, state.valid]);
299
- const getInputValidationProps = React__namespace.useCallback((externalProps = {}) => useRenderElement.mergeProps({
300
- onChange(event) {
301
- // Workaround for https://github.com/facebook/react/issues/9023
302
- if (event.nativeEvent.defaultPrevented) {
303
- return;
304
- }
305
- clearErrors(name);
306
- if (!shouldValidateOnChange()) {
307
- commit(event.currentTarget.value, true);
308
- return;
309
- }
384
+ } : useRenderElement.EMPTY_OBJECT), [getDescriptionProps, state.disabled, state.valid]);
385
+ return React__namespace.useMemo(() => ({
386
+ getValidationProps,
387
+ inputRef,
388
+ registerInput,
389
+ commit,
390
+ change
391
+ }), [getValidationProps, registerInput, commit, change]);
392
+ }
310
393
 
311
- // When validating on change, run client-side validation even if
312
- // externally invalid
313
- const element = event.currentTarget;
314
- if (element.value === '') {
315
- // Ignore the debounce time for empty values.
316
- commit(element.value);
317
- return;
394
+ function useFieldControlRegistration(params) {
395
+ const {
396
+ commit,
397
+ invalid,
398
+ markedDirtyRef,
399
+ name,
400
+ setRegisteredFieldName,
401
+ setRegisteredFieldId,
402
+ setValidityData,
403
+ validityData
404
+ } = params;
405
+ const {
406
+ formRef
407
+ } = useLabelableId.useFormContext();
408
+ const activeFieldControlSourceRef = React__namespace.useRef(null);
409
+ const registrationRef = React__namespace.useRef(null);
410
+ const fallbackControlRef = React__namespace.useRef(null);
411
+ const getValueForForm = useButton.useStableCallback(() => {
412
+ const registration = registrationRef.current;
413
+ if (!registration) {
414
+ return undefined;
415
+ }
416
+ if (registration.getValue) {
417
+ return registration.getValue();
418
+ }
419
+ return registration.value;
420
+ });
421
+ function getRegistrationValue(registration) {
422
+ return registration.value === undefined ? getValueForForm() : registration.value;
423
+ }
424
+ const validate = useButton.useStableCallback(() => {
425
+ const registration = registrationRef.current;
426
+ markedDirtyRef.current = true;
427
+ if (!registration) {
428
+ commit(validityData.value);
429
+ return;
430
+ }
431
+ commit(getRegistrationValue(registration));
432
+ });
433
+ function refreshRegistration() {
434
+ const registration = registrationRef.current;
435
+ if (!registration || !registration.id) {
436
+ return;
437
+ }
438
+ formRef.current.fields.set(registration.id, {
439
+ getValue: getValueForForm,
440
+ name: name ?? registration.name,
441
+ controlRef: registration.controlRef ?? fallbackControlRef,
442
+ validityData: getCombinedFieldValidityData(validityData, invalid),
443
+ validate
444
+ });
445
+ }
446
+ function deleteRegistration(id = registrationRef.current?.id) {
447
+ if (id) {
448
+ formRef.current.fields.delete(id);
449
+ }
450
+ }
451
+ function syncInitialValue() {
452
+ const registration = registrationRef.current;
453
+ if (!registration) {
454
+ return;
455
+ }
456
+ const initialValue = getRegistrationValue(registration);
457
+ if (validityData.initialValue === null && initialValue !== null) {
458
+ setValidityData(prev => ({
459
+ ...prev,
460
+ initialValue
461
+ }));
462
+ }
463
+ }
464
+ useButton.useIsoLayoutEffect(() => {
465
+ const registration = registrationRef.current;
466
+ if (!registration || !registration.id) {
467
+ return;
468
+ }
469
+ setRegisteredFieldName(name ? undefined : registration.name);
470
+ formRef.current.fields.set(registration.id, {
471
+ getValue: getValueForForm,
472
+ name: name ?? registration.name,
473
+ controlRef: registration.controlRef ?? fallbackControlRef,
474
+ validityData: getCombinedFieldValidityData(validityData, invalid),
475
+ validate
476
+ });
477
+ }, [formRef, getValueForForm, invalid, name, setRegisteredFieldName, validate, validityData]);
478
+ useButton.useIsoLayoutEffect(() => {
479
+ const fields = formRef.current.fields;
480
+ return () => {
481
+ const id = registrationRef.current?.id;
482
+ if (id) {
483
+ fields.delete(id);
318
484
  }
319
- timeout.clear();
320
- if (validationDebounceTime) {
321
- timeout.start(validationDebounceTime, () => {
322
- commit(element.value);
323
- });
324
- } else {
325
- commit(element.value);
485
+ };
486
+ }, [formRef]);
487
+ const register = useButton.useStableCallback((source, registration) => {
488
+ if (!registration) {
489
+ if (activeFieldControlSourceRef.current === source) {
490
+ activeFieldControlSourceRef.current = null;
491
+ deleteRegistration();
492
+ registrationRef.current = null;
493
+ setRegisteredFieldName(undefined);
494
+ setRegisteredFieldId(undefined);
326
495
  }
496
+ return;
327
497
  }
328
- }, getValidationProps(externalProps)), [getValidationProps, clearErrors, name, timeout, commit, validationDebounceTime, shouldValidateOnChange]);
329
- return React__namespace.useMemo(() => ({
330
- getValidationProps,
331
- getInputValidationProps,
332
- inputRef,
333
- commit
334
- }), [getValidationProps, getInputValidationProps, commit]);
498
+ const previousId = registrationRef.current?.id;
499
+ activeFieldControlSourceRef.current = source;
500
+ registrationRef.current = registration;
501
+ if (!name) {
502
+ setRegisteredFieldName(registration.name);
503
+ }
504
+ setRegisteredFieldId(registration.id);
505
+ if (previousId && previousId !== registration.id) {
506
+ deleteRegistration(previousId);
507
+ }
508
+ syncInitialValue();
509
+ refreshRegistration();
510
+ });
511
+ return [validate, register];
335
512
  }
336
513
 
337
514
  const FieldRootInner = /*#__PURE__*/React__namespace.forwardRef(function FieldRootInner(componentProps, forwardedRef) {
@@ -352,11 +529,10 @@ const FieldRootInner = /*#__PURE__*/React__namespace.forwardRef(function FieldRo
352
529
  dirty: dirtyProp,
353
530
  touched: touchedProp,
354
531
  actionsRef,
532
+ style,
355
533
  ...elementProps
356
534
  } = componentProps;
357
- const {
358
- disabled: disabledFieldset
359
- } = useFieldsetRootContext();
535
+ const disabledFieldset = useFieldsetRootContext(true)?.disabled;
360
536
  const validate = useButton.useStableCallback(validateProp || (() => null));
361
537
  const disabled = disabledFieldset || disabledProp;
362
538
  const [touchedState, setTouchedUnwrapped] = React__namespace.useState(false);
@@ -365,7 +541,19 @@ const FieldRootInner = /*#__PURE__*/React__namespace.forwardRef(function FieldRo
365
541
  const [focused, setFocused] = React__namespace.useState(false);
366
542
  const dirty = dirtyProp ?? dirtyState;
367
543
  const touched = touchedProp ?? touchedState;
368
- const markedDirtyRef = React__namespace.useRef(false);
544
+ const markedDirtyRef = React__namespace.useRef(dirty);
545
+ const registeredFieldIdRef = React__namespace.useRef(undefined);
546
+ const [registeredFieldName, setRegisteredFieldName] = React__namespace.useState();
547
+ const effectiveName = name ?? registeredFieldName;
548
+ useButton.useIsoLayoutEffect(() => {
549
+ if (dirtyProp !== undefined) {
550
+ markedDirtyRef.current = dirtyProp;
551
+ }
552
+ }, [dirtyProp]);
553
+ const getRegisteredFieldId = React__namespace.useCallback(() => registeredFieldIdRef.current, []);
554
+ const setRegisteredFieldId = React__namespace.useCallback(id => {
555
+ registeredFieldIdRef.current = id;
556
+ }, []);
369
557
  const setDirty = useButton.useStableCallback(value => {
370
558
  if (dirtyProp !== undefined) {
371
559
  return;
@@ -382,7 +570,8 @@ const FieldRootInner = /*#__PURE__*/React__namespace.forwardRef(function FieldRo
382
570
  setTouchedUnwrapped(value);
383
571
  });
384
572
  const shouldValidateOnChange = useButton.useStableCallback(() => validationMode === 'onChange' || validationMode === 'onSubmit' && submitAttemptedRef.current);
385
- const hasFormError = !!name && Object.hasOwn(errors, name) && errors[name] !== undefined;
573
+ const formError = effectiveName && Object.hasOwn(errors, effectiveName) ? errors[effectiveName] : null;
574
+ const hasFormError = !!(Array.isArray(formError) ? formError.length : formError);
386
575
  const invalid = invalidProp === true || hasFormError;
387
576
  const [validityData, setValidityData] = React__namespace.useState({
388
577
  state: useLabelableId.DEFAULT_VALIDITY_STATE,
@@ -391,7 +580,7 @@ const FieldRootInner = /*#__PURE__*/React__namespace.forwardRef(function FieldRo
391
580
  value: null,
392
581
  initialValue: null
393
582
  });
394
- const valid = !invalid && validityData.state.valid;
583
+ const valid = disabled ? null : !invalid && validityData.state.valid;
395
584
  const state = React__namespace.useMemo(() => ({
396
585
  disabled,
397
586
  touched,
@@ -408,19 +597,25 @@ const FieldRootInner = /*#__PURE__*/React__namespace.forwardRef(function FieldRo
408
597
  invalid,
409
598
  markedDirtyRef,
410
599
  state,
600
+ shouldValidateOnChange,
601
+ getRegisteredFieldId
602
+ });
603
+ const [validateFieldControl, registerFieldControl] = useFieldControlRegistration({
604
+ commit: validation.commit,
605
+ invalid,
606
+ markedDirtyRef,
411
607
  name,
412
- shouldValidateOnChange
608
+ setRegisteredFieldName,
609
+ setRegisteredFieldId,
610
+ setValidityData,
611
+ validityData
413
612
  });
414
- const handleImperativeValidate = React__namespace.useCallback(() => {
415
- markedDirtyRef.current = true;
416
- validation.commit(validityData.value);
417
- }, [validation, validityData]);
418
613
  React__namespace.useImperativeHandle(actionsRef, () => ({
419
- validate: handleImperativeValidate
420
- }), [handleImperativeValidate]);
614
+ validate: validateFieldControl
615
+ }), [validateFieldControl]);
421
616
  const contextValue = React__namespace.useMemo(() => ({
422
617
  invalid,
423
- name,
618
+ name: effectiveName,
424
619
  validityData,
425
620
  setValidityData,
426
621
  disabled,
@@ -438,8 +633,9 @@ const FieldRootInner = /*#__PURE__*/React__namespace.forwardRef(function FieldRo
438
633
  shouldValidateOnChange,
439
634
  state,
440
635
  markedDirtyRef,
636
+ registerFieldControl,
441
637
  validation
442
- }), [invalid, name, validityData, disabled, touched, setTouched, dirty, setDirty, filled, setFilled, focused, setFocused, validate, validationMode, validationDebounceTime, shouldValidateOnChange, state, validation]);
638
+ }), [invalid, effectiveName, validityData, disabled, touched, setTouched, dirty, setDirty, filled, setFilled, focused, setFocused, validate, validationMode, validationDebounceTime, shouldValidateOnChange, state, registerFieldControl, validation]);
443
639
  const element = useRenderElement.useRenderElement('div', componentProps, {
444
640
  ref: forwardedRef,
445
641
  state,
@@ -479,14 +675,20 @@ const FieldLabel = /*#__PURE__*/React__namespace.forwardRef(function FieldLabel(
479
675
  const {
480
676
  render,
481
677
  className,
678
+ style,
482
679
  id: idProp,
483
680
  nativeLabel = true,
484
681
  ...elementProps
485
682
  } = componentProps;
486
683
  const fieldRootContext = useLabelableId.useFieldRootContext(false);
684
+ const fieldItemContext = useFieldItemContext();
487
685
  const {
488
686
  labelId
489
687
  } = useLabelableId.useLabelableContext();
688
+ const state = {
689
+ ...fieldRootContext.state,
690
+ disabled: fieldRootContext.disabled || fieldItemContext.disabled
691
+ };
490
692
  const labelRef = React__namespace.useRef(null);
491
693
  const labelProps = useLabel.useLabel({
492
694
  id: labelId ?? idProp,
@@ -514,7 +716,7 @@ const FieldLabel = /*#__PURE__*/React__namespace.forwardRef(function FieldLabel(
514
716
  }
515
717
  const element = useRenderElement.useRenderElement('label', componentProps, {
516
718
  ref: [forwardedRef, labelRef],
517
- state: fieldRootContext.state,
719
+ state,
518
720
  props: [labelProps, elementProps],
519
721
  stateAttributesMapping: useLabelableId.fieldValidityMapping
520
722
  });
@@ -539,6 +741,7 @@ const FieldError = /*#__PURE__*/React__namespace.forwardRef(function FieldError(
539
741
  id: idProp,
540
742
  className,
541
743
  match,
744
+ style,
542
745
  ...elementProps
543
746
  } = componentProps;
544
747
  const id = useButton.useBaseUiId(idProp);
@@ -553,14 +756,18 @@ const FieldError = /*#__PURE__*/React__namespace.forwardRef(function FieldError(
553
756
  const {
554
757
  errors
555
758
  } = useLabelableId.useFormContext();
556
- const formError = name ? errors[name] : null;
759
+ const formError = name && Object.hasOwn(errors, name) ? errors[name] : null;
760
+ const hasFormError = !!(Array.isArray(formError) ? formError.length : formError);
761
+ const hasSpecificMatch = typeof match === 'string';
557
762
  let rendered = false;
558
- if (formError || match === true) {
763
+ if (match === true) {
559
764
  rendered = true;
560
- } else if (match) {
765
+ } else if (fieldState.disabled) {
766
+ rendered = false;
767
+ } else if (hasSpecificMatch) {
561
768
  rendered = Boolean(validityData.state[match]);
562
769
  } else {
563
- rendered = validityData.state.valid === false;
770
+ rendered = hasFormError || validityData.state.valid === false;
564
771
  }
565
772
  const {
566
773
  mounted,
@@ -579,17 +786,21 @@ const FieldError = /*#__PURE__*/React__namespace.forwardRef(function FieldError(
579
786
  const errorRef = React__namespace.useRef(null);
580
787
  const [lastRenderedMessage, setLastRenderedMessage] = React__namespace.useState(null);
581
788
  const [lastRenderedMessageKey, setLastRenderedMessageKey] = React__namespace.useState(null);
582
- const errorMessage = formError || (validityData.errors.length > 1 ? /*#__PURE__*/jsxRuntime.jsx("ul", {
583
- children: validityData.errors.map(message => /*#__PURE__*/jsxRuntime.jsx("li", {
584
- children: message
585
- }, message))
586
- }) : validityData.error);
587
- let errorKey = validityData.error;
588
- if (formError != null) {
589
- errorKey = Array.isArray(formError) ? JSON.stringify(formError) : formError;
789
+ let error = validityData.error;
790
+ if (!hasSpecificMatch && hasFormError) {
791
+ error = formError;
590
792
  } else if (validityData.errors.length > 1) {
591
- errorKey = JSON.stringify(validityData.errors);
793
+ error = validityData.errors;
794
+ }
795
+ let errorMessage = error ?? '';
796
+ if (Array.isArray(error)) {
797
+ errorMessage = error.length > 1 ? /*#__PURE__*/jsxRuntime.jsx("ul", {
798
+ children: error.map(message => /*#__PURE__*/jsxRuntime.jsx("li", {
799
+ children: message
800
+ }, message))
801
+ }) : error[0] ?? '';
592
802
  }
803
+ const errorKey = Array.isArray(error) ? JSON.stringify(error) : error;
593
804
  if (rendered && errorKey !== lastRenderedMessageKey) {
594
805
  setLastRenderedMessageKey(errorKey);
595
806
  setLastRenderedMessage(errorMessage);
@@ -635,13 +846,19 @@ const FieldDescription = /*#__PURE__*/React__namespace.forwardRef(function Field
635
846
  render,
636
847
  id: idProp,
637
848
  className,
849
+ style,
638
850
  ...elementProps
639
851
  } = componentProps;
640
852
  const id = useButton.useBaseUiId(idProp);
641
853
  const fieldRootContext = useLabelableId.useFieldRootContext(false);
854
+ const fieldItemContext = useFieldItemContext();
642
855
  const {
643
856
  setMessageIds
644
857
  } = useLabelableId.useLabelableContext();
858
+ const state = {
859
+ ...fieldRootContext.state,
860
+ disabled: fieldRootContext.disabled || fieldItemContext.disabled
861
+ };
645
862
  useButton.useIsoLayoutEffect(() => {
646
863
  if (!id) {
647
864
  return undefined;
@@ -653,7 +870,7 @@ const FieldDescription = /*#__PURE__*/React__namespace.forwardRef(function Field
653
870
  }, [id, setMessageIds]);
654
871
  const element = useRenderElement.useRenderElement('p', componentProps, {
655
872
  ref: forwardedRef,
656
- state: fieldRootContext.state,
873
+ state,
657
874
  props: [{
658
875
  id
659
876
  }, elementProps],