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 SelectProps = 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; changeHandler?: (value: SelectProps["options"][number]["value"]) => void; }; /** * # 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, changeHandler, ...props }: SelectProps) { 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.5 left-2 text-slate-500 bg-white px-1.5 rounded-t", "dark:text-white/60 dark:bg-black", "twui-input-label", 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 } onChange={(e) => { changeHandler?.( e.target.value as (typeof options)[number]["value"] ); props.onChange?.(e); }} > {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> ); }