]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
On touchscreens add content overlay for opened menu (#3088)
authorKim <1877318+kimsible@users.noreply.github.com>
Mon, 17 Aug 2020 08:17:54 +0000 (10:17 +0200)
committerGitHub <noreply@github.com>
Mon, 17 Aug 2020 08:17:54 +0000 (10:17 +0200)
* Overflow:hidden on touchscreen when modal-open

* Do not display menu by default on touchscreens

* Add content overlay on touchscreens when menu is opened

* Fix zIndex overlay for search infos

* On touchscreens close menu on swipe left

Co-authored-by: kimsible <kimsible@users.noreply.github.com>
client/src/app/app.component.ts
client/src/app/core/menu/menu.service.ts
client/src/app/core/routing/menu-guard.service.ts
client/src/app/core/wrappers/screen.service.ts
client/src/sass/bootstrap.scss

index b8af4e2c7889dd2cb1bebbd637be6a7cfc2ebf16..5b0439e6bb30f28e97c0608e6db5ada6ff02efd2 100644 (file)
@@ -180,8 +180,8 @@ export class AppComponent implements OnInit, AfterViewInit {
 
     eventsObs.pipe(
       filter((e: Event): e is GuardsCheckStart => e instanceof GuardsCheckStart),
-      filter(() => this.screenService.isInSmallView())
-    ).subscribe(() => this.menu.isMenuDisplayed = false) // User clicked on a link in the menu, change the page
+      filter(() => this.screenService.isInSmallView() || !!this.screenService.isInTouchScreen())
+    ).subscribe(() => this.menu.setMenuDisplay(false)) // User clicked on a link in the menu, change the page
   }
 
   private injectBroadcastMessage () {
index ef5271f976056d30711f8c1b7a9f919e206a3eb1..671ee3e4f55b5d623f329097bf0b0e223efc70e4 100644 (file)
@@ -12,22 +12,45 @@ export class MenuService {
   constructor (
     private screenService: ScreenService
   ) {
-    // Do not display menu on small screens
-    if (this.screenService.isInSmallView()) {
-      this.isMenuDisplayed = false
+    // Do not display menu on small or touch screens
+    if (this.screenService.isInSmallView() || this.screenService.isInTouchScreen()) {
+      this.setMenuDisplay(false)
     }
 
-    fromEvent(window, 'resize')
-      .pipe(debounceTime(200))
-      .subscribe(() => this.onResize())
+    this.handleWindowResize()
   }
 
   toggleMenu () {
-    this.isMenuDisplayed = !this.isMenuDisplayed
+    this.setMenuDisplay(!this.isMenuDisplayed)
     this.isMenuChangedByUser = true
   }
 
+  setMenuDisplay (display: boolean) {
+    this.isMenuDisplayed = display
+
+    // On touch screens, lock body scroll and display content overlay when memu is opened
+    if (this.screenService.isInTouchScreen()) {
+      if (this.isMenuDisplayed) {
+        document.body.classList.add('menu-open')
+        this.screenService.onFingerSwipe('left', () => { this.setMenuDisplay(false) })
+      } else {
+        document.body.classList.remove('menu-open')
+      }
+    }
+  }
+
   onResize () {
     this.isMenuDisplayed = window.innerWidth >= 800 && !this.isMenuChangedByUser
   }
+
+  private handleWindowResize () {
+    // On touch screens, do not handle window resize event since opened menu is handled with a content overlay
+    if (this.screenService.isInTouchScreen()) {
+      return
+    }
+
+    fromEvent(window, 'resize')
+      .pipe(debounceTime(200))
+      .subscribe(() => this.onResize())
+  }
 }
index 9df2856351133da0ab9a87ef09bbdb090a5dcb1e..501e009c0925f25a08d8bc95c8b76466418d5d45 100644 (file)
@@ -15,7 +15,7 @@ abstract class MenuGuard implements CanActivate, CanDeactivate<any> {
     // small screens already have the site-wide onResize from screenService
     // > medium screens have enough space to fit the administrative menus
     if (!this.screen.isInMobileView() && this.screen.isInMediumView()) {
-      this.menu.isMenuDisplayed = this.display
+      this.menu.setMenuDisplay(this.display)
     }
     return true
   }
index 96fb7ae57bc3a61dcc5a89efbcb06c11d348e701..88cf662b3e624734af90d594bfa8e8027b0be45d 100644 (file)
@@ -54,6 +54,64 @@ export class ScreenService {
     return this.windowInnerWidth
   }
 
+  // https://stackoverflow.com/questions/2264072/detect-a-finger-swipe-through-javascript-on-the-iphone-and-android
+  onFingerSwipe (direction: 'left' | 'right' | 'up' | 'down', action: () => void, removeEventOnEnd = true) {
+    let touchDownClientX: number
+    let touchDownClientY: number
+
+    const onTouchStart = (event: TouchEvent) => {
+      const firstTouch = event.touches[0]
+      touchDownClientX = firstTouch.clientX
+      touchDownClientY = firstTouch.clientY
+    }
+
+    const onTouchMove = (event: TouchEvent) => {
+      if (!touchDownClientX || !touchDownClientY) {
+        return
+      }
+
+      const touchUpClientX = event.touches[0].clientX
+      const touchUpClientY = event.touches[0].clientY
+
+      const touchClientX = Math.abs(touchDownClientX - touchUpClientX)
+      const touchClientY = Math.abs(touchDownClientY - touchUpClientY)
+
+      if (touchClientX > touchClientY) {
+        if (touchClientX > 0) {
+          if (direction === 'left') {
+            if (removeEventOnEnd) this.removeFingerSwipeEventListener(onTouchStart, onTouchMove)
+            action()
+          }
+        } else {
+          if (direction === 'right') {
+            if (removeEventOnEnd) this.removeFingerSwipeEventListener(onTouchStart, onTouchMove)
+            action()
+          }
+        }
+      } else {
+        if (touchClientY > 0) {
+          if (direction === 'up') {
+            if (removeEventOnEnd) this.removeFingerSwipeEventListener(onTouchStart, onTouchMove)
+            action()
+          }
+        } else {
+          if (direction === 'down') {
+            if (removeEventOnEnd) this.removeFingerSwipeEventListener(onTouchStart, onTouchMove)
+            action()
+          }
+        }
+      }
+    }
+
+    document.addEventListener('touchstart', onTouchStart, false)
+    document.addEventListener('touchmove', onTouchMove, false)
+  }
+
+  private removeFingerSwipeEventListener (onTouchStart: (event: TouchEvent) => void, onTouchMove: (event: TouchEvent) => void) {
+    document.removeEventListener('touchstart', onTouchStart)
+    document.removeEventListener('touchmove', onTouchMove)
+  }
+
   private refreshWindowInnerWidth () {
     this.lastFunctionCallTime = new Date().getTime()
 
index 308a28658d33316b3b0d40037abb0f9cc46c8919..a3b60198cceee2befb875e7154f3f510bc8b0c64 100644 (file)
@@ -150,6 +150,29 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
   width: 100vw; // Make sure the content fits all the available width
 }
 
+// On touchscreen devices, simply overflow: hidden to avoid detached overlay on scroll
+@media (hover: none) and (pointer: coarse) {
+  .modal-open, .menu-open {
+    overflow: hidden !important;
+  }
+
+  // On touchscreen devices display content overlay when opened menu
+  .menu-open {
+    .main-col {
+      &::before {
+        background-color: black;
+        width: 100vw;
+        height: 100vh;
+        opacity: 0.75;
+        content: '';
+        display: block;
+        position: fixed;
+        z-index: z('header') - 1;
+      }
+    }
+  }
+}
+
 // Nav customizations
 .nav .nav-link {
   display: flex !important;