Mats Bosson c2422d2da0 RadioGroup component
Implements headless RadioGroup with root + item sub-components, keyboard navigation (ArrowDown/Up/Left/Right), controlled/uncontrolled value via createControllableSignal, and disabled state. 8 tests passing.
2026-03-29 07:50:21 +07:00

58 lines
1.6 KiB
TypeScript

import type { JSX } from "solid-js";
import { splitProps } from "solid-js";
import { createControllableSignal } from "../../primitives/create-controllable-signal";
import { RadioGroupContextProvider, type RadioGroupContextValue } from "./radio-group-context";
export interface RadioGroupRootProps extends JSX.HTMLAttributes<HTMLDivElement> {
value?: string;
defaultValue?: string;
onValueChange?: (value: string) => void;
disabled?: boolean;
name?: string;
orientation?: "horizontal" | "vertical";
children: JSX.Element;
}
/**
* Root container for a group of mutually exclusive radio buttons.
*/
export function RadioGroupRoot(props: RadioGroupRootProps): JSX.Element {
const [local, rest] = splitProps(props, [
"value",
"defaultValue",
"onValueChange",
"disabled",
"name",
"orientation",
"children",
]);
const [selectedValue, setSelectedValue] = createControllableSignal<string | undefined>({
value: () => local.value,
defaultValue: () => local.defaultValue,
onChange: (v) => {
if (v !== undefined) local.onValueChange?.(v);
},
});
const ctx: RadioGroupContextValue = {
value: selectedValue,
onValueChange: (v) => setSelectedValue(v),
disabled: () => local.disabled ?? false,
name: () => local.name,
};
return (
<RadioGroupContextProvider value={ctx}>
<div
role="radiogroup"
aria-orientation={local.orientation}
data-disabled={local.disabled || undefined}
{...rest}
>
{local.children}
</div>
</RadioGroupContextProvider>
);
}