Spaces:
Runtime error
Runtime error
| 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> | |
| ); | |
| } | |