File size: 2,821 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
import { Combobox } from "@headlessui/react";
import clsx from "clsx";
import type { ReactNode } from "react";
import { useState } from "react";
import { HiCheck, HiChevronDown } from "react-icons/hi2";

interface Props<T> {
  label: string;
  items: T[];
  value: T | undefined;
  valueMapper: (e: T) => string;
  onChange: (value: T) => void;
  icon?: ReactNode;
}

const Combo = <T,>({ items, ...props }: Props<T>) => {
  const [query, setQuery] = useState("");

  const filtered =
    query === ""
      ? items
      : items.filter((e) => props.valueMapper(e).toLowerCase().includes(query.toLowerCase()));

  return (
    <Combobox as="div" value={props.value} onChange={props.onChange}>
      <Combobox.Label className="flex items-center gap-2 text-sm font-bold leading-6 text-slate-12">
        {props.icon}
        {props.label}
      </Combobox.Label>
      <div className="relative mt-1">
        <Combobox.Input
          className="w-full rounded-md border-0 bg-slate-1 py-1.5 pl-3 pr-10 text-slate-12 shadow-depth-1 transition-colors sm:text-sm sm:leading-6"
          onChange={(event) => setQuery(event.target.value)}
          displayValue={(e) => props.valueMapper(e as T)}
        />
        <Combobox.Button className="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none">
          <HiChevronDown className="h-5 w-5 text-gray-400" aria-hidden="true" />
        </Combobox.Button>

        {filtered.length > 0 && (
          <Combobox.Options className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-slate-1 py-1 text-slate-12 shadow-lg ring-opacity-5 focus:outline-none sm:text-sm">
            {filtered.map((e, i) => (
              <Combobox.Option
                key={i}
                value={e}
                className={({ active }) =>
                  clsx(
                    "relative cursor-default select-none py-2 pl-3 pr-9 ",
                    active ? "hover:bg-slate-3" : "text-slate-12"
                  )
                }
              >
                {({ selected }) => (
                  <>
                    <span className={clsx("block truncate", selected && "font-semibold")}>
                      {props.valueMapper(e)}
                    </span>

                    {selected && (
                      <span
                        className={clsx(
                          "absolute inset-y-0 right-0 flex items-center pr-4 text-slate-12"
                        )}
                      >
                        <HiCheck className="h-5 w-5" aria-hidden="true" />
                      </span>
                    )}
                  </>
                )}
              </Combobox.Option>
            ))}
          </Combobox.Options>
        )}
      </div>
    </Combobox>
  );
};

export default Combo;