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.
58 lines
1.6 KiB
TypeScript
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>
|
|
);
|
|
}
|