File size: 3,982 Bytes
56b6519
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import {
  Field,
  Label,
  Listbox,
  ListboxButton,
  ListboxOption,
  ListboxOptions,
  ListboxSelectedOption,
} from '@headlessui/react';
import { CheckIcon, ChevronDownIcon } from '@heroicons/react/20/solid';
import { ExclamationCircleIcon } from '@heroicons/react/24/outline';
import clsx from 'clsx';
import React from 'react';

type ListItem = {
  id: number;
  value: string;
  label?: string;
};

type SelectDropdownProps = {
  items: ListItem[];
  selected: ListItem | null;
  title: string;
  onChange: (item: ListItem) => void;
  placeholder?: string;
  requiredAlert?: boolean;
  requiredField?: boolean;
  disabled?: boolean;
};

const SelectDropdown: React.FC<SelectDropdownProps> = ({
  items,
  title,
  selected,
  onChange,
  placeholder,
  requiredAlert = false,
  requiredField = false,
  disabled = false,
}) => {
  return (
    <Field aria-required={requiredField} disabled={disabled}>
      <Label className="block text-sm font-medium leading-6 mb-2 text-gray-300">
        {title + ' '}
        {requiredField ? <span className="text-red-500 text-lg">*</span> : ''}
      </Label>
      <div
        className={`relative ${requiredAlert && (!selected?.id || !selected.value) && 'rounded-lg ring-1 ring-red-500'}`}
      >
        <Listbox disabled={disabled} onChange={onChange} value={selected}>
          <ListboxButton
            className={clsx(
              'inline-flex items-center justify-between w-full text-left rounded-lg bg-white/5 py-1.5 pl-3 text-left text-sm/6 text-white',
              'focus:outline-none data-[focus]:outline-2 data-[focus]:-outline-offset-2 data-[focus]:outline-white/25',
              'min-h-[2.3rem]',
              disabled && 'opacity-50 cursor-not-allowed',
            )}
            tabIndex={disabled ? -1 : undefined}
          >
            <div>
              <ListboxSelectedOption
                options={items.map(item => (
                  <ListboxOption
                    className="group flex cursor-default items-center gap-2 rounded-lg px-3 select-none data-[focus]:bg-white/10"
                    key={item.value}
                    value={item}
                  >
                    <CheckIcon className="invisible size-4 fill-white group-data-[selected]:visible" />
                    <div className="text-sm/6 text-white">
                      {item.label ? item.label : item.value}
                    </div>
                  </ListboxOption>
                ))}
                placeholder={placeholder}
              />
            </div>
            <ChevronDownIcon
              aria-hidden="true"
              className="size-4 fill-white/60 mr-1"
            />
          </ListboxButton>
          <ListboxOptions
            anchor="bottom"
            className={clsx(
              'w-[var(--button-width)] rounded-xl border border-white/5 bg-stone-800 p-1 [--anchor-gap:var(--spacing-1)] focus:outline-none',
              'transition duration-100 ease-in data-[leave]:data-[closed]:opacity-0',
              'z-50',
            )}
            transition
          >
            {items.map(item => (
              <ListboxOption
                className="group flex cursor-default items-center gap-2 rounded-lg py-1.5 px-3 select-none data-[focus]:bg-white/10"
                key={item.value}
                value={item}
              >
                <CheckIcon className="invisible size-4 fill-white group-data-[selected]:visible" />
                <div className="text-sm/6 text-white">
                  {item.label ? item.label : item.value}
                </div>
              </ListboxOption>
            ))}
          </ListboxOptions>
        </Listbox>
        {requiredAlert && (!selected?.id || !selected.value) ? (
          <span className="absolute right-5 top-0 mt-2 ml-2 text-red-500">
            <ExclamationCircleIcon className="size-5" />
          </span>
        ) : null}
      </div>
    </Field>
  );
};

export default SelectDropdown;