"use client"; import { useState, useRef, useEffect } from "react"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Check, X, Plus, Pencil } from "lucide-react"; import { cn } from "@/lib/utils"; import { getOptionColor } from "@/components/ui/option-colors"; export interface OptionMultiSelectProps { values: string[]; options: string[]; onAdd: (value: string) => void; onRemove: (value: string) => void; onRename?: (oldValue: string, newValue: string) => void; placeholder?: string; disabled?: boolean; } export function OptionMultiSelect({ values, options, onAdd, onRemove, onRename, placeholder = "Cerca o crea...", disabled, }: OptionMultiSelectProps) { const [isOpen, setIsOpen] = useState(false); const [query, setQuery] = useState(""); const [renaming, setRenaming] = useState(null); const [renameValue, setRenameValue] = useState(""); const inputRef = useRef(null); const containerRef = useRef(null); useEffect(() => { function handleClickOutside(e: MouseEvent) { if (containerRef.current && !containerRef.current.contains(e.target as Node)) { setIsOpen(false); setRenaming(null); } } document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, []); useEffect(() => { if (isOpen && inputRef.current) inputRef.current.focus(); }, [isOpen]); const selected = new Set(values); // Union of the shared pool + anything already on this row, filtered by query. const pool = Array.from(new Set([...options, ...values])).sort((a, b) => a.localeCompare(b, "it") ); const q = query.trim().toLowerCase(); const filtered = q ? pool.filter((o) => o.toLowerCase().includes(q)) : pool; const exactMatch = pool.some((o) => o.toLowerCase() === q); function toggle(value: string) { if (selected.has(value)) onRemove(value); else onAdd(value); } function createFromQuery() { const trimmed = query.trim(); if (!trimmed) return; if (!selected.has(trimmed)) onAdd(trimmed); setQuery(""); } function commitRename(oldValue: string) { const next = renameValue.trim(); setRenaming(null); if (next && next !== oldValue) onRename?.(oldValue, next); } return (
!disabled && setIsOpen((v) => !v)} className={cn( "flex flex-wrap gap-1 items-center px-2 py-1 rounded transition-colors duration-150 min-h-[28px]", disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer hover:bg-muted" )} > {values.length === 0 ? ( ) : ( values.map((v) => ( {v} )) )}
{isOpen && (
setQuery(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); if (!exactMatch) createFromQuery(); else if (filtered.length === 1) toggle(filtered[0]); } if (e.key === "Escape") { e.preventDefault(); setIsOpen(false); } }} className="text-sm h-8 mb-1.5 ring-1 ring-primary" />
{filtered.map((opt) => renaming === opt ? ( setRenameValue(e.target.value)} onBlur={() => commitRename(opt)} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); commitRename(opt); } if (e.key === "Escape") { e.preventDefault(); setRenaming(null); } }} className="text-sm h-7" /> ) : (
{onRename && ( )}
) )} {q && !exactMatch && ( )} {filtered.length === 0 && !q && (

Nessuna opzione ancora

)}
)}
); }