On touchscreens add content overlay for opened menu (#3088)

* 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>
This commit is contained in:
Kim 2020-08-17 10:17:54 +02:00 committed by GitHub
parent 30d55e75ca
commit 245b9d27bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 119 additions and 15 deletions

View 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 () {

View 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)
}
this.handleWindowResize()
}
toggleMenu () {
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())
}
toggleMenu () {
this.isMenuDisplayed = !this.isMenuDisplayed
this.isMenuChangedByUser = true
}
onResize () {
this.isMenuDisplayed = window.innerWidth >= 800 && !this.isMenuChangedByUser
}
}

View 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
}

View 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()

View 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;