feat (v1.0.0): initial refactor and redesign

This commit is contained in:
Aidan 2025-10-09 04:12:05 -04:00
parent 3058aa1ab4
commit fe9b50b30e
134 changed files with 17792 additions and 3670 deletions

View file

@ -0,0 +1,71 @@
"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>
)
}