import clsx from "clsx"; import { Select as SelectPrimitive } from "radix-ui"; import * as React from "react"; import { useGetKey, useGetSet } from "../../hooks"; import { shallow, useDebugEvents } from "../../utils"; import { mergeDefaults } from "../../utils/mergeDefaults"; import { useComposedRefs } from "../../utils/mergeRefs"; import * as styles from "./styles.module.css"; interface SelectProps extends Omit, "onChange"> { placeholder?: string; options: any[]; type: any; "data-label": string; "data-id": string; getOptionLabel: (option: any, idx: number) => string; defaultValue?: any; onChange?: (value: any) => void; position?: "item-aligned" | "popper"; required?: boolean; size?: "xs" | "sm" | "md" | "lg"; variant?: "solid" | "outline" | "ghost" | "subtle" | "elvated"; colorPalette?: string; } export const Select = React.forwardRef(function Select( args: SelectProps, ref: React.ForwardedRef, ) { const { options: _options, type: _type, defaultValue, onChange, placeholder, getOptionLabel, position = "popper", required, variant = "outline", size = "md", ...rest } = mergeDefaults(args, { options: [], getOptionLabel: (option) => typeof option === "string" ? option : JSON.stringify(option), }); const optionsRef = React.useRef(_options); const options = React.useMemo(() => { if (!shallow(optionsRef.current, _options, true)) { optionsRef.current = _options; } return optionsRef.current; }, [_options]); const selectRef = React.useRef(null); const key = useGetKey(rest); const dfv = React.useMemo(() => { return options?.findIndex((o) => shallow(o, defaultValue, true)) ?? -1; }, [options, defaultValue]); const initalValue = React.useMemo(() => { const newValue = dfv >= 0 ? options[dfv] : placeholder ? null : options?.[0]; return { value: newValue }; }, [dfv, options, placeholder]); const [{ value }, setState] = useGetSet(key, initalValue); const localValue = React.useMemo(() => { const index = options?.findIndex((o) => shallow(o, value, true)) ?? -1; return index >= 0 ? String(index) : ""; }, [value, options]); const { className, onChange: handleChange, ...props } = useDebugEvents< Omit >( Object.assign(rest, { onChange: (idxStr: string) => { if ( idxStr === "" || idxStr === "__empty__" || idxStr === undefined || idxStr === null ) { setState( { value: undefined }, process.env.PREVIEW ? `onChange` : undefined, ); onChange?.(null as any); return; } const idx = +idxStr; const value = options[idx]; setState({ value }, process.env.PREVIEW ? `onChange` : undefined); onChange?.(value ?? null); }, }), ); React.useEffect(() => { const parentForm = selectRef.current?.closest("form"); const handler = () => { setState({ value: defaultValue as string }); }; parentForm?.addEventListener("reset", handler); return () => { parentForm?.removeEventListener("reset", handler); }; }, []); return (
e.stopPropagation()}> { if ( process.env.PREVIEW && document.body.classList.contains("__design") ) { e.preventDefault(); e.target?.dispatchEvent( new Event("mousedown", { bubbles: true, cancelable: true }), ); } }} > {options?.map((option, idx) => { const label = getOptionLabel(option, idx); return ( {label} ); })}
); });