From d363ef5360b479d5f494ac1559fba59b2069fe9e Mon Sep 17 00:00:00 2001 From: Kim <1877318+kimsible@users.noreply.github.com> Date: Thu, 30 Apr 2020 19:23:54 +0200 Subject: [PATCH] Use modal instead of dropdown menu in small/mobile views (#2674) Co-Authored-By: Rigel Kent <par@rigelk.eu> --- .../menu/top-menu-dropdown.component.html | 33 ++++++++++-- .../menu/top-menu-dropdown.component.scss | 29 +++++++++++ .../menu/top-menu-dropdown.component.ts | 50 ++++++++++++++++--- 3 files changed, 101 insertions(+), 11 deletions(-) diff --git a/client/src/app/shared/menu/top-menu-dropdown.component.html b/client/src/app/shared/menu/top-menu-dropdown.component.html index 3087b2e98..d577f757d 100644 --- a/client/src/app/shared/menu/top-menu-dropdown.component.html +++ b/client/src/app/shared/menu/top-menu-dropdown.component.html @@ -1,10 +1,20 @@ -<div class="sub-menu"> - <ng-container *ngFor="let menuEntry of menuEntries"> +<div class="sub-menu" [ngClass]="{ 'no-scroll': isModalOpened }"> + <ng-container *ngFor="let menuEntry of menuEntries; index as id"> <a *ngIf="menuEntry.routerLink" [routerLink]="menuEntry.routerLink" routerLinkActive="active" class="title-page title-page-settings">{{ menuEntry.label }}</a> - <div *ngIf="!menuEntry.routerLink" ngbDropdown [container]="container" class="parent-entry" #dropdown="ngbDropdown" (mouseleave)="closeDropdownIfHovered(dropdown)"> + <div *ngIf="!menuEntry.routerLink" ngbDropdown [container]="container" class="parent-entry" + #dropdown="ngbDropdown" (mouseleave)="closeDropdownIfHovered(dropdown)"> <span + *ngIf="isInSmallView" + [ngClass]="{ active: !!suffixLabels[menuEntry.label] }" + (click)="openModal(id)" role="button" class="title-page title-page-settings"> + <ng-container i18n>{{ menuEntry.label }}</ng-container> + <ng-container *ngIf="!!suffixLabels[menuEntry.label]"> - {{ suffixLabels[menuEntry.label] }}</ng-container> + </span> + + <span + *ngIf="!isInSmallView" (mouseenter)="openDropdownOnHover(dropdown)" [ngClass]="{ active: !!suffixLabels[menuEntry.label] }" ngbDropdownAnchor (click)="dropdownAnchorClicked(dropdown)" role="button" class="title-page title-page-settings" > @@ -20,6 +30,21 @@ </a> </div> </div> - </ng-container> </div> + +<ng-template #modal let-close="close" let-dismiss="dismiss"> + <div class="modal-body"> + <ng-container *ngFor="let menuEntry of menuEntries; index as id"> + <div [ngClass]="{ hidden: id !== currentMenuEntryIndex }"> + <a *ngFor="let menuChild of menuEntry.children" + [ngClass]="{ icon: hasIcons }" + [routerLink]="menuChild.routerLink" routerLinkActive="active" (click)="dismissOtherModals()"> + <my-global-icon *ngIf="menuChild.iconName" [iconName]="menuChild.iconName"></my-global-icon> + + {{ menuChild.label }} + </a> + </div> + </ng-container> + </div> +</ng-template> diff --git a/client/src/app/shared/menu/top-menu-dropdown.component.scss b/client/src/app/shared/menu/top-menu-dropdown.component.scss index 1be699a88..5f90dcf80 100644 --- a/client/src/app/shared/menu/top-menu-dropdown.component.scss +++ b/client/src/app/shared/menu/top-menu-dropdown.component.scss @@ -25,3 +25,32 @@ top: -1px; } + +.sub-menu.no-scroll { + overflow-x: hidden; +} + +.modal-body { + .hidden { + display: none; + } + + a { + @include disable-default-a-behaviour; + + color: currentColor; + box-sizing: border-box; + display: block; + font-size: 1.2rem; + padding: 9px 12px; + text-align: initial; + text-transform: unset; + width: 100%; + + &.active { + color: var(--mainBackgroundColor) !important; + background-color: var(--mainHoverColor); + opacity: .9; + } + } +} diff --git a/client/src/app/shared/menu/top-menu-dropdown.component.ts b/client/src/app/shared/menu/top-menu-dropdown.component.ts index 24a083654..f98240804 100644 --- a/client/src/app/shared/menu/top-menu-dropdown.component.ts +++ b/client/src/app/shared/menu/top-menu-dropdown.component.ts @@ -1,8 +1,14 @@ -import { Component, Input, OnDestroy, OnInit } from '@angular/core' +import { + Component, + Input, + OnDestroy, + OnInit, + ViewChild +} from '@angular/core' import { filter, take } from 'rxjs/operators' import { NavigationEnd, Router } from '@angular/router' import { Subscription } from 'rxjs' -import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap' +import { NgbDropdown, NgbModal } from '@ng-bootstrap/ng-bootstrap' import { GlobalIconName } from '@app/shared/images/global-icon.component' import { ScreenService } from '@app/shared/misc/screen.service' @@ -26,31 +32,40 @@ export type TopMenuDropdownParam = { export class TopMenuDropdownComponent implements OnInit, OnDestroy { @Input() menuEntries: TopMenuDropdownParam[] = [] + @ViewChild('modal', { static: true }) modal: NgbModal + suffixLabels: { [ parentLabel: string ]: string } hasIcons = false container: undefined | 'body' = undefined + isModalOpened = false + currentMenuEntryIndex: number private openedOnHover = false private routeSub: Subscription constructor ( private router: Router, + private modalService: NgbModal, private screen: ScreenService - ) {} + ) { } + + get isInSmallView () { + return this.screen.isInSmallView() + } ngOnInit () { this.updateChildLabels(window.location.pathname) this.routeSub = this.router.events - .pipe(filter(event => event instanceof NavigationEnd)) - .subscribe(() => this.updateChildLabels(window.location.pathname)) + .pipe(filter(event => event instanceof NavigationEnd)) + .subscribe(() => this.updateChildLabels(window.location.pathname)) this.hasIcons = this.menuEntries.some( e => e.children && e.children.some(c => !!c.iconName) ) - // We have to set body for the container to avoid scroll overflow on mobile view - if (this.screen.isInMobileView()) { + // We have to set body for the container to avoid scroll overflow on mobile and small views + if (this.isInSmallView) { this.container = 'body' } } @@ -85,6 +100,27 @@ export class TopMenuDropdownComponent implements OnInit, OnDestroy { this.openedOnHover = false } + openModal (index: number) { + this.currentMenuEntryIndex = index + this.isModalOpened = true + + this.modalService.open(this.modal, { + centered: true, + beforeDismiss: async () => { + this.onModalDismiss() + return true + } + }) + } + + onModalDismiss () { + this.isModalOpened = false + } + + dismissOtherModals () { + this.modalService.dismissAll() + } + private updateChildLabels (path: string) { this.suffixLabels = {}