This commit is contained in:
Adam Bradley
2015-05-12 14:39:22 -05:00
parent 79c17318fb
commit 0c935944cb
9 changed files with 178 additions and 302 deletions

View File

@ -1,6 +1,6 @@
import {NgElement} from 'angular2/angular2';
import * as util from 'ionic/util';
import {Transition, ClickBlock} from 'ionic/ionic';
import {NavItem} from './nav-item'
const STAGED_STATE = 'staged';
@ -12,85 +12,19 @@ const ACTIVE_STATE = 'active';
const CACHED_STATE = 'cached';
/*
* Used by tabs and nav
*/
export class NavBase {
constructor(
element: NgElement
) {
this.domElement = element.domElement;
// this is our sane stack of items. This is synchronous and says an item
// is removed even if it's still animating out.
this._stack = [];
// The navItems array is what add/remove components from the dom.
// These arrays won't remove a component until they're
// done animating out.
this.navItems = [];
}
containsClass(Class) {
for (let i = 0; i < this._stack.length; i++) {
if (this._stack[i].Class === Class) {
return true;
}
}
return false;
constructor() {
this.items = [];
}
set initial(Class) {
if (!this.initialized) {
this.initialized = true
if (!this._init) {
this._init = true
this.push(Class);
}
}
getActive() {
for (let i = 0, ii = this.navItems.length; i < ii; i++) {
if (this.navItems[i].state === ACTIVE_STATE) {
return this.navItems[i];
}
}
return null;
}
getPrevious(item) {
if (item) {
return this._stack[ this._stack.indexOf(item) - 1 ];
}
return null;
}
getStagedEnteringItem() {
for (let i = 0, ii = this.navItems.length; i < ii; i++) {
if (this.navItems[i].state === STAGED_ENTERING_STATE) {
return this.navItems[i];
}
}
return null;
}
getStagedLeavingItem() {
for (let i = 0, ii = this.navItems.length; i < ii; i++) {
if (this.navItems[i].state === STAGED_LEAVING_STATE) {
return this.navItems[i];
}
}
return null;
}
getLeavingItems() {
let items = [];
for (let i = 0, ii = this.navItems.length; i < ii; i++) {
if (this.navItems[i].state === ACTIVELY_LEAVING_STATE || this.navItems[i].state === STAGED_LEAVING_STATE) {
items.push(this.navItems[i]);
}
}
return items;
}
push(Class: Function, params = {}, opts = {}) {
let resolve;
let promise = new Promise(res => { resolve = res; });
@ -99,7 +33,7 @@ export class NavBase {
opts.direction = opts.direction || 'forward';
// do not animate if this is the first in the stack
if (!this._stack.length) {
if (!this.items.length) {
opts.animation = 'none';
}
@ -107,14 +41,13 @@ export class NavBase {
let leavingItem = this.getActive() || {};
// create a new NavStackItem
let enteringItem = new NavStackItem(Class, params);
let enteringItem = new NavItem(this, Class, params);
// set that this item is staged (it's not ready to be animated in yet)
enteringItem.state = STAGED_STATE;
// add the item to the stack (just renders in the DOM, doesn't animate yet)
this._stack.push(enteringItem);
this.navItems.push(enteringItem);
// add the item to the stack
this.items.push(enteringItem);
// start the transition
this.transition(enteringItem, leavingItem, opts).then(() => {
@ -132,7 +65,7 @@ export class NavBase {
opts.direction = opts.direction || 'back';
// remove the last item
this._stack.pop();
this.items.pop();
// the entering item is now the new last item
let enteringItem = this.last()
@ -206,17 +139,64 @@ export class NavBase {
return promise;
}
getActive() {
for (let i = 0, ii = this.items.length; i < ii; i++) {
if (this.items[i].state === ACTIVE_STATE) {
return this.items[i];
}
}
return null;
}
getPrevious(item) {
if (item) {
return this.items[ this.items.indexOf(item) - 1 ];
}
return null;
}
getStagedEnteringItem() {
for (let i = 0, ii = this.items.length; i < ii; i++) {
if (this.items[i].state === STAGED_ENTERING_STATE) {
return this.items[i];
}
}
return null;
}
getStagedLeavingItem() {
for (let i = 0, ii = this.items.length; i < ii; i++) {
if (this.items[i].state === STAGED_LEAVING_STATE) {
return this.items[i];
}
}
return null;
}
getLeavingItems() {
let items = [];
for (let i = 0, ii = this.items.length; i < ii; i++) {
if (this.items[i].state === ACTIVELY_LEAVING_STATE || this.items[i].state === STAGED_LEAVING_STATE) {
items.push(this.items[i]);
}
}
return items;
}
last() {
return this._stack[this._stack.length - 1]
return this.items[this.items.length - 1]
}
length() {
return this._stack.length;
return this.items.length;
}
popAll() {
while (this._stack.length) {
const item = this._stack.pop()
while (this.items.length) {
const item = this.items.pop()
this._destroy(item)
}
}
@ -226,31 +206,31 @@ export class NavBase {
// then performs a normal pop.
popTo(index, opts = {}) {
// Abort if we're already here.
if (this._stack.length <= index + 1) {
if (this.items.length <= index + 1) {
return Promise.resolve();
}
// Save the current navItem, and remove all the other ones in front of our
// target nav item.
const current = this._stack.pop()
while (this._stack.length > index + 1) {
const item = this._stack.pop()
const current = this.items.pop()
while (this.items.length > index + 1) {
const item = this.items.pop()
this._destroy(item)
}
// Now put the current navItem back on the stack and run a normal pop animation.
this._stack.push(current)
this.items.push(current)
return this.pop(opts)
}
remove(index) {
const item = this._stack[index];
this._stack.splice(index, 1);
const item = this.items[index];
this.items.splice(index, 1);
this._destroy(item);
}
_destroy(navItem) {
util.array.remove(this.navItems, navItem);
util.array.remove(this.items, navItem);
}
getToolbars(pos: String) {
@ -270,27 +250,3 @@ export class NavBase {
return !!this.getPrevious(this.getActive());
}
}
class NavStackItem {
constructor(ComponentClass, params = {}) {
this.Class = ComponentClass;
this.params = params;
this.id = util.nextUid();
this._setupPromise = new Promise((resolve) => {
this._resolveSetup = resolve;
});
}
setup() {
return this._setupPromise;
}
finishSetup(navItem, componentInstance) {
this.navItem = navItem;
this.instance = componentInstance;
this._resolveSetup();
}
}

View File

@ -0,0 +1,13 @@
export class NavController {
constructor() {
}
push() {
return this.nav.push.apply(this.nav, arguments);
}
pop() {
return this.nav.pop.apply(this.nav, arguments);
}
}

View File

@ -1,33 +0,0 @@
import {
Decorator,
View,
For,
NgElement,
Parent,
Ancestor
} from 'angular2/angular2';
import {Optional} from 'angular2/di'
import {Nav} from 'ionic/ionic';
@Decorator({
selector: '[push-to]'
})
export class PushToNav {
constructor(
element: NgElement,
@Ancestor() viewportNav: Nav
) {
console.log('PUSH TO NAV', element.domElement, viewportNav);
this.navTag = element.domElement.getAttribute('push-to');
console.log('PUSH TO NAV', this.navTag);
}
}
@Decorator({
selector: '[href]'
})
export class HrefNav {
constructor(element: NgElement) {
}
}

View File

@ -1,127 +1,40 @@
import {
Component,
DynamicComponent,
Decorator,
Ancestor,
NgElement,
DynamicComponentLoader,
ElementRef,
Query,
View,
} from 'angular2/angular2';
import {ViewContainerRef} from 'angular2/src/core/compiler/view_container_ref';
import {
Injectable,
bind,
Optional,
} from 'angular2/di';
import {Toolbar} from 'ionic/components/toolbar/toolbar';
import {NavInjectable} from 'ionic/components/nav/nav';
import * as util from 'ionic/util';
/*
* NavController is the public interface for pushing, popping, etc that is injected
* by users.
* Each nav item exposes a new NavController.
*/
export class NavController {
constructor() {
// Contains data passed to this NavController's NavItem
this.params = {};
// this.navItem and this.nav are set by NavItem below.
}
addToolbar(placement: String, toolbar: Toolbar) {
this.navItem._toolbars[placement].push(toolbar);
}
removeToolbar(placement: String, toolbar: Toolbar) {
let bars = this.navItem._toolbars[placement];
bars && util.array.remove(bars, toolbar);
}
push() {
return this.nav.push.apply(this.nav, arguments);
}
pop() {
return this.nav.pop.apply(this.nav, arguments);
}
}
@Component({
selector: '.nav-item',
properties: {
_item: 'item'
},
injectables: [
// Allow all descendants to inject NavController and do nav operations.
NavController
]
})
@View({
// See below for this.
template: '<div class="nav-item-child"></div>',
directives: [NavItemDynamicComponent]
})
export class NavItem {
constructor(
// TODO for now, we can't inject tab as if it's a Nav
navInjectable: NavInjectable,
navCtrl: NavController,
element: NgElement
) {
this.domElement = element.domElement;
this._toolbars = {
top: [],
bottom: []
};
this.navCtrl = navCtrl;
navCtrl.nav = navInjectable.nav;
navCtrl.navItem = this;
constructor(nav, ComponentClass, params = {}) {
this.nav = nav;
this.Class = ComponentClass;
this.params = params;
this.id = util.nextUid();
}
set _item(data) {
var navChild = this.dynamicComponentChild;
setup() {
let resolve;
let promise = new Promise((res) => { resolve = res; });
if (this.initialized) return;
this.initialized = true;
let injector = null;
this.Class = data.Class;
util.extend(this.navCtrl.params, data.params || {});
this.nav.loader.loadIntoExistingLocation(this.Class, this.nav.itemContent.elementRef, injector)
.then((componentRef) => {
navChild._loader.loadIntoExistingLocation(this.Class, navChild._elementRef).then((instance) => {
this.instance = instance;
data.finishSetup(this, instance);
console.log('Component loadIntoExistingLocation completed')
resolve();
});
}
}
/**
* In angular 2.0.0-alpha.21, DynamicComponents are not allowed to publish injectables
* (bug, see https://github.com/angular/angular/issues/1596).
* To fix this problem, we have the `NavItem` above publish a `NavController ` injectable
* so the rest of the app has access to nav operations. Then we have a DynamicComponent
* instantiate the user's page.
* The `NavItem` class above can be turned into a DynamicComponent and this class can be removed
* once injectables are allowed on regular Components.
*/
@DynamicComponent({
selector: '.nav-item-child',
properties: {
_item: 'item'
}
})
export class NavItemDynamicComponent {
constructor(
loader: DynamicComponentLoader,
elementRef: ElementRef,
@Ancestor() navItem: NavItem
) {
this._loader = loader;
this._elementRef = elementRef;
navItem.dynamicComponentChild = this;
// let vc = new ViewContainerRef(this.nav.viewManager, this.nav.elementRef);
// debugger
// let view = vc.create(this.Class, -1, this.nav.itemContent.elementRef, injector);
return promise;
}
}

View File

@ -1,58 +1,76 @@
import {
Component,
View,
If,
For,
NgElement,
Query,
QueryList,
Descendants
} from 'angular2/angular2';
import {Parent, Ancestor} 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 {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
import {NavBase} from 'ionic/components/nav/nav-base';
import {NavItem, NavItemDynamicComponent} from 'ionic/components/nav/nav-item';
import {ToolbarContainer} from 'ionic/components/toolbar/toolbar';
/**
* We need a way for children to get ahold of the instantiated `Nav`.
* Until angular supports components adding themselves to the Injector,
* Nav is going to add an instance of a "NavInjectable" class to the injector.
* This NavInjectable will have a pointer to the Nav class on `this.nav`.
* Now descendant components (only our private ones) will have access to NavInjectable,
* and be able to get access to the Nav through `navInjectable.nav` (@see navItem)
*/
export class NavInjectable {}
@Component({
selector: 'ion-nav',
properties: {
initial: 'initial'
},
injectables: [
// Add NavInjectable to the injector for this and all descendants
NavInjectable
]
}
})
@View({
template: `
<header class="toolbar-container" [class.hide]="hideHeader">
<div *for="#toolbar of getToolbars('top')" [toolbar-create]="toolbar"></div>
<header class="toolbar-container">
<header-container></header-container>
</header>
<section class="nav-item-container">
<div class="nav-item" *for="#item of navItems" [item]="item"></div>
<content-container></content-container>
</section>
<footer class="toolbar-container" [class.hide]="hideFooter">
<div *for="#toolbar of getToolbars('bottom')" [toolbar-create]="toolbar"></div>
<footer class="toolbar-container">
<footer-container></footer-container>
</footer>
`,
directives: [NavItem, For, If, ToolbarContainer]
directives: [HeaderContainer, ContentContainer, FooterContainer]
})
export class Nav extends NavBase {
constructor(
element: NgElement,
navInjectable: NavInjectable
loader: DynamicComponentLoader,
elementRef: ElementRef
) {
super(element);
// Add the nav to navInjectable.
navInjectable.nav = this;
super();
this.loader = loader;
this.viewManager = loader._viewManager;
this.elementRef = elementRef;
}
}
@Directive({
selector: 'header-container'
})
class HeaderContainer {
constructor(@Ancestor() nav: Nav) {
nav.itemHeader = this;
}
}
@Directive({
selector: 'content-container'
})
class ContentContainer {
constructor(@Ancestor() nav: Nav, elementRef: ElementRef) {
nav.itemContent = {
elementRef: elementRef
};
}
}
@Directive({
selector: 'footer-container'
})
class FooterContainer {
constructor(@Ancestor() nav: Nav) {
nav.footerContainer = this;
}
}

View File

@ -9,11 +9,14 @@ import {FirstPage} from './pages/first-page'
@Component({ selector: 'ion-app' })
@View({
templateUrl: 'main.html'
templateUrl: 'main.html',
directives: [Nav]
})
class IonicApp {
constructor() {
console.log('IonicApp Start');
this.initial = FirstPage;
}
}

View File

@ -1,12 +1,14 @@
import {Component, View, Parent} from 'angular2/angular2'
import {NavController, Toolbar, Content} from 'ionic/ionic'
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
import {View} from 'angular2/src/core/annotations_impl/view';
import {NavController} from 'ionic/ionic'
import {SecondPage} from './second-page'
@Component()
@View({
templateUrl: 'pages/first-page.html',
directives: [Toolbar]
directives: []
})
export class FirstPage {
constructor(

View File

@ -1,12 +1,14 @@
import {Component, View, Parent} from 'angular2/angular2'
import {NavController, Toolbar, Content} from 'ionic/ionic'
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
import {View} from 'angular2/src/core/annotations_impl/view';
import {NavController} from 'ionic/ionic'
import {ThirdPage} from './third-page'
@Component()
@View({
templateUrl: 'pages/second-page.html',
directives: [Toolbar]
directives: []
})
export class SecondPage {
constructor(

View File

@ -1,11 +1,13 @@
import {Component, View, Parent} from 'angular2/angular2'
import {NavController, Toolbar, Content} from 'ionic/ionic'
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
import {View} from 'angular2/src/core/annotations_impl/view';
import {NavController} from 'ionic/ionic'
@Component()
@View({
templateUrl: 'pages/third-page.html',
directives: [Toolbar, Content]
directives: []
})
export class ThirdPage {
constructor(