@arun-dev/headless 2.1.0 → 3.0.1
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 +38 -22
- package/dist/button/index.cjs +25 -136
- package/dist/button/index.d.cts +34 -23
- package/dist/button/index.d.ts +34 -23
- package/dist/button/index.js +23 -16
- package/dist/{chunk-PYLPZVWQ.js → chunk-OQTWWZ4B.js} +3 -38
- package/dist/index.cjs +3 -43
- package/dist/index.d.cts +18 -61
- package/dist/index.d.ts +18 -61
- package/dist/index.js +1 -7
- package/dist/switch/index.cjs +117 -199
- package/dist/switch/index.d.cts +41 -42
- package/dist/switch/index.d.ts +41 -42
- package/dist/switch/index.js +74 -40
- package/package.json +2 -2
- package/dist/chunk-TNMGZWHA.js +0 -96
package/dist/switch/index.cjs
CHANGED
|
@@ -31,11 +31,44 @@ __export(index_parts_exports, {
|
|
|
31
31
|
Thumb: () => SwitchThumb
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
// src/switch/
|
|
35
|
-
var
|
|
34
|
+
// src/switch/SwitchRoot.tsx
|
|
35
|
+
var import_react4 = require("react");
|
|
36
36
|
|
|
37
|
-
// src/
|
|
37
|
+
// src/core/useControlled.ts
|
|
38
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");
|
|
39
72
|
|
|
40
73
|
// src/core/mergeProps.ts
|
|
41
74
|
function isEventHandler(key) {
|
|
@@ -69,18 +102,9 @@ function isSyntheticEvent(event) {
|
|
|
69
102
|
function makeEventPreventable(event) {
|
|
70
103
|
if (Object.prototype.hasOwnProperty.call(event, "preventComponentHandler")) return;
|
|
71
104
|
const target = event;
|
|
72
|
-
|
|
105
|
+
target.preventComponentHandler = () => {
|
|
73
106
|
target.componentHandlerPrevented = true;
|
|
74
107
|
};
|
|
75
|
-
target.preventComponentHandler = prevent;
|
|
76
|
-
const native = event.nativeEvent;
|
|
77
|
-
const stopImmediate = native?.stopImmediatePropagation?.bind(native);
|
|
78
|
-
if (native && stopImmediate) {
|
|
79
|
-
native.stopImmediatePropagation = () => {
|
|
80
|
-
stopImmediate();
|
|
81
|
-
prevent();
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
108
|
}
|
|
85
109
|
function isComponentHandlerPrevented(event) {
|
|
86
110
|
return typeof event === "object" && event !== null && event.componentHandlerPrevented === true;
|
|
@@ -118,189 +142,45 @@ function mergeProps(...objects) {
|
|
|
118
142
|
return merged;
|
|
119
143
|
}
|
|
120
144
|
|
|
121
|
-
// src/useButton.ts
|
|
122
|
-
var ACTIVATION_HANDLERS = [
|
|
123
|
-
"onClick",
|
|
124
|
-
"onDoubleClick",
|
|
125
|
-
"onMouseDown",
|
|
126
|
-
"onMouseUp",
|
|
127
|
-
"onPointerDown",
|
|
128
|
-
"onPointerUp",
|
|
129
|
-
"onTouchStart",
|
|
130
|
-
"onTouchEnd",
|
|
131
|
-
"onKeyDown",
|
|
132
|
-
"onKeyUp",
|
|
133
|
-
"onKeyPress"
|
|
134
|
-
];
|
|
135
|
-
function activationHandlers() {
|
|
136
|
-
return {
|
|
137
|
-
onKeyDown(event) {
|
|
138
|
-
if (event.target !== event.currentTarget || event.defaultPrevented) return;
|
|
139
|
-
if (event.key === " ") {
|
|
140
|
-
event.preventDefault();
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
if (event.key === "Enter") {
|
|
144
|
-
event.preventDefault();
|
|
145
|
-
click(event.currentTarget);
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
|
-
onKeyUp(event) {
|
|
149
|
-
if (event.target !== event.currentTarget || event.defaultPrevented) return;
|
|
150
|
-
if (event.key === " ") click(event.currentTarget);
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
function click(element) {
|
|
155
|
-
if (element && typeof element.click === "function") {
|
|
156
|
-
element.click();
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
function useButton({
|
|
160
|
-
disabled = false,
|
|
161
|
-
native,
|
|
162
|
-
props = {}
|
|
163
|
-
}) {
|
|
164
|
-
const elementRef = (0, import_react.useRef)(null);
|
|
165
|
-
(0, import_react.useEffect)(() => {
|
|
166
|
-
if (process.env.NODE_ENV === "production") return;
|
|
167
|
-
const element = elementRef.current;
|
|
168
|
-
if (!element) return;
|
|
169
|
-
const isButtonTag = element.tagName === "BUTTON";
|
|
170
|
-
if (native && !isButtonTag) {
|
|
171
|
-
console.error(
|
|
172
|
-
`A component expected a native <button> but rendered <${element.tagName.toLowerCase()}>. Focus, keyboard activation and \`disabled\` will not behave natively.`
|
|
173
|
-
);
|
|
174
|
-
} else if (!native && isButtonTag) {
|
|
175
|
-
console.error(
|
|
176
|
-
"A component rendered a native <button> while treating it as a non-native element, so it carries synthesised attributes it does not need."
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
}, [native]);
|
|
180
|
-
return { props: buttonProps({ disabled, native, props }), ref: elementRef };
|
|
181
|
-
}
|
|
182
|
-
function buttonProps({ disabled, native, props }) {
|
|
183
|
-
if (!disabled) {
|
|
184
|
-
return native ? mergeProps({ type: "button" }, props) : mergeProps({ tabIndex: 0 }, activationHandlers(), props);
|
|
185
|
-
}
|
|
186
|
-
if (native) return mergeProps({ type: "button" }, props, { disabled: true, "data-disabled": "" });
|
|
187
|
-
const sanitised = {};
|
|
188
|
-
for (const key of Object.keys(props)) {
|
|
189
|
-
if (key === "href" || ACTIVATION_HANDLERS.includes(key)) continue;
|
|
190
|
-
sanitised[key] = props[key];
|
|
191
|
-
}
|
|
192
|
-
return {
|
|
193
|
-
...sanitised,
|
|
194
|
-
"aria-disabled": true,
|
|
195
|
-
"data-disabled": "",
|
|
196
|
-
// Mirrors a native disabled button, which is not focusable.
|
|
197
|
-
tabIndex: -1
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
function retractActivationProps(props) {
|
|
201
|
-
const overrides = {};
|
|
202
|
-
for (const key of Object.keys(props)) {
|
|
203
|
-
if (key === "href" || ACTIVATION_HANDLERS.includes(key)) overrides[key] = void 0;
|
|
204
|
-
}
|
|
205
|
-
return overrides;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// src/core/useControlled.ts
|
|
209
|
-
var import_react2 = require("react");
|
|
210
|
-
function useControlled({
|
|
211
|
-
controlled,
|
|
212
|
-
default: defaultValue,
|
|
213
|
-
name = "Component",
|
|
214
|
-
state = "value"
|
|
215
|
-
}) {
|
|
216
|
-
const { current: isControlled } = (0, import_react2.useRef)(controlled !== void 0);
|
|
217
|
-
const [uncontrolled, setUncontrolled] = (0, import_react2.useState)(defaultValue);
|
|
218
|
-
const defaultRef = (0, import_react2.useRef)(defaultValue);
|
|
219
|
-
const value = isControlled ? controlled : uncontrolled;
|
|
220
|
-
if (process.env.NODE_ENV !== "production") {
|
|
221
|
-
if (isControlled !== (controlled !== void 0)) {
|
|
222
|
-
console.error(
|
|
223
|
-
`${name}: cannot switch between controlled and uncontrolled \`${state}\`. Decide which one this component is for the whole of its life.`
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
if (!isControlled && defaultRef.current !== defaultValue) {
|
|
227
|
-
console.error(
|
|
228
|
-
`${name}: cannot change the default \`${state}\` after mount. It is only read once, so later changes are silently ignored.`
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
const setValue = (0, import_react2.useCallback)(
|
|
233
|
-
(next) => {
|
|
234
|
-
if (!isControlled) setUncontrolled(next);
|
|
235
|
-
},
|
|
236
|
-
[isControlled]
|
|
237
|
-
);
|
|
238
|
-
return [value, setValue];
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// src/core/useRender.ts
|
|
242
|
-
var import_react3 = require("react");
|
|
243
|
-
|
|
244
|
-
// src/core/stateAttributes.ts
|
|
245
|
-
function getStateAttributes(state, mapping) {
|
|
246
|
-
if (!mapping) return {};
|
|
247
|
-
const attributes = {};
|
|
248
|
-
for (const key of Object.keys(state)) {
|
|
249
|
-
const toAttributes = mapping[key];
|
|
250
|
-
if (!toAttributes) continue;
|
|
251
|
-
const result = toAttributes(state[key]);
|
|
252
|
-
if (result) Object.assign(attributes, result);
|
|
253
|
-
}
|
|
254
|
-
return attributes;
|
|
255
|
-
}
|
|
256
|
-
function booleanAttribute(whenTrue, whenFalse) {
|
|
257
|
-
return (value) => {
|
|
258
|
-
if (value) return { [whenTrue]: "" };
|
|
259
|
-
return whenFalse ? { [whenFalse]: "" } : null;
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
var disabledAttribute = booleanAttribute("data-disabled");
|
|
263
|
-
|
|
264
145
|
// src/core/useRender.ts
|
|
265
146
|
function useRender({
|
|
266
147
|
render,
|
|
267
148
|
defaultTagName,
|
|
268
149
|
props,
|
|
269
|
-
consumerProps
|
|
270
|
-
state,
|
|
271
|
-
stateAttributes
|
|
150
|
+
consumerProps
|
|
272
151
|
}) {
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
return (0, import_react3.cloneElement)(render, mergeProps(merged, render.props));
|
|
152
|
+
const merged = mergeProps(props, consumerProps);
|
|
153
|
+
if ((0, import_react2.isValidElement)(render)) {
|
|
154
|
+
return (0, import_react2.cloneElement)(render, mergeProps(merged, render.props));
|
|
277
155
|
}
|
|
278
|
-
return (0,
|
|
156
|
+
return (0, import_react2.createElement)(defaultTagName, merged);
|
|
279
157
|
}
|
|
280
158
|
|
|
281
159
|
// src/switch/SwitchRootContext.ts
|
|
282
|
-
var
|
|
283
|
-
var SwitchRootContext = (0,
|
|
160
|
+
var import_react3 = require("react");
|
|
161
|
+
var SwitchRootContext = (0, import_react3.createContext)(null);
|
|
284
162
|
function useSwitchRootContext() {
|
|
285
|
-
const context = (0,
|
|
163
|
+
const context = (0, import_react3.useContext)(SwitchRootContext);
|
|
286
164
|
if (context === null) {
|
|
287
165
|
throw new Error("<Switch.Thumb> must be rendered inside <Switch.Root>.");
|
|
288
166
|
}
|
|
289
167
|
return context;
|
|
290
168
|
}
|
|
291
169
|
|
|
292
|
-
// src/switch/
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
170
|
+
// src/switch/switchDataAttributes.ts
|
|
171
|
+
function switchDataAttributes({ checked, disabled }) {
|
|
172
|
+
return {
|
|
173
|
+
"data-checked": checked ? "" : void 0,
|
|
174
|
+
"data-unchecked": checked ? void 0 : "",
|
|
175
|
+
"data-disabled": disabled ? "" : void 0
|
|
176
|
+
};
|
|
177
|
+
}
|
|
297
178
|
|
|
298
|
-
// src/switch/
|
|
179
|
+
// src/switch/SwitchRoot.tsx
|
|
299
180
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
300
181
|
function SwitchRoot({
|
|
301
182
|
checked: checkedProp,
|
|
302
183
|
defaultChecked,
|
|
303
|
-
nativeButton,
|
|
304
184
|
onCheckedChange,
|
|
305
185
|
disabled = false,
|
|
306
186
|
name,
|
|
@@ -316,54 +196,92 @@ function SwitchRoot({
|
|
|
316
196
|
name: "Switch.Root",
|
|
317
197
|
state: "checked"
|
|
318
198
|
});
|
|
319
|
-
const state = (0,
|
|
320
|
-
const
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
props: rest
|
|
325
|
-
});
|
|
326
|
-
const safeRender = disabled && !isNativeButton && render !== void 0 ? (0, import_react5.cloneElement)(render, retractActivationProps(render.props)) : render;
|
|
199
|
+
const state = (0, import_react4.useMemo)(() => ({ checked, disabled }), [checked, disabled]);
|
|
200
|
+
const elementRef = (0, import_react4.useRef)(null);
|
|
201
|
+
const inputRef = (0, import_react4.useRef)(null);
|
|
202
|
+
useNativeButtonWarning(elementRef);
|
|
203
|
+
useFormReset({ elementRef, inputRef, checked, setChecked, onCheckedChange });
|
|
327
204
|
const element = useRender({
|
|
328
|
-
render
|
|
205
|
+
render,
|
|
329
206
|
defaultTagName: "button",
|
|
330
|
-
state,
|
|
331
|
-
stateAttributes: switchStateAttributes,
|
|
332
207
|
props: {
|
|
208
|
+
// Without this a switch inside a form would submit it on every toggle.
|
|
209
|
+
type: "button",
|
|
333
210
|
role: "switch",
|
|
334
211
|
"aria-checked": checked,
|
|
212
|
+
// The platform suppresses activation, focus and the click handler below.
|
|
213
|
+
disabled: disabled || void 0,
|
|
214
|
+
...switchDataAttributes(state),
|
|
335
215
|
className,
|
|
336
216
|
children,
|
|
337
|
-
ref:
|
|
217
|
+
ref: elementRef,
|
|
338
218
|
onClick() {
|
|
339
|
-
if (disabled) return;
|
|
340
219
|
const next = !checked;
|
|
341
220
|
setChecked(next);
|
|
342
221
|
onCheckedChange?.(next);
|
|
343
222
|
}
|
|
344
223
|
},
|
|
345
|
-
consumerProps
|
|
224
|
+
consumerProps: rest
|
|
346
225
|
});
|
|
347
226
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SwitchRootContext.Provider, { value: state, children: [
|
|
348
227
|
element,
|
|
349
|
-
name !== void 0
|
|
228
|
+
name !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
229
|
+
"input",
|
|
230
|
+
{
|
|
231
|
+
ref: inputRef,
|
|
232
|
+
type: "checkbox",
|
|
233
|
+
hidden: true,
|
|
234
|
+
readOnly: true,
|
|
235
|
+
name,
|
|
236
|
+
value,
|
|
237
|
+
checked,
|
|
238
|
+
disabled
|
|
239
|
+
}
|
|
240
|
+
) : null
|
|
350
241
|
] });
|
|
351
242
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
...rest
|
|
243
|
+
function useFormReset({
|
|
244
|
+
elementRef,
|
|
245
|
+
inputRef,
|
|
246
|
+
checked,
|
|
247
|
+
setChecked,
|
|
248
|
+
onCheckedChange
|
|
359
249
|
}) {
|
|
250
|
+
const { current: initialChecked } = (0, import_react4.useRef)(checked);
|
|
251
|
+
(0, import_react4.useEffect)(() => {
|
|
252
|
+
const form = elementRef.current?.form;
|
|
253
|
+
if (!form) return;
|
|
254
|
+
function onReset(event) {
|
|
255
|
+
queueMicrotask(() => {
|
|
256
|
+
if (event.defaultPrevented) return;
|
|
257
|
+
if (inputRef.current) inputRef.current.checked = checked;
|
|
258
|
+
if (checked === initialChecked) return;
|
|
259
|
+
setChecked(initialChecked);
|
|
260
|
+
onCheckedChange?.(initialChecked);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
form.addEventListener("reset", onReset);
|
|
264
|
+
return () => form.removeEventListener("reset", onReset);
|
|
265
|
+
}, [elementRef, inputRef, checked, setChecked, onCheckedChange, initialChecked]);
|
|
266
|
+
}
|
|
267
|
+
function useNativeButtonWarning(elementRef) {
|
|
268
|
+
(0, import_react4.useEffect)(() => {
|
|
269
|
+
if (process.env.NODE_ENV === "production") return;
|
|
270
|
+
const element = elementRef.current;
|
|
271
|
+
if (!element || element.tagName === "BUTTON") return;
|
|
272
|
+
console.error(
|
|
273
|
+
`Switch.Root rendered <${element.tagName.toLowerCase()}> instead of <button>. Focus, Space and Enter activation and \`disabled\` all come from the button element; pass a \`render\` component that forwards its props to one.`
|
|
274
|
+
);
|
|
275
|
+
}, [elementRef]);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/switch/SwitchThumb.tsx
|
|
279
|
+
function SwitchThumb({ className, children, render, ...rest }) {
|
|
360
280
|
const state = useSwitchRootContext();
|
|
361
281
|
return useRender({
|
|
362
282
|
render,
|
|
363
283
|
defaultTagName: "span",
|
|
364
|
-
state,
|
|
365
|
-
stateAttributes: switchStateAttributes,
|
|
366
|
-
props: { "aria-hidden": true, className, children },
|
|
284
|
+
props: { "aria-hidden": true, ...switchDataAttributes(state), className, children },
|
|
367
285
|
consumerProps: rest
|
|
368
286
|
});
|
|
369
287
|
}
|
package/dist/switch/index.d.cts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { ReactElement, Ref, ComponentPropsWithRef } from 'react';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Switch.Root's own props. Everything else — `id`, `className`, `children`, `aria-*`,
|
|
6
|
+
* `data-*`, event handlers — comes from React's own `<button>` props, so it is typed
|
|
7
|
+
* and checked without being declared here.
|
|
8
|
+
*
|
|
9
|
+
* `id` in particular is how a switch gets an accessible name, paired with a
|
|
10
|
+
* `<label htmlFor>`. A wrapping `<label>` names the rendered `<button>` too, but
|
|
11
|
+
* `jsx-a11y/label-has-associated-control` rejects a button as a nested control, so the
|
|
12
|
+
* explicit pairing is the one that passes lint.
|
|
13
|
+
*/
|
|
14
|
+
type SwitchRootOwnProps = {
|
|
5
15
|
/**
|
|
6
16
|
* Controlled state. Provide `onCheckedChange` alongside it.
|
|
7
17
|
*
|
|
@@ -12,64 +22,57 @@ interface SwitchRootProps {
|
|
|
12
22
|
checked?: boolean;
|
|
13
23
|
/** Initial state when uncontrolled. Read once, at mount. */
|
|
14
24
|
defaultChecked?: boolean;
|
|
15
|
-
/**
|
|
16
|
-
* Whether the rendered element is a native `<button>`.
|
|
17
|
-
*
|
|
18
|
-
* Inferred from `render`, which is right for an element literal. Set it explicitly
|
|
19
|
-
* when rendering a *component* — `render={<Tooltip.Trigger />}` cannot be inspected,
|
|
20
|
-
* so it is treated as non-native and picks up attributes it does not need. A mismatch
|
|
21
|
-
* logs a development warning.
|
|
22
|
-
*/
|
|
23
|
-
nativeButton?: boolean;
|
|
24
25
|
onCheckedChange?: (checked: boolean) => void;
|
|
25
26
|
disabled?: boolean;
|
|
26
27
|
/**
|
|
27
28
|
* Submits with the enclosing form when checked, mirroring a native checkbox:
|
|
28
|
-
* an unchecked control contributes nothing.
|
|
29
|
+
* an unchecked or disabled control contributes nothing.
|
|
29
30
|
*/
|
|
30
31
|
name?: string;
|
|
31
32
|
/** Value submitted when checked. Defaults to `"on"`, as a native checkbox does. */
|
|
32
33
|
value?: string;
|
|
33
34
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* Declared rather than left to the prop spread because it is how a switch gets an
|
|
37
|
-
* accessible name: `Switch.Root` renders a `<button>`, which a wrapping `<label>`
|
|
38
|
-
* would name implicitly, but `jsx-a11y/label-has-associated-control` rejects a
|
|
39
|
-
* `<button>` as a nested control. The explicit association keeps the rule quiet
|
|
40
|
-
* without a disable at the call site.
|
|
41
|
-
*/
|
|
42
|
-
id?: string;
|
|
43
|
-
className?: string;
|
|
44
|
-
children?: ReactNode;
|
|
45
|
-
/**
|
|
46
|
-
* Element to render instead of the default `<button>`. Props, className, event
|
|
35
|
+
* Component to render instead of the default `<button>`. Props, className, event
|
|
47
36
|
* handlers and ref are merged onto it.
|
|
37
|
+
*
|
|
38
|
+
* It must render a native `<button>` — a wrapper such as `<Tooltip.Trigger />` that
|
|
39
|
+
* forwards its props to one. Anything else is reported in development.
|
|
48
40
|
*/
|
|
49
41
|
render?: ReactElement;
|
|
42
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
50
43
|
ref?: Ref<HTMLElement>;
|
|
51
|
-
}
|
|
44
|
+
};
|
|
45
|
+
type SwitchRootProps = SwitchRootOwnProps & Omit<ComponentPropsWithRef<'button'>, keyof SwitchRootOwnProps>;
|
|
52
46
|
/**
|
|
53
47
|
* A switch — an immediate on/off control, distinct from a checkbox in that it takes
|
|
54
48
|
* effect at once rather than on submit.
|
|
55
49
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* it carries `role="switch"`
|
|
50
|
+
* Always a native `<button>`, which is the whole reason this component is short: the
|
|
51
|
+
* platform supplies focusability, Space and Enter activation, and `disabled`, so none
|
|
52
|
+
* of it is synthesised here. Per the WAI-ARIA switch pattern it carries `role="switch"`
|
|
53
|
+
* and `aria-checked`.
|
|
54
|
+
*
|
|
55
|
+
* Inside a form it behaves as a checkbox would there: `form.reset()` returns it to the
|
|
56
|
+
* state it mounted with. The change is reported through `onCheckedChange`, so a
|
|
57
|
+
* controlled switch moves only if its parent accepts it.
|
|
59
58
|
*
|
|
60
59
|
* It has no accessible name of its own — pair it with a `<label htmlFor>` by `id`, or
|
|
61
|
-
* pass `aria-label` or `aria-labelledby`. That is the consumer's decision, not a
|
|
62
|
-
* component should guess.
|
|
60
|
+
* pass `aria-label` or `aria-labelledby`. That is the consumer's decision, not one a
|
|
61
|
+
* headless component should guess.
|
|
63
62
|
*/
|
|
64
|
-
declare function SwitchRoot({ checked: checkedProp, defaultChecked,
|
|
63
|
+
declare function SwitchRoot({ checked: checkedProp, defaultChecked, onCheckedChange, disabled, name, value, className, children, render, ...rest }: SwitchRootProps): react.JSX.Element;
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Switch.Thumb's own props. Everything else — `id`, `className`, `children`, `aria-*`,
|
|
67
|
+
* `data-*` — comes from React's own `<span>` props.
|
|
68
|
+
*/
|
|
69
|
+
type SwitchThumbOwnProps = {
|
|
69
70
|
/** Element to render instead of the default `<span>`. */
|
|
70
71
|
render?: ReactElement;
|
|
72
|
+
/** Ref to the rendered element, whatever `render` makes it. */
|
|
71
73
|
ref?: Ref<HTMLElement>;
|
|
72
|
-
}
|
|
74
|
+
};
|
|
75
|
+
type SwitchThumbProps = SwitchThumbOwnProps & Omit<ComponentPropsWithRef<'span'>, keyof SwitchThumbOwnProps>;
|
|
73
76
|
/**
|
|
74
77
|
* The moving part of the switch.
|
|
75
78
|
*
|
|
@@ -83,17 +86,13 @@ interface SwitchThumbProps {
|
|
|
83
86
|
* Purely presentational — hidden from assistive technology, since the Root already
|
|
84
87
|
* announces the state.
|
|
85
88
|
*/
|
|
86
|
-
declare function SwitchThumb({ className, children, render, ...rest }: SwitchThumbProps
|
|
89
|
+
declare function SwitchThumb({ className, children, render, ...rest }: SwitchThumbProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
87
90
|
|
|
88
91
|
declare namespace index_parts {
|
|
89
92
|
export { SwitchRoot as Root, SwitchThumb as Thumb };
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
/**
|
|
93
|
-
* A type alias rather than an interface: only aliases get an implicit index
|
|
94
|
-
* signature, which is what lets this satisfy the `Record<string, unknown>` the
|
|
95
|
-
* state-attribute mapper is generic over.
|
|
96
|
-
*/
|
|
95
|
+
/** The state Switch.Root shares with its parts, and projects as `data-*` attributes. */
|
|
97
96
|
type SwitchState = {
|
|
98
97
|
checked: boolean;
|
|
99
98
|
disabled: boolean;
|
package/dist/switch/index.d.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { ReactElement, Ref, ComponentPropsWithRef } from 'react';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Switch.Root's own props. Everything else — `id`, `className`, `children`, `aria-*`,
|
|
6
|
+
* `data-*`, event handlers — comes from React's own `<button>` props, so it is typed
|
|
7
|
+
* and checked without being declared here.
|
|
8
|
+
*
|
|
9
|
+
* `id` in particular is how a switch gets an accessible name, paired with a
|
|
10
|
+
* `<label htmlFor>`. A wrapping `<label>` names the rendered `<button>` too, but
|
|
11
|
+
* `jsx-a11y/label-has-associated-control` rejects a button as a nested control, so the
|
|
12
|
+
* explicit pairing is the one that passes lint.
|
|
13
|
+
*/
|
|
14
|
+
type SwitchRootOwnProps = {
|
|
5
15
|
/**
|
|
6
16
|
* Controlled state. Provide `onCheckedChange` alongside it.
|
|
7
17
|
*
|
|
@@ -12,64 +22,57 @@ interface SwitchRootProps {
|
|
|
12
22
|
checked?: boolean;
|
|
13
23
|
/** Initial state when uncontrolled. Read once, at mount. */
|
|
14
24
|
defaultChecked?: boolean;
|
|
15
|
-
/**
|
|
16
|
-
* Whether the rendered element is a native `<button>`.
|
|
17
|
-
*
|
|
18
|
-
* Inferred from `render`, which is right for an element literal. Set it explicitly
|
|
19
|
-
* when rendering a *component* — `render={<Tooltip.Trigger />}` cannot be inspected,
|
|
20
|
-
* so it is treated as non-native and picks up attributes it does not need. A mismatch
|
|
21
|
-
* logs a development warning.
|
|
22
|
-
*/
|
|
23
|
-
nativeButton?: boolean;
|
|
24
25
|
onCheckedChange?: (checked: boolean) => void;
|
|
25
26
|
disabled?: boolean;
|
|
26
27
|
/**
|
|
27
28
|
* Submits with the enclosing form when checked, mirroring a native checkbox:
|
|
28
|
-
* an unchecked control contributes nothing.
|
|
29
|
+
* an unchecked or disabled control contributes nothing.
|
|
29
30
|
*/
|
|
30
31
|
name?: string;
|
|
31
32
|
/** Value submitted when checked. Defaults to `"on"`, as a native checkbox does. */
|
|
32
33
|
value?: string;
|
|
33
34
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* Declared rather than left to the prop spread because it is how a switch gets an
|
|
37
|
-
* accessible name: `Switch.Root` renders a `<button>`, which a wrapping `<label>`
|
|
38
|
-
* would name implicitly, but `jsx-a11y/label-has-associated-control` rejects a
|
|
39
|
-
* `<button>` as a nested control. The explicit association keeps the rule quiet
|
|
40
|
-
* without a disable at the call site.
|
|
41
|
-
*/
|
|
42
|
-
id?: string;
|
|
43
|
-
className?: string;
|
|
44
|
-
children?: ReactNode;
|
|
45
|
-
/**
|
|
46
|
-
* Element to render instead of the default `<button>`. Props, className, event
|
|
35
|
+
* Component to render instead of the default `<button>`. Props, className, event
|
|
47
36
|
* handlers and ref are merged onto it.
|
|
37
|
+
*
|
|
38
|
+
* It must render a native `<button>` — a wrapper such as `<Tooltip.Trigger />` that
|
|
39
|
+
* forwards its props to one. Anything else is reported in development.
|
|
48
40
|
*/
|
|
49
41
|
render?: ReactElement;
|
|
42
|
+
/** Ref to the rendered element. Merged with any ref on the `render` element. */
|
|
50
43
|
ref?: Ref<HTMLElement>;
|
|
51
|
-
}
|
|
44
|
+
};
|
|
45
|
+
type SwitchRootProps = SwitchRootOwnProps & Omit<ComponentPropsWithRef<'button'>, keyof SwitchRootOwnProps>;
|
|
52
46
|
/**
|
|
53
47
|
* A switch — an immediate on/off control, distinct from a checkbox in that it takes
|
|
54
48
|
* effect at once rather than on submit.
|
|
55
49
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* it carries `role="switch"`
|
|
50
|
+
* Always a native `<button>`, which is the whole reason this component is short: the
|
|
51
|
+
* platform supplies focusability, Space and Enter activation, and `disabled`, so none
|
|
52
|
+
* of it is synthesised here. Per the WAI-ARIA switch pattern it carries `role="switch"`
|
|
53
|
+
* and `aria-checked`.
|
|
54
|
+
*
|
|
55
|
+
* Inside a form it behaves as a checkbox would there: `form.reset()` returns it to the
|
|
56
|
+
* state it mounted with. The change is reported through `onCheckedChange`, so a
|
|
57
|
+
* controlled switch moves only if its parent accepts it.
|
|
59
58
|
*
|
|
60
59
|
* It has no accessible name of its own — pair it with a `<label htmlFor>` by `id`, or
|
|
61
|
-
* pass `aria-label` or `aria-labelledby`. That is the consumer's decision, not a
|
|
62
|
-
* component should guess.
|
|
60
|
+
* pass `aria-label` or `aria-labelledby`. That is the consumer's decision, not one a
|
|
61
|
+
* headless component should guess.
|
|
63
62
|
*/
|
|
64
|
-
declare function SwitchRoot({ checked: checkedProp, defaultChecked,
|
|
63
|
+
declare function SwitchRoot({ checked: checkedProp, defaultChecked, onCheckedChange, disabled, name, value, className, children, render, ...rest }: SwitchRootProps): react.JSX.Element;
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Switch.Thumb's own props. Everything else — `id`, `className`, `children`, `aria-*`,
|
|
67
|
+
* `data-*` — comes from React's own `<span>` props.
|
|
68
|
+
*/
|
|
69
|
+
type SwitchThumbOwnProps = {
|
|
69
70
|
/** Element to render instead of the default `<span>`. */
|
|
70
71
|
render?: ReactElement;
|
|
72
|
+
/** Ref to the rendered element, whatever `render` makes it. */
|
|
71
73
|
ref?: Ref<HTMLElement>;
|
|
72
|
-
}
|
|
74
|
+
};
|
|
75
|
+
type SwitchThumbProps = SwitchThumbOwnProps & Omit<ComponentPropsWithRef<'span'>, keyof SwitchThumbOwnProps>;
|
|
73
76
|
/**
|
|
74
77
|
* The moving part of the switch.
|
|
75
78
|
*
|
|
@@ -83,17 +86,13 @@ interface SwitchThumbProps {
|
|
|
83
86
|
* Purely presentational — hidden from assistive technology, since the Root already
|
|
84
87
|
* announces the state.
|
|
85
88
|
*/
|
|
86
|
-
declare function SwitchThumb({ className, children, render, ...rest }: SwitchThumbProps
|
|
89
|
+
declare function SwitchThumb({ className, children, render, ...rest }: SwitchThumbProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
87
90
|
|
|
88
91
|
declare namespace index_parts {
|
|
89
92
|
export { SwitchRoot as Root, SwitchThumb as Thumb };
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
/**
|
|
93
|
-
* A type alias rather than an interface: only aliases get an implicit index
|
|
94
|
-
* signature, which is what lets this satisfy the `Record<string, unknown>` the
|
|
95
|
-
* state-attribute mapper is generic over.
|
|
96
|
-
*/
|
|
95
|
+
/** The state Switch.Root shares with its parts, and projects as `data-*` attributes. */
|
|
97
96
|
type SwitchState = {
|
|
98
97
|
checked: boolean;
|
|
99
98
|
disabled: boolean;
|