Consistent behavior of the mobile menu
authorAlexander Ebert <ebert@woltlab.com>
Sun, 12 Dec 2021 15:42:31 +0000 (16:42 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Sun, 12 Dec 2021 15:42:31 +0000 (16:42 +0100)
com.woltlab.wcf/templates/header.tpl
com.woltlab.wcf/templates/pageHeader.tpl
ts/WoltLabSuite/Core/Ui/Mobile.ts
ts/WoltLabSuite/Core/Ui/Page/Menu/Container.ts
ts/WoltLabSuite/Core/Ui/Page/Menu/Main.ts
ts/WoltLabSuite/Core/Ui/Page/Menu/Provider.ts
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Mobile.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Container.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Main.js

index 5de4b28037e9f4b625bcc8455c5d893185fc9fba..d9f66db755a12f88f53ef39bea73171b04fe7377 100644 (file)
        
        {include file='headInclude'}
        
-<style>
-
-</style>
-
        {if !$canonicalURL|empty}
                <link rel="canonical" href="{$canonicalURL}">
        {/if}
index 4887cec3631c2f218a5f812c7bc4c4f0eff4dd37..8512fbbf1a5443257bad4c4ee3573829400bca2f 100644 (file)
@@ -20,9 +20,6 @@
                        require(['WoltLabSuite/Core/Ui/Page/Header/Fixed'], function(UiPageHeaderFixed) {
                                UiPageHeaderFixed.init();
                        });
-                       require(["WoltLabSuite/Core/Ui/Page/Menu/Main"], ({ PageMenuMain }) => {
-                               new PageMenuMain();
-                       })
                </script>
        </header>
        
index 3f15dc50e176602305165af1fc5beacf45340690..d1821f9301be903d821578240e1eff1af002d381 100644 (file)
@@ -14,13 +14,13 @@ import * as EventHandler from "../Event/Handler";
 import * as UiAlignment from "./Alignment";
 import UiCloseOverlay from "./CloseOverlay";
 import * as UiDropdownReusable from "./Dropdown/Reusable";
-//import UiPageMenuMain from "./Page/Menu/Main";
+import { PageMenuMain } from "./Page/Menu/Main";
 import UiPageMenuUser from "./Page/Menu/User";
 import * as UiScreen from "./Screen";
 
 interface MainMenuMorePayload {
   identifier: string;
-  handler: any;//UiPageMenuMain;
+  handler: any; //UiPageMenuMain;
 }
 
 let _dropdownMenu: HTMLUListElement | null = null;
@@ -30,7 +30,7 @@ let _enabledLGTouchNavigation = false;
 let _enableMobileMenu = false;
 const _knownMessages = new WeakSet<HTMLElement>();
 let _mobileSidebarEnabled = false;
-//let _pageMenuMain: UiPageMenuMain;
+let _pageMenuMain: PageMenuMain;
 let _pageMenuUser: UiPageMenuUser | undefined = undefined;
 let _messageGroups: HTMLCollection | null = null;
 const _sidebars: HTMLElement[] = [];
@@ -163,7 +163,8 @@ function initMessages(): void {
 
 function initMobileMenu(): void {
   if (_enableMobileMenu) {
-    //_pageMenuMain = new UiPageMenuMain();
+    _pageMenuMain = new PageMenuMain();
+    _pageMenuMain.enable();
 
     if (UiPageMenuUser.hasValidMenu()) {
       _pageMenuUser = new UiPageMenuUser();
@@ -378,7 +379,7 @@ export function setup(enableMobileMenu: boolean): void {
 export function enable(): void {
   _enabled = true;
   if (_enableMobileMenu) {
-    //_pageMenuMain.enable();
+    _pageMenuMain.enable();
     _pageMenuUser?.enable();
   }
 }
@@ -398,7 +399,7 @@ export function enableShadow(): void {
 export function disable(): void {
   _enabled = false;
   if (_enableMobileMenu) {
-    //_pageMenuMain.disable();
+    _pageMenuMain.disable();
     _pageMenuUser?.disable();
   }
 }
index 473e485dc2cc0f7a10568093cd4998f6d53a475c..c539480190fd768165c21c1419c390badc79f273 100644 (file)
@@ -1,6 +1,8 @@
 import { PageMenuProvider } from "./Provider";
 import { createFocusTrap, FocusTrap } from "focus-trap";
 import { pageOverlayClose, pageOverlayOpen, scrollDisable, scrollEnable } from "../../Screen";
+import UiCloseOverlay from "../../CloseOverlay";
+import DomUtil from "../../../Dom/Util";
 
 export class PageMenuContainer {
   private readonly container = document.createElement("div");
@@ -10,9 +12,18 @@ export class PageMenuContainer {
 
   constructor(provider: PageMenuProvider) {
     this.provider = provider;
+
+    const menuId = DomUtil.identify(this.provider.getMenuButton());
+    UiCloseOverlay.add(`WoltLabSuite/Core/Ui/PageMenu/Container-${menuId}`, () => {
+      if (!this.container.hidden) {
+        this.close();
+      }
+    });
   }
 
   open(): void {
+    UiCloseOverlay.execute();
+
     this.buildElements();
 
     this.content.innerHTML = "";
index 24ee52de24c8adf9c714b15456ab24969bfdd97b..e9e6b9934bf0588752142f62a5068d5e58a5aeb7 100644 (file)
@@ -56,7 +56,10 @@ function normalizeMenuItem(menuItem: HTMLElement): MenuItem {
   };
 }
 
+type CallbackOpen = (event: MouseEvent) => void;
+
 export class PageMenuMain implements PageMenuProvider {
+  private readonly callbackOpen: CallbackOpen;
   private readonly container: PageMenuContainer;
   private readonly mainMenu: HTMLElement;
 
@@ -65,11 +68,28 @@ export class PageMenuMain implements PageMenuProvider {
 
     this.container = new PageMenuContainer(this);
 
-    this.mainMenu.addEventListener("click", (event) => {
+    this.callbackOpen = (event) => {
       event.preventDefault();
+      event.stopPropagation();
 
       this.container.toggle();
-    });
+    };
+  }
+
+  enable(): void {
+    this.mainMenu.setAttribute("aria-expanded", "false");
+    this.mainMenu.setAttribute("role", "button");
+    this.mainMenu.tabIndex = 0;
+    this.mainMenu.addEventListener("click", this.callbackOpen);
+  }
+
+  disable(): void {
+    this.container.close();
+
+    this.mainMenu.removeAttribute("aria-expanded");
+    this.mainMenu.removeAttribute("role");
+    this.mainMenu.removeAttribute("tabindex");
+    this.mainMenu.removeEventListener("click", this.callbackOpen);
   }
 
   getContent(): DocumentFragment {
index ac074c8ac782d506cb953c3f56646abbacfae927..c7e2d031b1a9095c38f6f7b4a094e00dcf19935b 100644 (file)
@@ -1,4 +1,8 @@
 export interface PageMenuProvider {
+  disable(): void;
+
+  enable(): void;
+
   getContent(): DocumentFragment;
 
   getMenuButton(): HTMLElement;
index 98c916b1b61247ad9b0066c804626dcb31842132..112d6b17853fb701a7abf840b817567828866abf 100644 (file)
@@ -6,7 +6,7 @@
  * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @module  WoltLabSuite/Core/Ui/Mobile
  */
-define(["require", "exports", "tslib", "../Core", "../Dom/Change/Listener", "../Environment", "../Event/Handler", "./Alignment", "./CloseOverlay", "./Dropdown/Reusable", "./Page/Menu/User", "./Screen"], function (require, exports, tslib_1, Core, Listener_1, Environment, EventHandler, UiAlignment, CloseOverlay_1, UiDropdownReusable, User_1, UiScreen) {
+define(["require", "exports", "tslib", "../Core", "../Dom/Change/Listener", "../Environment", "../Event/Handler", "./Alignment", "./CloseOverlay", "./Dropdown/Reusable", "./Page/Menu/Main", "./Page/Menu/User", "./Screen"], function (require, exports, tslib_1, Core, Listener_1, Environment, EventHandler, UiAlignment, CloseOverlay_1, UiDropdownReusable, Main_1, User_1, UiScreen) {
     "use strict";
     Object.defineProperty(exports, "__esModule", { value: true });
     exports.removeShadow = exports.rebuildShadow = exports.disableShadow = exports.disable = exports.enableShadow = exports.enable = exports.setup = void 0;
@@ -26,7 +26,7 @@ define(["require", "exports", "tslib", "../Core", "../Dom/Change/Listener", "../
     let _enableMobileMenu = false;
     const _knownMessages = new WeakSet();
     let _mobileSidebarEnabled = false;
-    //let _pageMenuMain: UiPageMenuMain;
+    let _pageMenuMain;
     let _pageMenuUser = undefined;
     let _messageGroups = null;
     const _sidebars = [];
@@ -136,7 +136,8 @@ define(["require", "exports", "tslib", "../Core", "../Dom/Change/Listener", "../
     }
     function initMobileMenu() {
         if (_enableMobileMenu) {
-            //_pageMenuMain = new UiPageMenuMain();
+            _pageMenuMain = new Main_1.PageMenuMain();
+            _pageMenuMain.enable();
             if (User_1.default.hasValidMenu()) {
                 _pageMenuUser = new User_1.default();
             }
@@ -316,7 +317,7 @@ define(["require", "exports", "tslib", "../Core", "../Dom/Change/Listener", "../
     function enable() {
         _enabled = true;
         if (_enableMobileMenu) {
-            //_pageMenuMain.enable();
+            _pageMenuMain.enable();
             _pageMenuUser === null || _pageMenuUser === void 0 ? void 0 : _pageMenuUser.enable();
         }
     }
@@ -336,7 +337,7 @@ define(["require", "exports", "tslib", "../Core", "../Dom/Change/Listener", "../
     function disable() {
         _enabled = false;
         if (_enableMobileMenu) {
-            //_pageMenuMain.disable();
+            _pageMenuMain.disable();
             _pageMenuUser === null || _pageMenuUser === void 0 ? void 0 : _pageMenuUser.disable();
         }
     }
index 7d9ab2e5900b8da545c4f88d97a8a2c89eebda53..a90e0c038dff65290659012bac4c8c9a626315b2 100644 (file)
@@ -1,15 +1,24 @@
-define(["require", "exports", "focus-trap", "../../Screen"], function (require, exports, focus_trap_1, Screen_1) {
+define(["require", "exports", "tslib", "focus-trap", "../../Screen", "../../CloseOverlay", "../../../Dom/Util"], function (require, exports, tslib_1, focus_trap_1, Screen_1, CloseOverlay_1, Util_1) {
     "use strict";
     Object.defineProperty(exports, "__esModule", { value: true });
     exports.PageMenuContainer = void 0;
+    CloseOverlay_1 = (0, tslib_1.__importDefault)(CloseOverlay_1);
+    Util_1 = (0, tslib_1.__importDefault)(Util_1);
     class PageMenuContainer {
         constructor(provider) {
             this.container = document.createElement("div");
             this.content = document.createElement("div");
             this.focusTrap = undefined;
             this.provider = provider;
+            const menuId = Util_1.default.identify(this.provider.getMenuButton());
+            CloseOverlay_1.default.add(`WoltLabSuite/Core/Ui/PageMenu/Container-${menuId}`, () => {
+                if (!this.container.hidden) {
+                    this.close();
+                }
+            });
         }
         open() {
+            CloseOverlay_1.default.execute();
             this.buildElements();
             this.content.innerHTML = "";
             this.content.append(this.provider.getContent());
index 5a797f2448e994f40df037b291befd3859a66bf7..923ca0608de7f8e5e065205e3d0760fea386f84b 100644 (file)
@@ -47,10 +47,24 @@ define(["require", "exports", "tslib", "./Container", "../../../Language", "../.
         constructor() {
             this.mainMenu = document.querySelector(".mainMenu");
             this.container = new Container_1.default(this);
-            this.mainMenu.addEventListener("click", (event) => {
+            this.callbackOpen = (event) => {
                 event.preventDefault();
+                event.stopPropagation();
                 this.container.toggle();
-            });
+            };
+        }
+        enable() {
+            this.mainMenu.setAttribute("aria-expanded", "false");
+            this.mainMenu.setAttribute("role", "button");
+            this.mainMenu.tabIndex = 0;
+            this.mainMenu.addEventListener("click", this.callbackOpen);
+        }
+        disable() {
+            this.container.close();
+            this.mainMenu.removeAttribute("aria-expanded");
+            this.mainMenu.removeAttribute("role");
+            this.mainMenu.removeAttribute("tabindex");
+            this.mainMenu.removeEventListener("click", this.callbackOpen);
         }
         getContent() {
             const container = document.createElement("div");