import UiCloseOverlay from "./CloseOverlay";
import * as UiDropdownReusable from "./Dropdown/Reusable";
import { PageMenuMain } from "./Page/Menu/Main";
-import UiPageMenuUser from "./Page/Menu/User";
+import { hasValidUserMenu, PageMenuUser } from "./Page/Menu/User";
import * as UiScreen from "./Screen";
interface MainMenuMorePayload {
const _knownMessages = new WeakSet<HTMLElement>();
let _mobileSidebarEnabled = false;
let _pageMenuMain: PageMenuMain;
-let _pageMenuUser: UiPageMenuUser | undefined = undefined;
+let _pageMenuUser: PageMenuUser | undefined = undefined;
let _messageGroups: HTMLCollection | null = null;
const _sidebars: HTMLElement[] = [];
_pageMenuMain = new PageMenuMain();
_pageMenuMain.enable();
- if (UiPageMenuUser.hasValidMenu()) {
- _pageMenuUser = new UiPageMenuUser();
+ if (hasValidUserMenu()) {
+ _pageMenuUser = new PageMenuUser();
+ _pageMenuUser.enable();
}
}
}
/**
- * Provides the touch-friendly fullscreen main menu.
+ * Provides the touch-friendly main menu.
*
* @author Alexander Ebert
* @copyright 2001-2021 WoltLab GmbH
/**
- * Provides the touch-friendly fullscreen user menu.
+ * Provides the touch-friendly user menu.
*
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module WoltLabSuite/Core/Ui/Page/Menu/User
+ * @author Alexander Ebert
+ * @copyright 2001-2021 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Menu/User
*/
-import * as Core from "../../../Core";
-import * as EventHandler from "../../../Event/Handler";
+import PageMenuContainer from "./Container";
+import { PageMenuProvider } from "./Provider";
import * as Language from "../../../Language";
-import UiPageMenuAbstract from "./Abstract";
+import { getUserMenuProviders } from "../../User/Menu/Manager";
+import { UserMenuProvider } from "../../User/Menu/Data/Provider";
+import DomUtil from "../../../Dom/Util";
-interface EventPayload {
- count: number;
- identifier: string;
-}
+type CallbackOpen = (event: MouseEvent) => void;
+
+type Tab = HTMLAnchorElement;
+type TabPanel = HTMLDivElement;
+type TabComponents = [Tab, TabPanel];
+
+type TabList = HTMLDivElement;
+type TabPanelContainer = HTMLDivElement;
+type TabMenu = [TabList, TabPanelContainer];
+
+export class PageMenuUser implements PageMenuProvider {
+ private readonly callbackOpen: CallbackOpen;
+ private readonly container: PageMenuContainer;
+ private readonly userMenu: HTMLElement;
-class UiPageMenuUser extends UiPageMenuAbstract {
- /**
- * Initializes the touch-friendly fullscreen user menu.
- */
constructor() {
- super("com.woltlab.wcf.UserMenuMobile", "pageUserMenuMobile", "#pageHeader .userPanel");
+ this.userMenu = document.querySelector(".userPanel")!;
+
+ this.container = new PageMenuContainer(this);
- EventHandler.add("com.woltlab.wcf.userMenu", "updateBadge", (data) => this.updateBadge(data));
+ this.callbackOpen = (event) => {
+ event.preventDefault();
+ event.stopPropagation();
- this.button.setAttribute("aria-label", Language.get("wcf.menu.user"));
- this.button.setAttribute("role", "button");
+ this.container.toggle();
+ };
}
- close(event?: Event): boolean {
- // The user menu is not initialized if there are no items to display.
- if (this.menu === undefined) {
- return false;
- }
+ enable(): void {
+ this.userMenu.setAttribute("aria-expanded", "false");
+ this.userMenu.setAttribute("role", "button");
+ this.userMenu.tabIndex = 0;
+ this.userMenu.addEventListener("click", this.callbackOpen);
+ }
- const dropdown = window.WCF.Dropdown.Interactive.Handler.getOpenDropdown();
- if (dropdown) {
- if (event) {
- event.preventDefault();
- event.stopPropagation();
- }
+ disable(): void {
+ this.container.close();
- dropdown.close();
+ this.userMenu.removeAttribute("aria-expanded");
+ this.userMenu.removeAttribute("role");
+ this.userMenu.removeAttribute("tabindex");
+ this.userMenu.removeEventListener("click", this.callbackOpen);
+ }
- return true;
- }
+ getContent(): DocumentFragment {
+ const fragment = document.createDocumentFragment();
+ fragment.append(...this.buildTabMenu());
- return super.close(event);
+ return fragment;
}
- private updateBadge(data: EventPayload): void {
- this.menu.querySelectorAll(".menuOverlayItemBadge").forEach((item: HTMLElement) => {
- if (item.dataset.badgeIdentifier === data.identifier) {
- let badge = item.querySelector(".badge");
- if (data.count) {
- if (badge === null) {
- badge = document.createElement("span");
- badge.className = "badge badgeUpdate";
- item.appendChild(badge);
- }
-
- badge.textContent = data.count.toString();
- } else if (badge !== null) {
- badge.remove();
- }
-
- this.updateButtonState();
- }
- });
+ getMenuButton(): HTMLElement {
+ return this.userMenu;
}
- static hasValidMenu(): boolean {
- const menu = document.querySelector("#pageUserMenuMobile > .menuOverlayItemList")!;
- if (menu.childElementCount === 1 && menu.children[0].classList.contains("menuOverlayTitle")) {
- const userPanel = document.querySelector("#pageHeader .userPanel")!;
- userPanel.classList.add("hideUserPanel");
- return false;
- }
+ private buildTabMenu(): TabMenu {
+ const tabList = document.createElement("div");
+ tabList.classList.add("pageMenuUserTabList");
+ tabList.setAttribute("role", "tablist");
+ tabList.setAttribute("aria-label", Language.get("TODO"));
+
+ const tabPanelContainer = document.createElement("div");
- return true;
+ // TODO: Inject the control panel first.
+
+ getUserMenuProviders().forEach((provider) => {
+ const [tab, tabPanel] = this.buildTab(provider);
+
+ tabList.append(tab);
+ tabPanelContainer.append(tabPanel);
+ });
+
+ // TODO: Inject legacy user panel items.
+
+ return [tabList, tabPanelContainer];
+ }
+
+ private buildTab(provider: UserMenuProvider): TabComponents {
+ const tabId = DomUtil.getUniqueId();
+ const panelId = DomUtil.getUniqueId();
+
+ const tab = document.createElement("a");
+ tab.classList.add("pageMenuUserTab");
+ tab.id = tabId;
+ tab.setAttribute("aria-controls", panelId);
+ tab.setAttribute("aria-selected", "false");
+ tab.setAttribute("role", "tab");
+ tab.tabIndex = -1;
+
+ const button = provider.getPanelButton().querySelector("a")!;
+ tab.setAttribute("aria-label", button.dataset.title || button.title);
+ tab.innerHTML = button.querySelector(".icon")!.outerHTML;
+
+ const panel = document.createElement("div");
+ panel.classList.add("pageMenuUserTabPanel");
+ panel.id = panelId;
+ panel.hidden = true;
+ panel.setAttribute("aria-labelledby", tabId);
+ panel.setAttribute("role", "tabpanel");
+ panel.tabIndex = 0;
+
+ return [tab, panel];
}
}
-Core.enableLegacyInheritance(UiPageMenuUser);
+export function hasValidUserMenu(): boolean {
+ return true;
+}
-export = UiPageMenuUser;
+export default PageMenuUser;
return views.get(provider)!;
}
+export function getUserMenuProviders(): Set<UserMenuProvider> {
+ return providers;
+}
+
export function getContainer(): HTMLElement {
if (container === undefined) {
container = document.createElement("div");
UiAlignment = (0, tslib_1.__importStar)(UiAlignment);
CloseOverlay_1 = (0, tslib_1.__importDefault)(CloseOverlay_1);
UiDropdownReusable = (0, tslib_1.__importStar)(UiDropdownReusable);
- User_1 = (0, tslib_1.__importDefault)(User_1);
UiScreen = (0, tslib_1.__importStar)(UiScreen);
let _dropdownMenu = null;
let _dropdownMenuMessage = null;
if (_enableMobileMenu) {
_pageMenuMain = new Main_1.PageMenuMain();
_pageMenuMain.enable();
- if (User_1.default.hasValidMenu()) {
- _pageMenuUser = new User_1.default();
+ if ((0, User_1.hasValidUserMenu)()) {
+ _pageMenuUser = new User_1.PageMenuUser();
+ _pageMenuUser.enable();
}
}
}
/**
- * Provides the touch-friendly fullscreen main menu.
+ * Provides the touch-friendly main menu.
*
* @author Alexander Ebert
* @copyright 2001-2021 WoltLab GmbH
/**
- * Provides the touch-friendly fullscreen user menu.
+ * Provides the touch-friendly user menu.
*
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module WoltLabSuite/Core/Ui/Page/Menu/User
+ * @author Alexander Ebert
+ * @copyright 2001-2021 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Ui/Page/Menu/User
*/
-define(["require", "exports", "tslib", "../../../Core", "../../../Event/Handler", "../../../Language", "./Abstract"], function (require, exports, tslib_1, Core, EventHandler, Language, Abstract_1) {
+define(["require", "exports", "tslib", "./Container", "../../../Language", "../../User/Menu/Manager", "../../../Dom/Util"], function (require, exports, tslib_1, Container_1, Language, Manager_1, Util_1) {
"use strict";
- Core = (0, tslib_1.__importStar)(Core);
- EventHandler = (0, tslib_1.__importStar)(EventHandler);
+ Object.defineProperty(exports, "__esModule", { value: true });
+ exports.hasValidUserMenu = exports.PageMenuUser = void 0;
+ Container_1 = (0, tslib_1.__importDefault)(Container_1);
Language = (0, tslib_1.__importStar)(Language);
- Abstract_1 = (0, tslib_1.__importDefault)(Abstract_1);
- class UiPageMenuUser extends Abstract_1.default {
- /**
- * Initializes the touch-friendly fullscreen user menu.
- */
+ Util_1 = (0, tslib_1.__importDefault)(Util_1);
+ class PageMenuUser {
constructor() {
- super("com.woltlab.wcf.UserMenuMobile", "pageUserMenuMobile", "#pageHeader .userPanel");
- EventHandler.add("com.woltlab.wcf.userMenu", "updateBadge", (data) => this.updateBadge(data));
- this.button.setAttribute("aria-label", Language.get("wcf.menu.user"));
- this.button.setAttribute("role", "button");
+ this.userMenu = document.querySelector(".userPanel");
+ this.container = new Container_1.default(this);
+ this.callbackOpen = (event) => {
+ event.preventDefault();
+ event.stopPropagation();
+ this.container.toggle();
+ };
}
- close(event) {
- // The user menu is not initialized if there are no items to display.
- if (this.menu === undefined) {
- return false;
- }
- const dropdown = window.WCF.Dropdown.Interactive.Handler.getOpenDropdown();
- if (dropdown) {
- if (event) {
- event.preventDefault();
- event.stopPropagation();
- }
- dropdown.close();
- return true;
- }
- return super.close(event);
+ enable() {
+ this.userMenu.setAttribute("aria-expanded", "false");
+ this.userMenu.setAttribute("role", "button");
+ this.userMenu.tabIndex = 0;
+ this.userMenu.addEventListener("click", this.callbackOpen);
}
- updateBadge(data) {
- this.menu.querySelectorAll(".menuOverlayItemBadge").forEach((item) => {
- if (item.dataset.badgeIdentifier === data.identifier) {
- let badge = item.querySelector(".badge");
- if (data.count) {
- if (badge === null) {
- badge = document.createElement("span");
- badge.className = "badge badgeUpdate";
- item.appendChild(badge);
- }
- badge.textContent = data.count.toString();
- }
- else if (badge !== null) {
- badge.remove();
- }
- this.updateButtonState();
- }
+ disable() {
+ this.container.close();
+ this.userMenu.removeAttribute("aria-expanded");
+ this.userMenu.removeAttribute("role");
+ this.userMenu.removeAttribute("tabindex");
+ this.userMenu.removeEventListener("click", this.callbackOpen);
+ }
+ getContent() {
+ const fragment = document.createDocumentFragment();
+ fragment.append(...this.buildTabMenu());
+ return fragment;
+ }
+ getMenuButton() {
+ return this.userMenu;
+ }
+ buildTabMenu() {
+ const tabList = document.createElement("div");
+ tabList.classList.add("pageMenuUserTabList");
+ tabList.setAttribute("role", "tablist");
+ tabList.setAttribute("aria-label", Language.get("TODO"));
+ const tabPanelContainer = document.createElement("div");
+ // TODO: Inject the control panel first.
+ (0, Manager_1.getUserMenuProviders)().forEach((provider) => {
+ const [tab, tabPanel] = this.buildTab(provider);
+ tabList.append(tab);
+ tabPanelContainer.append(tabPanel);
});
+ // TODO: Inject legacy user panel items.
+ return [tabList, tabPanelContainer];
}
- static hasValidMenu() {
- const menu = document.querySelector("#pageUserMenuMobile > .menuOverlayItemList");
- if (menu.childElementCount === 1 && menu.children[0].classList.contains("menuOverlayTitle")) {
- const userPanel = document.querySelector("#pageHeader .userPanel");
- userPanel.classList.add("hideUserPanel");
- return false;
- }
- return true;
+ buildTab(provider) {
+ const tabId = Util_1.default.getUniqueId();
+ const panelId = Util_1.default.getUniqueId();
+ const tab = document.createElement("a");
+ tab.classList.add("pageMenuUserTab");
+ tab.id = tabId;
+ tab.setAttribute("aria-controls", panelId);
+ tab.setAttribute("aria-selected", "false");
+ tab.setAttribute("role", "tab");
+ tab.tabIndex = -1;
+ const button = provider.getPanelButton().querySelector("a");
+ tab.setAttribute("aria-label", button.dataset.title || button.title);
+ tab.innerHTML = button.querySelector(".icon").outerHTML;
+ const panel = document.createElement("div");
+ panel.classList.add("pageMenuUserTabPanel");
+ panel.id = panelId;
+ panel.hidden = true;
+ panel.setAttribute("aria-labelledby", tabId);
+ panel.setAttribute("role", "tabpanel");
+ panel.tabIndex = 0;
+ return [tab, panel];
}
}
- Core.enableLegacyInheritance(UiPageMenuUser);
- return UiPageMenuUser;
+ exports.PageMenuUser = PageMenuUser;
+ function hasValidUserMenu() {
+ return true;
+ }
+ exports.hasValidUserMenu = hasValidUserMenu;
+ exports.default = PageMenuUser;
});
define(["require", "exports", "tslib", "../../Alignment", "../../CloseOverlay", "../../../Event/Handler", "../../../Dom/Util"], function (require, exports, tslib_1, Alignment, CloseOverlay_1, EventHandler, Util_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
- exports.registerProvider = exports.getContainer = void 0;
+ exports.registerProvider = exports.getContainer = exports.getUserMenuProviders = void 0;
Alignment = (0, tslib_1.__importStar)(Alignment);
CloseOverlay_1 = (0, tslib_1.__importDefault)(CloseOverlay_1);
EventHandler = (0, tslib_1.__importStar)(EventHandler);
}
return views.get(provider);
}
+ function getUserMenuProviders() {
+ return providers;
+ }
+ exports.getUserMenuProviders = getUserMenuProviders;
function getContainer() {
if (container === undefined) {
container = document.createElement("div");