@jobber/components 8.26.3 → 8.27.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 (65) hide show
  1. package/Select.d.ts +1 -0
  2. package/Select.js +17 -0
  3. package/dist/BottomSheet-cjs.js +8 -7
  4. package/dist/BottomSheet-es.js +9 -8
  5. package/dist/ComboboxChipRemove-cjs.js +2 -1
  6. package/dist/ComboboxChipRemove-es.js +4 -3
  7. package/dist/DrawerDescription-cjs.js +4 -97
  8. package/dist/DrawerDescription-es.js +6 -98
  9. package/dist/FieldDescription-cjs.js +880 -0
  10. package/dist/FieldDescription-es.js +856 -0
  11. package/dist/InputNumberExperimental-cjs.js +5 -875
  12. package/dist/InputNumberExperimental-es.js +2 -854
  13. package/dist/MenuSubmenuTrigger-es.js +4 -4
  14. package/dist/Modal/index.cjs +12 -2
  15. package/dist/Modal/index.mjs +13 -3
  16. package/dist/NumberFieldInput-cjs.js +2 -2
  17. package/dist/NumberFieldInput-es.js +1 -1
  18. package/dist/ScrollAreaViewport-cjs.js +2122 -12
  19. package/dist/ScrollAreaViewport-es.js +2118 -13
  20. package/dist/Select/Select.d.ts +31 -0
  21. package/dist/Select/Select.types.d.ts +145 -0
  22. package/dist/Select/SelectBottomSheet.d.ts +4 -0
  23. package/dist/Select/SelectDropdown.d.ts +4 -0
  24. package/dist/Select/index.cjs +41 -0
  25. package/dist/Select/index.d.ts +2 -0
  26. package/dist/Select/index.mjs +35 -0
  27. package/dist/Select-cjs.js +231 -0
  28. package/dist/Select-es.js +229 -0
  29. package/dist/SelectPrimitive-cjs.js +20 -3
  30. package/dist/SelectPrimitive-es.js +20 -3
  31. package/dist/dialogReturnFocus-cjs.js +8 -5
  32. package/dist/dialogReturnFocus-es.js +9 -6
  33. package/dist/docs/Dialog/Dialog.md +74 -21
  34. package/dist/docs/Select/Select.md +428 -0
  35. package/dist/docs/SelectPrimitive/SelectPrimitive.md +14 -1
  36. package/dist/docs/index.md +1 -0
  37. package/dist/floating-ui.react-dom-es.js +1 -1
  38. package/dist/floating-ui.react-es.js +1 -1
  39. package/dist/floating-ui.utils.dom-es.js +1 -1
  40. package/dist/index.cjs +9 -0
  41. package/dist/index.d.mts +1 -0
  42. package/dist/index.d.ts +1 -0
  43. package/dist/index.mjs +8 -0
  44. package/dist/primitives/ComboboxPrimitive/index.cjs +1 -0
  45. package/dist/primitives/ComboboxPrimitive/index.mjs +1 -0
  46. package/dist/primitives/HelperText/HelperText.d.ts +2 -1
  47. package/dist/primitives/InputNumberExperimental/index.cjs +3 -1
  48. package/dist/primitives/InputNumberExperimental/index.mjs +3 -1
  49. package/dist/primitives/SelectPrimitive/SelectPrimitive.d.ts +11 -0
  50. package/dist/primitives/SelectPrimitive/index.d.ts +1 -1
  51. package/dist/primitives/SelectPrimitive/types.d.ts +15 -0
  52. package/dist/primitives/index.cjs +2 -0
  53. package/dist/primitives/index.mjs +2 -0
  54. package/dist/stringifyLocale-cjs.js +13 -0
  55. package/dist/stringifyLocale-es.js +11 -0
  56. package/dist/styles.css +300 -10
  57. package/dist/unstyledPrimitives/index.cjs +219 -2230
  58. package/dist/unstyledPrimitives/index.mjs +225 -2236
  59. package/dist/useButton-es.js +2 -2
  60. package/dist/useCompositeListItem-es.js +1 -1
  61. package/dist/useLabel-cjs.js +0 -11
  62. package/dist/useLabel-es.js +2 -12
  63. package/dist/useScrollLock-es.js +3 -3
  64. package/dist/utils/meta/meta.json +5 -0
  65. package/package.json +7 -2
