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:
parent
30d55e75ca
commit
245b9d27bc
|
@ -180,8 +180,8 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||||
|
|
||||||
eventsObs.pipe(
|
eventsObs.pipe(
|
||||||
filter((e: Event): e is GuardsCheckStart => e instanceof GuardsCheckStart),
|
filter((e: Event): e is GuardsCheckStart => e instanceof GuardsCheckStart),
|
||||||
filter(() => this.screenService.isInSmallView())
|
filter(() => this.screenService.isInSmallView() || !!this.screenService.isInTouchScreen())
|
||||||
).subscribe(() => this.menu.isMenuDisplayed = false) // User clicked on a link in the menu, change the page
|
).subscribe(() => this.menu.setMenuDisplay(false)) // User clicked on a link in the menu, change the page
|
||||||
}
|
}
|
||||||
|
|
||||||
private injectBroadcastMessage () {
|
private injectBroadcastMessage () {
|
||||||
|
|
|
@ -12,22 +12,45 @@ export class MenuService {
|
||||||
constructor (
|
constructor (
|
||||||
private screenService: ScreenService
|
private screenService: ScreenService
|
||||||
) {
|
) {
|
||||||
// Do not display menu on small screens
|
// Do not display menu on small or touch screens
|
||||||
if (this.screenService.isInSmallView()) {
|
if (this.screenService.isInSmallView() || this.screenService.isInTouchScreen()) {
|
||||||
this.isMenuDisplayed = false
|
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')
|
fromEvent(window, 'resize')
|
||||||
.pipe(debounceTime(200))
|
.pipe(debounceTime(200))
|
||||||
.subscribe(() => this.onResize())
|
.subscribe(() => this.onResize())
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleMenu () {
|
|
||||||
this.isMenuDisplayed = !this.isMenuDisplayed
|
|
||||||
this.isMenuChangedByUser = true
|
|
||||||
}
|
|
||||||
|
|
||||||
onResize () {
|
|
||||||
this.isMenuDisplayed = window.innerWidth >= 800 && !this.isMenuChangedByUser
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ abstract class MenuGuard implements CanActivate, CanDeactivate<any> {
|
||||||
// small screens already have the site-wide onResize from screenService
|
// small screens already have the site-wide onResize from screenService
|
||||||
// > medium screens have enough space to fit the administrative menus
|
// > medium screens have enough space to fit the administrative menus
|
||||||
if (!this.screen.isInMobileView() && this.screen.isInMediumView()) {
|
if (!this.screen.isInMobileView() && this.screen.isInMediumView()) {
|
||||||
this.menu.isMenuDisplayed = this.display
|
this.menu.setMenuDisplay(this.display)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,64 @@ export class ScreenService {
|
||||||
return this.windowInnerWidth
|
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 () {
|
private refreshWindowInnerWidth () {
|
||||||
this.lastFunctionCallTime = new Date().getTime()
|
this.lastFunctionCallTime = new Date().getTime()
|
||||||
|
|
||||||
|
|
23
client/src/sass/bootstrap.scss
vendored
23
client/src/sass/bootstrap.scss
vendored
|
@ -150,6 +150,29 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
|
||||||
width: 100vw; // Make sure the content fits all the available width
|
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 customizations
|
||||||
.nav .nav-link {
|
.nav .nav-link {
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user