"use client"; import React, { forwardRef } from "react"; import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "@/design-system/lib/utils"; /** * Radio 单选按钮组件 * * Design System 中的单选按钮组件,支持多种状态和尺寸。 * * @example * ```tsx * // 默认单选按钮 * 选项 1 * 选项 2 * * // 受控组件 * setValue(e.target.value)} * > * 选项 1 * * ``` */ /** * 单选按钮变体样式 */ const radioVariants = cva( // 基础样式 "peer h-4 w-4 shrink-0 rounded-full border-2 transition-all duration-250 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 appearance-none cursor-pointer", { variants: { variant: { default: "border-gray-300 checked:border-primary-500", success: "border-gray-300 checked:border-success-500", warning: "border-gray-300 checked:border-warning-500", error: "border-gray-300 checked:border-error-500", }, size: { sm: "h-3.5 w-3.5", md: "h-4 w-4", lg: "h-5 w-5", }, error: { true: "border-error-500", false: "", }, }, defaultVariants: { variant: "default", size: "md", error: false, }, } ); export type RadioVariant = VariantProps["variant"]; export type RadioSize = VariantProps["size"]; export interface RadioProps extends Omit, "size">, VariantProps { // 标签文本 label?: React.ReactNode; // 标签位置 labelPosition?: "left" | "right"; // 自定义单选按钮类名 radioClassName?: string; } /** * Radio 单选按钮组件 */ export const Radio = forwardRef( ( { variant = "default", size = "md", error = false, label, labelPosition = "right", className, radioClassName, disabled, ...props }, ref ) => { const radioId = React.useId(); const renderRadio = () => (
{/* 选中状态的圆点 */}
); const renderLabel = () => { if (!label) return null; return ( ); }; if (!label) { return renderRadio(); } return (
{labelPosition === "left" && renderLabel()} {renderRadio()} {labelPosition === "right" && renderLabel()}
); } ); Radio.displayName = "Radio"; /** * RadioGroup - 单选按钮组 */ export interface RadioGroupProps { children: React.ReactNode; name: string; label?: string; error?: string; required?: boolean; value?: string; onChange?: (value: string) => void; className?: string; orientation?: "vertical" | "horizontal"; } export function RadioGroup({ children, name, label, error, required, value, onChange, className, orientation = "vertical", }: RadioGroupProps) { // 为每个 Radio 注入 name 和 onChange const enhancedChildren = React.Children.map(children, (child) => { if (React.isValidElement(child)) { const childProps = child.props as { value?: string; onChange?: (e: React.ChangeEvent) => void }; return React.cloneElement(child as React.ReactElement, { name, checked: value !== undefined ? childProps.value === value : undefined, onChange: (e: React.ChangeEvent) => { onChange?.(e.target.value); childProps.onChange?.(e); }, }); } return child; }); return (
{label && (
{label} {required && *}
)}
{enhancedChildren}
{error &&

{error}

}
); }