"use client"; import { Command as CommandPrimitive } from "cmdk"; import { X } from "lucide-react"; import * as React from "react"; import { Badge } from "./badge"; import { Command, CommandGroup, CommandItem, CommandList } from "./command"; type Option = Record<"value" | "label", string | number>; export interface MultiSelectProps { options?: Option[]; onChange?: (values: Option[]) => void; placeholder?: string; } export const MultiSelect = ({ onChange, options = [], ...props }: MultiSelectProps) => { const inputRef = React.useRef(null); const [open, setOpen] = React.useState(false); const [selected, setSelected] = React.useState([]); const [inputValue, setInputValue] = React.useState(""); const handleUnselect = React.useCallback((option: Option) => { setSelected((prev) => prev.filter((s) => s.value !== option.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((option) => !selected.includes(option)); React.useEffect(() => { onChange?.(selected); }, [selected, onChange]); return (
{selected.map((option) => { return ( {option.label} ); })} {/* Avoid having the "Search" Icon */} setOpen(false)} onFocus={() => setOpen(true)} className="placeholder:text-muted-foreground ml-2 flex-1 bg-transparent outline-hidden" {...props} />
{open && selectables.length > 0 ? (
{selectables.map((option) => { return ( { e.preventDefault(); e.stopPropagation(); }} onSelect={(_value) => { setInputValue(""); setSelected((prev) => [...prev, option]); }} value={String(option.label)} className={"cursor-pointer"} > {option.label} ); })}
) : null}
); };