@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
@@ -4,6 +4,16 @@ var floatingUi_utils_dom = require('./floating-ui.utils.dom-cjs.js');
4
4
  var useButton = require('./useButton-cjs.js');
5
5
  var useLabelableId = require('./useLabelableId-cjs.js');
6
6
 
7
+ function stringifyLocale(locale) {
8
+ if (Array.isArray(locale)) {
9
+ return locale.map(value => stringifyLocale(value)).join(',');
10
+ }
11
+ if (locale == null) {
12
+ return '';
13
+ }
14
+ return String(locale);
15
+ }
16
+
7
17
  function useRegisteredLabelId(idProp, setLabelId) {
8
18
  const id = useButton.useBaseUiId(idProp);
9
19
  useButton.useIsoLayoutEffect(() => {
@@ -77,9 +87,9 @@ function focusElementWithVisible(element) {
77
87
  element.focus({
78
88
  // Available from Chrome 144+ (January 2026).
79
89
  // Safari and Firefox already support it.
80
- // @ts-expect-error not available in types yet
81
90
  focusVisible: true
82
91
  });
83
92
  }
84
93
 
94
+ exports.stringifyLocale = stringifyLocale;
85
95
  exports.useLabel = useLabel;
@@ -2,6 +2,16 @@ import { a as isHTMLElement } from './floating-ui.utils.dom-es.js';
2
2
  import { u as useBaseUiId, a as useIsoLayoutEffect, b as useStableCallback, g as getTarget, o as ownerDocument } from './useButton-es.js';
3
3
  import { u as useLabelableContext } from './useLabelableId-es.js';
4
4
 
5
+ function stringifyLocale(locale) {
6
+ if (Array.isArray(locale)) {
7
+ return locale.map(value => stringifyLocale(value)).join(',');
8
+ }
9
+ if (locale == null) {
10
+ return '';
11
+ }
12
+ return String(locale);
13
+ }
14
+
5
15
  function useRegisteredLabelId(idProp, setLabelId) {
6
16
  const id = useBaseUiId(idProp);
7
17
  useIsoLayoutEffect(() => {
@@ -75,9 +85,8 @@ function focusElementWithVisible(element) {
75
85
  element.focus({
76
86
  // Available from Chrome 144+ (January 2026).
77
87
  // Safari and Firefox already support it.
78
- // @ts-expect-error not available in types yet
79
88
  focusVisible: true
80
89
  });
81
90
  }
82
91
 
83
- export { useLabel as u };
92
+ export { stringifyLocale as s, useLabel as u };
@@ -1,8 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var ReactDOM = require('react-dom');
4
- var useButton = require('./useButton-cjs.js');
5
3
  var React = require('react');
4
+ var useButton = require('./useButton-cjs.js');
6
5
  var useRenderElement = require('./useRenderElement-cjs.js');
7
6
  var floatingUi_utils_dom = require('./floating-ui.utils.dom-cjs.js');
8
7
 
@@ -23,7 +22,6 @@ function _interopNamespaceDefault(e) {
23
22
  return Object.freeze(n);
24
23
  }
25
24
 
26
- var ReactDOM__namespace = /*#__PURE__*/_interopNamespaceDefault(ReactDOM);
27
25
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
28
26
 
29
27
  let FieldControlDataAttributes = /*#__PURE__*/function (FieldControlDataAttributes) {
@@ -32,11 +30,11 @@ let FieldControlDataAttributes = /*#__PURE__*/function (FieldControlDataAttribut
32
30
  */
33
31
  FieldControlDataAttributes["disabled"] = "data-disabled";
34
32
  /**
35
- * Present when the field is in valid state.
33
+ * Present when the field is in a valid state.
36
34
  */
37
35
  FieldControlDataAttributes["valid"] = "data-valid";
38
36
  /**
39
- * Present when the field is in invalid state.
37
+ * Present when the field is in an invalid state.
40
38
  */
41
39
  FieldControlDataAttributes["invalid"] = "data-invalid";
42
40
  /**
@@ -98,7 +96,7 @@ const fieldValidityMapping = {
98
96
  }
99
97
  };
100
98
 
101
- const FieldRootContext = /*#__PURE__*/React__namespace.createContext({
99
+ const DEFAULT_FIELD_ROOT_CONTEXT = {
102
100
  invalid: undefined,
103
101
  name: undefined,
104
102
  validityData: {
@@ -126,15 +124,18 @@ const FieldRootContext = /*#__PURE__*/React__namespace.createContext({
126
124
  markedDirtyRef: {
127
125
  current: false
128
126
  },
127
+ registerFieldControl: useRenderElement.NOOP,
129
128
  validation: {
130
- getValidationProps: (props = useRenderElement.EMPTY_OBJECT) => props,
131
- getInputValidationProps: (props = useRenderElement.EMPTY_OBJECT) => props,
129
+ getValidationProps: (_disabled, props = useRenderElement.EMPTY_OBJECT) => props,
132
130
  inputRef: {
133
131
  current: null
134
132
  },
135
- commit: async () => {}
133
+ registerInput: useRenderElement.NOOP,
134
+ commit: async () => {},
135
+ change: useRenderElement.NOOP
136
136
  }
137
- });
137
+ };
138
+ const FieldRootContext = /*#__PURE__*/React__namespace.createContext(DEFAULT_FIELD_ROOT_CONTEXT);
138
139
  if (process.env.NODE_ENV !== "production") FieldRootContext.displayName = "FieldRootContext";
139
140
  function useFieldRootContext(optional = true) {
140
141
  const context = React__namespace.useContext(FieldRootContext);
@@ -144,18 +145,31 @@ function useFieldRootContext(optional = true) {
144
145
  return context;
145
146
  }
146
147
 
147
- /**
148
- * Combines the field's client-side, stateful validity data with the external invalid state to
149
- * determine the field's true validity.
150
- */
151
- function getCombinedFieldValidityData(validityData, invalid) {
152
- return {
153
- ...validityData,
154
- state: {
155
- ...validityData.state,
156
- valid: !invalid && validityData.state.valid
148
+ function useRegisterFieldControl(controlRef, id, value, getFormValueOverride, enabled = true, name) {
149
+ const {
150
+ registerFieldControl
151
+ } = useFieldRootContext();
152
+ const sourceRef = React__namespace.useRef(null);
153
+ if (!sourceRef.current) {
154
+ sourceRef.current = Symbol();
155
+ }
156
+ useButton.useIsoLayoutEffect(() => {
157
+ const source = sourceRef.current;
158
+ if (!source || !enabled) {
159
+ return undefined;
157
160
  }
158
- };
161
+ const registration = {
162
+ controlRef,
163
+ getValue: getFormValueOverride,
164
+ id,
165
+ name,
166
+ value
167
+ };
168
+ registerFieldControl(source, registration);
169
+ return () => {
170
+ registerFieldControl(source, undefined);
171
+ };
172
+ }, [controlRef, enabled, getFormValueOverride, id, name, registerFieldControl, value]);
159
173
  }
160
174
 
161
175
  const FormContext = /*#__PURE__*/React__namespace.createContext({
@@ -176,74 +190,6 @@ function useFormContext() {
176
190
  return React__namespace.useContext(FormContext);
177
191
  }
178
192
 
179
- function useField(params) {
180
- const {
181
- enabled = true,
182
- value,
183
- id,
184
- name,
185
- controlRef,
186
- commit
187
- } = params;
188
- const {
189
- formRef
190
- } = useFormContext();
191
- const {
192
- invalid,
193
- markedDirtyRef,
194
- validityData,
195
- setValidityData
196
- } = useFieldRootContext();
197
- const getValue = useButton.useStableCallback(params.getValue);
198
- useButton.useIsoLayoutEffect(() => {
199
- if (!enabled) {
200
- return;
201
- }
202
- let initialValue = value;
203
- if (initialValue === undefined) {
204
- initialValue = getValue();
205
- }
206
- if (validityData.initialValue === null && initialValue !== null) {
207
- setValidityData(prev => ({
208
- ...prev,
209
- initialValue
210
- }));
211
- }
212
- }, [enabled, setValidityData, value, validityData.initialValue, getValue]);
213
- useButton.useIsoLayoutEffect(() => {
214
- if (!enabled || !id) {
215
- return;
216
- }
217
- formRef.current.fields.set(id, {
218
- getValue,
219
- name,
220
- controlRef,
221
- validityData: getCombinedFieldValidityData(validityData, invalid),
222
- validate(flushSync = true) {
223
- let nextValue = value;
224
- if (nextValue === undefined) {
225
- nextValue = getValue();
226
- }
227
- markedDirtyRef.current = true;
228
- if (!flushSync) {
229
- commit(nextValue);
230
- } else {
231
- // Synchronously update the validity state so the submit event can be prevented.
232
- ReactDOM__namespace.flushSync(() => commit(nextValue));
233
- }
234
- }
235
- });
236
- }, [commit, controlRef, enabled, formRef, getValue, id, invalid, markedDirtyRef, name, validityData, value]);
237
- useButton.useIsoLayoutEffect(() => {
238
- const fields = formRef.current.fields;
239
- return () => {
240
- if (id) {
241
- fields.delete(id);
242
- }
243
- };
244
- }, [formRef, id]);
245
- }
246
-
247
193
  /**
248
194
  * A context for providing [labelable elements](https://html.spec.whatwg.org/multipage/forms.html#category-label)\
249
195
  * with an accessible name (label) and description.
@@ -319,14 +265,14 @@ function useLabelableId(params = {}) {
319
265
  return controlId ?? defaultId;
320
266
  }
321
267
 
268
+ exports.DEFAULT_FIELD_ROOT_CONTEXT = DEFAULT_FIELD_ROOT_CONTEXT;
322
269
  exports.DEFAULT_FIELD_STATE_ATTRIBUTES = DEFAULT_FIELD_STATE_ATTRIBUTES;
323
270
  exports.DEFAULT_VALIDITY_STATE = DEFAULT_VALIDITY_STATE;
324
271
  exports.FieldRootContext = FieldRootContext;
325
272
  exports.LabelableContext = LabelableContext;
326
273
  exports.fieldValidityMapping = fieldValidityMapping;
327
- exports.getCombinedFieldValidityData = getCombinedFieldValidityData;
328
- exports.useField = useField;
329
274
  exports.useFieldRootContext = useFieldRootContext;
330
275
  exports.useFormContext = useFormContext;
331
276
  exports.useLabelableContext = useLabelableContext;
332
277
  exports.useLabelableId = useLabelableId;
278
+ exports.useRegisterFieldControl = useRegisterFieldControl;
@@ -1,7 +1,6 @@
1
- import * as ReactDOM from 'react-dom';
2
- import { b as useStableCallback, a as useIsoLayoutEffect, u as useBaseUiId } from './useButton-es.js';
3
1
  import * as React from 'react';
4
- import { N as NOOP, f as formatErrorMessage, E as EMPTY_OBJECT, a as useRefWithInit } from './useRenderElement-es.js';
2
+ import { a as useIsoLayoutEffect, u as useBaseUiId, b as useStableCallback } from './useButton-es.js';
3
+ import { N as NOOP, E as EMPTY_OBJECT, f as formatErrorMessage, a as useRefWithInit } from './useRenderElement-es.js';
5
4
  import { b as isElement } from './floating-ui.utils.dom-es.js';
6
5
 
7
6
  let FieldControlDataAttributes = /*#__PURE__*/function (FieldControlDataAttributes) {
@@ -10,11 +9,11 @@ let FieldControlDataAttributes = /*#__PURE__*/function (FieldControlDataAttribut
10
9
  */
11
10
  FieldControlDataAttributes["disabled"] = "data-disabled";
12
11
  /**
13
- * Present when the field is in valid state.
12
+ * Present when the field is in a valid state.
14
13
  */
15
14
  FieldControlDataAttributes["valid"] = "data-valid";
16
15
  /**
17
- * Present when the field is in invalid state.
16
+ * Present when the field is in an invalid state.
18
17
  */
19
18
  FieldControlDataAttributes["invalid"] = "data-invalid";
20
19
  /**
@@ -76,7 +75,7 @@ const fieldValidityMapping = {
76
75
  }
77
76
  };
78
77
 
79
- const FieldRootContext = /*#__PURE__*/React.createContext({
78
+ const DEFAULT_FIELD_ROOT_CONTEXT = {
80
79
  invalid: undefined,
81
80
  name: undefined,
82
81
  validityData: {
@@ -104,15 +103,18 @@ const FieldRootContext = /*#__PURE__*/React.createContext({
104
103
  markedDirtyRef: {
105
104
  current: false
106
105
  },
106
+ registerFieldControl: NOOP,
107
107
  validation: {
108
- getValidationProps: (props = EMPTY_OBJECT) => props,
109
- getInputValidationProps: (props = EMPTY_OBJECT) => props,
108
+ getValidationProps: (_disabled, props = EMPTY_OBJECT) => props,
110
109
  inputRef: {
111
110
  current: null
112
111
  },
113
- commit: async () => {}
112
+ registerInput: NOOP,
113
+ commit: async () => {},
114
+ change: NOOP
114
115
  }
115
- });
116
+ };
117
+ const FieldRootContext = /*#__PURE__*/React.createContext(DEFAULT_FIELD_ROOT_CONTEXT);
116
118
  if (process.env.NODE_ENV !== "production") FieldRootContext.displayName = "FieldRootContext";
117
119
  function useFieldRootContext(optional = true) {
118
120
  const context = React.useContext(FieldRootContext);
@@ -122,18 +124,31 @@ function useFieldRootContext(optional = true) {
122
124
  return context;
123
125
  }
124
126
 
125
- /**
126
- * Combines the field's client-side, stateful validity data with the external invalid state to
127
- * determine the field's true validity.
128
- */
129
- function getCombinedFieldValidityData(validityData, invalid) {
130
- return {
131
- ...validityData,
132
- state: {
133
- ...validityData.state,
134
- valid: !invalid && validityData.state.valid
127
+ function useRegisterFieldControl(controlRef, id, value, getFormValueOverride, enabled = true, name) {
128
+ const {
129
+ registerFieldControl
130
+ } = useFieldRootContext();
131
+ const sourceRef = React.useRef(null);
132
+ if (!sourceRef.current) {
133
+ sourceRef.current = Symbol();
134
+ }
135
+ useIsoLayoutEffect(() => {
136
+ const source = sourceRef.current;
137
+ if (!source || !enabled) {
138
+ return undefined;
135
139
  }
136
- };
140
+ const registration = {
141
+ controlRef,
142
+ getValue: getFormValueOverride,
143
+ id,
144
+ name,
145
+ value
146
+ };
147
+ registerFieldControl(source, registration);
148
+ return () => {
149
+ registerFieldControl(source, undefined);
150
+ };
151
+ }, [controlRef, enabled, getFormValueOverride, id, name, registerFieldControl, value]);
137
152
  }
138
153
 
139
154
  const FormContext = /*#__PURE__*/React.createContext({
@@ -154,74 +169,6 @@ function useFormContext() {
154
169
  return React.useContext(FormContext);
155
170
  }
156
171
 
157
- function useField(params) {
158
- const {
159
- enabled = true,
160
- value,
161
- id,
162
- name,
163
- controlRef,
164
- commit
165
- } = params;
166
- const {
167
- formRef
168
- } = useFormContext();
169
- const {
170
- invalid,
171
- markedDirtyRef,
172
- validityData,
173
- setValidityData
174
- } = useFieldRootContext();
175
- const getValue = useStableCallback(params.getValue);
176
- useIsoLayoutEffect(() => {
177
- if (!enabled) {
178
- return;
179
- }
180
- let initialValue = value;
181
- if (initialValue === undefined) {
182
- initialValue = getValue();
183
- }
184
- if (validityData.initialValue === null && initialValue !== null) {
185
- setValidityData(prev => ({
186
- ...prev,
187
- initialValue
188
- }));
189
- }
190
- }, [enabled, setValidityData, value, validityData.initialValue, getValue]);
191
- useIsoLayoutEffect(() => {
192
- if (!enabled || !id) {
193
- return;
194
- }
195
- formRef.current.fields.set(id, {
196
- getValue,
197
- name,
198
- controlRef,
199
- validityData: getCombinedFieldValidityData(validityData, invalid),
200
- validate(flushSync = true) {
201
- let nextValue = value;
202
- if (nextValue === undefined) {
203
- nextValue = getValue();
204
- }
205
- markedDirtyRef.current = true;
206
- if (!flushSync) {
207
- commit(nextValue);
208
- } else {
209
- // Synchronously update the validity state so the submit event can be prevented.
210
- ReactDOM.flushSync(() => commit(nextValue));
211
- }
212
- }
213
- });
214
- }, [commit, controlRef, enabled, formRef, getValue, id, invalid, markedDirtyRef, name, validityData, value]);
215
- useIsoLayoutEffect(() => {
216
- const fields = formRef.current.fields;
217
- return () => {
218
- if (id) {
219
- fields.delete(id);
220
- }
221
- };
222
- }, [formRef, id]);
223
- }
224
-
225
172
  /**
226
173
  * A context for providing [labelable elements](https://html.spec.whatwg.org/multipage/forms.html#category-label)\
227
174
  * with an accessible name (label) and description.
@@ -297,4 +244,4 @@ function useLabelableId(params = {}) {
297
244
  return controlId ?? defaultId;
298
245
  }
299
246
 
300
- export { DEFAULT_VALIDITY_STATE as D, FieldRootContext as F, LabelableContext as L, useFormContext as a, useFieldRootContext as b, useLabelableId as c, useField as d, DEFAULT_FIELD_STATE_ATTRIBUTES as e, fieldValidityMapping as f, getCombinedFieldValidityData as g, useLabelableContext as u };
247
+ export { DEFAULT_VALIDITY_STATE as D, FieldRootContext as F, LabelableContext as L, useFormContext as a, useFieldRootContext as b, useLabelableId as c, useRegisterFieldControl as d, DEFAULT_FIELD_ROOT_CONTEXT as e, fieldValidityMapping as f, DEFAULT_FIELD_STATE_ATTRIBUTES as g, useLabelableContext as u };
@@ -55,7 +55,7 @@ const EMPTY_PROPS = {};
55
55
  * Props can either be provided as objects or as functions that take the previous props as an argument.
56
56
  * The function will receive the merged props up to that point (going from left to right):
57
57
  * so in the case of `(obj1, obj2, fn, obj3)`, `fn` will receive the merged props of `obj1` and `obj2`.
58
- * The function is responsible for chaining event handlers if needed (i.e. we don't run the merge logic).
58
+ * The function is responsible for chaining event handlers if needed (that is, we don't run the merge logic).
59
59
  *
60
60
  * Event handlers returned by the functions are not automatically prevented when `preventBaseUIHandler` is called.
61
61
  * They must check `event.baseUIHandlerPrevented` themselves and bail out if it's true.
@@ -71,21 +71,23 @@ const EMPTY_PROPS = {};
71
71
  */
72
72
 
73
73
  function mergeProps(a, b, c, d, e) {
74
- // We need to mutably own `merged`
75
- let merged = {
76
- ...resolvePropsGetter(a, EMPTY_PROPS)
77
- };
74
+ if (!c && !d && !e && !a) {
75
+ return createInitialMergedProps(b);
76
+ }
77
+
78
+ // We need to mutably own `merged`.
79
+ let merged = createInitialMergedProps(a);
78
80
  if (b) {
79
- merged = mergeOne(merged, b);
81
+ merged = mergeInto(merged, b);
80
82
  }
81
83
  if (c) {
82
- merged = mergeOne(merged, c);
84
+ merged = mergeInto(merged, c);
83
85
  }
84
86
  if (d) {
85
- merged = mergeOne(merged, d);
87
+ merged = mergeInto(merged, d);
86
88
  }
87
89
  if (e) {
88
- merged = mergeOne(merged, e);
90
+ merged = mergeInto(merged, e);
89
91
  }
90
92
  return merged;
91
93
  }
@@ -109,24 +111,46 @@ function mergePropsN(props) {
109
111
  return EMPTY_PROPS;
110
112
  }
111
113
  if (props.length === 1) {
112
- return resolvePropsGetter(props[0], EMPTY_PROPS);
114
+ return createInitialMergedProps(props[0]);
113
115
  }
114
116
 
115
- // We need to mutably own `merged`
116
- let merged = {
117
- ...resolvePropsGetter(props[0], EMPTY_PROPS)
118
- };
117
+ // We need to mutably own `merged`.
118
+ let merged = createInitialMergedProps(props[0]);
119
119
  for (let i = 1; i < props.length; i += 1) {
120
- merged = mergeOne(merged, props[i]);
120
+ merged = mergeInto(merged, props[i]);
121
121
  }
122
122
  return merged;
123
123
  }
124
- function mergeOne(merged, inputProps) {
124
+ function createInitialMergedProps(inputProps) {
125
125
  if (isPropsGetter(inputProps)) {
126
- return inputProps(merged);
126
+ // Getter-returned handlers intentionally keep their existing semantics.
127
+ return {
128
+ ...resolvePropsGetter(inputProps, EMPTY_PROPS)
129
+ };
130
+ }
131
+ return copyInitialProps(inputProps);
132
+ }
133
+ function mergeInto(merged, inputProps) {
134
+ if (isPropsGetter(inputProps)) {
135
+ return resolvePropsGetter(inputProps, merged);
127
136
  }
128
137
  return mutablyMergeInto(merged, inputProps);
129
138
  }
139
+ function copyInitialProps(inputProps) {
140
+ const copiedProps = {
141
+ ...inputProps
142
+ };
143
+
144
+ // `copiedProps` is our fresh own-object copy, so iterating with `for...in` is safe here.
145
+ // eslint-disable-next-line guard-for-in
146
+ for (const propName in copiedProps) {
147
+ const propValue = copiedProps[propName];
148
+ if (isEventHandler(propName, propValue)) {
149
+ copiedProps[propName] = wrapEventHandler(propValue);
150
+ }
151
+ }
152
+ return copiedProps;
153
+ }
130
154
 
131
155
  /**
132
156
  * Merges two sets of props. In case of conflicts, the external props take precedence.
@@ -183,23 +207,36 @@ function mergeEventHandlers(ourHandler, theirHandler) {
183
207
  return ourHandler;
184
208
  }
185
209
  if (!ourHandler) {
186
- return theirHandler;
210
+ return wrapEventHandler(theirHandler);
187
211
  }
188
- return event => {
212
+ return (...args) => {
213
+ const event = args[0];
189
214
  if (isSyntheticEvent(event)) {
190
215
  const baseUIEvent = event;
191
216
  makeEventPreventable(baseUIEvent);
192
- const result = theirHandler(baseUIEvent);
217
+ const result = theirHandler(...args);
193
218
  if (!baseUIEvent.baseUIHandlerPrevented) {
194
- ourHandler?.(baseUIEvent);
219
+ ourHandler?.(...args);
195
220
  }
196
221
  return result;
197
222
  }
198
- const result = theirHandler(event);
199
- ourHandler?.(event);
223
+ const result = theirHandler(...args);
224
+ ourHandler?.(...args);
200
225
  return result;
201
226
  };
202
227
  }
228
+ function wrapEventHandler(handler) {
229
+ if (!handler) {
230
+ return handler;
231
+ }
232
+ return (...args) => {
233
+ const event = args[0];
234
+ if (isSyntheticEvent(event)) {
235
+ makeEventPreventable(event);
236
+ }
237
+ return handler(...args);
238
+ };
239
+ }
203
240
  function makeEventPreventable(event) {
204
241
  event.preventBaseUIHandler = () => {
205
242
  event.baseUIHandlerPrevented = true;
@@ -484,7 +521,12 @@ function useRenderElementProps(componentProps, params = {}) {
484
521
  const className = enabled ? resolveClassName(classNameProp, state) : undefined;
485
522
  const style = enabled ? resolveStyle(styleProp, state) : undefined;
486
523
  const stateProps = enabled ? getStateAttributesProps(state, stateAttributesMapping) : EMPTY_OBJECT;
487
- const outProps = enabled ? mergeObjects(stateProps, Array.isArray(props) ? mergePropsN(props) : props) ?? EMPTY_OBJECT : EMPTY_OBJECT;
524
+ const resolvedProps = enabled && props ? resolveRenderFunctionProps(props) : undefined;
525
+
526
+ // Ensure outProps is always a new mutable object when enabled, never EMPTY_OBJECT.
527
+ // This prevents potential TypeError when setting ref, className, or style properties,
528
+ // since EMPTY_OBJECT is frozen and mutations would fail in strict mode.
529
+ const outProps = enabled ? mergeObjects(stateProps, resolvedProps) ?? {} : EMPTY_OBJECT;
488
530
 
489
531
  // SAFETY: The `useMergedRefs` functions use a single hook to store the same value,
490
532
  // switching between them at runtime is safe. If this assertion fails, React will
@@ -512,12 +554,20 @@ function useRenderElementProps(componentProps, params = {}) {
512
554
  }
513
555
  return outProps;
514
556
  }
557
+ function resolveRenderFunctionProps(props) {
558
+ if (Array.isArray(props)) {
559
+ return mergePropsN(props);
560
+ }
561
+ return mergeProps(undefined, props);
562
+ }
515
563
 
516
564
  // The symbol React uses internally for lazy components
517
565
  // https://github.com/facebook/react/blob/a0566250b210499b4c5677f5ac2eedbd71d51a1b/packages/shared/ReactSymbols.js#L31
518
566
  //
519
567
  // TODO delete once https://github.com/facebook/react/issues/32392 is fixed
520
568
  const REACT_LAZY_TYPE = Symbol.for('react.lazy');
569
+ const COMPONENT_IDENTIFIER_PATTERN = /^[A-Z][A-Za-z0-9$]*$/;
570
+ const LOWERCASE_CHARACTER_PATTERN = /[a-z]/;
521
571
  function evaluateRenderProp(element, render, props, state) {
522
572
  if (render) {
523
573
  if (typeof render === 'function') {
@@ -546,6 +596,8 @@ function evaluateRenderProp(element, render, props, state) {
546
596
  // so we can throw before with custom message.
547
597
  if (process.env.NODE_ENV !== 'production') {
548
598
  if (! /*#__PURE__*/React__namespace.isValidElement(newElement)) {
599
+ // TODO: fix mui/no-guarded-throw
600
+ // eslint-disable-next-line mui/no-guarded-throw
549
601
  throw new Error(['Base UI: The `render` prop was provided an invalid React element as `React.isValidElement(render)` is `false`.', 'A valid React element must be provided to the `render` prop because it is cloned with props to replace the default element.', 'https://base-ui.com/r/invalid-render-prop'].join('\n'));
550
602
  }
551
603
  }
@@ -565,8 +617,10 @@ function warnIfRenderPropLooksLikeComponent(renderFn) {
565
617
  if (functionName.length === 0) {
566
618
  return;
567
619
  }
568
- const firstCharacterCode = functionName.charCodeAt(0);
569
- if (firstCharacterCode < 65 || firstCharacterCode > 90) {
620
+ if (!COMPONENT_IDENTIFIER_PATTERN.test(functionName)) {
621
+ return;
622
+ }
623
+ if (!LOWERCASE_CHARACTER_PATTERN.test(functionName)) {
570
624
  return;
571
625
  }
572
626
  warn(`The \`render\` prop received a function named \`${functionName}\` that starts with an uppercase letter.`, 'This usually means a React component was passed directly as `render={Component}`.', 'Base UI calls `render` as a plain function, which can break the Rules of Hooks during reconciliation.', 'If this is an intentional render callback, rename it to start with a lowercase letter.', 'Use `render={<Component />}` or `render={(props) => <Component {...props} />}` instead.', 'https://base-ui.com/r/invalid-render-prop');
@@ -599,3 +653,4 @@ exports.mergeProps = mergeProps;
599
653
  exports.useMergedRefs = useMergedRefs;
600
654
  exports.useRefWithInit = useRefWithInit;
601
655
  exports.useRenderElement = useRenderElement;
656
+ exports.warn = warn;