@charcuterie/ui 3.5.0 → 3.7.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/Checkbox/Checkbox.d.ts +39 -1
- package/dist/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/Checkbox/Checkbox.js +131 -71
- package/dist/Checkbox/Checkbox.js.map +1 -1
- package/dist/Field/Field.d.ts +67 -2
- package/dist/Field/Field.d.ts.map +1 -1
- package/dist/Field/Field.js +22 -0
- package/dist/Field/Field.js.map +1 -1
- package/dist/Field/FieldGroup.d.ts +28 -3
- package/dist/Field/FieldGroup.d.ts.map +1 -1
- package/dist/Field/FieldGroup.js +5 -2
- package/dist/Field/FieldGroup.js.map +1 -1
- package/dist/Stepper/Stepper.d.ts +89 -0
- package/dist/Stepper/Stepper.d.ts.map +1 -0
- package/dist/Stepper/Stepper.js +87 -0
- package/dist/Stepper/Stepper.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/slotProps.d.ts +14 -1
- package/dist/slotProps.d.ts.map +1 -1
- package/dist/slotProps.js +13 -0
- package/dist/slotProps.js.map +1 -1
- package/package.json +1 -1
- package/src/Checkbox/Checkbox.mdx +32 -0
- package/src/Checkbox/Checkbox.stories.tsx +74 -0
- package/src/Checkbox/Checkbox.test.tsx +110 -2
- package/src/Checkbox/Checkbox.tsx +234 -125
- package/src/Field/Field.mdx +41 -0
- package/src/Field/Field.stories.tsx +79 -0
- package/src/Field/Field.test.tsx +137 -0
- package/src/Field/Field.tsx +99 -25
- package/src/Field/FieldGroup.tsx +60 -19
- package/src/Stepper/Stepper.mdx +86 -0
- package/src/Stepper/Stepper.stories.tsx +224 -0
- package/src/Stepper/Stepper.test.tsx +112 -0
- package/src/Stepper/Stepper.tsx +246 -0
- package/src/index.ts +7 -0
- package/src/slotProps.ts +16 -2
- package/src/sourceRules.test.ts +8 -1
|
@@ -2,6 +2,20 @@ import type { ControlSize } from "@charcuterie/tokens";
|
|
|
2
2
|
import type { ReactNode } from "react";
|
|
3
3
|
export type CheckboxProps = {
|
|
4
4
|
className?: string;
|
|
5
|
+
/**
|
|
6
|
+
* Standing help under the box — units, a consequence, a warning
|
|
7
|
+
* about what ticking it does. Same slot, same words and same
|
|
8
|
+
* announcement order as `Field`'s: bound with `aria-describedby`,
|
|
9
|
+
* so a screen reader reads it **with** the control rather than
|
|
10
|
+
* announcing it from nowhere.
|
|
11
|
+
*
|
|
12
|
+
* It renders **outside** the `<label>` on purpose. A `<label>`'s
|
|
13
|
+
* text content *is* the control's accessible name, so a hint put
|
|
14
|
+
* inside it would be read twice — once as part of the name, once as
|
|
15
|
+
* the description — and a pointer press anywhere on the hint would
|
|
16
|
+
* toggle the box.
|
|
17
|
+
*/
|
|
18
|
+
description?: ReactNode;
|
|
5
19
|
/**
|
|
6
20
|
* The caller's own id, when it has one — a stable target for an
|
|
7
21
|
* autofill hint or a deep link. Left to React's `useId` on the
|
|
@@ -74,6 +88,30 @@ export type CheckboxProps = {
|
|
|
74
88
|
* also the one label association that needs no `for` at all, so there
|
|
75
89
|
* is no minted-then-dropped id to get wrong.
|
|
76
90
|
*
|
|
91
|
+
* ### A description is the one thing that cannot go in the `<label>`
|
|
92
|
+
*
|
|
93
|
+
* `Field` has had a `description` since it shipped and this component
|
|
94
|
+
* had none, so a hint could not follow its checkbox. Downstream that
|
|
95
|
+
* meant one hint out of three in a group stayed an app-styled
|
|
96
|
+
* paragraph and the box carried two hint typographies.
|
|
97
|
+
*
|
|
98
|
+
* The slot is here now, and it renders **outside** the `<label>`.
|
|
99
|
+
* A `<label>`'s text content is the control's accessible name, so a
|
|
100
|
+
* hint inside it is announced twice — once inside the name, once
|
|
101
|
+
* again as the description — and a pointer press on the hint toggles
|
|
102
|
+
* the box, which is not what a sentence of explanation should do. So
|
|
103
|
+
* the `<label>` is no longer the outermost element: a `<div>` holds
|
|
104
|
+
* the row and the hint, `className` moves out to it (`className` is
|
|
105
|
+
* the outermost box a component renders, and that rule does not bend
|
|
106
|
+
* for this one), and `aria-describedby` binds the two back together.
|
|
107
|
+
*
|
|
108
|
+
* There is no `error` here to go with it. `Field` derives
|
|
109
|
+
* `aria-invalid` from its error, and a lone boolean is almost never
|
|
110
|
+
* the invalid thing — a **group** of them is, and a group's error
|
|
111
|
+
* belongs on the `FieldGroup` around it, described rather than
|
|
112
|
+
* asserted. Adding one per box would put `aria-invalid` on a control
|
|
113
|
+
* that is not itself wrong.
|
|
114
|
+
*
|
|
77
115
|
* ### The `<input>` is the store
|
|
78
116
|
*
|
|
79
117
|
* `defaultChecked`, not `checked`. The DOM owns a checkbox's state
|
|
@@ -83,5 +121,5 @@ export type CheckboxProps = {
|
|
|
83
121
|
* `<input>`'s job. `isChecked` seeds the first paint and nothing
|
|
84
122
|
* after.
|
|
85
123
|
*/
|
|
86
|
-
export declare const Checkbox: ({ className, id, isChecked, isDisabled, isReadOnly, label, onChange, size, value, }: CheckboxProps) => ReactNode;
|
|
124
|
+
export declare const Checkbox: ({ className, description, id, isChecked, isDisabled, isReadOnly, label, onChange, size, value, }: CheckboxProps) => ReactNode;
|
|
87
125
|
//# sourceMappingURL=Checkbox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../../src/Checkbox/Checkbox.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../../src/Checkbox/Checkbox.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,OAAO,CAAA;AAKnD,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;OAIG;IACH,EAAE,CAAC,EAAE,MAAM,CAAA;IACX;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;OAKG;IACH,KAAK,EAAE,SAAS,CAAA;IAChB,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAA;IACvC,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB;;;;;;;;;;;;;;;;;OAiBG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAuCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,eAAO,MAAM,QAAQ,GAAI,kGAWtB,aAAa,KAAG,SA4LlB,CAAA"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useUniqueId } from "@charcuterie/logic";
|
|
2
3
|
import { FOCUS_RING_CLASS } from "../intentStyles.js";
|
|
3
4
|
import { toClassName } from "../toClassName.js";
|
|
4
5
|
const BOX_SIZE_CLASS = {
|
|
@@ -16,6 +17,21 @@ const TEXT_SIZE_CLASS = {
|
|
|
16
17
|
md: "text-md",
|
|
17
18
|
lg: "text-lg",
|
|
18
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* The description hangs under the **label text**, not under the box,
|
|
22
|
+
* so the hint and the words it explains share a left edge.
|
|
23
|
+
*
|
|
24
|
+
* Written out per size rather than computed, because the offset is
|
|
25
|
+
* `BOX_SIZE_CLASS` plus the label row's `gap-2` and a class name is
|
|
26
|
+
* never interpolated — `` `ps-${…}` `` generates nothing and fails
|
|
27
|
+
* silently. sm is 0.875rem + 0.5rem, md is 1rem + 0.5rem, lg is
|
|
28
|
+
* 1.25rem + 0.5rem.
|
|
29
|
+
*/
|
|
30
|
+
const DESCRIPTION_INDENT_CLASS = {
|
|
31
|
+
sm: "ps-5.5",
|
|
32
|
+
md: "ps-6",
|
|
33
|
+
lg: "ps-7",
|
|
34
|
+
};
|
|
19
35
|
/**
|
|
20
36
|
* A single boolean, on or off, that owns nothing but itself.
|
|
21
37
|
*
|
|
@@ -36,6 +52,30 @@ const TEXT_SIZE_CLASS = {
|
|
|
36
52
|
* also the one label association that needs no `for` at all, so there
|
|
37
53
|
* is no minted-then-dropped id to get wrong.
|
|
38
54
|
*
|
|
55
|
+
* ### A description is the one thing that cannot go in the `<label>`
|
|
56
|
+
*
|
|
57
|
+
* `Field` has had a `description` since it shipped and this component
|
|
58
|
+
* had none, so a hint could not follow its checkbox. Downstream that
|
|
59
|
+
* meant one hint out of three in a group stayed an app-styled
|
|
60
|
+
* paragraph and the box carried two hint typographies.
|
|
61
|
+
*
|
|
62
|
+
* The slot is here now, and it renders **outside** the `<label>`.
|
|
63
|
+
* A `<label>`'s text content is the control's accessible name, so a
|
|
64
|
+
* hint inside it is announced twice — once inside the name, once
|
|
65
|
+
* again as the description — and a pointer press on the hint toggles
|
|
66
|
+
* the box, which is not what a sentence of explanation should do. So
|
|
67
|
+
* the `<label>` is no longer the outermost element: a `<div>` holds
|
|
68
|
+
* the row and the hint, `className` moves out to it (`className` is
|
|
69
|
+
* the outermost box a component renders, and that rule does not bend
|
|
70
|
+
* for this one), and `aria-describedby` binds the two back together.
|
|
71
|
+
*
|
|
72
|
+
* There is no `error` here to go with it. `Field` derives
|
|
73
|
+
* `aria-invalid` from its error, and a lone boolean is almost never
|
|
74
|
+
* the invalid thing — a **group** of them is, and a group's error
|
|
75
|
+
* belongs on the `FieldGroup` around it, described rather than
|
|
76
|
+
* asserted. Adding one per box would put `aria-invalid` on a control
|
|
77
|
+
* that is not itself wrong.
|
|
78
|
+
*
|
|
39
79
|
* ### The `<input>` is the store
|
|
40
80
|
*
|
|
41
81
|
* `defaultChecked`, not `checked`. The DOM owns a checkbox's state
|
|
@@ -45,75 +85,95 @@ const TEXT_SIZE_CLASS = {
|
|
|
45
85
|
* `<input>`'s job. `isChecked` seeds the first paint and nothing
|
|
46
86
|
* after.
|
|
47
87
|
*/
|
|
48
|
-
export const Checkbox = ({ className, id, isChecked = false, isDisabled = false, isReadOnly = false, label, onChange, size = "md", value, }) =>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
88
|
+
export const Checkbox = ({ className, description, id, isChecked = false, isDisabled = false, isReadOnly = false, label, onChange, size = "md", value, }) => {
|
|
89
|
+
const baseId = useUniqueId();
|
|
90
|
+
const descriptionId = `${baseId}-description`;
|
|
91
|
+
return (_jsxs("div", { className: toClassName(
|
|
92
|
+
// The row and its hint stack, and the box stays inline-level so
|
|
93
|
+
// a `Checkbox` still sits in a sentence, a table cell or a flex
|
|
94
|
+
// row exactly where the bare `<label>` used to.
|
|
95
|
+
"inline-flex flex-col gap-1", className), children: [_jsxs("label", { className: toClassName("inline-flex items-center gap-2 text-content-secondary select-none", TEXT_SIZE_CLASS[size],
|
|
96
|
+
// One cursor, chosen by state — two cursor utilities of the same
|
|
97
|
+
// specificity do not resolve by class-list order.
|
|
98
|
+
isDisabled
|
|
99
|
+
? "cursor-not-allowed"
|
|
100
|
+
: isReadOnly
|
|
101
|
+
? "cursor-default"
|
|
102
|
+
: "cursor-pointer",
|
|
103
|
+
// Disabled dims the whole control with opacity rather than fading
|
|
104
|
+
// the box to a quieter token. The token scale has no step between
|
|
105
|
+
// `border-default` and `border-strong`, so a muted outline was
|
|
106
|
+
// either invisible or indistinguishable from enabled; `opacity-60`
|
|
107
|
+
// keeps the full shape and colour and just turns them down, which
|
|
108
|
+
// is the "present but inactive" the box actually is. Read-only is
|
|
109
|
+
// *not* dimmed — its whole point is that the value stays readable.
|
|
110
|
+
// The same treatment is on `Switch` and `RadioGroup`.
|
|
111
|
+
//
|
|
112
|
+
// It stays on the `<label>` and does NOT reach the description
|
|
113
|
+
// below, which is a contrast decision rather than an oversight.
|
|
114
|
+
// axe exempts a disabled control and its own label from the
|
|
115
|
+
// colour-contrast rule; a sibling paragraph gets no such
|
|
116
|
+
// exemption, and `content-secondary` at 60% measures **3.77:1**
|
|
117
|
+
// on `midnight` against a 4.5 threshold. A hint nobody can read
|
|
118
|
+
// is worse than a hint that does not dim with its control, and
|
|
119
|
+
// the hint is the part a user reads precisely when they are
|
|
120
|
+
// asking why the box is turned off.
|
|
121
|
+
isDisabled && "opacity-60"), children: [_jsxs("span", { className: "relative grid shrink-0 place-items-center", children: [_jsx("input", { "aria-describedby": description === undefined
|
|
122
|
+
? undefined
|
|
123
|
+
: descriptionId, "aria-readonly": isReadOnly || undefined, className: toClassName(
|
|
124
|
+
// `border-2` is the family's shared weight — the `Switch`
|
|
125
|
+
// track's `ring-2` and the `RadioGroup` ring match it, so the
|
|
126
|
+
// three read as one set — and a 2px edge is also what keeps a
|
|
127
|
+
// disabled box visible where a 1px one washed out.
|
|
128
|
+
"peer appearance-none rounded-sm border-2 bg-surface-sunken transition-colors duration-(--duration-fast) ease-standard",
|
|
129
|
+
// Read-only wears the **neutral** intent, not the accent: full
|
|
130
|
+
// contrast (so the value reads) but plainly not an actionable
|
|
131
|
+
// accent control, with a softer `border-default` resting edge.
|
|
132
|
+
// Three distinct states result — enabled (accent, crisp),
|
|
133
|
+
// disabled (accent, dimmed by the wrapper), read-only (neutral).
|
|
134
|
+
// No `disabled:` colour override; the wrapper's `opacity-60`
|
|
135
|
+
// does the dimming, and doubling it on the box is what made a
|
|
136
|
+
// disabled checkbox vanish.
|
|
137
|
+
// `indeterminate:` wears the same fill as `checked:`. It is
|
|
138
|
+
// the state a *group* box is in when some of its members are
|
|
139
|
+
// ticked, and it cannot be written in markup at all —
|
|
140
|
+
// `indeterminate` is a DOM property with no HTML attribute —
|
|
141
|
+
// so the only way to reach it is a consumer writing to the
|
|
142
|
+
// input. `DataTable`'s select-all box does exactly that, and
|
|
143
|
+
// before this line it painted **nothing**: the property was
|
|
144
|
+
// set, screen readers announced "mixed", and the box on
|
|
145
|
+
// screen was indistinguishable from empty.
|
|
146
|
+
isReadOnly
|
|
147
|
+
? "border-border-default checked:border-intent-neutral-solid checked:bg-intent-neutral-solid indeterminate:border-intent-neutral-solid indeterminate:bg-intent-neutral-solid"
|
|
148
|
+
: "border-border-strong checked:border-intent-accent-solid checked:bg-intent-accent-solid indeterminate:border-intent-accent-solid indeterminate:bg-intent-accent-solid", isDisabled
|
|
149
|
+
? "cursor-not-allowed"
|
|
150
|
+
: isReadOnly
|
|
151
|
+
? "cursor-default"
|
|
152
|
+
: "cursor-pointer", BOX_SIZE_CLASS[size], FOCUS_RING_CLASS), defaultChecked: isChecked, disabled: isDisabled, id: id, onChange: (event) => {
|
|
153
|
+
if (isReadOnly) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
onChange?.(event.target.checked);
|
|
157
|
+
}, onClick: (clickEvent) => {
|
|
158
|
+
// Native checkboxes have no `readonly`, so the toggle is
|
|
159
|
+
// blocked here — `preventDefault` on the click stops the
|
|
160
|
+
// checked flip a pointer would make.
|
|
161
|
+
if (isReadOnly) {
|
|
162
|
+
clickEvent.preventDefault();
|
|
163
|
+
}
|
|
164
|
+
}, onKeyDown: (keyEvent) => {
|
|
165
|
+
// …and Space, the keyboard toggle a click block does not
|
|
166
|
+
// cover.
|
|
167
|
+
if (isReadOnly && keyEvent.key === " ") {
|
|
168
|
+
keyEvent.preventDefault();
|
|
169
|
+
}
|
|
170
|
+
}, type: "checkbox", value: value }), _jsx("svg", { "aria-hidden": "true", className: toClassName("pointer-events-none invisible absolute peer-checked:visible peer-indeterminate:invisible",
|
|
171
|
+
// Matches whichever fill the box wears, so the tick keeps its
|
|
172
|
+
// guaranteed on-solid contrast in either intent.
|
|
173
|
+
isReadOnly
|
|
174
|
+
? "text-intent-neutral-on-solid"
|
|
175
|
+
: "text-intent-accent-on-solid", CHECK_SIZE_CLASS[size]), fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 3, viewBox: "0 0 24 24", children: _jsx("path", { d: "M20 6 9 17l-5-5" }) }), _jsx("svg", { "aria-hidden": "true", className: toClassName("pointer-events-none invisible absolute peer-indeterminate:visible", isReadOnly
|
|
176
|
+
? "text-intent-neutral-on-solid"
|
|
177
|
+
: "text-intent-accent-on-solid", CHECK_SIZE_CLASS[size]), fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeWidth: 3, viewBox: "0 0 24 24", children: _jsx("path", { d: "M6 12h12" }) })] }), _jsx("span", { children: label })] }), description === undefined ? null : (_jsx("p", { className: toClassName("text-content-secondary text-sm", DESCRIPTION_INDENT_CLASS[size]), id: descriptionId, children: description }))] }));
|
|
178
|
+
};
|
|
119
179
|
//# sourceMappingURL=Checkbox.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.js","sourceRoot":"","sources":["../../src/Checkbox/Checkbox.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"Checkbox.js","sourceRoot":"","sources":["../../src/Checkbox/Checkbox.tsx"],"names":[],"mappings":"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"}
|
package/dist/Field/Field.d.ts
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
|
-
import type { ReactElement, ReactNode } from "react";
|
|
1
|
+
import type { ComponentPropsWithRef, ReactElement, ReactNode, Ref } from "react";
|
|
2
2
|
import type { SlotProps } from "../slotProps.ts";
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Everything else a caller writes on a `Field` reaches **the control
|
|
5
|
+
* it clones onto**, not the `<div>` around it.
|
|
6
|
+
*
|
|
7
|
+
* That is not a new destination, it is the existing one finally
|
|
8
|
+
* declared. `SlotProps` was a *closed* five-key type over a runtime
|
|
9
|
+
* that already spread `...receivedSlotProps` into the clone, so
|
|
10
|
+
* `name`, `placeholder`, `autoFocus`, `onBlur` and every `aria-*` all
|
|
11
|
+
* arrived at the `<input>` and none of them type-checked. The only
|
|
12
|
+
* prop a consumer could legally pass was `className` — which is the
|
|
13
|
+
* one escape hatch the fleet's "configured by props, not a borrowed
|
|
14
|
+
* class" rule is trying to close, so the closed type forced the very
|
|
15
|
+
* thing the rule forbids
|
|
16
|
+
* ([decision](../../../../docs/decisions/2026-08-21-a-slot-components-rest-props-are-the-controls-props.md)).
|
|
17
|
+
*
|
|
18
|
+
* `className` stays on the `<div>`, alone, because a class is a
|
|
19
|
+
* statement about the box a **parent** lays out
|
|
20
|
+
* ([decision](../../../../docs/decisions/2026-08-19-classname-is-the-outermost-box-a-component-renders.md)).
|
|
21
|
+
* Every other prop is a statement about the control, and `id` is the
|
|
22
|
+
* proof: it has named the control since the component shipped, and a
|
|
23
|
+
* dated record settles its precedence
|
|
24
|
+
* ([decision](../../../../docs/decisions/2026-08-05-field-adopts-the-childs-own-id.md)).
|
|
25
|
+
* Routing the rest to the `<div>` would have moved it, silently, in
|
|
26
|
+
* every consumer.
|
|
27
|
+
*
|
|
28
|
+
* `"input"` because `SlotProps` already picks from
|
|
29
|
+
* `InputHTMLAttributes` and this is the same set widened, not a
|
|
30
|
+
* second opinion about what a control is. `ref` is re-declared over
|
|
31
|
+
* `HTMLElement`: the child may be a `Picker`'s `<button>` or a
|
|
32
|
+
* `contenteditable` `<div>`, so `Ref<HTMLInputElement>` would be a
|
|
33
|
+
* narrower claim than the slot makes.
|
|
34
|
+
*/
|
|
35
|
+
type ForwardedControlProps = Omit<ComponentPropsWithRef<"input">, "children" | "className" | "ref">;
|
|
36
|
+
export type FieldProps = ForwardedControlProps & SlotProps & {
|
|
4
37
|
/**
|
|
5
38
|
* The control. **Cloned, not wrapped** — the same slot contract
|
|
6
39
|
* as `Popover`'s trigger, so a `Field` can go around an `<input>`,
|
|
@@ -8,6 +41,10 @@ export type FieldProps = SlotProps & {
|
|
|
8
41
|
* changing that control's layout.
|
|
9
42
|
*/
|
|
10
43
|
children: ReactElement;
|
|
44
|
+
/**
|
|
45
|
+
* The `<div>` around the label, the control and the messages —
|
|
46
|
+
* the one prop that does **not** reach the control.
|
|
47
|
+
*/
|
|
11
48
|
className?: string;
|
|
12
49
|
/**
|
|
13
50
|
* Standing help — units, a format, a warning about what the value
|
|
@@ -23,6 +60,11 @@ export type FieldProps = SlotProps & {
|
|
|
23
60
|
error?: ReactNode;
|
|
24
61
|
isRequired?: boolean;
|
|
25
62
|
label: ReactNode;
|
|
63
|
+
/**
|
|
64
|
+
* Merged onto the control's own, never replacing it — the wiring
|
|
65
|
+
* rule `mergeSlotWiring` states for every slot.
|
|
66
|
+
*/
|
|
67
|
+
ref?: Ref<HTMLElement>;
|
|
26
68
|
};
|
|
27
69
|
/**
|
|
28
70
|
* Sixteen files in the fleet spell a label, a control, and a hint,
|
|
@@ -99,6 +141,29 @@ export type FieldProps = SlotProps & {
|
|
|
99
141
|
* `SlotProps` arriving from an outer slot is forwarded down, and
|
|
100
142
|
* `aria-describedby` is merged rather than replaced.
|
|
101
143
|
* `slotProps.ts` has the reasoning.
|
|
144
|
+
*
|
|
145
|
+
* ### …and a pass-through cannot tell who wrote the prop
|
|
146
|
+
*
|
|
147
|
+
* Which is why the rest props are the **control's** props and not the
|
|
148
|
+
* `<div>`'s. `receivedSlotProps` below is one object holding two
|
|
149
|
+
* things that are indistinguishable at runtime: what a cloning
|
|
150
|
+
* ancestor injected, and what the author typed on the `Field` itself.
|
|
151
|
+
* A `Tooltip` above hands down `getReferenceProps()` — hover, focus
|
|
152
|
+
* and dismiss handlers, `refs.setReference`, and an
|
|
153
|
+
* `aria-describedby` naming the tip — and every bit of it has to
|
|
154
|
+
* reach the `<input>` at the bottom. Sending the rest to the wrapper
|
|
155
|
+
* would need a closed runtime allow-list to hold that case back, and
|
|
156
|
+
* an allow-list is `FieldTooltip`'s silent drop rebuilt inside the
|
|
157
|
+
* fix for it: the first slot that writes an `aria-expanded` is broken
|
|
158
|
+
* and nothing says so.
|
|
159
|
+
*
|
|
160
|
+
* So there is one rule, and it has no exceptions to remember:
|
|
161
|
+
* `className` is the box, everything else is the control. A caller
|
|
162
|
+
* that needs a handle on the **box** — an `id` for a screenshot, a
|
|
163
|
+
* `hidden` that takes the label with it — wants `FieldGroup`, which
|
|
164
|
+
* wraps rather than clones and whose rest props land on its own
|
|
165
|
+
* `<fieldset>`.
|
|
102
166
|
*/
|
|
103
167
|
export declare const Field: ({ children, className, description, error, isRequired, label, ...receivedSlotProps }: FieldProps) => ReactNode;
|
|
168
|
+
export {};
|
|
104
169
|
//# sourceMappingURL=Field.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Field.d.ts","sourceRoot":"","sources":["../../src/Field/Field.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"Field.d.ts","sourceRoot":"","sources":["../../src/Field/Field.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,qBAAqB,EACrB,YAAY,EACZ,SAAS,EACT,GAAG,EACJ,MAAM,OAAO,CAAA;AAEd,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAIhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,KAAK,qBAAqB,GAAG,IAAI,CAC/B,qBAAqB,CAAC,OAAO,CAAC,EAC9B,UAAU,GAAG,WAAW,GAAG,KAAK,CACjC,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,qBAAqB,GAC5C,SAAS,GAAG;IACV;;;;;OAKG;IACH,QAAQ,EAAE,YAAY,CAAA;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,KAAK,EAAE,SAAS,CAAA;IAChB;;;OAGG;IACH,GAAG,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,CAAA;CACvB,CAAA;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiGG;AACH,eAAO,MAAM,KAAK,GAAI,sFAQnB,UAAU,KAAG,SAkIf,CAAA"}
|
package/dist/Field/Field.js
CHANGED
|
@@ -77,6 +77,28 @@ import { toClassName } from "../toClassName.js";
|
|
|
77
77
|
* `SlotProps` arriving from an outer slot is forwarded down, and
|
|
78
78
|
* `aria-describedby` is merged rather than replaced.
|
|
79
79
|
* `slotProps.ts` has the reasoning.
|
|
80
|
+
*
|
|
81
|
+
* ### …and a pass-through cannot tell who wrote the prop
|
|
82
|
+
*
|
|
83
|
+
* Which is why the rest props are the **control's** props and not the
|
|
84
|
+
* `<div>`'s. `receivedSlotProps` below is one object holding two
|
|
85
|
+
* things that are indistinguishable at runtime: what a cloning
|
|
86
|
+
* ancestor injected, and what the author typed on the `Field` itself.
|
|
87
|
+
* A `Tooltip` above hands down `getReferenceProps()` — hover, focus
|
|
88
|
+
* and dismiss handlers, `refs.setReference`, and an
|
|
89
|
+
* `aria-describedby` naming the tip — and every bit of it has to
|
|
90
|
+
* reach the `<input>` at the bottom. Sending the rest to the wrapper
|
|
91
|
+
* would need a closed runtime allow-list to hold that case back, and
|
|
92
|
+
* an allow-list is `FieldTooltip`'s silent drop rebuilt inside the
|
|
93
|
+
* fix for it: the first slot that writes an `aria-expanded` is broken
|
|
94
|
+
* and nothing says so.
|
|
95
|
+
*
|
|
96
|
+
* So there is one rule, and it has no exceptions to remember:
|
|
97
|
+
* `className` is the box, everything else is the control. A caller
|
|
98
|
+
* that needs a handle on the **box** — an `id` for a screenshot, a
|
|
99
|
+
* `hidden` that takes the label with it — wants `FieldGroup`, which
|
|
100
|
+
* wraps rather than clones and whose rest props land on its own
|
|
101
|
+
* `<fieldset>`.
|
|
80
102
|
*/
|
|
81
103
|
export const Field = ({ children, className, description, error, isRequired = false, label, ...receivedSlotProps }) => {
|
|
82
104
|
const baseId = useUniqueId();
|
package/dist/Field/Field.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Field.js","sourceRoot":"","sources":["../../src/Field/Field.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,cAAc,EACd,WAAW,GACZ,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"Field.js","sourceRoot":"","sources":["../../src/Field/Field.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,cAAc,EACd,WAAW,GACZ,MAAM,oBAAoB,CAAA;AAS3B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AA0E/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiGG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,EACpB,QAAQ,EACR,SAAS,EACT,WAAW,EACX,KAAK,EACL,UAAU,GAAG,KAAK,EAClB,KAAK,EACL,GAAG,iBAAiB,EACT,EAAa,EAAE;IAC1B,MAAM,MAAM,GAAG,WAAW,EAAE,CAAA;IAE5B;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,OAAO,GAAI,QAAQ,CAAC,KAAyB,CAAC,EAAE,CAAA;IAEtD,MAAM,SAAS,GACb,iBAAiB,CAAC,EAAE,IAAI,OAAO,IAAI,GAAG,MAAM,UAAU,CAAA;IAExD,MAAM,aAAa,GAAG,GAAG,MAAM,cAAc,CAAA;IAE7C,MAAM,OAAO,GAAG,GAAG,MAAM,QAAQ,CAAA;IAEjC,MAAM,WAAW,GAAG;QAClB,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa;QAChD,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO;KACrC;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAA;IAEZ,MAAM,aAAa,GAAG,cAAc,CAClC,QAAQ,EACR,cAAc,CAAC,iBAAiB,EAAE;QAChC,kBAAkB,EAChB,WAAW,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW;QAC9C,6DAA6D;QAC7D,+DAA+D;QAC/D,iDAAiD;QACjD,cAAc,EACZ,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;QACxC,sDAAsD;QACtD,8DAA8D;QAC9D,8DAA8D;QAC9D,gDAAgD;QAChD,eAAe,EAAE,UAAU,IAAI,SAAS;QACxC,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,UAAU,IAAI,SAAS;KAClC,CAAC,CACH,CAAA;IAED,OAAO,CACL,eACE,SAAS,EAAE,WAAW,CACpB,uBAAuB,EACvB,SAAS,CACV,aAED,iBACE,SAAS,EAAC,0CAA0C,EACpD,OAAO,EAAE,SAAS;gBAClB;;;;;;;;;;;;;;;;mBAgBG;gBACH,EAAE,EAAE,GAAG,SAAS,QAAQ,aAEvB,KAAK,EAEL,UAAU,CAAC,CAAC,CAAC,CACZ,8BAKc,MAAM,EAClB,SAAS,EAAC,iCAAiC,kBAGtC,CACR,CAAC,CAAC,CAAC,IAAI,IACF,EAEP,aAAa,EAUb,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAClC,YACE,SAAS,EAAC,gCAAgC,EAC1C,EAAE,EAAE,aAAa,YAEhB,WAAW,GACV,CACL,EAEA,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAC5B,YACE,SAAS,EAAC,oCAAoC,EAC9C,EAAE,EAAE,OAAO,YAEV,KAAK,GACJ,CACL,IACG,CACP,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -1,11 +1,36 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
|
-
|
|
1
|
+
import type { ComponentPropsWithRef, ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Everything a caller writes on a `FieldGroup` reaches the
|
|
4
|
+
* `<fieldset>` — `id`, `hidden`, `data-*`, `aria-*`, `ref`, and the
|
|
5
|
+
* three attributes that are the element's own: `disabled`, `form`
|
|
6
|
+
* and `name`.
|
|
7
|
+
*
|
|
8
|
+
* This is the **opposite** destination from `Field`'s, and the
|
|
9
|
+
* difference is not a preference. `Field` clones onto its child, so
|
|
10
|
+
* its props are that control's props; `FieldGroup` renders its
|
|
11
|
+
* children as-is, so the only element it owns is the `<fieldset>` and
|
|
12
|
+
* a prop has nowhere else to go. One rule reads both:
|
|
13
|
+
* ***a component's props land on the element it owns***
|
|
14
|
+
* ([decision](../../../../docs/decisions/2026-08-21-a-slot-components-rest-props-are-the-controls-props.md)).
|
|
15
|
+
*
|
|
16
|
+
* `disabled` is worth knowing about before reaching for it. On a
|
|
17
|
+
* `<fieldset>` it is not decoration — the platform disables every
|
|
18
|
+
* form control inside the element, not just the box itself. That is
|
|
19
|
+
* usually what a caller wants and occasionally a surprise, and it is
|
|
20
|
+
* a thing a `<div>`-based group could not have offered at all.
|
|
21
|
+
*/
|
|
22
|
+
export type FieldGroupProps = ComponentPropsWithRef<"fieldset"> & {
|
|
3
23
|
/**
|
|
4
24
|
* The controls. **Rendered as-is, not cloned** — that is the whole
|
|
5
25
|
* difference from `Field`, and it is why this is a separate
|
|
6
26
|
* component rather than a boolean on that one.
|
|
7
27
|
*/
|
|
8
28
|
children: ReactNode;
|
|
29
|
+
/**
|
|
30
|
+
* The `<fieldset>`, which is also the outermost element this
|
|
31
|
+
* component renders — so `className` is not the exception here that
|
|
32
|
+
* it is on `Field`.
|
|
33
|
+
*/
|
|
9
34
|
className?: string;
|
|
10
35
|
/** Standing help for the group as a whole. */
|
|
11
36
|
description?: ReactNode;
|
|
@@ -54,5 +79,5 @@ export type FieldGroupProps = {
|
|
|
54
79
|
* over — the alternative was to clone `aria-invalid` onto every
|
|
55
80
|
* child, which marks the valid ones invalid.
|
|
56
81
|
*/
|
|
57
|
-
export declare const FieldGroup: ({ children, className, description, error, isRequired, label, }: FieldGroupProps) => ReactNode;
|
|
82
|
+
export declare const FieldGroup: ({ children, className, description, error, isRequired, label, ...receivedFieldsetProps }: FieldGroupProps) => ReactNode;
|
|
58
83
|
//# sourceMappingURL=FieldGroup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FieldGroup.d.ts","sourceRoot":"","sources":["../../src/Field/FieldGroup.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"FieldGroup.d.ts","sourceRoot":"","sources":["../../src/Field/FieldGroup.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,qBAAqB,EACrB,SAAS,EACV,MAAM,OAAO,CAAA;AAKd;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,eAAe,GACzB,qBAAqB,CAAC,UAAU,CAAC,GAAG;IAClC;;;;OAIG;IACH,QAAQ,EAAE,SAAS,CAAA;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,8CAA8C;IAC9C,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,KAAK,EAAE,SAAS,CAAA;CACjB,CAAA;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,eAAO,MAAM,UAAU,GAAI,0FAQxB,eAAe,KAAG,SAiFpB,CAAA"}
|
package/dist/Field/FieldGroup.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useUniqueId } from "@charcuterie/logic";
|
|
3
|
+
import { mergeSlotProps } from "../slotProps.js";
|
|
3
4
|
import { toClassName } from "../toClassName.js";
|
|
4
5
|
/**
|
|
5
6
|
* A label over **several** controls.
|
|
@@ -41,7 +42,7 @@ import { toClassName } from "../toClassName.js";
|
|
|
41
42
|
* over — the alternative was to clone `aria-invalid` onto every
|
|
42
43
|
* child, which marks the valid ones invalid.
|
|
43
44
|
*/
|
|
44
|
-
export const FieldGroup = ({ children, className, description, error, isRequired = false, label, }) => {
|
|
45
|
+
export const FieldGroup = ({ children, className, description, error, isRequired = false, label, ...receivedFieldsetProps }) => {
|
|
45
46
|
const baseId = useUniqueId();
|
|
46
47
|
const descriptionId = `${baseId}-description`;
|
|
47
48
|
const errorId = `${baseId}-error`;
|
|
@@ -54,7 +55,9 @@ export const FieldGroup = ({ children, className, description, error, isRequired
|
|
|
54
55
|
]
|
|
55
56
|
.filter(Boolean)
|
|
56
57
|
.join(" ");
|
|
57
|
-
return (_jsxs("fieldset", {
|
|
58
|
+
return (_jsxs("fieldset", { ...mergeSlotProps(receivedFieldsetProps, {
|
|
59
|
+
"aria-describedby": describedBy === "" ? undefined : describedBy,
|
|
60
|
+
}), className: toClassName(
|
|
58
61
|
// A `<fieldset>` arrives with a border, an inline margin, a
|
|
59
62
|
// block padding, and `min-inline-size: min-content` — the
|
|
60
63
|
// last of which makes it refuse to shrink inside a flex or
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FieldGroup.js","sourceRoot":"","sources":["../../src/Field/FieldGroup.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"FieldGroup.js","sourceRoot":"","sources":["../../src/Field/FieldGroup.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAMhD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AA4C/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,EACzB,QAAQ,EACR,SAAS,EACT,WAAW,EACX,KAAK,EACL,UAAU,GAAG,KAAK,EAClB,KAAK,EACL,GAAG,qBAAqB,EACR,EAAa,EAAE;IAC/B,MAAM,MAAM,GAAG,WAAW,EAAE,CAAA;IAE5B,MAAM,aAAa,GAAG,GAAG,MAAM,cAAc,CAAA;IAE7C,MAAM,OAAO,GAAG,GAAG,MAAM,QAAQ,CAAA;IAEjC,iEAAiE;IACjE,gEAAgE;IAChE,2BAA2B;IAC3B,MAAM,WAAW,GAAG;QAClB,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa;QAChD,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO;KACrC;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAA;IAEZ,OAAO,CACL,uBAUM,cAAc,CAAC,qBAAqB,EAAE;YACxC,kBAAkB,EAChB,WAAW,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW;SAC/C,CAAC,EACF,SAAS,EAAE,WAAW;QACpB,4DAA4D;QAC5D,0DAA0D;QAC1D,2DAA2D;QAC3D,4DAA4D;QAC5D,4CAA4C;QAC5C,gDAAgD,EAChD,SAAS,CACV,aAED,kBAAQ,SAAS,EAAC,qDAAqD,aACpE,KAAK,EAEL,UAAU,CAAC,CAAC,CAAC,CACZ,8BAIc,MAAM,EAClB,SAAS,EAAC,iCAAiC,kBAGtC,CACR,CAAC,CAAC,CAAC,IAAI,IACD,EAER,QAAQ,EAIR,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAClC,YACE,SAAS,EAAC,gCAAgC,EAC1C,EAAE,EAAE,aAAa,YAEhB,WAAW,GACV,CACL,EAEA,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAC5B,YACE,SAAS,EAAC,oCAAoC,EAC9C,EAAE,EAAE,OAAO,YAEV,KAAK,GACJ,CACL,IACQ,CACZ,CAAA;AACH,CAAC,CAAA"}
|