@arun-dev/headless 4.10.0 → 4.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -40,6 +40,7 @@ __export(index_parts_exports, {
40
40
  List: () => ComboboxList,
41
41
  Popup: () => ComboboxPopup,
42
42
  Root: () => ComboboxRoot,
43
+ Separator: () => ComboboxSeparator,
43
44
  Status: () => ComboboxStatus,
44
45
  Trigger: () => ComboboxTrigger,
45
46
  Value: () => ComboboxValue
@@ -341,7 +342,7 @@ function ComboboxRoot({
341
342
  (0, import_react3.useEffect)(() => {
342
343
  if (lastSelectedKeyRef.current === selectedKey) return;
343
344
  lastSelectedKeyRef.current = selectedKey;
344
- setInputValueState(selectedLabel);
345
+ setInputValue(selectedLabel, "value-change");
345
346
  }, [selectedKey]);
346
347
  const [chips, setChips] = (0, import_react3.useState)([]);
347
348
  const registerChip = (0, import_react3.useCallback)((entry) => {
@@ -831,7 +832,8 @@ function ComboboxClear({ className, children, render, ...rest }) {
831
832
  type: "button",
832
833
  tabIndex: -1,
833
834
  disabled: disabled || void 0,
834
- ...comboboxClearDataAttributes(selectedItems.length > 0),
835
+ // Nothing to clear while disabled: a disabled field hides it, as it has no effect.
836
+ ...comboboxClearDataAttributes(!disabled && selectedItems.length > 0),
835
837
  className,
836
838
  children,
837
839
  ref: elementRef,
@@ -1085,6 +1087,17 @@ function ComboboxGroupLabel({
1085
1087
  });
1086
1088
  }
1087
1089
 
1090
+ // src/combobox/ComboboxSeparator.tsx
1091
+ function ComboboxSeparator({ className, render, ...rest }) {
1092
+ useComboboxRootContext("Separator");
1093
+ return useRender({
1094
+ render,
1095
+ defaultTagName: "div",
1096
+ props: { "aria-hidden": true, className },
1097
+ consumerProps: rest
1098
+ });
1099
+ }
1100
+
1088
1101
  // src/combobox/ComboboxEmpty.tsx
1089
1102
  function ComboboxEmpty({ className, children, render, ...rest }) {
1090
1103
  const { filteredItems, loading } = useComboboxRootContext("Empty");
@@ -3,7 +3,9 @@ import { ReactNode, ReactElement, Ref, ComponentPropsWithRef } from 'react';
3
3
  import { A as AnchorSide, a as AnchorAlign } from '../anchoring-Btxi0QwC.cjs';
4
4
 
5
5
  /** Why a value, the input's text or the open state changed — the second callback argument. */
6
- type ComboboxChangeReason = 'input' | 'input-press' | 'trigger-press' | 'item-press' | 'keyboard' | 'clear' | 'chip-remove' | 'escape' | 'outside-press' | 'blur' | 'form-reset';
6
+ type ComboboxChangeReason = 'input' | 'input-press' | 'trigger-press' | 'item-press' | 'keyboard' | 'clear' | 'chip-remove' | 'escape' | 'outside-press' | 'blur' | 'form-reset'
7
+ /** The input's text followed a change of `value` — the parent's, not the user's. */
8
+ | 'value-change';
7
9
  type ComboboxChangeDetails = {
8
10
  reason: ComboboxChangeReason;
9
11
  };
@@ -182,7 +184,8 @@ type ComboboxClearProps = ComboboxClearOwnProps & Omit<ComponentPropsWithRef<'bu
182
184
  * Empties the input and the selection — every selected item, with `multiple`. Give it an
183
185
  * `aria-label`.
184
186
  *
185
- * `data-visible` is present while there is a selection to clear; style it hidden otherwise.
187
+ * `data-visible` is present while there is a selection to clear and the Root is not disabled;
188
+ * style it hidden otherwise.
186
189
  * Out of the tab order, and a press keeps focus in the input.
187
190
  */
188
191
  declare function ComboboxClear({ className, children, render, ...rest }: ComboboxClearProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
@@ -285,6 +288,23 @@ type ComboboxGroupLabelProps = ComboboxGroupLabelOwnProps & Omit<ComponentPropsW
285
288
  */
286
289
  declare function ComboboxGroupLabel({ className, children, render, ...rest }: ComboboxGroupLabelProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
287
290
 
291
+ type ComboboxSeparatorOwnProps = {
292
+ /** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
293
+ render?: ReactElement;
294
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
295
+ ref?: Ref<HTMLElement>;
296
+ };
297
+ type ComboboxSeparatorProps = ComboboxSeparatorOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof ComboboxSeparatorOwnProps>;
298
+ /**
299
+ * A line between items or groups in the List, as an `<hr>` in a `<select>`. The arrow keys pass
300
+ * it.
301
+ *
302
+ * Visual only: `aria-hidden`, with no `role="separator"`. A listbox may own only options and
303
+ * groups, so a separator role inside one fails `aria-required-children`; a Group, named by its
304
+ * label, is what tells a screen reader where one set ends.
305
+ */
306
+ declare function ComboboxSeparator({ className, render, ...rest }: ComboboxSeparatorProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
307
+
288
308
  type ComboboxEmptyOwnProps = {
289
309
  /** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
290
310
  render?: ReactElement;
@@ -362,7 +382,7 @@ type ComboboxValueProps<T = unknown> = {
362
382
  declare function ComboboxValue<T = unknown>({ children }: ComboboxValueProps<T>): react.JSX.Element;
363
383
 
364
384
  declare namespace index_parts {
365
- export { ComboboxChip as Chip, ComboboxChipRemove as ChipRemove, ComboboxClear as Clear, ComboboxEmpty as Empty, ComboboxGroup as Group, ComboboxGroupLabel as GroupLabel, ComboboxInput as Input, ComboboxInputGroup as InputGroup, ComboboxItem as Item, ComboboxList as List, ComboboxPopup as Popup, ComboboxRoot as Root, ComboboxStatus as Status, ComboboxTrigger as Trigger, ComboboxValue as Value };
385
+ export { ComboboxChip as Chip, ComboboxChipRemove as ChipRemove, ComboboxClear as Clear, ComboboxEmpty as Empty, ComboboxGroup as Group, ComboboxGroupLabel as GroupLabel, ComboboxInput as Input, ComboboxInputGroup as InputGroup, ComboboxItem as Item, ComboboxList as List, ComboboxPopup as Popup, ComboboxRoot as Root, ComboboxSeparator as Separator, ComboboxStatus as Status, ComboboxTrigger as Trigger, ComboboxValue as Value };
366
386
  }
367
387
 
368
388
  /**
@@ -373,4 +393,4 @@ declare namespace index_parts {
373
393
  */
374
394
  declare function defaultFilter<T>(items: readonly T[], query: string, itemToString: (item: T) => string): T[];
375
395
 
376
- export { index_parts as Combobox, type ComboboxAlign, type ComboboxChangeDetails, type ComboboxChangeReason, type ComboboxChipProps, type ComboboxChipRemoveProps, type ComboboxClearProps, type ComboboxEmptyProps, type ComboboxFilter, type ComboboxGroup$1 as ComboboxGroup, type ComboboxGroupLabelProps, type ComboboxGroupProps, type ComboboxHighlightDetails, type ComboboxHighlightReason, type ComboboxInputGroupProps, type ComboboxInputProps, type ComboboxItemProps, type ComboboxListProps, type ComboboxPopupProps, type ComboboxRootProps, type ComboboxSide, type ComboboxState, type ComboboxStatusProps, type ComboboxTriggerProps, type ComboboxValue$1 as ComboboxValue, type ComboboxValueProps, defaultFilter };
396
+ export { index_parts as Combobox, type ComboboxAlign, type ComboboxChangeDetails, type ComboboxChangeReason, type ComboboxChipProps, type ComboboxChipRemoveProps, type ComboboxClearProps, type ComboboxEmptyProps, type ComboboxFilter, type ComboboxGroup$1 as ComboboxGroup, type ComboboxGroupLabelProps, type ComboboxGroupProps, type ComboboxHighlightDetails, type ComboboxHighlightReason, type ComboboxInputGroupProps, type ComboboxInputProps, type ComboboxItemProps, type ComboboxListProps, type ComboboxPopupProps, type ComboboxRootProps, type ComboboxSeparatorProps, type ComboboxSide, type ComboboxState, type ComboboxStatusProps, type ComboboxTriggerProps, type ComboboxValue$1 as ComboboxValue, type ComboboxValueProps, defaultFilter };
@@ -3,7 +3,9 @@ import { ReactNode, ReactElement, Ref, ComponentPropsWithRef } from 'react';
3
3
  import { A as AnchorSide, a as AnchorAlign } from '../anchoring-Btxi0QwC.js';
4
4
 
5
5
  /** Why a value, the input's text or the open state changed — the second callback argument. */
6
- type ComboboxChangeReason = 'input' | 'input-press' | 'trigger-press' | 'item-press' | 'keyboard' | 'clear' | 'chip-remove' | 'escape' | 'outside-press' | 'blur' | 'form-reset';
6
+ type ComboboxChangeReason = 'input' | 'input-press' | 'trigger-press' | 'item-press' | 'keyboard' | 'clear' | 'chip-remove' | 'escape' | 'outside-press' | 'blur' | 'form-reset'
7
+ /** The input's text followed a change of `value` — the parent's, not the user's. */
8
+ | 'value-change';
7
9
  type ComboboxChangeDetails = {
8
10
  reason: ComboboxChangeReason;
9
11
  };
@@ -182,7 +184,8 @@ type ComboboxClearProps = ComboboxClearOwnProps & Omit<ComponentPropsWithRef<'bu
182
184
  * Empties the input and the selection — every selected item, with `multiple`. Give it an
183
185
  * `aria-label`.
184
186
  *
185
- * `data-visible` is present while there is a selection to clear; style it hidden otherwise.
187
+ * `data-visible` is present while there is a selection to clear and the Root is not disabled;
188
+ * style it hidden otherwise.
186
189
  * Out of the tab order, and a press keeps focus in the input.
187
190
  */
188
191
  declare function ComboboxClear({ className, children, render, ...rest }: ComboboxClearProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
@@ -285,6 +288,23 @@ type ComboboxGroupLabelProps = ComboboxGroupLabelOwnProps & Omit<ComponentPropsW
285
288
  */
286
289
  declare function ComboboxGroupLabel({ className, children, render, ...rest }: ComboboxGroupLabelProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
287
290
 
291
+ type ComboboxSeparatorOwnProps = {
292
+ /** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
293
+ render?: ReactElement;
294
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
295
+ ref?: Ref<HTMLElement>;
296
+ };
297
+ type ComboboxSeparatorProps = ComboboxSeparatorOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof ComboboxSeparatorOwnProps>;
298
+ /**
299
+ * A line between items or groups in the List, as an `<hr>` in a `<select>`. The arrow keys pass
300
+ * it.
301
+ *
302
+ * Visual only: `aria-hidden`, with no `role="separator"`. A listbox may own only options and
303
+ * groups, so a separator role inside one fails `aria-required-children`; a Group, named by its
304
+ * label, is what tells a screen reader where one set ends.
305
+ */
306
+ declare function ComboboxSeparator({ className, render, ...rest }: ComboboxSeparatorProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
307
+
288
308
  type ComboboxEmptyOwnProps = {
289
309
  /** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
290
310
  render?: ReactElement;
@@ -362,7 +382,7 @@ type ComboboxValueProps<T = unknown> = {
362
382
  declare function ComboboxValue<T = unknown>({ children }: ComboboxValueProps<T>): react.JSX.Element;
363
383
 
364
384
  declare namespace index_parts {
365
- export { ComboboxChip as Chip, ComboboxChipRemove as ChipRemove, ComboboxClear as Clear, ComboboxEmpty as Empty, ComboboxGroup as Group, ComboboxGroupLabel as GroupLabel, ComboboxInput as Input, ComboboxInputGroup as InputGroup, ComboboxItem as Item, ComboboxList as List, ComboboxPopup as Popup, ComboboxRoot as Root, ComboboxStatus as Status, ComboboxTrigger as Trigger, ComboboxValue as Value };
385
+ export { ComboboxChip as Chip, ComboboxChipRemove as ChipRemove, ComboboxClear as Clear, ComboboxEmpty as Empty, ComboboxGroup as Group, ComboboxGroupLabel as GroupLabel, ComboboxInput as Input, ComboboxInputGroup as InputGroup, ComboboxItem as Item, ComboboxList as List, ComboboxPopup as Popup, ComboboxRoot as Root, ComboboxSeparator as Separator, ComboboxStatus as Status, ComboboxTrigger as Trigger, ComboboxValue as Value };
366
386
  }
367
387
 
368
388
  /**
@@ -373,4 +393,4 @@ declare namespace index_parts {
373
393
  */
374
394
  declare function defaultFilter<T>(items: readonly T[], query: string, itemToString: (item: T) => string): T[];
375
395
 
376
- export { index_parts as Combobox, type ComboboxAlign, type ComboboxChangeDetails, type ComboboxChangeReason, type ComboboxChipProps, type ComboboxChipRemoveProps, type ComboboxClearProps, type ComboboxEmptyProps, type ComboboxFilter, type ComboboxGroup$1 as ComboboxGroup, type ComboboxGroupLabelProps, type ComboboxGroupProps, type ComboboxHighlightDetails, type ComboboxHighlightReason, type ComboboxInputGroupProps, type ComboboxInputProps, type ComboboxItemProps, type ComboboxListProps, type ComboboxPopupProps, type ComboboxRootProps, type ComboboxSide, type ComboboxState, type ComboboxStatusProps, type ComboboxTriggerProps, type ComboboxValue$1 as ComboboxValue, type ComboboxValueProps, defaultFilter };
396
+ export { index_parts as Combobox, type ComboboxAlign, type ComboboxChangeDetails, type ComboboxChangeReason, type ComboboxChipProps, type ComboboxChipRemoveProps, type ComboboxClearProps, type ComboboxEmptyProps, type ComboboxFilter, type ComboboxGroup$1 as ComboboxGroup, type ComboboxGroupLabelProps, type ComboboxGroupProps, type ComboboxHighlightDetails, type ComboboxHighlightReason, type ComboboxInputGroupProps, type ComboboxInputProps, type ComboboxItemProps, type ComboboxListProps, type ComboboxPopupProps, type ComboboxRootProps, type ComboboxSeparatorProps, type ComboboxSide, type ComboboxState, type ComboboxStatusProps, type ComboboxTriggerProps, type ComboboxValue$1 as ComboboxValue, type ComboboxValueProps, defaultFilter };
@@ -30,6 +30,7 @@ __export(index_parts_exports, {
30
30
  List: () => ComboboxList,
31
31
  Popup: () => ComboboxPopup,
32
32
  Root: () => ComboboxRoot,
33
+ Separator: () => ComboboxSeparator,
33
34
  Status: () => ComboboxStatus,
34
35
  Trigger: () => ComboboxTrigger,
35
36
  Value: () => ComboboxValue
@@ -247,7 +248,7 @@ function ComboboxRoot({
247
248
  useEffect(() => {
248
249
  if (lastSelectedKeyRef.current === selectedKey) return;
249
250
  lastSelectedKeyRef.current = selectedKey;
250
- setInputValueState(selectedLabel);
251
+ setInputValue(selectedLabel, "value-change");
251
252
  }, [selectedKey]);
252
253
  const [chips, setChips] = useState([]);
253
254
  const registerChip = useCallback((entry) => {
@@ -648,7 +649,8 @@ function ComboboxClear({ className, children, render, ...rest }) {
648
649
  type: "button",
649
650
  tabIndex: -1,
650
651
  disabled: disabled || void 0,
651
- ...comboboxClearDataAttributes(selectedItems.length > 0),
652
+ // Nothing to clear while disabled: a disabled field hides it, as it has no effect.
653
+ ...comboboxClearDataAttributes(!disabled && selectedItems.length > 0),
652
654
  className,
653
655
  children,
654
656
  ref: elementRef,
@@ -902,6 +904,17 @@ function ComboboxGroupLabel({
902
904
  });
903
905
  }
904
906
 
907
+ // src/combobox/ComboboxSeparator.tsx
908
+ function ComboboxSeparator({ className, render, ...rest }) {
909
+ useComboboxRootContext("Separator");
910
+ return useRender({
911
+ render,
912
+ defaultTagName: "div",
913
+ props: { "aria-hidden": true, className },
914
+ consumerProps: rest
915
+ });
916
+ }
917
+
905
918
  // src/combobox/ComboboxEmpty.tsx
906
919
  function ComboboxEmpty({ className, children, render, ...rest }) {
907
920
  const { filteredItems, loading } = useComboboxRootContext("Empty");
@@ -0,0 +1,287 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/field/index.ts
21
+ var field_exports = {};
22
+ __export(field_exports, {
23
+ Field: () => index_parts_exports
24
+ });
25
+ module.exports = __toCommonJS(field_exports);
26
+
27
+ // src/field/index.parts.ts
28
+ var index_parts_exports = {};
29
+ __export(index_parts_exports, {
30
+ Control: () => FieldControl,
31
+ Description: () => FieldDescription,
32
+ Error: () => FieldError,
33
+ Label: () => FieldLabel,
34
+ Root: () => FieldRoot
35
+ });
36
+
37
+ // src/field/FieldRoot.tsx
38
+ var import_react3 = require("react");
39
+
40
+ // src/core/useRender.ts
41
+ var import_react = require("react");
42
+
43
+ // src/core/mergeProps.ts
44
+ function isEventHandler(key) {
45
+ return key.length > 2 && key.startsWith("on") && key[2] === key[2]?.toUpperCase();
46
+ }
47
+ function mergeRefs(...refs) {
48
+ return (node) => {
49
+ const cleanups = refs.map((ref) => {
50
+ if (typeof ref === "function") {
51
+ const result = ref(node);
52
+ return typeof result === "function" ? result : () => ref(null);
53
+ }
54
+ if (ref && typeof ref === "object") {
55
+ ref.current = node;
56
+ return () => {
57
+ ref.current = null;
58
+ };
59
+ }
60
+ return void 0;
61
+ });
62
+ return () => {
63
+ cleanups.forEach((cleanup) => {
64
+ if (typeof cleanup === "function") cleanup();
65
+ });
66
+ };
67
+ };
68
+ }
69
+ function isSyntheticEvent(event) {
70
+ return typeof event === "object" && event !== null && "nativeEvent" in event;
71
+ }
72
+ function makeEventPreventable(event) {
73
+ if (Object.prototype.hasOwnProperty.call(event, "preventComponentHandler")) return;
74
+ const target = event;
75
+ target.preventComponentHandler = () => {
76
+ target.componentHandlerPrevented = true;
77
+ };
78
+ }
79
+ function isComponentHandlerPrevented(event) {
80
+ return typeof event === "object" && event !== null && event.componentHandlerPrevented === true;
81
+ }
82
+ function chainHandlers(earlier, value) {
83
+ return (...args) => {
84
+ const event = args[0];
85
+ if (isSyntheticEvent(event)) makeEventPreventable(event);
86
+ value(...args);
87
+ if (isComponentHandlerPrevented(event)) return;
88
+ earlier(...args);
89
+ };
90
+ }
91
+ function mergeProps(...objects) {
92
+ const merged = {};
93
+ for (const props of objects) {
94
+ if (!props) continue;
95
+ for (const key of Object.keys(props)) {
96
+ const value = props[key];
97
+ if (value === void 0) continue;
98
+ if (isEventHandler(key) && typeof value === "function") {
99
+ const earlier = merged[key];
100
+ merged[key] = typeof earlier === "function" ? chainHandlers(earlier, value) : value;
101
+ } else if (key === "className") {
102
+ merged.className = merged.className ? `${String(merged.className)} ${String(value)}` : value;
103
+ } else if (key === "style") {
104
+ merged.style = { ...merged.style, ...value };
105
+ } else if (key === "ref") {
106
+ merged.ref = merged.ref ? mergeRefs(merged.ref, value) : value;
107
+ } else {
108
+ merged[key] = value;
109
+ }
110
+ }
111
+ }
112
+ return merged;
113
+ }
114
+
115
+ // src/core/useRender.ts
116
+ function useRender({
117
+ render,
118
+ defaultTagName,
119
+ props,
120
+ consumerProps
121
+ }) {
122
+ const merged = mergeProps(props, consumerProps);
123
+ if ((0, import_react.isValidElement)(render)) {
124
+ return (0, import_react.cloneElement)(render, mergeProps(merged, render.props));
125
+ }
126
+ return (0, import_react.createElement)(defaultTagName, merged);
127
+ }
128
+
129
+ // src/field/FieldRootContext.ts
130
+ var import_react2 = require("react");
131
+ var FieldRootContext = (0, import_react2.createContext)(null);
132
+ function useFieldRootContext(part) {
133
+ const context = (0, import_react2.useContext)(FieldRootContext);
134
+ if (context === null) {
135
+ throw new Error(`<Field.${part}> must be rendered inside <Field.Root>.`);
136
+ }
137
+ return context;
138
+ }
139
+ function fieldDataAttributes({ invalid, disabled, required }) {
140
+ return {
141
+ "data-invalid": invalid ? "" : void 0,
142
+ "data-disabled": disabled ? "" : void 0,
143
+ "data-required": required ? "" : void 0
144
+ };
145
+ }
146
+
147
+ // src/field/FieldRoot.tsx
148
+ var import_jsx_runtime = require("react/jsx-runtime");
149
+ function usePresence() {
150
+ const [count, setCount] = (0, import_react3.useState)(0);
151
+ const register = (0, import_react3.useCallback)(() => {
152
+ setCount((n) => n + 1);
153
+ return () => setCount((n) => n - 1);
154
+ }, []);
155
+ return [count > 0, register];
156
+ }
157
+ function FieldRoot({
158
+ invalid = false,
159
+ disabled = false,
160
+ required = false,
161
+ className,
162
+ children,
163
+ render,
164
+ ...rest
165
+ }) {
166
+ const id = (0, import_react3.useId)();
167
+ const [hasDescription, registerDescription] = usePresence();
168
+ const [hasError, registerError] = usePresence();
169
+ const context = (0, import_react3.useMemo)(
170
+ () => ({
171
+ invalid,
172
+ disabled,
173
+ required,
174
+ controlId: `${id}-control`,
175
+ descriptionId: `${id}-description`,
176
+ errorId: `${id}-error`,
177
+ hasDescription,
178
+ hasError,
179
+ registerDescription,
180
+ registerError
181
+ }),
182
+ [invalid, disabled, required, id, hasDescription, hasError, registerDescription, registerError]
183
+ );
184
+ const element = useRender({
185
+ render,
186
+ defaultTagName: "div",
187
+ props: { ...fieldDataAttributes({ invalid, disabled, required }), className, children },
188
+ consumerProps: rest
189
+ });
190
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FieldRootContext.Provider, { value: context, children: element });
191
+ }
192
+
193
+ // src/field/FieldLabel.tsx
194
+ function FieldLabel({ className, children, render, ...rest }) {
195
+ const { controlId, ...state } = useFieldRootContext("Label");
196
+ return useRender({
197
+ render,
198
+ defaultTagName: "label",
199
+ props: { htmlFor: controlId, ...fieldDataAttributes(state), className, children },
200
+ consumerProps: rest
201
+ });
202
+ }
203
+
204
+ // src/field/FieldControl.tsx
205
+ function FieldControl({
206
+ render,
207
+ "aria-describedby": describedBy,
208
+ ...rest
209
+ }) {
210
+ const { controlId, descriptionId, errorId, hasDescription, hasError, ...state } = useFieldRootContext("Control");
211
+ const describedByIds = [
212
+ hasDescription ? descriptionId : void 0,
213
+ hasError ? errorId : void 0,
214
+ describedBy
215
+ ].filter(Boolean).join(" ");
216
+ return useRender({
217
+ render,
218
+ defaultTagName: "input",
219
+ props: {
220
+ id: controlId,
221
+ "aria-describedby": describedByIds || void 0,
222
+ "aria-invalid": state.invalid || void 0,
223
+ disabled: state.disabled || void 0,
224
+ required: state.required || void 0,
225
+ ...fieldDataAttributes(state)
226
+ },
227
+ consumerProps: rest
228
+ });
229
+ }
230
+
231
+ // src/field/FieldDescription.tsx
232
+ var import_react4 = require("react");
233
+ function FieldDescription({ className, children, render, ...rest }) {
234
+ const context = useFieldRootContext("Description");
235
+ const { descriptionId, registerDescription, invalid, disabled, required } = context;
236
+ (0, import_react4.useLayoutEffect)(registerDescription, [registerDescription]);
237
+ return useRender({
238
+ render,
239
+ defaultTagName: "p",
240
+ props: {
241
+ id: descriptionId,
242
+ ...fieldDataAttributes({ invalid, disabled, required }),
243
+ className,
244
+ children
245
+ },
246
+ consumerProps: rest
247
+ });
248
+ }
249
+
250
+ // src/field/FieldError.tsx
251
+ var import_react5 = require("react");
252
+ var import_jsx_runtime2 = require("react/jsx-runtime");
253
+ function FieldError({ className, children, render, ...rest }) {
254
+ const context = useFieldRootContext("Error");
255
+ const { invalid, disabled, required } = context;
256
+ if (!invalid) return null;
257
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
258
+ ShownError,
259
+ {
260
+ className,
261
+ render,
262
+ state: { invalid, disabled, required },
263
+ ...rest,
264
+ children
265
+ }
266
+ );
267
+ }
268
+ function ShownError({
269
+ className,
270
+ children,
271
+ render,
272
+ state,
273
+ ...rest
274
+ }) {
275
+ const { errorId, registerError } = useFieldRootContext("Error");
276
+ (0, import_react5.useLayoutEffect)(registerError, [registerError]);
277
+ return useRender({
278
+ render,
279
+ defaultTagName: "p",
280
+ props: { id: errorId, ...fieldDataAttributes(state), className, children },
281
+ consumerProps: rest
282
+ });
283
+ }
284
+ // Annotate the CommonJS export names for ESM import in node:
285
+ 0 && (module.exports = {
286
+ Field
287
+ });
@@ -0,0 +1,92 @@
1
+ import * as react from 'react';
2
+ import { ReactElement, Ref, ComponentPropsWithRef } from 'react';
3
+
4
+ type FieldRootOwnProps = {
5
+ /** Shows the Error and marks the control `aria-invalid`. Yours to set — Field validates nothing. */
6
+ invalid?: boolean;
7
+ /** Disables the control and marks every part `data-disabled`. */
8
+ disabled?: boolean;
9
+ /** Marks the control `required`, so the browser's own validation reports it. */
10
+ required?: boolean;
11
+ /** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
12
+ render?: ReactElement;
13
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
14
+ ref?: Ref<HTMLElement>;
15
+ };
16
+ type FieldRootProps = FieldRootOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof FieldRootOwnProps>;
17
+ /**
18
+ * One control with its label, description and error (decision 15). Generates their ids and
19
+ * holds `invalid`, `disabled` and `required`; `Control` puts them on the control.
20
+ *
21
+ * Validity is yours: set `invalid` from react-hook-form's `fieldState`, or from what the
22
+ * browser reported. A group of controls under one label is a native `<fieldset>`.
23
+ */
24
+ declare function FieldRoot({ invalid, disabled, required, className, children, render, ...rest }: FieldRootProps): react.JSX.Element;
25
+
26
+ type FieldLabelOwnProps = {
27
+ /** Element to render instead of the default `<label>`. Props and ref are merged onto it. */
28
+ render?: ReactElement;
29
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
30
+ ref?: Ref<HTMLElement>;
31
+ };
32
+ type FieldLabelProps = FieldLabelOwnProps & Omit<ComponentPropsWithRef<'label'>, keyof FieldLabelOwnProps>;
33
+ /** Names the control: a `<label>` whose `htmlFor` is the control's id, so a press focuses it. */
34
+ declare function FieldLabel({ className, children, render, ...rest }: FieldLabelProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
35
+
36
+ type FieldControlOwnProps = {
37
+ /**
38
+ * The control: `<Input />`, `<Select />`, `<Checkbox.Root />`, `<Combobox.Input />` or your
39
+ * own. Defaults to an `<input>`. The Field's `id`, `aria-describedby`, `aria-invalid`,
40
+ * `disabled` and `required` are merged onto it.
41
+ */
42
+ render?: ReactElement;
43
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
44
+ ref?: Ref<HTMLElement>;
45
+ };
46
+ type FieldControlProps = FieldControlOwnProps & Omit<ComponentPropsWithRef<'input'>, keyof FieldControlOwnProps>;
47
+ /**
48
+ * Puts the Field on its control. `render` is the seam: Field does not reach into each control,
49
+ * it hands the control the attributes that tie it to the label, description and error.
50
+ *
51
+ * `aria-describedby` names the Description, then the Error — only those rendered — followed by
52
+ * any of your own. The id is the Field's: an `id` of your own breaks the Label's `htmlFor`.
53
+ */
54
+ declare function FieldControl({ render, 'aria-describedby': describedBy, ...rest }: FieldControlProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
55
+
56
+ type FieldDescriptionOwnProps = {
57
+ /** Element to render instead of the default `<p>`. Props and ref are merged onto it. */
58
+ render?: ReactElement;
59
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
60
+ ref?: Ref<HTMLElement>;
61
+ };
62
+ type FieldDescriptionProps = FieldDescriptionOwnProps & Omit<ComponentPropsWithRef<'p'>, keyof FieldDescriptionOwnProps>;
63
+ /**
64
+ * Help text under the control, read out with it through `aria-describedby`.
65
+ */
66
+ declare function FieldDescription({ className, children, render, ...rest }: FieldDescriptionProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
67
+
68
+ type FieldErrorOwnProps = {
69
+ /** Element to render instead of the default `<p>`. Props and ref are merged onto it. */
70
+ render?: ReactElement;
71
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
72
+ ref?: Ref<HTMLElement>;
73
+ };
74
+ type FieldErrorProps = FieldErrorOwnProps & Omit<ComponentPropsWithRef<'p'>, keyof FieldErrorOwnProps>;
75
+ /**
76
+ * What is wrong, read out with the control through `aria-describedby`. Rendered only while the
77
+ * Root is `invalid`, so it is named only while it is shown.
78
+ */
79
+ declare function FieldError({ className, children, render, ...rest }: FieldErrorProps): react.JSX.Element | null;
80
+
81
+ declare namespace index_parts {
82
+ export { FieldControl as Control, FieldDescription as Description, FieldError as Error, FieldLabel as Label, FieldRoot as Root };
83
+ }
84
+
85
+ /** The state Field.Root shares with its parts, and projects as `data-*` attributes. */
86
+ type FieldState = {
87
+ invalid: boolean;
88
+ disabled: boolean;
89
+ required: boolean;
90
+ };
91
+
92
+ export { index_parts as Field, type FieldControlProps, type FieldDescriptionProps, type FieldErrorProps, type FieldLabelProps, type FieldRootProps, type FieldState };
@@ -0,0 +1,92 @@
1
+ import * as react from 'react';
2
+ import { ReactElement, Ref, ComponentPropsWithRef } from 'react';
3
+
4
+ type FieldRootOwnProps = {
5
+ /** Shows the Error and marks the control `aria-invalid`. Yours to set — Field validates nothing. */
6
+ invalid?: boolean;
7
+ /** Disables the control and marks every part `data-disabled`. */
8
+ disabled?: boolean;
9
+ /** Marks the control `required`, so the browser's own validation reports it. */
10
+ required?: boolean;
11
+ /** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
12
+ render?: ReactElement;
13
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
14
+ ref?: Ref<HTMLElement>;
15
+ };
16
+ type FieldRootProps = FieldRootOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof FieldRootOwnProps>;
17
+ /**
18
+ * One control with its label, description and error (decision 15). Generates their ids and
19
+ * holds `invalid`, `disabled` and `required`; `Control` puts them on the control.
20
+ *
21
+ * Validity is yours: set `invalid` from react-hook-form's `fieldState`, or from what the
22
+ * browser reported. A group of controls under one label is a native `<fieldset>`.
23
+ */
24
+ declare function FieldRoot({ invalid, disabled, required, className, children, render, ...rest }: FieldRootProps): react.JSX.Element;
25
+
26
+ type FieldLabelOwnProps = {
27
+ /** Element to render instead of the default `<label>`. Props and ref are merged onto it. */
28
+ render?: ReactElement;
29
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
30
+ ref?: Ref<HTMLElement>;
31
+ };
32
+ type FieldLabelProps = FieldLabelOwnProps & Omit<ComponentPropsWithRef<'label'>, keyof FieldLabelOwnProps>;
33
+ /** Names the control: a `<label>` whose `htmlFor` is the control's id, so a press focuses it. */
34
+ declare function FieldLabel({ className, children, render, ...rest }: FieldLabelProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
35
+
36
+ type FieldControlOwnProps = {
37
+ /**
38
+ * The control: `<Input />`, `<Select />`, `<Checkbox.Root />`, `<Combobox.Input />` or your
39
+ * own. Defaults to an `<input>`. The Field's `id`, `aria-describedby`, `aria-invalid`,
40
+ * `disabled` and `required` are merged onto it.
41
+ */
42
+ render?: ReactElement;
43
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
44
+ ref?: Ref<HTMLElement>;
45
+ };
46
+ type FieldControlProps = FieldControlOwnProps & Omit<ComponentPropsWithRef<'input'>, keyof FieldControlOwnProps>;
47
+ /**
48
+ * Puts the Field on its control. `render` is the seam: Field does not reach into each control,
49
+ * it hands the control the attributes that tie it to the label, description and error.
50
+ *
51
+ * `aria-describedby` names the Description, then the Error — only those rendered — followed by
52
+ * any of your own. The id is the Field's: an `id` of your own breaks the Label's `htmlFor`.
53
+ */
54
+ declare function FieldControl({ render, 'aria-describedby': describedBy, ...rest }: FieldControlProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
55
+
56
+ type FieldDescriptionOwnProps = {
57
+ /** Element to render instead of the default `<p>`. Props and ref are merged onto it. */
58
+ render?: ReactElement;
59
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
60
+ ref?: Ref<HTMLElement>;
61
+ };
62
+ type FieldDescriptionProps = FieldDescriptionOwnProps & Omit<ComponentPropsWithRef<'p'>, keyof FieldDescriptionOwnProps>;
63
+ /**
64
+ * Help text under the control, read out with it through `aria-describedby`.
65
+ */
66
+ declare function FieldDescription({ className, children, render, ...rest }: FieldDescriptionProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
67
+
68
+ type FieldErrorOwnProps = {
69
+ /** Element to render instead of the default `<p>`. Props and ref are merged onto it. */
70
+ render?: ReactElement;
71
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
72
+ ref?: Ref<HTMLElement>;
73
+ };
74
+ type FieldErrorProps = FieldErrorOwnProps & Omit<ComponentPropsWithRef<'p'>, keyof FieldErrorOwnProps>;
75
+ /**
76
+ * What is wrong, read out with the control through `aria-describedby`. Rendered only while the
77
+ * Root is `invalid`, so it is named only while it is shown.
78
+ */
79
+ declare function FieldError({ className, children, render, ...rest }: FieldErrorProps): react.JSX.Element | null;
80
+
81
+ declare namespace index_parts {
82
+ export { FieldControl as Control, FieldDescription as Description, FieldError as Error, FieldLabel as Label, FieldRoot as Root };
83
+ }
84
+
85
+ /** The state Field.Root shares with its parts, and projects as `data-*` attributes. */
86
+ type FieldState = {
87
+ invalid: boolean;
88
+ disabled: boolean;
89
+ required: boolean;
90
+ };
91
+
92
+ export { index_parts as Field, type FieldControlProps, type FieldDescriptionProps, type FieldErrorProps, type FieldLabelProps, type FieldRootProps, type FieldState };
@@ -0,0 +1,176 @@
1
+ import {
2
+ __export,
3
+ useRender
4
+ } from "../chunk-OQTWWZ4B.js";
5
+
6
+ // src/field/index.parts.ts
7
+ var index_parts_exports = {};
8
+ __export(index_parts_exports, {
9
+ Control: () => FieldControl,
10
+ Description: () => FieldDescription,
11
+ Error: () => FieldError,
12
+ Label: () => FieldLabel,
13
+ Root: () => FieldRoot
14
+ });
15
+
16
+ // src/field/FieldRoot.tsx
17
+ import { useCallback, useId, useMemo, useState } from "react";
18
+
19
+ // src/field/FieldRootContext.ts
20
+ import { createContext, useContext } from "react";
21
+ var FieldRootContext = createContext(null);
22
+ function useFieldRootContext(part) {
23
+ const context = useContext(FieldRootContext);
24
+ if (context === null) {
25
+ throw new Error(`<Field.${part}> must be rendered inside <Field.Root>.`);
26
+ }
27
+ return context;
28
+ }
29
+ function fieldDataAttributes({ invalid, disabled, required }) {
30
+ return {
31
+ "data-invalid": invalid ? "" : void 0,
32
+ "data-disabled": disabled ? "" : void 0,
33
+ "data-required": required ? "" : void 0
34
+ };
35
+ }
36
+
37
+ // src/field/FieldRoot.tsx
38
+ import { jsx } from "react/jsx-runtime";
39
+ function usePresence() {
40
+ const [count, setCount] = useState(0);
41
+ const register = useCallback(() => {
42
+ setCount((n) => n + 1);
43
+ return () => setCount((n) => n - 1);
44
+ }, []);
45
+ return [count > 0, register];
46
+ }
47
+ function FieldRoot({
48
+ invalid = false,
49
+ disabled = false,
50
+ required = false,
51
+ className,
52
+ children,
53
+ render,
54
+ ...rest
55
+ }) {
56
+ const id = useId();
57
+ const [hasDescription, registerDescription] = usePresence();
58
+ const [hasError, registerError] = usePresence();
59
+ const context = useMemo(
60
+ () => ({
61
+ invalid,
62
+ disabled,
63
+ required,
64
+ controlId: `${id}-control`,
65
+ descriptionId: `${id}-description`,
66
+ errorId: `${id}-error`,
67
+ hasDescription,
68
+ hasError,
69
+ registerDescription,
70
+ registerError
71
+ }),
72
+ [invalid, disabled, required, id, hasDescription, hasError, registerDescription, registerError]
73
+ );
74
+ const element = useRender({
75
+ render,
76
+ defaultTagName: "div",
77
+ props: { ...fieldDataAttributes({ invalid, disabled, required }), className, children },
78
+ consumerProps: rest
79
+ });
80
+ return /* @__PURE__ */ jsx(FieldRootContext.Provider, { value: context, children: element });
81
+ }
82
+
83
+ // src/field/FieldLabel.tsx
84
+ function FieldLabel({ className, children, render, ...rest }) {
85
+ const { controlId, ...state } = useFieldRootContext("Label");
86
+ return useRender({
87
+ render,
88
+ defaultTagName: "label",
89
+ props: { htmlFor: controlId, ...fieldDataAttributes(state), className, children },
90
+ consumerProps: rest
91
+ });
92
+ }
93
+
94
+ // src/field/FieldControl.tsx
95
+ function FieldControl({
96
+ render,
97
+ "aria-describedby": describedBy,
98
+ ...rest
99
+ }) {
100
+ const { controlId, descriptionId, errorId, hasDescription, hasError, ...state } = useFieldRootContext("Control");
101
+ const describedByIds = [
102
+ hasDescription ? descriptionId : void 0,
103
+ hasError ? errorId : void 0,
104
+ describedBy
105
+ ].filter(Boolean).join(" ");
106
+ return useRender({
107
+ render,
108
+ defaultTagName: "input",
109
+ props: {
110
+ id: controlId,
111
+ "aria-describedby": describedByIds || void 0,
112
+ "aria-invalid": state.invalid || void 0,
113
+ disabled: state.disabled || void 0,
114
+ required: state.required || void 0,
115
+ ...fieldDataAttributes(state)
116
+ },
117
+ consumerProps: rest
118
+ });
119
+ }
120
+
121
+ // src/field/FieldDescription.tsx
122
+ import { useLayoutEffect } from "react";
123
+ function FieldDescription({ className, children, render, ...rest }) {
124
+ const context = useFieldRootContext("Description");
125
+ const { descriptionId, registerDescription, invalid, disabled, required } = context;
126
+ useLayoutEffect(registerDescription, [registerDescription]);
127
+ return useRender({
128
+ render,
129
+ defaultTagName: "p",
130
+ props: {
131
+ id: descriptionId,
132
+ ...fieldDataAttributes({ invalid, disabled, required }),
133
+ className,
134
+ children
135
+ },
136
+ consumerProps: rest
137
+ });
138
+ }
139
+
140
+ // src/field/FieldError.tsx
141
+ import { useLayoutEffect as useLayoutEffect2 } from "react";
142
+ import { jsx as jsx2 } from "react/jsx-runtime";
143
+ function FieldError({ className, children, render, ...rest }) {
144
+ const context = useFieldRootContext("Error");
145
+ const { invalid, disabled, required } = context;
146
+ if (!invalid) return null;
147
+ return /* @__PURE__ */ jsx2(
148
+ ShownError,
149
+ {
150
+ className,
151
+ render,
152
+ state: { invalid, disabled, required },
153
+ ...rest,
154
+ children
155
+ }
156
+ );
157
+ }
158
+ function ShownError({
159
+ className,
160
+ children,
161
+ render,
162
+ state,
163
+ ...rest
164
+ }) {
165
+ const { errorId, registerError } = useFieldRootContext("Error");
166
+ useLayoutEffect2(registerError, [registerError]);
167
+ return useRender({
168
+ render,
169
+ defaultTagName: "p",
170
+ props: { id: errorId, ...fieldDataAttributes(state), className, children },
171
+ consumerProps: rest
172
+ });
173
+ }
174
+ export {
175
+ index_parts_exports as Field
176
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arun-dev/headless",
3
- "version": "4.10.0",
3
+ "version": "4.12.0",
4
4
  "description": "Unstyled React behaviour primitives — render engine, controlled/uncontrolled state, and data-* state projection. Ships no CSS and no class names.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -104,6 +104,16 @@
104
104
  "default": "./dist/radio-group/index.cjs"
105
105
  }
106
106
  },
107
+ "./field": {
108
+ "import": {
109
+ "types": "./dist/field/index.d.ts",
110
+ "default": "./dist/field/index.js"
111
+ },
112
+ "require": {
113
+ "types": "./dist/field/index.d.cts",
114
+ "default": "./dist/field/index.cjs"
115
+ }
116
+ },
107
117
  "./switch": {
108
118
  "import": {
109
119
  "types": "./dist/switch/index.d.ts",