mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 03:32:21 +08:00
nav wip
This commit is contained in:
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
13
ionic/components/nav/nav-controller.js
Normal file
13
ionic/components/nav/nav-controller.js
Normal 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);
|
||||
}
|
||||
}
|
@ -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) {
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
Reference in New Issue
Block a user