Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
mastodon
GitHub Repository: mastodon/joinmastodon
Path: blob/main/components/SelectMenu.tsx
1006 views
1
import {
2
Listbox,
3
ListboxButton,
4
ListboxOption,
5
ListboxOptions,
6
} from "@headlessui/react"
7
import classNames from "classnames"
8
import DisclosureArrow from "../public/ui/disclosure-arrow.svg?inline"
9
10
export type SelectMenuProps = {
11
/** The label shown before the dropdown */
12
label: React.ReactNode
13
/** Callback that sends along the current value of the input */
14
onChange: (value: string) => void
15
/** Controlled current value */
16
value: string
17
/** Options (passed to `<option>`s) of the input */
18
options: {
19
label: React.ReactNode
20
value: string
21
}[]
22
}
23
/** Styled replacement for <select> inputs */
24
export const SelectMenu = ({
25
label,
26
onChange,
27
options,
28
value,
29
}: SelectMenuProps) => {
30
const selectedLabel = options.find((option) => option.value === value)?.label
31
32
return (
33
<Listbox value={value} onChange={onChange}>
34
<div className="b3 inline-flex w-full sm:w-auto">
35
<div className="w-full relative">
36
<ListboxButton className="flex w-full items-center gap-2 cursor-pointer rounded-md border border-gray-3 p-4 text-left focus:outline-none focus:ring-1 focus:ring-blurple-500 sm:w-auto">
37
<span className="block truncate text-gray-1">
38
<span className="font-medium">{label}: </span>
39
<span className="font-bold">{selectedLabel}</span>
40
</span>
41
42
<span className="pointer-events-none">
43
<DisclosureArrow
44
className="h-4 w-4 text-gray-2"
45
fill="currentColor"
46
/>
47
</span>
48
</ListboxButton>
49
50
<ListboxOptions className="absolute z-10 mt-1 max-h-56 w-full min-w-max overflow-auto rounded-md bg-white py-1 shadow-lg ring-1 ring-gray-3 focus:outline-none">
51
{options.map(({ label: optionLabel, value: optionValue }) => (
52
<ListboxOption
53
key={optionValue}
54
value={optionValue}
55
className={({ focus }) =>
56
classNames(
57
focus ? "bg-blurple-500 text-white" : "text-gray-1",
58
"relative cursor-pointer select-none py-3 px-4 font-medium text-gray-1"
59
)
60
}
61
>
62
{({ selected }) => (
63
<>
64
<span className={selected ? "font-bold" : ""}>
65
{optionLabel}
66
</span>
67
</>
68
)}
69
</ListboxOption>
70
))}
71
</ListboxOptions>
72
</div>
73
</div>
74
</Listbox>
75
)
76
}
77
export default SelectMenu
78
79