import { KeyboardEvent, 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 EntityIcon from "../../../layouts/icons/EntityIcon"; import clsx from "clsx"; import HintTooltip from "../../tooltips/HintTooltip"; import ColorBadge from "../../badges/ColorBadge"; import { useApp } from "../../../../hooks"; import Link from "next/link"; export interface RefInputSelectorDarkMode { focus: () => void; } type GroupDto = { name: string; items: ItemDto[] }; type ItemDto = { group?: string; name: string | ReactNode; value: string | number | undefined; color?: Colors; disabled?: boolean; link?: string; img?: ReactNode; }; interface Props { name?: string; title?: string; value?: string | number | undefined; disabled?: boolean; options: ItemDto[]; 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; autoFocus?: boolean; readOnly?: boolean; renderSelected?: (item: ItemDto) => ReactNode; } const InputSelectorDarkMode = ( { name, title, value, options, disabled, setValue, className, withSearch = true, withLabel = true, withColors = false, selectPlaceholder, onNew, required, onNewRoute, help, hint, icon, borderless, autoFocus, readOnly, renderSelected, }: 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); } function trySelect(event: KeyboardEvent) { if (event.code === "Enter") { if (filteredItems().length > 0) { setSelected(filteredItems()[0]); } } } const filteredItems = () => { if (!options) { return []; } return options.filter( (f) => f.name?.toString().toUpperCase().includes(searchInput.toUpperCase()) || f.value?.toString().toUpperCase().includes(searchInput.toUpperCase()) ); }; function itemsByGroup(): GroupDto[] { const groups: GroupDto[] = []; filteredItems().forEach((item) => { const group = groups.find((f) => f.name === item.group); if (group) { group.items.push(item); } else { groups.push({ name: item.group ?? "", items: [item] }); } }); return groups; } return ( {({ open }) => (
{withLabel && title && (
{title} {required && *}
{help && }
{hint}
)}
{icon && (
)} {withColors && selected && }
{selected ? ( {renderSelected ? renderSelected(selected) : selected.name} ) : ( {selectPlaceholder ?? t("shared.select")}... )}
inputSearch.current?.focus()} onBlur={() => setSearchInput("")} className={clsx( "absolute z-10 mt-1 max-h-72 w-full overflow-auto rounded-sm bg-slate-800 py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm" )} > {(withSearch || onNew || onNewRoute) && (
setSearchInput(e.target.value)} className="block w-full rounded-none rounded-l-md border border-slate-300 bg-white px-3 py-2 pl-10 text-sm focus:border-accent-300 focus:outline-none focus:ring-slate-300 sm:text-sm" />
{onNew && ( )} {onNewRoute && ( )}
)} {itemsByGroup().map((group, idx) => ( {group.name && ( clsx("relative cursor-default select-none py-1 pl-3 pr-9", active ? "bg-accent-500 text-black" : "text-slate-50") } value={group} > {({ selected, active }) => ( {group.name} )} )} {group.items.map((item, idx) => ( clsx( "relative cursor-default select-none py-2 pl-3 pr-9", !item.disabled && active && "bg-accent-500 text-black", !item.disabled && !active && "text-slate-50", item.disabled && "cursor-not-allowed bg-slate-900 text-slate-600" ) } value={item} > {({ selected, active }) => ( {item.link ? ( ) : ( )} )} ))} ))} {withSearch && filteredItems().length === 0 &&
{t("shared.noRecords")}
}
)}
); }; function Item({ item, selected, active, withColors = true }: { item: ItemDto; selected: boolean; active: boolean; withColors?: boolean }) { return (
{item.img && item.img}
{withColors && item.color !== undefined && }
{item.name}
{selected ? ( ) : null}
); } export default forwardRef(InputSelectorDarkMode);