Improve Color Picker *2 (#195)
This commit is contained in:
parent
23cd62d148
commit
3bbcb9cbdc
279
src/index.tsx
279
src/index.tsx
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user