diff --git a/ionic/components.js b/ionic/components.js
index 86654b3ca5..3d6ce00826 100644
--- a/ionic/components.js
+++ b/ionic/components.js
@@ -11,8 +11,9 @@ export * from 'ionic/components/form/form'
export * from 'ionic/components/input/input'
export * from 'ionic/components/layout/layout'
export * from 'ionic/components/list/list'
-export * from 'ionic/components/nav-pane/nav-pane'
+export * from 'ionic/components/nav-pane/nav-pane' //TODO remove
export * from 'ionic/components/nav/nav'
+export * from 'ionic/components/nav/nav-item'
export * from 'ionic/components/radio/radio-button'
export * from 'ionic/components/radio/radio-group'
export * from 'ionic/components/search-bar/search-bar'
diff --git a/ionic/components/app/structure.scss b/ionic/components/app/structure.scss
index 08e3b54987..8d11427e63 100644
--- a/ionic/components/app/structure.scss
+++ b/ionic/components/app/structure.scss
@@ -43,7 +43,7 @@ html {
height: 100%;
}
- .nav-pane-container {
+ .nav-item-container {
// container of many .nav-pane's, each one containing one view
position: relative;
@include flex(1);
diff --git a/ionic/components/nav/nav-controller.js b/ionic/components/nav/nav-controller.js
new file mode 100644
index 0000000000..f1ba702efc
--- /dev/null
+++ b/ionic/components/nav/nav-controller.js
@@ -0,0 +1,199 @@
+import {NgElement} from 'angular2/angular2';
+import * as util from 'ionic/util';
+
+export class NavControllerBase {
+ constructor(
+ element: NgElement
+ ) {
+ this.domElement = element.domElement
+ this.domElement.classList.add('nav')
+
+ // 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 = []
+
+ // _ngForLoopArray is what adds/removes components from the dom. It won't
+ // remove a component until it's done animating out.
+ this._ngForLoopArray = []
+ }
+
+ containsClass(Class) {
+ for (let i = 0; i < this._stack.length; i++) {
+ if (this._stack[i].Class === Class) {
+ return true
+ }
+ }
+ return false
+ }
+
+ set initial(Class) {
+ if (!this.initialized) {
+ this.initialized = true
+ this.push(Class)
+ }
+ }
+ //TODO let the view handle enter/leave so splitview can override
+
+ /**
+ * Push a new view into the history stack.
+ *
+ * @param view the new view
+ * @param shouldAnimate whether to animate
+ */
+ // TODO don't push same component twice if one is already pushing
+ // TODO only animate if state hasn't changed
+ // TODO make sure the timing is together
+ // TODO allow starting an animation in the middle (eg gestures). Leave
+ // most of this up to the animation's implementation.
+ push(Class: Function, params = {}, opts = {}) {
+ util.defaults(opts, {
+ sync: this._stack.length === 0
+ })
+
+ let pushedItem = new NavStackData(Class, params)
+ this._stack.push(pushedItem)
+ this._ngForLoopArray.push(pushedItem)
+
+ return pushedItem.waitForSetup().then(() => {
+ let current = this._getPrevious(pushedItem)
+ current && current.leaveReverse(opts)
+ return pushedItem.enter(opts)
+ })
+ }
+
+ /**
+ * Pop a view off the history
+ *
+ * @param shouldAnimate whether to animate
+ */
+ pop(opts = {}) {
+ util.defaults(opts, {
+ sync: false
+ })
+ let current = this._stack.pop()
+ let dest = this.peek()
+
+ dest && dest.enterReverse(opts)
+ return current && current.leave(opts).then(() => this._destroy(current))
+ }
+
+ peek() {
+ return this._stack[this._stack.length - 1]
+ }
+
+ popAll() {
+ while (this._stack.length) {
+ const item = this._stack.pop()
+ this._destroy(item)
+ }
+ }
+
+ // Pop from the current item to the item at the specified index.
+ // Removes every item in the stack between the current and the given index,
+ // then performs a normal pop.
+ popTo(index, opts = {}) {
+ // Abort if we're already here.
+ if (this._stack.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()
+ this._destroy(item)
+ }
+
+ // Now put the current navItem back on the stack and run a normal pop animation.
+ this._stack.push(current)
+ return this.pop(opts)
+ }
+
+ setStack(stack) {
+ this._stack = stack.slice()
+ this._ngForLoopArray = stack.slice()
+ }
+
+ remove(index) {
+ const item = this._stack[index]
+ this._stack.splice(index, 1)
+ this._destroy(item)
+ }
+
+ _destroy(navItem) {
+ console.warn(
+`Component "${navItem.Class.name}" was popped from the nav stack, But were keeping its element in the DOM for now because of an ng2 bug.`
+ );
+ //util.array.remove(this._ngForLoopArray, navItem)
+ }
+
+ _getPrevious(item) {
+ return this._stack[ this._stack.indexOf(item) - 1 ]
+ }
+}
+
+class NavStackData {
+ constructor(ComponentClass, params = {}) {
+ this.Class = ComponentClass
+ this.params = params
+ this._setupPromise = new Promise((resolve) => {
+ this._resolveSetupPromise = resolve
+ })
+ }
+ waitForSetup() {
+ return this._setupPromise
+ }
+ finishSetup(navItem, componentInstance) {
+ this.navItem = navItem
+ this.instance = componentInstance
+ this._resolveSetupPromise()
+ }
+ setAnimation(state) {
+ if (!state) {
+ this.navItem.domElement.removeAttribute('animate')
+ this.navItem.domElement.classList.remove('start')
+ } else {
+ this.navItem.domElement.setAttribute('animate', state)
+ }
+ }
+ setShown(isShown) {
+ this.navItem.domElement.classList[isShown?'add':'remove']('shown')
+ }
+ startAnimation() {
+ this.navItem.domElement.classList.add('start')
+ }
+ _animate({ isShown, animation }) {
+ this.setAnimation(animation)
+ this.setShown(isShown)
+ if (animation) {
+ // We have to wait two rafs for the element to show. Yawn.
+ return util.dom.rafPromise().then(util.dom.rafPromise).then(() => {
+ this.startAnimation()
+ return util.dom.transitionEndPromise(this.navItem.domElement).then(() => {
+ this.setAnimation(null)
+ })
+ })
+ } else {
+ return Promise.resolve()
+ }
+ }
+ enterReverse(opts) {
+ return this.enter( util.extend({reverse: true}, opts) )
+ }
+ enter({ reverse = false, sync = false } = {}) {
+ return this._animate({
+ isShown: true,
+ animation: sync ? null : (reverse ? 'enter-reverse' : 'enter')
+ })
+ }
+ leave({ reverse = false, sync = false } = {}) {
+ return this._animate({
+ isShown: false,
+ animation: sync ? null : (reverse ? 'leave-reverse' : 'leave')
+ })
+ }
+ leaveReverse(opts) {
+ return this.leave( util.extend({reverse: true}, opts) )
+ }
+}
diff --git a/ionic/components/nav/nav-item.js b/ionic/components/nav/nav-item.js
new file mode 100644
index 0000000000..a0dad27f89
--- /dev/null
+++ b/ionic/components/nav/nav-item.js
@@ -0,0 +1,71 @@
+import {
+ DynamicComponent,
+ Parent,
+ NgElement,
+ DynamicComponentLoader,
+ ElementRef,
+} from 'angular2/angular2';
+import {
+ Injectable,
+ bind,
+ Optional,
+} from 'angular2/di';
+import {Nav} from 'ionic/components/nav/nav'
+import {Tab} from 'ionic/components/tabs/tab'
+import * as util from 'ionic/util';
+
+@DynamicComponent({
+ selector: 'ion-nav-item',
+ properties: {
+ _item: 'item'
+ }
+})
+export class NavItem {
+ constructor(
+ @NgElement() element: NgElement,
+ loader: DynamicComponentLoader,
+ elementRef: ElementRef
+
+ // FIXME: this is temporary until ng2 lets us inject tabs as a Nav
+ // @Optional() @Parent() viewportNav: Nav,
+ // @Optional() @Parent() viewportTab: Tab
+ ) {
+ this._loader = loader;
+ this._elementRef = elementRef;
+ // this.viewport = viewportTab || viewportNav;
+ this.domElement = element.domElement;
+ this.params = {};
+ }
+
+ set _item(data) {
+ if (this.initialized) return;
+ this.initialized = true;
+ this.Class = data.Class;
+ this._item = data;
+
+ util.extend(this.params, data.params);
+
+ this._loader.loadIntoExistingLocation(data.Class, this._elementRef).then(instance => {
+ this.instance = instance
+ data.finishSetup(this, instance)
+ })
+ }
+
+ // /**
+ // * Push out of this view into another view
+ // */
+ // push(Class: Function, opts = {}) {
+ // return this.viewport.push(Class, opts)
+ // }
+
+ // /**
+ // * Go back
+ // */
+ // pop(opts) {
+ // return this.viewport.pop(opts)
+ // }
+
+ // popTo(index, opts) {
+ // return this.viewport.popTo(index, opts)
+ // }
+}
diff --git a/ionic/components/nav/nav.js b/ionic/components/nav/nav.js
index 1c0ccc0528..52d20ebe1f 100644
--- a/ionic/components/nav/nav.js
+++ b/ionic/components/nav/nav.js
@@ -3,239 +3,41 @@ import {
View as NgView,
For,
NgElement,
- ComponentInjector
} from 'angular2/angular2';
-import {bind} from 'angular2/di';
-import {NavPane} from 'ionic/components/nav-pane/nav-pane';
-import * as util from 'ionic/util';
+import {NavControllerBase} from 'ionic/components/nav/nav-controller';
+import {NavItem} from 'ionic/components/nav/nav-item';
+import {Toolbar} from 'ionic/components/toolbar/toolbar';
-export class TestNav {
- constructor(v) {
- this.value = v;
- }
-};
-
-console.log(ComponentInjector)
@Component({
selector: 'ion-nav',
properties: {
initial: 'initial'
- },
- injectables: [
- bind(TestNav).toFactory((e) => {
- debugger;
- return e;
- }, [Nav])
- ]
+ }
})
@NgView({
template: `