From cb2de3a2a2c60d12290c0af1450fc10eeca9f2fc Mon Sep 17 00:00:00 2001 From: Jeremy Thomas Date: Tue, 25 Jun 2024 16:20:07 +0100 Subject: [PATCH] Add Hex prompt --- docs/_react/bulma-customizer/src/App.jsx | 21 +- .../bulma-customizer/src/components/Color.jsx | 243 +++++++++++++++--- .../src/components/Color.module.css | 21 ++ .../src/components/Slider.jsx | 10 +- .../src/components/Slider.module.css | 2 +- 5 files changed, 251 insertions(+), 46 deletions(-) diff --git a/docs/_react/bulma-customizer/src/App.jsx b/docs/_react/bulma-customizer/src/App.jsx index 19853760..51444f5c 100644 --- a/docs/_react/bulma-customizer/src/App.jsx +++ b/docs/_react/bulma-customizer/src/App.jsx @@ -49,15 +49,20 @@ function App() { getVar: (id) => { return context.cssvars[id]; }, - updateVar: (id, newValue, unit) => { - const computedValue = `${newValue}${unit}`; - - document.documentElement.style.setProperty( - `--bulma-${id}`, - computedValue, - ); - + updateVar: (id, newValue) => { setContext((context) => { + const { start, unit } = context.cssvars[id]; + const computedValue = `${newValue}${unit}`; + + if (start === newValue) { + document.documentElement.style.removeProperty(`--bulma-${id}`); + } else { + document.documentElement.style.setProperty( + `--bulma-${id}`, + computedValue, + ); + } + return { ...context, cssvars: { diff --git a/docs/_react/bulma-customizer/src/components/Color.jsx b/docs/_react/bulma-customizer/src/components/Color.jsx index 62951bba..a2096361 100644 --- a/docs/_react/bulma-customizer/src/components/Color.jsx +++ b/docs/_react/bulma-customizer/src/components/Color.jsx @@ -1,4 +1,4 @@ -import { useContext } from "react"; +import { useContext, useEffect, useState } from "react"; import PropTypes from "prop-types"; import Slider from "./Slider"; @@ -6,12 +6,103 @@ import Slider from "./Slider"; import cn from "./Color.module.css"; import { CustomizerContext } from "../App"; -function Color({ color }) { - // const [hue, setHue] = useState(h.start); - // const [saturation, setSaturation] = useState(s.start); - // const [lightness, setLightness] = useState(l.start); +function hslToHex(h, s, l) { + s /= 100; + l /= 100; + let c = (1 - Math.abs(2 * l - 1)) * s; + let x = c * (1 - Math.abs(((h / 60) % 2) - 1)); + let m = l - c / 2; + let r = 0, + g = 0, + b = 0; + + if (0 <= h && h < 60) { + r = c; + g = x; + b = 0; + } else if (60 <= h && h < 120) { + r = x; + g = c; + b = 0; + } else if (120 <= h && h < 180) { + r = 0; + g = c; + b = x; + } else if (180 <= h && h < 240) { + r = 0; + g = x; + b = c; + } else if (240 <= h && h < 300) { + r = x; + g = 0; + b = c; + } else if (300 <= h && h < 360) { + r = c; + g = 0; + b = x; + } + + r = Math.round((r + m) * 255); + g = Math.round((g + m) * 255); + b = Math.round((b + m) * 255); + + return `${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase()}`; +} + +function hexToHsl(hex) { + // Remove the hash at the start if it's there + hex = hex.replace(/^#/, ""); + + // Parse the hex values + let r = parseInt(hex.slice(0, 2), 16); + let g = parseInt(hex.slice(2, 4), 16); + let b = parseInt(hex.slice(4, 6), 16); + + // Convert the RGB values to the range [0, 1] + r /= 255; + g /= 255; + b /= 255; + + // Find the maximum and minimum values to get the lightness + let max = Math.max(r, g, b); + let min = Math.min(r, g, b); + let h, + s, + l = (max + min) / 2; + + if (max === min) { + h = s = 0; // Achromatic + } else { + let d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + + switch (max) { + case r: + h = (g - b) / d + (g < b ? 6 : 0); + break; + case g: + h = (b - r) / d + 2; + break; + case b: + h = (r - g) / d + 4; + break; + } + + h *= 60; + } + + return { + hue: Math.round(h), + saturation: Math.round(s * 100), + lightness: Math.round(l * 100), + }; +} + +function Color({ color }) { const { cssvars, updateVar } = useContext(CustomizerContext); + const [hexValue, setHexValue] = useState(""); + const hName = `${color}-h`; const sName = `${color}-s`; const lName = `${color}-l`; @@ -28,14 +119,74 @@ function Color({ color }) { const handleReset = (event) => { event.preventDefault(); - updateVar(h.id, h.start, h.unit); - updateVar(s.id, s.start, s.unit); - updateVar(l.id, l.start, l.unit); - // document.documentElement.style.removeProperty(`--bulma-${hName}`); - // document.documentElement.style.removeProperty(`--bulma-${sName}`); - // document.documentElement.style.removeProperty(`--bulma-${lName}`); + updateVar(h.id, h.start); + updateVar(s.id, s.start); + updateVar(l.id, l.start); }; + const handleHexInput = (event) => { + event.preventDefault(); + + let value = window.prompt("Enter a Hexadecimal value (e.g. 00d1b2)"); + + if (value.startsWith("#")) { + value = value.replace(/^#/, ""); + } + + const hexPattern = /^([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6})$/; + + if (!hexPattern.test(value) || value.length > 6) { + window.prompt("That is not a valid Hexadecimal value. Please try again."); + return; + } + + if (value.length === 3) { + value = value[0] + value[0] + value[1] + value[1] + value[2] + value[2]; + } + + const { hue, saturation, lightness } = hexToHsl(value); + + updateVar(h.id, hue); + updateVar(s.id, saturation); + updateVar(l.id, lightness); + }; + + const handleHexChange = (event) => { + let value = event.target.value; + + if (value.startsWith("#")) { + value = value.replace(/^#/, ""); + } + + setHexValue(value); + + const hexPattern = /^([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6})$/; + + if (!hexPattern.test(value) || value.length < 6) { + return; + } + + const { hue, saturation, lightness } = hexToHsl(value); + + updateVar(h.id, hue); + updateVar(s.id, saturation); + updateVar(l.id, lightness); + }; + + const handleInputChange = (event, cssvar) => { + let value = event.target.value; + updateVar(cssvar.id, value); + }; + + useEffect(() => { + if (!h) { + return; + } + + const hex = hslToHex(h.current, s.current, l.current); + setHexValue(hex); + }, [h, s, l]); + if (!h) { return; } @@ -51,52 +202,80 @@ function Color({ color }) {

{name}

- + +
+

+ # +

+

+ +

+

+ Copy +

+

Hue

-

- - {h.current} - {h.unit} - +

+ handleInputChange(e, h)} + size="3" + /> + {h.unit}

Saturation

-

- - {s.current} - {s.unit} - +

+ handleInputChange(e, s)} + size="3" + /> + {s.unit}

Lightness

-

- - {l.current} - {l.unit} - +

+ handleInputChange(e, l)} + size="3" + /> + {l.unit}

+
); diff --git a/docs/_react/bulma-customizer/src/components/Color.module.css b/docs/_react/bulma-customizer/src/components/Color.module.css index 48088b73..bce0b8d8 100644 --- a/docs/_react/bulma-customizer/src/components/Color.module.css +++ b/docs/_react/bulma-customizer/src/components/Color.module.css @@ -47,3 +47,24 @@ color: var(--bulma-text-strong); width: 6rem; } + +.form { + display: flex; + align-items: center; + font-family: var(--bulma-family-code); + gap: 0.25em; +} + +.form input { + font-family: inherit; + font-size: inherit; + padding: 0.25em; + height: auto; + border-radius: 0.25em; + width: 3em; + padding: 0 0.25em; +} + +.form span { + opacity: 0.5; +} diff --git a/docs/_react/bulma-customizer/src/components/Slider.jsx b/docs/_react/bulma-customizer/src/components/Slider.jsx index 1adee34a..c46f62b5 100644 --- a/docs/_react/bulma-customizer/src/components/Slider.jsx +++ b/docs/_react/bulma-customizer/src/components/Slider.jsx @@ -27,14 +27,14 @@ const valueToX = (value, width, min, max) => { function Slider({ id, color, kind }) { const { cssvars, updateVar } = useContext(CustomizerContext); - const { start, unit, current } = cssvars[id]; + const { start, current } = cssvars[id]; const [min, max] = RANGES[kind]; const sliderRef = useRef(null); const handleRef = useRef(null); const [isMoving, setMoving] = useState(false); - const [x, setX] = useState(valueToX(start, 240, min, max)); + const [x, setX] = useState(valueToX(start, 360, min, max)); const handleMouseDown = (event) => { setMoving(true); @@ -69,11 +69,11 @@ function Slider({ id, color, kind }) { const slider = sliderRef.current; const sliderRect = slider.getBoundingClientRect(); const final = xToValue(x, sliderRect.width, min, max); - updateVar(id, final, unit); - }, [id, min, max, updateVar, unit, x]); + updateVar(id, final); + }, [id, min, max, updateVar, x]); useEffect(() => { - const newX = valueToX(current, 240, min, max); + const newX = valueToX(current, 360, min, max); setX(newX); }, [min, max, current]); diff --git a/docs/_react/bulma-customizer/src/components/Slider.module.css b/docs/_react/bulma-customizer/src/components/Slider.module.css index 9c46d0b5..4e7433d5 100644 --- a/docs/_react/bulma-customizer/src/components/Slider.module.css +++ b/docs/_react/bulma-customizer/src/components/Slider.module.css @@ -1,6 +1,6 @@ .main { position: relative; - width: 15rem; + width: 360px; padding: 0.375rem 0; cursor: grab; }