From c7bacb860e8adbdccf41a51f891dd2001f1343a7 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Thu, 30 Dec 2021 13:46:58 +0100 Subject: [PATCH] Defer the main menu creation into a separate module The admin panel uses a completely different markup that has no common logic with the frontend. --- ts/WoltLabSuite/Core/Bootstrap.ts | 5 +- ts/WoltLabSuite/Core/BootstrapFrontend.ts | 6 +- ts/WoltLabSuite/Core/Ui/Mobile.ts | 8 ++- ts/WoltLabSuite/Core/Ui/Page/Menu/Main.ts | 62 ++----------------- .../Core/Ui/Page/Menu/Main/Frontend.ts | 53 ++++++++++++++++ .../Core/Ui/Page/Menu/Main/Provider.ts | 14 +++++ .../files/js/WoltLabSuite/Core/Bootstrap.js | 3 +- .../js/WoltLabSuite/Core/BootstrapFrontend.js | 8 ++- .../files/js/WoltLabSuite/Core/Ui/Mobile.js | 6 +- .../js/WoltLabSuite/Core/Ui/Page/Menu/Main.js | 42 +------------ .../Core/Ui/Page/Menu/Main/Frontend.js | 49 +++++++++++++++ .../Core/Ui/Page/Menu/Main/Provider.js | 4 ++ 12 files changed, 155 insertions(+), 105 deletions(-) create mode 100644 ts/WoltLabSuite/Core/Ui/Page/Menu/Main/Frontend.ts create mode 100644 ts/WoltLabSuite/Core/Ui/Page/Menu/Main/Provider.ts create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Main/Frontend.js create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Main/Provider.js diff --git a/ts/WoltLabSuite/Core/Bootstrap.ts b/ts/WoltLabSuite/Core/Bootstrap.ts index cab8f249db..c889aeec0c 100644 --- a/ts/WoltLabSuite/Core/Bootstrap.ts +++ b/ts/WoltLabSuite/Core/Bootstrap.ts @@ -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(); diff --git a/ts/WoltLabSuite/Core/BootstrapFrontend.ts b/ts/WoltLabSuite/Core/BootstrapFrontend.ts index 49ed6cca95..b50a8cb778 100644 --- a/ts/WoltLabSuite/Core/BootstrapFrontend.ts +++ b/ts/WoltLabSuite/Core/BootstrapFrontend.ts @@ -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) { diff --git a/ts/WoltLabSuite/Core/Ui/Mobile.ts b/ts/WoltLabSuite/Core/Ui/Mobile.ts index ce5ffdcb01..7af8c4d6da 100644 --- a/ts/WoltLabSuite/Core/Ui/Mobile.ts +++ b/ts/WoltLabSuite/Core/Ui/Mobile.ts @@ -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); }); diff --git a/ts/WoltLabSuite/Core/Ui/Page/Menu/Main.ts b/ts/WoltLabSuite/Core/Ui/Page/Menu/Main.ts index 4bf6c98929..e5cff9c0a3 100644 --- a/ts/WoltLabSuite/Core/Ui/Page/Menu/Main.ts +++ b/ts/WoltLabSuite/Core/Ui/Page/Menu/Main.ts @@ -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 index 0000000000..b6e8541a11 --- /dev/null +++ b/ts/WoltLabSuite/Core/Ui/Page/Menu/Main/Frontend.ts @@ -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 index 0000000000..f765dda658 --- /dev/null +++ b/ts/WoltLabSuite/Core/Ui/Page/Menu/Main/Provider.ts @@ -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[]; +} diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Bootstrap.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Bootstrap.js index 1bf9afd0fc..fe08e20d48 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Bootstrap.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Bootstrap.js @@ -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(); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/BootstrapFrontend.js b/wcfsetup/install/files/js/WoltLabSuite/Core/BootstrapFrontend.js index 6d6bd0e031..ca7a9d3706 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/BootstrapFrontend.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/BootstrapFrontend.js @@ -6,7 +6,7 @@ * @license GNU Lesser General Public License * @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(); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Mobile.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Mobile.js index 2f65f749bc..e0c4319039 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Mobile.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Mobile.js @@ -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); }); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Main.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Main.js index 8f061562a5..b822974986 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Main.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Main.js @@ -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 index 0000000000..99dbd90989 --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Main/Frontend.js @@ -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 index 0000000000..2ae92b6a8b --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Page/Menu/Main/Provider.js @@ -0,0 +1,4 @@ +define(["require", "exports"], function (require, exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); +}); -- 2.20.1