Improved color picker (#174)
* Add react-color * Prettier * Better styles * Use enum for color pickers instead of strings * Run prettier on .scss file
This commit is contained in:
parent
e7e676e1eb
commit
b5c67260d7
45
package-lock.json
generated
45
package-lock.json
generated
@ -1034,6 +1034,11 @@
|
|||||||
"@hapi/hoek": "^8.3.0"
|
"@hapi/hoek": "^8.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@icons/material": {
|
||||||
|
"version": "0.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
|
||||||
|
"integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw=="
|
||||||
|
},
|
||||||
"@jest/console": {
|
"@jest/console": {
|
||||||
"version": "24.9.0",
|
"version": "24.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz",
|
||||||
@ -1511,6 +1516,15 @@
|
|||||||
"csstype": "^2.2.0"
|
"csstype": "^2.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/react-color": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-J6mYm43Sid9y+OjZ7NDfJ2VVkeeuTPNVImNFITgQNXodHteKfl/t/5pAR5Z9buodZ2tCctsZjgiMlQOpfntakw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/react-dom": {
|
"@types/react-dom": {
|
||||||
"version": "16.9.4",
|
"version": "16.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.4.tgz",
|
||||||
@ -9369,6 +9383,11 @@
|
|||||||
"object-visit": "^1.0.0"
|
"object-visit": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"material-colors": {
|
||||||
|
"version": "1.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
|
||||||
|
"integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
|
||||||
|
},
|
||||||
"md5.js": {
|
"md5.js": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||||
@ -12094,6 +12113,19 @@
|
|||||||
"whatwg-fetch": "^3.0.0"
|
"whatwg-fetch": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-color": {
|
||||||
|
"version": "2.17.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-color/-/react-color-2.17.3.tgz",
|
||||||
|
"integrity": "sha512-1dtO8LqAVotPIChlmo6kLtFS1FP89ll8/OiA8EcFRDR+ntcK+0ukJgByuIQHRtzvigf26dV5HklnxDIvhON9VQ==",
|
||||||
|
"requires": {
|
||||||
|
"@icons/material": "^0.2.4",
|
||||||
|
"lodash": "^4.17.11",
|
||||||
|
"material-colors": "^1.2.1",
|
||||||
|
"prop-types": "^15.5.10",
|
||||||
|
"reactcss": "^1.2.0",
|
||||||
|
"tinycolor2": "^1.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-dev-utils": {
|
"react-dev-utils": {
|
||||||
"version": "10.0.0",
|
"version": "10.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.0.0.tgz",
|
||||||
@ -12315,6 +12347,14 @@
|
|||||||
"workbox-webpack-plugin": "4.3.1"
|
"workbox-webpack-plugin": "4.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"reactcss": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
|
||||||
|
"requires": {
|
||||||
|
"lodash": "^4.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"read-pkg": {
|
"read-pkg": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
|
||||||
@ -14397,6 +14437,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
||||||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
|
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
|
||||||
},
|
},
|
||||||
|
"tinycolor2": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
|
||||||
|
"integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
|
||||||
|
},
|
||||||
"tmp": {
|
"tmp": {
|
||||||
"version": "0.0.33",
|
"version": "0.0.33",
|
||||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||||
|
@ -7,12 +7,14 @@
|
|||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "16.12.0",
|
"react": "16.12.0",
|
||||||
|
"react-color": "^2.17.3",
|
||||||
"react-dom": "16.12.0",
|
"react-dom": "16.12.0",
|
||||||
"react-scripts": "3.3.0",
|
"react-scripts": "3.3.0",
|
||||||
"roughjs": "3.1.0"
|
"roughjs": "3.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "16.9.17",
|
"@types/react": "16.9.17",
|
||||||
|
"@types/react-color": "^3.0.1",
|
||||||
"@types/react-dom": "16.9.4",
|
"@types/react-dom": "16.9.4",
|
||||||
"husky": "3.1.0",
|
"husky": "3.1.0",
|
||||||
"lint-staged": "9.5.0",
|
"lint-staged": "9.5.0",
|
||||||
|
143
src/index.tsx
143
src/index.tsx
@ -2,6 +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 { moveOneLeft, moveAllLeft, moveOneRight, moveAllRight } from "./zindex";
|
import { moveOneLeft, moveAllLeft, moveOneRight, moveAllRight } from "./zindex";
|
||||||
|
|
||||||
@ -816,9 +817,16 @@ 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;
|
||||||
@ -889,7 +897,6 @@ const SHAPES = [
|
|||||||
|
|
||||||
const shapesShortcutKeys = SHAPES.map(shape => shape.value[0]);
|
const shapesShortcutKeys = SHAPES.map(shape => shape.value[0]);
|
||||||
|
|
||||||
|
|
||||||
function capitalize(str: string) {
|
function capitalize(str: string) {
|
||||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
}
|
}
|
||||||
@ -953,6 +960,7 @@ 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",
|
||||||
@ -1134,7 +1142,11 @@ class App extends React.Component<{}, AppState> {
|
|||||||
<h4>Shapes</h4>
|
<h4>Shapes</h4>
|
||||||
<div className="panelTools">
|
<div className="panelTools">
|
||||||
{SHAPES.map(({ value, icon }) => (
|
{SHAPES.map(({ value, icon }) => (
|
||||||
<label key={value} className="tool" title={`${capitalize(value)} - ${capitalize(value)[0]}`}>
|
<label
|
||||||
|
key={value}
|
||||||
|
className="tool"
|
||||||
|
title={`${capitalize(value)} - ${capitalize(value)[0]}`}
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
checked={this.state.elementType === value}
|
checked={this.state.elementType === value}
|
||||||
@ -1152,36 +1164,123 @@ class App extends React.Component<{}, AppState> {
|
|||||||
</div>
|
</div>
|
||||||
<h4>Colors</h4>
|
<h4>Colors</h4>
|
||||||
<div className="panelColumn">
|
<div className="panelColumn">
|
||||||
<label>
|
<h5>Canvas Background</h5>
|
||||||
<input
|
<div>
|
||||||
type="color"
|
<button
|
||||||
value={this.state.viewBackgroundColor}
|
className="swatch"
|
||||||
onChange={e => {
|
style={{
|
||||||
this.setState({ viewBackgroundColor: e.target.value });
|
backgroundColor: this.state.viewBackgroundColor
|
||||||
|
}}
|
||||||
|
onClick={() =>
|
||||||
|
this.setState(s => ({
|
||||||
|
currentColorPicker:
|
||||||
|
s.currentColorPicker === ColorPicker.CANVAS_BACKGROUND
|
||||||
|
? null
|
||||||
|
: ColorPicker.CANVAS_BACKGROUND
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
></button>
|
||||||
|
{this.state.currentColorPicker === ColorPicker.CANVAS_BACKGROUND ? (
|
||||||
|
<div className="popover">
|
||||||
|
<div
|
||||||
|
className="cover"
|
||||||
|
onClick={() => this.setState({ currentColorPicker: null })}
|
||||||
|
></div>
|
||||||
|
<SketchPicker
|
||||||
|
color={this.state.viewBackgroundColor}
|
||||||
|
onChange={color => {
|
||||||
|
this.setState({ viewBackgroundColor: color.hex });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
Background
|
</div>
|
||||||
</label>
|
) : null}
|
||||||
<label>
|
|
||||||
<input
|
<input
|
||||||
type="color"
|
type="text"
|
||||||
|
className="swatch-input"
|
||||||
|
value={this.state.viewBackgroundColor}
|
||||||
|
onChange={e =>
|
||||||
|
this.setState({ viewBackgroundColor: e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<h5>Shape Stroke</h5>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
className="swatch"
|
||||||
|
style={{
|
||||||
|
backgroundColor: this.state.currentItemStrokeColor
|
||||||
|
}}
|
||||||
|
onClick={() =>
|
||||||
|
this.setState(s => ({
|
||||||
|
currentColorPicker:
|
||||||
|
s.currentColorPicker === ColorPicker.SHAPE_STROKE
|
||||||
|
? null
|
||||||
|
: ColorPicker.SHAPE_STROKE
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
></button>
|
||||||
|
{this.state.currentColorPicker === ColorPicker.SHAPE_STROKE ? (
|
||||||
|
<div className="popover">
|
||||||
|
<div
|
||||||
|
className="cover"
|
||||||
|
onClick={() => this.setState({ currentColorPicker: null })}
|
||||||
|
></div>
|
||||||
|
<SketchPicker
|
||||||
|
color={this.state.currentItemStrokeColor}
|
||||||
|
onChange={color => {
|
||||||
|
this.setState({ currentItemStrokeColor: color.hex });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="swatch-input"
|
||||||
value={this.state.currentItemStrokeColor}
|
value={this.state.currentItemStrokeColor}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
this.setState({ currentItemStrokeColor: e.target.value });
|
this.setState({ currentItemStrokeColor: e.target.value });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
Shape Stroke
|
</div>
|
||||||
</label>
|
<h5>Shape Background</h5>
|
||||||
<label>
|
<div>
|
||||||
<input
|
<button
|
||||||
type="color"
|
className="swatch"
|
||||||
value={this.state.currentItemBackgroundColor}
|
style={{
|
||||||
onChange={e => {
|
backgroundColor: this.state.currentItemBackgroundColor
|
||||||
this.setState({ currentItemBackgroundColor: e.target.value });
|
}}
|
||||||
|
onClick={() =>
|
||||||
|
this.setState(s => ({
|
||||||
|
currentColorPicker:
|
||||||
|
s.currentColorPicker === ColorPicker.SHAPE_BACKGROUND
|
||||||
|
? null
|
||||||
|
: ColorPicker.SHAPE_BACKGROUND
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
></button>
|
||||||
|
{this.state.currentColorPicker === ColorPicker.SHAPE_BACKGROUND ? (
|
||||||
|
<div className="popover">
|
||||||
|
<div
|
||||||
|
className="cover"
|
||||||
|
onClick={() => this.setState({ currentColorPicker: null })}
|
||||||
|
></div>
|
||||||
|
<SketchPicker
|
||||||
|
color={this.state.currentItemBackgroundColor}
|
||||||
|
onChange={color => {
|
||||||
|
this.setState({ currentItemBackgroundColor: color.hex });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
Shape Background
|
</div>
|
||||||
</label>
|
) : null}
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="swatch-input"
|
||||||
|
value={this.state.currentItemStrokeColor}
|
||||||
|
onChange={e => {
|
||||||
|
this.setState({ currentItemStrokeColor: e.target.value });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h4>Canvas</h4>
|
<h4>Canvas</h4>
|
||||||
<div className="panelColumn">
|
<div className="panelColumn">
|
||||||
|
@ -22,7 +22,6 @@ body {
|
|||||||
.sidePanel {
|
.sidePanel {
|
||||||
width: 230px;
|
width: 230px;
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
|
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
@ -42,6 +41,17 @@ body {
|
|||||||
.panelColumn {
|
.panelColumn {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5:first-of-type {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,3 +144,32 @@ button {
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popover {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
.cover {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.swatch {
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
display: inline;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swatch-input {
|
||||||
|
font-size: 16px;
|
||||||
|
display: inline;
|
||||||
|
width: 100px;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user