* Enables the mobile UI.
*/
export function enable(): void {
+ UiCloseOverlay.execute();
+
_enabled = true;
if (_enableMobileMenu) {
_pageMenuMain.enable();
* Disables the mobile UI.
*/
export function disable(): void {
+ UiCloseOverlay.execute();
+
_enabled = false;
if (_enableMobileMenu) {
_pageMenuMain.disable();
this.buildElements();
- this.content.innerHTML = "";
- this.content.append(this.provider.getContent());
+ if (this.content.childElementCount === 0) {
+ this.content.append(this.provider.getContent());
+ }
+
this.provider.getMenuButton().setAttribute("aria-expanded", "true");
pageOverlayOpen();
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 userMenuProviders = new Map<HTMLAnchorElement, UserMenuProvider>();
private readonly tabPanels = new Map<HTMLAnchorElement, HTMLDivElement>();
private readonly tabs: HTMLAnchorElement[] = [];
private readonly userMenu: HTMLElement;
getContent(): DocumentFragment {
const fragment = document.createDocumentFragment();
- fragment.append(...this.buildTabMenu());
+ fragment.append(this.buildTabMenu());
return fragment;
}
}
refresh(): void {
- this.openNotifications();
+ const activeTab = this.tabs.find((element) => element.getAttribute("aria-selected") === "true");
+ if (activeTab === undefined) {
+ this.openNotifications();
+ } else {
+ // The UI elements in the tab panel are shared and can appear in a different
+ // context. The element might have been moved elsewhere while the menu was
+ // closed.
+ this.attachViewToPanel(activeTab);
+ }
}
private openNotifications(): void {
throw new Error("Unable to find the notifications tab.");
}
- notifications.click();
+ this.openTab(notifications);
}
private openTab(tab: HTMLAnchorElement): void {
if (document.activeElement !== tab) {
tab.focus();
}
+
+ this.attachViewToPanel(tab);
+ }
+
+ private attachViewToPanel(tab: HTMLAnchorElement): void {
+ const tabPanel = this.tabPanels.get(tab)!;
+ if (tabPanel.childElementCount === 0) {
+ const provider = this.userMenuProviders.get(tab);
+ if (provider) {
+ const view = provider.getView();
+ tabPanel.append(view.getElement());
+ void view.open();
+ } else {
+ throw new Error("TODO: Legacy user panel menus");
+ }
+ }
}
private keydown(event: KeyboardEvent): void {
this.tabs[index].focus();
}
- private buildTabMenu(): TabMenu {
+ private buildTabMenu(): HTMLDivElement {
+ const tabContainer = document.createElement("div");
+ tabContainer.classList.add("pageMenuUserTabContainer");
+
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");
+ tabContainer.append(tabList);
// TODO: Inject the control panel first.
const [tab, tabPanel] = this.buildTab(provider);
tabList.append(tab);
- tabPanelContainer.append(tabPanel);
+ tabContainer.append(tabPanel);
this.tabs.push(tab);
this.tabPanels.set(tab, tabPanel);
+ this.userMenuProviders.set(tab, provider);
});
// TODO: Inject legacy user panel items.
- return [tabList, tabPanelContainer];
+ return tabContainer;
}
private buildTab(provider: UserMenuProvider): TabComponents {
panel.setAttribute("aria-labelledby", tabId);
panel.setAttribute("role", "tabpanel");
panel.tabIndex = 0;
- panel.textContent = "panel for #" + provider.getPanelButton().id;
return [tab, panel];
}
* Enables the mobile UI.
*/
function enable() {
+ CloseOverlay_1.default.execute();
_enabled = true;
if (_enableMobileMenu) {
_pageMenuMain.enable();
* Disables the mobile UI.
*/
function disable() {
+ CloseOverlay_1.default.execute();
_enabled = false;
if (_enableMobileMenu) {
_pageMenuMain.disable();
open() {
CloseOverlay_1.default.execute();
this.buildElements();
- this.content.innerHTML = "";
- this.content.append(this.provider.getContent());
+ if (this.content.childElementCount === 0) {
+ this.content.append(this.provider.getContent());
+ }
this.provider.getMenuButton().setAttribute("aria-expanded", "true");
(0, Screen_1.pageOverlayOpen)();
(0, Screen_1.scrollDisable)();
Util_1 = (0, tslib_1.__importDefault)(Util_1);
class PageMenuUser {
constructor() {
+ this.userMenuProviders = new Map();
this.tabPanels = new Map();
this.tabs = [];
this.userMenu = document.querySelector(".userPanel");
}
getContent() {
const fragment = document.createDocumentFragment();
- fragment.append(...this.buildTabMenu());
+ fragment.append(this.buildTabMenu());
return fragment;
}
getMenuButton() {
return this.userMenu;
}
refresh() {
- this.openNotifications();
+ const activeTab = this.tabs.find((element) => element.getAttribute("aria-selected") === "true");
+ if (activeTab === undefined) {
+ this.openNotifications();
+ }
+ else {
+ // The UI elements in the tab panel are shared and can appear in a different
+ // context. The element might have been moved elsewhere while the menu was
+ // closed.
+ this.attachViewToPanel(activeTab);
+ }
}
openNotifications() {
const notifications = this.tabs.find((element) => element.dataset.origin === "userNotifications");
if (!notifications) {
throw new Error("Unable to find the notifications tab.");
}
- notifications.click();
+ this.openTab(notifications);
}
openTab(tab) {
if (tab.getAttribute("aria-selected") === "true") {
if (document.activeElement !== tab) {
tab.focus();
}
+ this.attachViewToPanel(tab);
+ }
+ attachViewToPanel(tab) {
+ const tabPanel = this.tabPanels.get(tab);
+ if (tabPanel.childElementCount === 0) {
+ const provider = this.userMenuProviders.get(tab);
+ if (provider) {
+ const view = provider.getView();
+ tabPanel.append(view.getElement());
+ void view.open();
+ }
+ else {
+ throw new Error("TODO: Legacy user panel menus");
+ }
+ }
}
keydown(event) {
const tab = event.currentTarget;
this.tabs[index].focus();
}
buildTabMenu() {
+ const tabContainer = document.createElement("div");
+ tabContainer.classList.add("pageMenuUserTabContainer");
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");
+ tabContainer.append(tabList);
// TODO: Inject the control panel first.
(0, Manager_1.getUserMenuProviders)().forEach((provider) => {
const [tab, tabPanel] = this.buildTab(provider);
tabList.append(tab);
- tabPanelContainer.append(tabPanel);
+ tabContainer.append(tabPanel);
this.tabs.push(tab);
this.tabPanels.set(tab, tabPanel);
+ this.userMenuProviders.set(tab, provider);
});
// TODO: Inject legacy user panel items.
- return [tabList, tabPanelContainer];
+ return tabContainer;
}
buildTab(provider) {
const tabId = Util_1.default.getUniqueId();
panel.setAttribute("aria-labelledby", tabId);
panel.setAttribute("role", "tabpanel");
panel.tabIndex = 0;
- panel.textContent = "panel for #" + provider.getPanelButton().id;
return [tab, panel];
}
}
.userPanelAvatar {
display: block;
- padding: 0 5px;
}
}
--- /dev/null
+.pageMenuContainer {
+ background-color: rgba(0, 0, 0, 0.34);
+ bottom: 0;
+ display: grid;
+ grid-template-areas: "content";
+ grid-template-columns: auto;
+ left: 0;
+ overflow: hidden;
+ position: fixed;
+ right: 0;
+ top: 50px;
+ z-index: 300;
+}
+
+.pageMenuContent {
+ --background-color: #{$wcfUserMenuBackground};
+ --background-color-active: #{$wcfUserMenuBackgroundActive};
+ --border-color: #{$wcfUserMenuBorder};
+ --color: #{$wcfUserMenuText};
+ --color-dimmed: #{$wcfUserMenuTextDimmed};
+ --color-indicator: #{$wcfUserMenuIndicator};
+
+ background-color: var(--background-color);
+ grid-area: content;
+}
+
+.pageMenuMainContainer {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+
+.pageMenuMainNavigationFooter {
+ margin-top: auto;
+}
+
+.pageMenuMainItem {
+ border-bottom: 1px solid var(--border-color);
+ position: relative;
+}
+
+.pageMenuMainItemExpandable {
+ display: grid;
+ grid-template-areas:
+ "item button"
+ "list list";
+ grid-template-columns: auto 44px;
+}
+
+.pageMenuMainItemLink {
+ align-items: center;
+ color: inherit;
+ display: flex;
+ font-weight: 600;
+ grid-area: item;
+ min-height: 44px;
+ padding: 0 10px;
+}
+
+.pageMenuMainItemToggle {
+ align-items: center;
+ display: flex;
+ justify-content: center;
+ position: relative;
+
+ &::before {
+ border-left: 1px solid var(--border-color);
+ bottom: 10px;
+ content: "";
+ left: 0;
+ position: absolute;
+ top: 10px;
+ }
+
+ .icon {
+ transform: rotate(0);
+ }
+
+ &[aria-expanded="true"] .icon {
+ transform: rotate(180deg);
+ }
+}
+
+.pageMenuMainItemList {
+ grid-area: list;
+}
+
+.pageMenuMainItem .pageMenuMainItemList {
+ padding: 10px 0 20px 0;
+
+ .pageMenuMainItem {
+ border-bottom-width: 0;
+ }
+
+ .pageMenuMainItemLink {
+ font-weight: 400;
+ min-height: 34px;
+ padding-left: 20px;
+ }
+}
+
+.pageMenuMainNavigationFooter .pageMenuMainItem:last-child {
+ border-bottom-width: 0;
+}
+
+.pageMenuUserTabContainer {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+
+.pageMenuUserTabList {
+ border-bottom: 1px solid var(--border-color);
+ display: grid;
+ grid-auto-columns: minmax(70px, 1fr);
+ grid-auto-flow: column;
+ overflow: auto;
+}
+
+.pageMenuUserTab {
+ align-items: center;
+ display: flex;
+ justify-content: center;
+ height: 50px;
+ position: relative;
+
+ &:not(:last-child) {
+ border-right: 1px solid var(--border-color);
+ }
+
+ &[aria-selected="true"] {
+ background-color: var(--background-color-active);
+ }
+}
+
+.pageMenuUserTabPanel {
+ flex: 1 auto;
+}
+
+@include screen-md-down {
+ .mainMenu[aria-expanded="true"]::before {
+ content: $fa-var-times;
+ }
+
+ .userPanel.userPanelLoggedIn[aria-expanded="true"] {
+ &::before {
+ content: $fa-var-times;
+ color: $wcfHeaderLink;
+ font-family: FontAwesome;
+ font-size: 28px;
+ line-height: 32px;
+ padding: 5px 5px;
+ }
+
+ .userPanelAvatar {
+ display: none;
+ }
+ }
+}
--color-indicator: #{$wcfUserMenuIndicator};
background-color: var(--background-color);
- border-radius: 5px;
- box-shadow: 0 19px 38px rgb(0 0 0 / 30%), 0 15px 12px rgb(0 0 0 / 22%);
color: var(--color);
pointer-events: all;
- position: fixed;
- width: 400px;
- z-index: 450;
&.userMenuControlPanel {
.userMenuItemImage {
.icon {
color: var(--color);
}
+}
- /* TODO: This is for the old mobile menu only. */
- @include screen-xs {
- border-radius: 0;
- bottom: 0 !important;
- display: flex;
- left: 0 !important;
- flex-direction: column;
- right: 0 !important;
- top: 50px !important;
- width: 100%;
-
- .userMenuContent {
- flex: 1 auto;
- }
+.dropdownMenuContainer .userMenu {
+ border-radius: 5px;
+ box-shadow: 0 19px 38px rgb(0 0 0 / 30%), 0 15px 12px rgb(0 0 0 / 22%);
+ position: fixed;
+ width: 400px;
+ z-index: 450;
+}
+
+.pageMenuUserTabPanel .userMenu {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+
+ .userMenuContent {
+ flex: 1 auto;
}
}