2020-01-11 19:10:41 -08:00
|
|
|
import React from "react";
|
2020-01-07 07:50:59 +05:00
|
|
|
import { Popover } from "./Popover";
|
2020-01-06 21:29:44 +04:00
|
|
|
|
2020-01-11 19:10:41 -08:00
|
|
|
import "./ColorPicker.css";
|
|
|
|
|
|
|
|
// This is a narrow reimplementation of the awesome react-color Twitter component
|
|
|
|
// https://github.com/casesandberg/react-color/blob/master/src/components/twitter/Twitter.js
|
|
|
|
|
|
|
|
const Picker = function({
|
|
|
|
colors,
|
|
|
|
color,
|
|
|
|
onChange
|
|
|
|
}: {
|
|
|
|
colors: string[];
|
2020-01-21 00:16:22 +01:00
|
|
|
color: string | null;
|
2020-01-11 19:10:41 -08:00
|
|
|
onChange: (color: string) => void;
|
|
|
|
}) {
|
|
|
|
return (
|
|
|
|
<div className="color-picker">
|
|
|
|
<div className="color-picker-triangle-shadow"></div>
|
|
|
|
<div className="color-picker-triangle"></div>
|
|
|
|
<div className="color-picker-content">
|
2020-01-19 18:27:36 -03:00
|
|
|
<div className="colors-gallery">
|
|
|
|
{colors.map(color => (
|
|
|
|
<div
|
|
|
|
className="color-picker-swatch"
|
|
|
|
onClick={() => {
|
|
|
|
onChange(color);
|
|
|
|
}}
|
|
|
|
title={color}
|
|
|
|
tabIndex={0}
|
|
|
|
style={{ backgroundColor: color }}
|
|
|
|
key={color}
|
|
|
|
>
|
|
|
|
{color === "transparent" ? (
|
|
|
|
<div className="color-picker-transparent"></div>
|
|
|
|
) : (
|
|
|
|
undefined
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
))}
|
2020-01-11 19:10:41 -08:00
|
|
|
</div>
|
2020-01-19 18:27:36 -03:00
|
|
|
<ColorInput
|
|
|
|
color={color}
|
|
|
|
onChange={color => {
|
|
|
|
onChange(color);
|
|
|
|
}}
|
|
|
|
/>
|
2020-01-11 19:10:41 -08:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|
2020-01-07 23:37:22 +05:00
|
|
|
|
2020-01-19 18:27:36 -03:00
|
|
|
function ColorInput({
|
|
|
|
color,
|
|
|
|
onChange
|
|
|
|
}: {
|
2020-01-21 00:16:22 +01:00
|
|
|
color: string | null;
|
2020-01-19 18:27:36 -03:00
|
|
|
onChange: (color: string) => void;
|
|
|
|
}) {
|
|
|
|
const colorRegex = /^([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8}|transparent)$/;
|
|
|
|
const [innerValue, setInnerValue] = React.useState(color);
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
setInnerValue(color);
|
|
|
|
}, [color]);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="color-input-container">
|
|
|
|
<div className="color-picker-hash">#</div>
|
|
|
|
<input
|
|
|
|
spellCheck={false}
|
|
|
|
className="color-picker-input"
|
|
|
|
aria-label="Hex color code"
|
|
|
|
onChange={e => {
|
|
|
|
const value = e.target.value;
|
|
|
|
if (value.match(colorRegex)) {
|
|
|
|
onChange(value === "transparent" ? "transparent" : "#" + value);
|
|
|
|
}
|
|
|
|
setInnerValue(value);
|
|
|
|
}}
|
|
|
|
value={(innerValue || "").replace(/^#/, "")}
|
|
|
|
onPaste={e => onChange(e.clipboardData.getData("text"))}
|
|
|
|
onBlur={() => setInnerValue(color)}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-01-06 21:29:44 +04:00
|
|
|
export function ColorPicker({
|
2020-01-11 23:58:44 +01:00
|
|
|
type,
|
2020-01-06 21:29:44 +04:00
|
|
|
color,
|
|
|
|
onChange
|
|
|
|
}: {
|
2020-01-11 23:58:44 +01:00
|
|
|
type: "canvasBackground" | "elementBackground" | "elementStroke";
|
2020-01-21 00:16:22 +01:00
|
|
|
color: string | null;
|
2020-01-06 21:29:44 +04:00
|
|
|
onChange: (color: string) => void;
|
|
|
|
}) {
|
|
|
|
const [isActive, setActive] = React.useState(false);
|
2020-01-19 18:27:36 -03:00
|
|
|
|
2020-01-06 21:29:44 +04:00
|
|
|
return (
|
|
|
|
<div>
|
2020-01-19 18:27:36 -03:00
|
|
|
<div className="color-picker-control-container">
|
|
|
|
<button
|
|
|
|
className="color-picker-label-swatch"
|
|
|
|
aria-label="Change color"
|
|
|
|
style={color ? { backgroundColor: color } : undefined}
|
|
|
|
onClick={() => setActive(!isActive)}
|
|
|
|
/>
|
|
|
|
<ColorInput
|
|
|
|
color={color}
|
|
|
|
onChange={color => {
|
|
|
|
onChange(color);
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</div>
|
2020-01-07 23:37:22 +05:00
|
|
|
<React.Suspense fallback="">
|
|
|
|
{isActive ? (
|
|
|
|
<Popover onCloseRequest={() => setActive(false)}>
|
2020-01-11 19:10:41 -08:00
|
|
|
<Picker
|
2020-01-11 23:58:44 +01:00
|
|
|
colors={colors[type]}
|
2020-01-21 00:16:22 +01:00
|
|
|
color={color || null}
|
2020-01-07 23:37:22 +05:00
|
|
|
onChange={changedColor => {
|
2020-01-11 19:10:41 -08:00
|
|
|
onChange(changedColor);
|
2020-01-07 23:37:22 +05:00
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</Popover>
|
|
|
|
) : null}
|
|
|
|
</React.Suspense>
|
2020-01-06 21:29:44 +04:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2020-01-11 23:58:44 +01:00
|
|
|
|
2020-01-16 19:14:00 +02:00
|
|
|
// https://yeun.github.io/open-color/
|
2020-01-11 23:58:44 +01:00
|
|
|
const colors = {
|
2020-01-16 19:14:00 +02:00
|
|
|
// Shade 0
|
2020-01-11 23:58:44 +01:00
|
|
|
canvasBackground: [
|
2020-01-16 20:16:08 +02:00
|
|
|
"#ffffff",
|
|
|
|
"#f8f9fa",
|
|
|
|
"#f1f3f5",
|
|
|
|
"#fff5f5",
|
|
|
|
"#fff0f6",
|
|
|
|
"#f8f0fc",
|
|
|
|
"#f3f0ff",
|
|
|
|
"#edf2ff",
|
|
|
|
"#e7f5ff",
|
|
|
|
"#e3fafc",
|
|
|
|
"#e6fcf5",
|
|
|
|
"#ebfbee",
|
|
|
|
"#f4fce3",
|
|
|
|
"#fff9db",
|
|
|
|
"#fff4e6"
|
2020-01-11 23:58:44 +01:00
|
|
|
],
|
2020-01-16 19:14:00 +02:00
|
|
|
// Shade 6
|
2020-01-11 23:58:44 +01:00
|
|
|
elementBackground: [
|
2020-01-16 19:14:00 +02:00
|
|
|
"transparent",
|
2020-01-16 20:16:08 +02:00
|
|
|
"#ced4da",
|
|
|
|
"#868e96",
|
|
|
|
"#fa5252",
|
|
|
|
"#e64980",
|
|
|
|
"#be4bdb",
|
|
|
|
"#7950f2",
|
|
|
|
"#4c6ef5",
|
|
|
|
"#228be6",
|
|
|
|
"#15aabf",
|
|
|
|
"#12b886",
|
|
|
|
"#40c057",
|
|
|
|
"#82c91e",
|
|
|
|
"#fab005",
|
|
|
|
"#fd7e14"
|
2020-01-11 23:58:44 +01:00
|
|
|
],
|
2020-01-16 19:14:00 +02:00
|
|
|
// Shade 9
|
2020-01-11 23:58:44 +01:00
|
|
|
elementStroke: [
|
2020-01-16 19:14:00 +02:00
|
|
|
"#000000",
|
2020-01-16 20:16:08 +02:00
|
|
|
"#343a40",
|
2020-01-16 19:14:00 +02:00
|
|
|
"#495057",
|
2020-01-16 20:16:08 +02:00
|
|
|
"#c92a2a",
|
|
|
|
"#a61e4d",
|
|
|
|
"#862e9c",
|
|
|
|
"#5f3dc4",
|
|
|
|
"#364fc7",
|
|
|
|
"#1864ab",
|
|
|
|
"#0b7285",
|
|
|
|
"#087f5b",
|
|
|
|
"#2b8a3e",
|
|
|
|
"#5c940d",
|
|
|
|
"#e67700",
|
|
|
|
"#d9480f"
|
2020-01-11 23:58:44 +01:00
|
|
|
]
|
|
|
|
};
|