118 lines
3.3 KiB
TypeScript
118 lines
3.3 KiB
TypeScript
|
import { ChevronDown, LucideProps } from "lucide-react";
|
||
|
import {
|
||
|
DetailedHTMLProps,
|
||
|
ForwardRefExoticComponent,
|
||
|
InputHTMLAttributes,
|
||
|
LabelHTMLAttributes,
|
||
|
RefAttributes,
|
||
|
RefObject,
|
||
|
SelectHTMLAttributes,
|
||
|
} from "react";
|
||
|
import { twMerge } from "tailwind-merge";
|
||
|
|
||
|
type SelectOptionObject = {
|
||
|
title: string;
|
||
|
value: string;
|
||
|
default?: boolean;
|
||
|
};
|
||
|
|
||
|
type SelectOption = SelectOptionObject | SelectOptionObject[];
|
||
|
|
||
|
/**
|
||
|
* # Select Element
|
||
|
* @className twui-select-wrapper
|
||
|
* @className twui-select
|
||
|
* @className twui-select-dropdown-icon
|
||
|
*/
|
||
|
export default function Select({
|
||
|
label,
|
||
|
options,
|
||
|
componentRef,
|
||
|
labelProps,
|
||
|
wrapperProps,
|
||
|
showLabel,
|
||
|
iconProps,
|
||
|
...props
|
||
|
}: DetailedHTMLProps<
|
||
|
SelectHTMLAttributes<HTMLSelectElement>,
|
||
|
HTMLSelectElement
|
||
|
> & {
|
||
|
options: SelectOptionObject[];
|
||
|
label?: string;
|
||
|
showLabel?: boolean;
|
||
|
wrapperProps?: DetailedHTMLProps<
|
||
|
InputHTMLAttributes<HTMLDivElement>,
|
||
|
HTMLDivElement
|
||
|
>;
|
||
|
labelProps?: DetailedHTMLProps<
|
||
|
LabelHTMLAttributes<HTMLLabelElement>,
|
||
|
HTMLLabelElement
|
||
|
>;
|
||
|
componentRef?: RefObject<HTMLSelectElement>;
|
||
|
iconProps?: LucideProps;
|
||
|
}) {
|
||
|
return (
|
||
|
<div
|
||
|
{...wrapperProps}
|
||
|
className={twMerge(
|
||
|
"relative w-full flex items-center",
|
||
|
wrapperProps?.className
|
||
|
)}
|
||
|
>
|
||
|
{showLabel && (
|
||
|
<label
|
||
|
htmlFor={props.name}
|
||
|
{...labelProps}
|
||
|
className={twMerge(
|
||
|
"text-xs absolute -top-2 left-4 text-slate-600 bg-white px-2",
|
||
|
"dark:text-white/60 dark:bg-black",
|
||
|
labelProps?.className
|
||
|
)}
|
||
|
>
|
||
|
{label || props.name}
|
||
|
</label>
|
||
|
)}
|
||
|
|
||
|
<select
|
||
|
{...props}
|
||
|
className={twMerge(
|
||
|
"w-full pl-3 py-2 border rounded-md appearance-none pr-8",
|
||
|
"border-slate-300 dark:border-white/20",
|
||
|
"focus:border-slate-700 dark:focus:border-white/50",
|
||
|
"outline-slate-300 dark:outline-white/20",
|
||
|
"focus:outline-slate-700 dark:focus:outline-white/50",
|
||
|
"bg-white dark:bg-black",
|
||
|
"twui-select",
|
||
|
props.className
|
||
|
)}
|
||
|
ref={componentRef}
|
||
|
defaultValue={
|
||
|
options.flat().find((opt) => opt.default)?.value ||
|
||
|
undefined
|
||
|
}
|
||
|
>
|
||
|
{options.flat().map((option, index) => {
|
||
|
return (
|
||
|
<option
|
||
|
key={index}
|
||
|
value={option.value}
|
||
|
// selected={option.default || undefined}
|
||
|
>
|
||
|
{option.title}
|
||
|
</option>
|
||
|
);
|
||
|
})}
|
||
|
</select>
|
||
|
|
||
|
<ChevronDown
|
||
|
size={20}
|
||
|
{...iconProps}
|
||
|
className={twMerge(
|
||
|
"absolute right-2 pointer-events-none",
|
||
|
iconProps?.className
|
||
|
)}
|
||
|
/>
|
||
|
</div>
|
||
|
);
|
||
|
}
|