Browse Source

Improved color picker (#174)

* Add react-color

* Prettier

* Better styles

* Use enum for color pickers instead of strings

* Run prettier on .scss file
vanilla_orig
Jared Palmer 5 years ago
parent
commit
b5c67260d7
4 changed files with 205 additions and 20 deletions
  1. 45
    0
      package-lock.json
  2. 2
    0
      package.json
  3. 118
    19
      src/index.tsx
  4. 40
    1
      src/styles.scss

+ 45
- 0
package-lock.json View File

@@ -1034,6 +1034,11 @@
1034 1034
         "@hapi/hoek": "^8.3.0"
1035 1035
       }
1036 1036
     },
1037
+    "@icons/material": {
1038
+      "version": "0.2.4",
1039
+      "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
1040
+      "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw=="
1041
+    },
1037 1042
     "@jest/console": {
1038 1043
       "version": "24.9.0",
1039 1044
       "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz",
@@ -1511,6 +1516,15 @@
1511 1516
         "csstype": "^2.2.0"
1512 1517
       }
1513 1518
     },
1519
+    "@types/react-color": {
1520
+      "version": "3.0.1",
1521
+      "resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-3.0.1.tgz",
1522
+      "integrity": "sha512-J6mYm43Sid9y+OjZ7NDfJ2VVkeeuTPNVImNFITgQNXodHteKfl/t/5pAR5Z9buodZ2tCctsZjgiMlQOpfntakw==",
1523
+      "dev": true,
1524
+      "requires": {
1525
+        "@types/react": "*"
1526
+      }
1527
+    },
1514 1528
     "@types/react-dom": {
1515 1529
       "version": "16.9.4",
1516 1530
       "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.4.tgz",
@@ -9369,6 +9383,11 @@
9369 9383
         "object-visit": "^1.0.0"
9370 9384
       }
9371 9385
     },
9386
+    "material-colors": {
9387
+      "version": "1.2.6",
9388
+      "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
9389
+      "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
9390
+    },
9372 9391
     "md5.js": {
9373 9392
       "version": "1.3.5",
9374 9393
       "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@@ -12094,6 +12113,19 @@
12094 12113
         "whatwg-fetch": "^3.0.0"
12095 12114
       }
12096 12115
     },
12116
+    "react-color": {
12117
+      "version": "2.17.3",
12118
+      "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.17.3.tgz",
12119
+      "integrity": "sha512-1dtO8LqAVotPIChlmo6kLtFS1FP89ll8/OiA8EcFRDR+ntcK+0ukJgByuIQHRtzvigf26dV5HklnxDIvhON9VQ==",
12120
+      "requires": {
12121
+        "@icons/material": "^0.2.4",
12122
+        "lodash": "^4.17.11",
12123
+        "material-colors": "^1.2.1",
12124
+        "prop-types": "^15.5.10",
12125
+        "reactcss": "^1.2.0",
12126
+        "tinycolor2": "^1.4.1"
12127
+      }
12128
+    },
12097 12129
     "react-dev-utils": {
12098 12130
       "version": "10.0.0",
12099 12131
       "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.0.0.tgz",
@@ -12315,6 +12347,14 @@
12315 12347
         "workbox-webpack-plugin": "4.3.1"
12316 12348
       }
12317 12349
     },
12350
+    "reactcss": {
12351
+      "version": "1.2.3",
12352
+      "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
12353
+      "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
12354
+      "requires": {
12355
+        "lodash": "^4.0.1"
12356
+      }
12357
+    },
12318 12358
     "read-pkg": {
12319 12359
       "version": "3.0.0",
12320 12360
       "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
@@ -14397,6 +14437,11 @@
14397 14437
       "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
14398 14438
       "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
14399 14439
     },
