import React, { useEffect, useMemo, useState } from 'react'; import { ColorFormats } from 'tinycolor2'; import styles from './color-picker.scss'; import { rgbToHex, rgbToHsv, hsvToRgb, hsvToHslString, rgbaToString } from './color'; import { Mouse, useKey } from '@vev/utils'; type Props = { rgba: ColorFormats.RGBA; hue: number; dragging: boolean; onDrag: (dragging: boolean) => void; onChange: (value: string) => void; }; export function ColorSpace({ rgba, hue, dragging, onDrag, onChange }: Props) { // Convert to hsv const hsv = useMemo(() => { const value = rgbToHsv( { h: hue, }, rgba, ); value.h = hue; return value; }, [rgba, hue]); // Colordot position const [pos, setPos] = useState({ left: hsv.s * 100 + '%', bottom: hsv.v * 100 + '%' }); // Background color const [hsl, setHsl] = useState(hsvToHslString(hsv)); // Colordot color const hex = rgbToHex(rgba); // Force re-render const [, forceUpdate] = useState(0); // Container cl const cl = dragging ? `${styles.colorBox} ${styles.dragging}` : styles.colorBox; const boxRect = React.useRef>({ top: 0, left: 0, width: 0, height: 0 }); /** Change between controlled and uncontrolled component based on dragging */ useEffect(() => { if (!dragging) { setPos({ left: hsv.s * 100 + '%', bottom: hsv.v * 100 + '%' }); setHsl(hsvToHslString(hsv)); } }, [hsv]); const handleChange = (e: MouseEvent | React.MouseEvent) => { const { top = 0, left = 0, width = 1, height = 1 } = boxRect.current; const s = Math.max(0, Math.min(1, (e.pageX - left) / width)); const v = 1 - Math.max(0, Math.min(1, (e.pageY - top) / height)); const nextRgba = hsvToRgb({ h: hsv.h, s, v, a: rgba.a || 1 } as ColorFormats.HSVA); setPos({ left: s * 100 + '%', bottom: v * 100 + '%' }); onChange(rgbaToString(nextRgba)); }; const handleMouseDown = (e: React.MouseEvent) => { onDrag(true); Mouse.on('mousemove', handleChange); Mouse.on('mouseup', handleMouseUp); boxRect.current = e.currentTarget.getBoundingClientRect(); handleChange(e); }; const handleMouseUp = () => { onDrag(false); Mouse.off('mousemove', handleChange); Mouse.off('mouseup', handleMouseUp); // force re-render to remove cursor style forceUpdate(Date.now()); }; useKey('Escape', handleMouseUp); return (
); }