'use client'; import * as React from 'react'; import { X } from 'lucide-react'; import { Badge } from '@/components/ui/badge'; import { Command, CommandGroup, CommandItem, CommandList } from '@/components/ui/command'; import { Command as CommandPrimitive } from 'cmdk'; type MultiSelectItem = Record<'value' | 'label', string>; interface MultiSelectProps { items: MultiSelectItem[]; placeholder?: string; } // Credits: https://github.com/mxkaske/mxkaske.dev/blob/main/components/craft/fancy-multi-select.tsx export function MultiSelect({ items, placeholder }: 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((item: MultiSelectItem) => { setSelected((prev) => prev.filter((s) => s.value !== item.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 = items.filter( (item) => !selected.includes(item), ); console.log(selectables, selected, inputValue); return (
{selected.map((item) => { return ( {item.label} ); })} {/* Avoid having the "Search" Icon */} setOpen(false)} onFocus={() => setOpen(true)} placeholder={placeholder ?? 'Select an item...'} className="ml-2 flex-1 bg-transparent outline-none placeholder:text-muted-foreground" />
{open && selectables.length > 0 ? (
{selectables.map((item) => { return ( { e.preventDefault(); e.stopPropagation(); }} onSelect={(value) => { setInputValue(''); setSelected((prev) => [...prev, item]); }} className={'cursor-pointer'} > {item.label} ); })}
) : null}
); }