// Adapted from Kibo UI (MIT), with owned React/CSS APIs. See THIRD_PARTY_NOTICES.md. "use client"; import { useId, useRef, useState, type ComponentProps } from "react"; export type TagsProps = Omit< ComponentProps<"div">, "onChange" | "defaultValue" > & { label: string; value?: readonly string[]; defaultValue?: readonly string[]; onValueChange?: (tags: string[]) => void; suggestions?: readonly string[]; disabled?: boolean; name?: string; }; export function Tags({ label, value, defaultValue = [], onValueChange, suggestions = [], disabled, name, className = "", ...props }: TagsProps) { const id = useId(); const ref = useRef(null); const [internal, setInternal] = useState(defaultValue); const [input, setInput] = useState(""); const [notice, setNotice] = useState(""); const current = value ?? internal; const update = (next: string[]) => { if (value === undefined) setInternal(next); onValueChange?.(next); }; const add = () => { const tag = input.trim(); if (!tag) return; if (current.includes(tag)) { setNotice("Tag already selected."); return; } update([...current, tag]); setInput(""); setNotice(`Added ${tag}`); }; return (
{current.map((tag) => ( {tag} {name && ( )} ))}
setInput(event.target.value)} onKeyDown={(event) => { if (event.key === "Enter" && !event.nativeEvent.isComposing) { event.preventDefault(); add(); } }} />
{suggestions .filter((tag) => !current.includes(tag)) .map((tag) => ( {notice}
); }