import { ChevronDown, Info, LucideProps } from "lucide-react"; import React, { ComponentProps, DetailedHTMLProps, Dispatch, InputHTMLAttributes, LabelHTMLAttributes, ReactNode, RefObject, SelectHTMLAttributes, SetStateAction, } from "react"; import { twMerge } from "tailwind-merge"; import Row from "../layout/Row"; import Dropdown from "../elements/Dropdown"; import Card from "../elements/Card"; import Span from "../layout/Span"; import Stack from "../layout/Stack"; import twuiSlugify from "../utils/slugify"; import twuiSlugToNormalText from "../utils/slug-to-normal-text"; export type TWUISelectValidityObject = { isValid?: boolean; msg?: string; }; export type TWUISelectOptionObject< KeyType extends string, T extends { [k: string]: any } = any > = { title?: string; value: KeyType; default?: boolean; data?: T; }; export type TWUISelectProps< KeyType extends string, T extends { [k: string]: any } = any > = DetailedHTMLProps< SelectHTMLAttributes, HTMLSelectElement > & { options: TWUISelectOptionObject[]; label?: string; showLabel?: boolean; wrapperProps?: DetailedHTMLProps< InputHTMLAttributes, HTMLDivElement >; wrapperWrapperProps?: ComponentProps; labelProps?: DetailedHTMLProps< LabelHTMLAttributes, HTMLLabelElement >; componentRef?: RefObject; iconProps?: LucideProps; changeHandler?: (value: KeyType, data?: T) => void; info?: string | ReactNode; validateValueFn?: (value: string) => Promise; dispatchState?: Dispatch>; name?: KeyType; }; export type TWUISelectValueObject< KeyType extends string, T extends { [k: string]: any } = { [k: string]: any } > = { value: KeyType; data?: T; }; /** * # Select Element * @className twui-select-wrapper * @className twui-select * @className twui-select-dropdown-icon */ export default function Select< KeyType extends string, T extends { [k: string]: any } = { [k: string]: any } >({ label, options, componentRef, labelProps, wrapperProps, showLabel, iconProps, changeHandler, info, validateValueFn, wrapperWrapperProps, dispatchState, ...props }: TWUISelectProps) { const [validity, setValidity] = React.useState({ isValid: true, }); const selectRef = componentRef || React.useRef(null); const [value, setValue] = React.useState>( { value: options[0]?.value, data: options[0]?.data, } ); React.useEffect(() => { setTimeout(() => { requestAnimationFrame(() => { const currentSelectValue = selectRef.current?.value; if (currentSelectValue && validateValueFn) { validateValueFn(currentSelectValue).then((res) => { setValidity(res); }); } }); }, 200); }, []); React.useEffect(() => { dispatchState?.(value.data); }, [value]); const selectID = label ? twuiSlugify(label) : props.name ? twuiSlugify(props.name) : props.title ? twuiSlugify(props.title) : `select-${Math.round(Math.random() * 1000000)}`; return (
{showLabel && ( )} {info && (
} hoverOpen > {typeof info == "string" ? ( {info} ) : ( info )} )} {!validity.isValid && validity.msg ? ( {validity.msg} ) : undefined}
); }