"use client" import * as React from "react" import { cn } from "@/lib/utils" export type RadioGroupOption = { label: React.ReactNode value: string description?: React.ReactNode disabled?: boolean } export type RadioGroupProps = Omit, "onChange"> & { name?: string value?: string defaultValue?: string onValueChange?: (value: string) => void options: RadioGroupOption[] orientation?: "vertical" | "horizontal" size?: "sm" | "default" | "lg" invalid?: boolean disabled?: boolean itemClassName?: string itemLabelClassName?: string itemDescriptionClassName?: string } const sizeClassName = { sm: "size-3.5", default: "size-4", lg: "size-5", } function RadioGroup({ name, value, defaultValue, onValueChange, options, orientation = "vertical", size = "default", invalid = false, disabled = false, className, itemClassName, itemLabelClassName, itemDescriptionClassName, ...props }: RadioGroupProps) { const generatedName = React.useId() const resolvedName = name ?? generatedName const [internalValue, setInternalValue] = React.useState(defaultValue ?? "") const isControlled = value !== undefined const currentValue = isControlled ? value : internalValue const itemRefs = React.useRef>([]) const enabledOptions = options.filter((option) => !option.disabled && !disabled) const setValue = (nextValue: string) => { if (!isControlled) setInternalValue(nextValue) onValueChange?.(nextValue) } const focusOption = (nextIndex: number) => { const option = enabledOptions[nextIndex] if (!option) return const optionIndex = options.findIndex((item) => item.value === option.value) itemRefs.current[optionIndex]?.focus() setValue(option.value) } const handleKeyDown: React.KeyboardEventHandler = (event) => { if (!["ArrowDown", "ArrowRight", "ArrowUp", "ArrowLeft", "Home", "End"].includes(event.key)) return if (enabledOptions.length === 0) return event.preventDefault() const currentEnabledIndex = Math.max( 0, enabledOptions.findIndex((option) => option.value === currentValue) ) if (event.key === "Home") { focusOption(0) return } if (event.key === "End") { focusOption(enabledOptions.length - 1) return } const direction = event.key === "ArrowDown" || event.key === "ArrowRight" ? 1 : -1 focusOption((currentEnabledIndex + direction + enabledOptions.length) % enabledOptions.length) } return (
{options.map((option, index) => { const checked = option.value === currentValue const optionDisabled = disabled || option.disabled return ( ) })}
) } export { RadioGroup }