import { Ref, forwardRef, useImperativeHandle, useRef, ReactNode } from "react"; import { Fragment, useEffect, useState } from "react"; import { Listbox, 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 RefInputSelector { focus: () => void; } interface Props { name?: string; title?: string; value?: string | number | undefined; disabled?: boolean; options: { name: string | ReactNode; value: string | number | undefined; color?: Colors; disabled?: boolean }[]; setValue?: React.Dispatch>; 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; isLoading?: boolean; } const InputSelector = ( { name, title, value, options, disabled, setValue, className, withSearch = true, withLabel = true, withColors = false, selectPlaceholder, onNew, required, onNewRoute, help, hint, icon, borderless, darkMode, autoFocus, readOnly, isLoading, }: Props, ref: Ref ) => { const { t } = useApp(); const button = useRef(null); const inputSearch = useRef(null); const [selected, setSelected] = useState<{ name: string | ReactNode; value: string | number | undefined; color?: Colors }>(); const [searchInput, setSearchInput] = useState(""); useEffect(() => { const selected = options?.find((f) => f.value === value); setSelected(selected); }, [options, value]); useEffect(() => { if (selected && setValue && value !== selected?.value) { setValue(selected?.value); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [selected]); 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()) ); }; return ( {({ open }) => (
{withLabel && title && (
{title} {required && *}
{help && }
{hint}
)}
{icon && (
)} {withColors && selected && }
{selected ? ( {selected?.name} ) : ( {isLoading ? {t("shared.loading")}... : {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} > {({ selected, active }) => ( <>
{withColors && item.color !== undefined && }
{item.name}
{selected ? ( ) : null} )}
))} {withSearch && filteredItems().length === 0 &&
{t("shared.noRecords")}
}
)}
); }; export default forwardRef(InputSelector);