tabs navbar updates

This commit is contained in:
Adam Bradley
2015-06-14 23:55:48 -05:00
parent 8676deb325
commit a1d0f2c69e
8 changed files with 144 additions and 205 deletions

View File

@ -1,3 +1,4 @@
import {Parent} from 'angular2/src/core/annotations_impl/visibility';
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations'; import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
import {View} from 'angular2/src/core/annotations_impl/view'; import {View} from 'angular2/src/core/annotations_impl/view';
import {ElementRef} from 'angular2/src/core/compiler/element_ref'; import {ElementRef} from 'angular2/src/core/compiler/element_ref';
@ -38,7 +39,9 @@ import * as dom from '../../util/dom';
}) })
export class Navbar { export class Navbar {
constructor(item: ViewItem, elementRef: ElementRef) { 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 { class BackButton {
constructor(item: ViewItem, elementRef: ElementRef) { constructor(@Parent() navbar: Navbar, item: ViewItem, elementRef: ElementRef) {
this.item = item; this.item = item;
item.backButtonElement(elementRef.domElement); navbar.backButtonElement = elementRef.domElement;
} }
goBack(ev) { goBack(ev) {
@ -65,8 +68,8 @@ class BackButton {
selector: '.navbar-title' selector: '.navbar-title'
}) })
export class Title { export class Title {
constructor(item: ViewItem, elementRef: ElementRef) { constructor(@Parent() navbar: Navbar, elementRef: ElementRef) {
item.titleElement(elementRef.domElement); navbar.titleElement = elementRef.domElement;
} }
} }
@ -74,8 +77,8 @@ export class Title {
selector: '.navbar-item' selector: '.navbar-item'
}) })
export class NavbarItem { export class NavbarItem {
constructor(item: ViewItem, elementRef: ElementRef) { constructor(@Parent() navbar: Navbar, elementRef: ElementRef) {
item.navbarItemElements(elementRef.domElement); navbar.itemElements.push(elementRef.domElement);
} }
} }

View File

@ -8,8 +8,8 @@ import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_compone
import {Injector} from 'angular2/di'; import {Injector} from 'angular2/di';
import {ViewController} from '../view/view-controller'; import {ViewController} from '../view/view-controller';
import {Tabs} from './tabs';
import {ViewItem} from '../view/view-item'; import {ViewItem} from '../view/view-item';
import {Tabs} from './tabs';
import {Content} from '../content/content'; import {Content} from '../content/content';
import {IonicComponent} from '../../config/component'; import {IonicComponent} from '../../config/component';
@ -32,10 +32,7 @@ import {IonicComponent} from '../../config/component';
} }
}) })
@View({ @View({
template: ` template: '<template pane-anchor></template><content></content>',
<template pane-anchor></template>
<content></content>
`,
directives: [TabPaneAnchor] directives: [TabPaneAnchor]
}) })
export class Tab extends ViewController { export class Tab extends ViewController {
@ -53,26 +50,32 @@ export class Tab extends ViewController {
super(tabs, compiler, elementRef, loader, injector); super(tabs, compiler, elementRef, loader, injector);
this.tabs = tabs; this.tabs = tabs;
// the navbar is already provided by the container of Tabs, which contains Tab this.childNavbar(true);
// Views which come into this Tab should not create their own navbar, but use the parent's
this.parentNavbar(true);
let item = this.item = new ViewItem(tabs.parent); let item = this.item = new ViewItem(tabs.parent);
item.setInstance(this); item.setInstance(this);
item.setViewElement(elementRef.domElement); 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.panelId = 'tab-panel-' + item.id;
this.labeledBy = 'tab-button-' + item.id; this.labeledBy = 'tab-button-' + item.id;
} }
onInit() { onInit() {
if ( this.item._initial ) { if (this._initialResolve) {
this.tabs.select(this); this.tabs.select(this).then(() => {
this._initialResolve();
this._initialResolve = null;
});
} }
} }
loadInitial(callback) { load(callback) {
if (!this._loaded && this.initial) { if (!this._loaded && this.initial) {
let opts = { let opts = {
animate: false, 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() { get isSelected() {
return this.tabs.isActive(this.item); return this.tabs.isActive(this.item);
} }

View File

@ -47,15 +47,26 @@ import {Config} from '../../config/component';
export class Tabs extends ViewController { export class Tabs extends ViewController {
constructor( constructor(
@Optional() viewCtrl: ViewController, @Optional() parentViewCtrl: ViewController,
@Optional() item: ViewItem, @Optional() viewItem: ViewItem,
compiler: Compiler, compiler: Compiler,
elementRef: ElementRef, elementRef: ElementRef,
loader: DynamicComponentLoader, loader: DynamicComponentLoader,
injector: Injector injector: Injector
) { ) {
super(viewCtrl, compiler, elementRef, loader, injector); super(parentViewCtrl, compiler, elementRef, loader, injector);
this.item = item; this.item = viewItem;
this.item.navbarView = () => {
let activeTab = this.getActive();
if (activeTab && activeTab.instance) {
return activeTab.instance.navbarView();
}
return {};
};
this.childNavbar(true);
Config(this, { Config(this, {
'tabBarPlacement': { 'tabBarPlacement': {
'default': 'bottom', 'default': 'bottom',
@ -65,12 +76,15 @@ export class Tabs extends ViewController {
}); });
} }
addTab(tabItem) { addTab(tab) {
this.add(tabItem); // 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) { if (this.length() === 1) {
this.item && this.item.waitForResolve(); // this was the first tab added, queue this one to be loaded and selected
tabItem._initial = true; 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()) { if (!enteringItem || !enteringItem.instance || this.isTransitioning()) {
return; return Promise.reject();
} }
enteringItem.instance.loadInitial(() => { return new Promise(resolve => {
let opts = { enteringItem.instance.load(() => {
animate: false let opts = {
}; animate: false
};
let leavingItem = this.getActive() || new ViewItem(); let leavingItem = this.getActive() || new ViewItem();
leavingItem.shouldDestroy = false; leavingItem.shouldDestroy = false;
leavingItem.shouldCache = true; leavingItem.shouldCache = true;
leavingItem.willCache(); leavingItem.willCache();
// set the Tab navbarView from the active view in the tab this.transition(enteringItem, leavingItem, opts, () => {
enteringItem.navbarView = (enteringItem.instance.getActive() || {}).navbarView; resolve();
if (leavingItem.instance) { });
leavingItem.navbarView = (leavingItem.instance.getActive() || {}).navbarView;
}
this.transition(enteringItem, leavingItem, opts, () => {
this.item && this.item.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'
// }
// }
// }
// });

View File

@ -60,7 +60,7 @@ export class ViewController {
leavingItem.shouldCache = true; leavingItem.shouldCache = true;
leavingItem.willCache(); leavingItem.willCache();
// create a new NavStackItem // create a new ViewItem
let enteringItem = new ViewItem(this, ComponentClass, params); let enteringItem = new ViewItem(this, ComponentClass, params);
// add the item to the stack // add the item to the stack
@ -387,11 +387,11 @@ export class ViewController {
} }
} }
parentNavbar() { childNavbar() {
if (arguments.length) { if (arguments.length) {
this._parentNavbar = arguments[0]; this._childNavbar = arguments[0];
} }
return this._parentNavbar; return this._childNavbar;
} }
add(item) { add(item) {

View File

@ -22,6 +22,7 @@ export class ViewItem {
this.protos = {}; this.protos = {};
this._nbItms = []; this._nbItms = [];
this._promises = [];
} }
addProtoViewRef(name, protoViewRef) { addProtoViewRef(name, protoViewRef) {
@ -44,7 +45,7 @@ export class ViewItem {
// figure out the sturcture of this Component // figure out the sturcture of this Component
// does it have a navbar? Is it tabs? Should it not have a navbar or any toolbars? // 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 // get the appropriate Pane which this ViewItem will fit into
viewCtrl.panes.get(itemStructure, pane => { viewCtrl.panes.get(itemStructure, pane => {
@ -100,25 +101,25 @@ export class ViewItem {
// this item has finished loading // this item has finished loading
this.loaded(); this.loaded();
// all done, fire the callback // fire callback when all child promises have been resolved
if (this._wait) { Promise.all(this._promises).then(() => {
this._waitCallback = callback;
} else {
callback(); callback();
} this._promises = [];
});
}); });
}); });
} }
addPromise(childPromise) {
this._promises.push(childPromise);
}
getProtoViewStructure(componentProtoViewRef) { inspectStructure(componentProtoViewRef) {
let navbar = false; let navbar = false;
let tabs = false; let tabs = false;
let toolbars = []; let key = '_';
let key = '';
componentProtoViewRef._protoView.elementBinders.forEach(rootElementBinder => { componentProtoViewRef._protoView.elementBinders.forEach(rootElementBinder => {
if (!rootElementBinder.componentDirective || !rootElementBinder.nestedProtoView) return; if (!rootElementBinder.componentDirective || !rootElementBinder.nestedProtoView) return;
@ -137,33 +138,20 @@ export class ViewItem {
}); });
}); });
if (this.viewCtrl.parentNavbar()) { if (this.viewCtrl.childNavbar()) {
navbar = false; navbar = false;
} }
if (navbar) key += 'n' if (navbar) key += 'n'
if (tabs) key += 't' if (tabs) key += 't'
key += 'b' + toolbars.length;
return { return {
navbar, navbar,
tabs, tabs,
toolbars,
key key
}; };
} }
waitForResolve() {
this._wait = true;
}
resolve() {
if (this._wait) {
this._waitCallback && this._waitCallback();
this._wait = this._waitCallback = null;
}
}
setInstance(instance) { setInstance(instance) {
this.instance = instance; this.instance = instance;
} }
@ -196,81 +184,31 @@ export class ViewItem {
return this.viewEle; return this.viewEle;
} }
navbarElement() { navbarView() {
if (arguments.length) { if (arguments.length) {
this._nbEle = arguments[0]; this._nbView = arguments[0];
return;
} } else if (this._nbView) {
if (this._nbEle) { return this._nbView;
// this ViewItem already has an assigned navbarElement
return this._nbEle;
} }
let instance = this.instance; return {};
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 navbarElement() {
// so if this is true, then get the active ViewItem inside this instance return this.navbarView().element;
let activeChildViewItem = instance.getActive();
if (activeChildViewItem) {
return activeChildViewItem.navbarElement();
}
}
} }
titleElement() { titleElement() {
if (arguments.length) { return this.navbarView().titleElement;
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();
}
}
} }
backButtonElement() { backButtonElement() {
if (arguments.length) { return this.navbarView().backButtonElement;
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();
}
}
} }
navbarItemElements() { navbarItemElements() {
if (arguments.length) { return this.navbarView().itemElements;
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();
}
}
} }

View File

@ -19,63 +19,61 @@ class IOSTransition extends Transition {
constructor(nav, opts) { constructor(nav, opts) {
super(nav, opts); super(nav, opts);
const self = this;
// global duration and easing for all child animations // global duration and easing for all child animations
self.duration(DURATION); this.duration(DURATION);
self.easing(EASING); this.easing(EASING);
// entering item moves to center // entering item moves to center
self.enteringView this.enteringView
.to(TRANSLATEX, CENTER) .to(TRANSLATEX, CENTER)
.to(OPACITY, 1); .to(OPACITY, 1);
self.enteringTitle this.enteringTitle
.fadeIn() .fadeIn()
.to(TRANSLATEX, CENTER); .to(TRANSLATEX, CENTER);
// leaving view moves off screen // leaving view moves off screen
self.leavingView this.leavingView
.from(TRANSLATEX, CENTER) .from(TRANSLATEX, CENTER)
.from(OPACITY, 1); .from(OPACITY, 1);
self.leavingTitle this.leavingTitle
.from(TRANSLATEX, CENTER) .from(TRANSLATEX, CENTER)
.from(OPACITY, 1); .from(OPACITY, 1);
// set properties depending on direction // set properties depending on direction
if (opts.direction === 'back') { if (opts.direction === 'back') {
// back direction // back direction
self.enteringView this.enteringView
.from(TRANSLATEX, OFF_LEFT) .from(TRANSLATEX, OFF_LEFT)
.from(OPACITY, OFF_OPACITY) .from(OPACITY, OFF_OPACITY)
.to(OPACITY, 1); .to(OPACITY, 1);
self.enteringTitle this.enteringTitle
.from(TRANSLATEX, OFF_LEFT); .from(TRANSLATEX, OFF_LEFT);
self.leavingView this.leavingView
.to(TRANSLATEX, OFF_RIGHT) .to(TRANSLATEX, OFF_RIGHT)
.to(OPACITY, 1); .to(OPACITY, 1);
self.leavingTitle this.leavingTitle
.to(TRANSLATEX, OFF_RIGHT) .to(TRANSLATEX, OFF_RIGHT)
.to(OPACITY, 0); .to(OPACITY, 0);
} else { } else {
// forward direction // forward direction
self.enteringView this.enteringView
.from(TRANSLATEX, '99%') .from(TRANSLATEX, '99%')
.from(OPACITY, 1); .from(OPACITY, 1);
self.enteringTitle this.enteringTitle
.from(TRANSLATEX, '97%'); .from(TRANSLATEX, '97%');
self.leavingView this.leavingView
.to(TRANSLATEX, OFF_LEFT) .to(TRANSLATEX, OFF_LEFT)
.to(OPACITY, OFF_OPACITY); .to(OPACITY, OFF_OPACITY);
self.leavingTitle this.leavingTitle
.to(TRANSLATEX, OFF_LEFT) .to(TRANSLATEX, OFF_LEFT)
.to(OPACITY, 0); .to(OPACITY, 0);
} }

View File

@ -12,60 +12,59 @@ export class Transition extends Animation {
constructor(nav, opts) { constructor(nav, opts) {
super(); super();
const self = this;
// get the entering and leaving items // get the entering and leaving items
let enteringItem = self.entering = nav.getStagedEnteringItem(); let enteringItem = this.entering = nav.getStagedEnteringItem();
let leavingItem = self.leaving = nav.getStagedLeavingItem(); let leavingItem = this.leaving = nav.getStagedLeavingItem();
// create animation for the entering item's "ion-view" element // create animation for the entering item's "ion-view" element
self.enteringView = new Animation(enteringItem.viewElement()); this.enteringView = new Animation(enteringItem.viewElement());
self.enteringView.before.addClass(SHOW_VIEW_CSS); this.enteringView.before.addClass(SHOW_VIEW_CSS);
self.add(self.enteringView); this.add(this.enteringView);
if (opts.navbar !== false) { 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); enteringNavbar.before.addClass(SHOW_NAVBAR_CSS);
if (enteringItem.enableBack) { if (enteringItem.enableBack) {
// only animate in the back button if the entering view has it enabled // 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 enteringBackButton
.before.addClass(SHOW_BACK_BUTTON) .before.addClass(SHOW_BACK_BUTTON)
.fromTo('opacity', 0.02, 1) .fromTo('opacity', 0.02, 1)
enteringNavbar.add(enteringBackButton); enteringNavbar.add(enteringBackButton);
} }
self.enteringTitle = new Animation(enteringItem.titleElement()); this.enteringTitle = new Animation(enteringItem.titleElement());
enteringNavbar.add(self.enteringTitle); enteringNavbar.add(this.enteringTitle);
self.add(enteringNavbar); this.add(enteringNavbar);
self.enteringNavbarItems = new Animation(enteringItem.navbarItemElements()) this.enteringNavbarItems = new Animation(enteringItem.navbarItemElements())
self.enteringNavbarItems.fromTo('opacity', 0.02, 1) this.enteringNavbarItems.fromTo('opacity', 0.02, 1)
enteringNavbar.add(self.enteringNavbarItems); enteringNavbar.add(this.enteringNavbarItems);
} }
if (leavingItem) { if (leavingItem) {
self.leavingView = new Animation(leavingItem.viewElement()); this.leavingView = new Animation(leavingItem.viewElement());
self.leavingView.after.removeClass(SHOW_VIEW_CSS); 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); leavingNavbar.after.removeClass(SHOW_NAVBAR_CSS);
let leavingBackButton = self.leavingBackButton = new Animation(leavingItem.backButtonElement()); let leavingBackButton = this.leavingBackButton = new Animation(leavingItem.backButtonElement());
leavingBackButton leavingBackButton
.after.removeClass(SHOW_BACK_BUTTON) .after.removeClass(SHOW_BACK_BUTTON)
.fadeOut(); .fadeOut();
leavingNavbar.add(leavingBackButton); leavingNavbar.add(leavingBackButton);
self.leavingTitle = new Animation(leavingItem.titleElement()); this.leavingTitle = new Animation(leavingItem.titleElement());
leavingNavbar.add(self.leavingTitle); leavingNavbar.add(this.leavingTitle);
self.leavingNavbarItems = new Animation(leavingItem.navbarItemElements()) this.leavingNavbarItems = new Animation(leavingItem.navbarItemElements())
self.leavingNavbarItems.fadeOut(); this.leavingNavbarItems.fadeOut();
leavingNavbar.add(self.leavingNavbarItems); leavingNavbar.add(this.leavingNavbarItems);
self.add(self.leavingView, leavingNavbar); this.add(this.leavingView, leavingNavbar);
} }
} }

View File

@ -5,8 +5,7 @@
<!-- https://www.chromium.org/developers/design-documents/chromium-graphics/how-to-get-gpu-rasterization --> <!-- https://www.chromium.org/developers/design-documents/chromium-graphics/how-to-get-gpu-rasterization -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<link rel="stylesheet" href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css"> <link href="/dist/css/ionic.css" rel="stylesheet">
<link href="/../../../../dist/css/ionic.css" rel="stylesheet">
<style> <style>
/* hack to create tall scrollable areas for testing using <f></f> */ /* hack to create tall scrollable areas for testing using <f></f> */
@ -41,18 +40,19 @@
</ion-app> </ion-app>
<!-- web animations polyfill for non-chrome browsers --> <!-- web animations polyfill for non-chrome browsers -->
<script src="/../../../../dist/vendor/web-animations-js/web-animations.min.js"></script> <script src="/dist/vendor/web-animations-js/web-animations.min.js"></script>
<!--<script src="extension-register.js" type="text/javascript"></script> <!--<script src="extension-register.js" type="text/javascript"></script>
<script src="extension-cjs.js" type="text/javascript"></script> <script src="extension-cjs.js" type="text/javascript"></script>
<script src="runtime_paths.js" type="text/javascript"></script> --> <script src="runtime_paths.js" type="text/javascript"></script> -->
<script src="/../../../../scripts/resources/traceur-runtime.js" type="text/javascript"></script> <script src="/scripts/resources/traceur-runtime.js" type="text/javascript"></script>
<script src="/../../../../scripts/resources/Reflect.js" type="text/javascript"></script> <script src="/scripts/resources/Reflect.js" type="text/javascript"></script>
<script src="/../../../../scripts/resources/system.src.js"></script> <script src="/scripts/resources/system.src.js"></script>
<script src="/../../../../config.js"></script> <script src="/config.js"></script>
<script src="/../../../../dist/js/dependencies.js"></script> <script src="/dist/js/dependencies.js"></script>
<script src="/../../../../dist/js/ionic.bundle.js"></script> <script src="/dist/js/ionic.bundle.js"></script>
<script src="/../../../../scripts/resources/zone-microtask.js" type="text/javascript"></script> <script src="/scripts/resources/zone-microtask.js" type="text/javascript"></script>
<script src="/../../../../scripts/resources/long-stack-trace-zone.js" type="text/javascript"></script> <script src="/scripts/resources/long-stack-trace-zone.js" type="text/javascript"></script>
<!-- <script src="../../../../bundle.js"></script> --> <!-- <script src="../../../../bundle.js"></script> -->
<script type="text/javascript">System.import(window.location.pathname.substring(1) + 'index').then(function(m) { m.main(); }, console.error.bind(console))</script> <script type="text/javascript">System.import(window.location.pathname.substring(1) + 'index').then(function(m) { m.main(); }, console.error.bind(console))</script>
</body> </body>