瀏覽代碼

Add ColorInput component (#455)

* Add ColorInput component

* Use valid color on input blur

* Darken input text and add labels
Guillermo Peralta Scura 5 年之前
父節點
當前提交
7ae52f1164
共有 2 個文件被更改,包括 96 次插入58 次删除
  1. 19 8
      src/components/ColorPicker.css
  2. 77 50
      src/components/ColorPicker.tsx

+ 19 - 8
src/components/ColorPicker.css

@@ -7,6 +7,11 @@
   position: relative;
 }
 
+.color-picker-control-container {
+  display: flex;
+  align-items: center;
+}
+
 .color-picker-triangle-shadow {
   width: 0px;
   height: 0px;
@@ -33,13 +38,17 @@
   padding: 15px 9px 9px 15px;
 }
 
+.colors-gallery {
+  display: flex;
+  flex-wrap: wrap;
+}
+
 .color-picker-swatch {
+  position: relative;
   height: 30px;
   width: 30px;
   cursor: pointer;
-  position: relative;
   outline: none;
-  float: left;
   border-radius: 4px;
   margin: 0px 6px 6px 0px;
   box-sizing: border-box;
@@ -69,17 +78,20 @@
   height: 30px;
   width: 30px;
   border-radius: 4px 0px 0px 4px;
-  float: left;
-  color: #868e96;
+  color: #495057;
   display: flex;
   align-items: center;
   justify-content: center;
 }
 
+.color-input-container {
+  display: flex;
+}
+
 .color-picker-input {
   width: 100px;
   font-size: 14px;
-  color: #868e96;
+  color: #343a40;
   border: 0px;
   outline: none;
   height: 28px;
@@ -92,9 +104,8 @@
 }
 
 .color-picker-label-swatch {
-  height: 24px;
-  width: 24px;
-  display: inline-block;
+  height: 30px;
+  width: 30px;
   margin-right: 4px;
   border: 1px solid #dee2e6;
 }

+ 77 - 50
src/components/ColorPicker.tsx

@@ -15,71 +15,105 @@ const Picker = function({
   color: string | undefined;
   onChange: (color: string) => void;
 }) {
-  const [innerValue, setInnerValue] = React.useState(color);
-  React.useEffect(() => {
-    setInnerValue(color);
-  }, [color]);
   return (
     <div className="color-picker">
       <div className="color-picker-triangle-shadow"></div>
       <div className="color-picker-triangle"></div>
       <div className="color-picker-content">
-        {colors.map(color => (
-          <div
-            className="color-picker-swatch"
-            onClick={() => {
-              onChange(color);
-            }}
-            title={color}
-            tabIndex={0}
-            style={{ backgroundColor: color }}
-            key={color}
-          >
-            {color === "transparent" ? (
-              <div className="color-picker-transparent"></div>
-            ) : (
-              undefined
-            )}
-          </div>
-        ))}
-        <div className="color-picker-hash">#</div>
-        <div style={{ position: "relative" }}>
-          <input
-            spellCheck={false}
-            className="color-picker-input"
-            onChange={e => {
-              const value = e.target.value;
-              if (value.match(/^([0-9a-f]{3}|[0-9a-f]{6}|transparent)$/)) {
-                onChange(value === "transparent" ? "transparent" : "#" + value);
-              }
-              setInnerValue(value);
-            }}
-            value={(innerValue || "").replace(/^#/, "")}
-          />
+        <div className="colors-gallery">
+          {colors.map(color => (
+            <div
+              className="color-picker-swatch"
+              onClick={() => {
+                onChange(color);
+              }}
+              title={color}
+              tabIndex={0}
+              style={{ backgroundColor: color }}
+              key={color}
+            >
+              {color === "transparent" ? (
+                <div className="color-picker-transparent"></div>
+              ) : (
+                undefined
+              )}
+            </div>
+          ))}
         </div>
-        <div style={{ clear: "both" }}></div>
+        <ColorInput
+          color={color}
+          onChange={color => {
+            onChange(color);
+          }}
+        />
       </div>
     </div>
   );
 };
 
+function ColorInput({
+  color,
+  onChange
+}: {
+  color: string | undefined;
+  onChange: (color: string) => void;
+}) {
+  const colorRegex = /^([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8}|transparent)$/;
+  const [innerValue, setInnerValue] = React.useState(color);
+
+  React.useEffect(() => {
+    setInnerValue(color);
+  }, [color]);
+
+  return (
+    <div className="color-input-container">
+      <div className="color-picker-hash">#</div>
+      <input
+        spellCheck={false}
+        className="color-picker-input"
+        aria-label="Hex color code"
+        onChange={e => {
+          const value = e.target.value;
+          if (value.match(colorRegex)) {
+            onChange(value === "transparent" ? "transparent" : "#" + value);
+          }
+          setInnerValue(value);
+        }}
+        value={(innerValue || "").replace(/^#/, "")}
+        onPaste={e => onChange(e.clipboardData.getData("text"))}
+        onBlur={() => setInnerValue(color)}
+      />
+    </div>
+  );
+}
+
 export function ColorPicker({
   type,
   color,
   onChange
 }: {
   type: "canvasBackground" | "elementBackground" | "elementStroke";
-  color: string | null;
+  color: string | undefined;
   onChange: (color: string) => void;
 }) {
   const [isActive, setActive] = React.useState(false);
+
   return (
     <div>
-      <button
-        className="color-picker-label-swatch"
-        style={color ? { backgroundColor: color } : undefined}
-        onClick={() => setActive(!isActive)}
-      />
+      <div className="color-picker-control-container">
+        <button
+          className="color-picker-label-swatch"
+          aria-label="Change color"
+          style={color ? { backgroundColor: color } : undefined}
+          onClick={() => setActive(!isActive)}
+        />
+        <ColorInput
+          color={color}
+          onChange={color => {
+            onChange(color);
+          }}
+        />
+      </div>
       <React.Suspense fallback="">
         {isActive ? (
           <Popover onCloseRequest={() => setActive(false)}>
@@ -93,13 +127,6 @@ export function ColorPicker({
           </Popover>
         ) : null}
       </React.Suspense>
-      <input
-        type="text"
-        className="color-picker-swatch-input"
-        value={color || ""}
-        onPaste={e => onChange(e.clipboardData.getData("text"))}
-        onChange={e => onChange(e.target.value)}
-      />
     </div>
   );
 }