UNPKG

phx-react

Version:

PHX REACT

208 lines • 7.52 kB
import { useEffect, useMemo, useRef, useState } from 'react'; import * as React from 'react'; import TextInput from './TextInput'; const basicColors = [ '#d0021b', '#f5a623', '#f8e71c', '#8b572a', '#7ed321', '#417505', '#bd10e0', '#9013fe', '#4a90e2', '#50e3c2', '#b8e986', '#000000', '#4a4a4a', '#9b9b9b', '#ffffff', ]; const WIDTH = 214; const HEIGHT = 150; export default function ColorPicker({ color, onChange }) { const [selfColor, setSelfColor] = useState(transformColor('hex', color)); const [inputColor, setInputColor] = useState(color); const innerDivRef = useRef(null); const saturationPosition = useMemo(() => ({ x: (selfColor.hsv.s / 100) * WIDTH, y: ((100 - selfColor.hsv.v) / 100) * HEIGHT, }), [selfColor.hsv.s, selfColor.hsv.v]); const huePosition = useMemo(() => ({ x: (selfColor.hsv.h / 360) * WIDTH, }), [selfColor.hsv]); const onSetHex = (hex) => { setInputColor(hex); if (/^#[0-9A-Fa-f]{6}$/i.test(hex)) { const newColor = transformColor('hex', hex); setSelfColor(newColor); } }; // eslint-disable-next-line no-use-before-define const onMoveSaturation = ({ x, y }) => { const newHsv = { ...selfColor.hsv, s: (x / WIDTH) * 100, v: 100 - (y / HEIGHT) * 100, }; const newColor = transformColor('hsv', newHsv); setSelfColor(newColor); setInputColor(newColor.hex); }; // eslint-disable-next-line no-use-before-define const onMoveHue = ({ x }) => { const newHsv = { ...selfColor.hsv, h: (x / WIDTH) * 360 }; const newColor = transformColor('hsv', newHsv); setSelfColor(newColor); setInputColor(newColor.hex); }; useEffect(() => { // Check if the dropdown is actually active if (innerDivRef.current !== null && onChange) { onChange(selfColor.hex); setInputColor(selfColor.hex); } }, [selfColor, onChange]); useEffect(() => { if (color === undefined) return; const newColor = transformColor('hex', color); setSelfColor(newColor); setInputColor(newColor.hex); }, [color]); return (React.createElement("div", { ref: innerDivRef, className: 'color-picker-wrapper', style: { width: WIDTH } }, React.createElement(TextInput, { label: 'Hex', onChange: onSetHex, value: inputColor }), React.createElement("div", { className: 'color-picker-basic-color' }, basicColors.map((basicColor) => (React.createElement("button", { key: basicColor, className: basicColor === selfColor.hex ? ' active' : '', onClick: () => { setInputColor(basicColor); setSelfColor(transformColor('hex', basicColor)); }, style: { backgroundColor: basicColor }, type: 'button' })))), React.createElement(MoveWrapper, { className: 'color-picker-saturation', onChange: onMoveSaturation, style: { backgroundColor: `hsl(${selfColor.hsv.h}, 100%, 50%)` } }, React.createElement("div", { className: 'color-picker-saturation_cursor', style: { backgroundColor: selfColor.hex, left: saturationPosition.x, top: saturationPosition.y, } })), React.createElement(MoveWrapper, { className: 'color-picker-hue', onChange: onMoveHue }, React.createElement("div", { className: 'color-picker-hue_cursor', style: { backgroundColor: `hsl(${selfColor.hsv.h}, 100%, 50%)`, left: huePosition.x, } })), React.createElement("div", { className: 'color-picker-color', style: { backgroundColor: selfColor.hex } }))); } function MoveWrapper({ children, className, onChange, style }) { const divRef = useRef(null); const move = (e) => { if (divRef.current) { const { current: div } = divRef; const { height, left, top, width } = div.getBoundingClientRect(); const x = clamp(e.clientX - left, width, 0); const y = clamp(e.clientY - top, height, 0); onChange({ x, y }); } }; const onMouseDown = (e) => { if (e.button !== 0) return; move(e); const onMouseMove = (_e) => { move(_e); }; const onMouseUp = (_e) => { document.removeEventListener('mousemove', onMouseMove, false); document.removeEventListener('mouseup', onMouseUp, false); move(_e); }; document.addEventListener('mousemove', onMouseMove, false); document.addEventListener('mouseup', onMouseUp, false); }; return (React.createElement("div", { ref: divRef, className: className, onMouseDown: onMouseDown, style: style }, children)); } function clamp(value, max, min) { return value > max ? max : value < min ? min : value; } export function toHex(value) { if (!value.startsWith('#')) { const ctx = document.createElement('canvas').getContext('2d'); if (!ctx) { throw new Error('2d context not supported or canvas already initialized'); } ctx.fillStyle = value; return ctx.fillStyle; } else if (value.length === 4 || value.length === 5) { value = value .split('') .map((v, i) => (i ? v + v : '#')) .join(''); return value; } else if (value.length === 7 || value.length === 9) { return value; } return '#000000'; } function hex2rgb(hex) { const rbgArr = (hex // @ts-ignore .replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, (m, r, g, b) => '#' + r + r + g + g + b + b) .substring(1) .match(/.{2}/g) || []).map((x) => parseInt(x, 16)); return { b: rbgArr[2], g: rbgArr[1], r: rbgArr[0], }; } function rgb2hsv({ b, g, r }) { r /= 255; g /= 255; b /= 255; const max = Math.max(r, g, b); const d = max - Math.min(r, g, b); const h = d ? (max === r ? (g - b) / d + (g < b ? 6 : 0) : max === g ? 2 + (b - r) / d : 4 + (r - g) / d) * 60 : 0; const s = max ? (d / max) * 100 : 0; const v = max * 100; return { h, s, v }; } function hsv2rgb({ h, s, v }) { s /= 100; v /= 100; const i = ~~(h / 60); const f = h / 60 - i; const p = v * (1 - s); const q = v * (1 - s * f); const t = v * (1 - s * (1 - f)); const index = i % 6; const r = Math.round([v, q, p, p, t, v][index] * 255); const g = Math.round([t, v, v, q, p, p][index] * 255); const b = Math.round([p, p, t, v, v, q][index] * 255); return { b, g, r }; } function rgb2hex({ b, g, r }) { return '#' + [r, g, b].map((x) => x.toString(16).padStart(2, '0')).join(''); } function transformColor(format, color) { let hex = toHex('#121212'); let rgb = hex2rgb(hex); let hsv = rgb2hsv(rgb); if (format === 'hex') { const value = color; hex = toHex(value); rgb = hex2rgb(hex); hsv = rgb2hsv(rgb); } else if (format === 'rgb') { const value = color; rgb = value; hex = rgb2hex(rgb); hsv = rgb2hsv(rgb); } else if (format === 'hsv') { const value = color; hsv = value; rgb = hsv2rgb(hsv); hex = rgb2hex(rgb); } return { hex, hsv, rgb }; } //# sourceMappingURL=ColorPicker.js.map