Use separate inputs for the RGB color channels
authorAlexander Ebert <ebert@woltlab.com>
Sun, 29 May 2022 18:06:26 +0000 (20:06 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Sun, 29 May 2022 18:06:26 +0000 (20:06 +0200)
ts/WoltLabSuite/Core/Ui/Color/Picker.ts
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Color/Picker.js
wcfsetup/install/files/style/ui/colorPicker.scss

index 89eeb1979ede7c0b2703a9c962032a29ef6417b2..cb564b5c690d7d007ed81c937a7e087252d2c2dd 100644 (file)
@@ -18,12 +18,19 @@ import * as ColorUtil from "../../ColorUtil";
 
 type CallbackSubmit = (data: ColorUtil.RGBA) => void;
 
+const enum Channel {
+  R,
+  G,
+  B,
+}
+
 interface ColorPickerOptions {
   callbackSubmit: CallbackSubmit;
 }
 
 class UiColorPicker implements DialogCallbackObject {
   protected alphaInput: HTMLInputElement | null = null;
+  private readonly channels = new Map<Channel, HTMLInputElement>();
   protected colorInput: HTMLInputElement | null = null;
   protected colorTextInput: HTMLInputElement | null = null;
   protected readonly element: HTMLElement;
@@ -68,7 +75,18 @@ class UiColorPicker implements DialogCallbackObject {
       <dl>
         <dt>${Language.get("wcf.style.colorPicker.color")}</dt>
         <dd>
-          <input type="color">
+          <div class="inputAddon colorPickerChannel">
+            <span class="inputPrefix">R</span>
+            <input type="number" min="0" max="255" data-channel="r">
+          </div>
+          <div class="inputAddon colorPickerChannel">
+            <span class="inputPrefix">G</span>
+            <input type="number" min="0" max="255" data-channel="g">
+          </div>
+          <div class="inputAddon colorPickerChannel">
+            <span class="inputPrefix">B</span>
+            <input type="number" min="0" max="255" data-channel="b">
+          </div>
         </dd>
       </dl>
       <dl>
@@ -104,8 +122,13 @@ class UiColorPicker implements DialogCallbackObject {
 </div>`,
       options: {
         onSetup: (content) => {
-          this.colorInput = content.querySelector("input[type=color]") as HTMLInputElement;
-          this.colorInput.addEventListener("input", () => this.updateColor());
+          this.channels.set(Channel.R, content.querySelector('input[data-channel="r"]') as HTMLInputElement);
+          this.channels.set(Channel.G, content.querySelector('input[data-channel="g"]') as HTMLInputElement);
+          this.channels.set(Channel.B, content.querySelector('input[data-channel="b"]') as HTMLInputElement);
+          this.channels.forEach((input) => {
+            input.addEventListener("input", () => this.updateColor());
+          });
+
           this.alphaInput = content.querySelector("input[type=range]") as HTMLInputElement;
           this.alphaInput.addEventListener("input", () => this.updateColor());
 
@@ -182,10 +205,12 @@ class UiColorPicker implements DialogCallbackObject {
    * @since 5.5
    */
   protected getColor(): ColorUtil.RGBA {
-    const color = this.colorInput!.value;
-    const alpha = this.alphaInput!.value;
-
-    return { ...(ColorUtil.hexToRgb(color) as ColorUtil.RGB), a: +alpha };
+    return {
+      r: parseInt(this.channels.get(Channel.R)!.value, 10),
+      g: parseInt(this.channels.get(Channel.G)!.value, 10),
+      b: parseInt(this.channels.get(Channel.B)!.value, 10),
+      a: parseInt(this.alphaInput!.value, 10),
+    };
   }
 
   /**
@@ -207,7 +232,9 @@ class UiColorPicker implements DialogCallbackObject {
       color = ColorUtil.stringToRgba(color);
     }
 
-    this.colorInput!.value = `#${ColorUtil.rgbToHex(color.r, color.g, color.b)}`;
+    this.channels.get(Channel.R)!.value = color.r.toString();
+    this.channels.get(Channel.G)!.value = color.g.toString();
+    this.channels.get(Channel.B)!.value = color.b.toString();
     this.alphaInput!.value = color.a.toString();
 
     this.newColor!.style.backgroundColor = ColorUtil.rgbaToString(color);
index e2642d3c186c0c1603a881c6bbf6b20a6360e6a7..9fcd975eee97498bbc5b70be0395586f14f5017b 100644 (file)
@@ -21,6 +21,7 @@ define(["require", "exports", "tslib", "../../Core", "../Dialog", "../../Dom/Uti
          */
         constructor(element, options) {
             this.alphaInput = null;
+            this.channels = new Map();
             this.colorInput = null;
             this.colorTextInput = null;
             this.newColor = null;
@@ -48,7 +49,18 @@ define(["require", "exports", "tslib", "../../Core", "../Dialog", "../../Dom/Uti
       <dl>
         <dt>${Language.get("wcf.style.colorPicker.color")}</dt>
         <dd>
-          <input type="color">
+          <div class="inputAddon colorPickerChannel">
+            <span class="inputPrefix">R</span>
+            <input type="number" min="0" max="255" data-channel="r">
+          </div>
+          <div class="inputAddon colorPickerChannel">
+            <span class="inputPrefix">G</span>
+            <input type="number" min="0" max="255" data-channel="g">
+          </div>
+          <div class="inputAddon colorPickerChannel">
+            <span class="inputPrefix">B</span>
+            <input type="number" min="0" max="255" data-channel="b">
+          </div>
         </dd>
       </dl>
       <dl>
@@ -84,8 +96,12 @@ define(["require", "exports", "tslib", "../../Core", "../Dialog", "../../Dom/Uti
 </div>`,
                 options: {
                     onSetup: (content) => {
-                        this.colorInput = content.querySelector("input[type=color]");
-                        this.colorInput.addEventListener("input", () => this.updateColor());
+                        this.channels.set(0 /* R */, content.querySelector('input[data-channel="r"]'));
+                        this.channels.set(1 /* G */, content.querySelector('input[data-channel="g"]'));
+                        this.channels.set(2 /* B */, content.querySelector('input[data-channel="b"]'));
+                        this.channels.forEach((input) => {
+                            input.addEventListener("input", () => this.updateColor());
+                        });
                         this.alphaInput = content.querySelector("input[type=range]");
                         this.alphaInput.addEventListener("input", () => this.updateColor());
                         this.newColor = content.querySelector(".colorPickerColorNew > span");
@@ -154,9 +170,12 @@ define(["require", "exports", "tslib", "../../Core", "../Dialog", "../../Dom/Uti
          * @since 5.5
          */
         getColor() {
-            const color = this.colorInput.value;
-            const alpha = this.alphaInput.value;
-            return Object.assign(Object.assign({}, ColorUtil.hexToRgb(color)), { a: +alpha });
+            return {
+                r: parseInt(this.channels.get(0 /* R */).value, 10),
+                g: parseInt(this.channels.get(1 /* G */).value, 10),
+                b: parseInt(this.channels.get(2 /* B */).value, 10),
+                a: parseInt(this.alphaInput.value, 10),
+            };
         }
         /**
          * Opens the color picker after clicking on the picker button.
@@ -175,7 +194,9 @@ define(["require", "exports", "tslib", "../../Core", "../Dialog", "../../Dom/Uti
             if (typeof color === "string") {
                 color = ColorUtil.stringToRgba(color);
             }
-            this.colorInput.value = `#${ColorUtil.rgbToHex(color.r, color.g, color.b)}`;
+            this.channels.get(0 /* R */).value = color.r.toString();
+            this.channels.get(1 /* G */).value = color.g.toString();
+            this.channels.get(2 /* B */).value = color.b.toString();
             this.alphaInput.value = color.a.toString();
             this.newColor.style.backgroundColor = ColorUtil.rgbaToString(color);
             this.colorTextInput.value = ColorUtil.rgbaToHex(color);
index b5b5cde44bd280bbeed14f138b9885d902180adc..fde7af87a73a594f43592f6a7f5d1261d6a5eb07 100644 (file)
                border-top-width: 0;
        }
 }
+
+.colorPickerChannel {
+       display: inline-flex;
+}