@@ -0,0 +1,880 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var useButton = require('./useButton-cjs.js');
5
+ var useLabelableId = require('./useLabelableId-cjs.js');
6
+ var useRenderElement = require('./useRenderElement-cjs.js');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
+ var useLabel = require('./useLabel-cjs.js');
9
+
10
+ function _interopNamespaceDefault(e) {
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n.default = e;
24
+ return Object.freeze(n);
25
+ }
26
+
27
+ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
28
+
29
+ const FieldItemContext = /*#__PURE__*/React__namespace.createContext({
30
+ disabled: false
31
+ });
32
+ if (process.env.NODE_ENV !== "production") FieldItemContext.displayName = "FieldItemContext";
33
+ function useFieldItemContext() {
34
+ const context = React__namespace.useContext(FieldItemContext);
35
+ return context;
36
+ }
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 = useButton.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
+ } = useLabelableId.useLabelableContext();
58
+ const registerControlId = useButton.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(useLabelableId.LabelableContext.Provider, {
103
+ value: contextValue,
104
+ children: props.children
105
+ });
106
+ };
107
+ if (process.env.NODE_ENV !== "production") LabelableProvider.displayName = "LabelableProvider";
108
+
109
+ /**
110
+ * Combines the field's client-side, stateful validity data with the external invalid state to
111
+ * determine the field's true validity.
112
+ */
113
+ function getCombinedFieldValidityData(validityData, invalid) {
114
+ return {
115
+ ...validityData,
116
+ state: {
117
+ ...validityData.state,
118
+ valid: !invalid && validityData.state.valid
119
+ }
120
+ };
121
+ }
122
+
123
+ const validityKeys = Object.keys(useLabelableId.DEFAULT_VALIDITY_STATE);
124
+ function isOnlyValueMissing(state) {
125
+ if (!state || state.valid || !state.valueMissing) {
126
+ return false;
127
+ }
128
+ let onlyValueMissing = false;
129
+ for (const key of validityKeys) {
130
+ if (key === 'valid') {
131
+ continue;
132
+ }
133
+ if (key === 'valueMissing') {
134
+ onlyValueMissing = state[key];
135
+ } else if (state[key]) {
136
+ onlyValueMissing = false;
137
+ }
138
+ }
139
+ return onlyValueMissing;
140
+ }
141
+
142
+ /**
143
+ * Picks the input whose native validity should represent a field that owns several inputs (such as a
144
+ * checkbox group). Prefers the first enabled currently-invalid input, where "first" follows Set
145
+ * insertion order (mount order), and otherwise returns the first enabled input. Disabled inputs are
146
+ * skipped because they don't participate in native constraint validation.
147
+ */
148
+ function findRepresentativeInput(inputs) {
149
+ let fallback = null;
150
+ for (const input of inputs) {
151
+ if (input.disabled) {
152
+ continue;
153
+ }
154
+ if (!input.validity.valid) {
155
+ return input;
156
+ }
157
+ fallback ??= input;
158
+ }
159
+ return fallback;
160
+ }
161
+ function clearCustomValidity(element, inputs) {
162
+ let didClearElement = false;
163
+ for (const input of inputs) {
164
+ input.setCustomValidity('');
165
+ didClearElement ||= input === element;
166
+ }
167
+ if (!didClearElement) {
168
+ element.setCustomValidity('');
169
+ }
170
+ }
171
+ function useFieldValidation(params) {
172
+ const {
173
+ formRef
174
+ } = useLabelableId.useFormContext();
175
+ const {
176
+ setValidityData,
177
+ validate,
178
+ validityData,
179
+ validationDebounceTime,
180
+ invalid,
181
+ markedDirtyRef,
182
+ state,
183
+ shouldValidateOnChange,
184
+ getRegisteredFieldId
185
+ } = params;
186
+ const {
187
+ controlId,
188
+ getDescriptionProps
189
+ } = useLabelableId.useLabelableContext();
190
+ const timeout = useButton.useTimeout();
191
+ const inputRef = React__namespace.useRef(null);
192
+ const registeredInputs = useRenderElement.useRefWithInit(() => new Set()).current;
193
+ const validationCommitIdRef = React__namespace.useRef(0);
194
+
195
+ // Checkbox groups register several inputs against a single field. Track them so a `required`
196
+ // checkbox can't be satisfied by another input in the group, matching native per-checkbox behavior.
197
+ const registerInput = React__namespace.useCallback(element => {
198
+ if (!element) {
199
+ return undefined;
200
+ }
201
+ registeredInputs.add(element);
202
+ return () => {
203
+ registeredInputs.delete(element);
204
+ };
205
+ }, [registeredInputs]);
206
+ const commit = useButton.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
+ validationCommitIdRef.current += 1;
215
+ const validationCommitId = validationCommitIdRef.current;
216
+ function updateRegisteredFieldValidity(nextValidityData, externalInvalid = invalid) {
217
+ const fieldId = getRegisteredFieldId() ?? controlId;
218
+ if (fieldId == null) {
219
+ return;
220
+ }
221
+ const currentFieldData = formRef.current.fields.get(fieldId);
222
+ if (!currentFieldData) {
223
+ return;
224
+ }
225
+ const validityDataWithFormErrors = getCombinedFieldValidityData(nextValidityData, externalInvalid);
226
+ formRef.current.fields.set(fieldId, {
227
+ ...currentFieldData,
228
+ validityData: validityDataWithFormErrors
229
+ });
230
+ }
231
+ if (revalidate) {
232
+ if (state.valid !== false) {
233
+ return;
234
+ }
235
+ const currentNativeValidity = element.validity;
236
+ if (!currentNativeValidity.valueMissing) {
237
+ // The 'valueMissing' (required) condition has been resolved by the user typing.
238
+ // Temporarily mark the field as valid for this onChange event.
239
+ // Other native errors (e.g., typeMismatch) will be caught by full validation on blur or submit.
240
+ const nextValidityData = {
241
+ value,
242
+ state: {
243
+ ...useLabelableId.DEFAULT_VALIDITY_STATE,
244
+ valid: true
245
+ },
246
+ error: '',
247
+ errors: [],
248
+ initialValue: validityData.initialValue
249
+ };
250
+ clearCustomValidity(element, registeredInputs);
251
+
252
+ // The required value is now present; ignore stale external invalid state for this pass.
253
+ updateRegisteredFieldValidity(nextValidityData, false);
254
+ setValidityData(nextValidityData);
255
+ return;
256
+ }
257
+
258
+ // Value is still missing, or other conditions apply.
259
+ // Let's use a representation of current validity for isOnlyValueMissing.
260
+ const currentNativeValidityObject = validityKeys.reduce((acc, key) => {
261
+ acc[key] = currentNativeValidity[key];
262
+ return acc;
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;
269
+ }
270
+
271
+ // If valueMissing is still true AND it's the only issue, or if the field is now natively valid,
272
+ // let it fall through to the main validation logic below.
273
+ }
274
+ function getState(el) {
275
+ const computedState = validityKeys.reduce((acc, key) => {
276
+ acc[key] = el.validity[key];
277
+ return acc;
278
+ }, {});
279
+ let hasOnlyValueMissingError = false;
280
+ for (const key of validityKeys) {
281
+ if (key === 'valid') {
282
+ continue;
283
+ }
284
+ if (key === 'valueMissing' && computedState[key]) {
285
+ hasOnlyValueMissingError = true;
286
+ } else if (computedState[key]) {
287
+ return computedState;
288
+ }
289
+ }
290
+
291
+ // Only make `valueMissing` mark the field invalid if it's been changed
292
+ // to reduce error noise.
293
+ if (hasOnlyValueMissingError && !markedDirtyRef.current) {
294
+ computedState.valid = true;
295
+ computedState.valueMissing = false;
296
+ }
297
+ return computedState;
298
+ }
299
+ timeout.clear();
300
+ let result = null;
301
+ let validationErrors = [];
302
+ const nextState = getState(element);
303
+ let defaultValidationMessage;
304
+ const isValidatingOnChange = shouldValidateOnChange();
305
+ if (element.validationMessage && !isValidatingOnChange) {
306
+ // not validating on change, if there is a `validationMessage` from
307
+ // native validity, set errors and skip calling the custom validate fn
308
+ defaultValidationMessage = element.validationMessage;
309
+ validationErrors = [element.validationMessage];
310
+ } else {
311
+ // call the validate function because either
312
+ // - validating on change, or
313
+ // - native constraint validations passed, custom validity check is next
314
+ const formValues = Array.from(formRef.current.fields.values()).reduce((acc, field) => {
315
+ if (field.name) {
316
+ acc[field.name] = field.getValue();
317
+ }
318
+ return acc;
319
+ }, {});
320
+ const resultOrPromise = validate(value, formValues);
321
+ if (typeof resultOrPromise === 'object' && resultOrPromise !== null && 'then' in resultOrPromise) {
322
+ result = await resultOrPromise;
323
+ if (validationCommitId !== validationCommitIdRef.current) {
324
+ return;
325
+ }
326
+ } else {
327
+ result = resultOrPromise;
328
+ }
329
+ if (result !== null) {
330
+ nextState.valid = false;
331
+ nextState.customError = true;
332
+ if (Array.isArray(result)) {
333
+ validationErrors = result;
334
+ element.setCustomValidity(result.join('\n'));
335
+ } else if (result) {
336
+ validationErrors = [result];
337
+ element.setCustomValidity(result);
338
+ }
339
+ } else if (isValidatingOnChange) {
340
+ // validate function returned no errors, if validating on change
341
+ // we need to clear the custom validity state
342
+ clearCustomValidity(element, registeredInputs);
343
+ nextState.customError = false;
344
+ if (element.validationMessage) {
345
+ defaultValidationMessage = element.validationMessage;
346
+ validationErrors = [element.validationMessage];
347
+ } else if (element.validity.valid && !nextState.valid) {
348
+ nextState.valid = true;
349
+ }
350
+ }
351
+ }
352
+ const nextValidityData = {
353
+ value,
354
+ state: nextState,
355
+ error: defaultValidationMessage ?? (Array.isArray(result) ? result[0] : result ?? ''),
356
+ errors: validationErrors,
357
+ initialValue: validityData.initialValue
358
+ };
359
+
360
+ // Keep Form-level errors part of overall field validity for submit blocking/focus logic.
361
+ updateRegisteredFieldValidity(nextValidityData);
362
+ setValidityData(nextValidityData);
363
+ });
364
+ const change = useButton.useStableCallback(value => {
365
+ timeout.clear();
366
+ const validateOnChange = shouldValidateOnChange();
367
+ if (validateOnChange && value !== '' && validationDebounceTime) {
368
+ validationCommitIdRef.current += 1;
369
+ timeout.start(validationDebounceTime, () => {
370
+ commit(value);
371
+ });
372
+ } else {
373
+ commit(value, !validateOnChange);
374
+ }
375
+ });
376
+ const getValidationProps = React__namespace.useCallback((disabled, externalProps = {}) => useRenderElement.mergeProps(getDescriptionProps(externalProps), state.valid === false && !state.disabled && !disabled ? {
377
+ 'aria-invalid': true
378
+ } : useRenderElement.EMPTY_OBJECT), [getDescriptionProps, state.disabled, state.valid]);
379
+ return React__namespace.useMemo(() => ({
380
+ getValidationProps,
381
+ inputRef,
382
+ registerInput,
383
+ commit,
384
+ change
385
+ }), [getValidationProps, registerInput, commit, change]);
386
+ }
387
+
388
+ function useFieldControlRegistration(params) {
389
+ const {
390
+ commit,
391
+ invalid,
392
+ markedDirtyRef,
393
+ name,
394
+ setRegisteredFieldName,
395
+ setRegisteredFieldId,
396
+ setValidityData,
397
+ validityData
398
+ } = params;
399
+ const {
400
+ formRef
401
+ } = useLabelableId.useFormContext();
402
+ const activeFieldControlSourceRef = React__namespace.useRef(null);
403
+ const registrationRef = React__namespace.useRef(null);
404
+ const fallbackControlRef = React__namespace.useRef(null);
405
+ const getValueForForm = useButton.useStableCallback(() => {
406
+ const registration = registrationRef.current;
407
+ if (!registration) {
408
+ return undefined;
409
+ }
410
+ if (registration.getValue) {
411
+ return registration.getValue();
412
+ }
413
+ return registration.value;
414
+ });
415
+ function getRegistrationValue(registration) {
416
+ return registration.value === undefined ? getValueForForm() : registration.value;
417
+ }
418
+ const validate = useButton.useStableCallback(() => {
419
+ const registration = registrationRef.current;
420
+ markedDirtyRef.current = true;
421
+ if (!registration) {
422
+ commit(validityData.value);
423
+ return;
424
+ }
425
+ commit(getRegistrationValue(registration));
426
+ });
427
+ function refreshRegistration() {
428
+ const registration = registrationRef.current;
429
+ if (!registration || !registration.id) {
430
+ return;
431
+ }
432
+ formRef.current.fields.set(registration.id, {
433
+ getValue: getValueForForm,
434
+ name: name ?? registration.name,
435
+ controlRef: registration.controlRef ?? fallbackControlRef,
436
+ validityData: getCombinedFieldValidityData(validityData, invalid),
437
+ validate
438
+ });
439
+ }
440
+ function deleteRegistration(id = registrationRef.current?.id) {
441
+ if (id) {
442
+ formRef.current.fields.delete(id);
443
+ }
444
+ }
445
+ function syncInitialValue() {
446
+ const registration = registrationRef.current;
447
+ if (!registration) {
448
+ return;
449
+ }
450
+ const initialValue = getRegistrationValue(registration);
451
+ if (validityData.initialValue === null && initialValue !== null) {
452
+ setValidityData(prev => ({
453
+ ...prev,
454
+ initialValue
455
+ }));
456
+ }
457
+ }
458
+ useButton.useIsoLayoutEffect(() => {
459
+ const registration = registrationRef.current;
460
+ if (!registration || !registration.id) {
461
+ return;
462
+ }
463
+ setRegisteredFieldName(name ? undefined : registration.name);
464
+ formRef.current.fields.set(registration.id, {
465
+ getValue: getValueForForm,
466
+ name: name ?? registration.name,
467
+ controlRef: registration.controlRef ?? fallbackControlRef,
468
+ validityData: getCombinedFieldValidityData(validityData, invalid),
469
+ validate
470
+ });
471
+ }, [formRef, getValueForForm, invalid, name, setRegisteredFieldName, validate, validityData]);
472
+ useButton.useIsoLayoutEffect(() => {
473
+ const fields = formRef.current.fields;
474
+ return () => {
475
+ const id = registrationRef.current?.id;
476
+ if (id) {
477
+ fields.delete(id);
478
+ }
479
+ };
480
+ }, [formRef]);
481
+ const register = useButton.useStableCallback((source, registration) => {
482
+ if (!registration) {
483
+ if (activeFieldControlSourceRef.current === source) {
484
+ activeFieldControlSourceRef.current = null;
485
+ deleteRegistration();
486
+ registrationRef.current = null;
487
+ setRegisteredFieldName(undefined);
488
+ setRegisteredFieldId(undefined);
489
+ }
490
+ return;
491
+ }
492
+ const previousId = registrationRef.current?.id;
493
+ activeFieldControlSourceRef.current = source;
494
+ registrationRef.current = registration;
495
+ if (!name) {
496
+ setRegisteredFieldName(registration.name);
497
+ }
498
+ setRegisteredFieldId(registration.id);
499
+ if (previousId && previousId !== registration.id) {
500
+ deleteRegistration(previousId);
501
+ }
502
+ syncInitialValue();
503
+ refreshRegistration();
504
+ });
505
+ return [validate, register];
506
+ }
507
+
508
+ const FieldRootInner = /*#__PURE__*/React__namespace.forwardRef(function FieldRootInner(componentProps, forwardedRef) {
509
+ const {
510
+ errors,
511
+ validationMode: formValidationMode,
512
+ submitAttemptedRef
513
+ } = useLabelableId.useFormContext();
514
+ const {
515
+ render,
516
+ className,
517
+ validate: validateProp,
518
+ validationDebounceTime = 0,
519
+ validationMode = formValidationMode,
520
+ name,
521
+ disabled: disabledProp = false,
522
+ invalid: invalidProp,
523
+ dirty: dirtyProp,
524
+ touched: touchedProp,
525
+ actionsRef,
526
+ style,
527
+ ...elementProps
528
+ } = componentProps;
529
+ const disabledFieldset = useFieldsetRootContext(true)?.disabled;
530
+ const validate = useButton.useStableCallback(validateProp || (() => null));
531
+ const disabled = disabledFieldset || disabledProp;
532
+ const [touchedState, setTouchedUnwrapped] = React__namespace.useState(false);
533
+ const [dirtyState, setDirtyUnwrapped] = React__namespace.useState(false);
534
+ const [filled, setFilled] = React__namespace.useState(false);
535
+ const [focused, setFocused] = React__namespace.useState(false);
536
+ const dirty = dirtyProp ?? dirtyState;
537
+ const touched = touchedProp ?? touchedState;
538
+ const markedDirtyRef = React__namespace.useRef(dirty);
539
+ const registeredFieldIdRef = React__namespace.useRef(undefined);
540
+ const [registeredFieldName, setRegisteredFieldName] = React__namespace.useState();
541
+ const effectiveName = name ?? registeredFieldName;
542
+ useButton.useIsoLayoutEffect(() => {
543
+ if (dirtyProp !== undefined) {
544
+ markedDirtyRef.current = dirtyProp;
545
+ }
546
+ }, [dirtyProp]);
547
+ const getRegisteredFieldId = React__namespace.useCallback(() => registeredFieldIdRef.current, []);
548
+ const setRegisteredFieldId = React__namespace.useCallback(id => {
549
+ registeredFieldIdRef.current = id;
550
+ }, []);
551
+ const setDirty = useButton.useStableCallback(value => {
552
+ if (dirtyProp !== undefined) {
553
+ return;
554
+ }
555
+ if (value) {
556
+ markedDirtyRef.current = true;
557
+ }
558
+ setDirtyUnwrapped(value);
559
+ });
560
+ const setTouched = useButton.useStableCallback(value => {
561
+ if (touchedProp !== undefined) {
562
+ return;
563
+ }
564
+ setTouchedUnwrapped(value);
565
+ });
566
+ const shouldValidateOnChange = useButton.useStableCallback(() => validationMode === 'onChange' || validationMode === 'onSubmit' && submitAttemptedRef.current);
567
+ const formError = effectiveName && Object.hasOwn(errors, effectiveName) ? errors[effectiveName] : null;
568
+ const hasFormError = !!(Array.isArray(formError) ? formError.length : formError);
569
+ const invalid = invalidProp === true || hasFormError;
570
+ const [validityData, setValidityData] = React__namespace.useState({
571
+ state: useLabelableId.DEFAULT_VALIDITY_STATE,
572
+ error: '',
573
+ errors: [],
574
+ value: null,
575
+ initialValue: null
576
+ });
577
+ const valid = disabled ? null : !invalid && validityData.state.valid;
578
+ const state = React__namespace.useMemo(() => ({
579
+ disabled,
580
+ touched,
581
+ dirty,
582
+ valid,
583
+ filled,
584
+ focused
585
+ }), [disabled, touched, dirty, valid, filled, focused]);
586
+ const validation = useFieldValidation({
587
+ setValidityData,
588
+ validate,
589
+ validityData,
590
+ validationDebounceTime,
591
+ invalid,
592
+ markedDirtyRef,
593
+ state,
594
+ shouldValidateOnChange,
595
+ getRegisteredFieldId
596
+ });
597
+ const [validateFieldControl, registerFieldControl] = useFieldControlRegistration({
598
+ commit: validation.commit,
599
+ invalid,
600
+ markedDirtyRef,
601
+ name,
602
+ setRegisteredFieldName,
603
+ setRegisteredFieldId,
604
+ setValidityData,
605
+ validityData
606
+ });
607
+ React__namespace.useImperativeHandle(actionsRef, () => ({
608
+ validate: validateFieldControl
609
+ }), [validateFieldControl]);
610
+ const contextValue = React__namespace.useMemo(() => ({
611
+ invalid,
612
+ name: effectiveName,
613
+ validityData,
614
+ setValidityData,
615
+ disabled,
616
+ touched,
617
+ setTouched,
618
+ dirty,
619
+ setDirty,
620
+ filled,
621
+ setFilled,
622
+ focused,
623
+ setFocused,
624
+ validate,
625
+ validationMode,
626
+ validationDebounceTime,
627
+ shouldValidateOnChange,
628
+ state,
629
+ markedDirtyRef,
630
+ registerFieldControl,
631
+ validation
632
+ }), [invalid, effectiveName, validityData, disabled, touched, setTouched, dirty, setDirty, filled, setFilled, focused, setFocused, validate, validationMode, validationDebounceTime, shouldValidateOnChange, state, registerFieldControl, validation]);
633
+ const element = useRenderElement.useRenderElement('div', componentProps, {
634
+ ref: forwardedRef,
635
+ state,
636
+ props: elementProps,
637
+ stateAttributesMapping: useLabelableId.fieldValidityMapping
638
+ });
639
+ return /*#__PURE__*/jsxRuntime.jsx(useLabelableId.FieldRootContext.Provider, {
640
+ value: contextValue,
641
+ children: element
642
+ });
643
+ });
644
+
645
+ /**
646
+ * Groups all parts of the field.
647
+ * Renders a `<div>` element.
648
+ *
649
+ * Documentation: [Base UI Field](https://base-ui.com/react/components/field)
650
+ */
651
+ if (process.env.NODE_ENV !== "production") FieldRootInner.displayName = "FieldRootInner";
652
+ const FieldRoot = /*#__PURE__*/React__namespace.forwardRef(function FieldRoot(componentProps, forwardedRef) {
653
+ return /*#__PURE__*/jsxRuntime.jsx(LabelableProvider, {
654
+ children: /*#__PURE__*/jsxRuntime.jsx(FieldRootInner, {
655
+ ...componentProps,
656
+ ref: forwardedRef
657
+ })
658
+ });
659
+ });
660
+ if (process.env.NODE_ENV !== "production") FieldRoot.displayName = "FieldRoot";
661
+
662
+ /**
663
+ * An accessible label that is automatically associated with the field control.
664
+ * Renders a `<label>` element.
665
+ *
666
+ * Documentation: [Base UI Field](https://base-ui.com/react/components/field)
667
+ */
668
+ const FieldLabel = /*#__PURE__*/React__namespace.forwardRef(function FieldLabel(componentProps, forwardedRef) {
669
+ const {
670
+ render,
671
+ className,
672
+ style,
673
+ id: idProp,
674
+ nativeLabel = true,
675
+ ...elementProps
676
+ } = componentProps;
677
+ const fieldRootContext = useLabelableId.useFieldRootContext(false);
678
+ const fieldItemContext = useFieldItemContext();
679
+ const {
680
+ labelId
681
+ } = useLabelableId.useLabelableContext();
682
+ const state = {
683
+ ...fieldRootContext.state,
684
+ disabled: fieldRootContext.disabled || fieldItemContext.disabled
685
+ };
686
+ const labelRef = React__namespace.useRef(null);
687
+ const labelProps = useLabel.useLabel({
688
+ id: labelId ?? idProp,
689
+ native: nativeLabel
690
+ });
691
+ if (process.env.NODE_ENV !== 'production') {
692
+ // eslint-disable-next-line react-hooks/rules-of-hooks
693
+ React__namespace.useEffect(() => {
694
+ if (!labelRef.current) {
695
+ return;
696
+ }
697
+ const isLabelTag = labelRef.current.tagName === 'LABEL';
698
+ if (nativeLabel) {
699
+ if (!isLabelTag) {
700
+ const ownerStackMessage = useButton.SafeReact.captureOwnerStack?.() || '';
701
+ const message = '<Field.Label> expected a <label> element because the `nativeLabel` prop is true. ' + 'Rendering a non-<label> disables native label association, so `htmlFor` will not ' + 'work. Use a real <label> in the `render` prop, or set `nativeLabel` to `false`.';
702
+ useButton.error(`${message}${ownerStackMessage}`);
703
+ }
704
+ } else if (isLabelTag) {
705
+ const ownerStackMessage = useButton.SafeReact.captureOwnerStack?.() || '';
706
+ const message = '<Field.Label> expected a non-<label> element because the `nativeLabel` prop is false. ' + 'Rendering a <label> assumes native label behavior while Base UI treats it as ' + 'non-native, which can cause unexpected pointer behavior. Use a non-<label> in the ' + '`render` prop, or set `nativeLabel` to `true`.';
707
+ useButton.error(`${message}${ownerStackMessage}`);
708
+ }
709
+ }, [nativeLabel]);
710
+ }
711
+ const element = useRenderElement.useRenderElement('label', componentProps, {
712
+ ref: [forwardedRef, labelRef],
713
+ state,
714
+ props: [labelProps, elementProps],
715
+ stateAttributesMapping: useLabelableId.fieldValidityMapping
716
+ });
717
+ return element;
718
+ });
719
+ if (process.env.NODE_ENV !== "production") FieldLabel.displayName = "FieldLabel";
720
+
721
+ const stateAttributesMapping = {
722
+ ...useLabelableId.fieldValidityMapping,
723
+ ...useButton.transitionStatusMapping
724
+ };
725
+
726
+ /**
727
+ * An error message displayed if the field control fails validation.
728
+ * Renders a `<div>` element.
729
+ *
730
+ * Documentation: [Base UI Field](https://base-ui.com/react/components/field)
731
+ */
732
+ const FieldError = /*#__PURE__*/React__namespace.forwardRef(function FieldError(componentProps, forwardedRef) {
733
+ const {
734
+ render,
735
+ id: idProp,
736
+ className,
737
+ match,
738
+ style,
739
+ ...elementProps
740
+ } = componentProps;
741
+ const id = useButton.useBaseUiId(idProp);
742
+ const {
743
+ validityData,
744
+ state: fieldState,
745
+ name
746
+ } = useLabelableId.useFieldRootContext(false);
747
+ const {
748
+ setMessageIds
749
+ } = useLabelableId.useLabelableContext();
750
+ const {
751
+ errors
752
+ } = useLabelableId.useFormContext();
753
+ const formError = name && Object.hasOwn(errors, name) ? errors[name] : null;
754
+ const hasFormError = !!(Array.isArray(formError) ? formError.length : formError);
755
+ const hasSpecificMatch = typeof match === 'string';
756
+ let rendered = false;
757
+ if (match === true) {
758
+ rendered = true;
759
+ } else if (fieldState.disabled) {
760
+ rendered = false;
761
+ } else if (hasSpecificMatch) {
762
+ rendered = Boolean(validityData.state[match]);
763
+ } else {
764
+ rendered = hasFormError || validityData.state.valid === false;
765
+ }
766
+ const {
767
+ mounted,
768
+ transitionStatus,
769
+ setMounted
770
+ } = useButton.useTransitionStatus(rendered);
771
+ useButton.useIsoLayoutEffect(() => {
772
+ if (!rendered || !id) {
773
+ return undefined;
774
+ }
775
+ setMessageIds(v => v.concat(id));
776
+ return () => {
777
+ setMessageIds(v => v.filter(item => item !== id));
778
+ };
779
+ }, [rendered, id, setMessageIds]);
780
+ const errorRef = React__namespace.useRef(null);
781
+ const [lastRenderedMessage, setLastRenderedMessage] = React__namespace.useState(null);
782
+ const [lastRenderedMessageKey, setLastRenderedMessageKey] = React__namespace.useState(null);
783
+ let error = validityData.error;
784
+ if (!hasSpecificMatch && hasFormError) {
785
+ error = formError;
786
+ } else if (validityData.errors.length > 1) {
787
+ error = validityData.errors;
788
+ }
789
+ let errorMessage = error ?? '';
790
+ if (Array.isArray(error)) {
791
+ errorMessage = error.length > 1 ? /*#__PURE__*/jsxRuntime.jsx("ul", {
792
+ children: error.map(message => /*#__PURE__*/jsxRuntime.jsx("li", {
793
+ children: message
794
+ }, message))
795
+ }) : error[0] ?? '';
796
+ }
797
+ const errorKey = Array.isArray(error) ? JSON.stringify(error) : error;
798
+ if (rendered && errorKey !== lastRenderedMessageKey) {
799
+ setLastRenderedMessageKey(errorKey);
800
+ setLastRenderedMessage(errorMessage);
801
+ }
802
+ useButton.useOpenChangeComplete({
803
+ open: rendered,
804
+ ref: errorRef,
805
+ onComplete() {
806
+ if (!rendered) {
807
+ setMounted(false);
808
+ }
809
+ }
810
+ });
811
+ const state = {
812
+ ...fieldState,
813
+ transitionStatus
814
+ };
815
+ const element = useRenderElement.useRenderElement('div', componentProps, {
816
+ ref: [forwardedRef, errorRef],
817
+ state,
818
+ props: [{
819
+ id,
820
+ children: rendered ? errorMessage : lastRenderedMessage
821
+ }, elementProps],
822
+ stateAttributesMapping,
823
+ enabled: mounted
824
+ });
825
+ if (!mounted) {
826
+ return null;
827
+ }
828
+ return element;
829
+ });
830
+ if (process.env.NODE_ENV !== "production") FieldError.displayName = "FieldError";
831
+
832
+ /**
833
+ * A paragraph with additional information about the field.
834
+ * Renders a `<p>` element.
835
+ *
836
+ * Documentation: [Base UI Field](https://base-ui.com/react/components/field)
837
+ */
838
+ const FieldDescription = /*#__PURE__*/React__namespace.forwardRef(function FieldDescription(componentProps, forwardedRef) {
839
+ const {
840
+ render,
841
+ id: idProp,
842
+ className,
843
+ style,
844
+ ...elementProps
845
+ } = componentProps;
846
+ const id = useButton.useBaseUiId(idProp);
847
+ const fieldRootContext = useLabelableId.useFieldRootContext(false);
848
+ const fieldItemContext = useFieldItemContext();
849
+ const {
850
+ setMessageIds
851
+ } = useLabelableId.useLabelableContext();
852
+ const state = {
853
+ ...fieldRootContext.state,
854
+ disabled: fieldRootContext.disabled || fieldItemContext.disabled
855
+ };
856
+ useButton.useIsoLayoutEffect(() => {
857
+ if (!id) {
858
+ return undefined;
859
+ }
860
+ setMessageIds(v => v.concat(id));
861
+ return () => {
862
+ setMessageIds(v => v.filter(item => item !== id));
863
+ };
864
+ }, [id, setMessageIds]);
865
+ const element = useRenderElement.useRenderElement('p', componentProps, {
866
+ ref: forwardedRef,
867
+ state,
868
+ props: [{
869
+ id
870
+ }, elementProps],
871
+ stateAttributesMapping: useLabelableId.fieldValidityMapping
872
+ });
873
+ return element;
874
+ });
875
+ if (process.env.NODE_ENV !== "production") FieldDescription.displayName = "FieldDescription";
876
+
877
+ exports.FieldDescription = FieldDescription;
878
+ exports.FieldError = FieldError;
879
+ exports.FieldLabel = FieldLabel;
880
+ exports.FieldRoot = FieldRoot;