import { useState, useRef, useEffect } from 'react'; import { X, Tag } from 'lucide-react'; interface TagInputProps { tags: string[]; onChange: (tags: string[]) => void; suggestions?: string[]; placeholder?: string; } export default function TagInput({ tags, onChange, suggestions = [], placeholder = 'Add tag...', }: TagInputProps) { const [input, setInput] = useState(''); const [showSuggestions, setShowSuggestions] = useState(false); const [selectedIndex, setSelectedIndex] = useState(0); const inputRef = useRef(null); const containerRef = useRef(null); const filtered = input.trim() ? suggestions.filter((s) => s.toLowerCase().includes(input.toLowerCase()) && !tags.includes(s)) : []; useEffect(() => { setSelectedIndex(0); }, [input]); useEffect(() => { function handleClickOutside(e: MouseEvent) { if (containerRef.current && !containerRef.current.contains(e.target as Node)) { setShowSuggestions(false); } } document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); function addTag(tag: string) { const normalized = tag .trim() .toLowerCase() .replace(/[^a-z0-9_-]/g, ''); if (normalized && !tags.includes(normalized) && tags.length < 10) { onChange([...tags, normalized]); } setInput(''); setShowSuggestions(false); inputRef.current?.focus(); } function removeTag(tag: string) { onChange(tags.filter((t) => t !== tag)); inputRef.current?.focus(); } function handleKeyDown(e: React.KeyboardEvent) { if (e.key === 'Enter' || e.key === ',') { e.preventDefault(); if (filtered.length > 0 && showSuggestions) { addTag(filtered[selectedIndex] || filtered[0]); } else if (input.trim()) { addTag(input); } } else if (e.key === 'Backspace' && !input && tags.length > 0) { removeTag(tags[tags.length - 1]); } else if (e.key === 'ArrowDown' && showSuggestions) { e.preventDefault(); setSelectedIndex((i) => Math.min(i + 1, filtered.length - 1)); } else if (e.key === 'ArrowUp' && showSuggestions) { e.preventDefault(); setSelectedIndex((i) => Math.max(i - 1, 0)); } else if (e.key === 'Escape') { setShowSuggestions(false); } } return (
inputRef.current?.focus()} > {tags.map((tag) => ( {tag} ))} { setInput(e.target.value); setShowSuggestions(true); }} onFocus={() => setShowSuggestions(true)} onKeyDown={handleKeyDown} placeholder={tags.length === 0 ? placeholder : ''} className="flex-1 min-w-[60px] bg-transparent border-none outline-none text-[11px] text-[var(--text-primary)] placeholder:text-[var(--text-tertiary)]" />
{showSuggestions && filtered.length > 0 && (
{filtered.slice(0, 8).map((suggestion, i) => ( ))}
)}
); }