BetterDiscordApp-rauenzi/renderer/src/ui/settings/components/color.jsx

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>;
}