import { Select as KSelect } from "@kobalte/core/select"; import { splitProps, Show, createMemo } from "solid-js"; import { tv, type VariantProps } from "tailwind-variants"; import { ChevronDown, Check } from "lucide-solid"; const selectStyles = tv( { slots: { root: "flex flex-col gap-1.5 w-full", label: "text-sm font-medium text-muted", trigger: [ "flex h-10 w-full text-main items-center justify-between rounded-md border border-light bg-app px-3 py-2 text-sm", "ring-offset-white placeholder:text-muted focus:outline-none focus:ring-1 focus:ring-blue-500", "disabled:cursor-not-allowed disabled:opacity-50", ], content: [ "relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-light bg-app shadow-md text-main", "data-[expanded]:animate-in data-[closed]:animate-out", ], listbox: "p-1", item: [ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none", "focus:bg-foreground focus:text-main data-[disabled]:pointer-events-none data-[disabled]:opacity-50", ], itemIndicator: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", description: "mt-1 text-xs text-muted", }, variants: { size: { sm: { trigger: "h-8 text-xs", item: "py-1 text-xs" }, md: { trigger: "h-10 text-sm", item: "py-1.5 text-sm" }, lg: { trigger: "h-12 text-base", item: "py-2 text-base" }, }, }, defaultVariants: { size: "md", }, }, { twMerge: true, }, ); type SelectVariants = VariantProps; interface Option { label: string; value: string; disabled?: boolean; } // 重新定义接口,使 value 和 onChange 处理的是 string 类型 export interface SelectProps extends SelectVariants { options: Option[]; value?: string; defaultValue?: string; onChange?: (value: string) => void; label?: string; description?: string; placeholder?: string; disabled?: boolean; name?: string; class?: string; } export const Select = (props: SelectProps) => { const [local, variantProps, others] = splitProps( props, [ "options", "label", "description", "placeholder", "class", "value", "defaultValue", "onChange", ], ["size"], ); const styles = selectStyles(variantProps); const selectedOption = createMemo(() => { if (local.value === undefined) return undefined; return local.options.find((opt) => opt.value === local.value); }); const defaultOption = createMemo(() => { if (local.defaultValue === undefined) return undefined; return local.options.find((opt) => opt.value === local.defaultValue); }); const handleValueChange = (opt: Option | null) => { if (opt === null) { local.onChange?.(""); return; } local.onChange?.(opt.value); }; return ( multiple={false} options={local.options} optionValue="value" optionTextValue="label" optionDisabled="disabled" placeholder={local.placeholder} value={selectedOption()} defaultValue={defaultOption()} onChange={handleValueChange} class={styles.root({ class: local.class })} {...others} itemComponent={(itemProps) => ( {itemProps.item.textValue} )} > {local.label} > {(state) => ( {state.selectedOption()?.label} )} {local.description} ); };