Defer the main menu creation into a separate module
authorAlexander Ebert <ebert@woltlab.com>
Thu, 30 Dec 2021 12:46:58 +0000 (13:46 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Thu, 30 Dec 2021 12:46:58 +0000 (13:46 +0100)
The admin panel uses a completely different markup that has no common logic with the frontend.

12 files changed:
ts/WoltLabSuite/Core/Bootstrap.ts
ts/WoltLabSuite/Core/BootstrapFrontend.ts
ts/WoltLabSuite/Core/Ui/Mobile.ts
ts/WoltLabSuite/Core/Ui/Page/Menu/Main.ts
ts/WoltLabSuite/Core/Ui/Page/Menu/Main/Frontend.ts [new file with mode: 0644]
ts/WoltLabSuite/Core/Ui/Page/Menu/Main/Provider.ts [new file with mode: 0644]
wcfsetup/install/files/js/WoltLabSuite/Core/Bootstrap.js
wcfsetup/install/files/js/WoltLabSuite/Core/BootstrapFrontend.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Mobile.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Main.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Main/Frontend.js [new file with mode: 0644]
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Main/Provider.js [new file with mode: 0644]

index cab8f249db8f913600a9ad802162c780f288922a..c889aeec0cc6bbd5319afedd90dc571e6d9268d9 100644 (file)
@@ -33,6 +33,7 @@ import * as UiObjectActionToggle from "./Ui/Object/Action/Toggle";
 
 // perfectScrollbar does not need to be bound anywhere, it just has to be loaded for WCF.js
 import "perfect-scrollbar";
+import { PageMenuMainProvider } from "./Ui/Page/Menu/Main/Provider";
 
 // non strict equals by intent
 if (window.WCF == null) {
@@ -49,6 +50,7 @@ window.__wcf_bc_eventHandler = EventHandler;
 
 export interface BoostrapOptions {
   enableMobileMenu: boolean;
+  pageMenuMainProvider: PageMenuMainProvider;
 }
 
 function initA11y() {
@@ -72,6 +74,7 @@ export function setup(options: BoostrapOptions): void {
   options = Core.extend(
     {
       enableMobileMenu: true,
+      pageMenuMainProvider: undefined,
     },
     options,
   ) as BoostrapOptions;
@@ -89,7 +92,7 @@ export function setup(options: BoostrapOptions): void {
   DateTimeRelative.setup();
   DatePicker.init();
   UiDropdownSimple.setup();
-  UiMobile.setup(options.enableMobileMenu);
+  UiMobile.setup(options.enableMobileMenu, options.pageMenuMainProvider);
   UiTabMenu.setup();
   UiDialog.setup();
   UiTooltip.setup();
index 49ed6cca9547ad45274807948954b0ed3dde11d3..b50a8cb778ca9e3561cd438ecadbdd30edd359fd 100644 (file)
@@ -19,6 +19,7 @@ import * as UiMessageShareDialog from "./Ui/Message/Share/Dialog";
 import * as UiMessageShareProviders from "./Ui/Message/Share/Providers";
 import * as UiFeedDialog from "./Ui/Feed/Dialog";
 import User from "./User";
+import UiPageMenuMainFrontend from "./Ui/Page/Menu/Main/Frontend";
 
 interface BoostrapOptions {
   backgroundQueue: {
@@ -59,7 +60,10 @@ export function setup(options: BoostrapOptions): void {
   // Modify the URL of the background queue URL to always target the current domain to avoid CORS.
   options.backgroundQueue.url = window.WSC_API_URL + options.backgroundQueue.url.substr(window.WCF_PATH.length);
 
-  Bootstrap.setup({ enableMobileMenu: true });
+  Bootstrap.setup({
+    enableMobileMenu: true,
+    pageMenuMainProvider: new UiPageMenuMainFrontend(),
+  });
   UiPageHeaderMenu.init();
 
   if (options.styleChanger) {
index ce5ffdcb0139c64748a69d853406f2e7bb09b896..7af8c4d6da4536984f3b9d539b3eae5c2b295cee 100644 (file)
@@ -15,6 +15,7 @@ import UiCloseOverlay, { Origin } from "./CloseOverlay";
 import * as UiDropdownReusable from "./Dropdown/Reusable";
 import { closeSearchBar, openSearchBar } from "./Page/Header/Fixed";
 import { PageMenuMain } from "./Page/Menu/Main";
+import { PageMenuMainProvider } from "./Page/Menu/Main/Provider";
 import { hasValidUserMenu, PageMenuUser } from "./Page/Menu/User";
 import * as UiScreen from "./Screen";
 
@@ -28,6 +29,7 @@ let _mobileSidebarEnabled = false;
 let _pageMenuMain: PageMenuMain;
 let _pageMenuUser: PageMenuUser | undefined = undefined;
 let _messageGroups: HTMLCollection | null = null;
+let _pageMenuMainProvider: PageMenuMainProvider;
 const _sidebars: HTMLElement[] = [];
 
 function init(): void {
@@ -203,7 +205,7 @@ function initMessages(): void {
 
 function initMobileMenu(): void {
   if (_enableMobileMenu) {
-    _pageMenuMain = new PageMenuMain();
+    _pageMenuMain = new PageMenuMain(_pageMenuMainProvider);
     _pageMenuMain.enable();
 
     if (hasValidUserMenu()) {
@@ -367,8 +369,10 @@ function rebuildMobileNavigation(navigation: HTMLElement): void {
 /**
  * Initializes the mobile UI.
  */
-export function setup(enableMobileMenu: boolean): void {
+export function setup(enableMobileMenu: boolean, pageMenuMainProvider: PageMenuMainProvider): void {
   _enableMobileMenu = enableMobileMenu;
+  _pageMenuMainProvider = pageMenuMainProvider;
+
   document.querySelectorAll(".sidebar").forEach((sidebar: HTMLElement) => {
     _sidebars.push(sidebar);
   });
index 4bf6c9892929f872897260c6fd47eaf60bf80ccf..e5cff9c0a3aaa922acaf24e984e3935ce0bc116d 100644 (file)
@@ -11,59 +11,7 @@ import PageMenuContainer, { Orientation } from "./Container";
 import { PageMenuProvider } from "./Provider";
 import * as Language from "../../../Language";
 import DomUtil from "../../../Dom/Util";
-
-type MenuItemDepth = 0 | 1 | 2;
-
-type MenuItem = {
-  active: boolean;
-  children: MenuItem[];
-  counter: number;
-  depth: MenuItemDepth;
-  link?: string;
-  title: string;
-};
-
-function normalizeMenuItem(menuItem: HTMLElement, depth: MenuItemDepth): MenuItem {
-  const anchor = menuItem.querySelector(".boxMenuLink") as HTMLAnchorElement;
-  const title = anchor.querySelector(".boxMenuLinkTitle")!.textContent as string;
-
-  let counter = 0;
-  const outstandingItems = anchor.querySelector(".boxMenuLinkOutstandingItems");
-  if (outstandingItems) {
-    counter = +outstandingItems.textContent!.replace(/[^0-9]/, "");
-  }
-
-  const subMenu = menuItem.querySelector("ol");
-  let children: MenuItem[] = [];
-  if (subMenu instanceof HTMLOListElement) {
-    let childDepth = depth;
-    if (childDepth < 2) {
-      childDepth = (depth + 1) as MenuItemDepth;
-    }
-
-    children = Array.from(subMenu.children).map((subMenuItem: HTMLElement) => {
-      return normalizeMenuItem(subMenuItem, childDepth);
-    });
-  }
-
-  // `link.href` represents the computed link, not the raw value.
-  const href = anchor.getAttribute("href");
-  let link: string | undefined = undefined;
-  if (href && href !== "#") {
-    link = anchor.href;
-  }
-
-  const active = menuItem.classList.contains("active");
-
-  return {
-    active,
-    children,
-    counter,
-    depth,
-    link,
-    title,
-  };
-}
+import { MenuItem, PageMenuMainProvider } from "./Main/Provider";
 
 type CallbackOpen = (event: MouseEvent) => void;
 
@@ -71,9 +19,11 @@ export class PageMenuMain implements PageMenuProvider {
   private readonly callbackOpen: CallbackOpen;
   private readonly container: PageMenuContainer;
   private readonly mainMenu: HTMLElement;
+  private readonly menuItemProvider: PageMenuMainProvider;
 
-  constructor() {
+  constructor(menuItemProvider: PageMenuMainProvider) {
     this.mainMenu = document.querySelector(".mainMenu")!;
+    this.menuItemProvider = menuItemProvider;
 
     this.container = new PageMenuContainer(this, Orientation.Left);
 
@@ -168,9 +118,7 @@ export class PageMenuMain implements PageMenuProvider {
   }
 
   private buildMenu(boxMenu: HTMLElement): HTMLElement {
-    const menuItems: MenuItem[] = Array.from(boxMenu.children).map((element: HTMLElement) => {
-      return normalizeMenuItem(element, 0);
-    });
+    const menuItems = this.menuItemProvider.getMenuItems(boxMenu);
 
     const nav = document.createElement("nav");
     nav.classList.add("pageMenuMainNavigation");
diff --git a/ts/WoltLabSuite/Core/Ui/Page/Menu/Main/Frontend.ts b/ts/WoltLabSuite/Core/Ui/Page/Menu/Main/Frontend.ts
new file mode 100644 (file)
index 0000000..b6e8541
--- /dev/null
@@ -0,0 +1,53 @@
+import { MenuItem, MenuItemDepth, PageMenuMainProvider } from "./Provider";
+
+function normalizeMenuItem(menuItem: HTMLElement, depth: MenuItemDepth): MenuItem {
+  const anchor = menuItem.querySelector(".boxMenuLink") as HTMLAnchorElement;
+  const title = anchor.querySelector(".boxMenuLinkTitle")!.textContent as string;
+
+  let counter = 0;
+  const outstandingItems = anchor.querySelector(".boxMenuLinkOutstandingItems");
+  if (outstandingItems) {
+    counter = +outstandingItems.textContent!.replace(/[^0-9]/, "");
+  }
+
+  const subMenu = menuItem.querySelector("ol");
+  let children: MenuItem[] = [];
+  if (subMenu instanceof HTMLOListElement) {
+    let childDepth = depth;
+    if (childDepth < 2) {
+      childDepth = (depth + 1) as MenuItemDepth;
+    }
+
+    children = Array.from(subMenu.children).map((subMenuItem: HTMLElement) => {
+      return normalizeMenuItem(subMenuItem, childDepth);
+    });
+  }
+
+  // `link.href` represents the computed link, not the raw value.
+  const href = anchor.getAttribute("href");
+  let link: string | undefined = undefined;
+  if (href && href !== "#") {
+    link = anchor.href;
+  }
+
+  const active = menuItem.classList.contains("active");
+
+  return {
+    active,
+    children,
+    counter,
+    depth,
+    link,
+    title,
+  };
+}
+
+export class UiPageMenuMainFrontend implements PageMenuMainProvider {
+  getMenuItems(container:HTMLElement): MenuItem[] {
+    return Array.from(container.children).map((element: HTMLElement) => {
+      return normalizeMenuItem(element, 0);
+    });
+  }
+}
+
+export default UiPageMenuMainFrontend;
diff --git a/ts/WoltLabSuite/Core/Ui/Page/Menu/Main/Provider.ts b/ts/WoltLabSuite/Core/Ui/Page/Menu/Main/Provider.ts
new file mode 100644 (file)
index 0000000..f765dda
--- /dev/null
@@ -0,0 +1,14 @@
+export type MenuItemDepth = 0 | 1 | 2;
+
+export type MenuItem = {
+  active: boolean;
+  children: MenuItem[];
+  counter: number;
+  depth: MenuItemDepth;
+  link?: string;
+  title: string;
+};
+
+export interface PageMenuMainProvider {
+  getMenuItems(container: HTMLElement): MenuItem[];
+}
index 1bf9afd0fcf24a6e6fc53f9a5a92b57f17e741f1..fe08e20d4872a9e0215318f6674b37a9b96fafd0 100644 (file)
@@ -63,6 +63,7 @@ define(["require", "exports", "tslib", "./Core", "./Date/Picker", "./Date/Time/R
     function setup(options) {
         options = Core.extend({
             enableMobileMenu: true,
+            pageMenuMainProvider: undefined,
         }, options);
         StringUtil.setupI18n({
             decimalPoint: Language.get("wcf.global.decimalPoint"),
@@ -75,7 +76,7 @@ define(["require", "exports", "tslib", "./Core", "./Date/Picker", "./Date/Time/R
         DateTimeRelative.setup();
         Picker_1.default.init();
         Simple_1.default.setup();
-        UiMobile.setup(options.enableMobileMenu);
+        UiMobile.setup(options.enableMobileMenu, options.pageMenuMainProvider);
         UiTabMenu.setup();
         Dialog_1.default.setup();
         UiTooltip.setup();
index 6d6bd0e0318784e1ea5d008a1f33fafdea6300bd..ca7a9d3706cf31a23250daf4e963a426618b5086 100644 (file)
@@ -6,7 +6,7 @@
  * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @module  WoltLabSuite/Core/BootstrapFrontend
  */
-define(["require", "exports", "tslib", "./BackgroundQueue", "./Bootstrap", "./Controller/Style/Changer", "./Controller/Popover", "./Ui/User/Ignore", "./Ui/Page/Header/Menu", "./Ui/Message/UserConsent", "./Ajax", "./Ui/Message/Share/Dialog", "./Ui/Message/Share/Providers", "./Ui/Feed/Dialog", "./User"], function (require, exports, tslib_1, BackgroundQueue, Bootstrap, ControllerStyleChanger, ControllerPopover, UiUserIgnore, UiPageHeaderMenu, UiMessageUserConsent, Ajax, UiMessageShareDialog, UiMessageShareProviders, UiFeedDialog, User_1) {
+define(["require", "exports", "tslib", "./BackgroundQueue", "./Bootstrap", "./Controller/Style/Changer", "./Controller/Popover", "./Ui/User/Ignore", "./Ui/Page/Header/Menu", "./Ui/Message/UserConsent", "./Ajax", "./Ui/Message/Share/Dialog", "./Ui/Message/Share/Providers", "./Ui/Feed/Dialog", "./User", "./Ui/Page/Menu/Main/Frontend"], function (require, exports, tslib_1, BackgroundQueue, Bootstrap, ControllerStyleChanger, ControllerPopover, UiUserIgnore, UiPageHeaderMenu, UiMessageUserConsent, Ajax, UiMessageShareDialog, UiMessageShareProviders, UiFeedDialog, User_1, Frontend_1) {
     "use strict";
     Object.defineProperty(exports, "__esModule", { value: true });
     exports.setup = void 0;
@@ -22,6 +22,7 @@ define(["require", "exports", "tslib", "./BackgroundQueue", "./Bootstrap", "./Co
     UiMessageShareProviders = (0, tslib_1.__importStar)(UiMessageShareProviders);
     UiFeedDialog = (0, tslib_1.__importStar)(UiFeedDialog);
     User_1 = (0, tslib_1.__importDefault)(User_1);
+    Frontend_1 = (0, tslib_1.__importDefault)(Frontend_1);
     /**
      * Initializes user profile popover.
      */
@@ -45,7 +46,10 @@ define(["require", "exports", "tslib", "./BackgroundQueue", "./Bootstrap", "./Co
     function setup(options) {
         // Modify the URL of the background queue URL to always target the current domain to avoid CORS.
         options.backgroundQueue.url = window.WSC_API_URL + options.backgroundQueue.url.substr(window.WCF_PATH.length);
-        Bootstrap.setup({ enableMobileMenu: true });
+        Bootstrap.setup({
+            enableMobileMenu: true,
+            pageMenuMainProvider: new Frontend_1.default(),
+        });
         UiPageHeaderMenu.init();
         if (options.styleChanger) {
             ControllerStyleChanger.setup();
index 2f65f749bcf43306996104fd840d0c56e9d59b8a..e0c431903909d6197742d2e548ed8595572fa1d6 100644 (file)
@@ -27,6 +27,7 @@ define(["require", "exports", "tslib", "../Core", "../Dom/Change/Listener", "../
     let _pageMenuMain;
     let _pageMenuUser = undefined;
     let _messageGroups = null;
+    let _pageMenuMainProvider;
     const _sidebars = [];
     function init() {
         _enabled = true;
@@ -168,7 +169,7 @@ define(["require", "exports", "tslib", "../Core", "../Dom/Change/Listener", "../
     }
     function initMobileMenu() {
         if (_enableMobileMenu) {
-            _pageMenuMain = new Main_1.PageMenuMain();
+            _pageMenuMain = new Main_1.PageMenuMain(_pageMenuMainProvider);
             _pageMenuMain.enable();
             if ((0, User_1.hasValidUserMenu)()) {
                 _pageMenuUser = new User_1.PageMenuUser();
@@ -301,8 +302,9 @@ define(["require", "exports", "tslib", "../Core", "../Dom/Change/Listener", "../
     /**
      * Initializes the mobile UI.
      */
-    function setup(enableMobileMenu) {
+    function setup(enableMobileMenu, pageMenuMainProvider) {
         _enableMobileMenu = enableMobileMenu;
+        _pageMenuMainProvider = pageMenuMainProvider;
         document.querySelectorAll(".sidebar").forEach((sidebar) => {
             _sidebars.push(sidebar);
         });
index 8f061562a543c25feedacbe868ea8c87a8400fc3..b822974986ece0daca4da8d015fb2ff0cb6486bf 100644 (file)
@@ -13,44 +13,10 @@ define(["require", "exports", "tslib", "./Container", "../../../Language", "../.
     Container_1 = (0, tslib_1.__importDefault)(Container_1);
     Language = (0, tslib_1.__importStar)(Language);
     Util_1 = (0, tslib_1.__importDefault)(Util_1);
-    function normalizeMenuItem(menuItem, depth) {
-        const anchor = menuItem.querySelector(".boxMenuLink");
-        const title = anchor.querySelector(".boxMenuLinkTitle").textContent;
-        let counter = 0;
-        const outstandingItems = anchor.querySelector(".boxMenuLinkOutstandingItems");
-        if (outstandingItems) {
-            counter = +outstandingItems.textContent.replace(/[^0-9]/, "");
-        }
-        const subMenu = menuItem.querySelector("ol");
-        let children = [];
-        if (subMenu instanceof HTMLOListElement) {
-            let childDepth = depth;
-            if (childDepth < 2) {
-                childDepth = (depth + 1);
-            }
-            children = Array.from(subMenu.children).map((subMenuItem) => {
-                return normalizeMenuItem(subMenuItem, childDepth);
-            });
-        }
-        // `link.href` represents the computed link, not the raw value.
-        const href = anchor.getAttribute("href");
-        let link = undefined;
-        if (href && href !== "#") {
-            link = anchor.href;
-        }
-        const active = menuItem.classList.contains("active");
-        return {
-            active,
-            children,
-            counter,
-            depth,
-            link,
-            title,
-        };
-    }
     class PageMenuMain {
-        constructor() {
+        constructor(menuItemProvider) {
             this.mainMenu = document.querySelector(".mainMenu");
+            this.menuItemProvider = menuItemProvider;
             this.container = new Container_1.default(this, "left" /* Left */);
             this.callbackOpen = (event) => {
                 event.preventDefault();
@@ -121,9 +87,7 @@ define(["require", "exports", "tslib", "./Container", "../../../Language", "../.
             return nav;
         }
         buildMenu(boxMenu) {
-            const menuItems = Array.from(boxMenu.children).map((element) => {
-                return normalizeMenuItem(element, 0);
-            });
+            const menuItems = this.menuItemProvider.getMenuItems(boxMenu);
             const nav = document.createElement("nav");
             nav.classList.add("pageMenuMainNavigation");
             nav.append(this.buildMenuItemList(menuItems));
diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Main/Frontend.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Main/Frontend.js
new file mode 100644 (file)
index 0000000..99dbd90
--- /dev/null
@@ -0,0 +1,49 @@
+define(["require", "exports"], function (require, exports) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    exports.UiPageMenuMainFrontend = void 0;
+    function normalizeMenuItem(menuItem, depth) {
+        const anchor = menuItem.querySelector(".boxMenuLink");
+        const title = anchor.querySelector(".boxMenuLinkTitle").textContent;
+        let counter = 0;
+        const outstandingItems = anchor.querySelector(".boxMenuLinkOutstandingItems");
+        if (outstandingItems) {
+            counter = +outstandingItems.textContent.replace(/[^0-9]/, "");
+        }
+        const subMenu = menuItem.querySelector("ol");
+        let children = [];
+        if (subMenu instanceof HTMLOListElement) {
+            let childDepth = depth;
+            if (childDepth < 2) {
+                childDepth = (depth + 1);
+            }
+            children = Array.from(subMenu.children).map((subMenuItem) => {
+                return normalizeMenuItem(subMenuItem, childDepth);
+            });
+        }
+        // `link.href` represents the computed link, not the raw value.
+        const href = anchor.getAttribute("href");
+        let link = undefined;
+        if (href && href !== "#") {
+            link = anchor.href;
+        }
+        const active = menuItem.classList.contains("active");
+        return {
+            active,
+            children,
+            counter,
+            depth,
+            link,
+            title,
+        };
+    }
+    class UiPageMenuMainFrontend {
+        getMenuItems(container) {
+            return Array.from(container.children).map((element) => {
+                return normalizeMenuItem(element, 0);
+            });
+        }
+    }
+    exports.UiPageMenuMainFrontend = UiPageMenuMainFrontend;
+    exports.default = UiPageMenuMainFrontend;
+});
diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Main/Provider.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Main/Provider.js
new file mode 100644 (file)
index 0000000..2ae92b6
--- /dev/null
@@ -0,0 +1,4 @@
+define(["require", "exports"], function (require, exports) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+});