Improve Color Picker *2 (#195)

This commit is contained in:
Christopher Chedeau 2020-01-05 19:57:50 -08:00 committed by GitHub
parent 23cd62d148
commit 3bbcb9cbdc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 78 additions and 204 deletions

View File

@ -2,7 +2,7 @@ import React from "react";
import ReactDOM from "react-dom";
import rough from "roughjs/bin/wrappers/rough";
import { RoughCanvas } from "roughjs/bin/canvas";
import { SketchPicker } from "react-color";
import { TwitterPicker } from "react-color";
import { moveOneLeft, moveAllLeft, moveOneRight, moveAllRight } from "./zindex";
import { roundRect } from "./roundRect";
@ -980,16 +980,9 @@ function restore(
}
}
enum ColorPicker {
CANVAS_BACKGROUND,
SHAPE_STROKE,
SHAPE_BACKGROUND
}
type AppState = {
draggingElement: ExcalidrawElement | null;
resizingElement: ExcalidrawElement | null;
currentColorPicker: ColorPicker | null;
elementType: string;
exportBackground: boolean;
currentItemStrokeColor: string;
@ -1124,70 +1117,17 @@ const hasStroke = () =>
element.type === "arrow")
);
function getSelectedFillStyles() {
const fillStyles = Array.from(
function getSelectedAttribute<T>(
getAttribute: (element: ExcalidrawElement) => T
): T | null {
const attributes = Array.from(
new Set(
elements
.filter(element => element.isSelected)
.map(element => element.fillStyle)
.map(element => getAttribute(element))
)
);
return fillStyles.length === 1 ? fillStyles[0] : null;
}
function getSelectedStrokeWidth() {
const strokeWidth = Array.from(
new Set(
elements
.filter(element => element.isSelected)
.map(element => element.strokeWidth)
)
);
return strokeWidth.length === 1 ? strokeWidth[0] : null;
}
function getSelectedRoughness() {
const roughness = Array.from(
new Set(
elements
.filter(element => element.isSelected)
.map(element => element.roughness)
)
);
return roughness.length === 1 ? roughness[0] : null;
}
function getSelectedOpacity() {
const opacity = Array.from(
new Set(
elements
.filter(element => element.isSelected)
.map(element => element.opacity)
)
);
return opacity.length === 1 ? opacity[0] : null;
}
function getSelectedStrokeColor() {
const strokeColors = Array.from(
new Set(
elements
.filter(element => element.isSelected)
.map(element => element.strokeColor)
)
);
return strokeColors.length === 1 ? strokeColors[0] : null;
}
function getSelectedBackgroundColor() {
const backgroundColors = Array.from(
new Set(
elements
.filter(element => element.isSelected)
.map(element => element.backgroundColor)
)
);
return backgroundColors.length === 1 ? backgroundColors[0] : null;
return attributes.length === 1 ? attributes[0] : null;
}
function addTextElement(element: ExcalidrawTextElement) {
@ -1254,6 +1194,56 @@ function ButtonSelect<T>({
);
}
function ColorPicker({
color,
onChange
}: {
color: string | null;
onChange: (color: string) => void;
}) {
const [isActive, setActive] = React.useState(false);
return (
<div>
<button
className="swatch"
style={color ? { backgroundColor: color } : undefined}
onClick={() => setActive(!isActive)}
/>
{isActive ? (
<div className="popover">
<div className="cover" onClick={() => setActive(false)} />
<TwitterPicker
colors={[
"#000000",
"#ABB8C3",
"#FFFFFF",
"#FF6900",
"#FCB900",
"#00D084",
"#8ED1FC",
"#0693E3",
"#EB144C",
"#F78DA7",
"#9900EF"
]}
width="205px"
color={color || undefined}
onChange={changedColor => {
onChange(changedColor.hex);
}}
/>
</div>
) : null}
<input
type="text"
className="swatch-input"
value={color || ""}
onChange={e => onChange(e.target.value)}
/>
</div>
);
}
const ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5;
const ELEMENT_TRANSLATE_AMOUNT = 1;
@ -1282,7 +1272,6 @@ class App extends React.Component<{}, AppState> {
draggingElement: null,
resizingElement: null,
elementType: "selection",
currentColorPicker: null,
exportBackground: true,
currentItemStrokeColor: "#000000",
currentItemBackgroundColor: "#ffffff",
@ -1524,101 +1513,20 @@ class App extends React.Component<{}, AppState> {
<button onClick={this.moveAllLeft}>Send to back</button>
</div>
<h5>Stroke Color</h5>
<div>
<button
className="swatch"
style={{
backgroundColor:
getSelectedStrokeColor() ||
this.state.currentItemStrokeColor
}}
onClick={() =>
this.setState(s => ({
currentColorPicker:
s.currentColorPicker === ColorPicker.SHAPE_STROKE
? null
: ColorPicker.SHAPE_STROKE
}))
}
/>
{this.state.currentColorPicker === ColorPicker.SHAPE_STROKE && (
<div className="popover">
<div
className="cover"
onClick={() =>
this.setState({ currentColorPicker: null })
}
/>
<SketchPicker
color={this.state.currentItemStrokeColor}
onChange={color => this.changeStrokeColor(color.hex)}
/>
</div>
)}
<input
type="text"
className="swatch-input"
value={
getSelectedStrokeColor() ||
this.state.currentItemStrokeColor
}
onChange={e => this.changeStrokeColor(e.target.value)}
/>
</div>
<ColorPicker
color={getSelectedAttribute(element => element.strokeColor)}
onChange={color => this.changeStrokeColor(color)}
/>
{hasBackground() && (
<>
<h5>Background Color</h5>
<div>
<button
className="swatch"
style={{
backgroundColor:
getSelectedBackgroundColor() ||
this.state.currentItemBackgroundColor
}}
onClick={() =>
this.setState(s => ({
currentColorPicker:
s.currentColorPicker ===
ColorPicker.SHAPE_BACKGROUND
? null
: ColorPicker.SHAPE_BACKGROUND
}))
}
/>
{this.state.currentColorPicker ===
ColorPicker.SHAPE_BACKGROUND ? (
<div className="popover">
<div
className="cover"
onClick={() =>
this.setState({ currentColorPicker: null })
}
/>
<SketchPicker
color={this.state.currentItemBackgroundColor}
onChange={color =>
this.changeBackgroundColor(color.hex)
}
/>
</div>
) : null}
<input
type="text"
className="swatch-input"
value={
getSelectedBackgroundColor() ||
this.state.currentItemBackgroundColor
}
onChange={e => this.changeBackgroundColor(e.target.value)}
/>
</div>
</>
)}
{hasBackground() && (
<>
<ColorPicker
color={getSelectedAttribute(
element => element.backgroundColor
)}
onChange={color => this.changeBackgroundColor(color)}
/>
<h5>Fill</h5>
<ButtonSelect
options={[
@ -1626,7 +1534,7 @@ class App extends React.Component<{}, AppState> {
{ value: "hachure", text: "Hachure" },
{ value: "cross-hatch", text: "Cross-hatch" }
]}
value={getSelectedFillStyles()}
value={getSelectedAttribute(element => element.fillStyle)}
onChange={value => {
this.changeProperty(element => {
element.fillStyle = value;
@ -1645,7 +1553,7 @@ class App extends React.Component<{}, AppState> {
{ value: 2, text: "Bold" },
{ value: 4, text: "Extra Bold" }
]}
value={getSelectedStrokeWidth()}
value={getSelectedAttribute(element => element.strokeWidth)}
onChange={value => {
this.changeProperty(element => {
element.strokeWidth = value;
@ -1653,14 +1561,14 @@ class App extends React.Component<{}, AppState> {
}}
/>
<h5>Slopiness</h5>
<h5>Sloppiness</h5>
<ButtonSelect
options={[
{ value: 0, text: "Draftsman" },
{ value: 1, text: "Artist" },
{ value: 3, text: "Cartoonist" }
]}
value={getSelectedRoughness()}
value={getSelectedAttribute(element => element.roughness)}
onChange={value =>
this.changeProperty(element => {
element.roughness = value;
@ -1677,7 +1585,7 @@ class App extends React.Component<{}, AppState> {
max="100"
onChange={this.changeOpacity}
value={
getSelectedOpacity() ||
getSelectedAttribute(element => element.opacity) ||
0 /* Put the opacity at 0 if there are two conflicting ones */
}
/>
@ -1690,45 +1598,10 @@ class App extends React.Component<{}, AppState> {
<h4>Canvas</h4>
<div className="panelColumn">
<h5>Canvas Background Color</h5>
<div>
<button
className="swatch"
style={{
backgroundColor: this.state.viewBackgroundColor
}}
onClick={() =>
this.setState(s => ({
currentColorPicker:
s.currentColorPicker === ColorPicker.CANVAS_BACKGROUND
? null
: ColorPicker.CANVAS_BACKGROUND
}))
}
/>
{this.state.currentColorPicker ===
ColorPicker.CANVAS_BACKGROUND ? (
<div className="popover">
<div
className="cover"
onClick={() => this.setState({ currentColorPicker: null })}
/>
<SketchPicker
color={this.state.viewBackgroundColor}
onChange={color => {
this.setState({ viewBackgroundColor: color.hex });
}}
/>
</div>
) : null}
<input
type="text"
className="swatch-input"
value={this.state.viewBackgroundColor}
onChange={e =>
this.setState({ viewBackgroundColor: e.target.value })
}
/>
</div>
<ColorPicker
color={this.state.viewBackgroundColor}
onChange={color => this.setState({ viewBackgroundColor: color })}
/>
<button
onClick={this.clearCanvas}
title="Clear the canvas & reset background color"

View File

@ -24,6 +24,7 @@ body {
background-color: #eee;
padding: 10px;
overflow-y: auto;
position: relative;
h4 {
margin: 10px 0 10px 0;
@ -50,7 +51,7 @@ body {
color: #333;
}
h5:first-of-type {
h5:first-child {
margin-top: 0;
}