import { Ref, forwardRef, useImperativeHandle, useRef, ReactNode } from "react"; import { Fragment, useEffect, useState } from "react"; import { Combobox, Transition } from "@headlessui/react"; import { Colors } from "../../../application/enums/shared/Colors"; import ColorBadge from "../badges/ColorBadge"; import HintTooltip from "../tooltips/HintTooltip"; import EntityIcon from "../../layouts/icons/EntityIcon"; import clsx from "clsx"; import { useApp } from "../../../hooks"; import Link from "next/link"; export interface RefInputCombobox { focus: () => void; } interface Props { name?: string; title?: string; value?: (string | number)[]; disabled?: boolean; options: { value: string | number | undefined; name?: string | ReactNode; color?: Colors; disabled?: boolean }[]; onChange?: (value: (string | number)[]) => void; className?: string; withSearch?: boolean; withLabel?: boolean; withColors?: boolean; selectPlaceholder?: string; onNew?: () => void; onNewRoute?: string; required?: boolean; help?: string; hint?: ReactNode; icon?: string; borderless?: boolean; darkMode?: boolean; autoFocus?: boolean; readOnly?: boolean; renderHiddenInputValue?: (item: string | number) => string; prefix?: string; } const InputCombobox = ( { name, title, value, options, disabled, onChange, className, withSearch = true, withLabel = true, withColors = false, selectPlaceholder, onNew, required, onNewRoute, help, hint, icon, borderless, darkMode, autoFocus, readOnly, renderHiddenInputValue, prefix, }: Props, ref: Ref ) => { const { t } = useApp(); const button = useRef(null); const inputSearch = useRef(null); const [selected, setSelected] = useState<(string | number)[]>(value || []); const [searchInput, setSearchInput] = useState(""); useEffect(() => { if (value && !isEqual(selected.sort(), value?.sort())) { setSelected(value); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [value]); useEffect(() => { if (onChange && !isEqual(selected.sort(), value?.sort())) { onChange(selected); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [selected]); function isEqual(a: any, b: any) { return JSON.stringify(a) === JSON.stringify(b); } useImperativeHandle(ref, () => ({ focus })); function focus() { setTimeout(() => { button.current?.focus(); button.current?.click(); }, 1); } const filteredItems = () => { if (!options) { return []; } return options.filter( (f) => f.name?.toString().toUpperCase().includes(searchInput.toUpperCase()) || f.value?.toString().toUpperCase().includes(searchInput.toUpperCase()) ); }; function getSelectedOptions() { return options.filter((f) => selected.includes(f.value?.toString() ?? "")); } return ( // @ts-ignore {({ open }) => (
{/* {renderHiddenInputValue && <> {selected?.map((item, idx) => { return ; })} } */} {withLabel && title && (
{title} {required && *}
{help && }
{hint}
)}
{icon && (
)} {/* {withColors && selected && } */}
{/* {JSON.stringify(selected)} */} {selected.length > 0 ? ( {prefix ?? ""} {getSelectedOptions() .map((f) => f.name ?? f.value) .join(", ")} ) : ( {selectPlaceholder ?? t("shared.select")}... )}
inputSearch.current?.focus()} onBlur={() => setSearchInput("")} className={clsx( "absolute z-10 mt-1 max-h-72 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm", darkMode && "dark:bg-gray-800" )} > {(withSearch || onNew || onNewRoute) && (
setSearchInput(e.target.value)} className="block w-full rounded-none rounded-l-md border border-gray-300 bg-white px-3 py-2 pl-10 text-sm focus:border-accent-300 focus:outline-none focus:ring-gray-300 sm:text-sm" />
{onNew && ( )} {onNewRoute && ( )}
)} {filteredItems().map((item, idx) => ( clsx( "relative cursor-default select-none py-2 pl-3 pr-9", !item.disabled && active && "bg-accent-600 text-white", !item.disabled && !active && "text-gray-900", item.disabled && "cursor-not-allowed bg-gray-100 text-gray-400", darkMode && !item.disabled && active && "dark:bg-accent-500 dark:text-black", darkMode && !item.disabled && !active && "dark:text-gray-50", darkMode && item.disabled && "cursor-not-allowed dark:bg-gray-900 dark:text-gray-600" ) } value={item.value} > {({ selected, active }) => ( <>
{withColors && item.color !== undefined && }
{item.name || item.value}
{selected ? ( ) : null} )}
))} {withSearch && filteredItems().length === 0 &&
{t("shared.noRecords")}
}
)}
); }; export default forwardRef(InputCombobox);