diff --git a/src/components/ion-tabs/core-ion-tabs.html b/src/components/ion-tabs/core-ion-tabs.html
index e255871b6..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/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/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 ae3f68830..358b840bc 100644
--- a/src/core/mainmenu/pages/menu/menu.ts
+++ b/src/core/mainmenu/pages/menu/menu.ts
@@ -30,10 +30,12 @@ import { CoreMainMenuDelegate, CoreMainMenuHandlerToDisplay } from '../../provid
})
export class CoreMainMenuPage implements OnDestroy {
tabs: CoreMainMenuHandlerToDisplay[] = [];
+ allHandlers: CoreMainMenuHandlerToDisplay[];
loaded = false;
redirectPage: string;
redirectParams: any;
showTabs = false;
+ tabsPlacement = 'bottom';
protected subscription;
protected redirectObs: any;
@@ -42,7 +44,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 +81,34 @@ 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) {
+ 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.
const newTabs = [];
@@ -94,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.
@@ -105,17 +155,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 +193,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/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 ac38ef92b..5f8fe59ed 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';
@@ -52,8 +53,12 @@ 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.
+ 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.
@@ -158,4 +163,62 @@ 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 (!this.isResponsiveMainMenuItemsDisabledInCurrentSite() && window && window.innerWidth) {
+ let numElements;
+
+ 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 maximum elements to show and skip more button.
+ numElements = numElements >= 5 ? 5 : numElements;
+ }
+
+ // Set a mÃnimum elements to show and skip more button.
+ return numElements > 1 ? numElements - 1 : 1;
+ }
+
+ 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';
+ }
+
+ /**
+ * 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');
+ }
}