"use client"; import { X } from "lucide-react"; import * as React from "react"; import { Badge } from "@/components/ui/badge"; import { Command, CommandGroup, CommandItem } from "@/components/ui/command"; import { Command as CommandPrimitive } from "cmdk"; type Option = { label: string; value: string; }; type Props = { options: Option[]; placeholder?: string; selected: Option[]; setSelected: React.Dispatch>; }; function deduplicate(options: Option[]): Option[] { const seen = new Set(); return options.filter((o) => { if (seen.has(o.value)) { return false; } seen.add(o.value); return true; }); } export const MultiSelect: React.FC = ({ options, placeholder, selected, setSelected }) => { const inputRef = React.useRef(null); const [open, setOpen] = React.useState(false); const [inputValue, setInputValue] = React.useState(""); const handleUnselect = (o: Option) => { setSelected((prev) => prev.filter((s) => s.value !== o.value)); }; const handleKeyDown = React.useCallback((e: React.KeyboardEvent) => { const input = inputRef.current; if (input) { if (e.key === "Delete" || e.key === "Backspace") { if (input.value === "") { setSelected((prev) => { const newSelected = [...prev]; newSelected.pop(); return newSelected; }); } } // This is not a default behaviour of the field if (e.key === "Escape") { input.blur(); } } }, []); const selectables = options.filter((o) => !selected.includes(o)); return (
{selected.map((o) => { return ( {o.label} ); })} {/* Avoid having the "Search" Icon */} setOpen(false)} onFocus={() => setOpen(true)} placeholder={placeholder} className="flex-1 w-full bg-transparent outline-none placeholder:text-content-subtle" />
{open && selectables.length > 0 ? (
{selectables .filter((o) => !selected.some((s) => s.value === o.value)) .map((o) => { return ( { e.preventDefault(); e.stopPropagation(); }} onSelect={(_value) => { setInputValue(""); setSelected((prev) => { return deduplicate([...prev, o]); }); }} className={"cursor-pointer"} > {o.label} ); })}
) : null}
); };