14440
+    "tinycolor2": {
14441
+      "version": "1.4.1",
14442
+      "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
14443
+      "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
14444
+    },
14400 14445
     "tmp": {
14401 14446
       "version": "0.0.33",
14402 14447
       "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",

+ 2
- 0
package.json View File

@@ -7,12 +7,14 @@
7 7
   "main": "src/index.js",
8 8
   "dependencies": {
9 9
     "react": "16.12.0",
10
+    "react-color": "^2.17.3",
10 11
     "react-dom": "16.12.0",
11 12
     "react-scripts": "3.3.0",
12 13
     "roughjs": "3.1.0"
13 14
   },
14 15
   "devDependencies": {
15 16
     "@types/react": "16.9.17",
17
+    "@types/react-color": "^3.0.1",
16 18
     "@types/react-dom": "16.9.4",
17 19
     "husky": "3.1.0",
18 20
     "lint-staged": "9.5.0",

+ 118
- 19
src/index.tsx View File

@@ -2,6 +2,7 @@ import React from "react";
2 2
 import ReactDOM from "react-dom";
3 3
 import rough from "roughjs/bin/wrappers/rough";
4 4
 import { RoughCanvas } from "roughjs/bin/canvas";
5
+import { SketchPicker } from "react-color";
5 6
 
6 7
 import { moveOneLeft, moveAllLeft, moveOneRight, moveAllRight } from "./zindex";
7 8
 
@@ -816,9 +817,16 @@ function restore(
816 817
   }
817 818
 }
818 819
 
820
+enum ColorPicker {
821
+  CANVAS_BACKGROUND,
822
+  SHAPE_STROKE,
823
+  SHAPE_BACKGROUND
824
+}
825
+
819 826
 type AppState = {
820 827
   draggingElement: ExcalidrawElement | null;
821 828
   resizingElement: ExcalidrawElement | null;
829
+  currentColorPicker: ColorPicker | null;
822 830
   elementType: string;
823 831
   exportBackground: boolean;
824 832
   currentItemStrokeColor: string;
@@ -889,7 +897,6 @@ const SHAPES = [
889 897
 
890 898
 const shapesShortcutKeys = SHAPES.map(shape => shape.value[0]);
891 899
 
892
-
893 900
 function capitalize(str: string) {
894 901
   return str.charAt(0).toUpperCase() + str.slice(1);
895 902
 }
@@ -953,6 +960,7 @@ class App extends React.Component<{}, AppState> {
953 960
     draggingElement: null,
954 961
     resizingElement: null,
955 962
     elementType: "selection",
963
+    currentColorPicker: null,
956 964
     exportBackground: true,
957 965
     currentItemStrokeColor: "#000000",
958 966
     currentItemBackgroundColor: "#ffffff",
@@ -1134,7 +1142,11 @@ class App extends React.Component<{}, AppState> {
1134 1142
           <h4>Shapes</h4>
1135 1143
           <div className="panelTools">
1136 1144
             {SHAPES.map(({ value, icon }) => (
1137
-              <label key={value} className="tool" title={`${capitalize(value)} - ${capitalize(value)[0]}`}>
1145
+              <label
1146
+                key={value}
1147
+                className="tool"
1148
+                title={`${capitalize(value)} - ${capitalize(value)[0]}`}
1149
+              >
1138 1150
                 <input
1139 1151
                   type="radio"
1140 1152
                   checked={this.state.elementType === value}
@@ -1152,36 +1164,123 @@ class App extends React.Component<{}, AppState> {
1152 1164
           </div>
1153 1165
           <h4>Colors</h4>
1154 1166
           <div className="panelColumn">
1155
-            <label>
1167
+            <h5>Canvas Background</h5>
1168
+            <div>
1169
+              <button
1170
+                className="swatch"
1171
+                style={{
1172
+                  backgroundColor: this.state.viewBackgroundColor
1173
+                }}
1174
+                onClick={() =>
1175
+                  this.setState(s => ({
1176
+                    currentColorPicker:
1177
+                      s.currentColorPicker === ColorPicker.CANVAS_BACKGROUND
1178
+                        ? null
1179
+                        : ColorPicker.CANVAS_BACKGROUND
1180
+                  }))
1181
+                }
1182
+              ></button>
1183
+              {this.state.currentColorPicker === ColorPicker.CANVAS_BACKGROUND ? (
1184
+                <div className="popover">
1185
+                  <div
1186
+                    className="cover"
1187
+                    onClick={() => this.setState({ currentColorPicker: null })}
1188
+                  ></div>
1189
+                  <SketchPicker
1190
+                    color={this.state.viewBackgroundColor}
1191
+                    onChange={color => {
1192
+                      this.setState({ viewBackgroundColor: color.hex });
1193
+                    }}
1194
+                  />
1195
+                </div>
1196
+              ) : null}
1156 1197
               <input
1157
-                type="color"
1198
+                type="text"
1199
+                className="swatch-input"
1158 1200
                 value={this.state.viewBackgroundColor}
1159
-                onChange={e => {
1160
-                  this.setState({ viewBackgroundColor: e.target.value });
1161
-                }}
1201
+                onChange={e =>
1202
+                  this.setState({ viewBackgroundColor: e.target.value })
1203
+                }
1162 1204
               />
1163
-              Background
1164
-            </label>
1165
-            <label>
1205
+            </div>
1206
+            <h5>Shape Stroke</h5>
1207
+            <div>
1208
+              <button
1209
+                className="swatch"
1210
+                style={{
1211
+                  backgroundColor: this.state.currentItemStrokeColor
1212
+                }}
1213
+                onClick={() =>
1214
+                  this.setState(s => ({
1215
+                    currentColorPicker:
1216
+                      s.currentColorPicker === ColorPicker.SHAPE_STROKE
1217
+                        ? null
1218
+                        : ColorPicker.SHAPE_STROKE
1219
+                  }))
1220
+                }
1221
+              ></button>
1222
+              {this.state.currentColorPicker === ColorPicker.SHAPE_STROKE ? (
1223
+                <div className="popover">
1224
+                  <div
1225
+                    className="cover"
1226
+                    onClick={() => this.setState({ currentColorPicker: null })}
1227
+                  ></div>
1228
+                  <SketchPicker
1229
+                    color={this.state.currentItemStrokeColor}
1230
+                    onChange={color => {
1231
+                      this.setState({ currentItemStrokeColor: color.hex });
1232
+                    }}
1233
+                  />
1234
+                </div>
1235
+              ) : null}
1166 1236
               <input
1167
-                type="color"
1237
+                type="text"
1238
+                className="swatch-input"
1168 1239
                 value={this.state.currentItemStrokeColor}
1169 1240
                 onChange={e => {
1170 1241
                   this.setState({ currentItemStrokeColor: e.target.value });
1171 1242
                 }}
1172 1243
               />
1173
-              Shape Stroke
1174
-            </label>
1175
-            <label>
1244
+            </div>
1245
+            <h5>Shape Background</h5>
1246
+            <div>
1247
+              <button
1248
+                className="swatch"
1249
+                style={{
1250
+                  backgroundColor: this.state.currentItemBackgroundColor
1251
+                }}
1252
+                onClick={() =>
1253
+                  this.setState(s => ({
1254
+                    currentColorPicker:
1255
+                      s.currentColorPicker === ColorPicker.SHAPE_BACKGROUND
1256
+                        ? null
1257
+                        : ColorPicker.SHAPE_BACKGROUND
1258
+                  }))
1259
+                }
1260
+              ></button>
1261
+              {this.state.currentColorPicker === ColorPicker.SHAPE_BACKGROUND ? (
1262
+                <div className="popover">
1263
+                  <div
1264
+                    className="cover"
1265
+                    onClick={() => this.setState({ currentColorPicker: null })}
1266
+                  ></div>
1267
+                  <SketchPicker
1268
+                    color={this.state.currentItemBackgroundColor}
1269
+                    onChange={color => {
1270
+                      this.setState({ currentItemBackgroundColor: color.hex });
1271
+                    }}
1272
+                  />
1273
+                </div>
1274
+              ) : null}
1176 1275
               <input
1177
-                type="color"
1178
-                value={this.state.currentItemBackgroundColor}
1276
+                type="text"
1277
+                className="swatch-input"
1278
+                value={this.state.currentItemStrokeColor}
1179 1279
                 onChange={e => {
1180
-                  this.setState({ currentItemBackgroundColor: e.target.value });
1280
+                  this.setState({ currentItemStrokeColor: e.target.value });
1181 1281
                 }}
1182 1282
               />
1183
-              Shape Background
1184
-            </label>
1283
+            </div>
1185 1284
           </div>
1186 1285
           <h4>Canvas</h4>
1187 1286
           <div className="panelColumn">

+ 40
- 1
src/styles.scss View File

@@ -22,7 +22,6 @@ body {
22 22
 .sidePanel {
23 23
   width: 230px;
24 24
   background-color: #eee;
25
-
26 25
   padding: 10px;
27 26
   overflow-y: auto;
28 27
 
@@ -42,6 +41,17 @@ body {
42 41
   .panelColumn {
43 42
     display: flex;
44 43
     flex-direction: column;
44
+
45
+    h5 {
46
+      margin-top: 4px;
47
+      margin-bottom: 4px;
48
+      font-size: 12px;
49
+      color: #333;
50
+    }
51
+
52
+    h5:first-of-type {
53
+      margin-top: 0;
54
+    }
45 55
   }
46 56
 }
47 57
 
@@ -134,3 +144,32 @@ button {
134 144
     cursor: not-allowed;
135 145
   }
136 146
 }
147
+
148
+.popover {
149
+  position: absolute;
150
+  z-index: 2;
151
+
152
+  .cover {
153
+    position: fixed;
154
+    top: 0;
155
+    left: 0;
156
+    right: 0;
157
+    bottom: 0;
158
+  }
159
+}
160
+
161
+.swatch {
162
+  height: 24px;
163
+  width: 24px;
164
+  display: inline;
165
+  margin-right: 4px;
166
+}
167
+
168
+.swatch-input {
169
+  font-size: 16px;
170
+  display: inline;
171
+  width: 100px;
172
+  border-radius: 2px;
173
+  padding: 2px 4px;
174
+  border: 1px solid #ddd;
175
+}

Loading…
Cancel
Save