first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,147 @@
# RadioGroup
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs --> - radio-group.variants — `radioGroupVariants` and `ra…
- [radio-group.variants](./radio-group.variants.md) — `radioGroupVariants` and `radioGroupItemVariants` are the cva (class-variance-authority) configurations powering `<Radi…
- radio-group.variants — `radioGroupVariants` and `ra…
- [radio-group.variants](./radio-group.variants.md) — `radioGroupVariants` and `radioGroupItemVariants` are the cva (class-variance-authority) configurations powering `<Radi…
- radio-group.variants — `radioGroupVariants` and `ra…
- [radio-group.variants](./radio-group.variants.md) — `radioGroupVariants` and `radioGroupItemVariants` are the cva (class-variance-authority) configurations powering `<Radi…
- radio-group.variants — `radioGroupVariants` and `ra…
- [radio-group.variants](./radio-group.variants.md) — `radioGroupVariants` and `radioGroupItemVariants` are the cva (class-variance-authority) configurations powering `<Radi…
- [radio-group.variants](./radio-group.variants.md) — `radioGroupVariants` and `radioGroupItemVariants` are the cva (class-variance-authority) configurations powering `<Radi…
## Purpose
`<RadioGroup>` and `<RadioGroupItem>` are the chassis radio-group primitives built on `@radix-ui/react-radio-group`. Radix supplies the a11y semantics (`role="radiogroup"` on the container, `role="radio"` on each item, roving tabindex across items, arrow-key navigation, native `Space` activation, controlled `value` propagation); the chassis layers a vertical-stack flex/gap layout via cva and a circular indicator dot rendered with `<CircleIcon fill-current>` from `lucide-react`.
Per the shadcn convention, items always sit inside the group via `<RadioGroupItem>` — rendering an item without a group is unsupported. Both components forward refs to their underlying Radix elements so consumers can focus / measure / drive animations.
## Features
- Container renders a `<div role="radiogroup">` with `grid gap-2` so items stack vertically with a small uniform gap (NOT `space-y-*` per the shadcn skill rule).
- Items render a `<button role="radio">` with the circular `border-primary` outline.
- Three sizes for items (`sm`, `default`, `lg`) scaling the radio circle.
- Indicator dot rendered via `<CircleIcon fill-current text-current>` so the dot inherits the surrounding text colour and the chassis switches it without an extra theme prop.
- Controlled and uncontrolled use via Radix `value` / `defaultValue` / `onValueChange`.
- `disabled` per-item or per-group blocks pointer + keyboard input.
- Refs forwarded on both `<RadioGroup>` and `<RadioGroupItem>`.
- `motion-reduce:transition-none` collapses transitions for `prefers-reduced-motion: reduce`.
- Theme-driven variant overrides via `useThemedVariants("RadioGroup" | "RadioGroupItem")`.
## Usage: Basic Example
```tsx
import { RadioGroup, RadioGroupItem } from "@suaveplan/react-ui";
export function PlanPicker() {
return (
<RadioGroup defaultValue="pro">
<div className="flex items-center gap-2">
<RadioGroupItem value="free" id="plan-free" />
<label htmlFor="plan-free">Free</label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="pro" id="plan-pro" />
<label htmlFor="plan-pro">Pro</label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="team" id="plan-team" />
<label htmlFor="plan-team">Team</label>
</div>
</RadioGroup>
);
}
```
## Usage: Advanced Example
```tsx
import { RadioGroup, RadioGroupItem, FieldSet, FieldLegend } from "@suaveplan/react-ui";
import { useState } from "react";
interface DeliverySpeed {
readonly id: string;
readonly label: string;
readonly description: string;
readonly disabled?: boolean;
}
export function DeliveryPicker({ options }: { options: readonly DeliverySpeed[] }) {
const [speed, setSpeed] = useState("standard");
return (
<FieldSet>
<FieldLegend>Delivery speed</FieldLegend>
<RadioGroup value={speed} onValueChange={setSpeed}>
{options.map((opt) => (
<div key={opt.id} className="flex items-start gap-2">
<RadioGroupItem
value={opt.id}
id={`delivery-${opt.id}`}
disabled={opt.disabled}
className="mt-1"
/>
<div>
<label htmlFor={`delivery-${opt.id}`}>{opt.label}</label>
<p className="text-sm text-muted-foreground">
{opt.description}
</p>
</div>
</div>
))}
</RadioGroup>
</FieldSet>
);
}
```
## API Reference
### `<RadioGroup>`
`forwardRef<ComponentRef<typeof RadioGroupPrimitive.Root>, RadioGroupProps>` — renders the Radix Root.
#### `RadioGroupProps`
Inherits every prop from Radix `RadioGroupPrimitive.Root` except `asChild`. Common props:
| Prop | Type | Default | Description |
|---|---|---|---|
| `value` | `string` | _uncontrolled_ | Currently selected value. |
| `defaultValue` | `string` | _none_ | Initial value for uncontrolled use. |
| `onValueChange` | `(value: string) => void` | _none_ | Fires on value change. |
| `disabled` | `boolean` | `false` | Disables every item. |
| `name` | `string` | _none_ | Native form-field name. |
| `orientation` | `"horizontal" \| "vertical"` | `"vertical"` | Arrow-key navigation axis. |
### `<RadioGroupItem>`
`forwardRef<ComponentRef<typeof RadioGroupPrimitive.Item>, RadioGroupItemProps>` — renders a Radix Item containing the indicator dot.
#### `RadioGroupItemProps`
Inherits every prop from Radix `RadioGroupPrimitive.Item` except `asChild`. Adds:
| Prop | Type | Default | Description |
|---|---|---|---|
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Visual variant scaling the radio circle. |
| `value` | `string` | _required_ | Unique value within the surrounding group. |
#### `radioGroupVariants` / `radioGroupItemVariants`
Both bare cva resolvers exported for external composition.
## Implementation Notes
- The container uses `grid gap-2` rather than `flex flex-col gap-2` because `grid` collapses cross-axis layout in a way that keeps the items column-aligned even when they have wildly different content lengths (label + description blocks).
- Per the shadcn skill rule, the chassis NEVER uses `space-y-*` — `grid gap-*` flows the same in RTL.
- The indicator dot uses `fill-current text-current` so it inherits the surrounding text colour automatically; theme overrides do not need to special-case the dot.
- Refs target the Radix Root + Item. The Item ref attaches to the rendered `<button>` so consumers can `.focus()` programmatically.
- Items that require a description/help text typically wrap the radio + content in `<div className="flex items-start gap-2">` with `mt-1` on the radio so the dot aligns with the first line of text.