aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src
diff options
context:
space:
mode:
Diffstat (limited to 'client/src')
-rw-r--r--client/src/app/app.component.ts4
-rw-r--r--client/src/app/core/menu/menu.service.ts37
-rw-r--r--client/src/app/core/routing/menu-guard.service.ts2
-rw-r--r--client/src/app/core/wrappers/screen.service.ts58
-rw-r--r--client/src/sass/bootstrap.scss23
5 files changed, 114 insertions, 10 deletions
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts
index b8af4e2c7..5b0439e6b 100644
--- a/client/src/app/app.component.ts
+++ b/client/src/app/app.component.ts
@@ -180,8 +180,8 @@ export class AppComponent implements OnInit, AfterViewInit {
180 180
181 eventsObs.pipe( 181 eventsObs.pipe(
182 filter((e: Event): e is GuardsCheckStart => e instanceof GuardsCheckStart), 182 filter((e: Event): e is GuardsCheckStart => e instanceof GuardsCheckStart),
183 filter(() => this.screenService.isInSmallView()) 183 filter(() => this.screenService.isInSmallView() || !!this.screenService.isInTouchScreen())
184 ).subscribe(() => this.menu.isMenuDisplayed = false) // User clicked on a link in the menu, change the page 184 ).subscribe(() => this.menu.setMenuDisplay(false)) // User clicked on a link in the menu, change the page
185 } 185 }
186 186
187 private injectBroadcastMessage () { 187 private injectBroadcastMessage () {
diff --git a/client/src/app/core/menu/menu.service.ts b/client/src/app/core/menu/menu.service.ts
index ef5271f97..671ee3e4f 100644
--- a/client/src/app/core/menu/menu.service.ts
+++ b/client/src/app/core/menu/menu.service.ts
@@ -12,22 +12,45 @@ export class MenuService {
12 constructor ( 12 constructor (
13 private screenService: ScreenService 13 private screenService: ScreenService
14 ) { 14 ) {
15 // Do not display menu on small screens 15 // Do not display menu on small or touch screens
16 if (this.screenService.isInSmallView()) { 16 if (this.screenService.isInSmallView() || this.screenService.isInTouchScreen()) {
17 this.isMenuDisplayed = false 17 this.setMenuDisplay(false)
18 } 18 }
19 19
20 fromEvent(window, 'resize') 20 this.handleWindowResize()
21 .pipe(debounceTime(200))
22 .subscribe(() => this.onResize())
23 } 21 }
24 22
25 toggleMenu () { 23 toggleMenu () {
26 this.isMenuDisplayed = !this.isMenuDisplayed 24 this.setMenuDisplay(!this.isMenuDisplayed)
27 this.isMenuChangedByUser = true 25 this.isMenuChangedByUser = true
28 } 26 }
29 27
28 setMenuDisplay (display: boolean) {
29 this.isMenuDisplayed = display
30
31 // On touch screens, lock body scroll and display content overlay when memu is opened
32 if (this.screenService.isInTouchScreen()) {
33 if (this.isMenuDisplayed) {
34 document.body.classList.add('menu-open')
35 this.screenService.onFingerSwipe('left', () => { this.setMenuDisplay(false) })
36 } else {
37 document.body.classList.remove('menu-open')
38 }
39 }
40 }
41
30 onResize () { 42 onResize () {
31 this.isMenuDisplayed = window.innerWidth >= 800 && !this.isMenuChangedByUser 43 this.isMenuDisplayed = window.innerWidth >= 800 && !this.isMenuChangedByUser
32 } 44 }
45
46 private handleWindowResize () {
47 // On touch screens, do not handle window resize event since opened menu is handled with a content overlay
48 if (this.screenService.isInTouchScreen()) {
49 return
50 }
51
52 fromEvent(window, 'resize')
53 .pipe(debounceTime(200))
54 .subscribe(() => this.onResize())
55 }
33} 56}
diff --git a/client/src/app/core/routing/menu-guard.service.ts b/client/src/app/core/routing/menu-guard.service.ts
index 9df285635..501e009c0 100644
--- a/client/src/app/core/routing/menu-guard.service.ts
+++ b/client/src/app/core/routing/menu-guard.service.ts
@@ -15,7 +15,7 @@ abstract class MenuGuard implements CanActivate, CanDeactivate<any> {
15 // small screens already have the site-wide onResize from screenService 15 // small screens already have the site-wide onResize from screenService
16 // > medium screens have enough space to fit the administrative menus 16 // > medium screens have enough space to fit the administrative menus
17 if (!this.screen.isInMobileView() && this.screen.isInMediumView()) { 17 if (!this.screen.isInMobileView() && this.screen.isInMediumView()) {
18 this.menu.isMenuDisplayed = this.display 18 this.menu.setMenuDisplay(this.display)
19 } 19 }
20 return true 20 return true
21 } 21 }
diff --git a/client/src/app/core/wrappers/screen.service.ts b/client/src/app/core/wrappers/screen.service.ts
index 96fb7ae57..88cf662b3 100644
--- a/client/src/app/core/wrappers/screen.service.ts
+++ b/client/src/app/core/wrappers/screen.service.ts
@@ -54,6 +54,64 @@ export class ScreenService {
54 return this.windowInnerWidth 54 return this.windowInnerWidth
55 } 55 }
56 56
57 // https://stackoverflow.com/questions/2264072/detect-a-finger-swipe-through-javascript-on-the-iphone-and-android
58 onFingerSwipe (direction: 'left' | 'right' | 'up' | 'down', action: () => void, removeEventOnEnd = true) {
59 let touchDownClientX: number
60 let touchDownClientY: number
61
62 const onTouchStart = (event: TouchEvent) => {
63 const firstTouch = event.touches[0]
64 touchDownClientX = firstTouch.clientX
65 touchDownClientY = firstTouch.clientY
66 }
67
68 const onTouchMove = (event: TouchEvent) => {
69 if (!touchDownClientX || !touchDownClientY) {
70 return
71 }
72
73 const touchUpClientX = event.touches[0].clientX
74 const touchUpClientY = event.touches[0].clientY
75
76 const touchClientX = Math.abs(touchDownClientX - touchUpClientX)
77 const touchClientY = Math.abs(touchDownClientY - touchUpClientY)
78
79 if (touchClientX > touchClientY) {
80 if (touchClientX > 0) {
81 if (direction === 'left') {
82 if (removeEventOnEnd) this.removeFingerSwipeEventListener(onTouchStart, onTouchMove)
83 action()
84 }
85 } else {
86 if (direction === 'right') {
87 if (removeEventOnEnd) this.removeFingerSwipeEventListener(onTouchStart, onTouchMove)
88 action()
89 }
90 }
91 } else {
92 if (touchClientY > 0) {
93 if (direction === 'up') {
94 if (removeEventOnEnd) this.removeFingerSwipeEventListener(onTouchStart, onTouchMove)
95 action()
96 }
97 } else {
98 if (direction === 'down') {
99 if (removeEventOnEnd) this.removeFingerSwipeEventListener(onTouchStart, onTouchMove)
100 action()
101 }
102 }
103 }
104 }
105
106 document.addEventListener('touchstart', onTouchStart, false)
107 document.addEventListener('touchmove', onTouchMove, false)
108 }
109
110 private removeFingerSwipeEventListener (onTouchStart: (event: TouchEvent) => void, onTouchMove: (event: TouchEvent) => void) {
111 document.removeEventListener('touchstart', onTouchStart)
112 document.removeEventListener('touchmove', onTouchMove)
113 }
114
57 private refreshWindowInnerWidth () { 115 private refreshWindowInnerWidth () {
58 this.lastFunctionCallTime = new Date().getTime() 116 this.lastFunctionCallTime = new Date().getTime()
59 117
diff --git a/client/src/sass/bootstrap.scss b/client/src/sass/bootstrap.scss
index 308a28658..a3b60198c 100644
--- a/client/src/sass/bootstrap.scss
+++ b/client/src/sass/bootstrap.scss
@@ -150,6 +150,29 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
150 width: 100vw; // Make sure the content fits all the available width 150 width: 100vw; // Make sure the content fits all the available width
151} 151}
152 152
153// On touchscreen devices, simply overflow: hidden to avoid detached overlay on scroll
154@media (hover: none) and (pointer: coarse) {
155 .modal-open, .menu-open {
156 overflow: hidden !important;
157 }
158
159 // On touchscreen devices display content overlay when opened menu
160 .menu-open {
161 .main-col {
162 &::before {
163 background-color: black;
164 width: 100vw;
165 height: 100vh;
166 opacity: 0.75;
167 content: '';
168 display: block;
169 position: fixed;
170 z-index: z('header') - 1;
171 }
172 }
173 }
174}
175
153// Nav customizations 176// Nav customizations
154.nav .nav-link { 177.nav .nav-link {
155 display: flex !important; 178 display: flex !important;