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 = {}