@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.
- package/dist/chunk-DAO4JK6U.js +136 -0
- package/dist/index.js +7 -123
- package/dist/switch/index.cjs +246 -0
- package/dist/switch/index.d.cts +76 -0
- package/dist/switch/index.d.ts +76 -0
- package/dist/switch/index.js +102 -0
- package/package.json +11 -1
|
@@ -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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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.
|
|
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": {
|