79 lines
2.1 KiB
TypeScript
79 lines
2.1 KiB
TypeScript
"use client"
|
|
|
|
import type { LucideIcon } from "lucide-react"
|
|
|
|
import { cn } from "@/lib/utils"
|
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
|
|
|
interface SelectableCardOption {
|
|
value: string
|
|
label: string
|
|
description?: string
|
|
icon?: LucideIcon
|
|
}
|
|
|
|
interface SelectableCardGroupProps {
|
|
options: SelectableCardOption[]
|
|
value: string
|
|
onValueChange: (value: string) => void
|
|
className?: string
|
|
columns?: 1 | 2 | 3
|
|
disabled?: boolean
|
|
}
|
|
|
|
function SelectableCardGroup({
|
|
options,
|
|
value,
|
|
onValueChange,
|
|
className,
|
|
columns = 2,
|
|
disabled = false,
|
|
}: SelectableCardGroupProps) {
|
|
return (
|
|
<RadioGroup
|
|
value={value}
|
|
onValueChange={(v) => onValueChange(String(v))}
|
|
disabled={disabled}
|
|
className={cn(
|
|
"grid gap-3",
|
|
columns === 1 && "grid-cols-1",
|
|
columns === 2 && "grid-cols-1 sm:grid-cols-2",
|
|
columns === 3 && "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
|
|
className
|
|
)}
|
|
>
|
|
{options.map((opt) => {
|
|
const Icon = opt.icon
|
|
return (
|
|
<label
|
|
key={opt.value}
|
|
htmlFor={`selectable-card-${opt.value}`}
|
|
className={cn(
|
|
"flex items-start gap-3 rounded-xl border bg-card p-4 transition-colors has-data-checked:border-primary has-data-checked:bg-primary/5 has-data-checked:ring-1 has-data-checked:ring-primary/30",
|
|
disabled ? "cursor-not-allowed opacity-70" : "cursor-pointer"
|
|
)}
|
|
>
|
|
<RadioGroupItem
|
|
id={`selectable-card-${opt.value}`}
|
|
value={opt.value}
|
|
className="mt-1"
|
|
/>
|
|
<div className="flex min-w-0 flex-col gap-1">
|
|
<div className="flex items-center gap-2 font-medium">
|
|
{Icon && <Icon className="size-4 text-primary" />}
|
|
{opt.label}
|
|
</div>
|
|
{opt.description && (
|
|
<p className="text-sm text-muted-foreground">{opt.description}</p>
|
|
)}
|
|
</div>
|
|
</label>
|
|
)
|
|
})}
|
|
</RadioGroup>
|
|
)
|
|
}
|
|
|
|
export { SelectableCardGroup }
|
|
export type { SelectableCardOption }
|