@arun-dev/headless 0.3.0 → 0.4.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/README.md +11 -3
- package/dist/{chunk-LU4ZQE3V.js → chunk-I477HMLD.js} +59 -4
- package/dist/index.cjs +18 -3
- package/dist/index.d.cts +24 -1
- package/dist/index.d.ts +24 -1
- package/dist/index.js +3 -39
- package/dist/switch/index.cjs +53 -5
- package/dist/switch/index.js +6 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,9 +72,17 @@ Every part takes a `render` prop to change the element, and spreads unrecognised
|
|
|
72
72
|
<Switch.Root render={<Tooltip.Trigger />} />
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
`className` is concatenated,
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
`className` is concatenated, `style` is merged, and refs are merged — so a `ref` on the `render`
|
|
76
|
+
element and a `ref` on the component both receive the node.
|
|
77
|
+
|
|
78
|
+
Event handlers are chained rather than replaced, and **your handler runs before the component's**,
|
|
79
|
+
so you can stop it:
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
<Switch.Root onClick={(event) => event.preventComponentHandler()} />
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`preventDefault()` is left alone — it still means only "cancel the browser's default action".
|
|
78
86
|
|
|
79
87
|
## Engine
|
|
80
88
|
|
|
@@ -30,6 +30,18 @@ function mergeRefs(...refs) {
|
|
|
30
30
|
};
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
+
function isSyntheticEvent(event) {
|
|
34
|
+
return typeof event === "object" && event !== null && "nativeEvent" in event;
|
|
35
|
+
}
|
|
36
|
+
function makeEventPreventable(event) {
|
|
37
|
+
const target = event;
|
|
38
|
+
target.preventComponentHandler = () => {
|
|
39
|
+
target.componentHandlerPrevented = true;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function isComponentHandlerPrevented(event) {
|
|
43
|
+
return typeof event === "object" && event !== null && event.componentHandlerPrevented === true;
|
|
44
|
+
}
|
|
33
45
|
function mergeProps(...objects) {
|
|
34
46
|
const merged = {};
|
|
35
47
|
for (const props of objects) {
|
|
@@ -38,10 +50,13 @@ function mergeProps(...objects) {
|
|
|
38
50
|
const value = props[key];
|
|
39
51
|
if (value === void 0) continue;
|
|
40
52
|
if (isEventHandler(key) && typeof value === "function") {
|
|
41
|
-
const
|
|
42
|
-
merged[key] = typeof
|
|
43
|
-
|
|
53
|
+
const earlier = merged[key];
|
|
54
|
+
merged[key] = typeof earlier === "function" ? (...args) => {
|
|
55
|
+
const event = args[0];
|
|
56
|
+
if (isSyntheticEvent(event)) makeEventPreventable(event);
|
|
44
57
|
value(...args);
|
|
58
|
+
if (isComponentHandlerPrevented(event)) return;
|
|
59
|
+
earlier(...args);
|
|
45
60
|
} : value;
|
|
46
61
|
} else if (key === "className") {
|
|
47
62
|
merged.className = merged.className ? `${String(merged.className)} ${String(value)}` : value;
|
|
@@ -127,6 +142,44 @@ function useControlled({
|
|
|
127
142
|
return [value, setValue];
|
|
128
143
|
}
|
|
129
144
|
|
|
145
|
+
// src/useButton.ts
|
|
146
|
+
var ACTIVATION_HANDLERS = [
|
|
147
|
+
"onClick",
|
|
148
|
+
"onDoubleClick",
|
|
149
|
+
"onMouseDown",
|
|
150
|
+
"onMouseUp",
|
|
151
|
+
"onPointerDown",
|
|
152
|
+
"onPointerUp",
|
|
153
|
+
"onTouchStart",
|
|
154
|
+
"onTouchEnd",
|
|
155
|
+
"onKeyDown",
|
|
156
|
+
"onKeyUp",
|
|
157
|
+
"onKeyPress"
|
|
158
|
+
];
|
|
159
|
+
function useButton({ disabled = false, native, props = {} }) {
|
|
160
|
+
if (!disabled) return props;
|
|
161
|
+
if (native) return { ...props, disabled: true, "data-disabled": "" };
|
|
162
|
+
const sanitised = {};
|
|
163
|
+
for (const key of Object.keys(props)) {
|
|
164
|
+
if (key === "href" || ACTIVATION_HANDLERS.includes(key)) continue;
|
|
165
|
+
sanitised[key] = props[key];
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
...sanitised,
|
|
169
|
+
"aria-disabled": true,
|
|
170
|
+
"data-disabled": "",
|
|
171
|
+
// Mirrors a native disabled button, which is not focusable.
|
|
172
|
+
tabIndex: -1
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function retractActivationProps(props) {
|
|
176
|
+
const overrides = {};
|
|
177
|
+
for (const key of Object.keys(props)) {
|
|
178
|
+
if (key === "href" || ACTIVATION_HANDLERS.includes(key)) overrides[key] = void 0;
|
|
179
|
+
}
|
|
180
|
+
return overrides;
|
|
181
|
+
}
|
|
182
|
+
|
|
130
183
|
export {
|
|
131
184
|
__export,
|
|
132
185
|
mergeProps,
|
|
@@ -134,5 +187,7 @@ export {
|
|
|
134
187
|
booleanAttribute,
|
|
135
188
|
disabledAttribute,
|
|
136
189
|
useRender,
|
|
137
|
-
useControlled
|
|
190
|
+
useControlled,
|
|
191
|
+
useButton,
|
|
192
|
+
retractActivationProps
|
|
138
193
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -57,6 +57,18 @@ function mergeRefs(...refs) {
|
|
|
57
57
|
};
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
|
+
function isSyntheticEvent(event) {
|
|
61
|
+
return typeof event === "object" && event !== null && "nativeEvent" in event;
|
|
62
|
+
}
|
|
63
|
+
function makeEventPreventable(event) {
|
|
64
|
+
const target = event;
|
|
65
|
+
target.preventComponentHandler = () => {
|
|
66
|
+
target.componentHandlerPrevented = true;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function isComponentHandlerPrevented(event) {
|
|
70
|
+
return typeof event === "object" && event !== null && event.componentHandlerPrevented === true;
|
|
71
|
+
}
|
|
60
72
|
function mergeProps(...objects) {
|
|
61
73
|
const merged = {};
|
|
62
74
|
for (const props of objects) {
|
|
@@ -65,10 +77,13 @@ function mergeProps(...objects) {
|
|
|
65
77
|
const value = props[key];
|
|
66
78
|
if (value === void 0) continue;
|
|
67
79
|
if (isEventHandler(key) && typeof value === "function") {
|
|
68
|
-
const
|
|
69
|
-
merged[key] = typeof
|
|
70
|
-
|
|
80
|
+
const earlier = merged[key];
|
|
81
|
+
merged[key] = typeof earlier === "function" ? (...args) => {
|
|
82
|
+
const event = args[0];
|
|
83
|
+
if (isSyntheticEvent(event)) makeEventPreventable(event);
|
|
71
84
|
value(...args);
|
|
85
|
+
if (isComponentHandlerPrevented(event)) return;
|
|
86
|
+
earlier(...args);
|
|
72
87
|
} : value;
|
|
73
88
|
} else if (key === "className") {
|
|
74
89
|
merged.className = merged.className ? `${String(merged.className)} ${String(value)}` : value;
|
package/dist/index.d.cts
CHANGED
|
@@ -8,6 +8,18 @@ import { ReactElement } from 'react';
|
|
|
8
8
|
* kind of prop is combined rather than replaced.
|
|
9
9
|
*/
|
|
10
10
|
type UnknownProps = Record<string, unknown>;
|
|
11
|
+
/**
|
|
12
|
+
* An event a consumer's handler can use to stop the component's own handler running.
|
|
13
|
+
*
|
|
14
|
+
* `preventDefault()` is deliberately not overloaded for this: it already means "cancel
|
|
15
|
+
* the browser's default action", which is a different statement. A link inside a
|
|
16
|
+
* component may well want one without the other.
|
|
17
|
+
*/
|
|
18
|
+
type ComponentEvent<E = unknown> = E & {
|
|
19
|
+
/** Stops the component's own handler for this event. */
|
|
20
|
+
preventComponentHandler(): void;
|
|
21
|
+
readonly componentHandlerPrevented?: boolean;
|
|
22
|
+
};
|
|
11
23
|
/**
|
|
12
24
|
* Merge prop objects left to right. Later objects win for plain values, but:
|
|
13
25
|
* - event handlers are chained rather than replaced
|
|
@@ -17,6 +29,17 @@ type UnknownProps = Record<string, unknown>;
|
|
|
17
29
|
*
|
|
18
30
|
* `undefined` values are skipped, so an absent key on a later object never
|
|
19
31
|
* clobbers a value set by an earlier one.
|
|
32
|
+
*
|
|
33
|
+
* **Event handlers run right to left** — the last object's handler first, the first
|
|
34
|
+
* object's last. Since a component passes its own props first and the consumer's last,
|
|
35
|
+
* the consumer's handler runs before the component's and can stop it:
|
|
36
|
+
*
|
|
37
|
+
* <Switch.Root onClick={(event) => event.preventComponentHandler()} />
|
|
38
|
+
*
|
|
39
|
+
* That is the opposite direction to plain values, where later objects win. The escape
|
|
40
|
+
* hatch belongs to the consumer: a component already decides which props reach the
|
|
41
|
+
* element, so it never needs the chain to suppress anything — it declines to attach the
|
|
42
|
+
* handler instead. See docs/architecture.md decision 8.
|
|
20
43
|
*/
|
|
21
44
|
declare function mergeProps(...objects: (UnknownProps | undefined)[]): UnknownProps;
|
|
22
45
|
|
|
@@ -124,4 +147,4 @@ declare function useButton({ disabled, native, props }: UseButtonParams): Unknow
|
|
|
124
147
|
*/
|
|
125
148
|
declare function retractActivationProps(props: UnknownProps): UnknownProps;
|
|
126
149
|
|
|
127
|
-
export { type StateAttributeMapping, type UnknownProps, type UseButtonParams, type UseRenderParams, booleanAttribute, disabledAttribute, getStateAttributes, mergeProps, retractActivationProps, useButton, useControlled, useRender };
|
|
150
|
+
export { type ComponentEvent, type StateAttributeMapping, type UnknownProps, type UseButtonParams, type UseRenderParams, booleanAttribute, disabledAttribute, getStateAttributes, mergeProps, retractActivationProps, useButton, useControlled, useRender };
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,18 @@ import { ReactElement } from 'react';
|
|
|
8
8
|
* kind of prop is combined rather than replaced.
|
|
9
9
|
*/
|
|
10
10
|
type UnknownProps = Record<string, unknown>;
|
|
11
|
+
/**
|
|
12
|
+
* An event a consumer's handler can use to stop the component's own handler running.
|
|
13
|
+
*
|
|
14
|
+
* `preventDefault()` is deliberately not overloaded for this: it already means "cancel
|
|
15
|
+
* the browser's default action", which is a different statement. A link inside a
|
|
16
|
+
* component may well want one without the other.
|
|
17
|
+
*/
|
|
18
|
+
type ComponentEvent<E = unknown> = E & {
|
|
19
|
+
/** Stops the component's own handler for this event. */
|
|
20
|
+
preventComponentHandler(): void;
|
|
21
|
+
readonly componentHandlerPrevented?: boolean;
|
|
22
|
+
};
|
|
11
23
|
/**
|
|
12
24
|
* Merge prop objects left to right. Later objects win for plain values, but:
|
|
13
25
|
* - event handlers are chained rather than replaced
|
|
@@ -17,6 +29,17 @@ type UnknownProps = Record<string, unknown>;
|
|
|
17
29
|
*
|
|
18
30
|
* `undefined` values are skipped, so an absent key on a later object never
|
|
19
31
|
* clobbers a value set by an earlier one.
|
|
32
|
+
*
|
|
33
|
+
* **Event handlers run right to left** — the last object's handler first, the first
|
|
34
|
+
* object's last. Since a component passes its own props first and the consumer's last,
|
|
35
|
+
* the consumer's handler runs before the component's and can stop it:
|
|
36
|
+
*
|
|
37
|
+
* <Switch.Root onClick={(event) => event.preventComponentHandler()} />
|
|
38
|
+
*
|
|
39
|
+
* That is the opposite direction to plain values, where later objects win. The escape
|
|
40
|
+
* hatch belongs to the consumer: a component already decides which props reach the
|
|
41
|
+
* element, so it never needs the chain to suppress anything — it declines to attach the
|
|
42
|
+
* handler instead. See docs/architecture.md decision 8.
|
|
20
43
|
*/
|
|
21
44
|
declare function mergeProps(...objects: (UnknownProps | undefined)[]): UnknownProps;
|
|
22
45
|
|
|
@@ -124,4 +147,4 @@ declare function useButton({ disabled, native, props }: UseButtonParams): Unknow
|
|
|
124
147
|
*/
|
|
125
148
|
declare function retractActivationProps(props: UnknownProps): UnknownProps;
|
|
126
149
|
|
|
127
|
-
export { type StateAttributeMapping, type UnknownProps, type UseButtonParams, type UseRenderParams, booleanAttribute, disabledAttribute, getStateAttributes, mergeProps, retractActivationProps, useButton, useControlled, useRender };
|
|
150
|
+
export { type ComponentEvent, type StateAttributeMapping, type UnknownProps, type UseButtonParams, type UseRenderParams, booleanAttribute, disabledAttribute, getStateAttributes, mergeProps, retractActivationProps, useButton, useControlled, useRender };
|
package/dist/index.js
CHANGED
|
@@ -3,47 +3,11 @@ import {
|
|
|
3
3
|
disabledAttribute,
|
|
4
4
|
getStateAttributes,
|
|
5
5
|
mergeProps,
|
|
6
|
+
retractActivationProps,
|
|
7
|
+
useButton,
|
|
6
8
|
useControlled,
|
|
7
9
|
useRender
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
|
|
10
|
-
// src/useButton.ts
|
|
11
|
-
var ACTIVATION_HANDLERS = [
|
|
12
|
-
"onClick",
|
|
13
|
-
"onDoubleClick",
|
|
14
|
-
"onMouseDown",
|
|
15
|
-
"onMouseUp",
|
|
16
|
-
"onPointerDown",
|
|
17
|
-
"onPointerUp",
|
|
18
|
-
"onTouchStart",
|
|
19
|
-
"onTouchEnd",
|
|
20
|
-
"onKeyDown",
|
|
21
|
-
"onKeyUp",
|
|
22
|
-
"onKeyPress"
|
|
23
|
-
];
|
|
24
|
-
function useButton({ disabled = false, native, props = {} }) {
|
|
25
|
-
if (!disabled) return props;
|
|
26
|
-
if (native) return { ...props, disabled: true, "data-disabled": "" };
|
|
27
|
-
const sanitised = {};
|
|
28
|
-
for (const key of Object.keys(props)) {
|
|
29
|
-
if (key === "href" || ACTIVATION_HANDLERS.includes(key)) continue;
|
|
30
|
-
sanitised[key] = props[key];
|
|
31
|
-
}
|
|
32
|
-
return {
|
|
33
|
-
...sanitised,
|
|
34
|
-
"aria-disabled": true,
|
|
35
|
-
"data-disabled": "",
|
|
36
|
-
// Mirrors a native disabled button, which is not focusable.
|
|
37
|
-
tabIndex: -1
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
function retractActivationProps(props) {
|
|
41
|
-
const overrides = {};
|
|
42
|
-
for (const key of Object.keys(props)) {
|
|
43
|
-
if (key === "href" || ACTIVATION_HANDLERS.includes(key)) overrides[key] = void 0;
|
|
44
|
-
}
|
|
45
|
-
return overrides;
|
|
46
|
-
}
|
|
10
|
+
} from "./chunk-I477HMLD.js";
|
|
47
11
|
export {
|
|
48
12
|
booleanAttribute,
|
|
49
13
|
disabledAttribute,
|
package/dist/switch/index.cjs
CHANGED
|
@@ -34,6 +34,37 @@ __export(index_parts_exports, {
|
|
|
34
34
|
// src/switch/root/SwitchRoot.tsx
|
|
35
35
|
var import_react4 = require("react");
|
|
36
36
|
|
|
37
|
+
// src/useButton.ts
|
|
38
|
+
var ACTIVATION_HANDLERS = [
|
|
39
|
+
"onClick",
|
|
40
|
+
"onDoubleClick",
|
|
41
|
+
"onMouseDown",
|
|
42
|
+
"onMouseUp",
|
|
43
|
+
"onPointerDown",
|
|
44
|
+
"onPointerUp",
|
|
45
|
+
"onTouchStart",
|
|
46
|
+
"onTouchEnd",
|
|
47
|
+
"onKeyDown",
|
|
48
|
+
"onKeyUp",
|
|
49
|
+
"onKeyPress"
|
|
50
|
+
];
|
|
51
|
+
function useButton({ disabled = false, native, props = {} }) {
|
|
52
|
+
if (!disabled) return props;
|
|
53
|
+
if (native) return { ...props, disabled: true, "data-disabled": "" };
|
|
54
|
+
const sanitised = {};
|
|
55
|
+
for (const key of Object.keys(props)) {
|
|
56
|
+
if (key === "href" || ACTIVATION_HANDLERS.includes(key)) continue;
|
|
57
|
+
sanitised[key] = props[key];
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
...sanitised,
|
|
61
|
+
"aria-disabled": true,
|
|
62
|
+
"data-disabled": "",
|
|
63
|
+
// Mirrors a native disabled button, which is not focusable.
|
|
64
|
+
tabIndex: -1
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
37
68
|
// src/core/useControlled.ts
|
|
38
69
|
var import_react = require("react");
|
|
39
70
|
function useControlled({
|
|
@@ -96,6 +127,18 @@ function mergeRefs(...refs) {
|
|
|
96
127
|
};
|
|
97
128
|
};
|
|
98
129
|
}
|
|
130
|
+
function isSyntheticEvent(event) {
|
|
131
|
+
return typeof event === "object" && event !== null && "nativeEvent" in event;
|
|
132
|
+
}
|
|
133
|
+
function makeEventPreventable(event) {
|
|
134
|
+
const target = event;
|
|
135
|
+
target.preventComponentHandler = () => {
|
|
136
|
+
target.componentHandlerPrevented = true;
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function isComponentHandlerPrevented(event) {
|
|
140
|
+
return typeof event === "object" && event !== null && event.componentHandlerPrevented === true;
|
|
141
|
+
}
|
|
99
142
|
function mergeProps(...objects) {
|
|
100
143
|
const merged = {};
|
|
101
144
|
for (const props of objects) {
|
|
@@ -104,10 +147,13 @@ function mergeProps(...objects) {
|
|
|
104
147
|
const value = props[key];
|
|
105
148
|
if (value === void 0) continue;
|
|
106
149
|
if (isEventHandler(key) && typeof value === "function") {
|
|
107
|
-
const
|
|
108
|
-
merged[key] = typeof
|
|
109
|
-
|
|
150
|
+
const earlier = merged[key];
|
|
151
|
+
merged[key] = typeof earlier === "function" ? (...args) => {
|
|
152
|
+
const event = args[0];
|
|
153
|
+
if (isSyntheticEvent(event)) makeEventPreventable(event);
|
|
110
154
|
value(...args);
|
|
155
|
+
if (isComponentHandlerPrevented(event)) return;
|
|
156
|
+
earlier(...args);
|
|
111
157
|
} : value;
|
|
112
158
|
} else if (key === "className") {
|
|
113
159
|
merged.className = merged.className ? `${String(merged.className)} ${String(value)}` : value;
|
|
@@ -197,6 +243,8 @@ function SwitchRoot({
|
|
|
197
243
|
state: "checked"
|
|
198
244
|
});
|
|
199
245
|
const state = (0, import_react4.useMemo)(() => ({ checked, disabled }), [checked, disabled]);
|
|
246
|
+
const isNativeButton = render === void 0 || render.type === "button";
|
|
247
|
+
const consumerProps = useButton({ disabled, native: isNativeButton, props: rest });
|
|
200
248
|
const element = useRender({
|
|
201
249
|
render,
|
|
202
250
|
defaultTagName: "button",
|
|
@@ -207,16 +255,16 @@ function SwitchRoot({
|
|
|
207
255
|
type: "button",
|
|
208
256
|
role: "switch",
|
|
209
257
|
"aria-checked": checked,
|
|
210
|
-
disabled,
|
|
211
258
|
className,
|
|
212
259
|
children,
|
|
213
260
|
onClick() {
|
|
261
|
+
if (disabled) return;
|
|
214
262
|
const next = !checked;
|
|
215
263
|
setChecked(next);
|
|
216
264
|
onCheckedChange?.(next);
|
|
217
265
|
}
|
|
218
266
|
},
|
|
219
|
-
|
|
267
|
+
consumerProps
|
|
220
268
|
]
|
|
221
269
|
});
|
|
222
270
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SwitchRootContext.Provider, { value: state, children: [
|
package/dist/switch/index.js
CHANGED
|
@@ -2,9 +2,10 @@ import {
|
|
|
2
2
|
__export,
|
|
3
3
|
booleanAttribute,
|
|
4
4
|
disabledAttribute,
|
|
5
|
+
useButton,
|
|
5
6
|
useControlled,
|
|
6
7
|
useRender
|
|
7
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-I477HMLD.js";
|
|
8
9
|
|
|
9
10
|
// src/switch/index.parts.ts
|
|
10
11
|
var index_parts_exports = {};
|
|
@@ -54,6 +55,8 @@ function SwitchRoot({
|
|
|
54
55
|
state: "checked"
|
|
55
56
|
});
|
|
56
57
|
const state = useMemo(() => ({ checked, disabled }), [checked, disabled]);
|
|
58
|
+
const isNativeButton = render === void 0 || render.type === "button";
|
|
59
|
+
const consumerProps = useButton({ disabled, native: isNativeButton, props: rest });
|
|
57
60
|
const element = useRender({
|
|
58
61
|
render,
|
|
59
62
|
defaultTagName: "button",
|
|
@@ -64,16 +67,16 @@ function SwitchRoot({
|
|
|
64
67
|
type: "button",
|
|
65
68
|
role: "switch",
|
|
66
69
|
"aria-checked": checked,
|
|
67
|
-
disabled,
|
|
68
70
|
className,
|
|
69
71
|
children,
|
|
70
72
|
onClick() {
|
|
73
|
+
if (disabled) return;
|
|
71
74
|
const next = !checked;
|
|
72
75
|
setChecked(next);
|
|
73
76
|
onCheckedChange?.(next);
|
|
74
77
|
}
|
|
75
78
|
},
|
|
76
|
-
|
|
79
|
+
consumerProps
|
|
77
80
|
]
|
|
78
81
|
});
|
|
79
82
|
return /* @__PURE__ */ jsxs(SwitchRootContext.Provider, { value: state, children: [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arun-dev/headless",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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",
|