Add support for exclusive selections
authorAlexander Ebert <ebert@woltlab.com>
Tue, 3 Oct 2023 14:36:02 +0000 (16:36 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Thu, 5 Oct 2023 15:22:07 +0000 (17:22 +0200)
ts/WoltLabSuite/Core/Element/woltlab-core-menu-group.ts
ts/WoltLabSuite/Core/Element/woltlab-core-menu-item.ts
wcfsetup/install/files/js/WoltLabSuite/Core/Element/woltlab-core-menu-group.js
wcfsetup/install/files/js/WoltLabSuite/Core/Element/woltlab-core-menu-item.js

index 455cb89fd4c07f7a49ff987a943e1a6e0f640145..39cd8d94929ab19abcd207cbab43a2569578a203 100644 (file)
@@ -18,9 +18,19 @@ export class WoltlabCoreMenuGroupElement extends HTMLElement {
 
         this.#items.add(element);
 
-        element.setRole("menuitemcheckbox");
+        if (this.multiple) {
+          element.setRole("menuitemcheckbox");
+        } else {
+          element.setRole("menuitemradio");
+        }
 
         element.addEventListener("change", () => {
+          if (!this.multiple) {
+            this.#items.forEach((item) => {
+              item.selected = item === element;
+            });
+          }
+
           this.#updateValue();
         });
       }
@@ -33,6 +43,10 @@ export class WoltlabCoreMenuGroupElement extends HTMLElement {
     this.label = this.getAttribute("label")!;
   }
 
+  get multiple(): boolean {
+    return this.hasAttribute("multiple");
+  }
+
   get label(): string {
     return this.getAttribute("label")!;
   }
index 824bc3e6e7a940918ac094602477bc938f452012..7c8ea82531ad61cb739e143ff1bdc4dd7d1d9a7c 100644 (file)
@@ -1,4 +1,4 @@
-type Role = "menuitem" | "menuitemcheckbox";
+type Role = "menuitem" | "menuitemcheckbox" | "menuitemradio";
 
 interface WoltlabCoreMenuItemEventMap {
   beforeSelect: CustomEvent;
@@ -17,6 +17,11 @@ export class WoltlabCoreMenuItemElement extends HTMLElement {
         return;
       }
 
+      const role = this.getAttribute("role") as Role;
+      if (role === "menuitemradio" && this.selected) {
+        return;
+      }
+
       const evt = new CustomEvent("beforeSelect", {
         cancelable: true,
       });
@@ -88,7 +93,7 @@ export class WoltlabCoreMenuItemElement extends HTMLElement {
 
     if (role === "menuitem") {
       this.#checkmark?.remove();
-    } else if (role === "menuitemcheckbox") {
+    } else if (role === "menuitemcheckbox" || role === "menuitemradio") {
       if (this.#checkmark === undefined) {
         this.#checkmark = document.createElement("fa-icon");
         this.#checkmark.setIcon("check");
@@ -101,7 +106,7 @@ export class WoltlabCoreMenuItemElement extends HTMLElement {
 
   #updateAriaSelected(): void {
     const role = this.getAttribute("role") as Role;
-    if (role === "menuitemcheckbox") {
+    if (role === "menuitemcheckbox" || role === "menuitemradio") {
       this.setAttribute("aria-checked", String(this.selected === true));
     }
   }
index 256097c83c998b948513f0624bc03bbfb2f5b168..d6390089a527898ee25d6adf31338704820c9b05 100644 (file)
@@ -17,8 +17,18 @@ define(["require", "exports", "tslib", "./woltlab-core-menu-item"], function (re
                         continue;
                     }
                     this.#items.add(element);
-                    element.setRole("menuitemcheckbox");
+                    if (this.multiple) {
+                        element.setRole("menuitemcheckbox");
+                    }
+                    else {
+                        element.setRole("menuitemradio");
+                    }
                     element.addEventListener("change", () => {
+                        if (!this.multiple) {
+                            this.#items.forEach((item) => {
+                                item.selected = item === element;
+                            });
+                        }
                         this.#updateValue();
                     });
                 }
@@ -27,6 +37,9 @@ define(["require", "exports", "tslib", "./woltlab-core-menu-item"], function (re
             this.setAttribute("role", "group");
             this.label = this.getAttribute("label");
         }
+        get multiple() {
+            return this.hasAttribute("multiple");
+        }
         get label() {
             return this.getAttribute("label");
         }
index bb28a320bd64cd3068713073d53a4209c4ce805f..19ae4c19066c05e2971978a0927f0d4c9f36201e 100644 (file)
@@ -11,6 +11,10 @@ define(["require", "exports"], function (require, exports) {
                 if (this.disabled) {
                     return;
                 }
+                const role = this.getAttribute("role");
+                if (role === "menuitemradio" && this.selected) {
+                    return;
+                }
                 const evt = new CustomEvent("beforeSelect", {
                     cancelable: true,
                 });
@@ -69,7 +73,7 @@ define(["require", "exports"], function (require, exports) {
             if (role === "menuitem") {
                 this.#checkmark?.remove();
             }
-            else if (role === "menuitemcheckbox") {
+            else if (role === "menuitemcheckbox" || role === "menuitemradio") {
                 if (this.#checkmark === undefined) {
                     this.#checkmark = document.createElement("fa-icon");
                     this.#checkmark.setIcon("check");
@@ -80,7 +84,7 @@ define(["require", "exports"], function (require, exports) {
         }
         #updateAriaSelected() {
             const role = this.getAttribute("role");
-            if (role === "menuitemcheckbox") {
+            if (role === "menuitemcheckbox" || role === "menuitemradio") {
                 this.setAttribute("aria-checked", String(this.selected === true));
             }
         }