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 ReactDOM from "react-dom";
import rough from "roughjs/bin/wrappers/rough"; import rough from "roughjs/bin/wrappers/rough";
import { RoughCanvas } from "roughjs/bin/canvas"; import { RoughCanvas } from "roughjs/bin/canvas";
import { SketchPicker } from "react-color"; import { TwitterPicker } from "react-color";
import { moveOneLeft, moveAllLeft, moveOneRight, moveAllRight } from "./zindex"; import { moveOneLeft, moveAllLeft, moveOneRight, moveAllRight } from "./zindex";
import { roundRect } from "./roundRect"; import { roundRect } from "./roundRect";
@ -980,16 +980,9 @@ function restore(
} }
} }
enum ColorPicker {
CANVAS_BACKGROUND,
SHAPE_STROKE,
SHAPE_BACKGROUND
}
type AppState = { type AppState = {
draggingElement: ExcalidrawElement | null; draggingElement: ExcalidrawElement | null;
resizingElement: ExcalidrawElement | null; resizingElement: ExcalidrawElement | null;
currentColorPicker: ColorPicker | null;
elementType: string; elementType: string;
exportBackground: boolean; exportBackground: boolean;
currentItemStrokeColor: string; currentItemStrokeColor: string;
@ -1124,70 +1117,17 @@ const hasStroke = () =>
element.type === "arrow") element.type === "arrow")
); );
function getSelectedFillStyles() { function getSelectedAttribute<T>(
const fillStyles = Array.from( getAttribute: (element: ExcalidrawElement) => T
): T | null {
const attributes = Array.from(
new Set( new Set(
elements elements
.filter(element => element.isSelected) .filter(element => element.isSelected)
.map(element => element.fillStyle) .map(element => getAttribute(element))
) )
); );
return fillStyles.length === 1 ? fillStyles[0] : null; return attributes.length === 1 ? attributes[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;
} }
function addTextElement(element: ExcalidrawTextElement) { 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_SHIFT_TRANSLATE_AMOUNT = 5;
const ELEMENT_TRANSLATE_AMOUNT = 1; const ELEMENT_TRANSLATE_AMOUNT = 1;
@ -1282,7 +1272,6 @@ class App extends React.Component<{}, AppState> {
draggingElement: null, draggingElement: null,
resizingElement: null, resizingElement: null,
elementType: "selection", elementType: "selection",
currentColorPicker: null,
exportBackground: true, exportBackground: true,
currentItemStrokeColor: "#000000", currentItemStrokeColor: "#000000",
currentItemBackgroundColor: "#ffffff", currentItemBackgroundColor: "#ffffff",
@ -1524,101 +1513,20 @@ class App extends React.Component<{}, AppState> {
<button onClick={this.moveAllLeft}>Send to back</button> <button onClick={this.moveAllLeft}>Send to back</button>
</div> </div>
<h5>Stroke Color</h5> <h5>Stroke Color</h5>
<div> <ColorPicker
<button color={getSelectedAttribute(element => element.strokeColor)}
className="swatch" onChange={color => this.changeStrokeColor(color)}
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>
{hasBackground() && ( {hasBackground() && (
<> <>
<h5>Background Color</h5> <h5>Background Color</h5>
<div> <ColorPicker
<button color={getSelectedAttribute(
className="swatch" element => element.backgroundColor
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>
</>
)} )}
onChange={color => this.changeBackgroundColor(color)}
{hasBackground() && ( />
<>
<h5>Fill</h5> <h5>Fill</h5>
<ButtonSelect <ButtonSelect
options={[ options={[
@ -1626,7 +1534,7 @@ class App extends React.Component<{}, AppState> {
{ value: "hachure", text: "Hachure" }, { value: "hachure", text: "Hachure" },
{ value: "cross-hatch", text: "Cross-hatch" } { value: "cross-hatch", text: "Cross-hatch" }
]} ]}
value={getSelectedFillStyles()} value={getSelectedAttribute(element => element.fillStyle)}
onChange={value => { onChange={value => {
this.changeProperty(element => { this.changeProperty(element => {
element.fillStyle = value; element.fillStyle = value;
@ -1645,7 +1553,7 @@ class App extends React.Component<{}, AppState> {
{ value: 2, text: "Bold" }, { value: 2, text: "Bold" },
{ value: 4, text: "Extra Bold" } { value: 4, text: "Extra Bold" }
]} ]}
value={getSelectedStrokeWidth()} value={getSelectedAttribute(element => element.strokeWidth)}
onChange={value => { onChange={value => {
this.changeProperty(element => { this.changeProperty(element => {
element.strokeWidth = value; element.strokeWidth = value;
@ -1653,14 +1561,14 @@ class App extends React.Component<{}, AppState> {
}} }}
/> />
<h5>Slopiness</h5> <h5>Sloppiness</h5>
<ButtonSelect <ButtonSelect
options={[ options={[
{ value: 0, text: "Draftsman" }, { value: 0, text: "Draftsman" },
{ value: 1, text: "Artist" }, { value: 1, text: "Artist" },
{ value: 3, text: "Cartoonist" } { value: 3, text: "Cartoonist" }
]} ]}
value={getSelectedRoughness()} value={getSelectedAttribute(element => element.roughness)}
onChange={value => onChange={value =>
this.changeProperty(element => { this.changeProperty(element => {
element.roughness = value; element.roughness = value;
@ -1677,7 +1585,7 @@ class App extends React.Component<{}, AppState> {
max="100" max="100"
onChange={this.changeOpacity} onChange={this.changeOpacity}
value={ value={
getSelectedOpacity() || getSelectedAttribute(element => element.opacity) ||
0 /* Put the opacity at 0 if there are two conflicting ones */ 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> <h4>Canvas</h4>
<div className="panelColumn"> <div className="panelColumn">
<h5>Canvas Background Color</h5> <h5>Canvas Background Color</h5>
<div> <ColorPicker
<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} color={this.state.viewBackgroundColor}
onChange={color => { onChange={color => this.setState({ viewBackgroundColor: 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>
<button <button
onClick={this.clearCanvas} onClick={this.clearCanvas}
title="Clear the canvas & reset background color" title="Clear the canvas & reset background color"

View File

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