dia-gov's picture
Upload 569 files
cd6f98e verified
raw
history blame
3.8 kB
import { Listbox, Transition } from "@headlessui/react";
import clsx from "clsx";
import { Fragment } from "react";
import type { IconType } from "react-icons";
import { FaCheck } from "react-icons/fa";
import { HiOutlineChevronUpDown } from "react-icons/hi2";
interface Props<T> {
value?: T;
onChange?: (value: T | undefined) => void | Promise<void>;
items?: T[];
valueMapper?: (item?: T) => string | undefined;
icon: IconType;
disabled?: boolean;
defaultValue: T;
}
export default function Select<T>(props: Props<T>) {
return (
<Listbox
value={props.value || props.defaultValue}
onChange={props.onChange}
disabled={props.disabled}
>
{({ open }) => (
<>
<div className="relative">
<Listbox.Button
className={clsx(
"border-1 relative h-6 w-full cursor-default rounded-md border border-black bg-white px-1 text-left text-xs text-black focus:outline-none focus:ring-1 focus:ring-indigo-500",
props.disabled && "cursor-not-allowed bg-neutral-300 text-neutral-700"
)}
>
<span className="flex flex-row items-center">
{props.icon({
className: "text-white bg-black rounded-sm ring-2 ring-black",
size: "1em",
})}
<span className="ml-2 block min-w-[60px] flex-grow truncate capitalize">
{props.valueMapper?.(props.value || props.defaultValue)}
</span>
<HiOutlineChevronUpDown className="h-5 w-5 pl-1 text-black" aria-hidden="true" />
</span>
</Listbox.Button>
<Transition
show={open}
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute z-10 mt-1 max-h-60 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">
{(!props.items || props.items?.length == 0) && (
<div className="px-1 text-xs">No options available</div>
)}
{props.items?.map((item, i) => (
<Listbox.Option
key={i}
className={({ active }) =>
clsx(
active ? "bg-indigo-600 text-white" : "text-gray-900",
"relative cursor-default select-none py-2 pl-3 pr-9"
)
}
value={item}
>
{({ selected, active }) => (
<>
<div className="flex">
<span
className={clsx(selected && "font-semibold", "truncate capitalize")}
>
{props.valueMapper?.(item)}
</span>
</div>
{selected ? (
<span
className={clsx(
active ? "text-white" : "text-indigo-600",
"absolute inset-y-0 right-0 flex items-center pr-4"
)}
>
<FaCheck className="h-4 w-4" aria-hidden="true" />
</span>
) : null}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</>
)}
</Listbox>
);
}