2026-10-15 13:58:01 +00:00
|
|
|
import { Label } from "@repo/shadcn-ui/components/label";
|
|
|
|
|
import {
|
|
|
|
|
Select,
|
|
|
|
|
SelectContent,
|
|
|
|
|
SelectItem,
|
|
|
|
|
SelectTrigger,
|
|
|
|
|
SelectValue,
|
|
|
|
|
} from "@repo/shadcn-ui/components/select";
|
2026-10-10 16:34:25 +00:00
|
|
|
import type { ReactNode } from "react";
|
2026-10-15 13:58:01 +00:00
|
|
|
import { useId } from "react";
|
2026-10-10 16:34:25 +00:00
|
|
|
|
2026-10-15 13:58:01 +00:00
|
|
|
/** Selección compacta con vista previa; el consumidor aporta iconos, textos y claves válidas. */
|
2026-10-10 16:34:25 +00:00
|
|
|
export function IconPicker({
|
|
|
|
|
label,
|
2026-10-15 13:58:01 +00:00
|
|
|
description,
|
|
|
|
|
describedBy,
|
2026-10-10 16:34:25 +00:00
|
|
|
options,
|
|
|
|
|
value,
|
|
|
|
|
onChange,
|
|
|
|
|
disabled = false,
|
|
|
|
|
}: {
|
|
|
|
|
label: string;
|
2026-10-15 13:58:01 +00:00
|
|
|
description?: string;
|
|
|
|
|
describedBy?: string;
|
2026-10-10 16:34:25 +00:00
|
|
|
options: readonly { value: string; label: string; icon: ReactNode }[];
|
|
|
|
|
value: string;
|
|
|
|
|
onChange: (value: string) => void;
|
|
|
|
|
disabled?: boolean;
|
|
|
|
|
}) {
|
2026-10-15 13:58:01 +00:00
|
|
|
const id = useId();
|
|
|
|
|
const selected = options.find((option) => option.value === value);
|
|
|
|
|
const descriptionIds =
|
|
|
|
|
[description ? `${id}-description` : undefined, describedBy].filter(Boolean).join(" ") ||
|
|
|
|
|
undefined;
|
|
|
|
|
|
2026-10-10 16:34:25 +00:00
|
|
|
return (
|
2026-10-15 13:58:01 +00:00
|
|
|
<div className="space-y-2">
|
|
|
|
|
<Label htmlFor={id}>{label}</Label>
|
|
|
|
|
<Select
|
|
|
|
|
disabled={disabled}
|
|
|
|
|
items={options.map((option) => ({ value: option.value, label: option.label }))}
|
|
|
|
|
onValueChange={(next) => {
|
|
|
|
|
if (typeof next === "string") {
|
|
|
|
|
onChange(next);
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
value={value}
|
|
|
|
|
>
|
|
|
|
|
<SelectTrigger aria-describedby={descriptionIds} className="w-full sm:max-w-64" id={id}>
|
|
|
|
|
<SelectValue>
|
|
|
|
|
<span aria-hidden="true" className="shrink-0">
|
|
|
|
|
{selected?.icon}
|
|
|
|
|
</span>
|
|
|
|
|
<span className="truncate">{selected?.label}</span>
|
|
|
|
|
</SelectValue>
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
{options.map((option) => (
|
|
|
|
|
<SelectItem key={option.value} value={option.value}>
|
|
|
|
|
<span aria-hidden="true" className="shrink-0">
|
|
|
|
|
{option.icon}
|
|
|
|
|
</span>
|
|
|
|
|
{option.label}
|
|
|
|
|
</SelectItem>
|
|
|
|
|
))}
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
{description && (
|
|
|
|
|
<p className="text-sm text-muted-foreground" id={`${id}-description`}>
|
|
|
|
|
{description}
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2026-10-10 16:34:25 +00:00
|
|
|
);
|
|
|
|
|
}
|