@arun-dev/headless 4.1.0 → 4.2.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.
@@ -0,0 +1,298 @@
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/radio-group/index.ts
21
+ var radio_group_exports = {};
22
+ __export(radio_group_exports, {
23
+ RadioGroup: () => index_parts_exports
24
+ });
25
+ module.exports = __toCommonJS(radio_group_exports);
26
+
27
+ // src/radio-group/index.parts.ts
28
+ var index_parts_exports = {};
29
+ __export(index_parts_exports, {
30
+ Item: () => RadioGroupItem,
31
+ Root: () => RadioGroupRoot
32
+ });
33
+
34
+ // src/radio-group/RadioGroupRoot.tsx
35
+ var import_react4 = require("react");
36
+
37
+ // src/core/useControlled.ts
38
+ var import_react = require("react");
39
+ function useControlled({
40
+ controlled,
41
+ default: defaultValue,
42
+ name = "Component",
43
+ state = "value"
44
+ }) {
45
+ const { current: isControlled } = (0, import_react.useRef)(controlled !== void 0);
46
+ const [uncontrolled, setUncontrolled] = (0, import_react.useState)(defaultValue);
47
+ const defaultRef = (0, import_react.useRef)(defaultValue);
48
+ const value = isControlled ? controlled : uncontrolled;
49
+ if (process.env.NODE_ENV !== "production") {
50
+ if (isControlled !== (controlled !== void 0)) {
51
+ console.error(
52
+ `${name}: cannot switch between controlled and uncontrolled \`${state}\`. Decide which one this component is for the whole of its life.`
53
+ );
54
+ }
55
+ if (!isControlled && defaultRef.current !== defaultValue) {
56
+ console.error(
57
+ `${name}: cannot change the default \`${state}\` after mount. It is only read once, so later changes are silently ignored.`
58
+ );
59
+ }
60
+ }
61
+ const setValue = (0, import_react.useCallback)(
62
+ (next) => {
63
+ if (!isControlled) setUncontrolled(next);
64
+ },
65
+ [isControlled]
66
+ );
67
+ return [value, setValue];
68
+ }
69
+
70
+ // src/core/useRender.ts
71
+ var import_react2 = require("react");
72
+
73
+ // src/core/mergeProps.ts
74
+ function isEventHandler(key) {
75
+ return key.length > 2 && key.startsWith("on") && key[2] === key[2]?.toUpperCase();
76
+ }
77
+ function mergeRefs(...refs) {
78
+ return (node) => {
79
+ const cleanups = refs.map((ref) => {
80
+ if (typeof ref === "function") {
81
+ const result = ref(node);
82
+ return typeof result === "function" ? result : () => ref(null);
83
+ }
84
+ if (ref && typeof ref === "object") {
85
+ ref.current = node;
86
+ return () => {
87
+ ref.current = null;
88
+ };
89
+ }
90
+ return void 0;
91
+ });
92
+ return () => {
93
+ cleanups.forEach((cleanup) => {
94
+ if (typeof cleanup === "function") cleanup();
95
+ });
96
+ };
97
+ };
98
+ }
99
+ function isSyntheticEvent(event) {
100
+ return typeof event === "object" && event !== null && "nativeEvent" in event;
101
+ }
102
+ function makeEventPreventable(event) {
103
+ if (Object.prototype.hasOwnProperty.call(event, "preventComponentHandler")) return;
104
+ const target = event;
105
+ target.preventComponentHandler = () => {
106
+ target.componentHandlerPrevented = true;
107
+ };
108
+ }
109
+ function isComponentHandlerPrevented(event) {
110
+ return typeof event === "object" && event !== null && event.componentHandlerPrevented === true;
111
+ }
112
+ function chainHandlers(earlier, value) {
113
+ return (...args) => {
114
+ const event = args[0];
115
+ if (isSyntheticEvent(event)) makeEventPreventable(event);
116
+ value(...args);
117
+ if (isComponentHandlerPrevented(event)) return;
118
+ earlier(...args);
119
+ };
120
+ }
121
+ function mergeProps(...objects) {
122
+ const merged = {};
123
+ for (const props of objects) {
124
+ if (!props) continue;
125
+ for (const key of Object.keys(props)) {
126
+ const value = props[key];
127
+ if (value === void 0) continue;
128
+ if (isEventHandler(key) && typeof value === "function") {
129
+ const earlier = merged[key];
130
+ merged[key] = typeof earlier === "function" ? chainHandlers(earlier, value) : value;
131
+ } else if (key === "className") {
132
+ merged.className = merged.className ? `${String(merged.className)} ${String(value)}` : value;
133
+ } else if (key === "style") {
134
+ merged.style = { ...merged.style, ...value };
135
+ } else if (key === "ref") {
136
+ merged.ref = merged.ref ? mergeRefs(merged.ref, value) : value;
137
+ } else {
138
+ merged[key] = value;
139
+ }
140
+ }
141
+ }
142
+ return merged;
143
+ }
144
+
145
+ // src/core/useRender.ts
146
+ function useRender({
147
+ render,
148
+ defaultTagName,
149
+ props,
150
+ consumerProps
151
+ }) {
152
+ const merged = mergeProps(props, consumerProps);
153
+ if ((0, import_react2.isValidElement)(render)) {
154
+ return (0, import_react2.cloneElement)(render, mergeProps(merged, render.props));
155
+ }
156
+ return (0, import_react2.createElement)(defaultTagName, merged);
157
+ }
158
+
159
+ // src/radio-group/RadioGroupRootContext.ts
160
+ var import_react3 = require("react");
161
+ var RadioGroupRootContext = (0, import_react3.createContext)(null);
162
+ function useRadioGroupRootContext() {
163
+ const context = (0, import_react3.useContext)(RadioGroupRootContext);
164
+ if (context === null) {
165
+ throw new Error("<RadioGroup.Item> must be rendered inside <RadioGroup.Root>.");
166
+ }
167
+ return context;
168
+ }
169
+
170
+ // src/radio-group/radioGroupDataAttributes.ts
171
+ function radioGroupItemDataAttributes({ checked, disabled }) {
172
+ return {
173
+ "data-checked": checked ? "" : void 0,
174
+ "data-unchecked": checked ? void 0 : "",
175
+ "data-disabled": disabled ? "" : void 0
176
+ };
177
+ }
178
+ function radioGroupDataAttributes({ disabled }) {
179
+ return {
180
+ "data-disabled": disabled ? "" : void 0
181
+ };
182
+ }
183
+
184
+ // src/radio-group/RadioGroupRoot.tsx
185
+ var import_jsx_runtime = require("react/jsx-runtime");
186
+ function radiosIn(root, name) {
187
+ if (!root) return [];
188
+ return [...root.querySelectorAll('input[type="radio"]')].filter(
189
+ (input) => input.name === name
190
+ );
191
+ }
192
+ function RadioGroupRoot({
193
+ value: valueProp,
194
+ defaultValue,
195
+ onValueChange,
196
+ name: nameProp,
197
+ disabled = false,
198
+ required = false,
199
+ form,
200
+ className,
201
+ children,
202
+ render,
203
+ ...rest
204
+ }) {
205
+ const [value, setValue] = useControlled({
206
+ controlled: valueProp,
207
+ default: defaultValue ?? null,
208
+ name: "RadioGroup.Root",
209
+ state: "value"
210
+ });
211
+ const generatedName = (0, import_react4.useId)();
212
+ const name = nameProp ?? generatedName;
213
+ const commitValue = (0, import_react4.useCallback)(
214
+ (next) => {
215
+ setValue(next);
216
+ onValueChange?.(next);
217
+ },
218
+ [setValue, onValueChange]
219
+ );
220
+ const elementRef = (0, import_react4.useRef)(null);
221
+ const { current: initialValue } = (0, import_react4.useRef)(value);
222
+ (0, import_react4.useEffect)(() => {
223
+ const owner = radiosIn(elementRef.current, name)[0]?.form;
224
+ if (!owner) return;
225
+ function onReset(event) {
226
+ queueMicrotask(() => {
227
+ if (event.defaultPrevented) return;
228
+ for (const input of radiosIn(elementRef.current, name)) {
229
+ input.checked = input.value === value;
230
+ }
231
+ if (value === initialValue) return;
232
+ commitValue(initialValue);
233
+ });
234
+ }
235
+ owner.addEventListener("reset", onReset);
236
+ return () => owner.removeEventListener("reset", onReset);
237
+ }, [name, value, commitValue, initialValue]);
238
+ const state = (0, import_react4.useMemo)(
239
+ () => ({ value, disabled, required, name, form, select: commitValue }),
240
+ [value, disabled, required, name, form, commitValue]
241
+ );
242
+ const element = useRender({
243
+ render,
244
+ defaultTagName: "div",
245
+ props: {
246
+ role: "radiogroup",
247
+ "aria-disabled": disabled || void 0,
248
+ "aria-required": required || void 0,
249
+ ...radioGroupDataAttributes(state),
250
+ className,
251
+ children,
252
+ ref: elementRef
253
+ },
254
+ consumerProps: rest
255
+ });
256
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RadioGroupRootContext.Provider, { value: state, children: element });
257
+ }
258
+
259
+ // src/radio-group/RadioGroupItem.tsx
260
+ var import_react5 = require("react");
261
+ function RadioGroupItem({
262
+ value,
263
+ disabled: disabledProp = false,
264
+ className,
265
+ render,
266
+ ...rest
267
+ }) {
268
+ const group = useRadioGroupRootContext();
269
+ const disabled = group.disabled || disabledProp;
270
+ const checked = group.value === value;
271
+ const state = (0, import_react5.useMemo)(() => ({ checked, disabled }), [checked, disabled]);
272
+ return useRender({
273
+ render,
274
+ defaultTagName: "input",
275
+ props: {
276
+ type: "radio",
277
+ name: group.name,
278
+ value,
279
+ // Controlled from the group's value, so React restores it after any change the
280
+ // group declines — the platform never gets the last word on what is checked.
281
+ checked,
282
+ form: group.form,
283
+ required: group.required || void 0,
284
+ disabled: disabled || void 0,
285
+ ...radioGroupItemDataAttributes(state),
286
+ className,
287
+ onChange() {
288
+ if (disabled) return;
289
+ group.select(value);
290
+ }
291
+ },
292
+ consumerProps: rest
293
+ });
294
+ }
295
+ // Annotate the CommonJS export names for ESM import in node:
296
+ 0 && (module.exports = {
297
+ RadioGroup
298
+ });
@@ -0,0 +1,128 @@
1
+ import * as react from 'react';
2
+ import { ReactElement, Ref, ComponentPropsWithRef } from 'react';
3
+
4
+ /**
5
+ * RadioGroup.Root's own props. Everything else — `id`, `className`, `children`,
6
+ * `aria-*`, `data-*`, event handlers — comes from React's own `<div>` props.
7
+ *
8
+ * `aria-labelledby` in particular is how a group gets its accessible name, pointing at
9
+ * the visible caption. A `<fieldset>` with a `<legend>` does the same through `render`.
10
+ */
11
+ type RadioGroupRootOwnProps = {
12
+ /**
13
+ * Controlled value — the `value` of the selected item, or `null` for none. Provide
14
+ * `onValueChange` alongside it.
15
+ *
16
+ * Never `undefined` once mounted. The mode is latched at mount, so an `undefined`
17
+ * first render makes the group uncontrolled for good. Use `null` for "nothing
18
+ * selected" — `value={x ?? null}`.
19
+ */
20
+ value?: string | null;
21
+ /** Initial value when uncontrolled. Read once, at mount. */
22
+ defaultValue?: string | null;
23
+ /**
24
+ * Called with the value being moved to, in both controlled and uncontrolled modes.
25
+ * A user can only move to a value; `null` arrives only from `form.reset()` returning
26
+ * a group that mounted with nothing selected.
27
+ */
28
+ onValueChange?: (value: string | null) => void;
29
+ /**
30
+ * The name every radio in the group shares, and submits under.
31
+ *
32
+ * The platform groups radios by name — that is what makes arrow keys move between
33
+ * them and keeps one checked at a time — so one is generated when this is omitted.
34
+ * A generated name still submits: pass your own when the group is inside a form.
35
+ */
36
+ name?: string;
37
+ /** Disables every radio in the group. */
38
+ disabled?: boolean;
39
+ /** Blocks form submission until a radio is selected, via the platform's validation. */
40
+ required?: boolean;
41
+ /** Associates the radios with a `<form>` by id, when the group is rendered outside it. */
42
+ form?: string;
43
+ /**
44
+ * Component to render instead of the default `<div>` — a `<fieldset>`, say. Props,
45
+ * className, event handlers and ref are merged onto it.
46
+ */
47
+ render?: ReactElement;
48
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
49
+ ref?: Ref<HTMLElement>;
50
+ };
51
+ type RadioGroupRootProps = RadioGroupRootOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof RadioGroupRootOwnProps>;
52
+ /**
53
+ * A set of radios, of which at most one is selected.
54
+ *
55
+ * The radios are native `<input type="radio">` elements — the native element passes all
56
+ * three of decision 7's tests, so nothing here rebuilds what the platform already does.
57
+ * Arrow keys move the selection, Tab enters the group at the checked radio, `required`
58
+ * blocks submission and the form submits the checked value, all without JavaScript.
59
+ *
60
+ * What the group adds is the one thing the platform leaves to the page: a single value
61
+ * in React. It owns that value, and every radio derives `checked` from it.
62
+ *
63
+ * Inside a form, `form.reset()` returns the group to the value it mounted with. The
64
+ * platform resets the inputs by itself, but React's value would not follow — the same
65
+ * seam `useFormReset` closes for Checkbox and Switch, closed here for a group.
66
+ */
67
+ declare function RadioGroupRoot({ value: valueProp, defaultValue, onValueChange, name: nameProp, disabled, required, form, className, children, render, ...rest }: RadioGroupRootProps): react.JSX.Element;
68
+
69
+ /**
70
+ * RadioGroup.Item's own props. Everything else — `id`, `className`, `aria-*`, `data-*`,
71
+ * event handlers — comes from React's own `<input>` props.
72
+ *
73
+ * `name`, `checked`, `defaultChecked`, `required` and `form` belong to the group and are
74
+ * not accepted here: a radio's checkedness is the group's value, not its own. `type` is
75
+ * always `radio`, and `children` is left out because an `<input>` cannot hold any.
76
+ */
77
+ type RadioGroupItemOwnProps = {
78
+ /** The group's value when this radio is selected, and what the form submits for it. */
79
+ value: string;
80
+ /** Disables this radio alone. A disabled group disables it regardless. */
81
+ disabled?: boolean;
82
+ /** Element to render instead of the default `<input type="radio">`. */
83
+ render?: ReactElement;
84
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
85
+ ref?: Ref<HTMLElement>;
86
+ };
87
+ type RadioGroupItemProps = RadioGroupItemOwnProps & Omit<ComponentPropsWithRef<'input'>, keyof RadioGroupItemOwnProps | 'type' | 'name' | 'checked' | 'defaultChecked' | 'required' | 'form' | 'children'>;
88
+ /**
89
+ * One radio — a native `<input type="radio">`.
90
+ *
91
+ * Void, so it has no parts: there is no indicator to hold, and the dot is drawn by CSS
92
+ * off `:checked` or the `data-*` attributes. Decision 11 — one element, one component.
93
+ *
94
+ * It has no accessible name of its own — wrap it in a `<label>`, pair it with one by
95
+ * `id`, or pass `aria-label`. A wrapping label names it natively, but
96
+ * jsx-a11y/label-has-associated-control cannot see through this component to the input,
97
+ * so under that rule pair it by `id` too.
98
+ */
99
+ declare function RadioGroupItem({ value, disabled: disabledProp, className, render, ...rest }: RadioGroupItemProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
100
+
101
+ declare namespace index_parts {
102
+ export { RadioGroupItem as Item, RadioGroupRoot as Root };
103
+ }
104
+
105
+ /**
106
+ * The state RadioGroup.Root shares with its items.
107
+ *
108
+ * `value` is `null` when nothing is selected — a real state for a radio group, and the
109
+ * one a native group starts in. `null` rather than `undefined`, because `undefined` is
110
+ * what tells `useControlled` a group is uncontrolled.
111
+ */
112
+ type RadioGroupState = {
113
+ value: string | null;
114
+ disabled: boolean;
115
+ required: boolean;
116
+ /** Always set — the consumer's, or one generated so the radios still form a group. */
117
+ name: string;
118
+ form: string | undefined;
119
+ /** The group's single setter. Every change goes through it (decision 10, rule 5). */
120
+ select: (value: string) => void;
121
+ };
122
+ /** What one radio projects as `data-*` attributes. */
123
+ type RadioGroupItemState = {
124
+ checked: boolean;
125
+ disabled: boolean;
126
+ };
127
+
128
+ export { index_parts as RadioGroup, type RadioGroupItemProps, type RadioGroupItemState, type RadioGroupRootProps, type RadioGroupState };
@@ -0,0 +1,128 @@
1
+ import * as react from 'react';
2
+ import { ReactElement, Ref, ComponentPropsWithRef } from 'react';
3
+
4
+ /**
5
+ * RadioGroup.Root's own props. Everything else — `id`, `className`, `children`,
6
+ * `aria-*`, `data-*`, event handlers — comes from React's own `<div>` props.
7
+ *
8
+ * `aria-labelledby` in particular is how a group gets its accessible name, pointing at
9
+ * the visible caption. A `<fieldset>` with a `<legend>` does the same through `render`.
10
+ */
11
+ type RadioGroupRootOwnProps = {
12
+ /**
13
+ * Controlled value — the `value` of the selected item, or `null` for none. Provide
14
+ * `onValueChange` alongside it.
15
+ *
16
+ * Never `undefined` once mounted. The mode is latched at mount, so an `undefined`
17
+ * first render makes the group uncontrolled for good. Use `null` for "nothing
18
+ * selected" — `value={x ?? null}`.
19
+ */
20
+ value?: string | null;
21
+ /** Initial value when uncontrolled. Read once, at mount. */
22
+ defaultValue?: string | null;
23
+ /**
24
+ * Called with the value being moved to, in both controlled and uncontrolled modes.
25
+ * A user can only move to a value; `null` arrives only from `form.reset()` returning
26
+ * a group that mounted with nothing selected.
27
+ */
28
+ onValueChange?: (value: string | null) => void;
29
+ /**
30
+ * The name every radio in the group shares, and submits under.
31
+ *
32
+ * The platform groups radios by name — that is what makes arrow keys move between
33
+ * them and keeps one checked at a time — so one is generated when this is omitted.
34
+ * A generated name still submits: pass your own when the group is inside a form.
35
+ */
36
+ name?: string;
37
+ /** Disables every radio in the group. */
38
+ disabled?: boolean;
39
+ /** Blocks form submission until a radio is selected, via the platform's validation. */
40
+ required?: boolean;
41
+ /** Associates the radios with a `<form>` by id, when the group is rendered outside it. */
42
+ form?: string;
43
+ /**
44
+ * Component to render instead of the default `<div>` — a `<fieldset>`, say. Props,
45
+ * className, event handlers and ref are merged onto it.
46
+ */
47
+ render?: ReactElement;
48
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
49
+ ref?: Ref<HTMLElement>;
50
+ };
51
+ type RadioGroupRootProps = RadioGroupRootOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof RadioGroupRootOwnProps>;
52
+ /**
53
+ * A set of radios, of which at most one is selected.
54
+ *
55
+ * The radios are native `<input type="radio">` elements — the native element passes all
56
+ * three of decision 7's tests, so nothing here rebuilds what the platform already does.
57
+ * Arrow keys move the selection, Tab enters the group at the checked radio, `required`
58
+ * blocks submission and the form submits the checked value, all without JavaScript.
59
+ *
60
+ * What the group adds is the one thing the platform leaves to the page: a single value
61
+ * in React. It owns that value, and every radio derives `checked` from it.
62
+ *
63
+ * Inside a form, `form.reset()` returns the group to the value it mounted with. The
64
+ * platform resets the inputs by itself, but React's value would not follow — the same
65
+ * seam `useFormReset` closes for Checkbox and Switch, closed here for a group.
66
+ */
67
+ declare function RadioGroupRoot({ value: valueProp, defaultValue, onValueChange, name: nameProp, disabled, required, form, className, children, render, ...rest }: RadioGroupRootProps): react.JSX.Element;
68
+
69
+ /**
70
+ * RadioGroup.Item's own props. Everything else — `id`, `className`, `aria-*`, `data-*`,
71
+ * event handlers — comes from React's own `<input>` props.
72
+ *
73
+ * `name`, `checked`, `defaultChecked`, `required` and `form` belong to the group and are
74
+ * not accepted here: a radio's checkedness is the group's value, not its own. `type` is
75
+ * always `radio`, and `children` is left out because an `<input>` cannot hold any.
76
+ */
77
+ type RadioGroupItemOwnProps = {
78
+ /** The group's value when this radio is selected, and what the form submits for it. */
79
+ value: string;
80
+ /** Disables this radio alone. A disabled group disables it regardless. */
81
+ disabled?: boolean;
82
+ /** Element to render instead of the default `<input type="radio">`. */
83
+ render?: ReactElement;
84
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
85
+ ref?: Ref<HTMLElement>;
86
+ };
87
+ type RadioGroupItemProps = RadioGroupItemOwnProps & Omit<ComponentPropsWithRef<'input'>, keyof RadioGroupItemOwnProps | 'type' | 'name' | 'checked' | 'defaultChecked' | 'required' | 'form' | 'children'>;
88
+ /**
89
+ * One radio — a native `<input type="radio">`.
90
+ *
91
+ * Void, so it has no parts: there is no indicator to hold, and the dot is drawn by CSS
92
+ * off `:checked` or the `data-*` attributes. Decision 11 — one element, one component.
93
+ *
94
+ * It has no accessible name of its own — wrap it in a `<label>`, pair it with one by
95
+ * `id`, or pass `aria-label`. A wrapping label names it natively, but
96
+ * jsx-a11y/label-has-associated-control cannot see through this component to the input,
97
+ * so under that rule pair it by `id` too.
98
+ */
99
+ declare function RadioGroupItem({ value, disabled: disabledProp, className, render, ...rest }: RadioGroupItemProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
100
+
101
+ declare namespace index_parts {
102
+ export { RadioGroupItem as Item, RadioGroupRoot as Root };
103
+ }
104
+
105
+ /**
106
+ * The state RadioGroup.Root shares with its items.
107
+ *
108
+ * `value` is `null` when nothing is selected — a real state for a radio group, and the
109
+ * one a native group starts in. `null` rather than `undefined`, because `undefined` is
110
+ * what tells `useControlled` a group is uncontrolled.
111
+ */
112
+ type RadioGroupState = {
113
+ value: string | null;
114
+ disabled: boolean;
115
+ required: boolean;
116
+ /** Always set — the consumer's, or one generated so the radios still form a group. */
117
+ name: string;
118
+ form: string | undefined;
119
+ /** The group's single setter. Every change goes through it (decision 10, rule 5). */
120
+ select: (value: string) => void;
121
+ };
122
+ /** What one radio projects as `data-*` attributes. */
123
+ type RadioGroupItemState = {
124
+ checked: boolean;
125
+ disabled: boolean;
126
+ };
127
+
128
+ export { index_parts as RadioGroup, type RadioGroupItemProps, type RadioGroupItemState, type RadioGroupRootProps, type RadioGroupState };
@@ -0,0 +1,157 @@
1
+ import {
2
+ useControlled
3
+ } from "../chunk-UEIB3I3T.js";
4
+ import {
5
+ __export,
6
+ useRender
7
+ } from "../chunk-OQTWWZ4B.js";
8
+
9
+ // src/radio-group/index.parts.ts
10
+ var index_parts_exports = {};
11
+ __export(index_parts_exports, {
12
+ Item: () => RadioGroupItem,
13
+ Root: () => RadioGroupRoot
14
+ });
15
+
16
+ // src/radio-group/RadioGroupRoot.tsx
17
+ import { useCallback, useEffect, useId, useMemo, useRef } from "react";
18
+
19
+ // src/radio-group/RadioGroupRootContext.ts
20
+ import { createContext, useContext } from "react";
21
+ var RadioGroupRootContext = createContext(null);
22
+ function useRadioGroupRootContext() {
23
+ const context = useContext(RadioGroupRootContext);
24
+ if (context === null) {
25
+ throw new Error("<RadioGroup.Item> must be rendered inside <RadioGroup.Root>.");
26
+ }
27
+ return context;
28
+ }
29
+
30
+ // src/radio-group/radioGroupDataAttributes.ts
31
+ function radioGroupItemDataAttributes({ checked, disabled }) {
32
+ return {
33
+ "data-checked": checked ? "" : void 0,
34
+ "data-unchecked": checked ? void 0 : "",
35
+ "data-disabled": disabled ? "" : void 0
36
+ };
37
+ }
38
+ function radioGroupDataAttributes({ disabled }) {
39
+ return {
40
+ "data-disabled": disabled ? "" : void 0
41
+ };
42
+ }
43
+
44
+ // src/radio-group/RadioGroupRoot.tsx
45
+ import { jsx } from "react/jsx-runtime";
46
+ function radiosIn(root, name) {
47
+ if (!root) return [];
48
+ return [...root.querySelectorAll('input[type="radio"]')].filter(
49
+ (input) => input.name === name
50
+ );
51
+ }
52
+ function RadioGroupRoot({
53
+ value: valueProp,
54
+ defaultValue,
55
+ onValueChange,
56
+ name: nameProp,
57
+ disabled = false,
58
+ required = false,
59
+ form,
60
+ className,
61
+ children,
62
+ render,
63
+ ...rest
64
+ }) {
65
+ const [value, setValue] = useControlled({
66
+ controlled: valueProp,
67
+ default: defaultValue ?? null,
68
+ name: "RadioGroup.Root",
69
+ state: "value"
70
+ });
71
+ const generatedName = useId();
72
+ const name = nameProp ?? generatedName;
73
+ const commitValue = useCallback(
74
+ (next) => {
75
+ setValue(next);
76
+ onValueChange?.(next);
77
+ },
78
+ [setValue, onValueChange]
79
+ );
80
+ const elementRef = useRef(null);
81
+ const { current: initialValue } = useRef(value);
82
+ useEffect(() => {
83
+ const owner = radiosIn(elementRef.current, name)[0]?.form;
84
+ if (!owner) return;
85
+ function onReset(event) {
86
+ queueMicrotask(() => {
87
+ if (event.defaultPrevented) return;
88
+ for (const input of radiosIn(elementRef.current, name)) {
89
+ input.checked = input.value === value;
90
+ }
91
+ if (value === initialValue) return;
92
+ commitValue(initialValue);
93
+ });
94
+ }
95
+ owner.addEventListener("reset", onReset);
96
+ return () => owner.removeEventListener("reset", onReset);
97
+ }, [name, value, commitValue, initialValue]);
98
+ const state = useMemo(
99
+ () => ({ value, disabled, required, name, form, select: commitValue }),
100
+ [value, disabled, required, name, form, commitValue]
101
+ );
102
+ const element = useRender({
103
+ render,
104
+ defaultTagName: "div",
105
+ props: {
106
+ role: "radiogroup",
107
+ "aria-disabled": disabled || void 0,
108
+ "aria-required": required || void 0,
109
+ ...radioGroupDataAttributes(state),
110
+ className,
111
+ children,
112
+ ref: elementRef
113
+ },
114
+ consumerProps: rest
115
+ });
116
+ return /* @__PURE__ */ jsx(RadioGroupRootContext.Provider, { value: state, children: element });
117
+ }
118
+
119
+ // src/radio-group/RadioGroupItem.tsx
120
+ import { useMemo as useMemo2 } from "react";
121
+ function RadioGroupItem({
122
+ value,
123
+ disabled: disabledProp = false,
124
+ className,
125
+ render,
126
+ ...rest
127
+ }) {
128
+ const group = useRadioGroupRootContext();
129
+ const disabled = group.disabled || disabledProp;
130
+ const checked = group.value === value;
131
+ const state = useMemo2(() => ({ checked, disabled }), [checked, disabled]);
132
+ return useRender({
133
+ render,
134
+ defaultTagName: "input",
135
+ props: {
136
+ type: "radio",
137
+ name: group.name,
138
+ value,
139
+ // Controlled from the group's value, so React restores it after any change the
140
+ // group declines — the platform never gets the last word on what is checked.
141
+ checked,
142
+ form: group.form,
143
+ required: group.required || void 0,
144
+ disabled: disabled || void 0,
145
+ ...radioGroupItemDataAttributes(state),
146
+ className,
147
+ onChange() {
148
+ if (disabled) return;
149
+ group.select(value);
150
+ }
151
+ },
152
+ consumerProps: rest
153
+ });
154
+ }
155
+ export {
156
+ index_parts_exports as RadioGroup
157
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arun-dev/headless",
3
- "version": "4.1.0",
3
+ "version": "4.2.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",
@@ -54,6 +54,16 @@
54
54
  "default": "./dist/checkbox/index.cjs"
55
55
  }
56
56
  },
57
+ "./radio-group": {
58
+ "import": {
59
+ "types": "./dist/radio-group/index.d.ts",
60
+ "default": "./dist/radio-group/index.js"
61
+ },
62
+ "require": {
63
+ "types": "./dist/radio-group/index.d.cts",
64
+ "default": "./dist/radio-group/index.cjs"
65
+ }
66
+ },
57
67
  "./switch": {
58
68
  "import": {
59
69
  "types": "./dist/switch/index.d.ts",