new-personal-site/components/lib/form/Select.tsx

118 lines
3.3 KiB
TypeScript
Raw Normal View History

2024-12-09 15:36:17 +00:00
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>
);
}