mirror of
https://github.com/jgthms/bulma.git
synced 2024-10-17 09:04:18 +00:00
Add Hex prompt
This commit is contained in:
parent
2cc6593269
commit
cb2de3a2a2
@ -49,15 +49,20 @@ function App() {
|
|||||||
getVar: (id) => {
|
getVar: (id) => {
|
||||||
return context.cssvars[id];
|
return context.cssvars[id];
|
||||||
},
|
},
|
||||||
updateVar: (id, newValue, unit) => {
|
updateVar: (id, newValue) => {
|
||||||
|
setContext((context) => {
|
||||||
|
const { start, unit } = context.cssvars[id];
|
||||||
const computedValue = `${newValue}${unit}`;
|
const computedValue = `${newValue}${unit}`;
|
||||||
|
|
||||||
|
if (start === newValue) {
|
||||||
|
document.documentElement.style.removeProperty(`--bulma-${id}`);
|
||||||
|
} else {
|
||||||
document.documentElement.style.setProperty(
|
document.documentElement.style.setProperty(
|
||||||
`--bulma-${id}`,
|
`--bulma-${id}`,
|
||||||
computedValue,
|
computedValue,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
setContext((context) => {
|
|
||||||
return {
|
return {
|
||||||
...context,
|
...context,
|
||||||
cssvars: {
|
cssvars: {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useContext } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
import Slider from "./Slider";
|
import Slider from "./Slider";
|
||||||
@ -6,12 +6,103 @@ import Slider from "./Slider";
|
|||||||
import cn from "./Color.module.css";
|
import cn from "./Color.module.css";
|
||||||
import { CustomizerContext } from "../App";
|
import { CustomizerContext } from "../App";
|
||||||
|
|
||||||
function Color({ color }) {
|
function hslToHex(h, s, l) {
|
||||||
// const [hue, setHue] = useState(h.start);
|
s /= 100;
|
||||||
// const [saturation, setSaturation] = useState(s.start);
|
l /= 100;
|
||||||
// const [lightness, setLightness] = useState(l.start);
|
|
||||||
|
|
||||||
|
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 { cssvars, updateVar } = useContext(CustomizerContext);
|
||||||
|
const [hexValue, setHexValue] = useState("");
|
||||||
|
|
||||||
const hName = `${color}-h`;
|
const hName = `${color}-h`;
|
||||||
const sName = `${color}-s`;
|
const sName = `${color}-s`;
|
||||||
const lName = `${color}-l`;
|
const lName = `${color}-l`;
|
||||||
@ -28,14 +119,74 @@ function Color({ color }) {
|
|||||||
|
|
||||||
const handleReset = (event) => {
|
const handleReset = (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
updateVar(h.id, h.start, h.unit);
|
updateVar(h.id, h.start);
|
||||||
updateVar(s.id, s.start, s.unit);
|
updateVar(s.id, s.start);
|
||||||
updateVar(l.id, l.start, l.unit);
|
updateVar(l.id, l.start);
|
||||||
// document.documentElement.style.removeProperty(`--bulma-${hName}`);
|
|
||||||
// document.documentElement.style.removeProperty(`--bulma-${sName}`);
|
|
||||||
// document.documentElement.style.removeProperty(`--bulma-${lName}`);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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) {
|
if (!h) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -51,52 +202,80 @@ function Color({ color }) {
|
|||||||
<p>{name}</p>
|
<p>{name}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button className="button is-small" onClick={handleHexInput}>
|
||||||
className="button is-small"
|
Enter a Hex code
|
||||||
onClick={handleReset}
|
|
||||||
disabled={isDisabled}
|
|
||||||
>
|
|
||||||
Reset
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<div className="is-hidden field has-addons">
|
||||||
|
<p className="control">
|
||||||
|
<span className="button is-static">#</span>
|
||||||
|
</p>
|
||||||
|
<p className="control">
|
||||||
|
<input
|
||||||
|
className="input"
|
||||||
|
type="text"
|
||||||
|
value={hexValue}
|
||||||
|
onChange={handleHexChange}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<p className="control">
|
||||||
|
<span className="button is-icon">Copy</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cn.lines}>
|
<div className={cn.lines}>
|
||||||
<div className={cn.line}>
|
<div className={cn.line}>
|
||||||
<p>Hue</p>
|
<p>Hue</p>
|
||||||
<Slider id={hName} kind="hue" color={color} />
|
<Slider id={hName} kind="hue" color={color} />
|
||||||
<p>
|
<p className={cn.form}>
|
||||||
<code>
|
<input
|
||||||
{h.current}
|
type="text"
|
||||||
{h.unit}
|
className="input"
|
||||||
</code>
|
value={h.current}
|
||||||
|
onChange={(e) => handleInputChange(e, h)}
|
||||||
|
size="3"
|
||||||
|
/>
|
||||||
|
<span>{h.unit}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cn.line}>
|
<div className={cn.line}>
|
||||||
<p>Saturation</p>
|
<p>Saturation</p>
|
||||||
<Slider id={sName} kind="saturation" color={color} />
|
<Slider id={sName} kind="saturation" color={color} />
|
||||||
<p>
|
<p className={cn.form}>
|
||||||
<code>
|
<input
|
||||||
{s.current}
|
type="text"
|
||||||
{s.unit}
|
className="input"
|
||||||
</code>
|
value={s.current}
|
||||||
|
onChange={(e) => handleInputChange(e, s)}
|
||||||
|
size="3"
|
||||||
|
/>
|
||||||
|
<span>{s.unit}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cn.line}>
|
<div className={cn.line}>
|
||||||
<p>Lightness</p>
|
<p>Lightness</p>
|
||||||
<Slider id={lName} kind="lightness" color={color} />
|
<Slider id={lName} kind="lightness" color={color} />
|
||||||
<p>
|
<p className={cn.form}>
|
||||||
<code>
|
<input
|
||||||
{l.current}
|
type="text"
|
||||||
{l.unit}
|
className="input"
|
||||||
</code>
|
value={l.current}
|
||||||
|
onChange={(e) => handleInputChange(e, l)}
|
||||||
|
size="3"
|
||||||
|
/>
|
||||||
|
<span>{l.unit}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cn.side}>
|
<div className={cn.side}>
|
||||||
<button className={`button is-${color}`}>{name}</button>
|
<button className={`button is-${color}`}>{name}</button>
|
||||||
|
<button className="button" onClick={handleReset} disabled={isDisabled}>
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -47,3 +47,24 @@
|
|||||||
color: var(--bulma-text-strong);
|
color: var(--bulma-text-strong);
|
||||||
width: 6rem;
|
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;
|
||||||
|
}
|
||||||
|
@ -27,14 +27,14 @@ const valueToX = (value, width, min, max) => {
|
|||||||
|
|
||||||
function Slider({ id, color, kind }) {
|
function Slider({ id, color, kind }) {
|
||||||
const { cssvars, updateVar } = useContext(CustomizerContext);
|
const { cssvars, updateVar } = useContext(CustomizerContext);
|
||||||
const { start, unit, current } = cssvars[id];
|
const { start, current } = cssvars[id];
|
||||||
const [min, max] = RANGES[kind];
|
const [min, max] = RANGES[kind];
|
||||||
|
|
||||||
const sliderRef = useRef(null);
|
const sliderRef = useRef(null);
|
||||||
const handleRef = useRef(null);
|
const handleRef = useRef(null);
|
||||||
|
|
||||||
const [isMoving, setMoving] = useState(false);
|
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) => {
|
const handleMouseDown = (event) => {
|
||||||
setMoving(true);
|
setMoving(true);
|
||||||
@ -69,11 +69,11 @@ function Slider({ id, color, kind }) {
|
|||||||
const slider = sliderRef.current;
|
const slider = sliderRef.current;
|
||||||
const sliderRect = slider.getBoundingClientRect();
|
const sliderRect = slider.getBoundingClientRect();
|
||||||
const final = xToValue(x, sliderRect.width, min, max);
|
const final = xToValue(x, sliderRect.width, min, max);
|
||||||
updateVar(id, final, unit);
|
updateVar(id, final);
|
||||||
}, [id, min, max, updateVar, unit, x]);
|
}, [id, min, max, updateVar, x]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const newX = valueToX(current, 240, min, max);
|
const newX = valueToX(current, 360, min, max);
|
||||||
setX(newX);
|
setX(newX);
|
||||||
}, [min, max, current]);
|
}, [min, max, current]);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.main {
|
.main {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 15rem;
|
width: 360px;
|
||||||
padding: 0.375rem 0;
|
padding: 0.375rem 0;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user