Sfoglia il codice sorgente

Side panel (#95)

* Side panel

* Update arrow icon
Paulo Menezes 5 anni fa
parent
commit
b1a90c0020
4 ha cambiato i file con 202 aggiunte e 112 eliminazioni
  1. 29 0
      package-lock.json
  2. 3 0
      package.json
  3. 117 103
      src/index.tsx
  4. 53 9
      src/styles.css

+ 29 - 0
package-lock.json

@@ -1000,6 +1000,35 @@
       "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz",
       "integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg=="
     },
+    "@fortawesome/fontawesome-common-types": {
+      "version": "0.2.26",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.26.tgz",
+      "integrity": "sha512-CcM/fIFwZlRdiWG/25xE/wHbtyUuCtqoCTrr6BsWw7hH072fR++n4L56KPydAr3ANgMJMjT8v83ZFIsDc7kE+A=="
+    },
+    "@fortawesome/fontawesome-svg-core": {
+      "version": "1.2.26",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.26.tgz",
+      "integrity": "sha512-3Dfd/v2IztP1TxKOxZiB5+4kaOZK9mNy0KU1vVK7nFlPWz3gzxrCWB+AloQhQUoJ8HhGqbzjliK89Vl7PExGbw==",
+      "requires": {
+        "@fortawesome/fontawesome-common-types": "^0.2.26"
+      }
+    },
+    "@fortawesome/free-solid-svg-icons": {
+      "version": "5.12.0",
+      "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.12.0.tgz",
+      "integrity": "sha512-CnpsWs6GhTs9ekNB3d8rcO5HYqRkXbYKf2YNiAlTWbj5eVlPqsd/XH1F9If8jkcR1aegryAbln/qYeKVZzpM0g==",
+      "requires": {
+        "@fortawesome/fontawesome-common-types": "^0.2.26"
+      }
+    },
+    "@fortawesome/react-fontawesome": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.8.tgz",
+      "integrity": "sha512-I5h9YQg/ePA3Br9ISS18fcwOYmzQYDSM1ftH03/8nHkiqIVHtUyQBw482+60dnzvlr82gHt3mGm+nDUp159FCw==",
+      "requires": {
+        "prop-types": "^15.5.10"
+      }
+    },
     "@hapi/address": {
       "version": "2.1.4",
       "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",

+ 3 - 0
package.json

@@ -6,6 +6,9 @@
   "keywords": [],
   "main": "src/index.js",
   "dependencies": {
+    "@fortawesome/fontawesome-svg-core": "^1.2.26",
+    "@fortawesome/free-solid-svg-icons": "^5.12.0",
+    "@fortawesome/react-fontawesome": "^0.1.8",
     "lodash": "4.17.15",
     "react": "16.12.0",
     "react-dom": "16.12.0",

+ 117 - 103
src/index.tsx

@@ -2,6 +2,14 @@ import React from "react";
 import ReactDOM from "react-dom";
 import rough from "roughjs/bin/wrappers/rough";
 import { RoughCanvas } from "roughjs/bin/canvas";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import {
+  faMousePointer,
+  faSquare,
+  faCircle,
+  faLongArrowAltRight,
+  faFont
+} from "@fortawesome/free-solid-svg-icons";
 
 import "./styles.css";
 
@@ -598,28 +606,28 @@ const KEYS = {
 
 const SHAPES = [
   {
-    label: "Rectange",
+    icon: faMousePointer,
+    value: "selection"
+  },
+  {
+    icon: faSquare,
     value: "rectangle"
   },
   {
-    label: "Ellipse",
+    icon: faCircle,
     value: "ellipse"
   },
   {
-    label: "Arrow",
+    icon: faLongArrowAltRight,
     value: "arrow"
   },
   {
-    label: "Text",
+    icon: faFont,
     value: "text"
-  },
-  {
-    label: "Selection",
-    value: "selection"
   }
 ];
 
-const shapesShortcutKeys = SHAPES.map(shape => shape.label[0].toLowerCase());
+const shapesShortcutKeys = SHAPES.map(shape => shape.value[0]);
 
 function findElementByKey(key: string) {
   const defaultElement = "selection";
@@ -716,6 +724,7 @@ class App extends React.Component<{}, AppState> {
   public render() {
     return (
       <div
+        className="container"
         onCut={e => {
           e.clipboardData.setData(
             "text/plain",
@@ -756,28 +765,111 @@ class App extends React.Component<{}, AppState> {
           e.preventDefault();
         }}
       >
-        <fieldset>
-          <legend>Shapes</legend>
-          {SHAPES.map(({ value, label }) => (
-            <label key={value}>
+        <div className="sidePanel">
+          <h4>Shapes</h4>
+          <div className="panelTools">
+            {SHAPES.map(({ value, icon }) => (
+              <label key={value} className="tool">
+                <input
+                  type="radio"
+                  checked={this.state.elementType === value}
+                  onChange={() => {
+                    this.setState({ elementType: value });
+                    clearSelection();
+                    this.forceUpdate();
+                  }}
+                />
+                <div className="toolIcon">
+                  <FontAwesomeIcon icon={icon} />
+                </div>
+              </label>
+            ))}
+          </div>
+          <h4>Colors</h4>
+          <div className="panelColumn">
+            <label>
               <input
-                type="radio"
-                checked={this.state.elementType === value}
-                onChange={() => {
-                  this.setState({ elementType: value });
-                  clearSelection();
-                  this.forceUpdate();
+                type="color"
+                value={this.state.viewBackgroundColor}
+                onChange={e => {
+                  this.setState({ viewBackgroundColor: e.target.value });
                 }}
               />
-              <span>{label}</span>
+              Background
             </label>
-          ))}
-        </fieldset>
-
+            <label>
+              <input
+                type="color"
+                value={this.state.currentItemStrokeColor}
+                onChange={e => {
+                  this.setState({ currentItemStrokeColor: e.target.value });
+                }}
+              />
+              Shape Stroke
+            </label>
+            <label>
+              <input
+                type="color"
+                value={this.state.currentItemBackgroundColor}
+                onChange={e => {
+                  this.setState({ currentItemBackgroundColor: e.target.value });
+                }}
+              />
+              Shape Background
+            </label>
+          </div>
+          <h4>Export</h4>
+          <div className="panelColumn">
+            <button
+              onClick={() => {
+                exportAsPNG({
+                  exportBackground: this.state.exportBackground,
+                  exportVisibleOnly: this.state.exportVisibleOnly,
+                  exportPadding: this.state.exportPadding,
+                  viewBackgroundColor: this.state.viewBackgroundColor
+                });
+              }}
+            >
+              Export to png
+            </button>
+            <label>
+              <input
+                type="checkbox"
+                checked={this.state.exportBackground}
+                onChange={e => {
+                  this.setState({ exportBackground: e.target.checked });
+                }}
+              />
+              background
+            </label>
+            <label>
+              <input
+                type="checkbox"
+                checked={this.state.exportVisibleOnly}
+                onChange={e => {
+                  this.setState({ exportVisibleOnly: e.target.checked });
+                }}
+              />
+              visible area only
+            </label>
+            <div>
+              (padding:
+              <input
+                type="number"
+                value={this.state.exportPadding}
+                onChange={e => {
+                  this.setState({ exportPadding: Number(e.target.value) });
+                }}
+                disabled={!this.state.exportVisibleOnly}
+              />
+              px)
+            </div>
+          </div>
+        </div>
         <canvas
           id="canvas"
-          width={window.innerWidth}
-          height={window.innerHeight - 210}
+          width={window.innerWidth - 250}
+          height={window.innerHeight}
           onWheel={e => {
             e.preventDefault();
             const { deltaX, deltaY } = e;
@@ -958,84 +1050,6 @@ class App extends React.Component<{}, AppState> {
             this.forceUpdate();
           }}
         />
-        <fieldset>
-          <legend>Colors</legend>
-          <label>
-            <input
-              type="color"
-              value={this.state.viewBackgroundColor}
-              onChange={e => {
-                this.setState({ viewBackgroundColor: e.target.value });
-              }}
-            />
-            Background
-          </label>
-          <label>
-            <input
-              type="color"
-              value={this.state.currentItemStrokeColor}
-              onChange={e => {
-                this.setState({ currentItemStrokeColor: e.target.value });
-              }}
-            />
-            Shape Stroke
-          </label>
-          <label>
-            <input
-              type="color"
-              value={this.state.currentItemBackgroundColor}
-              onChange={e => {
-                this.setState({ currentItemBackgroundColor: e.target.value });
-              }}
-            />
-            Shape Background
-          </label>
-        </fieldset>
-        <fieldset>
-          <legend>Export</legend>
-          <button
-            onClick={() => {
-              exportAsPNG({
-                exportBackground: this.state.exportBackground,
-                exportVisibleOnly: this.state.exportVisibleOnly,
-                exportPadding: this.state.exportPadding,
-                viewBackgroundColor: this.state.viewBackgroundColor
-              });
-            }}
-          >
-            Export to png
-          </button>
-          <label>
-            <input
-              type="checkbox"
-              checked={this.state.exportBackground}
-              onChange={e => {
-                this.setState({ exportBackground: e.target.checked });
-              }}
-            />
-            background
-          </label>
-          <label>
-            <input
-              type="checkbox"
-              checked={this.state.exportVisibleOnly}
-              onChange={e => {
-                this.setState({ exportVisibleOnly: e.target.checked });
-              }}
-            />
-            visible area only
-          </label>
-          (padding:
-          <input
-            type="number"
-            value={this.state.exportPadding}
-            onChange={e => {
-              this.setState({ exportPadding: Number(e.target.value) });
-            }}
-            disabled={!this.state.exportVisibleOnly}
-          />
-          px)
-        </fieldset>
       </div>
     );
   }

+ 53 - 9
src/styles.css

@@ -7,25 +7,62 @@
 
 body {
   margin: 0;
+  font-family: Arial, Helvetica, sans-serif;
 }
 
-/* Controls - Begin */
-fieldset {
-  margin: 5px;
+.container {
+  display: flex;
+}
+
+.sidePanel {
+  width: 230px;
+  background-color: #eee;
+
+  padding: 10px;
+}
+
+.sidePanel h4 {
+  margin: 10px 0 10px 0;
+}
+
+.sidePanel .panelTools {
+  display: flex;
+}
+
+.sidePanel .panelColumn {
+  display: flex;
+  flex-direction: column;
+}
+
+.tool input[type="radio"] {
+  display: none;
+}
+
+.tool input[type="radio"] + .toolIcon {
+  background-color: #ddd;
+
+  width: 41px;
+  height: 41px;
+
+  display: flex;
+  justify-content: center;
+  align-items: center;
+
+  border-radius: 3px;
+}
+
+.tool input[type="radio"]:checked + .toolIcon {
+  background-color: #bdbebc;
 }
 
 label {
-  margin-right: 10px;
+  margin-right: 6px;
 }
 
 label span {
   display: inline-block;
 }
 
-label span::first-letter {
-  text-decoration: underline;
-}
-
 input[type="number"] {
   width: 30px;
 }
@@ -33,4 +70,11 @@ input[type="number"] {
 input {
   margin-right: 5px;
 }
-/* Controls - End */
+
+button {
+  background-color: #ddd;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+
+  padding: 5px;
+}