SelectMenu.tsx 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. import { Listbox } from "@headlessui/react"
  2. import classNames from "classnames"
  3. import DisclosureArrow from "../public/ui/disclosure-arrow.svg?inline"
  4. export type SelectMenuProps = {
  5. /** The label shown before the dropdown */
  6. label: React.ReactNode
  7. /** Callback that sends along the current value of the input */
  8. onChange: (value: string) => void
  9. /** Controlled current value */
  10. value: string
  11. /** Options (passed to `<option>`s) of the input */
  12. options: {
  13. label: React.ReactNode
  14. value: string
  15. }[]
  16. }
  17. /** Styled replacement for <select> inputs */
  18. export const SelectMenu = ({
  19. label,
  20. onChange,
  21. options,
  22. value,
  23. }: SelectMenuProps) => {
  24. const selectedLabel = options.find((option) => option.value === value)?.label
  25. return (
  26. <Listbox value={value} onChange={onChange}>
  27. <div className="b3 inline-flex w-full sm:w-auto">
  28. <div className="relative w-full sm:w-auto">
  29. <Listbox.Button className="relative w-full cursor-pointer rounded-md border border-gray-3 py-4 pl-4 pr-10 text-left focus:outline-none focus:ring-1 focus:ring-blurple-500 sm:w-auto">
  30. <span className="block truncate text-gray-1">
  31. <span className="font-medium">{label}: </span>
  32. <span className="font-bold">{selectedLabel}</span>
  33. </span>
  34. <span className="end-3 pointer-events-none absolute block-start-5">
  35. <DisclosureArrow
  36. className="h-4 w-4 text-gray-2"
  37. fill="currentColor"
  38. />
  39. </span>
  40. </Listbox.Button>
  41. <Listbox.Options 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">
  42. {options.map(({ label: optionLabel, value: optionValue }) => (
  43. <Listbox.Option
  44. key={optionValue}
  45. value={optionValue}
  46. className={({ active }) =>
  47. classNames(
  48. active ? "bg-blurple-500 text-white" : "text-gray-1",
  49. "relative cursor-pointer select-none py-3 px-4 font-medium text-gray-1"
  50. )
  51. }
  52. >
  53. {({ selected }) => (
  54. <>
  55. <span className={selected ? "font-bold" : ""}>
  56. {optionLabel}
  57. </span>
  58. </>
  59. )}
  60. </Listbox.Option>
  61. ))}
  62. </Listbox.Options>
  63. </div>
  64. </div>
  65. </Listbox>
  66. )
  67. }
  68. export default SelectMenu