select.tsx

1'use client'
2
3import * as React from 'react'
4import * as SelectPrimitive from '@radix-ui/react-select'
5import { Check, ChevronDown } from 'lucide-react'
6import { cn } from '@/lib/utils'
7
8export interface SelectOption {
9	value: string | null
10	label: string
11}
12
13interface SelectProps {
14	value: string | null
15	onValueChange: (value: string | null) => void
16	options: SelectOption[]
17}
18
19export function Select({ value, onValueChange, options }: SelectProps) {
20	return (
21		<SelectPrimitive.Root
22			value={value || undefined}
23			onValueChange={onValueChange}
24		>
25			<SelectPrimitive.Trigger className="flex h-10 w-full items-center justify-between rounded-md border border-gray-700 bg-gray-800 px-3 py-2 text-sm placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50">
26				<SelectPrimitive.Value>
27					{options.find((opt) => opt.value === value)?.label}
28				</SelectPrimitive.Value>
29				<SelectPrimitive.Icon>
30					<ChevronDown className="h-4 w-4 opacity-50" />
31				</SelectPrimitive.Icon>
32			</SelectPrimitive.Trigger>
33			<SelectPrimitive.Portal>
34				<SelectPrimitive.Content className="relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-gray-700 bg-gray-800 text-gray-300 shadow-md animate-in fade-in-80">
35					<SelectPrimitive.Viewport className="p-1">
36						{options.map((option) => (
37							<SelectPrimitive.Item
38								key={option.value ?? 'all'}
39								value={option.value ?? 'all'}
40								className={cn(
41									'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm font-medium outline-none focus:bg-gray-700 data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
42								)}
43								disabled={option.value === null}
44							>
45								<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
46									<SelectPrimitive.ItemIndicator>
47										<Check className="h-4 w-4" />
48									</SelectPrimitive.ItemIndicator>
49								</span>
50								<SelectPrimitive.ItemText>
51									{option.label}
52								</SelectPrimitive.ItemText>
53							</SelectPrimitive.Item>
54						))}
55					</SelectPrimitive.Viewport>
56				</SelectPrimitive.Content>
57			</SelectPrimitive.Portal>
58		</SelectPrimitive.Root>
59	)
60}
61