File size: 3,798 Bytes
cd6f98e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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>
  );
}