فهرست منبع

Improve Color Picker *2 (#195)

Christopher Chedeau 5 سال پیش
والد
کامیت
3bbcb9cbdc
2فایلهای تغییر یافته به همراه78 افزوده شده و 204 حذف شده
  1. 76 203
      src/index.tsx
  2. 2 1
      src/styles.scss

+ 76 - 203
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(
-    new Set(
-      elements
-        .filter(element => element.isSelected)
-        .map(element => element.fillStyle)
-    )
-  );
-  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(
+function getSelectedAttribute<T>(
+  getAttribute: (element: ExcalidrawElement) => T
+): T | null {
+  const attributes = Array.from(
     new Set(
       elements
         .filter(element => element.isSelected)
-        .map(element => element.backgroundColor)
+        .map(element => getAttribute(element))
     )
   );
-  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"

+ 2 - 1
src/styles.scss

@@ -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;
     }