MOBILE-2416 core: Implement and apply core-ion-tabs
This commit is contained in:
		
							parent
							
								
									bc71bbbdfc
								
							
						
					
					
						commit
						d6c7a586a6
					
				| @ -661,3 +661,8 @@ canvas[core-chart] { | ||||
|     height: 100% !important; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // For some reason, in iOS the pages don't inherit the background-color from ion-app, set it explicitly. | ||||
| .ion-page { | ||||
|   background-color: $background-color; | ||||
| } | ||||
| @ -45,6 +45,8 @@ import { CoreRecaptchaComponent } from './recaptcha/recaptcha'; | ||||
| import { CoreRecaptchaModalComponent } from './recaptcha/recaptchamodal'; | ||||
| import { CoreNavigationBarComponent } from './navigation-bar/navigation-bar'; | ||||
| import { CoreAttachmentsComponent } from './attachments/attachments'; | ||||
| import { CoreIonTabsComponent } from './ion-tabs/ion-tabs'; | ||||
| import { CoreIonTabComponent } from './ion-tabs/ion-tab'; | ||||
| 
 | ||||
| @NgModule({ | ||||
|     declarations: [ | ||||
| @ -75,7 +77,9 @@ import { CoreAttachmentsComponent } from './attachments/attachments'; | ||||
|         CoreRecaptchaComponent, | ||||
|         CoreRecaptchaModalComponent, | ||||
|         CoreNavigationBarComponent, | ||||
|         CoreAttachmentsComponent | ||||
|         CoreAttachmentsComponent, | ||||
|         CoreIonTabsComponent, | ||||
|         CoreIonTabComponent | ||||
|     ], | ||||
|     entryComponents: [ | ||||
|         CoreContextMenuPopoverComponent, | ||||
| @ -113,7 +117,9 @@ import { CoreAttachmentsComponent } from './attachments/attachments'; | ||||
|         CoreTimerComponent, | ||||
|         CoreRecaptchaComponent, | ||||
|         CoreNavigationBarComponent, | ||||
|         CoreAttachmentsComponent | ||||
|         CoreAttachmentsComponent, | ||||
|         CoreIonTabsComponent, | ||||
|         CoreIonTabComponent | ||||
|     ] | ||||
| }) | ||||
| export class CoreComponentsModule {} | ||||
|  | ||||
							
								
								
									
										61
									
								
								src/components/ion-tabs/ion-tab.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/components/ion-tabs/ion-tab.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | ||||
| // (C) Copyright 2015 Martin Dougiamas
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { | ||||
|     Component, Optional, ElementRef, NgZone, Renderer, ComponentFactoryResolver, ChangeDetectorRef, ErrorHandler, OnInit, | ||||
|     OnDestroy, ViewEncapsulation | ||||
| } from '@angular/core'; | ||||
| import { Tab, App, Config, Platform, GestureController, DeepLinker, DomController } from 'ionic-angular'; | ||||
| import { TransitionController } from 'ionic-angular/transitions/transition-controller'; | ||||
| import { CoreIonTabsComponent } from './ion-tabs'; | ||||
| 
 | ||||
| /** | ||||
|  * Equivalent to ion-tab, but to be used inside core-ion-tabs. | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'core-ion-tab', | ||||
|     template: '<div #viewport></div><div class="nav-decor"></div>', | ||||
|     host: { | ||||
|         '[attr.id]': '_tabId', | ||||
|         '[attr.aria-labelledby]': '_btnId', | ||||
|         'role': 'tabpanel' | ||||
|     }, | ||||
|     encapsulation: ViewEncapsulation.None, | ||||
| }) | ||||
| export class CoreIonTabComponent extends Tab implements OnInit, OnDestroy { | ||||
| 
 | ||||
|     constructor(parent: CoreIonTabsComponent, app: App, config: Config, plt: Platform, elementRef: ElementRef, zone: NgZone, | ||||
|             renderer: Renderer, cfr: ComponentFactoryResolver, _cd: ChangeDetectorRef, gestureCtrl: GestureController, | ||||
|             transCtrl: TransitionController, @Optional() linker: DeepLinker, _dom: DomController, errHandler: ErrorHandler) { | ||||
|         super(parent, app, config, plt, elementRef, zone, renderer, cfr, _cd, gestureCtrl, transCtrl, linker, _dom, errHandler); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Component being initialized. | ||||
|      */ | ||||
|     ngOnInit(): void { | ||||
|         super.ngOnInit(); | ||||
| 
 | ||||
|         this.parent.add(this, true); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Component destroyed. | ||||
|      */ | ||||
|     ngOnDestroy(): void { | ||||
|         super.ngOnDestroy(); | ||||
| 
 | ||||
|         this.parent.remove(this); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										13
									
								
								src/components/ion-tabs/ion-tabs.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/components/ion-tabs/ion-tabs.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| <div class="tabbar" role="tablist" #tabbar> | ||||
|     <a [hidden]="_loaded === false" *ngFor="let t of _tabs" [tab]="t" class="tab-button" role="tab" href="#" (ionSelect)="select(t)"></a> | ||||
|     <div class="tab-highlight"></div> | ||||
|     <div *ngIf="_loaded === false" class="core-ion-tabs-loading"> | ||||
|         <span class="core-ion-tabs-loading-spinner"> | ||||
|             <ion-spinner></ion-spinner> | ||||
|         </span> | ||||
|     </div> | ||||
| </div> | ||||
| <div #originalTabs> | ||||
|     <ng-content></ng-content> | ||||
| </div> | ||||
| <div #portal tab-portal></div> | ||||
							
								
								
									
										93
									
								
								src/components/ion-tabs/ion-tabs.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/components/ion-tabs/ion-tabs.scss
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | ||||
| core-ion-tabs { | ||||
|     .tabbar { | ||||
|         z-index: 101; // For some reason, the regular z-index isn't enough with our tabs, use a higher one. | ||||
| 
 | ||||
|         .core-ion-tabs-loading { | ||||
|             width: 100%; | ||||
|             display: table; | ||||
| 
 | ||||
|             .core-ion-tabs-loading-spinner { | ||||
|                 display: table-cell; | ||||
|                 text-align: center; | ||||
|                 vertical-align: middle; | ||||
| 
 | ||||
|                 .spinner circle, .spinner line { | ||||
|                     stroke: $white; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| .ios core-ion-tabs .core-ion-tabs-loading { | ||||
|     min-height: $tabs-ios-tab-min-height; | ||||
| } | ||||
| 
 | ||||
| .md core-ion-tabs .core-ion-tabs-loading { | ||||
|     min-height: $tabs-md-tab-min-height; | ||||
| } | ||||
| 
 | ||||
| .wp core-ion-tabs .core-ion-tabs-loading { | ||||
|     min-height: $tabs-wp-tab-min-height; | ||||
| } | ||||
| 
 | ||||
| // Copy some styles from ion-tabs and ion-tab. | ||||
| core-ion-tabs, core-ion-tab { | ||||
|     @include position(0, null, null, 0); | ||||
| 
 | ||||
|     position: absolute; | ||||
|     z-index: $z-index-page-container; | ||||
|     display: block; | ||||
| 
 | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     overflow: hidden; | ||||
|     contain: strict; | ||||
| } | ||||
| 
 | ||||
| core-ion-tab { | ||||
|     display: none; | ||||
| } | ||||
| 
 | ||||
| core-ion-tab.show-tab { | ||||
|     display: block; | ||||
| } | ||||
| 
 | ||||
| @mixin core-ion-tabs-statusbar-padding($toolbar-height, $toolbar-padding, $content-padding, $cordova-statusbar-padding, $modal-max-width, $style-title: false) { | ||||
| 
 | ||||
|     core-ion-tab > .ion-page, | ||||
|     core-ion-tab > .ion-page > ion-header, | ||||
|     core-ion-tabs > .ion-page.tab-subpage > ion-header { | ||||
|         @include toolbar-statusbar-padding($toolbar-height, $toolbar-padding, $content-padding, $cordova-statusbar-padding); | ||||
| 
 | ||||
|         // If we should style the title elements in the toolbar | ||||
|         @if ($style-title) { | ||||
|             @include toolbar-title-statusbar-padding($toolbar-height, $toolbar-padding, $content-padding, $cordova-statusbar-padding); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @media only screen and (max-width: $modal-max-width) { | ||||
|         .modal-wrapper > .ion-page > ion-header { | ||||
|             @include toolbar-statusbar-padding($toolbar-height, $toolbar-padding, $content-padding, $cordova-statusbar-padding); | ||||
| 
 | ||||
|             // If we should style the title elements in the toolbar | ||||
|             @if ($style-title) { | ||||
|                 @include toolbar-title-statusbar-padding($toolbar-height, $toolbar-padding, $content-padding, $cordova-statusbar-padding); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| .ios { | ||||
|     @include core-ion-tabs-statusbar-padding($toolbar-ios-height, $toolbar-ios-padding, $content-ios-padding, $cordova-ios-statusbar-padding, $cordova-ios-statusbar-padding-modal-max-width, true); | ||||
| } | ||||
| 
 | ||||
| .md { | ||||
|    @include core-ion-tabs-statusbar-padding($toolbar-md-height, $toolbar-md-padding, $content-md-padding, $cordova-md-statusbar-padding, $cordova-md-statusbar-padding-modal-max-width); | ||||
| } | ||||
| 
 | ||||
| .wp { | ||||
|     @include core-ion-tabs-statusbar-padding($toolbar-wp-height, $toolbar-wp-padding, $content-wp-padding, $cordova-wp-statusbar-padding, $cordova-wp-statusbar-padding-modal-max-width); | ||||
| } | ||||
							
								
								
									
										196
									
								
								src/components/ion-tabs/ion-tabs.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								src/components/ion-tabs/ion-tabs.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,196 @@ | ||||
| // (C) Copyright 2015 Martin Dougiamas
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Component, Optional, ElementRef, Renderer, ViewEncapsulation, forwardRef, ViewChild, Input } from '@angular/core'; | ||||
| import { Tabs, NavController, ViewController, App, Config, Platform, DeepLinker, Keyboard, RootNode } from 'ionic-angular'; | ||||
| import { CoreIonTabComponent } from './ion-tab'; | ||||
| import { CoreUtilsProvider } from '@providers/utils/utils'; | ||||
| 
 | ||||
| /** | ||||
|  * Equivalent to ion-tabs. It has 2 improvements: | ||||
|  *     - If a core-ion-tab is added or removed, it will be reflected in the tab bar in the right position. | ||||
|  *     - It supports a loaded input to tell when are the tabs ready. | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'core-ion-tabs', | ||||
|     templateUrl: 'ion-tabs.html', | ||||
|     encapsulation: ViewEncapsulation.None, | ||||
|     providers: [{provide: RootNode, useExisting: forwardRef(() => CoreIonTabsComponent) }] | ||||
| }) | ||||
| export class CoreIonTabsComponent extends Tabs { | ||||
| 
 | ||||
|     /** | ||||
|      * Whether the tabs have been loaded. If defined, tabs won't be initialized until it's set to true. | ||||
|      */ | ||||
|     @Input() set loaded(val: boolean) { | ||||
|         this._loaded = this.utils.isTrueOrOne(val); | ||||
| 
 | ||||
|         if (this.viewInit && !this.initialized) { | ||||
|             // Use a setTimeout to make sure the tabs have been loaded.
 | ||||
|             setTimeout(() => { | ||||
|                 this.initTabs(); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @ViewChild('originalTabs') originalTabsRef: ElementRef; | ||||
| 
 | ||||
|     _loaded: boolean; // Whether tabs have been loaded.
 | ||||
| 
 | ||||
|     /** | ||||
|      * List of tabs that haven't been initialized yet. This is required because IonTab calls add() on the constructor, | ||||
|      * but we need it to be called in OnInit to be able to determine the tab position. | ||||
|      * @type {CoreIonTabComponent[]} | ||||
|      */ | ||||
|     protected tabsNotInit: CoreIonTabComponent[] = []; | ||||
| 
 | ||||
|     protected tabsIds: string[] = []; // An array to keep the order of tab IDs when they're sorted.
 | ||||
|     protected tabsNotInitIds: string[] = []; // An array to keep the order of tab IDs for non-init tabs.
 | ||||
|     protected viewInit = false; // Whether the view has been initialized.
 | ||||
|     protected initialized = false; // Whether tabs have been initialized.
 | ||||
| 
 | ||||
|     constructor(protected utils: CoreUtilsProvider, @Optional() parent: NavController, @Optional() viewCtrl: ViewController, | ||||
|             _app: App, config: Config, elementRef: ElementRef, _plt: Platform, renderer: Renderer, _linker: DeepLinker, | ||||
|             keyboard?: Keyboard) { | ||||
|         super(parent, viewCtrl, _app, config, elementRef, _plt, renderer, _linker, keyboard); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * View has been initialized. | ||||
|      */ | ||||
|     ngAfterViewInit(): void { | ||||
|         this.viewInit = true; | ||||
| 
 | ||||
|         super.ngAfterViewInit(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Add a new tab if it isn't already in the list of tabs. | ||||
|      * | ||||
|      * @param {CoreIonTabComponent} tab The tab to add. | ||||
|      * @param {boolean} [isInit] Whether the tab has been initialized. | ||||
|      * @return {string} The tab ID. | ||||
|      */ | ||||
|     add(tab: CoreIonTabComponent, isInit?: boolean): string { | ||||
|         // Check if tab is already in the list of initialized tabs.
 | ||||
|         let position = this._tabs.indexOf(tab); | ||||
| 
 | ||||
|         if (position != -1) { | ||||
|             return this.tabsIds[position]; | ||||
|         } | ||||
| 
 | ||||
|         // Now check if the tab is in the not init list.
 | ||||
|         position = this.tabsNotInit.indexOf(tab); | ||||
|         if (position != -1) { | ||||
|             if (!isInit) { | ||||
|                 return this.tabsNotInitIds[position]; | ||||
|             } | ||||
| 
 | ||||
|             // The tab wasn't initialized but now it is. Move it from one array to the other.
 | ||||
|             const tabId = this.tabsNotInitIds[position]; | ||||
|             this.tabsNotInit.splice(position, 1); | ||||
|             this.tabsNotInitIds.splice(position, 1); | ||||
| 
 | ||||
|             this._tabs.push(tab); | ||||
|             this.tabsIds.push(tabId); | ||||
| 
 | ||||
|             this.sortTabs(); | ||||
| 
 | ||||
|             return tabId; | ||||
|         } | ||||
| 
 | ||||
|         // Tab is new. In this case isInit should always be false, but check it just in case.
 | ||||
|         const id = this.id + '-' + (++this._ids); | ||||
| 
 | ||||
|         if (isInit) { | ||||
|             this._tabs.push(tab); | ||||
|             this.tabsIds.push(id); | ||||
| 
 | ||||
|             this.sortTabs(); | ||||
|         } else { | ||||
|             this.tabsNotInit.push(tab); | ||||
|             this.tabsNotInitIds.push(id); | ||||
|         } | ||||
| 
 | ||||
|         return id; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Initialize the tabs. | ||||
|      * | ||||
|      * @return {Promise<any>} Promise resolved when done. | ||||
|      */ | ||||
|     initTabs(): Promise<any> { | ||||
|         if (!this.initialized && (this._loaded || typeof this._loaded == 'undefined')) { | ||||
|             this.initialized = true; | ||||
| 
 | ||||
|             return super.initTabs(); | ||||
|         } else { | ||||
|             // Tabs not loaded yet. Set the tab bar position so the tab bar is shown, it'll have a spinner.
 | ||||
|             this.setTabbarPosition(-1, 0); | ||||
| 
 | ||||
|             return Promise.resolve(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Remove a tab from the list of tabs. | ||||
|      * | ||||
|      * @param {CoreIonTabComponent} tab The tab to remove. | ||||
|      */ | ||||
|     remove(tab: CoreIonTabComponent): void { | ||||
|         // First search in the list of initialized tabs.
 | ||||
|         let index = this._tabs.indexOf(tab); | ||||
| 
 | ||||
|         if (index != -1) { | ||||
|             this._tabs.splice(index, 1); | ||||
|             this.tabsIds.splice(index, 1); | ||||
|         } else { | ||||
|             // Not found, search in the list of non-init tabs.
 | ||||
|             index = this.tabsNotInit.indexOf(tab); | ||||
| 
 | ||||
|             if (index != -1) { | ||||
|                 this.tabsNotInit.splice(index, 1); | ||||
|                 this.tabsNotInitIds.splice(index, 1); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sort the tabs, keeping the same order as in the original list. | ||||
|      */ | ||||
|     sortTabs(): void { | ||||
|         if (this.originalTabsRef) { | ||||
|             const newTabs = [], | ||||
|                 newTabsIds = [], | ||||
|                 originalTabsEl = this.originalTabsRef.nativeElement; | ||||
| 
 | ||||
|             this._tabs.forEach((tab, index) => { | ||||
|                 const originalIndex = Array.prototype.indexOf.call(originalTabsEl.children, tab.getNativeElement()); | ||||
|                 if (originalIndex != -1) { | ||||
|                     newTabs[originalIndex] = tab; | ||||
|                     newTabsIds[originalIndex] = this.tabsIds[index]; | ||||
|                 } | ||||
|             }); | ||||
| 
 | ||||
|             // Remove undefined values. It can happen if the view has some tabs that were destroyed but weren't removed yet.
 | ||||
|             this._tabs = newTabs.filter((tab) => { | ||||
|                 return typeof tab != 'undefined'; | ||||
|             }); | ||||
|             this.tabsIds = newTabsIds.filter((id) => { | ||||
|                 return typeof id != 'undefined'; | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -28,7 +28,7 @@ page-core-login-init { | ||||
|             margin-bottom: 30px; | ||||
|         } | ||||
| 
 | ||||
|         .spinner circle { | ||||
|         .spinner circle, .spinner line { | ||||
|             stroke: $core-init-screen-spinner-color; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| <ion-tabs *ngIf="loaded" #mainTabs [selectedIndex]="initialTab" tabsPlacement="bottom" tabsLayout="title-hide"> | ||||
|     <ion-tab [enabled]="false" [show]="false" [root]="redirectPage" [rootParams]="redirectParams"></ion-tab> | ||||
|     <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}}"></ion-tab> | ||||
| </ion-tabs> | ||||
| <core-ion-tabs #mainTabs [hidden]="!showTabs" [loaded]="loaded" [selectedIndex]="initialTab" tabsPlacement="bottom" tabsLayout="title-hide"> | ||||
|     <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 root="CoreMainMenuMorePage" [tabTitle]="'core.more' | translate" tabIcon="more"></core-ion-tab> | ||||
| </core-ion-tabs> | ||||
|  | ||||
| @ -15,6 +15,7 @@ | ||||
| import { NgModule } from '@angular/core'; | ||||
| import { IonicPageModule } from 'ionic-angular'; | ||||
| import { TranslateModule } from '@ngx-translate/core'; | ||||
| import { CoreComponentsModule } from '@components/components.module'; | ||||
| import { CoreMainMenuPage } from './menu'; | ||||
| 
 | ||||
| @NgModule({ | ||||
| @ -22,6 +23,7 @@ import { CoreMainMenuPage } from './menu'; | ||||
|         CoreMainMenuPage, | ||||
|     ], | ||||
|     imports: [ | ||||
|         CoreComponentsModule, | ||||
|         IonicPageModule.forChild(CoreMainMenuPage), | ||||
|         TranslateModule.forChild() | ||||
|     ], | ||||
|  | ||||
| @ -12,11 +12,11 @@ | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Component, OnDestroy, ViewChild } from '@angular/core'; | ||||
| import { IonicPage, NavController, NavParams, Tabs } from 'ionic-angular'; | ||||
| import { Component, OnDestroy } from '@angular/core'; | ||||
| import { IonicPage, NavController, NavParams } from 'ionic-angular'; | ||||
| import { CoreSitesProvider } from '@providers/sites'; | ||||
| import { CoreMainMenuProvider } from '../../providers/mainmenu'; | ||||
| import { CoreMainMenuDelegate, CoreMainMenuHandlerData } from '../../providers/delegate'; | ||||
| import { CoreMainMenuDelegate, CoreMainMenuHandlerToDisplay } from '../../providers/delegate'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that displays the main menu of the app. | ||||
| @ -27,41 +27,14 @@ import { CoreMainMenuDelegate, CoreMainMenuHandlerData } from '../../providers/d | ||||
|     templateUrl: 'menu.html', | ||||
| }) | ||||
| export class CoreMainMenuPage implements OnDestroy { | ||||
|     // Use a setter to wait for ion-tabs to be loaded because it's inside a ngIf.
 | ||||
|     @ViewChild('mainTabs') set mainTabs(ionTabs: Tabs) { | ||||
|         if (ionTabs && this.redirectPage && !this.redirectPageLoaded) { | ||||
|             // Tabs ready and there is a redirect page set. Load it.
 | ||||
|             this.redirectPageLoaded = true; | ||||
| 
 | ||||
|             // Check if the page is the root page of any of the tabs.
 | ||||
|             let indexToSelect = 0; | ||||
|             for (let i = 0; i < this.tabs.length; i++) { | ||||
|                 if (this.tabs[i].page == this.redirectPage) { | ||||
|                     indexToSelect = i + 1; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             // Use a setTimeout, otherwise loading the first tab opens a new state for some reason.
 | ||||
|             setTimeout(() => { | ||||
|                 ionTabs.select(indexToSelect); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     tabs: CoreMainMenuHandlerData[] = []; | ||||
|     loaded: boolean; | ||||
|     tabs: CoreMainMenuHandlerToDisplay[] = []; | ||||
|     loaded = false; | ||||
|     redirectPage: string; | ||||
|     redirectParams: any; | ||||
|     initialTab: number; | ||||
|     showTabs = false; | ||||
| 
 | ||||
|     protected subscription; | ||||
|     protected moreTabData = { | ||||
|         page: 'CoreMainMenuMorePage', | ||||
|         title: 'core.more', | ||||
|         icon: 'more' | ||||
|     }; | ||||
|     protected moreTabAdded = false; | ||||
|     protected redirectPageLoaded = false; | ||||
| 
 | ||||
|     constructor(private menuDelegate: CoreMainMenuDelegate, private sitesProvider: CoreSitesProvider, navParams: NavParams, | ||||
| @ -80,42 +53,58 @@ export class CoreMainMenuPage implements OnDestroy { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this.showTabs = true; | ||||
| 
 | ||||
|         const site = this.sitesProvider.getCurrentSite(), | ||||
|             displaySiteHome = site.getInfo() && site.getInfo().userhomepage === 0; | ||||
| 
 | ||||
|         this.subscription = this.menuDelegate.getHandlers().subscribe((handlers) => { | ||||
|             handlers = handlers.slice(0, CoreMainMenuProvider.NUM_MAIN_HANDLERS); // Get main handlers.
 | ||||
| 
 | ||||
|             // Check if handlers are already in tabs. Add the ones that aren't.
 | ||||
|             // @todo: https://github.com/ionic-team/ionic/issues/13633
 | ||||
|             // 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 = []; | ||||
| 
 | ||||
|             for (let i = 0; i < handlers.length; i++) { | ||||
|                 const handler = handlers[i], | ||||
|                     shouldSelect = (displaySiteHome && handler.name == 'CoreSiteHome') || | ||||
|                                    (!displaySiteHome && handler.name == 'CoreCourses'); | ||||
|                 let found = false; | ||||
|                 const handler = handlers[i]; | ||||
| 
 | ||||
|                 for (let j = 0; j < this.tabs.length; j++) { | ||||
|                     const tab = this.tabs[j]; | ||||
|                     if (tab.title == handler.title && tab.icon == handler.icon) { | ||||
|                         found = true; | ||||
|                         if (shouldSelect) { | ||||
|                             this.initialTab = j; | ||||
|                         } | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 // Check if the handler is already in the tabs list. If so, use it.
 | ||||
|                 const tab = this.tabs.find((tab) => { | ||||
|                     return tab.title == handler.title && tab.icon == handler.icon; | ||||
|                 }); | ||||
| 
 | ||||
|                 if (!found) { | ||||
|                     this.tabs.push(handler); | ||||
|                     if (shouldSelect) { | ||||
|                         this.initialTab = this.tabs.length; | ||||
|                     } | ||||
|                 } | ||||
|                 newTabs.push(tab || handler); | ||||
|             } | ||||
| 
 | ||||
|             if (!this.moreTabAdded) { | ||||
|                 this.moreTabAdded = true; | ||||
|                 this.tabs.push(this.moreTabData); // Add "More" tab.
 | ||||
|             this.tabs = newTabs; | ||||
| 
 | ||||
|             // Sort them by priority so new handlers are in the right position.
 | ||||
|             this.tabs.sort((a, b) => { | ||||
|                 return b.priority - a.priority; | ||||
|             }); | ||||
| 
 | ||||
|             if (typeof this.initialTab == 'undefined' && !this.loaded) { | ||||
|                 // Calculate the tab to load.
 | ||||
|                 if (this.redirectPage) { | ||||
|                     // Check if the redirect page is the root page of any of the tabs.
 | ||||
|                     this.initialTab = 0; | ||||
| 
 | ||||
|                     for (let i = 0; i < this.tabs.length; i++) { | ||||
|                         if (this.tabs[i].page == this.redirectPage) { | ||||
|                             this.initialTab = i + 1; | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                 } else { | ||||
|                     // By default, course overview will be loaded (3.3+). Check if we need to select Site Home or My Courses.
 | ||||
|                     for (let i = 0; i < this.tabs.length; i++) { | ||||
|                         const handler = handlers[i]; | ||||
|                         if ((displaySiteHome && handler.name == 'CoreSiteHome') || | ||||
|                                 (!displaySiteHome && handler.name == 'CoreCourses')) { | ||||
|                             this.initialTab = i; | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             this.loaded = this.menuDelegate.areHandlersLoaded(); | ||||
|  | ||||
| @ -99,6 +99,12 @@ export interface CoreMainMenuHandlerToDisplay extends CoreMainMenuHandlerData { | ||||
|      * @type {string} | ||||
|      */ | ||||
|     name?: string; | ||||
| 
 | ||||
|     /** | ||||
|      * Priority of the handler. | ||||
|      * @type {number} | ||||
|      */ | ||||
|     priority?: number; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -168,7 +174,9 @@ export class CoreMainMenuDelegate extends CoreDelegate { | ||||
| 
 | ||||
|         // Return only the display data.
 | ||||
|         const displayData = handlersData.map((item) => { | ||||
|             // Move the name and the priority to the display data.
 | ||||
|             item.data.name = item.name; | ||||
|             item.data.priority = item.priority; | ||||
| 
 | ||||
|             return item.data; | ||||
|         }); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user