"use client"; import { cn } from "@/lib/utils"; import React, { useEffect, useId, useRef, useState } from "react"; import { type FieldErrors, type FieldValues, type Path, type UseFormRegisterReturn, } from "react-hook-form"; interface Option { value: string; text: string; selected: boolean; } type MultiSelectProps = { label: string; items: { value: string; text: string }[]; value?: string[]; errors?: FieldErrors; required?: boolean; placeholder?: string; className?: string; disabled?: boolean; active?: boolean; height?: "sm" | "default"; } & UseFormRegisterReturn>; function MultiSelect({ name, label, items, errors, placeholder = "Select an option", className, disabled, active, height, onChange, onBlur, ref, value, required, }: MultiSelectProps) { const id = useId(); const error = errors && errors[name]; const [options, setOptions] = useState([]); const [selected, setSelected] = useState([]); const [show, setShow] = useState(false); const dropdownRef = useRef(null); const trigger = useRef(null); useEffect(() => { const newOptions = items.map((item) => ({ ...item, selected: value?.includes(item.value) ?? false, })); setOptions(newOptions); const newSelectedIndices = items .map((item, index) => (value?.includes(item.value) ? index : -1)) .filter((index) => index !== -1); setSelected(newSelectedIndices); }, [items, value]); const open = () => { if (!disabled) { setShow(true); } }; const isOpen = () => { return show === true; }; const combinedRef = (el: HTMLDivElement) => { trigger.current = el; if (typeof ref === "function") { ref(el); } }; const handleSelect = (index: number) => { if (disabled) return; const newOptions = [...options]; let newSelected = [...selected]; if (!newOptions[index].selected) { newOptions[index].selected = true; newSelected.push(index); } else { newOptions[index].selected = false; newSelected = newSelected.filter((i) => i !== index); } setOptions(newOptions); setSelected(newSelected); const selectedValues = newSelected.map((i) => newOptions[i].value); onChange?.({ target: { name, value: selectedValues } }); }; const remove = (index: number, event: React.MouseEvent) => { if (disabled) return; event.stopPropagation(); const newOptions = [...options]; const newSelected = selected.filter((i) => i !== index); newOptions[index].selected = false; setOptions(newOptions); setSelected(newSelected); const selectedValues = newSelected.map((i) => newOptions[i].value); onChange?.({ target: { name, value: selectedValues } }); }; useEffect(() => { const clickHandler = ({ target }: MouseEvent) => { if (!dropdownRef.current) return; if ( !show || dropdownRef.current.contains(target as Node) || trigger.current?.contains(target as Node) ) return; setShow(false); }; document.addEventListener("click", clickHandler); return () => document.removeEventListener("click", clickHandler); }, [show]); return (
{selected.map((index) => ( {options[index]?.text} {!disabled && ( )} ))} {selected.length === 0 && ( {placeholder} )}
{options.map((option, index) => (
handleSelect(index)} >
{option.selected && ( )}
{option.text}
))}
{error && (

{error.message as string}

)}
); } export default MultiSelect;