100 lines
4.7 KiB
JavaScript
100 lines
4.7 KiB
JavaScript
import {DiscordModules, React} from "modules";
|
|
|
|
const {useState, useCallback} = React;
|
|
|
|
|
|
const Checkmark = React.memo((props) => (
|
|
<svg width="16" height="16" viewBox="0 0 24 24" {...props}>
|
|
<path fillRule="evenodd" clipRule="evenodd" fill={props.color ?? "#fff"} d="M8.99991 16.17L4.82991 12L3.40991 13.41L8.99991 19L20.9999 7.00003L19.5899 5.59003L8.99991 16.17Z" />
|
|
</svg>
|
|
));
|
|
|
|
const Dropper = React.memo((props) => (
|
|
<svg width="14" height="14" viewBox="0 0 16 16" {...props}>
|
|
<g fill="none">
|
|
<path d="M-4-4h24v24H-4z"/>
|
|
<path fill={props.color ?? "#fff"} d="M14.994 1.006C13.858-.257 11.904-.3 10.72.89L8.637 2.975l-.696-.697-1.387 1.388 5.557 5.557 1.387-1.388-.697-.697 1.964-1.964c1.13-1.13 1.3-2.985.23-4.168zm-13.25 10.25c-.225.224-.408.48-.55.764L.02 14.37l1.39 1.39 2.35-1.174c.283-.14.54-.33.765-.55l4.808-4.808-2.776-2.776-4.813 4.803z" />
|
|
</g>
|
|
</svg>
|
|
));
|
|
|
|
const defaultColors = [1752220, 3066993, 3447003, 10181046, 15277667, 15844367, 15105570, 15158332, 9807270, 6323595, 1146986, 2067276, 2123412, 7419530, 11342935, 12745742, 11027200, 10038562, 9936031, 5533306];
|
|
|
|
const resolveColor = (color, hex = true) => {
|
|
switch (typeof color) {
|
|
case (hex && "number"): return `#${color.toString(16)}`;
|
|
case (!hex && "string"): return Number.parseInt(color.replace("#", ""), 16);
|
|
case (!hex && "number"): return color;
|
|
case (hex && "string"): return color;
|
|
|
|
default: return color;
|
|
}
|
|
};
|
|
|
|
const getRGB = (color) => {
|
|
let result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color);
|
|
if (result) return [parseInt(result[1]), parseInt(result[2]), parseInt(result[3])];
|
|
|
|
result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*\)/.exec(color);
|
|
if (result) return [parseFloat(result[1]) * 2.55, parseFloat(result[2]) * 2.55, parseFloat(result[3]) * 2.55];
|
|
|
|
result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color);
|
|
if (result) return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];
|
|
|
|
result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color);
|
|
if (result) return [parseInt(result[1] + result[1], 16), parseInt(result[2] + result[2], 16), parseInt(result[3] + result[3], 16)];
|
|
};
|
|
|
|
const luma = (color) => {
|
|
const rgb = (typeof(color) === "string") ? getRGB(color) : color;
|
|
return (0.2126 * rgb[0]) + (0.7152 * rgb[1]) + (0.0722 * rgb[2]); // SMPTE C, Rec. 709 weightings
|
|
};
|
|
|
|
const getContrastColor = (color) => {
|
|
return (luma(color) >= 165) ? "#000" : "#fff";
|
|
};
|
|
|
|
|
|
export default function Color({value: initialValue, onChange, colors = defaultColors, defaultValue}) {
|
|
const [value, setValue] = useState(initialValue);
|
|
const change = useCallback((e) => {
|
|
onChange?.(resolveColor(e.target.value));
|
|
setValue(e.target.value);
|
|
}, [onChange]);
|
|
|
|
const intValue = resolveColor(value, false);
|
|
return <div className="bd-color-picker-container">
|
|
<div className="bd-color-picker-controls">
|
|
<DiscordModules.Tooltip text="Default" position="bottom">
|
|
{props => (
|
|
<div {...props} className="bd-color-picker-default" style={{backgroundColor: resolveColor(defaultValue)}} onClick={() => change({target: {value: defaultValue}})}>
|
|
{intValue === resolveColor(defaultValue, false)
|
|
? <Checkmark width="25" height="25" />
|
|
: null
|
|
}
|
|
</div>
|
|
)}
|
|
</DiscordModules.Tooltip>
|
|
<DiscordModules.Tooltip text="Custom Color" position="bottom">
|
|
{props => (
|
|
<div className="bd-color-picker-custom">
|
|
<Dropper color={getContrastColor(resolveColor(value, true))} />
|
|
<input {...props} style={{backgroundColor: resolveColor(value)}} type="color" className="bd-color-picker" value={resolveColor(value)} onChange={change} />
|
|
</div>
|
|
)}
|
|
</DiscordModules.Tooltip>
|
|
</div>
|
|
<div className="bd-color-picker-swatch">
|
|
{
|
|
colors.map((int, index) => (
|
|
<div key={index} className="bd-color-picker-swatch-item" style={{backgroundColor: resolveColor(int)}} onClick={() => change({target: {value: int}})}>
|
|
{intValue === int
|
|
? <Checkmark color={getContrastColor(resolveColor(value, true))} />
|
|
: null
|
|
}
|
|
</div>
|
|
))
|
|
}
|
|
</div>
|
|
</div>;
|
|
} |