@gridui/radio-group 0.0.0-stage → 1.0.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 +93 -2
- package/dist/context.d.ts +13 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +252 -0
- package/dist/index.js.map +7 -0
- package/dist/radio-card.d.ts +15 -0
- package/dist/radio-group.d.ts +15 -0
- package/dist/radio.d.ts +10 -0
- package/dist/style.d.ts +11 -0
- package/dist/styles.css +185 -0
- package/dist/use-radio-item.d.ts +15 -0
- package/package.json +57 -3
package/README.md
CHANGED
|
@@ -1,3 +1,94 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Radio group
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@gridui/radio-group` owns `RadioGroup`, `Radio`, and `RadioCard` as one
|
|
4
|
+
single-selection family. Import from the package root and load
|
|
5
|
+
`@gridui/radio-group/styles.css` after `@gridui/theme/styles.css`.
|
|
6
|
+
The package does not depend on Controls or application layout.
|
|
7
|
+
|
|
8
|
+
## Selection and forms
|
|
9
|
+
|
|
10
|
+
`RadioGroup` retains string `value` / `onValueChange` for controlled state and
|
|
11
|
+
`defaultValue` for local state. `onValueChange(value, details)` receives the native
|
|
12
|
+
`details.event`; `details.cancel()` prevents an uncontrolled selection update.
|
|
13
|
+
Controlled callers own their state and must not commit a canceled change.
|
|
14
|
+
The shared event protocol comes from `@gridui/interaction`.
|
|
15
|
+
|
|
16
|
+
The group forwards `name`, `form`, `required`, `disabled`, orientation, loop, refs,
|
|
17
|
+
and native accessibility props. `readOnly` permits focus without changing the
|
|
18
|
+
selection. Items also respect disabled fieldsets. Native form reset restores the
|
|
19
|
+
initial uncontrolled value unless the reset event is canceled; controlled callers
|
|
20
|
+
reset their own state. Radix owns radio semantics, hidden form inputs, and keyboard
|
|
21
|
+
navigation. Provide an accessible group name independently of item labels.
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { Radio, RadioGroup } from "@gridui/radio-group";
|
|
25
|
+
|
|
26
|
+
<RadioGroup name="delivery" defaultValue="standard" aria-label="Delivery service">
|
|
27
|
+
<label><Radio value="standard" /> Standard delivery</label>
|
|
28
|
+
<label><Radio value="express" /> Express delivery</label>
|
|
29
|
+
</RadioGroup>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Use comfortable visible label hit areas for compact `Radio` controls. `Radio`
|
|
33
|
+
and `RadioCard` can coexist in one group and share the same selection context.
|
|
34
|
+
|
|
35
|
+
## Rich choices
|
|
36
|
+
|
|
37
|
+
`RadioCard` adds a required `label`, optional `description`, optional decorative
|
|
38
|
+
`leading` node, and `variant="outlined" | "plain"` to `RadioProps`, excluding
|
|
39
|
+
`asChild`. The default is `outlined`. Cards remain native radio buttons, not links
|
|
40
|
+
or independently managed toggles. Selection, form ownership, and callbacks stay
|
|
41
|
+
on `RadioGroup`; cards do not own columns or a second selection model.
|
|
42
|
+
|
|
43
|
+
The label and description have separate generated associations. Explicit
|
|
44
|
+
`aria-label` or `aria-labelledby` overrides the generated name. Supplied
|
|
45
|
+
`aria-describedby` is combined with the description association. `leading` is
|
|
46
|
+
hidden from assistive technology and must be decorative. Keep every slot free of
|
|
47
|
+
nested links, buttons, and inputs; essential information belongs in the label or
|
|
48
|
+
description. All slots accept phrasing content. Native refs point to the radio
|
|
49
|
+
button, and caller click/focus handlers can prevent a selection request.
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
<RadioGroup defaultValue="standard" aria-label="Delivery service">
|
|
53
|
+
<RadioCard value="standard" label="Standard delivery"
|
|
54
|
+
description="Delivered in three to five working days." />
|
|
55
|
+
<RadioCard value="express" label="Express delivery"
|
|
56
|
+
description="Delivered on the next working day." />
|
|
57
|
+
</RadioGroup>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Shared grid boundaries
|
|
61
|
+
|
|
62
|
+
The outlined variant owns its functional border. The plain variant adds no outer
|
|
63
|
+
border declarations, so `GridItem asChild` retains its structural seams. It does
|
|
64
|
+
not apply `border: 0`, inspect Grid internals, or infer an enclosing layout.
|
|
65
|
+
Both variants retain the themed background so a grid seam surface does not show
|
|
66
|
+
through the card body.
|
|
67
|
+
`RadioGroup asChild` delegates layout to its child without imposing the group's
|
|
68
|
+
default vertical flex arrangement. Grid remains responsible for responsive tracks,
|
|
69
|
+
shared boundaries, and optional cell padding.
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import { Grid, GridItem } from "@gridui/grid";
|
|
73
|
+
import { RadioCard, RadioGroup } from "@gridui/radio-group";
|
|
74
|
+
|
|
75
|
+
<RadioGroup asChild defaultValue="standard" aria-label="Delivery service">
|
|
76
|
+
<Grid columns={{ base: 1, md: 2 }} frame="cells">
|
|
77
|
+
<GridItem asChild padding={16}>
|
|
78
|
+
<RadioCard value="standard" variant="plain" label="Standard"
|
|
79
|
+
description="Three to five working days." />
|
|
80
|
+
</GridItem>
|
|
81
|
+
<GridItem asChild padding={16}>
|
|
82
|
+
<RadioCard value="express" variant="plain" label="Express"
|
|
83
|
+
description="Next working day." />
|
|
84
|
+
</GridItem>
|
|
85
|
+
</Grid>
|
|
86
|
+
</RadioGroup>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Migration
|
|
90
|
+
|
|
91
|
+
Import `Radio`, `RadioGroup`, and their prop types from `@gridui/radio-group`
|
|
92
|
+
instead of `@gridui/controls`, and load the new stylesheet. Controls no longer
|
|
93
|
+
contains or re-exports this family. Existing values, callbacks, refs, forms,
|
|
94
|
+
read-only behavior, and keyboard contracts remain available.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author WMXPY
|
|
3
|
+
* @package Radio Group
|
|
4
|
+
* @namespace Root
|
|
5
|
+
* @description Context
|
|
6
|
+
*/
|
|
7
|
+
export declare const RadioContext: import("react").Context<{
|
|
8
|
+
readonly readOnly: boolean;
|
|
9
|
+
readonly change: (value: string, event: Event) => void;
|
|
10
|
+
readonly navigation: {
|
|
11
|
+
current: Event | null;
|
|
12
|
+
};
|
|
13
|
+
} | null>;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author WMXPY
|
|
3
|
+
* @package Radio Group
|
|
4
|
+
* @namespace Root
|
|
5
|
+
* @description Index
|
|
6
|
+
*/
|
|
7
|
+
export type { RadioProps } from "./radio";
|
|
8
|
+
export { Radio } from "./radio";
|
|
9
|
+
export type { RadioCardProps } from "./radio-card";
|
|
10
|
+
export { RadioCard } from "./radio-card";
|
|
11
|
+
export type { RadioGroupProps } from "./radio-group";
|
|
12
|
+
export { RadioGroup } from "./radio-group";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// src/style.ts
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
var radioGroupStyles = cva("flex flex-col gap-gridui-8");
|
|
4
|
+
var radioStyles = cva("inline-flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-full border border-control-border bg-background text-foreground align-middle data-[state=checked]:bg-accent-background data-[state=checked]:text-accent-foreground data-disabled:cursor-not-allowed data-disabled:opacity-50 data-readonly:cursor-default aria-invalid:border-danger-foreground");
|
|
5
|
+
var radioCardStyles = cva("gridui-radio-card", {
|
|
6
|
+
variants: {
|
|
7
|
+
variant: {
|
|
8
|
+
outlined: "gridui-radio-card-outlined",
|
|
9
|
+
plain: "gridui-radio-card-plain"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
defaultVariants: { variant: "outlined" }
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// src/context.ts
|
|
16
|
+
import { createContext } from "react";
|
|
17
|
+
var RadioContext = createContext(null);
|
|
18
|
+
|
|
19
|
+
// src/use-radio-item.ts
|
|
20
|
+
import { useContext } from "react";
|
|
21
|
+
var useRadioItem = (props) => {
|
|
22
|
+
const context = useContext(RadioContext);
|
|
23
|
+
const onFocus = (event) => {
|
|
24
|
+
props.onFocus?.(event);
|
|
25
|
+
const navigation = context?.navigation.current;
|
|
26
|
+
if (navigation) {
|
|
27
|
+
context.navigation.current = null;
|
|
28
|
+
if (!event.defaultPrevented && !event.currentTarget.matches(":disabled")) {
|
|
29
|
+
context.change(props.value, navigation);
|
|
30
|
+
}
|
|
31
|
+
event.preventDefault();
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const onClick = (event) => {
|
|
35
|
+
props.onClick?.(event);
|
|
36
|
+
if (!event.defaultPrevented && !event.currentTarget.matches(":disabled")) {
|
|
37
|
+
context?.change(props.value, event.nativeEvent);
|
|
38
|
+
}
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
readOnly: context?.readOnly ?? false,
|
|
43
|
+
onFocus,
|
|
44
|
+
onClick
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// src/radio.tsx
|
|
49
|
+
import { cn } from "@gridui/style-utils";
|
|
50
|
+
import * as Primitive from "@radix-ui/react-radio-group";
|
|
51
|
+
import { jsx } from "react/jsx-runtime";
|
|
52
|
+
var Radio = (props) => {
|
|
53
|
+
const { className, ...restProps } = props;
|
|
54
|
+
const events = useRadioItem(props);
|
|
55
|
+
return /* @__PURE__ */ jsx(
|
|
56
|
+
Primitive.Item,
|
|
57
|
+
{
|
|
58
|
+
...restProps,
|
|
59
|
+
className: cn(radioStyles(), className),
|
|
60
|
+
"data-readonly": events.readOnly ? "" : void 0,
|
|
61
|
+
onFocus: events.onFocus,
|
|
62
|
+
onClick: events.onClick,
|
|
63
|
+
children: /* @__PURE__ */ jsx(
|
|
64
|
+
Primitive.Indicator,
|
|
65
|
+
{
|
|
66
|
+
className: "size-2 rounded-full bg-current"
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// src/radio-card.tsx
|
|
74
|
+
import { cn as cn2 } from "@gridui/style-utils";
|
|
75
|
+
import * as Primitive2 from "@radix-ui/react-radio-group";
|
|
76
|
+
import { useId } from "react";
|
|
77
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
78
|
+
var RadioCard = (props) => {
|
|
79
|
+
const {
|
|
80
|
+
label,
|
|
81
|
+
description,
|
|
82
|
+
leading,
|
|
83
|
+
variant = "outlined",
|
|
84
|
+
className,
|
|
85
|
+
"aria-label": ariaLabel,
|
|
86
|
+
"aria-labelledby": ariaLabelledBy,
|
|
87
|
+
"aria-describedby": ariaDescribedBy,
|
|
88
|
+
...restProps
|
|
89
|
+
} = props;
|
|
90
|
+
const labelId = useId();
|
|
91
|
+
const descriptionId = useId();
|
|
92
|
+
const described = description !== void 0 && description !== null && description !== false;
|
|
93
|
+
const descriptionIds = [
|
|
94
|
+
ariaDescribedBy,
|
|
95
|
+
described ? descriptionId : void 0
|
|
96
|
+
].filter(Boolean).join(" ") || void 0;
|
|
97
|
+
const events = useRadioItem(props);
|
|
98
|
+
return /* @__PURE__ */ jsxs(
|
|
99
|
+
Primitive2.Item,
|
|
100
|
+
{
|
|
101
|
+
...restProps,
|
|
102
|
+
className: cn2(radioCardStyles({ variant }), className),
|
|
103
|
+
"aria-label": ariaLabel,
|
|
104
|
+
"aria-labelledby": ariaLabelledBy ?? (ariaLabel ? void 0 : labelId),
|
|
105
|
+
"aria-describedby": descriptionIds,
|
|
106
|
+
"data-readonly": events.readOnly ? "" : void 0,
|
|
107
|
+
onFocus: events.onFocus,
|
|
108
|
+
onClick: events.onClick,
|
|
109
|
+
children: [
|
|
110
|
+
leading !== void 0 && leading !== null && /* @__PURE__ */ jsx2(
|
|
111
|
+
"span",
|
|
112
|
+
{
|
|
113
|
+
className: "gridui-radio-card-leading",
|
|
114
|
+
"aria-hidden": "true",
|
|
115
|
+
children: leading
|
|
116
|
+
}
|
|
117
|
+
),
|
|
118
|
+
/* @__PURE__ */ jsxs(
|
|
119
|
+
"span",
|
|
120
|
+
{
|
|
121
|
+
className: "gridui-radio-card-content",
|
|
122
|
+
children: [
|
|
123
|
+
/* @__PURE__ */ jsx2(
|
|
124
|
+
"span",
|
|
125
|
+
{
|
|
126
|
+
className: "type-ui",
|
|
127
|
+
id: labelId,
|
|
128
|
+
children: label
|
|
129
|
+
}
|
|
130
|
+
),
|
|
131
|
+
described && /* @__PURE__ */ jsx2(
|
|
132
|
+
"span",
|
|
133
|
+
{
|
|
134
|
+
className: "gridui-radio-card-description type-supporting",
|
|
135
|
+
id: descriptionId,
|
|
136
|
+
children: description
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
),
|
|
142
|
+
/* @__PURE__ */ jsx2(
|
|
143
|
+
"span",
|
|
144
|
+
{
|
|
145
|
+
className: "gridui-radio-card-indicator",
|
|
146
|
+
"aria-hidden": "true",
|
|
147
|
+
children: /* @__PURE__ */ jsx2(
|
|
148
|
+
Primitive2.Indicator,
|
|
149
|
+
{
|
|
150
|
+
className: "gridui-radio-card-dot"
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
)
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// src/radio-group.tsx
|
|
161
|
+
import { changeDetails } from "@gridui/interaction";
|
|
162
|
+
import { cn as cn3 } from "@gridui/style-utils";
|
|
163
|
+
import * as Primitive3 from "@radix-ui/react-radio-group";
|
|
164
|
+
import { useEffect, useImperativeHandle, useRef, useState } from "react";
|
|
165
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
166
|
+
var RadioGroup = (props) => {
|
|
167
|
+
const { asChild, className, value, defaultValue, onValueChange, readOnly = false, ref, onKeyDownCapture, onPointerDownCapture, ...restProps } = props;
|
|
168
|
+
const root = useRef(null);
|
|
169
|
+
const navigation = useRef(null);
|
|
170
|
+
const [
|
|
171
|
+
internalValue,
|
|
172
|
+
setInternalValue
|
|
173
|
+
] = useState(defaultValue ?? null);
|
|
174
|
+
const initial = useRef(defaultValue ?? null);
|
|
175
|
+
useImperativeHandle(ref, () => {
|
|
176
|
+
return root.current;
|
|
177
|
+
});
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
const form = restProps.form ? document.getElementById(restProps.form) : root.current?.closest("form");
|
|
180
|
+
if (!form || value !== void 0) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
let mounted = true;
|
|
184
|
+
const reset = (event) => {
|
|
185
|
+
setTimeout(() => {
|
|
186
|
+
if (mounted && !event.defaultPrevented) {
|
|
187
|
+
setInternalValue(initial.current);
|
|
188
|
+
}
|
|
189
|
+
}, 0);
|
|
190
|
+
};
|
|
191
|
+
form.addEventListener("reset", reset);
|
|
192
|
+
return () => {
|
|
193
|
+
mounted = false;
|
|
194
|
+
form.removeEventListener("reset", reset);
|
|
195
|
+
};
|
|
196
|
+
}, [
|
|
197
|
+
restProps.form,
|
|
198
|
+
value
|
|
199
|
+
]);
|
|
200
|
+
const change = (next, event) => {
|
|
201
|
+
if (readOnly || restProps.disabled) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const details = changeDetails(event);
|
|
205
|
+
onValueChange?.(next, details);
|
|
206
|
+
if (!details.isCanceled && value === void 0) {
|
|
207
|
+
setInternalValue(next);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
return /* @__PURE__ */ jsx3(
|
|
211
|
+
RadioContext.Provider,
|
|
212
|
+
{
|
|
213
|
+
value: {
|
|
214
|
+
readOnly,
|
|
215
|
+
change,
|
|
216
|
+
navigation
|
|
217
|
+
},
|
|
218
|
+
children: /* @__PURE__ */ jsx3(
|
|
219
|
+
Primitive3.Root,
|
|
220
|
+
{
|
|
221
|
+
...restProps,
|
|
222
|
+
asChild,
|
|
223
|
+
ref: root,
|
|
224
|
+
value: value === void 0 ? internalValue : value,
|
|
225
|
+
onKeyDownCapture: (event) => {
|
|
226
|
+
onKeyDownCapture?.(event);
|
|
227
|
+
navigation.current = !event.defaultPrevented && [
|
|
228
|
+
"ArrowUp",
|
|
229
|
+
"ArrowDown",
|
|
230
|
+
"ArrowLeft",
|
|
231
|
+
"ArrowRight",
|
|
232
|
+
"Home",
|
|
233
|
+
"End"
|
|
234
|
+
].includes(event.key) ? event.nativeEvent : null;
|
|
235
|
+
},
|
|
236
|
+
onPointerDownCapture: (event) => {
|
|
237
|
+
navigation.current = null;
|
|
238
|
+
onPointerDownCapture?.(event);
|
|
239
|
+
},
|
|
240
|
+
"aria-readonly": readOnly,
|
|
241
|
+
className: cn3(asChild ? void 0 : radioGroupStyles(), className)
|
|
242
|
+
}
|
|
243
|
+
)
|
|
244
|
+
}
|
|
245
|
+
);
|
|
246
|
+
};
|
|
247
|
+
export {
|
|
248
|
+
Radio,
|
|
249
|
+
RadioCard,
|
|
250
|
+
RadioGroup
|
|
251
|
+
};
|
|
252
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/style.ts", "../src/context.ts", "../src/use-radio-item.ts", "../src/radio.tsx", "../src/radio-card.tsx", "../src/radio-group.tsx"],
|
|
4
|
+
"sourcesContent": ["/**\n * @author WMXPY\n * @package Radio Group\n * @namespace Root\n * @description Style\n */\n\nimport { cva } from \"class-variance-authority\";\n\nexport const radioGroupStyles = cva(\"flex flex-col gap-gridui-8\");\nexport const radioStyles = cva(\"inline-flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-full border border-control-border bg-background text-foreground align-middle data-[state=checked]:bg-accent-background data-[state=checked]:text-accent-foreground data-disabled:cursor-not-allowed data-disabled:opacity-50 data-readonly:cursor-default aria-invalid:border-danger-foreground\");\nexport const radioCardStyles = cva(\"gridui-radio-card\", {\n variants: {\n variant: {\n outlined: \"gridui-radio-card-outlined\",\n plain: \"gridui-radio-card-plain\",\n },\n },\n defaultVariants: { variant: \"outlined\" },\n});\n", "/**\n * @author WMXPY\n * @package Radio Group\n * @namespace Root\n * @description Context\n */\n\nimport { createContext } from \"react\";\n\nexport const RadioContext = createContext<{\n readonly readOnly: boolean;\n readonly change: (value: string, event: Event) => void;\n readonly navigation: {\n current: Event | null;\n };\n} | null>(null);\n", "/**\n * @author WMXPY\n * @package Radio Group\n * @namespace Root\n * @description Use Radio Item\n */\n\nimport { RadioContext } from \"./context\";\nimport type * as Primitive from \"@radix-ui/react-radio-group\";\nimport type { ComponentPropsWithRef, FocusEvent, MouseEvent } from \"react\";\nimport { useContext } from \"react\";\n\ntype RadioItemEvents = Pick<ComponentPropsWithRef<typeof Primitive.Item>, \"onClick\" | \"onFocus\" | \"value\">;\n\nexport const useRadioItem = (props: RadioItemEvents) => {\n const context = useContext(RadioContext);\n const onFocus = (event: FocusEvent<HTMLButtonElement>): void => {\n props.onFocus?.(event);\n const navigation = context?.navigation.current;\n if (navigation) {\n context.navigation.current = null;\n if (!event.defaultPrevented && !event.currentTarget.matches(\":disabled\")) {\n context.change(props.value, navigation);\n }\n event.preventDefault();\n }\n };\n const onClick = (event: MouseEvent<HTMLButtonElement>): void => {\n props.onClick?.(event);\n if (!event.defaultPrevented && !event.currentTarget.matches(\":disabled\")) {\n context?.change(props.value, event.nativeEvent);\n }\n event.preventDefault();\n };\n return {\n readOnly: context?.readOnly ?? false,\n onFocus,\n onClick,\n };\n};\n", "/**\n * @author WMXPY\n * @package Radio Group\n * @namespace Root\n * @description Radio\n */\n\nimport { radioStyles } from \"./style\";\nimport { useRadioItem } from \"./use-radio-item\";\nimport { cn } from \"@gridui/style-utils\";\nimport * as Primitive from \"@radix-ui/react-radio-group\";\nimport type { ComponentPropsWithRef, FC } from \"react\";\n\nexport type RadioProps = Omit<ComponentPropsWithRef<typeof Primitive.Item>, \"children\">;\n\nexport const Radio: FC<RadioProps> = (props: RadioProps) => {\n const { className, ...restProps } = props;\n const events = useRadioItem(props);\n return (<Primitive.Item\n {...restProps}\n className={cn(radioStyles(), className)}\n data-readonly={events.readOnly ? \"\" : undefined}\n onFocus={events.onFocus}\n onClick={events.onClick}\n >\n <Primitive.Indicator\n className=\"size-2 rounded-full bg-current\"\n />\n </Primitive.Item>);\n};\n", "/**\n * @author WMXPY\n * @package Radio Group\n * @namespace Root\n * @description Radio Card\n */\n\nimport type { RadioProps } from \"./radio\";\nimport { radioCardStyles } from \"./style\";\nimport { useRadioItem } from \"./use-radio-item\";\nimport { cn } from \"@gridui/style-utils\";\nimport * as Primitive from \"@radix-ui/react-radio-group\";\nimport type { FC, ReactNode } from \"react\";\nimport { useId } from \"react\";\n\nexport type RadioCardProps = Omit<RadioProps, \"asChild\"> & {\n readonly label: ReactNode;\n readonly description?: ReactNode;\n readonly leading?: ReactNode;\n readonly variant?: \"outlined\" | \"plain\";\n};\n\nexport const RadioCard: FC<RadioCardProps> = (props: RadioCardProps) => {\n const {\n label, description, leading, variant = \"outlined\", className,\n \"aria-label\": ariaLabel, \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy, ...restProps\n } = props;\n const labelId = useId();\n const descriptionId = useId();\n const described = description !== undefined && description !== null && description !== false;\n const descriptionIds = [\n ariaDescribedBy,\n described ? descriptionId : undefined,\n ].filter(Boolean).join(\" \") || undefined;\n const events = useRadioItem(props);\n return (<Primitive.Item\n {...restProps}\n className={cn(radioCardStyles({ variant }), className)}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy ?? (ariaLabel ? undefined : labelId)}\n aria-describedby={descriptionIds}\n data-readonly={events.readOnly ? \"\" : undefined}\n onFocus={events.onFocus}\n onClick={events.onClick}\n >\n {leading !== undefined && leading !== null && (<span\n className=\"gridui-radio-card-leading\"\n aria-hidden=\"true\"\n >\n {leading}\n </span>)}\n <span\n className=\"gridui-radio-card-content\"\n >\n <span\n className=\"type-ui\"\n id={labelId}\n >\n {label}\n </span>\n {described && (<span\n className=\"gridui-radio-card-description type-supporting\"\n id={descriptionId}\n >\n {description}\n </span>)}\n </span>\n <span\n className=\"gridui-radio-card-indicator\"\n aria-hidden=\"true\"\n >\n <Primitive.Indicator\n className=\"gridui-radio-card-dot\"\n />\n </span>\n </Primitive.Item>);\n};\n", "/**\n * @author WMXPY\n * @package Radio Group\n * @namespace Root\n * @description Radio Group\n */\n\nimport { RadioContext } from \"./context\";\nimport { radioGroupStyles } from \"./style\";\nimport type { ChangeDetails } from \"@gridui/interaction\";\nimport { changeDetails } from \"@gridui/interaction\";\nimport { cn } from \"@gridui/style-utils\";\nimport * as Primitive from \"@radix-ui/react-radio-group\";\nimport type { ComponentPropsWithRef, FC } from \"react\";\nimport { useEffect, useImperativeHandle, useRef, useState } from \"react\";\n\nexport type RadioGroupProps = Omit<ComponentPropsWithRef<typeof Primitive.Root>, \"onValueChange\" | \"defaultValue\"> & {\n readonly defaultValue?: string | null | undefined;\n readonly readOnly?: boolean | undefined;\n readonly onValueChange?: ((value: string, details: ChangeDetails) => void) | undefined;\n};\nexport const RadioGroup: FC<RadioGroupProps> = (props: RadioGroupProps) => {\n const { asChild, className, value, defaultValue, onValueChange, readOnly = false, ref, onKeyDownCapture, onPointerDownCapture, ...restProps } = props;\n const root = useRef<HTMLDivElement>(null);\n const navigation = useRef<Event | null>(null);\n const [\n internalValue,\n setInternalValue,\n ] = useState<string | null>(defaultValue ?? null);\n const initial = useRef(defaultValue ?? null);\n useImperativeHandle(ref, () => {\n return root.current as HTMLDivElement;\n });\n useEffect(() => {\n const form = restProps.form ? document.getElementById(restProps.form) : root.current?.closest(\"form\");\n if (!form || value !== undefined) {\n return;\n }\n let mounted = true;\n const reset = (event: Event) => {\n // Wait until React bubbling handlers can cancel the native reset.\n setTimeout(() => {\n if (mounted && !event.defaultPrevented) {\n setInternalValue(initial.current);\n }\n }, 0);\n };\n form.addEventListener(\"reset\", reset);\n return () => {\n mounted = false;\n form.removeEventListener(\"reset\", reset);\n };\n }, [\n restProps.form,\n value,\n ]);\n const change = (next: string, event: Event) => {\n if (readOnly || restProps.disabled) {\n return;\n }\n const details = changeDetails(event);\n onValueChange?.(next, details);\n if (!details.isCanceled && value === undefined) {\n setInternalValue(next);\n }\n };\n return (<RadioContext.Provider\n value={{\n readOnly,\n change,\n navigation,\n }}\n >\n <Primitive.Root\n {...restProps}\n asChild={asChild}\n ref={root}\n value={value === undefined ? internalValue : value}\n onKeyDownCapture={(event) => {\n onKeyDownCapture?.(event);\n navigation.current = !event.defaultPrevented && [\n \"ArrowUp\",\n \"ArrowDown\",\n \"ArrowLeft\",\n \"ArrowRight\",\n \"Home\",\n \"End\",\n ].includes(event.key)\n ? event.nativeEvent : null;\n }}\n onPointerDownCapture={(event) => {\n navigation.current = null; onPointerDownCapture?.(event);\n }}\n aria-readonly={readOnly}\n className={cn(asChild ? undefined : radioGroupStyles(), className)}\n />\n </RadioContext.Provider>);\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["cn", "Primitive", "jsx", "cn", "Primitive", "jsx"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author WMXPY
|
|
3
|
+
* @package Radio Group
|
|
4
|
+
* @namespace Root
|
|
5
|
+
* @description Radio Card
|
|
6
|
+
*/
|
|
7
|
+
import type { RadioProps } from "./radio";
|
|
8
|
+
import type { FC, ReactNode } from "react";
|
|
9
|
+
export type RadioCardProps = Omit<RadioProps, "asChild"> & {
|
|
10
|
+
readonly label: ReactNode;
|
|
11
|
+
readonly description?: ReactNode;
|
|
12
|
+
readonly leading?: ReactNode;
|
|
13
|
+
readonly variant?: "outlined" | "plain";
|
|
14
|
+
};
|
|
15
|
+
export declare const RadioCard: FC<RadioCardProps>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author WMXPY
|
|
3
|
+
* @package Radio Group
|
|
4
|
+
* @namespace Root
|
|
5
|
+
* @description Radio Group
|
|
6
|
+
*/
|
|
7
|
+
import type { ChangeDetails } from "@gridui/interaction";
|
|
8
|
+
import * as Primitive from "@radix-ui/react-radio-group";
|
|
9
|
+
import type { ComponentPropsWithRef, FC } from "react";
|
|
10
|
+
export type RadioGroupProps = Omit<ComponentPropsWithRef<typeof Primitive.Root>, "onValueChange" | "defaultValue"> & {
|
|
11
|
+
readonly defaultValue?: string | null | undefined;
|
|
12
|
+
readonly readOnly?: boolean | undefined;
|
|
13
|
+
readonly onValueChange?: ((value: string, details: ChangeDetails) => void) | undefined;
|
|
14
|
+
};
|
|
15
|
+
export declare const RadioGroup: FC<RadioGroupProps>;
|
package/dist/radio.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author WMXPY
|
|
3
|
+
* @package Radio Group
|
|
4
|
+
* @namespace Root
|
|
5
|
+
* @description Radio
|
|
6
|
+
*/
|
|
7
|
+
import * as Primitive from "@radix-ui/react-radio-group";
|
|
8
|
+
import type { ComponentPropsWithRef, FC } from "react";
|
|
9
|
+
export type RadioProps = Omit<ComponentPropsWithRef<typeof Primitive.Item>, "children">;
|
|
10
|
+
export declare const Radio: FC<RadioProps>;
|
package/dist/style.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author WMXPY
|
|
3
|
+
* @package Radio Group
|
|
4
|
+
* @namespace Root
|
|
5
|
+
* @description Style
|
|
6
|
+
*/
|
|
7
|
+
export declare const radioGroupStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
8
|
+
export declare const radioStyles: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
9
|
+
export declare const radioCardStyles: (props?: ({
|
|
10
|
+
variant?: "outlined" | "plain" | null | undefined;
|
|
11
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
package/dist/styles.css
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties;
|
|
3
|
+
@layer theme, base, components, utilities;
|
|
4
|
+
@layer theme {
|
|
5
|
+
:root, :host {
|
|
6
|
+
--spacing: 0.25rem;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
@layer utilities {
|
|
10
|
+
.flex {
|
|
11
|
+
display: flex;
|
|
12
|
+
}
|
|
13
|
+
.inline-flex {
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
}
|
|
16
|
+
.size-2 {
|
|
17
|
+
width: calc(var(--spacing) * 2);
|
|
18
|
+
height: calc(var(--spacing) * 2);
|
|
19
|
+
}
|
|
20
|
+
.size-5 {
|
|
21
|
+
width: calc(var(--spacing) * 5);
|
|
22
|
+
height: calc(var(--spacing) * 5);
|
|
23
|
+
}
|
|
24
|
+
.shrink-0 {
|
|
25
|
+
flex-shrink: 0;
|
|
26
|
+
}
|
|
27
|
+
.cursor-pointer {
|
|
28
|
+
cursor: pointer;
|
|
29
|
+
}
|
|
30
|
+
.flex-col {
|
|
31
|
+
flex-direction: column;
|
|
32
|
+
}
|
|
33
|
+
.items-center {
|
|
34
|
+
align-items: center;
|
|
35
|
+
}
|
|
36
|
+
.justify-center {
|
|
37
|
+
justify-content: center;
|
|
38
|
+
}
|
|
39
|
+
.gap-gridui-8 {
|
|
40
|
+
gap: var(--gridui-space-8);
|
|
41
|
+
}
|
|
42
|
+
.rounded-full {
|
|
43
|
+
border-radius: calc(infinity * 1px);
|
|
44
|
+
}
|
|
45
|
+
.border {
|
|
46
|
+
border-style: var(--tw-border-style);
|
|
47
|
+
border-width: 1px;
|
|
48
|
+
}
|
|
49
|
+
.border-control-border {
|
|
50
|
+
border-color: var(--gridui-control-border);
|
|
51
|
+
}
|
|
52
|
+
.bg-background {
|
|
53
|
+
background-color: var(--gridui-background);
|
|
54
|
+
}
|
|
55
|
+
.bg-current {
|
|
56
|
+
background-color: currentcolor;
|
|
57
|
+
}
|
|
58
|
+
.align-middle {
|
|
59
|
+
vertical-align: middle;
|
|
60
|
+
}
|
|
61
|
+
.type-supporting {
|
|
62
|
+
font: var(--gridui-type-supporting);
|
|
63
|
+
letter-spacing: normal;
|
|
64
|
+
}
|
|
65
|
+
.type-ui {
|
|
66
|
+
font: var(--gridui-type-ui);
|
|
67
|
+
letter-spacing: normal;
|
|
68
|
+
}
|
|
69
|
+
.text-foreground {
|
|
70
|
+
color: var(--gridui-foreground);
|
|
71
|
+
}
|
|
72
|
+
.aria-invalid\:border-danger-foreground[aria-invalid="true"] {
|
|
73
|
+
border-color: var(--gridui-danger-foreground);
|
|
74
|
+
}
|
|
75
|
+
.data-disabled\:cursor-not-allowed[data-disabled] {
|
|
76
|
+
cursor: not-allowed;
|
|
77
|
+
}
|
|
78
|
+
.data-disabled\:opacity-50[data-disabled] {
|
|
79
|
+
opacity: 50%;
|
|
80
|
+
}
|
|
81
|
+
.data-readonly\:cursor-default[data-readonly] {
|
|
82
|
+
cursor: default;
|
|
83
|
+
}
|
|
84
|
+
.data-\[state\=checked\]\:bg-accent-background[data-state="checked"] {
|
|
85
|
+
background-color: var(--gridui-accent-background);
|
|
86
|
+
}
|
|
87
|
+
.data-\[state\=checked\]\:text-accent-foreground[data-state="checked"] {
|
|
88
|
+
color: var(--gridui-accent-foreground);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
@layer components {
|
|
92
|
+
.gridui-radio-card {
|
|
93
|
+
box-sizing: border-box;
|
|
94
|
+
position: relative;
|
|
95
|
+
display: flex;
|
|
96
|
+
align-items: flex-start;
|
|
97
|
+
gap: var(--gridui-space-16);
|
|
98
|
+
width: 100%;
|
|
99
|
+
min-width: 0;
|
|
100
|
+
min-height: 2.75rem;
|
|
101
|
+
padding: var(--gridui-space-16);
|
|
102
|
+
background: var(--gridui-background);
|
|
103
|
+
color: var(--gridui-foreground);
|
|
104
|
+
text-align: start;
|
|
105
|
+
cursor: pointer;
|
|
106
|
+
transition: background var(--gridui-motion-duration-quick) var(--gridui-motion-ease-out);
|
|
107
|
+
}
|
|
108
|
+
.gridui-radio-card-outlined {
|
|
109
|
+
border: 1px solid var(--gridui-control-border);
|
|
110
|
+
border-radius: var(--gridui-control-radius);
|
|
111
|
+
}
|
|
112
|
+
.gridui-radio-card[data-state="checked"] {
|
|
113
|
+
background: var(--gridui-surface);
|
|
114
|
+
}
|
|
115
|
+
.gridui-radio-card:disabled {
|
|
116
|
+
cursor: not-allowed;
|
|
117
|
+
opacity: 0.5;
|
|
118
|
+
}
|
|
119
|
+
.gridui-radio-card[data-readonly] {
|
|
120
|
+
cursor: default;
|
|
121
|
+
}
|
|
122
|
+
.gridui-radio-card:not(:disabled):not([data-readonly]):hover {
|
|
123
|
+
background: var(--gridui-surface-hover);
|
|
124
|
+
}
|
|
125
|
+
.gridui-radio-card-content {
|
|
126
|
+
display: flex;
|
|
127
|
+
flex: 1;
|
|
128
|
+
flex-direction: column;
|
|
129
|
+
gap: var(--gridui-space-8);
|
|
130
|
+
min-width: 0;
|
|
131
|
+
}
|
|
132
|
+
.gridui-radio-card-description, .gridui-radio-card-leading {
|
|
133
|
+
color: var(--gridui-muted);
|
|
134
|
+
}
|
|
135
|
+
.gridui-radio-card-leading {
|
|
136
|
+
display: inline-flex;
|
|
137
|
+
flex-shrink: 0;
|
|
138
|
+
}
|
|
139
|
+
.gridui-radio-card-leading svg {
|
|
140
|
+
width: 1.25rem;
|
|
141
|
+
height: 1.25rem;
|
|
142
|
+
}
|
|
143
|
+
.gridui-radio-card-indicator {
|
|
144
|
+
display: inline-flex;
|
|
145
|
+
align-items: center;
|
|
146
|
+
justify-content: center;
|
|
147
|
+
flex-shrink: 0;
|
|
148
|
+
width: 1.25rem;
|
|
149
|
+
height: 1.25rem;
|
|
150
|
+
border: 1px solid var(--gridui-control-border);
|
|
151
|
+
border-radius: 50%;
|
|
152
|
+
background: var(--gridui-background);
|
|
153
|
+
}
|
|
154
|
+
.gridui-radio-card[data-state="checked"] .gridui-radio-card-indicator {
|
|
155
|
+
border-color: var(--gridui-accent-background);
|
|
156
|
+
background: var(--gridui-accent-background);
|
|
157
|
+
color: var(--gridui-accent-foreground);
|
|
158
|
+
}
|
|
159
|
+
.gridui-radio-card[aria-invalid="true"] .gridui-radio-card-indicator {
|
|
160
|
+
border-color: var(--gridui-danger-foreground);
|
|
161
|
+
}
|
|
162
|
+
.gridui-radio-card-dot {
|
|
163
|
+
width: 0.5rem;
|
|
164
|
+
height: 0.5rem;
|
|
165
|
+
border-radius: 50%;
|
|
166
|
+
background: currentcolor;
|
|
167
|
+
}
|
|
168
|
+
@media (prefers-reduced-motion: reduce) {
|
|
169
|
+
.gridui-radio-card {
|
|
170
|
+
transition: none;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
@property --tw-border-style {
|
|
175
|
+
syntax: "*";
|
|
176
|
+
inherits: false;
|
|
177
|
+
initial-value: solid;
|
|
178
|
+
}
|
|
179
|
+
@layer properties {
|
|
180
|
+
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
|
|
181
|
+
*, ::before, ::after, ::backdrop {
|
|
182
|
+
--tw-border-style: solid;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author WMXPY
|
|
3
|
+
* @package Radio Group
|
|
4
|
+
* @namespace Root
|
|
5
|
+
* @description Use Radio Item
|
|
6
|
+
*/
|
|
7
|
+
import type * as Primitive from "@radix-ui/react-radio-group";
|
|
8
|
+
import type { ComponentPropsWithRef, FocusEvent, MouseEvent } from "react";
|
|
9
|
+
type RadioItemEvents = Pick<ComponentPropsWithRef<typeof Primitive.Item>, "onClick" | "onFocus" | "value">;
|
|
10
|
+
export declare const useRadioItem: (props: RadioItemEvents) => {
|
|
11
|
+
readOnly: boolean;
|
|
12
|
+
onFocus: (event: FocusEvent<HTMLButtonElement>) => void;
|
|
13
|
+
onClick: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
14
|
+
};
|
|
15
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,60 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gridui/radio-group",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "GridUI radio groups and rich single-choice cards",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/sudo-ui/GridUI-React-Monorepo.git",
|
|
10
|
+
"directory": "packages/radio-group"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"sideEffects": [
|
|
17
|
+
"**/*.css"
|
|
18
|
+
],
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./styles.css": "./dist/styles.css"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@gridui/interaction": "^1.0.0",
|
|
31
|
+
"@gridui/style-utils": "^2.1.1",
|
|
32
|
+
"@gridui/theme": "^2.1.1",
|
|
33
|
+
"@radix-ui/react-radio-group": "1.5.1",
|
|
34
|
+
"class-variance-authority": "0.7.1"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@gridui-internal/configuration": "0.0.0",
|
|
38
|
+
"@gridui/grid": "2.1.2",
|
|
39
|
+
"@jest/globals": "30.5.2",
|
|
40
|
+
"@storybook/react-vite": "10.6.3",
|
|
41
|
+
"@types/jest": "30.0.0",
|
|
42
|
+
"@types/node": "26.6.5",
|
|
43
|
+
"@types/react": "19.3.0",
|
|
44
|
+
"@types/react-dom": "19.3.0",
|
|
45
|
+
"jest": "30.5.2",
|
|
46
|
+
"react": "19.3.1",
|
|
47
|
+
"react-dom": "19.3.1",
|
|
48
|
+
"typescript": "6.0.3"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"react": "^19.0.0",
|
|
52
|
+
"react-dom": "^19.0.0"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "node ../../scripts/compilation/package.ts",
|
|
56
|
+
"compile": "tsc --project tsconfig.json",
|
|
57
|
+
"test": "jest",
|
|
58
|
+
"coverage": "jest --coverage"
|
|
59
|
+
}
|
|
6
60
|
}
|