From 82520c782325e41729a7e3a081c60c67a0a277e2 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Tue, 13 Sep 2022 17:10:51 +0200 Subject: [PATCH] Add the missing language switch to the mobile guest menu Fixes #4990 --- .../templates/headIncludeJavaScript.tpl | 3 +- ts/WoltLabSuite/Core/Ui/Page/Menu/Main.ts | 93 ++++++++++++++++--- .../install/files/acp/templates/header.tpl | 3 +- .../js/WoltLabSuite/Core/Ui/Page/Menu/Main.js | 83 ++++++++++++++--- wcfsetup/install/files/style/ui/pageMenu.scss | 6 ++ 5 files changed, 159 insertions(+), 29 deletions(-) diff --git a/com.woltlab.wcf/templates/headIncludeJavaScript.tpl b/com.woltlab.wcf/templates/headIncludeJavaScript.tpl index 4ff9f992da..d44ecb4157 100644 --- a/com.woltlab.wcf/templates/headIncludeJavaScript.tpl +++ b/com.woltlab.wcf/templates/headIncludeJavaScript.tpl @@ -158,7 +158,8 @@ window.addEventListener('pageshow', function(event) { 'wcf.global.rss.copy.success': '{jslang}wcf.global.rss.copy.success{/jslang}', 'wcf.global.rss.accessToken.info': '{jslang}wcf.global.rss.accessToken.info{/jslang}', 'wcf.global.rss.withoutAccessToken': '{jslang}wcf.global.rss.withoutAccessToken{/jslang}', - 'wcf.global.rss.withAccessToken': '{jslang}wcf.global.rss.withAccessToken{/jslang}' + 'wcf.global.rss.withAccessToken': '{jslang}wcf.global.rss.withAccessToken{/jslang}', + 'wcf.user.language': '{jslang}wcf.user.language{/jslang}' {if MODULE_LIKE} ,'wcf.like.button.like': '{jslang}wcf.like.button.like{/jslang}', 'wcf.like.button.dislike': '{jslang}wcf.like.button.dislike{/jslang}', diff --git a/ts/WoltLabSuite/Core/Ui/Page/Menu/Main.ts b/ts/WoltLabSuite/Core/Ui/Page/Menu/Main.ts index 91178d4e97..e4b0d6a08b 100644 --- a/ts/WoltLabSuite/Core/Ui/Page/Menu/Main.ts +++ b/ts/WoltLabSuite/Core/Ui/Page/Menu/Main.ts @@ -12,6 +12,7 @@ import { PageMenuProvider } from "./Provider"; import * as Language from "../../../Language"; import DomUtil from "../../../Dom/Util"; import { MenuItem, PageMenuMainProvider } from "./Main/Provider"; +import * as DropDownSimple from "../../Dropdown/Simple"; type CallbackOpen = (event: MouseEvent) => void; @@ -80,6 +81,11 @@ export class PageMenuMain implements PageMenuProvider { container.append(this.buildMainMenu()); + const languageMenu = this.buildLanguageMenu(); + if (languageMenu) { + container.append(languageMenu); + } + const footerMenu = this.buildFooterMenu(); if (footerMenu) { container.append(footerMenu); @@ -155,6 +161,60 @@ export class PageMenuMain implements PageMenuProvider { } } + private buildLanguageMenu(): HTMLElement | null { + const dropDownMenu = DropDownSimple.getDropdownMenu("pageLanguageContainer"); + if (dropDownMenu === undefined) { + return null; + } + + const children: MenuItem[] = []; + const languageMapping = new Map(); + Array.from(dropDownMenu.children).forEach((listItem: HTMLElement) => { + const identifier = listItem.dataset.languageCode!; + const title = listItem.querySelector("span")!.textContent!.trim(); + + languageMapping.set(identifier, listItem.querySelector("a")!); + + children.push({ + active: false, + children: [], + counter: 0, + depth: 1, + identifier, + title, + }); + }); + + const menuItems: MenuItem[] = [ + { + active: false, + children, + counter: 0, + depth: 0, + identifier: null, + title: Language.get("wcf.user.language"), + }, + ]; + + const nav = document.createElement("nav"); + nav.classList.add("pageMenuMainNavigation", "pageMenuMainNavigationLanguage"); + nav.append(this.buildMenuItemList(menuItems, true)); + + // Forward clicks on the language to the actual language picker element. + nav + .querySelectorAll(".pageMenuMainItemList .pageMenuMainItemLabel[data-identifier]") + .forEach((element: HTMLAnchorElement) => { + element.addEventListener("click", (event) => { + event.preventDefault(); + + const identifier = element.dataset.identifier!; + languageMapping.get(identifier)!.click(); + }); + }); + + return nav; + } + private buildFooterMenu(): HTMLElement | null { const box = document.querySelector('.box[data-box-identifier="com.woltlab.wcf.FooterMenu"]'); if (box === null) { @@ -176,32 +236,32 @@ export class PageMenuMain implements PageMenuProvider { const nav = document.createElement("nav"); nav.classList.add("pageMenuMainNavigation"); - nav.append(this.buildMenuItemList(menuItems)); + nav.append(this.buildMenuItemList(menuItems, false)); return nav; } - private buildMenuItemList(menuItems: MenuItem[]): HTMLUListElement { + private buildMenuItemList(menuItems: MenuItem[], isLanguageSelection: boolean): HTMLUListElement { const list = document.createElement("ul"); list.classList.add("pageMenuMainItemList"); menuItems .filter((menuItem) => { // Remove links that have no target (`#`) and do not contain any children. - if (!menuItem.link && menuItem.children.length === 0) { + if (!isLanguageSelection && !menuItem.link && menuItem.children.length === 0) { return false; } return true; }) .forEach((menuItem) => { - list.append(this.buildMenuItem(menuItem)); + list.append(this.buildMenuItem(menuItem, isLanguageSelection)); }); return list; } - private buildMenuItem(menuItem: MenuItem): HTMLLIElement { + private buildMenuItem(menuItem: MenuItem, isLanguageSelection: boolean): HTMLLIElement { const listItem = document.createElement("li"); listItem.dataset.depth = menuItem.depth.toString(); listItem.classList.add("pageMenuMainItem"); @@ -234,15 +294,22 @@ export class PageMenuMain implements PageMenuProvider { label.classList.add("pageMenuMainItemLabel"); label.href = "#"; label.textContent = menuItem.title; - label.addEventListener("click", (event) => { - event.preventDefault(); - const button = label.nextElementSibling as HTMLAnchorElement; - button.click(); - }); + if (!isLanguageSelection || !menuItem.identifier) { + label.addEventListener("click", (event) => { + event.preventDefault(); + + const button = label.nextElementSibling as HTMLAnchorElement; + button.click(); + }); - // The button to expand the link group is used instead. - label.setAttribute("aria-hidden", "true"); + // The button to expand the link group is used instead. + label.setAttribute("aria-hidden", "true"); + } + + if (isLanguageSelection && menuItem.identifier) { + label.dataset.identifier = menuItem.identifier; + } listItem.append(label); } @@ -266,7 +333,7 @@ export class PageMenuMain implements PageMenuProvider { } button.setAttribute("aria-label", ariaLabel); - const list = this.buildMenuItemList(menuItem.children); + const list = this.buildMenuItemList(menuItem.children, isLanguageSelection); list.id = menuId; list.hidden = true; diff --git a/wcfsetup/install/files/acp/templates/header.tpl b/wcfsetup/install/files/acp/templates/header.tpl index 1ddb07ea7d..13ab2be356 100644 --- a/wcfsetup/install/files/acp/templates/header.tpl +++ b/wcfsetup/install/files/acp/templates/header.tpl @@ -144,7 +144,8 @@ 'wcf.date.datePicker.hour': '{jslang}wcf.date.datePicker.hour{/jslang}', 'wcf.date.datePicker.minute': '{jslang}wcf.date.datePicker.minute{/jslang}', 'wcf.global.form.password.button.hide': '{jslang}wcf.global.form.password.button.hide{/jslang}', - 'wcf.global.form.password.button.show': '{jslang}wcf.global.form.password.button.show{/jslang}' + 'wcf.global.form.password.button.show': '{jslang}wcf.global.form.password.button.show{/jslang}', + 'wcf.user.language': '{jslang}wcf.user.language{/jslang}' {event name='javascriptLanguageImport'} }); 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 a803706fec..94a394ee69 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 @@ -6,13 +6,14 @@ * @license GNU Lesser General Public License * @module WoltLabSuite/Core/Ui/Page/Menu/Main */ -define(["require", "exports", "tslib", "./Container", "../../../Language", "../../../Dom/Util"], function (require, exports, tslib_1, Container_1, Language, Util_1) { +define(["require", "exports", "tslib", "./Container", "../../../Language", "../../../Dom/Util", "../../Dropdown/Simple"], function (require, exports, tslib_1, Container_1, Language, Util_1, DropDownSimple) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PageMenuMain = void 0; Container_1 = tslib_1.__importDefault(Container_1); Language = tslib_1.__importStar(Language); Util_1 = tslib_1.__importDefault(Util_1); + DropDownSimple = tslib_1.__importStar(DropDownSimple); class PageMenuMain { constructor(menuItemProvider) { this.menuItemBadges = new Map(); @@ -58,6 +59,10 @@ define(["require", "exports", "tslib", "./Container", "../../../Language", "../. container.classList.add("pageMenuMainContainer"); container.addEventListener("scroll", () => this.updateOverflowIndicator(container), { passive: true }); container.append(this.buildMainMenu()); + const languageMenu = this.buildLanguageMenu(); + if (languageMenu) { + container.append(languageMenu); + } const footerMenu = this.buildFooterMenu(); if (footerMenu) { container.append(footerMenu); @@ -116,6 +121,51 @@ define(["require", "exports", "tslib", "./Container", "../../../Language", "../. } } } + buildLanguageMenu() { + const dropDownMenu = DropDownSimple.getDropdownMenu("pageLanguageContainer"); + if (dropDownMenu === undefined) { + return null; + } + const children = []; + const languageMapping = new Map(); + Array.from(dropDownMenu.children).forEach((listItem) => { + const identifier = listItem.dataset.languageCode; + const title = listItem.querySelector("span").textContent.trim(); + languageMapping.set(identifier, listItem.querySelector("a")); + children.push({ + active: false, + children: [], + counter: 0, + depth: 1, + identifier, + title, + }); + }); + const menuItems = [ + { + active: false, + children, + counter: 0, + depth: 0, + identifier: null, + title: Language.get("wcf.user.language"), + }, + ]; + const nav = document.createElement("nav"); + nav.classList.add("pageMenuMainNavigation", "pageMenuMainNavigationLanguage"); + nav.append(this.buildMenuItemList(menuItems, true)); + // Forward clicks on the language to the actual language picker element. + nav + .querySelectorAll(".pageMenuMainItemList .pageMenuMainItemLabel[data-identifier]") + .forEach((element) => { + element.addEventListener("click", (event) => { + event.preventDefault(); + const identifier = element.dataset.identifier; + languageMapping.get(identifier).click(); + }); + }); + return nav; + } buildFooterMenu() { const box = document.querySelector('.box[data-box-identifier="com.woltlab.wcf.FooterMenu"]'); if (box === null) { @@ -132,26 +182,26 @@ define(["require", "exports", "tslib", "./Container", "../../../Language", "../. const menuItems = this.menuItemProvider.getMenuItems(boxMenu); const nav = document.createElement("nav"); nav.classList.add("pageMenuMainNavigation"); - nav.append(this.buildMenuItemList(menuItems)); + nav.append(this.buildMenuItemList(menuItems, false)); return nav; } - buildMenuItemList(menuItems) { + buildMenuItemList(menuItems, isLanguageSelection) { const list = document.createElement("ul"); list.classList.add("pageMenuMainItemList"); menuItems .filter((menuItem) => { // Remove links that have no target (`#`) and do not contain any children. - if (!menuItem.link && menuItem.children.length === 0) { + if (!isLanguageSelection && !menuItem.link && menuItem.children.length === 0) { return false; } return true; }) .forEach((menuItem) => { - list.append(this.buildMenuItem(menuItem)); + list.append(this.buildMenuItem(menuItem, isLanguageSelection)); }); return list; } - buildMenuItem(menuItem) { + buildMenuItem(menuItem, isLanguageSelection) { const listItem = document.createElement("li"); listItem.dataset.depth = menuItem.depth.toString(); listItem.classList.add("pageMenuMainItem"); @@ -180,13 +230,18 @@ define(["require", "exports", "tslib", "./Container", "../../../Language", "../. label.classList.add("pageMenuMainItemLabel"); label.href = "#"; label.textContent = menuItem.title; - label.addEventListener("click", (event) => { - event.preventDefault(); - const button = label.nextElementSibling; - button.click(); - }); - // The button to expand the link group is used instead. - label.setAttribute("aria-hidden", "true"); + if (!isLanguageSelection || !menuItem.identifier) { + label.addEventListener("click", (event) => { + event.preventDefault(); + const button = label.nextElementSibling; + button.click(); + }); + // The button to expand the link group is used instead. + label.setAttribute("aria-hidden", "true"); + } + if (isLanguageSelection && menuItem.identifier) { + label.dataset.identifier = menuItem.identifier; + } listItem.append(label); } if (menuItem.children.length) { @@ -204,7 +259,7 @@ define(["require", "exports", "tslib", "./Container", "../../../Language", "../. ariaLabel = Language.get("wcf.menu.page.button.toggle", { title: menuItem.title }); } button.setAttribute("aria-label", ariaLabel); - const list = this.buildMenuItemList(menuItem.children); + const list = this.buildMenuItemList(menuItem.children, isLanguageSelection); list.id = menuId; list.hidden = true; button.addEventListener("click", (event) => { diff --git a/wcfsetup/install/files/style/ui/pageMenu.scss b/wcfsetup/install/files/style/ui/pageMenu.scss index 5c7fd49e47..1d8c4469c0 100644 --- a/wcfsetup/install/files/style/ui/pageMenu.scss +++ b/wcfsetup/install/files/style/ui/pageMenu.scss @@ -55,6 +55,7 @@ } } +.pageMenuMainNavigationLanguage, .pageMenuMainNavigationFooter { /* The footer is placed at the very bottom of the main menu which is accomplished by setting `margin-top: auto`. However, this @@ -69,6 +70,11 @@ margin-top: auto; } +.pageMenuMainNavigationLanguage + .pageMenuMainNavigationFooter { + padding-top: 0; + margin-top: 0; +} + .pageMenuMainItem { border-bottom: 1px solid var(--border-color); column-gap: 10px; -- 2.20.1