commit
b172fafbd6
|
@ -1,5 +1,5 @@
|
||||||
<div class="tabbar" role="tablist" #tabbar [hidden]="hidden">
|
<div class="tabbar" role="tablist" #tabbar [hidden]="hidden">
|
||||||
<a [hidden]="_loaded === false" *ngFor="let t of _tabs" [tab]="t" class="tab-button" role="tab" href="#" (ionSelect)="select(t)" [attr.aria-hidden]="!t.show" [attr.aria-label]="t.tabTitle || ''"></a>
|
<a [hidden]="_loaded === false" *ngFor="let t of _tabs" [tab]="t" class="tab-button" role="tab" href="#" (ionSelect)="select(t)" [attr.aria-hidden]="!t.show" [attr.aria-label]="t.tabTitle || ''" [title]="t.tabTitle || ''"></a>
|
||||||
<div class="tab-highlight"></div>
|
<div class="tab-highlight"></div>
|
||||||
<div *ngIf="_loaded === false" class="core-ion-tabs-loading">
|
<div *ngIf="_loaded === false" class="core-ion-tabs-loading">
|
||||||
<span class="core-ion-tabs-loading-spinner">
|
<span class="core-ion-tabs-loading-spinner">
|
||||||
|
@ -7,7 +7,7 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div #originalTabs>
|
<div #originalTabs class="tabcontent">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</div>
|
</div>
|
||||||
<div #portal tab-portal></div>
|
<div #portal tab-portal></div>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
$core-sidetab-size: 60px !default;
|
||||||
|
|
||||||
ion-app.app-root core-ion-tabs {
|
ion-app.app-root core-ion-tabs {
|
||||||
.tabbar {
|
.tabbar {
|
||||||
z-index: 101; // For some reason, the regular z-index isn't enough with our tabs, use a higher one.
|
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 {
|
.tab-badge.badge {
|
||||||
background-color: $ion-tabs-badge-color;
|
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 {
|
ion-app.app-root.ios core-ion-tabs .core-ion-tabs-loading {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<core-ion-tabs #mainTabs [hidden]="!showTabs" [loaded]="loaded" tabsPlacement="bottom" tabsLayout="title-hide">
|
<core-ion-tabs #mainTabs [hidden]="!showTabs" [loaded]="loaded" tabsLayout="title-hide" [attr.tabsPlacement]="tabsPlacement">
|
||||||
<core-ion-tab [enabled]="false" [show]="false" [root]="redirectPage" [rootParams]="redirectParams"></core-ion-tab>
|
<core-ion-tab [enabled]="false" [show]="false" [root]="redirectPage" [rootParams]="redirectParams"></core-ion-tab>
|
||||||
<core-ion-tab *ngFor="let tab of tabs" [root]="tab.page" [rootParams]="tab.pageParams" [tabTitle]="tab.title | translate" [tabIcon]="tab.icon" [tabBadge]="tab.badge" class="{{tab.class}}"></core-ion-tab>
|
<core-ion-tab *ngFor="let tab of tabs" [root]="tab.page" [rootParams]="tab.pageParams" [tabTitle]="tab.title | translate" [tabIcon]="tab.icon" [tabBadge]="tab.badge" class="{{tab.class}}" [enabled]="!tab.hide" [show]="!tab.hide"></core-ion-tab>
|
||||||
<core-ion-tab root="CoreMainMenuMorePage" [tabTitle]="'core.more' | translate" tabIcon="more"></core-ion-tab>
|
<core-ion-tab root="CoreMainMenuMorePage" [tabTitle]="'core.more' | translate" tabIcon="more"></core-ion-tab>
|
||||||
</core-ion-tabs>
|
</core-ion-tabs>
|
||||||
|
|
|
@ -30,10 +30,12 @@ import { CoreMainMenuDelegate, CoreMainMenuHandlerToDisplay } from '../../provid
|
||||||
})
|
})
|
||||||
export class CoreMainMenuPage implements OnDestroy {
|
export class CoreMainMenuPage implements OnDestroy {
|
||||||
tabs: CoreMainMenuHandlerToDisplay[] = [];
|
tabs: CoreMainMenuHandlerToDisplay[] = [];
|
||||||
|
allHandlers: CoreMainMenuHandlerToDisplay[];
|
||||||
loaded = false;
|
loaded = false;
|
||||||
redirectPage: string;
|
redirectPage: string;
|
||||||
redirectParams: any;
|
redirectParams: any;
|
||||||
showTabs = false;
|
showTabs = false;
|
||||||
|
tabsPlacement = 'bottom';
|
||||||
|
|
||||||
protected subscription;
|
protected subscription;
|
||||||
protected redirectObs: any;
|
protected redirectObs: any;
|
||||||
|
@ -42,7 +44,8 @@ export class CoreMainMenuPage implements OnDestroy {
|
||||||
@ViewChild('mainTabs') mainTabs: CoreIonTabsComponent;
|
@ViewChild('mainTabs') mainTabs: CoreIonTabsComponent;
|
||||||
|
|
||||||
constructor(private menuDelegate: CoreMainMenuDelegate, private sitesProvider: CoreSitesProvider, navParams: NavParams,
|
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.
|
// Check if the menu was loaded with a redirect.
|
||||||
const redirectPage = navParams.get('redirectPage');
|
const redirectPage = navParams.get('redirectPage');
|
||||||
|
@ -78,10 +81,34 @@ export class CoreMainMenuPage implements OnDestroy {
|
||||||
|
|
||||||
this.subscription = this.menuDelegate.getHandlers().subscribe((handlers) => {
|
this.subscription = this.menuDelegate.getHandlers().subscribe((handlers) => {
|
||||||
// Remove the handlers that should only appear in the More menu.
|
// Remove the handlers that should only appear in the More menu.
|
||||||
handlers = handlers.filter((handler) => {
|
this.allHandlers = handlers.filter((handler) => {
|
||||||
return !handler.onlyInMore;
|
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.
|
// 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 = [];
|
const newTabs = [];
|
||||||
|
@ -94,9 +121,32 @@ export class CoreMainMenuPage implements OnDestroy {
|
||||||
return tab.title == handler.title && tab.icon == handler.icon;
|
return tab.title == handler.title && tab.icon == handler.icon;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tab ? tab.hide = false : null;
|
||||||
|
handler.hide = false;
|
||||||
|
|
||||||
newTabs.push(tab || handler);
|
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;
|
this.tabs = newTabs;
|
||||||
|
|
||||||
// Sort them by priority so new handlers are in the right position.
|
// 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();
|
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 {
|
ngOnDestroy(): void {
|
||||||
this.subscription && this.subscription.unsubscribe();
|
this.subscription && this.subscription.unsubscribe();
|
||||||
this.redirectObs && this.redirectObs.off();
|
this.redirectObs && this.redirectObs.off();
|
||||||
|
window.removeEventListener('resize', this.initHandlers.bind(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import { CoreMainMenuProvider, CoreMainMenuCustomItem } from '../../providers/ma
|
||||||
})
|
})
|
||||||
export class CoreMainMenuMorePage implements OnDestroy {
|
export class CoreMainMenuMorePage implements OnDestroy {
|
||||||
handlers: CoreMainMenuHandlerData[];
|
handlers: CoreMainMenuHandlerData[];
|
||||||
|
allHandlers: CoreMainMenuHandlerData[];
|
||||||
handlersLoaded: boolean;
|
handlersLoaded: boolean;
|
||||||
siteInfo: any;
|
siteInfo: any;
|
||||||
siteName: string;
|
siteName: string;
|
||||||
|
@ -58,32 +59,44 @@ export class CoreMainMenuMorePage implements OnDestroy {
|
||||||
ionViewDidLoad(): void {
|
ionViewDidLoad(): void {
|
||||||
// Load the handlers.
|
// Load the handlers.
|
||||||
this.subscription = this.menuDelegate.getHandlers().subscribe((handlers) => {
|
this.subscription = this.menuDelegate.getHandlers().subscribe((handlers) => {
|
||||||
// Calculate the main handlers to not display them in this view.
|
this.allHandlers = handlers;
|
||||||
const mainHandlers = handlers.filter((handler) => {
|
|
||||||
return !handler.onlyInMore;
|
|
||||||
}).slice(0, CoreMainMenuProvider.NUM_MAIN_HANDLERS);
|
|
||||||
|
|
||||||
// Get only the handlers that don't appear in the main view.
|
this.initHandlers();
|
||||||
this.handlers = [];
|
|
||||||
handlers.forEach((handler) => {
|
|
||||||
if (mainHandlers.indexOf(handler) == -1) {
|
|
||||||
this.handlers.push(handler);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.handlersLoaded = this.menuDelegate.areHandlersLoaded();
|
window.addEventListener('resize', this.initHandlers.bind(this));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page destroyed.
|
* Page destroyed.
|
||||||
*/
|
*/
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
window.removeEventListener('resize', this.initHandlers.bind(this));
|
||||||
|
|
||||||
if (this.subscription) {
|
if (this.subscription) {
|
||||||
this.subscription.unsubscribe();
|
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.
|
* Load the site info required by the view.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -111,6 +111,12 @@ export interface CoreMainMenuHandlerToDisplay extends CoreMainMenuHandlerData {
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
priority?: number;
|
priority?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide tab. Used then resizing.
|
||||||
|
* @type {[type]}
|
||||||
|
*/
|
||||||
|
hide?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { NavController } from 'ionic-angular';
|
||||||
import { CoreLangProvider } from '@providers/lang';
|
import { CoreLangProvider } from '@providers/lang';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreConfigConstants } from '../../../configconstants';
|
import { CoreConfigConstants } from '../../../configconstants';
|
||||||
|
@ -52,8 +53,12 @@ export interface CoreMainMenuCustomItem {
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CoreMainMenuProvider {
|
export class CoreMainMenuProvider {
|
||||||
static NUM_MAIN_HANDLERS = 4;
|
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.
|
* 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');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue