Files
tolaria/src/components/AccentColorPicker.tsx
2026-05-11 18:21:12 +02:00

77 lines
2.4 KiB
TypeScript

import type { MouseEvent } from 'react'
import { Check } from '@phosphor-icons/react'
import { Button } from './ui/button'
import { cn } from '@/lib/utils'
import { ACCENT_COLOR_PICKER_COLORS } from '../utils/typeColors'
type AccentColorIndicator = 'border' | 'check'
interface AccentColorPickerProps {
className?: string
disabled?: boolean
getOptionTestId?: (key: string) => string
indicator?: AccentColorIndicator
onSelectColor: (key: string) => void
selectedColor: string | null
size?: number
stopPropagation?: boolean
}
const DEFAULT_SWATCH_SIZE = 24
function selectedBorder(indicator: AccentColorIndicator, selected: boolean): string {
if (indicator === 'check') return '0 solid transparent'
return selected ? '2px solid var(--foreground)' : '2px solid transparent'
}
export function AccentColorPicker({
className,
disabled = false,
getOptionTestId,
indicator = 'border',
onSelectColor,
selectedColor,
size = DEFAULT_SWATCH_SIZE,
stopPropagation = false,
}: AccentColorPickerProps) {
const handleSelect = (event: MouseEvent<HTMLButtonElement>, key: string) => {
if (stopPropagation) event.stopPropagation()
if (!disabled) onSelectColor(key)
}
return (
<div className={cn('flex flex-wrap items-center gap-2', className)}>
{ACCENT_COLOR_PICKER_COLORS.map((color) => {
const selected = selectedColor === color.key
return (
<Button
key={color.key}
type="button"
variant="ghost"
size="icon-xs"
className={cn(
'shrink-0 rounded-full p-0 transition-transform',
disabled ? 'cursor-not-allowed opacity-50' : selected ? 'scale-110' : 'hover:scale-105',
)}
style={{
width: size,
height: size,
backgroundColor: color.css,
border: selectedBorder(indicator, selected),
}}
title={color.label}
aria-label={color.label}
data-testid={getOptionTestId?.(color.key)}
disabled={disabled}
onClick={(event) => handleSelect(event, color.key)}
>
{indicator === 'check' && selected && (
<Check size={Math.max(8, Math.round(size * 0.58))} weight="bold" style={{ color: 'var(--text-inverse)' }} />
)}
</Button>
)
})}
</div>
)
}