Handle changes to the requested size of an icon
authorAlexander Ebert <ebert@woltlab.com>
Wed, 10 Aug 2022 10:53:37 +0000 (12:53 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Fri, 12 Aug 2022 19:25:55 +0000 (21:25 +0200)
ts/WoltLabSuite/WebComponent/fa-brand.ts
ts/WoltLabSuite/WebComponent/fa-icon.ts
wcfsetup/install/files/js/WoltLabSuite/WebComponent/fa-brand.js
wcfsetup/install/files/js/WoltLabSuite/WebComponent/fa-icon.js
wcfsetup/install/files/lib/system/template/plugin/IconFunctionTemplatePlugin.class.php

index 3ea5ea07e3c1ab8cca31bac5d1402b4b82ceee0e..9c883e569df8657b35d4127dae7cb6b9b7606d5f 100644 (file)
@@ -1,5 +1,7 @@
 (() => {
-  const HeightMap = new Map<number, number>([
+  type IconSize = number;
+  type RenderSize = number;
+  const HeightMap = new Map<IconSize, RenderSize>([
     [16, 14],
     [24, 18],
     [32, 28],
   ]);
 
   class FaBrand extends HTMLElement {
+    private root?: ShadowRoot = undefined;
+    private svgStyle: HTMLStyleElement = document.createElement("style");
+
     connectedCallback() {
       this.validate();
 
-      const root = this.prepareRoot();
+      const root = this.getRoot();
 
       const slot = document.createElement("slot");
       slot.name = "svg";
       }
     }
 
-    private prepareRoot(): ShadowRoot {
-      const root = this.attachShadow({ mode: "open" });
+    private getRoot(): ShadowRoot {
+      if (this.root === undefined) {
+        this.root = this.attachShadow({ mode: "open" });
+
+        this.updateRenderSize();
+        this.root.append(this.svgStyle);
+      }
+
+      return this.root;
+    }
 
-      const iconHeight = HeightMap.get(this.size)!;
-      const style = document.createElement("style");
-      style.textContent = `
+    private updateRenderSize(): void {
+      const renderSize = HeightMap.get(this.size)!;
+      this.svgStyle.textContent = `
         ::slotted(svg) {
           fill: currentColor;
-          height: ${iconHeight}px;
+          height: ${renderSize}px;
           shape-rendering: geometricprecision;
         }
       `;
-      root.append(style);
-
-      return root;
     }
 
-    get size(): number {
+    get size(): IconSize {
       const size = this.getAttribute("size");
       if (size === null) {
         return 0;
 
       return parseInt(size);
     }
+
+    set size(size: number) {
+      if (!HeightMap.has(size)) {
+        throw new Error(`Refused to set the invalid icon size '${size}'.`);
+      }
+
+      this.setAttribute("size", size.toString());
+      this.updateRenderSize();
+    }
   }
 
   window.customElements.define("fa-brand", FaBrand);
index 913e5b7f2422900fa5d0600452f717434ae9a33c..2df931ed6dc274115caaac6f22771b34a0215b9d 100644 (file)
@@ -13,7 +13,9 @@
     return isFA6Free;
   }
 
-  const HeightMap = new Map<number, number>([
+  type IconSize = number;
+  type RenderSize = number;
+  const HeightMap = new Map<IconSize, RenderSize>([
     [16, 14],
     [24, 18],
     [32, 28],
       this.setAttribute("name", name);
     }
 
-    get size(): number {
+    get size(): IconSize {
       const size = this.getAttribute("size");
       if (size === null) {
         return 0;
       return parseInt(size);
     }
 
+    set size(size: number) {
+      if (!HeightMap.has(size)) {
+        throw new Error(`Refused to set the invalid icon size '${size}'.`);
+      }
+
+      this.setAttribute("size", size.toString());
+    }
+
     static get observedAttributes() {
       return ["name"];
     }
index 455e957092fd5b0f660926a5faeaf84b282a0bc7..d538919009d6c3ae6a3a1427803433b48aba5851 100644 (file)
         [144, 130],
     ]);
     class FaBrand extends HTMLElement {
+        constructor() {
+            super(...arguments);
+            this.root = undefined;
+            this.svgStyle = document.createElement("style");
+        }
         connectedCallback() {
             this.validate();
-            const root = this.prepareRoot();
+            const root = this.getRoot();
             const slot = document.createElement("slot");
             slot.name = "svg";
             root.append(slot);
                 throw new TypeError("Must provide a valid icon size.");
             }
         }
-        prepareRoot() {
-            const root = this.attachShadow({ mode: "open" });
-            const iconHeight = HeightMap.get(this.size);
-            const style = document.createElement("style");
-            style.textContent = `
+        getRoot() {
+            if (this.root === undefined) {
+                this.root = this.attachShadow({ mode: "open" });
+                this.updateRenderSize();
+                this.root.append(this.svgStyle);
+            }
+            return this.root;
+        }
+        updateRenderSize() {
+            const renderSize = HeightMap.get(this.size);
+            this.svgStyle.textContent = `
         ::slotted(svg) {
           fill: currentColor;
-          height: ${iconHeight}px;
+          height: ${renderSize}px;
           shape-rendering: geometricprecision;
         }
       `;
-            root.append(style);
-            return root;
         }
         get size() {
             const size = this.getAttribute("size");
             }
             return parseInt(size);
         }
+        set size(size) {
+            if (!HeightMap.has(size)) {
+                throw new Error(`Refused to set the invalid icon size '${size}'.`);
+            }
+            this.setAttribute("size", size.toString());
+            this.updateRenderSize();
+        }
     }
     window.customElements.define("fa-brand", FaBrand);
 })();
index e6e379f8c8c0b21284db2ac7ebf2f2eebe5feb1a..044de90923ddca1cf57434da102ac7e739014685 100644 (file)
             }
             return parseInt(size);
         }
+        set size(size) {
+            if (!HeightMap.has(size)) {
+                throw new Error(`Refused to set the invalid icon size '${size}'.`);
+            }
+            this.setAttribute("size", size.toString());
+        }
         static get observedAttributes() {
             return ["name"];
         }
index a46911c97943bb0f9e588a72cf5b72f8c4adba18..783a86eddb9c833b1a746e3d8c36e8fb246d68f9 100644 (file)
@@ -33,21 +33,21 @@ final class IconFunctionTemplatePlugin implements IFunctionTemplatePlugin
         $type = $tagArgs['type'] ?? '';
 
         if (!\in_array($size, self::SIZES)) {
-            throw new \InvalidArgumentException("An unsupported size `{$size}` was requested.");
+            throw new \InvalidArgumentException("An unsupported size '{$size}' was requested.");
         }
 
         if ($name === '') {
-            throw new \InvalidArgumentException("The `name` attribute must be present and non-empty.");
+            throw new \InvalidArgumentException("The 'name' attribute must be present and non-empty.");
         }
 
         if ($type !== '' && !\in_array($type, self::TYPES)) {
-            throw new \InvalidArgumentException("An unsupported type `${type}` was specified.");
+            throw new \InvalidArgumentException("An unsupported type '{$type}' was specified.");
         }
 
         if ($type === 'brand') {
             $svgFile = \WCF_DIR . "icon/font-awesome/v6/brands/{$name}.svg";
             if (!\file_exists($svgFile)) {
-                throw new \InvalidArgumentException("Unable to locate the icon for brand `${name}`.");
+                throw new \InvalidArgumentException("Unable to locate the icon for brand '{$name}'.");
             }
 
             $content = \file_get_contents($svgFile);