@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
@@ -6,17 +6,22 @@ import { A as ActivityIndicator } from './ActivityIndicator-es.js';
6
6
  import { B as Button } from './Button-es.js';
7
7
  import { I as Icon } from './Icon-es.js';
8
8
  import { f as formatErrorMessage, a as useRefWithInit, m as mergeProps, E as EMPTY_OBJECT, u as useRenderElement } from './useRenderElement-es.js';
9
- import { u as useBaseUiId, b as useStableCallback, h as useTimeout, S as SafeReact, j as error, t as transitionStatusMapping, k as useTransitionStatus, a as useIsoLayoutEffect, f as useOpenChangeComplete } from './useButton-es.js';
10
- import { u as useLabelableContext, L as LabelableContext, a as useFormContext, D as DEFAULT_VALIDITY_STATE, g as getCombinedFieldValidityData, f as fieldValidityMapping, F as FieldRootContext, b as useFieldRootContext } from './useLabelableId-es.js';
9
+ import { u as useBaseUiId, b as useStableCallback, h as useTimeout, a as useIsoLayoutEffect, S as SafeReact, j as error, t as transitionStatusMapping, k as useTransitionStatus, f as useOpenChangeComplete } from './useButton-es.js';
10
+ import { u as useLabelableContext, L as LabelableContext, a as useFormContext, D as DEFAULT_VALIDITY_STATE, f as fieldValidityMapping, F as FieldRootContext, b as useFieldRootContext } from './useLabelableId-es.js';
11
11
  import { jsx } from 'react/jsx-runtime';
12
12
  import { N as NumberFieldRoot, a as NumberFieldGroup, b as NumberFieldInput, c as NumberFieldIncrement, d as NumberFieldDecrement } from './NumberFieldInput-es.js';
13
13
  import { u as useLabel } from './useLabel-es.js';
14
14
 
15
- const FieldsetRootContext = /*#__PURE__*/React.createContext({
16
- legendId: undefined,
17
- setLegendId: () => {},
18
- disabled: undefined
15
+ const FieldItemContext = /*#__PURE__*/React.createContext({
16
+ disabled: false
19
17
  });
18
+ if (process.env.NODE_ENV !== "production") FieldItemContext.displayName = "FieldItemContext";
19
+ function useFieldItemContext() {
20
+ const context = React.useContext(FieldItemContext);
21
+ return context;
22
+ }
23
+
24
+ const FieldsetRootContext = /*#__PURE__*/React.createContext(undefined);
20
25
  if (process.env.NODE_ENV !== "production") FieldsetRootContext.displayName = "FieldsetRootContext";
21
26
  function useFieldsetRootContext(optional = false) {
22
27
  const context = React.useContext(FieldsetRootContext);
@@ -64,9 +69,12 @@ const LabelableProvider = function LabelableProvider(props) {
64
69
  });
65
70
  });
66
71
  const getDescriptionProps = React.useCallback(externalProps => {
67
- return mergeProps({
68
- 'aria-describedby': parentMessageIds.concat(messageIds).join(' ') || undefined
69
- }, externalProps);
72
+ const ids = externalProps['aria-describedby'] ? externalProps['aria-describedby'].split(' ') : [];
73
+ ids.push(...parentMessageIds, ...messageIds);
74
+ return {
75
+ ...externalProps,
76
+ 'aria-describedby': Array.from(new Set(ids)).join(' ') || undefined
77
+ };
70
78
  }, [parentMessageIds, messageIds]);
