75 lines
2.3 KiB
TypeScript
75 lines
2.3 KiB
TypeScript
import { useId } from "react";
|
|
|
|
import { cn } from "@/shared/ui/cn";
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/shared/ui/select";
|
|
|
|
export interface FieldSelectOption {
|
|
value: string;
|
|
label: string;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
export interface FieldSelectProps {
|
|
label: string;
|
|
value: string;
|
|
onValueChange: (value: string) => void;
|
|
options: FieldSelectOption[];
|
|
placeholder?: string;
|
|
disabled?: boolean;
|
|
className?: string;
|
|
contentClassName?: string;
|
|
id?: string;
|
|
}
|
|
|
|
export function FieldSelect({
|
|
label,
|
|
value,
|
|
onValueChange,
|
|
options,
|
|
placeholder = "请选择",
|
|
disabled,
|
|
className,
|
|
contentClassName,
|
|
id
|
|
}: FieldSelectProps) {
|
|
const generatedId = useId();
|
|
const triggerId = id ?? `field-select-${generatedId}`;
|
|
const selectableOptions = options.filter((option) => option.value !== "");
|
|
|
|
return (
|
|
<div className={cn("min-w-0", className)}>
|
|
<label htmlFor={triggerId} className="mb-1.5 block text-xs font-medium leading-4 text-slate-600">
|
|
{label}
|
|
</label>
|
|
<Select
|
|
value={value}
|
|
onValueChange={onValueChange}
|
|
disabled={disabled || selectableOptions.length === 0}
|
|
>
|
|
<SelectTrigger
|
|
id={triggerId}
|
|
className="h-auto min-h-10 w-full min-w-0 gap-2 rounded-lg border-[var(--surface-divider)] bg-[var(--surface-reading)] px-3 text-sm shadow-xs transition-[border-color,box-shadow,background-color] [@media(hover:hover)]:hover:border-[var(--surface-border)] motion-reduce:transition-none [&>span]:line-clamp-none [&>span]:whitespace-normal [&>span]:break-words [&>span]:text-left [&>svg]:shrink-0"
|
|
>
|
|
<SelectValue placeholder={placeholder} />
|
|
</SelectTrigger>
|
|
<SelectContent
|
|
position="popper"
|
|
sideOffset={4}
|
|
className={cn("z-[90] max-w-[calc(100vw-1.5rem)]", contentClassName)}
|
|
>
|
|
{selectableOptions.map((option) => (
|
|
<SelectItem
|
|
key={option.value}
|
|
value={option.value}
|
|
disabled={option.disabled}
|
|
className="whitespace-normal py-2 leading-5 [&>span:last-child]:whitespace-normal [&>span:last-child]:break-words"
|
|
>
|
|
{option.label}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
);
|
|
}
|