Add react color picker, show tailwind colors all the time
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
import { useState } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { HexColorPicker } from 'react-colorful'
|
||||
import { hexToRgb, rgbToHex, rgbToHsl, hslToRgb, rgbToHsb, hsbToRgb, findClosestTailwindColor } from '../utils/colors'
|
||||
import { TailwindColors } from './TailwindColors'
|
||||
|
||||
type RGB = {
|
||||
r: number
|
||||
@@ -23,52 +26,134 @@ type ColorFormat = 'hex' | 'rgb' | 'hsl' | 'hsb' | 'tailwind'
|
||||
export function ColorPicker() {
|
||||
const [color, setColor] = useState('#6366F1') // Default to indigo-500
|
||||
const [format, setFormat] = useState<ColorFormat>('hex')
|
||||
const [displayValue, setDisplayValue] = useState(color)
|
||||
const [closestTailwind, setClosestTailwind] = useState<{ name: string; hex: string }>({ name: 'indigo-500', hex: '#6366F1' })
|
||||
|
||||
useEffect(() => {
|
||||
const rgb = hexToRgb(color)
|
||||
if (!rgb) return
|
||||
|
||||
// Always update closest Tailwind color
|
||||
const closest = findClosestTailwindColor(color)
|
||||
setClosestTailwind(closest)
|
||||
|
||||
switch (format) {
|
||||
case 'hex':
|
||||
setDisplayValue(color)
|
||||
break
|
||||
case 'rgb':
|
||||
setDisplayValue(`rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`)
|
||||
break
|
||||
case 'hsl': {
|
||||
const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b)
|
||||
setDisplayValue(`hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`)
|
||||
break
|
||||
}
|
||||
case 'hsb': {
|
||||
const hsb = rgbToHsb(rgb.r, rgb.g, rgb.b)
|
||||
setDisplayValue(`hsb(${hsb.h}, ${hsb.s}%, ${hsb.b}%)`)
|
||||
break
|
||||
}
|
||||
case 'tailwind':
|
||||
setDisplayValue(closest.name)
|
||||
break
|
||||
}
|
||||
}, [color, format])
|
||||
|
||||
const handleInputChange = (value: string) => {
|
||||
let newHex = color
|
||||
|
||||
if (format === 'hex' && /^#[0-9A-Fa-f]{6}$/.test(value)) {
|
||||
newHex = value
|
||||
} else if (format === 'rgb') {
|
||||
const match = value.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/)
|
||||
if (match) {
|
||||
const [_, r, g, b] = match.map(Number)
|
||||
if (r <= 255 && g <= 255 && b <= 255) {
|
||||
newHex = rgbToHex(r, g, b)
|
||||
}
|
||||
}
|
||||
} else if (format === 'hsl') {
|
||||
const match = value.match(/^hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)$/)
|
||||
if (match) {
|
||||
const [_, h, s, l] = match.map(Number)
|
||||
if (h <= 360 && s <= 100 && l <= 100) {
|
||||
const rgb = hslToRgb(h, s, l)
|
||||
newHex = rgbToHex(rgb.r, rgb.g, rgb.b)
|
||||
}
|
||||
}
|
||||
} else if (format === 'hsb') {
|
||||
const match = value.match(/^hsb\((\d+),\s*(\d+)%,\s*(\d+)%\)$/)
|
||||
if (match) {
|
||||
const [_, h, s, b] = match.map(Number)
|
||||
if (h <= 360 && s <= 100 && b <= 100) {
|
||||
const rgb = hsbToRgb(h, s, b)
|
||||
newHex = rgbToHex(rgb.r, rgb.g, rgb.b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setColor(newHex)
|
||||
setDisplayValue(value)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-2xl mx-auto p-6 space-y-6">
|
||||
<div className="flex flex-col items-center gap-6">
|
||||
<h1 className="text-3xl font-bold">Color Picker</h1>
|
||||
|
||||
{/* Color Preview */}
|
||||
<div
|
||||
className="w-32 h-32 rounded-lg shadow-lg border border-zinc-200"
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
|
||||
{/* Color Input */}
|
||||
<div className="w-full max-w-md">
|
||||
<div className="flex gap-2 mb-4">
|
||||
{(['hex', 'rgb', 'hsl', 'hsb', 'tailwind'] as const).map((f) => (
|
||||
<button
|
||||
key={f}
|
||||
onClick={() => setFormat(f)}
|
||||
className={`px-3 py-1.5 rounded-md text-sm font-medium transition-colors
|
||||
${format === f
|
||||
? 'bg-indigo-500 text-white'
|
||||
: 'bg-zinc-100 text-zinc-700 hover:bg-zinc-200'
|
||||
}`}
|
||||
>
|
||||
{f.toUpperCase()}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
value={color}
|
||||
onChange={(e) => setColor(e.target.value)}
|
||||
className="w-full px-4 py-2 rounded-md border border-zinc-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
||||
placeholder="Enter color value..."
|
||||
/>
|
||||
<input
|
||||
type="color"
|
||||
value={color}
|
||||
onChange={(e) => setColor(e.target.value)}
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2 w-8 h-8 p-0 border-0 rounded-md cursor-pointer"
|
||||
<div className="w-full max-w-7xl mx-auto p-6">
|
||||
<h1 className="text-3xl font-bold text-center mb-8">Color Picker</h1>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-[1fr_2fr] gap-8">
|
||||
{/* Left Column: Color Picker and Input */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-col items-center gap-4 p-6 bg-white rounded-xl shadow-sm">
|
||||
{/* Color Preview */}
|
||||
<div
|
||||
className="w-32 h-32 rounded-lg shadow-lg border border-zinc-200"
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
|
||||
<div className="text-sm text-zinc-600">
|
||||
Closest Tailwind: <span className="font-medium">{closestTailwind.name}</span>
|
||||
</div>
|
||||
|
||||
{/* Color Picker */}
|
||||
<div className="w-full max-w-[200px]">
|
||||
<HexColorPicker color={color} onChange={setColor} />
|
||||
</div>
|
||||
|
||||
{/* Format Buttons */}
|
||||
<div className="flex flex-wrap gap-2 justify-center w-full">
|
||||
{(['hex', 'rgb', 'hsl', 'hsb', 'tailwind'] as const).map((f) => (
|
||||
<button
|
||||
key={f}
|
||||
onClick={() => setFormat(f)}
|
||||
className={`px-3 py-1.5 rounded-md text-sm font-medium transition-colors
|
||||
${format === f
|
||||
? 'bg-indigo-500 text-white'
|
||||
: 'bg-zinc-100 text-zinc-700 hover:bg-zinc-200'
|
||||
}`}
|
||||
>
|
||||
{f.toUpperCase()}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Color Input */}
|
||||
<div className="w-full">
|
||||
<input
|
||||
type="text"
|
||||
value={displayValue}
|
||||
onChange={(e) => handleInputChange(e.target.value)}
|
||||
className="w-full px-4 py-2 rounded-md border border-zinc-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
||||
placeholder="Enter color value..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column: Tailwind Colors */}
|
||||
<div className="bg-white rounded-xl shadow-sm p-4">
|
||||
<TailwindColors onColorSelect={setColor} selectedColor={color} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user