@arun-dev/headless 0.1.0 → 0.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,136 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/core/mergeProps.ts
8
+ function isEventHandler(key) {
9
+ return key.length > 2 && key.startsWith("on") && key[2] === key[2]?.toUpperCase();
10
+ }
11
+ function mergeRefs(...refs) {
12
+ return (node) => {
13
+ const cleanups = refs.map((ref) => {
14
+ if (typeof ref === "function") {
15
+ const result = ref(node);
16
+ return typeof result === "function" ? result : () => ref(null);
17
+ }
18
+ if (ref && typeof ref === "object") {
19
+ ref.current = node;
20
+ return () => {
21
+ ref.current = null;
22
+ };
23
+ }
24
+ return void 0;
25
+ });
26
+ return () => {
27
+ cleanups.forEach((cleanup) => {
28
+ if (typeof cleanup === "function") cleanup();
29
+ });
30
+ };
31
+ };
32
+ }
33
+ function mergeProps(...objects) {
34
+ const merged = {};
35
+ for (const props of objects) {
36
+ if (!props) continue;
37
+ for (const key of Object.keys(props)) {
38
+ const value = props[key];
39
+ if (value === void 0) continue;
40
+ if (isEventHandler(key) && typeof value === "function") {
41
+ const existing = merged[key];
42
+ merged[key] = typeof existing === "function" ? (...args) => {
43
+ existing(...args);
44
+ value(...args);
45
+ } : value;
46
+ } else if (key === "className") {
47
+ merged.className = merged.className ? `${String(merged.className)} ${String(value)}` : value;
48
+ } else if (key === "style") {
49
+ merged.style = { ...merged.style, ...value };
50
+ } else if (key === "ref") {
51
+ merged.ref = merged.ref ? mergeRefs(merged.ref, value) : value;
52
+ } else {
53
+ merged[key] = value;
54
+ }
55
+ }
56
+ }
57
+ return merged;
58
+ }
59
+
60
+ // src/core/stateAttributes.ts
61
+ function getStateAttributes(state, mapping) {
62
+ if (!mapping) return {};
63
+ const attributes = {};
64
+ for (const key of Object.keys(state)) {
65
+ const toAttributes = mapping[key];
66
+ if (!toAttributes) continue;
67
+ const result = toAttributes(state[key]);
68
+ if (result) Object.assign(attributes, result);
69
+ }
70
+ return attributes;
71
+ }
72
+ function booleanAttribute(whenTrue, whenFalse) {
73
+ return (value) => {
74
+ if (value) return { [whenTrue]: "" };
75
+ return whenFalse ? { [whenFalse]: "" } : null;
76
+ };
77
+ }
78
+
79
+ // src/core/useRender.ts
80
+ import { cloneElement, createElement, isValidElement } from "react";
81
+ function useRender({
82
+ render,
83
+ defaultTagName,
84
+ props,
85
+ state,
86
+ stateAttributes
87
+ }) {
88
+ const attributes = state ? getStateAttributes(state, stateAttributes) : {};
89
+ const merged = mergeProps(attributes, ...props);
90
+ if (isValidElement(render)) {
91
+ return cloneElement(render, mergeProps(merged, render.props));
92
+ }
93
+ return createElement(defaultTagName, merged);
94
+ }
95
+
96
+ // src/core/useControlled.ts
97
+ import { useCallback, useRef, useState } from "react";
98
+ function useControlled({
99
+ controlled,
100
+ default: defaultValue,
101
+ name = "Component",
102
+ state = "value"
103
+ }) {
104
+ const { current: isControlled } = useRef(controlled !== void 0);
105
+ const [uncontrolled, setUncontrolled] = useState(defaultValue);
106
+ const defaultRef = useRef(defaultValue);
107
+ const value = isControlled ? controlled : uncontrolled;
108
+ if (process.env.NODE_ENV !== "production") {
109
+ if (isControlled !== (controlled !== void 0)) {
110
+ console.error(
111
+ `${name}: cannot switch between controlled and uncontrolled \`${state}\`. Decide which one this component is for the whole of its life.`
112
+ );
113
+ }
114
+ if (!isControlled && defaultRef.current !== defaultValue) {
115
+ console.error(
116
+ `${name}: cannot change the default \`${state}\` after mount. It is only read once, so later changes are silently ignored.`
117
+ );
118
+ }
119
+ }
120
+ const setValue = useCallback(
121
+ (next) => {
122
+ if (!isControlled) setUncontrolled(next);
123
+ },
124
+ [isControlled]
125
+ );
126
+ return [value, setValue];
127
+ }
128
+
129
+ export {
130
+ __export,
131
+ mergeProps,
132
+ getStateAttributes,
133
+ booleanAttribute,
134
+ useRender,
135
+ useControlled
136
+ };
package/dist/index.js CHANGED
@@ -1,126 +1,10 @@
1
- // src/core/mergeProps.ts
2
- function isEventHandler(key) {
3
- return key.length > 2 && key.startsWith("on") && key[2] === key[2]?.toUpperCase();
4
- }
5
- function mergeRefs(...refs) {
6
- return (node) => {
7
- const cleanups = refs.map((ref) => {
8
- if (typeof ref === "function") {
9
- const result = ref(node);
10
- return typeof result === "function" ? result : () => ref(null);
11
- }
12
- if (ref && typeof ref === "object") {
13
- ref.current = node;
14
- return () => {
15
- ref.current = null;
16
- };
17
- }
18
- return void 0;
19
- });
20
- return () => {
21
- cleanups.forEach((cleanup) => {
22
- if (typeof cleanup === "function") cleanup();
23
- });
24
- };
25
- };
26
- }
27
- function mergeProps(...objects) {
28
- const merged = {};
29
- for (const props of objects) {
30
- if (!props) continue;
31
- for (const key of Object.keys(props)) {
32
- const value = props[key];
33
- if (value === void 0) continue;
34
- if (isEventHandler(key) && typeof value === "function") {
35
- const existing = merged[key];
36
- merged[key] = typeof existing === "function" ? (...args) => {
37
- existing(...args);
38
- value(...args);
39
- } : value;
40
- } else if (key === "className") {
41
- merged.className = merged.className ? `${String(merged.className)} ${String(value)}` : value;
42
- } else if (key === "style") {
43
- merged.style = { ...merged.style, ...value };
44
- } else if (key === "ref") {
45
- merged.ref = merged.ref ? mergeRefs(merged.ref, value) : value;
46
- } else {
47
- merged[key] = value;
48
- }
49
- }
50
- }
51
- return merged;
52
- }
53
-
54
- // src/core/useRender.ts
55
- import { cloneElement, createElement, isValidElement } from "react";
56
-
57
- // src/core/stateAttributes.ts
58
- function getStateAttributes(state, mapping) {
59
- if (!mapping) return {};
60
- const attributes = {};
61
- for (const key of Object.keys(state)) {
62
- const toAttributes = mapping[key];
63
- if (!toAttributes) continue;
64
- const result = toAttributes(state[key]);
65
- if (result) Object.assign(attributes, result);
66
- }
67
- return attributes;
68
- }
69
- function booleanAttribute(whenTrue, whenFalse) {
70
- return (value) => {
71
- if (value) return { [whenTrue]: "" };
72
- return whenFalse ? { [whenFalse]: "" } : null;
73
- };
74
- }
75
-
76
- // src/core/useRender.ts
77
- function useRender({
78
- render,
79
- defaultTagName,
80
- props,
81
- state,
82
- stateAttributes
83
- }) {
84
- const attributes = state ? getStateAttributes(state, stateAttributes) : {};
85
- const merged = mergeProps(attributes, ...props);
86
- if (isValidElement(render)) {
87
- return cloneElement(render, mergeProps(merged, render.props));
88
- }
89
- return createElement(defaultTagName, merged);
90
- }
91
-
92
- // src/core/useControlled.ts
93
- import { useCallback, useRef, useState } from "react";
94
- function useControlled({
95
- controlled,
96
- default: defaultValue,
97
- name = "Component",
98
- state = "value"
99
- }) {
100
- const { current: isControlled } = useRef(controlled !== void 0);
101
- const [uncontrolled, setUncontrolled] = useState(defaultValue);
102
- const defaultRef = useRef(defaultValue);
103
- const value = isControlled ? controlled : uncontrolled;
104
- if (process.env.NODE_ENV !== "production") {
105
- if (isControlled !== (controlled !== void 0)) {
106
- console.error(
107
- `${name}: cannot switch between controlled and uncontrolled \`${state}\`. Decide which one this component is for the whole of its life.`
108
- );
109
- }
110
- if (!isControlled && defaultRef.current !== defaultValue) {
111
- console.error(
112
- `${name}: cannot change the default \`${state}\` after mount. It is only read once, so later changes are silently ignored.`
113
- );
114
- }
115
- }
116
- const setValue = useCallback(
117
- (next) => {
118
- if (!isControlled) setUncontrolled(next);
119
- },
120
- [isControlled]
121
- );
122
- return [value, setValue];
123
- }
1
+ import {
2
+ booleanAttribute,
3
+ getStateAttributes,
4
+ mergeProps,
5
+ useControlled,
6
+ useRender
7
+ } from "./chunk-DAO4JK6U.js";
124
8
  export {
125
9
  booleanAttribute,
126
10
  getStateAttributes,
@@ -0,0 +1,246 @@
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/switch/index.ts
21
+ var switch_exports = {};
22
+ __export(switch_exports, {
23
+ Switch: () => index_parts_exports
24
+ });
25
+ module.exports = __toCommonJS(switch_exports);
26
+
27
+ // src/switch/index.parts.ts
28
+ var index_parts_exports = {};
29
+ __export(index_parts_exports, {
30
+ Root: () => SwitchRoot,
31
+ Thumb: () => SwitchThumb
32
+ });
33
+
34
+ // src/switch/root/SwitchRoot.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 mergeProps(...objects) {
100
+ const merged = {};
101
+ for (const props of objects) {
102
+ if (!props) continue;
103
+ for (const key of Object.keys(props)) {
104
+ const value = props[key];
105
+ if (value === void 0) continue;
106
+ if (isEventHandler(key) && typeof value === "function") {
107
+ const existing = merged[key];
108
+ merged[key] = typeof existing === "function" ? (...args) => {
109
+ existing(...args);
110
+ value(...args);
111
+ } : value;
112
+ } else if (key === "className") {
113
+ merged.className = merged.className ? `${String(merged.className)} ${String(value)}` : value;
114
+ } else if (key === "style") {
115
+ merged.style = { ...merged.style, ...value };
116
+ } else if (key === "ref") {
117
+ merged.ref = merged.ref ? mergeRefs(merged.ref, value) : value;
118
+ } else {
119
+ merged[key] = value;
120
+ }
121
+ }
122
+ }
123
+ return merged;
124
+ }
125
+
126
+ // src/core/stateAttributes.ts
127
+ function getStateAttributes(state, mapping) {
128
+ if (!mapping) return {};
129
+ const attributes = {};
130
+ for (const key of Object.keys(state)) {
131
+ const toAttributes = mapping[key];
132
+ if (!toAttributes) continue;
133
+ const result = toAttributes(state[key]);
134
+ if (result) Object.assign(attributes, result);
135
+ }
136
+ return attributes;
137
+ }
138
+ function booleanAttribute(whenTrue, whenFalse) {
139
+ return (value) => {
140
+ if (value) return { [whenTrue]: "" };
141
+ return whenFalse ? { [whenFalse]: "" } : null;
142
+ };
143
+ }
144
+
145
+ // src/core/useRender.ts
146
+ function useRender({
147
+ render,
148
+ defaultTagName,
149
+ props,
150
+ state,
151
+ stateAttributes
152
+ }) {
153
+ const attributes = state ? getStateAttributes(state, stateAttributes) : {};
154
+ const merged = mergeProps(attributes, ...props);
155
+ if ((0, import_react2.isValidElement)(render)) {
156
+ return (0, import_react2.cloneElement)(render, mergeProps(merged, render.props));
157
+ }
158
+ return (0, import_react2.createElement)(defaultTagName, merged);
159
+ }
160
+
161
+ // src/switch/SwitchRootContext.ts
162
+ var import_react3 = require("react");
163
+ var SwitchRootContext = (0, import_react3.createContext)(null);
164
+ function useSwitchRootContext() {
165
+ const context = (0, import_react3.useContext)(SwitchRootContext);
166
+ if (context === null) {
167
+ throw new Error("<Switch.Thumb> must be rendered inside <Switch.Root>.");
168
+ }
169
+ return context;
170
+ }
171
+
172
+ // src/switch/stateAttributes.ts
173
+ var switchStateAttributes = {
174
+ checked: booleanAttribute("data-checked", "data-unchecked"),
175
+ disabled: booleanAttribute("data-disabled")
176
+ };
177
+
178
+ // src/switch/root/SwitchRoot.tsx
179
+ var import_jsx_runtime = require("react/jsx-runtime");
180
+ function SwitchRoot({
181
+ checked: checkedProp,
182
+ defaultChecked,
183
+ onCheckedChange,
184
+ disabled = false,
185
+ name,
186
+ value = "on",
187
+ className,
188
+ children,
189
+ render,
190
+ ...rest
191
+ }) {
192
+ const [checked, setChecked] = useControlled({
193
+ controlled: checkedProp,
194
+ default: defaultChecked ?? false,
195
+ name: "Switch.Root",
196
+ state: "checked"
197
+ });
198
+ const state = (0, import_react4.useMemo)(() => ({ checked, disabled }), [checked, disabled]);
199
+ const element = useRender({
200
+ render,
201
+ defaultTagName: "button",
202
+ state,
203
+ stateAttributes: switchStateAttributes,
204
+ props: [
205
+ {
206
+ type: "button",
207
+ role: "switch",
208
+ "aria-checked": checked,
209
+ disabled,
210
+ className,
211
+ children,
212
+ onClick() {
213
+ const next = !checked;
214
+ setChecked(next);
215
+ onCheckedChange?.(next);
216
+ }
217
+ },
218
+ rest
219
+ ]
220
+ });
221
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SwitchRootContext.Provider, { value: state, children: [
222
+ element,
223
+ name !== void 0 && checked ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "hidden", name, value }) : null
224
+ ] });
225
+ }
226
+
227
+ // src/switch/thumb/SwitchThumb.tsx
228
+ function SwitchThumb({
229
+ className,
230
+ children,
231
+ render,
232
+ ...rest
233
+ }) {
234
+ const state = useSwitchRootContext();
235
+ return useRender({
236
+ render,
237
+ defaultTagName: "span",
238
+ state,
239
+ stateAttributes: switchStateAttributes,
240
+ props: [{ "aria-hidden": true, className, children }, rest]
241
+ });
242
+ }
243
+ // Annotate the CommonJS export names for ESM import in node:
244
+ 0 && (module.exports = {
245
+ Switch
246
+ });
@@ -0,0 +1,76 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, ReactElement, Ref } from 'react';
3
+
4
+ interface SwitchRootProps {
5
+ /** Controlled state. Provide `onCheckedChange` alongside it. */
6
+ checked?: boolean;
7
+ /** Initial state when uncontrolled. Read once, at mount. */
8
+ defaultChecked?: boolean;
9
+ onCheckedChange?: (checked: boolean) => void;
10
+ disabled?: boolean;
11
+ /**
12
+ * Submits with the enclosing form when checked, mirroring a native checkbox:
13
+ * an unchecked control contributes nothing.
14
+ */
15
+ name?: string;
16
+ /** Value submitted when checked. Defaults to `"on"`, as a native checkbox does. */
17
+ value?: string;
18
+ className?: string;
19
+ children?: ReactNode;
20
+ /**
21
+ * Element to render instead of the default `<button>`. Props, className, event
22
+ * handlers and ref are merged onto it.
23
+ */
24
+ render?: ReactElement;
25
+ ref?: Ref<HTMLElement>;
26
+ }
27
+ /**
28
+ * A switch — an immediate on/off control, distinct from a checkbox in that it takes
29
+ * effect at once rather than on submit.
30
+ *
31
+ * Renders a native `<button>`, which supplies focusability, Space and Enter
32
+ * activation, and the disabled semantics for free. Per the WAI-ARIA switch pattern
33
+ * it carries `role="switch"` and `aria-checked`.
34
+ *
35
+ * It has no accessible name of its own — wrap it in a `<label>`, or pass `aria-label`
36
+ * or `aria-labelledby`. That is the consumer's decision, not something a headless
37
+ * component should guess.
38
+ */
39
+ declare function SwitchRoot({ checked: checkedProp, defaultChecked, onCheckedChange, disabled, name, value, className, children, render, ...rest }: SwitchRootProps & Record<string, unknown>): react.JSX.Element;
40
+
41
+ interface SwitchThumbProps {
42
+ className?: string;
43
+ children?: ReactNode;
44
+ /** Element to render instead of the default `<span>`. */
45
+ render?: ReactElement;
46
+ ref?: Ref<HTMLElement>;
47
+ }
48
+ /**
49
+ * The moving part of the switch.
50
+ *
51
+ * Reads state from the Root rather than taking props, so a consumer cannot get the
52
+ * two out of step. Carries the same `data-*` attributes as the Root, which is what
53
+ * lets CSS move it:
54
+ *
55
+ * .switch-thumb[data-checked] { transform: translateX(100%); }
56
+ *
57
+ * Purely presentational — hidden from assistive technology, since the Root already
58
+ * announces the state.
59
+ */
60
+ declare function SwitchThumb({ className, children, render, ...rest }: SwitchThumbProps & Record<string, unknown>): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
61
+
62
+ declare namespace index_parts {
63
+ export { SwitchRoot as Root, SwitchThumb as Thumb };
64
+ }
65
+
66
+ /**
67
+ * A type alias rather than an interface: only aliases get an implicit index
68
+ * signature, which is what lets this satisfy the `Record<string, unknown>` the
69
+ * state-attribute mapper is generic over.
70
+ */
71
+ type SwitchState = {
72
+ checked: boolean;
73
+ disabled: boolean;
74
+ };
75
+
76
+ export { index_parts as Switch, type SwitchRootProps, type SwitchState, type SwitchThumbProps };
@@ -0,0 +1,76 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, ReactElement, Ref } from 'react';
3
+
4
+ interface SwitchRootProps {
5
+ /** Controlled state. Provide `onCheckedChange` alongside it. */
6
+ checked?: boolean;
7
+ /** Initial state when uncontrolled. Read once, at mount. */
8
+ defaultChecked?: boolean;
9
+ onCheckedChange?: (checked: boolean) => void;
10
+ disabled?: boolean;
11
+ /**
12
+ * Submits with the enclosing form when checked, mirroring a native checkbox:
13
+ * an unchecked control contributes nothing.
14
+ */
15
+ name?: string;
16
+ /** Value submitted when checked. Defaults to `"on"`, as a native checkbox does. */
17
+ value?: string;
18
+ className?: string;
19
+ children?: ReactNode;
20
+ /**
21
+ * Element to render instead of the default `<button>`. Props, className, event
22
+ * handlers and ref are merged onto it.
23
+ */
24
+ render?: ReactElement;
25
+ ref?: Ref<HTMLElement>;
26
+ }
27
+ /**
28
+ * A switch — an immediate on/off control, distinct from a checkbox in that it takes
29
+ * effect at once rather than on submit.
30
+ *
31
+ * Renders a native `<button>`, which supplies focusability, Space and Enter
32
+ * activation, and the disabled semantics for free. Per the WAI-ARIA switch pattern
33
+ * it carries `role="switch"` and `aria-checked`.
34
+ *
35
+ * It has no accessible name of its own — wrap it in a `<label>`, or pass `aria-label`
36
+ * or `aria-labelledby`. That is the consumer's decision, not something a headless
37
+ * component should guess.
38
+ */
39
+ declare function SwitchRoot({ checked: checkedProp, defaultChecked, onCheckedChange, disabled, name, value, className, children, render, ...rest }: SwitchRootProps & Record<string, unknown>): react.JSX.Element;
40
+
41
+ interface SwitchThumbProps {
42
+ className?: string;
43
+ children?: ReactNode;
44
+ /** Element to render instead of the default `<span>`. */
45
+ render?: ReactElement;
46
+ ref?: Ref<HTMLElement>;
47
+ }
48
+ /**
49
+ * The moving part of the switch.
50
+ *
51
+ * Reads state from the Root rather than taking props, so a consumer cannot get the
52
+ * two out of step. Carries the same `data-*` attributes as the Root, which is what
53
+ * lets CSS move it:
54
+ *
55
+ * .switch-thumb[data-checked] { transform: translateX(100%); }
56
+ *
57
+ * Purely presentational — hidden from assistive technology, since the Root already
58
+ * announces the state.
59
+ */
60
+ declare function SwitchThumb({ className, children, render, ...rest }: SwitchThumbProps & Record<string, unknown>): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
61
+
62
+ declare namespace index_parts {
63
+ export { SwitchRoot as Root, SwitchThumb as Thumb };
64
+ }
65
+
66
+ /**
67
+ * A type alias rather than an interface: only aliases get an implicit index
68
+ * signature, which is what lets this satisfy the `Record<string, unknown>` the
69
+ * state-attribute mapper is generic over.
70
+ */
71
+ type SwitchState = {
72
+ checked: boolean;
73
+ disabled: boolean;
74
+ };
75
+
76
+ export { index_parts as Switch, type SwitchRootProps, type SwitchState, type SwitchThumbProps };
@@ -0,0 +1,102 @@
1
+ import {
2
+ __export,
3
+ booleanAttribute,
4
+ useControlled,
5
+ useRender
6
+ } from "../chunk-DAO4JK6U.js";
7
+
8
+ // src/switch/index.parts.ts
9
+ var index_parts_exports = {};
10
+ __export(index_parts_exports, {
11
+ Root: () => SwitchRoot,
12
+ Thumb: () => SwitchThumb
13
+ });
14
+
15
+ // src/switch/root/SwitchRoot.tsx
16
+ import { useMemo } from "react";
17
+
18
+ // src/switch/SwitchRootContext.ts
19
+ import { createContext, useContext } from "react";
20
+ var SwitchRootContext = createContext(null);
21
+ function useSwitchRootContext() {
22
+ const context = useContext(SwitchRootContext);
23
+ if (context === null) {
24
+ throw new Error("<Switch.Thumb> must be rendered inside <Switch.Root>.");
25
+ }
26
+ return context;
27
+ }
28
+
29
+ // src/switch/stateAttributes.ts
30
+ var switchStateAttributes = {
31
+ checked: booleanAttribute("data-checked", "data-unchecked"),
32
+ disabled: booleanAttribute("data-disabled")
33
+ };
34
+
35
+ // src/switch/root/SwitchRoot.tsx
36
+ import { jsx, jsxs } from "react/jsx-runtime";
37
+ function SwitchRoot({
38
+ checked: checkedProp,
39
+ defaultChecked,
40
+ onCheckedChange,
41
+ disabled = false,
42
+ name,
43
+ value = "on",
44
+ className,
45
+ children,
46
+ render,
47
+ ...rest
48
+ }) {
49
+ const [checked, setChecked] = useControlled({
50
+ controlled: checkedProp,
51
+ default: defaultChecked ?? false,
52
+ name: "Switch.Root",
53
+ state: "checked"
54
+ });
55
+ const state = useMemo(() => ({ checked, disabled }), [checked, disabled]);
56
+ const element = useRender({
57
+ render,
58
+ defaultTagName: "button",
59
+ state,
60
+ stateAttributes: switchStateAttributes,
61
+ props: [
62
+ {
63
+ type: "button",
64
+ role: "switch",
65
+ "aria-checked": checked,
66
+ disabled,
67
+ className,
68
+ children,
69
+ onClick() {
70
+ const next = !checked;
71
+ setChecked(next);
72
+ onCheckedChange?.(next);
73
+ }
74
+ },
75
+ rest
76
+ ]
77
+ });
78
+ return /* @__PURE__ */ jsxs(SwitchRootContext.Provider, { value: state, children: [
79
+ element,
80
+ name !== void 0 && checked ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value }) : null
81
+ ] });
82
+ }
83
+
84
+ // src/switch/thumb/SwitchThumb.tsx
85
+ function SwitchThumb({
86
+ className,
87
+ children,
88
+ render,
89
+ ...rest
90
+ }) {
91
+ const state = useSwitchRootContext();
92
+ return useRender({
93
+ render,
94
+ defaultTagName: "span",
95
+ state,
96
+ stateAttributes: switchStateAttributes,
97
+ props: [{ "aria-hidden": true, className, children }, rest]
98
+ });
99
+ }
100
+ export {
101
+ index_parts_exports as Switch
102
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arun-dev/headless",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Unstyled React behaviour primitives — the render engine and state plumbing @arun-dev/ui is built on. Ships no CSS and no class names.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -33,6 +33,16 @@
33
33
  "types": "./dist/index.d.cts",
34
34
  "default": "./dist/index.cjs"
35
35
  }
36
+ },
37
+ "./switch": {
38
+ "import": {
39
+ "types": "./dist/switch/index.d.ts",
40
+ "default": "./dist/switch/index.js"
41
+ },
42
+ "require": {
43
+ "types": "./dist/switch/index.d.cts",
44
+ "default": "./dist/switch/index.cjs"
45
+ }
36
46
  }
37
47
  },
38
48
  "publishConfig": {