71
79
  const contextValue = React.useMemo(() => ({
72
80
  controlId,
@@ -84,6 +92,20 @@ const LabelableProvider = function LabelableProvider(props) {
84
92
  };
85
93
  if (process.env.NODE_ENV !== "production") LabelableProvider.displayName = "LabelableProvider";
86
94
 
95
+ /**
96
+ * Combines the field's client-side, stateful validity data with the external invalid state to
97
+ * determine the field's true validity.
98
+ */
99
+ function getCombinedFieldValidityData(validityData, invalid) {
100
+ return {
101
+ ...validityData,
102
+ state: {
103
+ ...validityData.state,
104
+ valid: !invalid && validityData.state.valid
105
+ }
106
+ };
107
+ }
108
+
87
109
  const validityKeys = Object.keys(DEFAULT_VALIDITY_STATE);
88
110
  function isOnlyValueMissing(state) {
89
111
  if (!state || state.valid || !state.valueMissing) {
@@ -96,17 +118,45 @@ function isOnlyValueMissing(state) {
96
118
  }
97
119
  if (key === 'valueMissing') {
98
120
  onlyValueMissing = state[key];
99
- }
100
- if (state[key]) {
121
+ } else if (state[key]) {
101
122
  onlyValueMissing = false;
102
123
  }
103
124
  }
104
125
  return onlyValueMissing;
105
126
  }
127
+
128
+ /**
129
+ * Picks the input whose native validity should represent a field that owns several inputs (such as a
130
+ * checkbox group). Prefers the first enabled currently-invalid input, where "first" follows Set
131
+ * insertion order (mount order), and otherwise returns the first enabled input. Disabled inputs are
132
+ * skipped because they don't participate in native constraint validation.
133
+ */
134
+ function findRepresentativeInput(inputs) {
135
+ let fallback = null;
136
+ for (const input of inputs) {
137
+ if (input.disabled) {
138
+ continue;
139
+ }
140
+ if (!input.validity.valid) {
141
+ return input;
142
+ }
143
+ fallback ??= input;
144
+ }
145
+ return fallback;
146
+ }
147
+ function clearCustomValidity(element, inputs) {
148
+ let didClearElement = false;
149
+ for (const input of inputs) {
150
+ input.setCustomValidity('');
151
+ didClearElement ||= input === element;
152
+ }
153
+ if (!didClearElement) {
154
+ element.setCustomValidity('');
155
+ }
156
+ }
106
157
  function useFieldValidation(params) {
107
158
  const {
108
- formRef,
109
- clearErrors
159
+ formRef
110
160
  } = useFormContext();
111
161
  const {
112
162
  setValidityData,
@@ -116,8 +166,8 @@ function useFieldValidation(params) {
116
166
  invalid,
117
167
  markedDirtyRef,
118
168
  state,
119
- name,
120
- shouldValidateOnChange
169
+ shouldValidateOnChange,
170
+ getRegisteredFieldId
121
171
  } = params;
122
172
  const {
123
173
  controlId,
@@ -125,11 +175,45 @@ function useFieldValidation(params) {
125
175
  } = useLabelableContext();
126
176
  const timeout = useTimeout();
127
177
  const inputRef = React.useRef(null);
178
+ const registeredInputs = useRefWithInit(() => new Set()).current;
179
+ const validationCommitIdRef = React.useRef(0);
180
+
181
+ // Checkbox groups register several inputs against a single field. Track them so a `required`
182
+ // checkbox can't be satisfied by another input in the group, matching native per-checkbox behavior.
183
+ const registerInput = React.useCallback(element => {
184
+ if (!element) {
185
+ return undefined;
186
+ }
187
+ registeredInputs.add(element);
188
+ return () => {
189
+ registeredInputs.delete(element);
190
+ };
191
+ }, [registeredInputs]);
128
192
  const commit = useStableCallback(async (value, revalidate = false) => {
129
- const element = inputRef.current;
193
+ // A field can own several inputs (a checkbox group), but only the last-mounted one wins the shared
194
+ // `inputRef`. Validate against the registry instead so every input counts; `inputRef` is the
195
+ // fallback only when no registered input applies (none registered, or all of them disabled).
196
+ const element = findRepresentativeInput(registeredInputs) ?? inputRef.current;
130
197
  if (!element) {
131
198
  return;
132
199
  }
200
+ validationCommitIdRef.current += 1;
201
+ const validationCommitId = validationCommitIdRef.current;
202
+ function updateRegisteredFieldValidity(nextValidityData, externalInvalid = invalid) {
203
+ const fieldId = getRegisteredFieldId() ?? controlId;
204
+ if (fieldId == null) {
205
+ return;
206
+ }
207
+ const currentFieldData = formRef.current.fields.get(fieldId);
208
+ if (!currentFieldData) {
209
+ return;
210
+ }
211
+ const validityDataWithFormErrors = getCombinedFieldValidityData(nextValidityData, externalInvalid);
212
+ formRef.current.fields.set(fieldId, {
213
+ ...currentFieldData,
214
+ validityData: validityDataWithFormErrors
215
+ });
216
+ }
133
217
  if (revalidate) {
134
218
  if (state.valid !== false) {
135
219
  return;
@@ -149,16 +233,10 @@ function useFieldValidation(params) {
149
233
  errors: [],
150
234
  initialValue: validityData.initialValue
151
235
  };
152
- element.setCustomValidity('');
153
- if (controlId) {
154
- const currentFieldData = formRef.current.fields.get(controlId);
155
- if (currentFieldData) {
156
- formRef.current.fields.set(controlId, {
157
- ...currentFieldData,
158
- ...getCombinedFieldValidityData(nextValidityData, false) // invalid = false
159
- });
160
- }
161
- }
236
+ clearCustomValidity(element, registeredInputs);
237
+
238
+ // The required value is now present; ignore stale external invalid state for this pass.
239
+ updateRegisteredFieldValidity(nextValidityData, false);
162
240
  setValidityData(nextValidityData);
163
241
  return;
164
242
  }
@@ -209,8 +287,8 @@ function useFieldValidation(params) {
209
287
  let validationErrors = [];
210
288
  const nextState = getState(element);
211
289
  let defaultValidationMessage;
212
- const validateOnChange = shouldValidateOnChange();
213
- if (element.validationMessage && !validateOnChange) {
290
+ const isValidatingOnChange = shouldValidateOnChange();
291
+ if (element.validationMessage && !isValidatingOnChange) {
214
292
  // not validating on change, if there is a `validationMessage` from
215
293
  // native validity, set errors and skip calling the custom validate fn
216
294
  defaultValidationMessage = element.validationMessage;
@@ -228,6 +306,9 @@ function useFieldValidation(params) {
228
306
  const resultOrPromise = validate(value, formValues);
229
307
  if (typeof resultOrPromise === 'object' && resultOrPromise !== null && 'then' in resultOrPromise) {
230
308
  result = await resultOrPromise;
309
+ if (validationCommitId !== validationCommitIdRef.current) {
310
+ return;
311
+ }
231
312
  } else {
232
313
  result = resultOrPromise;
233
314
  }
@@ -241,10 +322,10 @@ function useFieldValidation(params) {
241
322
  validationErrors = [result];
242
323
  element.setCustomValidity(result);
243
324
  }
244
- } else if (validateOnChange) {
325
+ } else if (isValidatingOnChange) {
245
326
  // validate function returned no errors, if validating on change
246
327
  // we need to clear the custom validity state
247
- element.setCustomValidity('');
328
+ clearCustomValidity(element, registeredInputs);
248
329
  nextState.customError = false;
249
330
  if (element.validationMessage) {
250
331
  defaultValidationMessage = element.validationMessage;
@@ -261,57 +342,153 @@ function useFieldValidation(params) {
261
342
  errors: validationErrors,
262
343
  initialValue: validityData.initialValue
263
344
  };
264
- if (controlId) {
265
- const currentFieldData = formRef.current.fields.get(controlId);
266
- if (currentFieldData) {
267
- formRef.current.fields.set(controlId, {
268
- ...currentFieldData,
269
- // Keep Form-level errors part of overall field validity for submit blocking/focus logic.
270
- ...getCombinedFieldValidityData(nextValidityData, invalid)
271
- });
272
- }
273
- }
345
+
346
+ // Keep Form-level errors part of overall field validity for submit blocking/focus logic.
347
+ updateRegisteredFieldValidity(nextValidityData);
274
348
  setValidityData(nextValidityData);
275
349
  });
276
- const getValidationProps = React.useCallback((externalProps = {}) => mergeProps(getDescriptionProps, state.valid === false ? {
350
+ const change = useStableCallback(value => {
351
+ timeout.clear();
352
+ const validateOnChange = shouldValidateOnChange();
353
+ if (validateOnChange && value !== '' && validationDebounceTime) {
354
+ validationCommitIdRef.current += 1;
355
+ timeout.start(validationDebounceTime, () => {
356
+ commit(value);
357
+ });
358
+ } else {
359
+ commit(value, !validateOnChange);
360
+ }
361
+ });
362
+ const getValidationProps = React.useCallback((disabled, externalProps = {}) => mergeProps(getDescriptionProps(externalProps), state.valid === false && !state.disabled && !disabled ? {
277
363
  'aria-invalid': true
278
- } : EMPTY_OBJECT, externalProps), [getDescriptionProps, state.valid]);
279
- const getInputValidationProps = React.useCallback((externalProps = {}) => mergeProps({
280
- onChange(event) {
281
- // Workaround for https://github.com/facebook/react/issues/9023
282
- if (event.nativeEvent.defaultPrevented) {
283
- return;
284
- }
285
- clearErrors(name);
286
- if (!shouldValidateOnChange()) {
287
- commit(event.currentTarget.value, true);
288
- return;
289
- }
364
+ } : EMPTY_OBJECT), [getDescriptionProps, state.disabled, state.valid]);
365
+ return React.useMemo(() => ({
366
+ getValidationProps,
367
+ inputRef,
368
+ registerInput,
369
+ commit,
370
+ change
371
+ }), [getValidationProps, registerInput, commit, change]);
372
+ }
290
373
 
291
- // When validating on change, run client-side validation even if
292
- // externally invalid
293
- const element = event.currentTarget;
294
- if (element.value === '') {
295
- // Ignore the debounce time for empty values.
296
- commit(element.value);
297
- return;
374
+ function useFieldControlRegistration(params) {
375
+ const {
376
+ commit,
377
+ invalid,
378
+ markedDirtyRef,
379
+ name,
380
+ setRegisteredFieldName,
381
+ setRegisteredFieldId,
382
+ setValidityData,
383
+ validityData
384
+ } = params;
385
+ const {
386
+ formRef
387
+ } = useFormContext();
388
+ const activeFieldControlSourceRef = React.useRef(null);
389
+ const registrationRef = React.useRef(null);
390
+ const fallbackControlRef = React.useRef(null);
391
+ const getValueForForm = useStableCallback(() => {
392
+ const registration = registrationRef.current;
393
+ if (!registration) {
394
+ return undefined;
395
+ }
396
+ if (registration.getValue) {
397
+ return registration.getValue();
398
+ }
399
+ return registration.value;
400
+ });
401
+ function getRegistrationValue(registration) {
402
+ return registration.value === undefined ? getValueForForm() : registration.value;
403
+ }
404
+ const validate = useStableCallback(() => {
405
+ const registration = registrationRef.current;
406
+ markedDirtyRef.current = true;
407
+ if (!registration) {
408
+ commit(validityData.value);
409
+ return;
410
+ }
411
+ commit(getRegistrationValue(registration));
412
+ });
413
+ function refreshRegistration() {
414
+ const registration = registrationRef.current;
415
+ if (!registration || !registration.id) {
416
+ return;
417
+ }
418
+ formRef.current.fields.set(registration.id, {
419
+ getValue: getValueForForm,
420
+ name: name ?? registration.name,
421
+ controlRef: registration.controlRef ?? fallbackControlRef,
422
+ validityData: getCombinedFieldValidityData(validityData, invalid),
423
+ validate
424
+ });
425
+ }
426
+ function deleteRegistration(id = registrationRef.current?.id) {
427
+ if (id) {
428
+ formRef.current.fields.delete(id);
429
+ }
430
+ }
431
+ function syncInitialValue() {
432
+ const registration = registrationRef.current;
433
+ if (!registration) {
434
+ return;
435
+ }
436
+ const initialValue = getRegistrationValue(registration);
437
+ if (validityData.initialValue === null && initialValue !== null) {
438
+ setValidityData(prev => ({
439
+ ...prev,
440
+ initialValue
441
+ }));
442
+ }
443
+ }
444
+ useIsoLayoutEffect(() => {
445
+ const registration = registrationRef.current;
446
+ if (!registration || !registration.id) {
447
+ return;
448
+ }
449
+ setRegisteredFieldName(name ? undefined : registration.name);
450
+ formRef.current.fields.set(registration.id, {
451
+ getValue: getValueForForm,
452
+ name: name ?? registration.name,
453
+ controlRef: registration.controlRef ?? fallbackControlRef,
454
+ validityData: getCombinedFieldValidityData(validityData, invalid),
455
+ validate
456
+ });
457
+ }, [formRef, getValueForForm, invalid, name, setRegisteredFieldName, validate, validityData]);
458
+ useIsoLayoutEffect(() => {
459
+ const fields = formRef.current.fields;
460
+ return () => {
461
+ const id = registrationRef.current?.id;
462
+ if (id) {
463
+ fields.delete(id);
298
464
  }
299
- timeout.clear();
300
- if (validationDebounceTime) {
301
- timeout.start(validationDebounceTime, () => {
302
- commit(element.value);
303
- });
304
- } else {
305
- commit(element.value);
465
+ };
466
+ }, [formRef]);
467
+ const register = useStableCallback((source, registration) => {
468
+ if (!registration) {
469
+ if (activeFieldControlSourceRef.current === source) {
470
+ activeFieldControlSourceRef.current = null;
471
+ deleteRegistration();
472
+ registrationRef.current = null;
473
+ setRegisteredFieldName(undefined);
474
+ setRegisteredFieldId(undefined);
306
475
  }
476
+ return;
307
477
  }
308
- }, getValidationProps(externalProps)), [getValidationProps, clearErrors, name, timeout, commit, validationDebounceTime, shouldValidateOnChange]);
309
- return React.useMemo(() => ({
310
- getValidationProps,
311
- getInputValidationProps,
312
- inputRef,
313
- commit
314
- }), [getValidationProps, getInputValidationProps, commit]);
478
+ const previousId = registrationRef.current?.id;
479
+ activeFieldControlSourceRef.current = source;
480
+ registrationRef.current = registration;
481
+ if (!name) {
482
+ setRegisteredFieldName(registration.name);
483
+ }
484
+ setRegisteredFieldId(registration.id);
485
+ if (previousId && previousId !== registration.id) {
486
+ deleteRegistration(previousId);
487
+ }
488
+ syncInitialValue();
489
+ refreshRegistration();
490
+ });
491
+ return [validate, register];
315
492
  }
316
493
 
317
494
  const FieldRootInner = /*#__PURE__*/React.forwardRef(function FieldRootInner(componentProps, forwardedRef) {
@@ -332,11 +509,10 @@ const FieldRootInner = /*#__PURE__*/React.forwardRef(function FieldRootInner(com
332
509
  dirty: dirtyProp,
333
510
  touched: touchedProp,
334
511
  actionsRef,
512
+ style,
335
513
  ...elementProps
336
514
  } = componentProps;
337
- const {
338
- disabled: disabledFieldset
339
- } = useFieldsetRootContext();
515
+ const disabledFieldset = useFieldsetRootContext(true)?.disabled;
340
516
  const validate = useStableCallback(validateProp || (() => null));
341
517
  const disabled = disabledFieldset || disabledProp;
342
518
  const [touchedState, setTouchedUnwrapped] = React.useState(false);
@@ -345,7 +521,19 @@ const FieldRootInner = /*#__PURE__*/React.forwardRef(function FieldRootInner(com
345
521
  const [focused, setFocused] = React.useState(false);
346
522
  const dirty = dirtyProp ?? dirtyState;
347
523
  const touched = touchedProp ?? touchedState;
348
- const markedDirtyRef = React.useRef(false);
524
+ const markedDirtyRef = React.useRef(dirty);
525
+ const registeredFieldIdRef = React.useRef(undefined);
526
+ const [registeredFieldName, setRegisteredFieldName] = React.useState();
527
+ const effectiveName = name ?? registeredFieldName;
528
+ useIsoLayoutEffect(() => {
529
+ if (dirtyProp !== undefined) {
530
+ markedDirtyRef.current = dirtyProp;
531
+ }
532
+ }, [dirtyProp]);
533
+ const getRegisteredFieldId = React.useCallback(() => registeredFieldIdRef.current, []);
534
+ const setRegisteredFieldId = React.useCallback(id => {
535
+ registeredFieldIdRef.current = id;
536
+ }, []);
349
537
  const setDirty = useStableCallback(value => {
350
538
  if (dirtyProp !== undefined) {
351
539
  return;
@@ -362,7 +550,8 @@ const FieldRootInner = /*#__PURE__*/React.forwardRef(function FieldRootInner(com
362
550
  setTouchedUnwrapped(value);
363
551
  });
364
552
  const shouldValidateOnChange = useStableCallback(() => validationMode === 'onChange' || validationMode === 'onSubmit' && submitAttemptedRef.current);
365
- const hasFormError = !!name && Object.hasOwn(errors, name) && errors[name] !== undefined;
553
+ const formError = effectiveName && Object.hasOwn(errors, effectiveName) ? errors[effectiveName] : null;
554
+ const hasFormError = !!(Array.isArray(formError) ? formError.length : formError);
366
555
  const invalid = invalidProp === true || hasFormError;
367
556
  const [validityData, setValidityData] = React.useState({
368
557
  state: DEFAULT_VALIDITY_STATE,
@@ -371,7 +560,7 @@ const FieldRootInner = /*#__PURE__*/React.forwardRef(function FieldRootInner(com
371
560
  value: null,
372
561
  initialValue: null
373
562
  });
374
- const valid = !invalid && validityData.state.valid;
563
+ const valid = disabled ? null : !invalid && validityData.state.valid;
375
564
  const state = React.useMemo(() => ({
376
565
  disabled,
377
566
  touched,
@@ -388,19 +577,25 @@ const FieldRootInner = /*#__PURE__*/React.forwardRef(function FieldRootInner(com
388
577
  invalid,
389
578
  markedDirtyRef,
390
579
  state,
580
+ shouldValidateOnChange,
581
+ getRegisteredFieldId
582
+ });
583
+ const [validateFieldControl, registerFieldControl] = useFieldControlRegistration({
584
+ commit: validation.commit,
585
+ invalid,
586
+ markedDirtyRef,
391
587
  name,
392
- shouldValidateOnChange
588
+ setRegisteredFieldName,
589
+ setRegisteredFieldId,
590
+ setValidityData,
591
+ validityData
393
592
  });
394
- const handleImperativeValidate = React.useCallback(() => {
395
- markedDirtyRef.current = true;
396
- validation.commit(validityData.value);
397
- }, [validation, validityData]);
398
593
  React.useImperativeHandle(actionsRef, () => ({
399
- validate: handleImperativeValidate
400
- }), [handleImperativeValidate]);
594
+ validate: validateFieldControl
595
+ }), [validateFieldControl]);
401
596
  const contextValue = React.useMemo(() => ({
402
597
  invalid,
403
- name,
598
+ name: effectiveName,
404
599
  validityData,
405
600
  setValidityData,
406
601
  disabled,
@@ -418,8 +613,9 @@ const FieldRootInner = /*#__PURE__*/React.forwardRef(function FieldRootInner(com
418
613
  shouldValidateOnChange,
419
614
  state,
420
615
  markedDirtyRef,
616
+ registerFieldControl,
421
617
  validation
422
- }), [invalid, name, validityData, disabled, touched, setTouched, dirty, setDirty, filled, setFilled, focused, setFocused, validate, validationMode, validationDebounceTime, shouldValidateOnChange, state, validation]);
618
+ }), [invalid, effectiveName, validityData, disabled, touched, setTouched, dirty, setDirty, filled, setFilled, focused, setFocused, validate, validationMode, validationDebounceTime, shouldValidateOnChange, state, registerFieldControl, validation]);
423
619
  const element = useRenderElement('div', componentProps, {
424
620
  ref: forwardedRef,
425
621
  state,
@@ -459,14 +655,20 @@ const FieldLabel = /*#__PURE__*/React.forwardRef(function FieldLabel(componentPr
459
655
  const {
460
656
  render,
461
657
  className,
658
+ style,
462
659
  id: idProp,
463
660
  nativeLabel = true,
464
661
  ...elementProps
465
662
  } = componentProps;
466
663
  const fieldRootContext = useFieldRootContext(false);
664
+ const fieldItemContext = useFieldItemContext();
467
665
  const {
468
666
  labelId
469
667
  } = useLabelableContext();
668
+ const state = {
669
+ ...fieldRootContext.state,
670
+ disabled: fieldRootContext.disabled || fieldItemContext.disabled
671
+ };
470
672
  const labelRef = React.useRef(null);
471
673
  const labelProps = useLabel({
472
674
  id: labelId ?? idProp,
@@ -494,7 +696,7 @@ const FieldLabel = /*#__PURE__*/React.forwardRef(function FieldLabel(componentPr
494
696
  }
495
697
  const element = useRenderElement('label', componentProps, {
496
698
  ref: [forwardedRef, labelRef],
497
- state: fieldRootContext.state,
699
+ state,
498
700
  props: [labelProps, elementProps],
499
701
  stateAttributesMapping: fieldValidityMapping
500
702
  });
@@ -519,6 +721,7 @@ const FieldError = /*#__PURE__*/React.forwardRef(function FieldError(componentPr
519
721
  id: idProp,
520
722
  className,
521
723
  match,
724
+ style,
522
725
  ...elementProps
523
726
  } = componentProps;
524
727
  const id = useBaseUiId(idProp);
@@ -533,14 +736,18 @@ const FieldError = /*#__PURE__*/React.forwardRef(function FieldError(componentPr
533
736
  const {
534
737
  errors
535
738
  } = useFormContext();
536
- const formError = name ? errors[name] : null;
739
+ const formError = name && Object.hasOwn(errors, name) ? errors[name] : null;
740
+ const hasFormError = !!(Array.isArray(formError) ? formError.length : formError);
741
+ const hasSpecificMatch = typeof match === 'string';
537
742
  let rendered = false;
538
- if (formError || match === true) {
743
+ if (match === true) {
539
744
  rendered = true;
540
- } else if (match) {
745
+ } else if (fieldState.disabled) {
746
+ rendered = false;
747
+ } else if (hasSpecificMatch) {
541
748
  rendered = Boolean(validityData.state[match]);
542
749
  } else {
543
- rendered = validityData.state.valid === false;
750
+ rendered = hasFormError || validityData.state.valid === false;
544
751
  }
545
752
  const {
546
753
  mounted,
@@ -559,17 +766,21 @@ const FieldError = /*#__PURE__*/React.forwardRef(function FieldError(componentPr
559
766
  const errorRef = React.useRef(null);
560
767
  const [lastRenderedMessage, setLastRenderedMessage] = React.useState(null);
561
768
  const [lastRenderedMessageKey, setLastRenderedMessageKey] = React.useState(null);
562
- const errorMessage = formError || (validityData.errors.length > 1 ? /*#__PURE__*/jsx("ul", {
563
- children: validityData.errors.map(message => /*#__PURE__*/jsx("li", {
564
- children: message
565
- }, message))
566
- }) : validityData.error);
567
- let errorKey = validityData.error;
568
- if (formError != null) {
569
- errorKey = Array.isArray(formError) ? JSON.stringify(formError) : formError;
769
+ let error = validityData.error;
770
+ if (!hasSpecificMatch && hasFormError) {
771
+ error = formError;
570
772
  } else if (validityData.errors.length > 1) {
571
- errorKey = JSON.stringify(validityData.errors);
773
+ error = validityData.errors;
774
+ }
775
+ let errorMessage = error ?? '';
776
+ if (Array.isArray(error)) {
777
+ errorMessage = error.length > 1 ? /*#__PURE__*/jsx("ul", {
778
+ children: error.map(message => /*#__PURE__*/jsx("li", {
779
+ children: message
780
+ }, message))
781
+ }) : error[0] ?? '';
572
782
  }
783
+ const errorKey = Array.isArray(error) ? JSON.stringify(error) : error;
573
784
  if (rendered && errorKey !== lastRenderedMessageKey) {
574
785
  setLastRenderedMessageKey(errorKey);
575
786
  setLastRenderedMessage(errorMessage);
@@ -615,13 +826,19 @@ const FieldDescription = /*#__PURE__*/React.forwardRef(function FieldDescription
615
826
  render,
616
827
  id: idProp,
617
828
  className,
829
+ style,
618
830
  ...elementProps
619
831
  } = componentProps;
620
832
  const id = useBaseUiId(idProp);
621
833
  const fieldRootContext = useFieldRootContext(false);
834
+ const fieldItemContext = useFieldItemContext();
622
835
  const {
623
836
  setMessageIds
624
837
  } = useLabelableContext();
838
+ const state = {
839
+ ...fieldRootContext.state,
840
+ disabled: fieldRootContext.disabled || fieldItemContext.disabled
841
+ };
625
842
  useIsoLayoutEffect(() => {
626
843
  if (!id) {
627
844
  return undefined;
@@ -633,7 +850,7 @@ const FieldDescription = /*#__PURE__*/React.forwardRef(function FieldDescription
633
850
  }, [id, setMessageIds]);
634
851
  const element = useRenderElement('p', componentProps, {
635
852
  ref: forwardedRef,
636
- state: fieldRootContext.state,
853
+ state,
637
854
  props: [{
638
855
  id
639
856
  }, elementProps],
@@ -20,7 +20,7 @@ require('../Heading-cjs.js');
20
20
  require('../Tooltip-cjs.js');
21
21
  require('../floating-ui.react-cjs.js');
22
22
  require('../floating-ui.utils.dom-cjs.js');
23
- require('../index.esm-cjs.js');
23
+ require('../floating-ui.utils-cjs.js');
24
24
  require('react/jsx-runtime');
25
25
  require('../floating-ui.react-dom-cjs.js');
26
26
  require('../AtlantisThemeContext-cjs.js');
@@ -18,7 +18,7 @@ import '../Heading-es.js';
18
18
  import '../Tooltip-es.js';
19
19
  import '../floating-ui.react-es.js';
20
20
  import '../floating-ui.utils.dom-es.js';
21
- import '../index.esm-es.js';
21
+ import '../floating-ui.utils-es.js';
22
22
  import 'react/jsx-runtime';
23
23
  import '../floating-ui.react-dom-es.js';
24
24
  import '../AtlantisThemeContext-es.js';