From 19e0cf59e54c9f90149bfffc09255ac1099665c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Wed, 6 Mar 2019 14:54:40 +0100 Subject: [PATCH 1/4] MOBILE-2903 menu: Show menu items depending on device width --- src/components/ion-tabs/ion-tabs.ts | 4 +++ src/core/mainmenu/pages/menu/menu.ts | 43 +++++++++++++++++-------- src/core/mainmenu/pages/more/more.ts | 39 ++++++++++++++-------- src/core/mainmenu/providers/mainmenu.ts | 19 +++++++++++ 4 files changed, 78 insertions(+), 27 deletions(-) diff --git a/src/components/ion-tabs/ion-tabs.ts b/src/components/ion-tabs/ion-tabs.ts index deeb76a53..76fff38fe 100644 --- a/src/components/ion-tabs/ion-tabs.ts +++ b/src/components/ion-tabs/ion-tabs.ts @@ -223,6 +223,10 @@ export class CoreIonTabsComponent extends Tabs implements OnDestroy { * @param {CoreIonTabComponent} tab The tab to remove. */ remove(tab: CoreIonTabComponent): void { + if (tab.isSelected) { + // TODO: If selected we should move this navigation to the phantom tab. + } + // First search in the list of initialized tabs. let index = this._tabs.indexOf(tab); diff --git a/src/core/mainmenu/pages/menu/menu.ts b/src/core/mainmenu/pages/menu/menu.ts index ae3f68830..05d1afcff 100644 --- a/src/core/mainmenu/pages/menu/menu.ts +++ b/src/core/mainmenu/pages/menu/menu.ts @@ -30,6 +30,7 @@ import { CoreMainMenuDelegate, CoreMainMenuHandlerToDisplay } from '../../provid }) export class CoreMainMenuPage implements OnDestroy { tabs: CoreMainMenuHandlerToDisplay[] = []; + allHandlers: CoreMainMenuHandlerToDisplay[]; loaded = false; redirectPage: string; redirectParams: any; @@ -42,7 +43,8 @@ export class CoreMainMenuPage implements OnDestroy { @ViewChild('mainTabs') mainTabs: CoreIonTabsComponent; constructor(private menuDelegate: CoreMainMenuDelegate, private sitesProvider: CoreSitesProvider, navParams: NavParams, - private navCtrl: NavController, private eventsProvider: CoreEventsProvider, private cdr: ChangeDetectorRef) { + private navCtrl: NavController, private eventsProvider: CoreEventsProvider, private cdr: ChangeDetectorRef, + private mainMenuProvider: CoreMainMenuProvider) { // Check if the menu was loaded with a redirect. const redirectPage = navParams.get('redirectPage'); @@ -78,10 +80,32 @@ export class CoreMainMenuPage implements OnDestroy { this.subscription = this.menuDelegate.getHandlers().subscribe((handlers) => { // Remove the handlers that should only appear in the More menu. - handlers = handlers.filter((handler) => { + this.allHandlers = handlers.filter((handler) => { return !handler.onlyInMore; }); - handlers = handlers.slice(0, CoreMainMenuProvider.NUM_MAIN_HANDLERS); // Get main handlers. + + this.initHandlers(); + + if (this.loaded && this.pendingRedirect) { + // Wait for tabs to be initialized and then handle the redirect. + setTimeout(() => { + if (this.pendingRedirect) { + this.handleRedirect(this.pendingRedirect); + delete this.pendingRedirect; + } + }); + } + }); + + window.addEventListener('resize', this.initHandlers.bind(this)); + } + + /** + * Init handlers on change (size or handlers). + */ + initHandlers(): void { + if (this.allHandlers) { + const handlers = this.allHandlers.slice(0, this.mainMenuProvider.getNumItems()); // Get main handlers. // Re-build the list of tabs. If a handler is already in the list, use existing object to prevent re-creating the tab. const newTabs = []; @@ -105,17 +129,7 @@ export class CoreMainMenuPage implements OnDestroy { }); this.loaded = this.menuDelegate.areHandlersLoaded(); - - if (this.loaded && this.pendingRedirect) { - // Wait for tabs to be initialized and then handle the redirect. - setTimeout(() => { - if (this.pendingRedirect) { - this.handleRedirect(this.pendingRedirect); - delete this.pendingRedirect; - } - }); - } - }); + } } /** @@ -153,5 +167,6 @@ export class CoreMainMenuPage implements OnDestroy { ngOnDestroy(): void { this.subscription && this.subscription.unsubscribe(); this.redirectObs && this.redirectObs.off(); + window.removeEventListener('resize', this.initHandlers.bind(this)); } } diff --git a/src/core/mainmenu/pages/more/more.ts b/src/core/mainmenu/pages/more/more.ts index 9027f7012..03fc83452 100644 --- a/src/core/mainmenu/pages/more/more.ts +++ b/src/core/mainmenu/pages/more/more.ts @@ -29,6 +29,7 @@ import { CoreMainMenuProvider, CoreMainMenuCustomItem } from '../../providers/ma }) export class CoreMainMenuMorePage implements OnDestroy { handlers: CoreMainMenuHandlerData[]; + allHandlers: CoreMainMenuHandlerData[]; handlersLoaded: boolean; siteInfo: any; siteName: string; @@ -58,32 +59,44 @@ export class CoreMainMenuMorePage implements OnDestroy { ionViewDidLoad(): void { // Load the handlers. this.subscription = this.menuDelegate.getHandlers().subscribe((handlers) => { - // Calculate the main handlers to not display them in this view. - const mainHandlers = handlers.filter((handler) => { - return !handler.onlyInMore; - }).slice(0, CoreMainMenuProvider.NUM_MAIN_HANDLERS); + this.allHandlers = handlers; - // Get only the handlers that don't appear in the main view. - this.handlers = []; - handlers.forEach((handler) => { - if (mainHandlers.indexOf(handler) == -1) { - this.handlers.push(handler); - } - }); - - this.handlersLoaded = this.menuDelegate.areHandlersLoaded(); + this.initHandlers(); }); + + window.addEventListener('resize', this.initHandlers.bind(this)); } /** * Page destroyed. */ ngOnDestroy(): void { + window.removeEventListener('resize', this.initHandlers.bind(this)); + if (this.subscription) { this.subscription.unsubscribe(); } } + /** + * Init handlers on change (size or handlers). + */ + initHandlers(): void { + if (this.allHandlers) { + // Calculate the main handlers not to display them in this view. + const mainHandlers = this.allHandlers.filter((handler) => { + return !handler.onlyInMore; + }).slice(0, this.mainMenuProvider.getNumItems()); + + // Get only the handlers that don't appear in the main view. + this.handlers = this.allHandlers.filter((handler) => { + return mainHandlers.indexOf(handler) == -1; + }); + + this.handlersLoaded = this.menuDelegate.areHandlersLoaded(); + } + } + /** * Load the site info required by the view. */ diff --git a/src/core/mainmenu/providers/mainmenu.ts b/src/core/mainmenu/providers/mainmenu.ts index ac38ef92b..e0fa54e02 100644 --- a/src/core/mainmenu/providers/mainmenu.ts +++ b/src/core/mainmenu/providers/mainmenu.ts @@ -52,6 +52,7 @@ export interface CoreMainMenuCustomItem { @Injectable() export class CoreMainMenuProvider { static NUM_MAIN_HANDLERS = 4; + static ITEM_MIN_WIDTH = 72; // Min with of every item, based on 5 items on a 360 pixel wide screen. constructor(private langProvider: CoreLangProvider, private sitesProvider: CoreSitesProvider) { } @@ -158,4 +159,22 @@ export class CoreMainMenuProvider { }); }); } + + /** + * Get the number of items to be shown on the main menu bar. + * + * @return {number} Number of items depending on the device width. + */ + getNumItems(): number { + if (window && window.innerWidth) { + let numElements; + + numElements = Math.floor(window.innerWidth / CoreMainMenuProvider.ITEM_MIN_WIDTH); + + // Set a mínimum elements to show and skip more button. + return numElements > 1 ? numElements - 1 : 1; + } + + return CoreMainMenuProvider.NUM_MAIN_HANDLERS; + } } From 809753aac1ce382b32bd9a89bc2088e203ac3dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 7 Mar 2019 12:08:25 +0100 Subject: [PATCH 2/4] MOBILE-2903 menu: Move menu to side on tablet --- src/components/ion-tabs/core-ion-tabs.html | 2 +- src/components/ion-tabs/ion-tabs.scss | 27 +++++++++++++++++ src/components/ion-tabs/ion-tabs.ts | 4 --- src/core/mainmenu/pages/menu/menu.html | 4 +-- src/core/mainmenu/pages/menu/menu.ts | 26 +++++++++++++++++ src/core/mainmenu/providers/delegate.ts | 6 ++++ src/core/mainmenu/providers/mainmenu.ts | 34 ++++++++++++++++++++-- 7 files changed, 94 insertions(+), 9 deletions(-) diff --git a/src/components/ion-tabs/core-ion-tabs.html b/src/components/ion-tabs/core-ion-tabs.html index e255871b6..d662320ef 100644 --- a/src/components/ion-tabs/core-ion-tabs.html +++ b/src/components/ion-tabs/core-ion-tabs.html @@ -7,7 +7,7 @@ -
+
diff --git a/src/components/ion-tabs/ion-tabs.scss b/src/components/ion-tabs/ion-tabs.scss index 22cf41ef9..a4efdc295 100644 --- a/src/components/ion-tabs/ion-tabs.scss +++ b/src/components/ion-tabs/ion-tabs.scss @@ -1,3 +1,5 @@ +$core-sidetab-size: 60px !default; + ion-app.app-root core-ion-tabs { .tabbar { z-index: 101; // For some reason, the regular z-index isn't enough with our tabs, use a higher one. @@ -21,6 +23,31 @@ ion-app.app-root core-ion-tabs { .tab-badge.badge { background-color: $ion-tabs-badge-color; } + + &[tabsplacement="side"] { + .tabbar { + @include float(start); + width: $core-sidetab-size; + height: 100%; + flex-direction: column; + .tab-button { + width: 100%; + .tab-badge.badge { + @include position(calc(50% - 30px), 2px, null, null); + } + } + } + + .tabcontent { + width: calc(100% - #{($core-sidetab-size)}); + position: absolute; + @include position(0, 0, 0, 0); + core-ion-tab { + @include position(0, 0, 0, $core-sidetab-size); + position: relative; + } + } + } } ion-app.app-root.ios core-ion-tabs .core-ion-tabs-loading { diff --git a/src/components/ion-tabs/ion-tabs.ts b/src/components/ion-tabs/ion-tabs.ts index 76fff38fe..deeb76a53 100644 --- a/src/components/ion-tabs/ion-tabs.ts +++ b/src/components/ion-tabs/ion-tabs.ts @@ -223,10 +223,6 @@ export class CoreIonTabsComponent extends Tabs implements OnDestroy { * @param {CoreIonTabComponent} tab The tab to remove. */ remove(tab: CoreIonTabComponent): void { - if (tab.isSelected) { - // TODO: If selected we should move this navigation to the phantom tab. - } - // First search in the list of initialized tabs. let index = this._tabs.indexOf(tab); diff --git a/src/core/mainmenu/pages/menu/menu.html b/src/core/mainmenu/pages/menu/menu.html index 9faebad6f..9ee9cf395 100644 --- a/src/core/mainmenu/pages/menu/menu.html +++ b/src/core/mainmenu/pages/menu/menu.html @@ -1,5 +1,5 @@ - + - + diff --git a/src/core/mainmenu/pages/menu/menu.ts b/src/core/mainmenu/pages/menu/menu.ts index 05d1afcff..358b840bc 100644 --- a/src/core/mainmenu/pages/menu/menu.ts +++ b/src/core/mainmenu/pages/menu/menu.ts @@ -35,6 +35,7 @@ export class CoreMainMenuPage implements OnDestroy { redirectPage: string; redirectParams: any; showTabs = false; + tabsPlacement = 'bottom'; protected subscription; protected redirectObs: any; @@ -105,6 +106,8 @@ export class CoreMainMenuPage implements OnDestroy { */ initHandlers(): void { if (this.allHandlers) { + this.tabsPlacement = this.mainMenuProvider.getTabPlacement(this.navCtrl); + const handlers = this.allHandlers.slice(0, this.mainMenuProvider.getNumItems()); // Get main handlers. // Re-build the list of tabs. If a handler is already in the list, use existing object to prevent re-creating the tab. @@ -118,9 +121,32 @@ export class CoreMainMenuPage implements OnDestroy { return tab.title == handler.title && tab.icon == handler.icon; }); + tab ? tab.hide = false : null; + handler.hide = false; + newTabs.push(tab || handler); } + // Maintain tab in phantom mode in case is not visible. + const selectedTab = this.mainTabs.getSelected(); + if (selectedTab) { + const oldTab = this.tabs.find((tab) => { + return tab.page == selectedTab.root && tab.icon == selectedTab.tabIcon; + }); + + if (oldTab) { + // Check if the selected handler is visible. + const isVisible = newTabs.some((newTab) => { + return oldTab.title == newTab.title && oldTab.icon == newTab.icon; + }); + + if (!isVisible) { + oldTab.hide = true; + newTabs.push(oldTab); + } + } + } + this.tabs = newTabs; // Sort them by priority so new handlers are in the right position. diff --git a/src/core/mainmenu/providers/delegate.ts b/src/core/mainmenu/providers/delegate.ts index 9f7ea3bdf..a32d6e040 100644 --- a/src/core/mainmenu/providers/delegate.ts +++ b/src/core/mainmenu/providers/delegate.ts @@ -111,6 +111,12 @@ export interface CoreMainMenuHandlerToDisplay extends CoreMainMenuHandlerData { * @type {number} */ priority?: number; + + /** + * Hide tab. Used then resizing. + * @type {[type]} + */ + hide?: boolean; } /** diff --git a/src/core/mainmenu/providers/mainmenu.ts b/src/core/mainmenu/providers/mainmenu.ts index e0fa54e02..b6eeefc75 100644 --- a/src/core/mainmenu/providers/mainmenu.ts +++ b/src/core/mainmenu/providers/mainmenu.ts @@ -13,6 +13,7 @@ // limitations under the License. import { Injectable } from '@angular/core'; +import { NavController } from 'ionic-angular'; import { CoreLangProvider } from '@providers/lang'; import { CoreSitesProvider } from '@providers/sites'; import { CoreConfigConstants } from '../../../configconstants'; @@ -53,8 +54,11 @@ export interface CoreMainMenuCustomItem { export class CoreMainMenuProvider { static NUM_MAIN_HANDLERS = 4; static ITEM_MIN_WIDTH = 72; // Min with of every item, based on 5 items on a 360 pixel wide screen. + protected tablet = false; - constructor(private langProvider: CoreLangProvider, private sitesProvider: CoreSitesProvider) { } + constructor(private langProvider: CoreLangProvider, private sitesProvider: CoreSitesProvider) { + this.tablet = window && window.innerWidth && window.innerWidth >= 576 && window.innerHeight >= 576; + } /** * Get a list of custom menu items for a certain site. @@ -169,7 +173,12 @@ export class CoreMainMenuProvider { if (window && window.innerWidth) { let numElements; - numElements = Math.floor(window.innerWidth / CoreMainMenuProvider.ITEM_MIN_WIDTH); + if (this.tablet) { + // Tablet, menu will be displayed vertically. + numElements = Math.floor(window.innerHeight / CoreMainMenuProvider.ITEM_MIN_WIDTH); + } else { + numElements = Math.floor(window.innerWidth / CoreMainMenuProvider.ITEM_MIN_WIDTH); + } // Set a mínimum elements to show and skip more button. return numElements > 1 ? numElements - 1 : 1; @@ -177,4 +186,25 @@ export class CoreMainMenuProvider { return CoreMainMenuProvider.NUM_MAIN_HANDLERS; } + + /** + * Get tabs placement depending on the device size. + * + * @param {NavController} navCtrl NavController to resize the content. + * @return {string} Tabs placement including side value. + */ + getTabPlacement(navCtrl: NavController): string { + const tablet = window && window.innerWidth && window.innerWidth >= 576 && window.innerHeight >= 576; + + if (tablet != this.tablet) { + this.tablet = tablet; + + // Resize so content margins can be updated. + setTimeout(() => { + navCtrl.resize(); + }, 500); + } + + return tablet ? 'side' : 'bottom'; + } } From 12d75f14b2fca7471ad2182970147ce08f9d1f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 2 May 2019 11:59:22 +0200 Subject: [PATCH 3/4] MOBILE-2903 menu: Disable responsive menu items is set on site --- src/components/ion-tabs/core-ion-tabs.html | 2 +- src/core/mainmenu/providers/mainmenu.ts | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/components/ion-tabs/core-ion-tabs.html b/src/components/ion-tabs/core-ion-tabs.html index d662320ef..123d340c1 100644 --- a/src/components/ion-tabs/core-ion-tabs.html +++ b/src/components/ion-tabs/core-ion-tabs.html @@ -1,5 +1,5 @@
- +
diff --git a/src/core/mainmenu/providers/mainmenu.ts b/src/core/mainmenu/providers/mainmenu.ts index b6eeefc75..59eab0b7a 100644 --- a/src/core/mainmenu/providers/mainmenu.ts +++ b/src/core/mainmenu/providers/mainmenu.ts @@ -170,7 +170,7 @@ export class CoreMainMenuProvider { * @return {number} Number of items depending on the device width. */ getNumItems(): number { - if (window && window.innerWidth) { + if (!this.isResponsiveMainMenuItemsDisabledInCurrentSite() && window && window.innerWidth) { let numElements; if (this.tablet) { @@ -207,4 +207,15 @@ export class CoreMainMenuProvider { return tablet ? 'side' : 'bottom'; } + + /** + * Check if responsive main menu items is disabled in the current site. + * + * @return {boolean} Whether it's disabled. + */ + protected isResponsiveMainMenuItemsDisabledInCurrentSite(): boolean { + const site = this.sitesProvider.getCurrentSite(); + + return site && site.isFeatureDisabled('NoDelegate_ResponsiveMainMenuItems'); + } } From 8f0032bb7b38a4593e69fd460737e22c9b48d66a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 14 May 2019 09:52:49 +0200 Subject: [PATCH 4/4] MOBILE-2903 menu: Show 5 items max on phones --- src/core/mainmenu/providers/mainmenu.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/mainmenu/providers/mainmenu.ts b/src/core/mainmenu/providers/mainmenu.ts index 59eab0b7a..5f8fe59ed 100644 --- a/src/core/mainmenu/providers/mainmenu.ts +++ b/src/core/mainmenu/providers/mainmenu.ts @@ -178,6 +178,9 @@ export class CoreMainMenuProvider { numElements = Math.floor(window.innerHeight / CoreMainMenuProvider.ITEM_MIN_WIDTH); } else { numElements = Math.floor(window.innerWidth / CoreMainMenuProvider.ITEM_MIN_WIDTH); + + // Set a maximum elements to show and skip more button. + numElements = numElements >= 5 ? 5 : numElements; } // Set a mínimum elements to show and skip more button.