Skip to content

Instantly share code, notes, and snippets.

@muhrusdi
Created July 28, 2022 08:41
Show Gist options
  • Save muhrusdi/093f7ab703e11871bfedb7da2fa93cca to your computer and use it in GitHub Desktop.
Save muhrusdi/093f7ab703e11871bfedb7da2fa93cca to your computer and use it in GitHub Desktop.
import { Listbox, Transition } from "@headlessui/react"
import { Button } from "@nextui-org/react"
import { Fragment, useState } from "react"
import { FiChevronDown } from "react-icons/fi"
import cn from "classnames"
import { Controller, get, useFormContext } from "react-hook-form"
import { PatternType, RoleMapType } from "@/types"
import { helperText } from "@/utils/constants"
type LocalSelectProps = {
children: React.ReactNode
defaultValue?: string
label?: string
name?: string
message?: string
required?: boolean
map?: RoleMapType
pattern?: PatternType
placeholder?: string
fullWidth?: boolean
}
type ItemProps = {
children: React.ReactNode
value: string | number
}
const LocalSelect: React.FC<LocalSelectProps> = ({
children,
label,
fullWidth,
message,
required,
pattern,
map,
placeholder,
...props
}) => {
const { control, formState } = useFormContext()
const error = get(formState.errors, props.name ?? "")
return (
<Controller
{...props}
name={props.name ?? ""}
control={control}
rules={{ required: message ?? required, pattern }}
render={({ field }) => {
return (
<Listbox value={field.value} onChange={field.onChange}>
{label ? (
<label className="mb-[0.375rem] pl-[0.25rem] text-[0.875rem] block">
{label}
</label>
) : null}
<div className="relative">
<Listbox.Button
className={cn({ ["w-full"]: fullWidth })}
auto
color="success"
bordered
css={{
borderColor: "$border",
justifyContent: "space-between",
height: 44,
color: "$text",
}}
iconRight={<FiChevronDown />}
as={Button}
>
{map
? map[field.value || ""]
? map[field.value || ""]
: field.value
: field.value || placeholder}
</Listbox.Button>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute mt-1 max-h-60 w-full z-[999999] 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">
{children}
</Listbox.Options>
</Transition>
</div>
{error?.type ? (
<div className="mt-[0.125rem] ml-[0.625rem] text-[#11181C] text-[0.625rem]">
{error?.message || helperText}
</div>
) : null}
</Listbox>
)
}}
/>
)
}
const Item: React.FC<ItemProps> = ({ children, ...props }) => {
return (
<Listbox.Option
className={({ active }) =>
`relative cursor-default select-none py-2 px-4 mb-0 ${
active ? "bg-amber-100 text-amber-900" : "text-gray-900"
}`
}
{...props}
>
{({ selected }) => (
<span
className={`block truncate ${
selected ? "font-medium" : "font-normal"
}`}
>
{children}
</span>
)}
</Listbox.Option>
)
}
export const Select = Object.assign(LocalSelect, { Item })
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment