refactor(Tabs): Tabs no longer extends NavController

This commit is contained in:
Adam Bradley
2015-10-11 22:44:18 -05:00
parent 8115f8d340
commit ffc5a5e2ce
5 changed files with 93 additions and 154 deletions

View File

@ -862,22 +862,6 @@ export class NavController extends Ion {
return null; return null;
} }
/**
* TODO
* @param {TODO} instance TODO
* @returns {TODO} TODO
*/
getByInstance(instance) {
if (instance) {
for (let i = 0, ii = this._views.length; i < ii; i++) {
if (this._views[i].instance === instance) {
return this._views[i];
}
}
}
return null;
}
/** /**
* TODO * TODO
* @param {TODO} index TODO * @param {TODO} index TODO

View File

@ -1,4 +1,4 @@
import {Directive, Component, Host, ElementRef, Compiler, DynamicComponentLoader, AppViewManager, forwardRef, Injector, NgZone, ViewContainerRef} from 'angular2/angular2'; import {Component, Directive, Host, ElementRef, Compiler, DynamicComponentLoader, AppViewManager, forwardRef, NgZone} from 'angular2/angular2';
import {IonicApp} from '../app/app'; import {IonicApp} from '../app/app';
import {IonicConfig} from '../../config/config'; import {IonicConfig} from '../../config/config';
@ -60,8 +60,8 @@ import {Tabs} from './tabs';
], ],
host: { host: {
'[attr.id]': 'panelId', '[attr.id]': 'panelId',
'[attr.aria-labelledby]': 'labeledBy', '[attr.aria-labelledby]': 'btnId',
'[attr.aria-hidden]': 'isNotSelected', '[class.show-tab]': 'isSelected',
'role': 'tabpanel' 'role': 'tabpanel'
}, },
template: '<template content-anchor></template><ng-content></ng-content>', template: '<template content-anchor></template><ng-content></ng-content>',
@ -69,13 +69,6 @@ import {Tabs} from './tabs';
}) })
export class Tab extends NavController { export class Tab extends NavController {
/**
* TODO
* @param {Tabs} tabs TODO
* @param {ElementRef} elementRef TODO
* @param {Injector} injector TODO
* @param {NgZone} zone TODO
*/
constructor( constructor(
@Host() tabs: Tabs, @Host() tabs: Tabs,
app: IonicApp, app: IonicApp,
@ -86,44 +79,18 @@ export class Tab extends NavController {
viewManager: AppViewManager, viewManager: AppViewManager,
zone: NgZone zone: NgZone
) { ) {
// A Tab is both a container of many pages, and is a page itself.
// A Tab is one page within it's Host Tabs (which also extends NavController)
// A Tab is a NavController for its child pages // A Tab is a NavController for its child pages
super(tabs, app, config, elementRef, compiler, loader, viewManager, zone); super(tabs, app, config, elementRef, compiler, loader, viewManager, zone);
this.tabs = tabs; this.tabs = tabs;
let viewCtrl = this.viewCtrl = new ViewController(tabs); this._isInitial = tabs.add(this);
viewCtrl.setInstance(this);
viewCtrl.setContentRef(elementRef);
this._initTab = tabs.addTab(this);
this.getNavbar = viewCtrl.getNavbar = () => {
let activeView = this.getActive();
return activeView && activeView.getNavbar();
};
viewCtrl.enableBack = () => {
// override ViewController's enableBack(), should use the
// active child nav item's enableBack() instead
let activeView = this.getActive();
return (activeView && activeView.enableBack());
};
this.panelId = 'tab-panel-' + viewCtrl.id;
this.labeledBy = 'tab-button-' + viewCtrl.id;
} }
onInit() { onInit() {
console.log('Tab onInit'); console.debug('Tab onInit');
if (this._initTab) { if (this._isInitial) {
this.tabs.select(this); this.tabs.select(this);
} else {
// TODO: OPTIONAL PRELOAD OTHER TABS!
// setTimeout(() => {
// this.load();
// }, 300);
} }
} }
@ -133,24 +100,14 @@ export class Tab extends NavController {
animate: false, animate: false,
navbar: false navbar: false
}; };
this.push(this.root, null, opts).then(() => { this.push(this.root, null, opts).then(callback);
callback && callback();
});
this._loaded = true; this._loaded = true;
} else { } else {
callback && callback(); callback();
} }
} }
get isSelected() {
return this.tabs.isActive(this.viewCtrl);
}
get isNotSelected() {
return !this.tabs.isActive(this.viewCtrl);
}
loadContainer(componentType, hostProtoViewRef, viewCtrl, done) { loadContainer(componentType, hostProtoViewRef, viewCtrl, done) {
this.loadNextToAnchor(componentType, this.contentAnchorRef, viewCtrl).then(componentRef => { this.loadNextToAnchor(componentType, this.contentAnchorRef, viewCtrl).then(componentRef => {

View File

@ -37,7 +37,7 @@ ion-tab {
flex-direction: column; flex-direction: column;
display: none; display: none;
&.show-view { &.show-tab {
display: flex; display: flex;
} }
} }
@ -46,6 +46,10 @@ ion-tabs > ion-navbar-section {
order: $flex-order-tab-bar-navbar; order: $flex-order-tab-bar-navbar;
} }
ion-tabs ion-navbar.toolbar.deselected-tab {
display: none;
}
ion-tab-bar-section { ion-tab-bar-section {
position: relative; position: relative;
order: $flex-order-tab-bar-bottom; order: $flex-order-tab-bar-bottom;

View File

@ -1,13 +1,11 @@
import {Component, Directive, Injector, ElementRef, Compiler, DynamicComponentLoader, AppViewManager, NgZone, Optional, Host, NgFor, forwardRef, ViewContainerRef} from 'angular2/angular2'; import {Directive, ElementRef, Optional, Host, NgFor, forwardRef, ViewContainerRef} from 'angular2/angular2';
import {Ion} from '../ion'; import {Ion} from '../ion';
import {IonicApp} from '../app/app'; import {IonicApp} from '../app/app';
import {IonicConfig} from '../../config/config'; import {IonicConfig} from '../../config/config';
import {NavController} from '../nav/nav-controller';
import {ViewController} from '../nav/view-controller'; import {ViewController} from '../nav/view-controller';
import {ConfigComponent} from '../../config/decorators'; import {ConfigComponent} from '../../config/decorators';
import {Icon} from '../icon/icon'; import {Icon} from '../icon/icon';
import * as dom from 'ionic/util/dom';
/** /**
@ -72,7 +70,7 @@ import * as dom from 'ionic/util/dom';
'</ion-navbar-section>' + '</ion-navbar-section>' +
'<ion-tab-bar-section>' + '<ion-tab-bar-section>' +
'<tab-bar role="tablist">' + '<tab-bar role="tablist">' +
'<a *ng-for="#t of tabs" [tab]="t" class="tab-button" role="tab">' + '<a *ng-for="#t of _tabs" [tab]="t" class="tab-button" role="tab">' +
'<icon [name]="t.tabIcon" [is-active]="t.isSelected" class="tab-button-icon"></icon>' + '<icon [name]="t.tabIcon" [is-active]="t.isSelected" class="tab-button-icon"></icon>' +
'<span class="tab-button-text">{{t.tabTitle}}</span>' + '<span class="tab-button-text">{{t.tabTitle}}</span>' +
'</a>' + '</a>' +
@ -90,71 +88,50 @@ import * as dom from 'ionic/util/dom';
forwardRef(() => TabNavBarAnchor) forwardRef(() => TabNavBarAnchor)
] ]
}) })
export class Tabs extends NavController { export class Tabs extends Ion {
/** /**
* TODO * Hi, I'm "Tabs". I'm really just another Page, with a few special features.
* "Tabs" can be a sibling to other pages that can be navigated to, which those
* sibling pages may or may not have their own tab bars (doesn't matter). The fact
* that "Tabs" can happen to have children "Tab" classes, and each "Tab" can have
* children pages with their own "ViewController" instance, as nothing to do with the
* point that "Tabs" is itself is just a page with its own instance of ViewController.
*/ */
constructor( constructor(
@Optional() hostNavCtrl: NavController,
@Optional() viewCtrl: ViewController,
app: IonicApp, app: IonicApp,
config: IonicConfig, config: IonicConfig,
elementRef: ElementRef, elementRef: ElementRef,
compiler: Compiler, @Optional() viewCtrl: ViewController
loader: DynamicComponentLoader,
viewManager: AppViewManager,
zone: NgZone
) { ) {
super(hostNavCtrl, app, config, elementRef, compiler, loader, viewManager, zone); super(elementRef, config);
this.app = app;
this._ready = new Promise(res => { this._isReady = res; }); // collection of children "Tab" instances, which extends NavController
this._tabs = [];
// Tabs may also be an actual ViewController which was navigated to // Tabs may also be an actual ViewController which was navigated to
// if Tabs is static and not navigated to within a NavController // if Tabs is static and not navigated to within a NavController
// then skip this and don't treat it as it's own ViewController // then skip this and don't treat it as it's own ViewController
if (viewCtrl) { if (viewCtrl) {
this.viewCtrl = viewCtrl; this._ready = new Promise(res => { this._isReady = res; });
// special overrides for the Tabs ViewController
// the Tabs ViewController does not have it's own navbar
// so find the navbar it should use within it's active Tab
viewCtrl.getNavbar = () => {
let activeTab = this.getActive();
if (activeTab && activeTab.instance) {
return activeTab.instance.getNavbar();
}
};
viewCtrl.contentRef = () => {
let activeTab = this.getActive();
if (activeTab && activeTab.instance) {
return activeTab.instance.viewCtrl.contentRef();
}
};
// a Tabs ViewController should not have a back button
// enableBack back button will later be determined
// by the active ViewController that has a navbar
viewCtrl.enableBack = () => {
return false;
};
viewCtrl.onReady = () => { viewCtrl.onReady = () => {
return this._ready; return this._ready;
}; };
} }
} }
/** /**
* @private * @private
* TODO
*/ */
addTab(tab) { add(tab) {
this._add(tab.viewCtrl); tab.id = ++_tabIds;
tab.btnId = 'tab-' + tab.id;
tab.panelId = 'tabpanel-' + tab.id;
this._tabs.push(tab);
// return true/false if it's the initial tab return (this._tabs.length === 1);
return (this.length() === 1);
} }
/** /**
@ -162,42 +139,66 @@ export class Tabs extends NavController {
* @param {Tab} tab TODO * @param {Tab} tab TODO
* @returns {TODO} TODO * @returns {TODO} TODO
*/ */
select(tab) { select(tabOrIndex) {
let enteringView = null; let selectedTab = null;
if (typeof tab === 'number') {
enteringView = this.getByIndex(tab); if (typeof tabOrIndex === 'number') {
selectedTab = this.getByIndex(tabOrIndex);
} else { } else {
enteringView = this.getByInstance(tab) selectedTab = tabOrIndex;
} }
// If we select the same tab as the active one, do some magic. if (!selectedTab || !this.app.isEnabled()) {
if(enteringView === this.getActive()) {
this._touchActive(tab);
return;
}
if (!enteringView || !enteringView.instance || !this.app.isEnabled()) {
return Promise.reject(); return Promise.reject();
} }
return new Promise(resolve => { let deselectedTab = this.getSelected();
enteringView.instance.load(() => {
let opts = {
animate: false
};
let leavingView = this.getActive() || new ViewController(); if (selectedTab === deselectedTab) {
leavingView.shouldDestroy = false; // no change
leavingView.shouldCache = true; return this._touchActive(selectedTab);
}
this.transition(enteringView, leavingView, opts, () => { console.debug('select tab', selectedTab.id);
this.highlight && this.highlight.select(tab);
this._isReady(); selectedTab.load(() => {
resolve(); this._isReady && this._isReady();
});
this._tabs.forEach(tab => {
tab.isSelected = (tab === selectedTab);
tab._views.forEach(viewCtrl => {
let navbarRef = viewCtrl.navbarRef();
if (navbarRef) {
navbarRef.nativeElement.classList[tab.isSelected ? 'remove': 'add']('deselected-tab');
}
}); });
}); });
}); this.highlight && this.highlight.select(selectedTab);
}
/**
* TODO
* @param {TODO} index TODO
* @returns {TODO} TODO
*/
getByIndex(index) {
if (index < this._tabs.length && index > -1) {
return this._tabs[index];
}
return null;
}
getSelected() {
for (let i = 0; i < this._tabs.length; i++) {
if (this._tabs[i].isSelected) {
return this._tabs[i];
}
}
return null;
} }
/** /**
@ -210,20 +211,17 @@ export class Tabs extends NavController {
if(stateLen > 1) { if(stateLen > 1) {
// Pop to the root view // Pop to the root view
tab.popToRoot(); return tab.popToRoot();
}
} }
/** return Promise.resolve();
* TODO
* @return TODO
*/
get tabs() {
return this.instances();
} }
} }
let _tabIds = -1;
/** /**
* @private * @private
* TODO * TODO
@ -232,8 +230,8 @@ export class Tabs extends NavController {
selector: '.tab-button', selector: '.tab-button',
inputs: ['tab'], inputs: ['tab'],
host: { host: {
'[attr.id]': 'btnId', '[attr.id]': 'tab.btnId',
'[attr.aria-controls]': 'panelId', '[attr.aria-controls]': 'tab.panelId',
'[attr.aria-selected]': 'tab.isSelected', '[attr.aria-selected]': 'tab.isSelected',
'[class.has-title]': 'hasTitle', '[class.has-title]': 'hasTitle',
'[class.has-icon]': 'hasIcon', '[class.has-icon]': 'hasIcon',
@ -254,10 +252,6 @@ class TabButton extends Ion {
onInit() { onInit() {
this.tab.btn = this; this.tab.btn = this;
let id = this.tab.viewCtrl.id;
this.btnId = 'tab-button-' + id;
this.panelId = 'tab-panel-' + id;
this.hasTitle = !!this.tab.tabTitle; this.hasTitle = !!this.tab.tabTitle;
this.hasIcon = !!this.tab.tabIcon; this.hasIcon = !!this.tab.tabIcon;
this.hasTitleOnly = (this.hasTitle && !this.hasIcon); this.hasTitleOnly = (this.hasTitle && !this.hasIcon);

View File

@ -104,7 +104,7 @@ class IOSTransition extends Animation {
this.add(leavingContent); this.add(leavingContent);
leavingContent leavingContent
.before.addClass(SHOW_VIEW_CSS) .before.addClass(SHOW_VIEW_CSS)
.before.setStyles({ zIndex: leavingView.index }) .before.setStyles({ zIndex: leavingView.index });
if (backDirection) { if (backDirection) {
leavingContent leavingContent