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 () {
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())
+ }
}
// 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
}
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()
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;