import React, { useState, useRef } from 'react'; import { Wrench, ArrowRight } from 'lucide-react'; import { Listbox, ListboxButton, Label, ListboxOptions, ListboxOption, Transition, } from '@headlessui/react'; import type { TPlugin } from 'librechat-data-provider'; import { useMultiSearch } from './MultiSearch'; import { useOnClickOutside } from '~/hooks'; import { CheckMark } from '~/svgs'; import { cn } from '~/utils/'; export type TMultiSelectDropDownProps = { title?: string; value: Array<{ icon?: string; name?: string; isButton?: boolean }>; disabled?: boolean; setSelected: (option: string) => void; availableValues: TPlugin[]; showAbove?: boolean; showLabel?: boolean; containerClassName?: string; optionsClassName?: string; labelClassName?: string; isSelected: (value: string) => boolean; className?: string; searchPlaceholder?: string; optionValueKey?: string; }; function MultiSelectDropDown({ title = 'Plugins', value, disabled, setSelected, availableValues, showAbove = false, showLabel = true, containerClassName, optionsClassName = '', labelClassName = '', isSelected, className, searchPlaceholder, optionValueKey = 'value', }: TMultiSelectDropDownProps) { const [isOpen, setIsOpen] = useState(false); const menuRef = useRef(null); const excludeIds = ['select-plugin', 'plugins-label', 'selected-plugins']; useOnClickOutside(menuRef, () => setIsOpen(false), excludeIds); const handleSelect: (value: string) => void = (option) => { setSelected(option); setIsOpen(true); }; // input will appear near the top of the menu, allowing correct filtering of different model menu items. This will // reset once the component is unmounted (as per a normal search) const [filteredValues, searchRender] = useMultiSearch({ availableOptions: availableValues, placeholder: searchPlaceholder, getTextKeyOverride: (option) => (option.name || '').toUpperCase(), }); const hasSearchRender = Boolean(searchRender); const options = hasSearchRender ? filteredValues : availableValues; const transitionProps = { className: 'top-full mt-3' }; if (showAbove) { transitionProps.className = 'bottom-full mb-3'; } const openProps = { open: isOpen }; return (
{/* the function typing is correct but there's still an issue here */} {/* @ts-ignore */} {() => ( <> setIsOpen((prev) => !prev)} {...openProps} > {' '} {showLabel && ( )} {!showLabel && title.length > 0 && ( {title}: )}
{value.map((v, i) => (
{v.icon ? ( {`${v} ) : ( )}
))}
{searchRender} {options.map((option, i: number) => { if (!option) { return null; } const selected = isSelected(option[optionValueKey]); return ( {!option.isButton && (
{option.icon ? ( {`${option.name} ) : ( )}
)} {option.name} {option.isButton && ( )} {selected && !option.isButton && ( )}
); })}
)}
); } export default MultiSelectDropDown;