Use a native button element for mobile message options
authorAlexander Ebert <ebert@woltlab.com>
Wed, 24 Aug 2022 12:27:05 +0000 (14:27 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Wed, 24 Aug 2022 12:27:05 +0000 (14:27 +0200)
ts/WoltLabSuite/Core/Ui/Mobile.ts
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Mobile.js
wcfsetup/install/files/style/ui/message.scss
wcfsetup/install/files/style/ui/wsc31.scss

index 2e7b8cc2fd8cff905df83185c031819999486404..d1bbb250417c65514bda353521357b7ffb066d74 100644 (file)
@@ -40,7 +40,6 @@ function init(): void {
 
   initButtonGroupNavigation();
   initMessages();
-  initMessagesA11y();
   initMobileMenu();
 
   UiCloseOverlay.add("WoltLabSuite/Core/Ui/Mobile", closeAllMenus);
@@ -88,8 +87,6 @@ function initButtonGroupNavigation(): void {
 }
 
 function initMessages(): void {
-  const isScreenSmDown = UiScreen.is("screen-sm-down");
-
   document.querySelectorAll(".message").forEach((message: HTMLElement) => {
     if (_knownMessages.has(message)) {
       return;
@@ -109,75 +106,31 @@ function initMessages(): void {
       const quickOptions = message.querySelector(".messageQuickOptions") as HTMLElement;
       if (quickOptions && navigation.childElementCount) {
         quickOptions.classList.add("active");
-        quickOptions.addEventListener("click", (event) => {
-          const target = event.target as HTMLElement;
 
-          if (_enabled && UiScreen.is("screen-sm-down") && target.nodeName !== "LABEL" && target.nodeName !== "INPUT") {
-            event.preventDefault();
-            event.stopPropagation();
+        let buttonWrapper = quickOptions.querySelector(".messageQuickOptionsMobile");
+        if (buttonWrapper === null) {
+          buttonWrapper = document.createElement("li");
+          buttonWrapper.innerHTML = `
+            <button aria-label="${Language.get("wcf.global.button.more")}">
+              <fa-icon size="16" name="ellipsis-vertical"></fa-icon>
+            </button>
+          `;
+          buttonWrapper.classList.add("messageQuickOptionsMobile");
+          quickOptions.append(buttonWrapper);
+        }
 
-            toggleMobileNavigation(message, quickOptions, navigation);
-          }
-        });
-        quickOptions.addEventListener("keydown", (event) => {
-          if (event.key === "Enter") {
-            event.preventDefault();
+        const button = buttonWrapper.querySelector("button")!;
+        button.addEventListener("click", (event) => {
+          event.stopPropagation();
 
-            quickOptions.click();
-          }
+          toggleMobileNavigation(message, quickOptions, navigation);
         });
-
-        if (isScreenSmDown) {
-          enableMessageA11y(quickOptions);
-        }
       }
     }
     _knownMessages.add(message);
   });
 }
 
-function enableMessageA11y(quickOptions: HTMLElement): void {
-  quickOptions.tabIndex = 0;
-  quickOptions.setAttribute("role", "button");
-  quickOptions.setAttribute("aria-label", Language.get("wcf.global.button.more"));
-}
-
-function disableMessageA11y(quickOptions: HTMLElement): void {
-  quickOptions.removeAttribute("tabindex");
-  quickOptions.removeAttribute("role");
-  quickOptions.removeAttribute("aria-label");
-}
-
-function initMessagesA11y(): void {
-  UiScreen.on("screen-sm-down", {
-    match() {
-      document.querySelectorAll(".message").forEach((message: HTMLElement) => {
-        const navigation = message.querySelector(".jsMobileNavigation") as HTMLAnchorElement;
-        if (navigation) {
-          const quickOptions = message.querySelector(".messageQuickOptions") as HTMLElement;
-          if (quickOptions && navigation.childElementCount) {
-            enableMessageA11y(quickOptions);
-          }
-        }
-      });
-    },
-    unmatch() {
-      document.querySelectorAll(".message").forEach((message: HTMLElement) => {
-        if (!_knownMessages.has(message)) {
-          return;
-        }
-
-        const navigation = message.querySelector(".jsMobileNavigation") as HTMLAnchorElement;
-        if (navigation) {
-          const quickOptions = message.querySelector(".messageQuickOptions") as HTMLElement;
-          if (quickOptions && navigation.childElementCount) {
-            disableMessageA11y(quickOptions);
-          }
-        }
-      });
-    },
-  });
-}
 function initMobileMenu(): void {
   if (_enableMobileMenu) {
     _pageMenuMain = new PageMenuMain(_pageMenuMainProvider);
index 9d10772a3ecc82fbe504e6a455a6b44967c5e0cd..167ac2ac612880c4d795eccea460cb571c988d4d 100644 (file)
@@ -36,7 +36,6 @@ define(["require", "exports", "tslib", "focus-trap", "../Core", "../Dom/Change/L
         _enabled = true;
         initButtonGroupNavigation();
         initMessages();
-        initMessagesA11y();
         initMobileMenu();
         CloseOverlay_1.default.add("WoltLabSuite/Core/Ui/Mobile", closeAllMenus);
         Listener_1.default.add("WoltLabSuite/Core/Ui/Mobile", () => {
@@ -75,7 +74,6 @@ define(["require", "exports", "tslib", "focus-trap", "../Core", "../Dom/Change/L
         });
     }
     function initMessages() {
-        const isScreenSmDown = UiScreen.is("screen-sm-down");
         document.querySelectorAll(".message").forEach((message) => {
             if (_knownMessages.has(message)) {
                 return;
@@ -92,67 +90,27 @@ define(["require", "exports", "tslib", "focus-trap", "../Core", "../Dom/Change/L
                 const quickOptions = message.querySelector(".messageQuickOptions");
                 if (quickOptions && navigation.childElementCount) {
                     quickOptions.classList.add("active");
-                    quickOptions.addEventListener("click", (event) => {
-                        const target = event.target;
-                        if (_enabled && UiScreen.is("screen-sm-down") && target.nodeName !== "LABEL" && target.nodeName !== "INPUT") {
-                            event.preventDefault();
-                            event.stopPropagation();
-                            toggleMobileNavigation(message, quickOptions, navigation);
-                        }
-                    });
-                    quickOptions.addEventListener("keydown", (event) => {
-                        if (event.key === "Enter") {
-                            event.preventDefault();
-                            quickOptions.click();
-                        }
-                    });
-                    if (isScreenSmDown) {
-                        enableMessageA11y(quickOptions);
+                    let buttonWrapper = quickOptions.querySelector(".messageQuickOptionsMobile");
+                    if (buttonWrapper === null) {
+                        buttonWrapper = document.createElement("li");
+                        buttonWrapper.innerHTML = `
+            <button aria-label="${Language.get("wcf.global.button.more")}">
+              <fa-icon size="16" name="ellipsis-vertical"></fa-icon>
+            </button>
+          `;
+                        buttonWrapper.classList.add("messageQuickOptionsMobile");
+                        quickOptions.append(buttonWrapper);
                     }
+                    const button = buttonWrapper.querySelector("button");
+                    button.addEventListener("click", (event) => {
+                        event.stopPropagation();
+                        toggleMobileNavigation(message, quickOptions, navigation);
+                    });
                 }
             }
             _knownMessages.add(message);
         });
     }
-    function enableMessageA11y(quickOptions) {
-        quickOptions.tabIndex = 0;
-        quickOptions.setAttribute("role", "button");
-        quickOptions.setAttribute("aria-label", Language.get("wcf.global.button.more"));
-    }
-    function disableMessageA11y(quickOptions) {
-        quickOptions.removeAttribute("tabindex");
-        quickOptions.removeAttribute("role");
-        quickOptions.removeAttribute("aria-label");
-    }
-    function initMessagesA11y() {
-        UiScreen.on("screen-sm-down", {
-            match() {
-                document.querySelectorAll(".message").forEach((message) => {
-                    const navigation = message.querySelector(".jsMobileNavigation");
-                    if (navigation) {
-                        const quickOptions = message.querySelector(".messageQuickOptions");
-                        if (quickOptions && navigation.childElementCount) {
-                            enableMessageA11y(quickOptions);
-                        }
-                    }
-                });
-            },
-            unmatch() {
-                document.querySelectorAll(".message").forEach((message) => {
-                    if (!_knownMessages.has(message)) {
-                        return;
-                    }
-                    const navigation = message.querySelector(".jsMobileNavigation");
-                    if (navigation) {
-                        const quickOptions = message.querySelector(".messageQuickOptions");
-                        if (quickOptions && navigation.childElementCount) {
-                            disableMessageA11y(quickOptions);
-                        }
-                    }
-                });
-            },
-        });
-    }
     function initMobileMenu() {
         if (_enableMobileMenu) {
             _pageMenuMain = new Main_1.PageMenuMain(_pageMenuMainProvider);
index 16571d24c1c7cd4da8ea5d386553460462da2e3e..2518bffbb8edd08449d5637482dd4dcd1526c22b 100644 (file)
                width: 24px;
 
                @extend .messageCheckboxLabel;
-
-               &::before {
-                       font-size: 25px;
-                       left: 2px;
-                       top: -6px;
-               }
        }
 
        &.jsMarked .messageClipboardCheckbox::after {
 }
 
 .messageQuickOptions {
+       column-gap: 5px;
+       display: flex;
+
        @include screen-md-up {
-               @include inlineList;
+               .messageQuickOptionsMobile {
+                       display: none;
+               }
        }
 
        @include screen-sm-down {
-               flex: 0 0 24px !important;
-               height: 1.5em;
-               opacity: 0;
-               position: relative;
-               transition: opacity 0.12s linear, visibility 0s linear 0.12s;
-               visibility: hidden;
-
-               &::before {
-                       content: $fa-var-ellipsis-v;
-                       font-family: FontAwesome;
-                       font-size: 18px;
-                       height: 24px;
-                       position: absolute;
-                       right: 0;
-                       text-align: center;
-                       top: -2px;
-                       width: 24px;
+               // Prevent a layout shift caused by the mobile button
+               // being added on-the-fly.
+               &:not(.active) {
+                       padding-right: 29px;
                }
 
-               &.active {
-                       opacity: 1;
-                       transition-delay: 0s;
-                       visibility: visible;
+               li:not(.jsMessageClipboardCheckbox):not(.messageQuickOptionsMobile) {
+                       display: none;
                }
 
-               > li {
-                       display: none;
+               .jsMessageClipboardCheckbox,
+               .messageQuickOptionsMobile {
+                       align-items: center;
+                       display: flex;
+                       justify-content: center;
+                       height: 24px;
+                       width: 24px;
                }
        }
 }
index 6b723d4be4ceae19f354e8d414cfb3df110a5528..75ab5631f0672e1984c2c1f05fc1d0f33479a699 100644 (file)
                }
        }
 
-       // message clipboard checkbox
-       @include screen-sm-down {
-               .messageQuickOptions > .jsMessageClipboardCheckbox {
-                       display: initial;
-                       position: relative;
-                       right: 30px;
-                       top: -1px;
-               }
-       }
-
        // tab menu overflow
        .tabMenuOverlayLeft {
                background: linear-gradient(