71 lines
2 KiB
TypeScript
71 lines
2 KiB
TypeScript
"use client"
|
|
|
|
import { type ReactNode } from 'react'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
export interface SegmentedOption<T extends string> {
|
|
id: T
|
|
label: string
|
|
icon?: ReactNode
|
|
disabled?: boolean
|
|
accentColor?: string
|
|
}
|
|
|
|
interface SegmentedControlProps<T extends string> {
|
|
options: SegmentedOption<T>[]
|
|
value: T
|
|
onChange?: (value: T) => void
|
|
disabled?: boolean
|
|
className?: string
|
|
}
|
|
|
|
export function SegmentedControl<T extends string>({
|
|
options,
|
|
value,
|
|
onChange,
|
|
disabled = false,
|
|
className,
|
|
}: SegmentedControlProps<T>) {
|
|
return (
|
|
<div className={cn('inline-flex rounded-xl border border-gray-800 bg-gray-900/60 p-1', className)}>
|
|
{options.map((option, index) => {
|
|
const isSelected = option.id === value
|
|
const isDisabled = disabled || option.disabled
|
|
const accent = option.accentColor ?? '#f9fafb'
|
|
|
|
return (
|
|
<button
|
|
key={option.id}
|
|
type="button"
|
|
aria-pressed={isSelected}
|
|
disabled={isDisabled}
|
|
onClick={() => {
|
|
if (!isDisabled && option.id !== value) onChange?.(option.id)
|
|
}}
|
|
className={cn(
|
|
'flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200',
|
|
isSelected && 'bg-gray-800 text-gray-100',
|
|
!isSelected && !isDisabled && 'text-gray-400 hover:text-gray-200 hover:bg-gray-800/50',
|
|
isDisabled && 'text-gray-600 cursor-not-allowed opacity-50',
|
|
index > 0 && 'ml-1'
|
|
)}
|
|
style={isSelected ? { boxShadow: `0 0 0 1px ${accent}`, color: accent } : undefined}
|
|
>
|
|
{option.icon && (
|
|
<span
|
|
aria-hidden="true"
|
|
className="flex items-center"
|
|
style={{
|
|
color: isSelected ? accent : isDisabled ? '#4b5563' : '#9ca3af',
|
|
}}
|
|
>
|
|
{option.icon}
|
|
</span>
|
|
)}
|
|
{option.label}
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
)
|
|
}
|