refactor: refactor color tool to match calculate blueprint
Rewrites all color components to use the glass panel design language, fixed-height two-panel layout, and tab-based navigation. - ColorManipulation: lg:grid-cols-5 split — left 2/5 shows ColorPicker + ColorInfo always; right 3/5 has Info/Adjust/Harmony/Gradient tabs; mobile 'Pick | Explore' switcher - ColorPicker: removes shadcn Input/Label, native input with dynamic contrast color matching the picked hue - ColorInfo: removes shadcn Button, native copy buttons on hover, metadata chips with bg-primary/5 background - ManipulationPanel: keeps Slider, replaces Button with glass action buttons, tighter spacing and muted labels - ExportMenu: keeps Select, replaces Buttons with glass action buttons, code preview in dark terminal box (#06060e) - ColorSwatch: rectangular full-width design for palette grids, hover reveals copy icon, hex label at bottom - PaletteGrid: denser grid (4→5 cols), smaller swatch height Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,54 +13,43 @@ interface ColorSwatchProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function ColorSwatch({
|
||||
color,
|
||||
size = 'md',
|
||||
showLabel = true,
|
||||
onClick,
|
||||
className,
|
||||
}: ColorSwatchProps) {
|
||||
export function ColorSwatch({ color, size = 'md', showLabel = true, onClick, className }: ColorSwatchProps) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const sizeClasses = {
|
||||
sm: 'h-12 w-12',
|
||||
md: 'h-16 w-16',
|
||||
lg: 'h-24 w-24',
|
||||
};
|
||||
|
||||
const handleCopy = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
const handleClick = () => {
|
||||
if (onClick) { onClick(); return; }
|
||||
navigator.clipboard.writeText(color);
|
||||
setCopied(true);
|
||||
toast.success(`Copied ${color}`);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
setTimeout(() => setCopied(false), 1500);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('flex flex-col items-center gap-2', className)}>
|
||||
<button
|
||||
className={cn(
|
||||
'relative rounded-lg ring-2 ring-border transition-all duration-200',
|
||||
'hover:scale-110 hover:ring-primary hover:shadow-lg',
|
||||
'focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||
'group active:scale-95',
|
||||
sizeClasses[size]
|
||||
)}
|
||||
style={{ backgroundColor: color }}
|
||||
onClick={onClick || handleCopy}
|
||||
aria-label={`Color ${color}`}
|
||||
>
|
||||
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-all duration-200 bg-black/30 rounded-lg backdrop-blur-sm">
|
||||
{copied ? (
|
||||
<Check className="h-5 w-5 text-white animate-scale-in" />
|
||||
) : (
|
||||
<Copy className="h-5 w-5 text-white" />
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
{showLabel && (
|
||||
<span className="text-xs font-mono text-muted-foreground">{color}</span>
|
||||
<button
|
||||
onClick={handleClick}
|
||||
title={color}
|
||||
aria-label={`Color ${color}`}
|
||||
className={cn(
|
||||
'group relative w-full rounded-lg overflow-hidden border border-white/8 transition-all',
|
||||
'hover:scale-[1.04] hover:border-white/20 hover:shadow-lg hover:shadow-black/20',
|
||||
size === 'sm' && 'h-10',
|
||||
size === 'md' && 'h-14',
|
||||
size === 'lg' && 'h-20',
|
||||
className
|
||||
)}
|
||||
</div>
|
||||
style={{ backgroundColor: color }}
|
||||
>
|
||||
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity bg-black/25">
|
||||
{copied
|
||||
? <Check className="w-3.5 h-3.5 text-white drop-shadow" />
|
||||
: <Copy className="w-3.5 h-3.5 text-white drop-shadow" />
|
||||
}
|
||||
</div>
|
||||
{showLabel && (
|
||||
<div className="absolute bottom-0 inset-x-0 px-1 py-0.5 text-[9px] font-mono text-white/70 bg-black/25 truncate text-center leading-tight">
|
||||
{color}
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user