From a1d0f2c69e2bfbdac3e7cc7b4cf67f25926e3665 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sun, 14 Jun 2015 23:55:48 -0500 Subject: [PATCH] tabs navbar updates --- ionic/components/nav-bar/nav-bar.js | 17 ++-- ionic/components/tabs/tab.js | 32 ++++--- ionic/components/tabs/tabs.js | 79 ++++++++-------- ionic/components/view/view-controller.js | 8 +- ionic/components/view/view-item.js | 112 +++++------------------ ionic/transitions/ios-transition.js | 30 +++--- ionic/transitions/transition.js | 49 +++++----- scripts/e2e/ionic.template.html | 22 ++--- 8 files changed, 144 insertions(+), 205 deletions(-) diff --git a/ionic/components/nav-bar/nav-bar.js b/ionic/components/nav-bar/nav-bar.js index 9607fb9997..2d714d051c 100644 --- a/ionic/components/nav-bar/nav-bar.js +++ b/ionic/components/nav-bar/nav-bar.js @@ -1,3 +1,4 @@ +import {Parent} from 'angular2/src/core/annotations_impl/visibility'; import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations'; import {View} from 'angular2/src/core/annotations_impl/view'; import {ElementRef} from 'angular2/src/core/compiler/element_ref'; @@ -38,7 +39,9 @@ import * as dom from '../../util/dom'; }) export class Navbar { constructor(item: ViewItem, elementRef: ElementRef) { - item.navbarElement(elementRef.domElement); + this.element = elementRef.domElement; + this.itemElements = []; + item.navbarView(this); } } @@ -49,9 +52,9 @@ export class Navbar { } }) class BackButton { - constructor(item: ViewItem, elementRef: ElementRef) { + constructor(@Parent() navbar: Navbar, item: ViewItem, elementRef: ElementRef) { this.item = item; - item.backButtonElement(elementRef.domElement); + navbar.backButtonElement = elementRef.domElement; } goBack(ev) { @@ -65,8 +68,8 @@ class BackButton { selector: '.navbar-title' }) export class Title { - constructor(item: ViewItem, elementRef: ElementRef) { - item.titleElement(elementRef.domElement); + constructor(@Parent() navbar: Navbar, elementRef: ElementRef) { + navbar.titleElement = elementRef.domElement; } } @@ -74,8 +77,8 @@ export class Title { selector: '.navbar-item' }) export class NavbarItem { - constructor(item: ViewItem, elementRef: ElementRef) { - item.navbarItemElements(elementRef.domElement); + constructor(@Parent() navbar: Navbar, elementRef: ElementRef) { + navbar.itemElements.push(elementRef.domElement); } } diff --git a/ionic/components/tabs/tab.js b/ionic/components/tabs/tab.js index 5df4ed97e1..c6c92b4bb4 100644 --- a/ionic/components/tabs/tab.js +++ b/ionic/components/tabs/tab.js @@ -8,8 +8,8 @@ import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_compone import {Injector} from 'angular2/di'; import {ViewController} from '../view/view-controller'; -import {Tabs} from './tabs'; import {ViewItem} from '../view/view-item'; +import {Tabs} from './tabs'; import {Content} from '../content/content'; import {IonicComponent} from '../../config/component'; @@ -32,10 +32,7 @@ import {IonicComponent} from '../../config/component'; } }) @View({ - template: ` - - - `, + template: '', directives: [TabPaneAnchor] }) export class Tab extends ViewController { @@ -53,26 +50,32 @@ export class Tab extends ViewController { super(tabs, compiler, elementRef, loader, injector); this.tabs = tabs; - // the navbar is already provided by the container of Tabs, which contains Tab - // Views which come into this Tab should not create their own navbar, but use the parent's - this.parentNavbar(true); + this.childNavbar(true); let item = this.item = new ViewItem(tabs.parent); item.setInstance(this); item.setViewElement(elementRef.domElement); - tabs.addTab(this.item); + tabs.addTab(this); + + this.navbarView = item.navbarView = () => { + let activeItem = this.getActive(); + return (activeItem && activeItem.navbarView()) || {}; + }; this.panelId = 'tab-panel-' + item.id; this.labeledBy = 'tab-button-' + item.id; } onInit() { - if ( this.item._initial ) { - this.tabs.select(this); + if (this._initialResolve) { + this.tabs.select(this).then(() => { + this._initialResolve(); + this._initialResolve = null; + }); } } - loadInitial(callback) { + load(callback) { if (!this._loaded && this.initial) { let opts = { animate: false, @@ -88,6 +91,11 @@ export class Tab extends ViewController { } } + queueInitial() { + // this Tab will be used as the initial one for the first load of Tabs + return new Promise(res => { this._initialResolve = res; }); + } + get isSelected() { return this.tabs.isActive(this.item); } diff --git a/ionic/components/tabs/tabs.js b/ionic/components/tabs/tabs.js index b05b1cdc2d..4be21ae67f 100644 --- a/ionic/components/tabs/tabs.js +++ b/ionic/components/tabs/tabs.js @@ -47,15 +47,26 @@ import {Config} from '../../config/component'; export class Tabs extends ViewController { constructor( - @Optional() viewCtrl: ViewController, - @Optional() item: ViewItem, + @Optional() parentViewCtrl: ViewController, + @Optional() viewItem: ViewItem, compiler: Compiler, elementRef: ElementRef, loader: DynamicComponentLoader, injector: Injector ) { - super(viewCtrl, compiler, elementRef, loader, injector); - this.item = item; + super(parentViewCtrl, compiler, elementRef, loader, injector); + this.item = viewItem; + + this.item.navbarView = () => { + let activeTab = this.getActive(); + if (activeTab && activeTab.instance) { + return activeTab.instance.navbarView(); + } + return {}; + }; + + this.childNavbar(true); + Config(this, { 'tabBarPlacement': { 'default': 'bottom', @@ -65,12 +76,15 @@ export class Tabs extends ViewController { }); } - addTab(tabItem) { - this.add(tabItem); + addTab(tab) { + // tab.item refers to the ViewItem of the individual Tab being added to Tabs (ViewController) + // this.item refers to the ViewItem instsance on Tabs + this.add(tab.item); if (this.length() === 1) { - this.item && this.item.waitForResolve(); - tabItem._initial = true; + // this was the first tab added, queue this one to be loaded and selected + let promise = tab.queueInitial(); + this.item && this.item.addPromise(promise); } } @@ -83,28 +97,25 @@ export class Tabs extends ViewController { } if (!enteringItem || !enteringItem.instance || this.isTransitioning()) { - return; + return Promise.reject(); } - enteringItem.instance.loadInitial(() => { - let opts = { - animate: false - }; + return new Promise(resolve => { + enteringItem.instance.load(() => { + let opts = { + animate: false + }; - let leavingItem = this.getActive() || new ViewItem(); - leavingItem.shouldDestroy = false; - leavingItem.shouldCache = true; - leavingItem.willCache(); + let leavingItem = this.getActive() || new ViewItem(); + leavingItem.shouldDestroy = false; + leavingItem.shouldCache = true; + leavingItem.willCache(); - // set the Tab navbarView from the active view in the tab - enteringItem.navbarView = (enteringItem.instance.getActive() || {}).navbarView; - if (leavingItem.instance) { - leavingItem.navbarView = (leavingItem.instance.getActive() || {}).navbarView; - } - - this.transition(enteringItem, leavingItem, opts, () => { - this.item && this.item.resolve(); + this.transition(enteringItem, leavingItem, opts, () => { + resolve(); + }); }); + }); } @@ -113,21 +124,3 @@ export class Tabs extends ViewController { } } -// new IonicComponent(Tabs, { -// properties: { -// tabBarPlacement: { -// defaults: { -// ios: 'bottom', -// android: 'top', -// core: 'bottom' -// } -// }, -// tabBarIcons: { -// defaults: { -// ios: 'top', -// android: 'top', -// core: 'top' -// } -// } -// } -// }); diff --git a/ionic/components/view/view-controller.js b/ionic/components/view/view-controller.js index 6250e2a653..5e07b9e35b 100644 --- a/ionic/components/view/view-controller.js +++ b/ionic/components/view/view-controller.js @@ -60,7 +60,7 @@ export class ViewController { leavingItem.shouldCache = true; leavingItem.willCache(); - // create a new NavStackItem + // create a new ViewItem let enteringItem = new ViewItem(this, ComponentClass, params); // add the item to the stack @@ -387,11 +387,11 @@ export class ViewController { } } - parentNavbar() { + childNavbar() { if (arguments.length) { - this._parentNavbar = arguments[0]; + this._childNavbar = arguments[0]; } - return this._parentNavbar; + return this._childNavbar; } add(item) { diff --git a/ionic/components/view/view-item.js b/ionic/components/view/view-item.js index ad6e7418aa..42d8c1f3ce 100644 --- a/ionic/components/view/view-item.js +++ b/ionic/components/view/view-item.js @@ -22,6 +22,7 @@ export class ViewItem { this.protos = {}; this._nbItms = []; + this._promises = []; } addProtoViewRef(name, protoViewRef) { @@ -44,7 +45,7 @@ export class ViewItem { // figure out the sturcture of this Component // does it have a navbar? Is it tabs? Should it not have a navbar or any toolbars? - let itemStructure = this.sturcture = this.getProtoViewStructure(componentProtoViewRef); + let itemStructure = this.sturcture = this.inspectStructure(componentProtoViewRef); // get the appropriate Pane which this ViewItem will fit into viewCtrl.panes.get(itemStructure, pane => { @@ -100,25 +101,25 @@ export class ViewItem { // this item has finished loading this.loaded(); - // all done, fire the callback - if (this._wait) { - this._waitCallback = callback; - - } else { + // fire callback when all child promises have been resolved + Promise.all(this._promises).then(() => { callback(); - } + this._promises = []; + }); }); }); } + addPromise(childPromise) { + this._promises.push(childPromise); + } - getProtoViewStructure(componentProtoViewRef) { + inspectStructure(componentProtoViewRef) { let navbar = false; let tabs = false; - let toolbars = []; - let key = ''; + let key = '_'; componentProtoViewRef._protoView.elementBinders.forEach(rootElementBinder => { if (!rootElementBinder.componentDirective || !rootElementBinder.nestedProtoView) return; @@ -137,33 +138,20 @@ export class ViewItem { }); }); - if (this.viewCtrl.parentNavbar()) { + if (this.viewCtrl.childNavbar()) { navbar = false; } if (navbar) key += 'n' if (tabs) key += 't' - key += 'b' + toolbars.length; return { navbar, tabs, - toolbars, key }; } - waitForResolve() { - this._wait = true; - } - - resolve() { - if (this._wait) { - this._waitCallback && this._waitCallback(); - this._wait = this._waitCallback = null; - } - } - setInstance(instance) { this.instance = instance; } @@ -196,81 +184,31 @@ export class ViewItem { return this.viewEle; } - navbarElement() { + navbarView() { if (arguments.length) { - this._nbEle = arguments[0]; - return; - } - if (this._nbEle) { - // this ViewItem already has an assigned navbarElement - return this._nbEle; + this._nbView = arguments[0]; + + } else if (this._nbView) { + return this._nbView; } - let instance = this.instance; - if (instance && instance.parentNavbar && instance.parentNavbar()) { - // this View doesn't actually have it's own navbar - // for example, Tab does not have a navbar, but Tabs does - // so if this is true, then get the active ViewItem inside this instance - let activeChildViewItem = instance.getActive(); - if (activeChildViewItem) { - return activeChildViewItem.navbarElement(); - } - } + return {}; + } + + navbarElement() { + return this.navbarView().element; } titleElement() { - if (arguments.length) { - this._ttEle = arguments[0]; - return; - } - if (this._ttEle) { - return this._ttEle; - } - - let instance = this.instance; - if (instance && instance.parentNavbar && instance.parentNavbar()) { - let activeChildViewItem = instance.getActive(); - if (activeChildViewItem) { - return activeChildViewItem.titleElement(); - } - } + return this.navbarView().titleElement; } backButtonElement() { - if (arguments.length) { - this._bbEle = arguments[0]; - return; - } - if (this._bbEle) { - return this._bbEle; - } - - let instance = this.instance; - if (instance && instance.parentNavbar && instance.parentNavbar()) { - let activeChildViewItem = instance.getActive(); - if (activeChildViewItem) { - return activeChildViewItem.backButtonElement(); - } - } + return this.navbarView().backButtonElement; } navbarItemElements() { - if (arguments.length) { - this._nbItms.push(arguments[0]); - return; - } - if (this._nbItms) { - return this._nbItms; - } - - - let instance = this.instance; - if (instance && instance.parentNavbar && instance.parentNavbar()) { - let activeChildViewItem = instance.getActive(); - if (activeChildViewItem) { - return activeChildViewItem.navbarItemElements(); - } - } + return this.navbarView().itemElements; } diff --git a/ionic/transitions/ios-transition.js b/ionic/transitions/ios-transition.js index 2472d797bd..f3219db822 100644 --- a/ionic/transitions/ios-transition.js +++ b/ionic/transitions/ios-transition.js @@ -19,63 +19,61 @@ class IOSTransition extends Transition { constructor(nav, opts) { super(nav, opts); - const self = this; - // global duration and easing for all child animations - self.duration(DURATION); - self.easing(EASING); + this.duration(DURATION); + this.easing(EASING); // entering item moves to center - self.enteringView + this.enteringView .to(TRANSLATEX, CENTER) .to(OPACITY, 1); - self.enteringTitle + this.enteringTitle .fadeIn() .to(TRANSLATEX, CENTER); // leaving view moves off screen - self.leavingView + this.leavingView .from(TRANSLATEX, CENTER) .from(OPACITY, 1); - self.leavingTitle + this.leavingTitle .from(TRANSLATEX, CENTER) .from(OPACITY, 1); // set properties depending on direction if (opts.direction === 'back') { // back direction - self.enteringView + this.enteringView .from(TRANSLATEX, OFF_LEFT) .from(OPACITY, OFF_OPACITY) .to(OPACITY, 1); - self.enteringTitle + this.enteringTitle .from(TRANSLATEX, OFF_LEFT); - self.leavingView + this.leavingView .to(TRANSLATEX, OFF_RIGHT) .to(OPACITY, 1); - self.leavingTitle + this.leavingTitle .to(TRANSLATEX, OFF_RIGHT) .to(OPACITY, 0); } else { // forward direction - self.enteringView + this.enteringView .from(TRANSLATEX, '99%') .from(OPACITY, 1); - self.enteringTitle + this.enteringTitle .from(TRANSLATEX, '97%'); - self.leavingView + this.leavingView .to(TRANSLATEX, OFF_LEFT) .to(OPACITY, OFF_OPACITY); - self.leavingTitle + this.leavingTitle .to(TRANSLATEX, OFF_LEFT) .to(OPACITY, 0); } diff --git a/ionic/transitions/transition.js b/ionic/transitions/transition.js index e6c98e1e90..1c71c5b15a 100644 --- a/ionic/transitions/transition.js +++ b/ionic/transitions/transition.js @@ -12,60 +12,59 @@ export class Transition extends Animation { constructor(nav, opts) { super(); - const self = this; - // get the entering and leaving items - let enteringItem = self.entering = nav.getStagedEnteringItem(); - let leavingItem = self.leaving = nav.getStagedLeavingItem(); + let enteringItem = this.entering = nav.getStagedEnteringItem(); + let leavingItem = this.leaving = nav.getStagedLeavingItem(); // create animation for the entering item's "ion-view" element - self.enteringView = new Animation(enteringItem.viewElement()); - self.enteringView.before.addClass(SHOW_VIEW_CSS); - self.add(self.enteringView); + this.enteringView = new Animation(enteringItem.viewElement()); + this.enteringView.before.addClass(SHOW_VIEW_CSS); + this.add(this.enteringView); if (opts.navbar !== false) { - let enteringNavbar = self.enteringNavbar = new Animation(enteringItem.navbarElement()); + + let enteringNavbar = this.enteringNavbar = new Animation(enteringItem.navbarElement()); enteringNavbar.before.addClass(SHOW_NAVBAR_CSS); if (enteringItem.enableBack) { // only animate in the back button if the entering view has it enabled - let enteringBackButton = self.enteringBackButton = new Animation(enteringItem.backButtonElement()); + let enteringBackButton = this.enteringBackButton = new Animation(enteringItem.backButtonElement()); enteringBackButton .before.addClass(SHOW_BACK_BUTTON) .fromTo('opacity', 0.02, 1) enteringNavbar.add(enteringBackButton); } - self.enteringTitle = new Animation(enteringItem.titleElement()); - enteringNavbar.add(self.enteringTitle); - self.add(enteringNavbar); + this.enteringTitle = new Animation(enteringItem.titleElement()); + enteringNavbar.add(this.enteringTitle); + this.add(enteringNavbar); - self.enteringNavbarItems = new Animation(enteringItem.navbarItemElements()) - self.enteringNavbarItems.fromTo('opacity', 0.02, 1) - enteringNavbar.add(self.enteringNavbarItems); + this.enteringNavbarItems = new Animation(enteringItem.navbarItemElements()) + this.enteringNavbarItems.fromTo('opacity', 0.02, 1) + enteringNavbar.add(this.enteringNavbarItems); } if (leavingItem) { - self.leavingView = new Animation(leavingItem.viewElement()); - self.leavingView.after.removeClass(SHOW_VIEW_CSS); + this.leavingView = new Animation(leavingItem.viewElement()); + this.leavingView.after.removeClass(SHOW_VIEW_CSS); - let leavingNavbar = self.leavingNavbar = new Animation(leavingItem.navbarElement()); + let leavingNavbar = this.leavingNavbar = new Animation(leavingItem.navbarElement()); leavingNavbar.after.removeClass(SHOW_NAVBAR_CSS); - let leavingBackButton = self.leavingBackButton = new Animation(leavingItem.backButtonElement()); + let leavingBackButton = this.leavingBackButton = new Animation(leavingItem.backButtonElement()); leavingBackButton .after.removeClass(SHOW_BACK_BUTTON) .fadeOut(); leavingNavbar.add(leavingBackButton); - self.leavingTitle = new Animation(leavingItem.titleElement()); - leavingNavbar.add(self.leavingTitle); + this.leavingTitle = new Animation(leavingItem.titleElement()); + leavingNavbar.add(this.leavingTitle); - self.leavingNavbarItems = new Animation(leavingItem.navbarItemElements()) - self.leavingNavbarItems.fadeOut(); - leavingNavbar.add(self.leavingNavbarItems); + this.leavingNavbarItems = new Animation(leavingItem.navbarItemElements()) + this.leavingNavbarItems.fadeOut(); + leavingNavbar.add(this.leavingNavbarItems); - self.add(self.leavingView, leavingNavbar); + this.add(this.leavingView, leavingNavbar); } } diff --git a/scripts/e2e/ionic.template.html b/scripts/e2e/ionic.template.html index ead7d6449d..7a67ef4554 100644 --- a/scripts/e2e/ionic.template.html +++ b/scripts/e2e/ionic.template.html @@ -5,8 +5,7 @@ - - +