From d78ef429f23091427cf43f2c1a0b3908a477fb66 Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Mon, 21 Oct 2024 12:41:05 +0200 Subject: [PATCH 1/9] on resizing the window hide the menu if the new window size is small --- app/views/layouts/base.html.erb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/views/layouts/base.html.erb b/app/views/layouts/base.html.erb index c3dbc96fbec6..008f5c6e5e8a 100644 --- a/app/views/layouts/base.html.erb +++ b/app/views/layouts/base.html.erb @@ -168,6 +168,12 @@ See COPYRIGHT and LICENSE files for more details. $('.can-hide-navigation').addClass('hidden-navigation'); } + window.addEventListener('resize', function() { + if (window.innerWidth < 1012) { + $('.can-hide-navigation').addClass('hidden-navigation'); + } + }); + if (mainMenuCollapsed === 'true') { savedMainMenuWidth = 0; } From 0ee1805c73e1151ff9c10209f720af39561f44be Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Mon, 28 Oct 2024 16:17:22 +0100 Subject: [PATCH 2/9] close menu when the screen is resized and its size is less than 1012px --- app/views/layouts/base.html.erb | 6 ------ .../src/app/core/main-menu/main-menu-toggle.service.ts | 8 ++++++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/views/layouts/base.html.erb b/app/views/layouts/base.html.erb index 008f5c6e5e8a..c3dbc96fbec6 100644 --- a/app/views/layouts/base.html.erb +++ b/app/views/layouts/base.html.erb @@ -168,12 +168,6 @@ See COPYRIGHT and LICENSE files for more details. $('.can-hide-navigation').addClass('hidden-navigation'); } - window.addEventListener('resize', function() { - if (window.innerWidth < 1012) { - $('.can-hide-navigation').addClass('hidden-navigation'); - } - }); - if (mainMenuCollapsed === 'true') { savedMainMenuWidth = 0; } diff --git a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts index 6db9707b6dca..62d205426866 100644 --- a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts +++ b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts @@ -72,6 +72,8 @@ export class MainMenuToggleService { public injector:Injector, readonly deviceService:DeviceService, ) { + // Add resize event listener + window.addEventListener('resize', this.onWindowResize.bind(this)); } public initializeMenu():void { @@ -99,6 +101,12 @@ export class MainMenuToggleService { this.closeWhenOnSmallDesktop(); } + private onWindowResize():void { + if (window.innerWidth < 1012) { + this.closeMenu(); + } + } + // click on arrow or hamburger icon public toggleNavigation(event?:JQuery.TriggeredEvent|Event):void { if (event) { From aa0fa9b2dbe0a3e4377bc304b91dde547b95545e Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Tue, 29 Oct 2024 15:03:52 +0100 Subject: [PATCH 3/9] show the menu if the window size is greater than 1012px --- .../main-menu/main-menu-toggle.service.ts | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts index 62d205426866..742fee7b3857 100644 --- a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts +++ b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts @@ -98,12 +98,18 @@ export class MainMenuToggleService { } // small desktop version default: hide menu on initialization - this.closeWhenOnSmallDesktop(); + this.adjustMenuVisibility(); } private onWindowResize():void { - if (window.innerWidth < 1012) { - this.closeMenu(); + this.adjustMenuVisibility(); + } + + private adjustMenuVisibility():void { + if (window.innerWidth >= 1012) { + this.setWidth(this.defaultWidth); // Open menu if the screen is wider than 1012px + } else { + this.closeMenu(); // Close menu if the screen is narrower } } @@ -141,13 +147,6 @@ export class MainMenuToggleService { jQuery('.searchable-menu--search-input').blur(); } - public closeWhenOnSmallDesktop():void { - if (this.deviceService.isSmallDesktop) { - this.closeMenu(); - window.OpenProject.guardedLocalStorage(this.localStorageStateKey, 'false'); - } - } - public saveWidth(width?:number):void { this.setWidth(width); window.OpenProject.guardedLocalStorage(this.localStorageKey, String(this.elementWidth)); @@ -158,11 +157,7 @@ export class MainMenuToggleService { if (width !== undefined) { // Leave a minimum amount of space for space for the content const maxMenuWidth = this.deviceService.isSmallDesktop ? window.innerWidth - 120 : window.innerWidth - 520; - if (width > maxMenuWidth) { - this.elementWidth = maxMenuWidth; - } else { - this.elementWidth = width as number; - } + this.elementWidth = Math.min(width as number, maxMenuWidth); } this.snapBack(); @@ -172,7 +167,6 @@ export class MainMenuToggleService { this.global.showNavigation = this.showNavigation; this.htmlNode.style.setProperty('--main-menu-width', `${this.elementWidth}px`); - // Send change event when size of menu is changing (menu toggled or resized) const changeEvent = jQuery.Event('change'); this.changeData.next(changeEvent); } From e95865a4d85f4ba503436fe6c7fcf79a95850148 Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Tue, 29 Oct 2024 15:15:48 +0100 Subject: [PATCH 4/9] add comment --- frontend/src/app/core/main-menu/main-menu-toggle.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts index 742fee7b3857..3b0b21b71294 100644 --- a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts +++ b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts @@ -167,6 +167,7 @@ export class MainMenuToggleService { this.global.showNavigation = this.showNavigation; this.htmlNode.style.setProperty('--main-menu-width', `${this.elementWidth}px`); + // Send change event when size of menu is changing (menu toggled or resized) const changeEvent = jQuery.Event('change'); this.changeData.next(changeEvent); } From cfe66dfb48b46eaecd230e670c2ee7d043abfa7f Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Mon, 4 Nov 2024 14:30:51 +0100 Subject: [PATCH 5/9] fix closing menu when the user close it and then refresh the page again --- .../main-menu/main-menu-toggle.service.ts | 98 ++++++++++--------- 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts index 3b0b21b71294..a574d5c62e41 100644 --- a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts +++ b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, OnInit } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @@ -34,7 +34,7 @@ import { DeviceService } from 'core-app/core/browser/device.service'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; @Injectable({ providedIn: 'root' }) -export class MainMenuToggleService { +export class MainMenuToggleService implements OnInit { public toggleTitle:string; private elementWidth:number; @@ -67,6 +67,8 @@ export class MainMenuToggleService { public changeData$ = this.changeData.asObservable(); + private wasCollapsedByUser = false; + constructor( protected I18n:I18nService, public injector:Injector, @@ -76,28 +78,29 @@ export class MainMenuToggleService { window.addEventListener('resize', this.onWindowResize.bind(this)); } + public ngOnInit():void { + this.initializeMenu(); + } + public initializeMenu():void { if (!this.mainMenu) { return; } this.elementWidth = parseInt(window.OpenProject.guardedLocalStorage(this.localStorageKey) as string); - const menuCollapsed = window.OpenProject.guardedLocalStorage(this.localStorageStateKey) as string; + const menuCollapsed = window.OpenProject.guardedLocalStorage(this.localStorageStateKey) === 'true'; + + // Set the initial value of the collapse tracking flag + this.wasCollapsedByUser = menuCollapsed; if (!this.elementWidth) { this.saveWidth(this.mainMenu.offsetWidth); - } else if (menuCollapsed && JSON.parse(menuCollapsed)) { + } else if (menuCollapsed) { this.closeMenu(); } else { this.setWidth(); } - const currentProject:CurrentProjectService = this.injector.get(CurrentProjectService); - if (jQuery(document.body).hasClass('controller-my') && this.elementWidth === 0 || currentProject.id === null) { - this.saveWidth(this.defaultWidth); - } - - // small desktop version default: hide menu on initialization this.adjustMenuVisibility(); } @@ -107,73 +110,77 @@ export class MainMenuToggleService { private adjustMenuVisibility():void { if (window.innerWidth >= 1012) { - this.setWidth(this.defaultWidth); // Open menu if the screen is wider than 1012px + // If wide screen, only open if it wasn't manually collapsed + if (!this.wasCollapsedByUser) { + this.setWidth(this.defaultWidth); + } } else { - this.closeMenu(); // Close menu if the screen is narrower + // Narrow screen: always close the menu and reset the collapse flag + this.closeMenu(); + this.wasCollapsedByUser = false; } } - // click on arrow or hamburger icon public toggleNavigation(event?:JQuery.TriggeredEvent|Event):void { if (event) { event.stopPropagation(); event.preventDefault(); } - if (!this.showNavigation) { // sidebar is hidden -> show menu - if (this.deviceService.isSmallDesktop) { // small desktop version - this.setWidth(window.innerWidth); - } else { // desktop version - const savedWidth = parseInt(window.OpenProject.guardedLocalStorage(this.localStorageKey) as string); - const widthToSave = savedWidth >= this.elementMinWidth ? savedWidth : this.defaultWidth; + // Update the flag based on whether the menu is currently visible + this.wasCollapsedByUser = this.showNavigation; - this.saveWidth(widthToSave); - } - } else { // sidebar is expanded -> close menu + if (this.showNavigation) { this.closeMenu(); + } else { + this.openMenu(); } - // Set focus on first visible main menu item. - // This needs to be called after AngularJS has rendered the menu, which happens some when after(!) we leave this - // method here. So we need to set the focus after a timeout. - setTimeout(() => { - jQuery('#main-menu [class*="-menu-item"]:visible').first().focus(); - }, 500); + // Save the collapsed state in localStorage + window.OpenProject.guardedLocalStorage(this.localStorageStateKey, String(!this.showNavigation)); } public closeMenu():void { this.setWidth(0); + this.wasCollapsedByUser = true; window.OpenProject.guardedLocalStorage(this.localStorageStateKey, 'true'); jQuery('.searchable-menu--search-input').blur(); } - public saveWidth(width?:number):void { - this.setWidth(width); - window.OpenProject.guardedLocalStorage(this.localStorageKey, String(this.elementWidth)); - window.OpenProject.guardedLocalStorage(this.localStorageStateKey, String(this.elementWidth === 0)); + public openMenu(): void { + this.setWidth(this.defaultWidth); + this.wasCollapsedByUser = false; + window.OpenProject.guardedLocalStorage(this.localStorageStateKey, 'false'); } - public setWidth(width?:any):void { + public setWidth(width?:number):void { if (width !== undefined) { - // Leave a minimum amount of space for space for the content - const maxMenuWidth = this.deviceService.isSmallDesktop ? window.innerWidth - 120 : window.innerWidth - 520; - this.elementWidth = Math.min(width as number, maxMenuWidth); + this.elementWidth = width; } - this.snapBack(); - this.setToggleTitle(); - this.toggleClassHidden(); + // Apply the width directly to the main menu + this.mainMenu.style.width = `${this.elementWidth}px`; - this.global.showNavigation = this.showNavigation; + // Apply to root CSS variable for any related layout adjustments this.htmlNode.style.setProperty('--main-menu-width', `${this.elementWidth}px`); - // Send change event when size of menu is changing (menu toggled or resized) - const changeEvent = jQuery.Event('change'); - this.changeData.next(changeEvent); + // Check if menu is open or closed and apply CSS class if needed + this.toggleClassHidden(); + + // Save the width if it's open + if (this.elementWidth > 0) { + window.OpenProject.guardedLocalStorage(this.localStorageKey, String(this.elementWidth)); + } + } + + public saveWidth(width?:number):void { + this.setWidth(width); + window.OpenProject.guardedLocalStorage(this.localStorageKey, String(this.elementWidth)); + window.OpenProject.guardedLocalStorage(this.localStorageStateKey, String(this.elementWidth === 0)); } public get showNavigation():boolean { - return (this.elementWidth >= this.elementMinWidth); + return this.elementWidth >= this.elementMinWidth; } private snapBack():void { @@ -192,6 +199,7 @@ export class MainMenuToggleService { } private toggleClassHidden():void { - this.hideElements.toggleClass('hidden-navigation', !this.showNavigation); + const isHidden = this.elementWidth < this.elementMinWidth; + this.hideElements.toggleClass('hidden-navigation', isHidden); } } From 6bb4cc92153758fc4a09a02674481aeade533817 Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Mon, 4 Nov 2024 15:18:44 +0100 Subject: [PATCH 6/9] fix eslint errors --- .../app/core/main-menu/main-menu-toggle.service.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts index a574d5c62e41..168344aa9a61 100644 --- a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts +++ b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injectable, Injector, OnInit } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @@ -34,7 +34,7 @@ import { DeviceService } from 'core-app/core/browser/device.service'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; @Injectable({ providedIn: 'root' }) -export class MainMenuToggleService implements OnInit { +export class MainMenuToggleService { public toggleTitle:string; private elementWidth:number; @@ -74,14 +74,11 @@ export class MainMenuToggleService implements OnInit { public injector:Injector, readonly deviceService:DeviceService, ) { + this.initializeMenu(); // Add resize event listener window.addEventListener('resize', this.onWindowResize.bind(this)); } - public ngOnInit():void { - this.initializeMenu(); - } - public initializeMenu():void { if (!this.mainMenu) { return; @@ -147,7 +144,7 @@ export class MainMenuToggleService implements OnInit { jQuery('.searchable-menu--search-input').blur(); } - public openMenu(): void { + public openMenu():void { this.setWidth(this.defaultWidth); this.wasCollapsedByUser = false; window.OpenProject.guardedLocalStorage(this.localStorageStateKey, 'false'); From 41066a84f12ab2308a5e0c9b05b176f57904ae70 Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Tue, 5 Nov 2024 09:24:40 +0100 Subject: [PATCH 7/9] set focus on first element of menu --- .../src/app/core/main-menu/main-menu-toggle.service.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts index 168344aa9a61..a8b110fdf35d 100644 --- a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts +++ b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts @@ -132,9 +132,14 @@ export class MainMenuToggleService { } else { this.openMenu(); } - // Save the collapsed state in localStorage window.OpenProject.guardedLocalStorage(this.localStorageStateKey, String(!this.showNavigation)); + // Set focus on first visible main menu item. + // This needs to be called after AngularJS has rendered the menu, which happens some when after(!) we leave this + // method here. So we need to set the focus after a timeout. + setTimeout(() => { + jQuery('#main-menu [class*="-menu-item"]:visible').first().focus(); + }, 500); } public closeMenu():void { From 7de41000de2f33ea8a7539af6f21f92de01a30de Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Mon, 11 Nov 2024 21:16:27 +0100 Subject: [PATCH 8/9] check if the screen is a large screen and menu is hidden because the screen size was resized to a small one --- .../main-menu/main-menu-toggle.service.ts | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts index a8b110fdf35d..fa79db28029a 100644 --- a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts +++ b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts @@ -66,6 +66,7 @@ export class MainMenuToggleService { private changeData = new BehaviorSubject({}); public changeData$ = this.changeData.asObservable(); + private wasHiddenDueToResize = false; private wasCollapsedByUser = false; @@ -107,31 +108,33 @@ export class MainMenuToggleService { private adjustMenuVisibility():void { if (window.innerWidth >= 1012) { - // If wide screen, only open if it wasn't manually collapsed - if (!this.wasCollapsedByUser) { + // On larger screens, reopen the menu if it was hidden only due to screen resizing + if (this.wasHiddenDueToResize && !this.wasCollapsedByUser) { this.setWidth(this.defaultWidth); + this.wasHiddenDueToResize = false; // Reset the flag since the menu is now shown } - } else { - // Narrow screen: always close the menu and reset the collapse flag - this.closeMenu(); - this.wasCollapsedByUser = false; + } else if (this.showNavigation) { + this.closeMenu(); + this.wasHiddenDueToResize = true; // Indicate that the menu was hidden due to resize } } - public toggleNavigation(event?:JQuery.TriggeredEvent|Event):void { + public toggleNavigation(event?:JQuery.TriggeredEvent | Event):void { if (event) { event.stopPropagation(); event.preventDefault(); } - // Update the flag based on whether the menu is currently visible + // Update the user collapse flag and clear `wasHiddenDueToResize` this.wasCollapsedByUser = this.showNavigation; + this.wasHiddenDueToResize = false; // Reset because a manual toggle overrides any resize behavior if (this.showNavigation) { this.closeMenu(); } else { this.openMenu(); } + // Save the collapsed state in localStorage window.OpenProject.guardedLocalStorage(this.localStorageStateKey, String(!this.showNavigation)); // Set focus on first visible main menu item. @@ -144,15 +147,11 @@ export class MainMenuToggleService { public closeMenu():void { this.setWidth(0); - this.wasCollapsedByUser = true; - window.OpenProject.guardedLocalStorage(this.localStorageStateKey, 'true'); jQuery('.searchable-menu--search-input').blur(); } public openMenu():void { this.setWidth(this.defaultWidth); - this.wasCollapsedByUser = false; - window.OpenProject.guardedLocalStorage(this.localStorageStateKey, 'false'); } public setWidth(width?:number):void { From eae65293d6ca4d8f35881b8a3bdd3807d3bf3af6 Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad Date: Tue, 3 Dec 2024 15:25:44 +0100 Subject: [PATCH 9/9] set toggle title --- frontend/src/app/core/main-menu/main-menu-toggle.service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts index fa79db28029a..f98505cc2413 100644 --- a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts +++ b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts @@ -119,7 +119,7 @@ export class MainMenuToggleService { } } - public toggleNavigation(event?:JQuery.TriggeredEvent | Event):void { + public toggleNavigation(event?:JQuery.TriggeredEvent|Event):void { if (event) { event.stopPropagation(); event.preventDefault(); @@ -167,6 +167,8 @@ export class MainMenuToggleService { // Check if menu is open or closed and apply CSS class if needed this.toggleClassHidden(); + this.snapBack(); + this.setToggleTitle(); // Save the width if it's open if (this.elementWidth > 0) {