add advanced tabs testg

This commit is contained in:
Andrew
2015-04-02 15:11:00 -06:00
parent 94c10459b8
commit f1bfac8ae7
17 changed files with 300 additions and 62 deletions

View File

@ -98,16 +98,16 @@ gulp.task('e2e', ['ionic-js', 'sass'], function() {
.pipe(rename(function(file) { .pipe(rename(function(file) {
file.dirname = file.dirname.replace(path.sep + 'test' + path.sep, path.sep) file.dirname = file.dirname.replace(path.sep + 'test' + path.sep, path.sep)
})) }))
.pipe(gulpif(/main.html$/, processMainHtml())) .pipe(gulpif(/main.js$/, processMain()))
.pipe(gulpif(/e2e.js$/, createPlatformTests())) .pipe(gulpif(/e2e.js$/, createPlatformTests()))
.pipe(gulp.dest(buildConfig.dist + '/e2e')) .pipe(gulp.dest(buildConfig.dist + '/e2e'))
function processMainHtml() { function processMain() {
return through2.obj(function(file, enc, next) { return through2.obj(function(file, enc, next) {
this.push(new VinylFile({ this.push(new VinylFile({
base: file.base, base: file.base,
contents: new Buffer(indexContents), contents: new Buffer(indexContents),
path: file.path.replace(/main.html$/, 'index.html'), path: file.path.replace(/main.js$/, 'index.html'),
})) }))
next(null, file) next(null, file)
}) })

View File

@ -10,5 +10,6 @@ export * from 'ionic2/components/list/list'
export * from 'ionic2/components/nav-view/nav-view' export * from 'ionic2/components/nav-view/nav-view'
export * from 'ionic2/components/nav-viewport/nav-viewport' export * from 'ionic2/components/nav-viewport/nav-viewport'
export * from 'ionic2/components/tabs/tabs' export * from 'ionic2/components/tabs/tabs'
export * from 'ionic2/components/tabs/tab'
export * from 'ionic2/components/toolbar/toolbar' export * from 'ionic2/components/toolbar/toolbar'
export * from 'ionic2/components/view/view' export * from 'ionic2/components/view/view'

View File

@ -1,5 +1,6 @@
import {DynamicComponent, Parent, NgElement} from 'angular2/angular2' import {DynamicComponent, Ancestor, NgElement} from 'angular2/angular2'
import {NavViewport} from 'ionic2/components' import {Optional} from 'angular2/src/di/annotations'
import {NavViewport, Tabs} from 'ionic2/components'
import {PrivateComponentLoader} from 'angular2/src/core/compiler/private_component_loader' import {PrivateComponentLoader} from 'angular2/src/core/compiler/private_component_loader'
import {PrivateComponentLocation} from 'angular2/src/core/compiler/private_component_location' import {PrivateComponentLocation} from 'angular2/src/core/compiler/private_component_location'
@ -13,12 +14,15 @@ export class NavView {
constructor( constructor(
loader: PrivateComponentLoader, loader: PrivateComponentLoader,
location: PrivateComponentLocation, location: PrivateComponentLocation,
@Parent() viewport: NavViewport, @NgElement() element: NgElement,
@NgElement() element: NgElement
// FIXME: this is temporary until ng2 lets us inject tabs as a NavViewport
@Optional() @Ancestor() viewportNav: NavViewport,
@Optional() @Ancestor() viewportTabs: Tabs
) { ) {
this.loader = loader this.loader = loader
this.location = location this.location = location
this.viewport = viewport this.viewport = viewportTabs || viewportNav
this.domElement = element.domElement this.domElement = element.domElement
} }

View File

@ -1,3 +1,9 @@
.nav-viewport {
flex: 1;
position: relative;
display: flex;
}
.nav-view { .nav-view {
position: absolute; position: absolute;
top: 0; top: 0;

View File

@ -1,33 +1,41 @@
import {Component, Template, For} from 'angular2/angular2' import {Component, Template, For, NgElement} from 'angular2/angular2'
import {ComponentConfig} from 'ionic2/config/component-config' import {ComponentConfig} from 'ionic2/config/component-config'
import {NavView} from 'ionic2/components/nav-view/nav-view' import {NavView} from 'ionic2/components/nav-view/nav-view'
import {array as arrayUtil, dom as domUtil} from 'ionic2/util' import {array as arrayUtil, dom as domUtil, isFunction} from 'ionic2/util'
@Component({ @Component({
selector: 'ion-nav-viewport', selector: 'ion-nav-viewport',
bind: { bind: {
initialComponent: 'initialComponent' initial: 'initial'
}, },
}) })
@Template({ @Template({
inline: ` inline: `
<section class="nav-view" *for="#item of _ngForLoopArray" [item]="item"> <section class="nav-view" *for="#item of getRawNavStack()" [item]="item">
</section> </section>
`, `,
directives: [NavView, For] directives: [NavView, For]
}) })
export class NavViewport { export class NavViewport {
constructor() { constructor(
// stack is our public stack of items. This is synchronous and says an item element: NgElement
) {
this.domElement = element.domElement
this.domElement.classList.add('nav-viewport')
// stack is our sane stack of items. This is synchronous and says an item
// is removed even if it's still animating out. // is removed even if it's still animating out.
this.stack = [] this._stack = []
// _ngForLoopArray is our loop that actually adds/removes components. It doesn't // _ngForLoopArray is actually adds/removes components from the dom. It won't
// remove a component until it's done animating out. // remove a component until it's done animating out.
this._ngForLoopArray = [] this._ngForLoopArray = []
} }
set initialComponent(Class) { getRawNavStack() {
return this._ngForLoopArray
}
set initial(Class) {
if (!this.initialized) { if (!this.initialized) {
this.initialized = true this.initialized = true
this.push(Class) this.push(Class)
@ -40,18 +48,25 @@ export class NavViewport {
* @param view the new view * @param view the new view
* @param shouldAnimate whether to animate * @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 only animate if state hasn't changed
// TODO make sure the timing is together // TODO make sure the timing is together
// TODO allow starting an animation in the middle (eg gestures). Leave // TODO allow starting an animation in the middle (eg gestures). Leave
// most of this up to the animation's implementation. // most of this up to the animation's implementation.
push(Class, opts = {}) { push(Class: Function, { sync = this._stack.length === 0 } = {}) {
let item = new NavItem(Class, opts) let pushedItem = new NavItem(Class)
this.stack.push(item) this._stack.push(pushedItem)
this._ngForLoopArray.push(item) this._ngForLoopArray.push(pushedItem)
return item.waitForSetup().then(() => {
let current = this.getPrevious(item) return pushedItem.waitForSetup().then(() => {
let current = this.getPrevious(pushedItem)
if (sync) {
current && current.leaveSync()
pushedItem.enterSync()
} else {
current && current.leaveReverse() current && current.leaveReverse()
return item.enter() return pushedItem.enter()
}
}) })
} }
@ -60,18 +75,23 @@ export class NavViewport {
* *
* @param shouldAnimate whether to animate * @param shouldAnimate whether to animate
*/ */
pop() { pop({ sync = false } = {}) {
let current = this.stack.pop() let current = this._stack.pop()
let previous = this.stack[this.stack.length - 1] let previous = this._stack[this._stack.length - 1]
previous.enterReverse() if (sync) {
return current.leave().then(() => { previous && previous.enterSync()
// The animation is done, remove it from the dom return Promise.resolve(remove())
} else {
previous && previous.enterReverse()
return current.leave().then(remove)
}
function remove() {
arrayUtil.remove(this._ngForLoopArray, current) arrayUtil.remove(this._ngForLoopArray, current)
}) }
} }
getPrevious(item) { getPrevious(item) {
return this.stack[ this.stack.indexOf(item) - 1 ] return this._stack[ this._stack.indexOf(item) - 1 ]
} }
// Animate a new view *in* // Animate a new view *in*
@ -125,6 +145,14 @@ class NavItem {
}) })
}) })
} }
enterSync() {
this.setAnimation(null)
return this.setShown(true)
}
leaveSync() {
this.setAnimation(null)
return this.setShown(false)
}
enter() { enter() {
return this._animate({ isShown: true, animation: 'enter' }) return this._animate({ isShown: true, animation: 'enter' })
} }

View File

@ -1,19 +1,48 @@
import {NgElement, Component, Template, Parent} from 'angular2/angular2'; import {
NgElement,
Component,
Template,
Ancestor,
PropertySetter,
For
} from 'angular2/angular2';
import {NavViewport, NavView, Tabs} from 'ionic2/components'
import {IonicComponent} from 'ionic2/config/component'
@Component({ @Component({
selector: 'ion-tab', selector: 'ion-tab',
bind: { bind: {
tabTitle: 'tab-title' title: 'tab-title',
initial: 'initial'
} }
}) })
@Template({ @Template({
inline: ` inline: `
<div [hidden]="!isSelected"> <section class="nav-view" *for="#item of getRawNavStack()" [item]="item">
<content></content> </section>
</div> `,
` directives: [For, NavView]
}) })
export class Tab { export class Tab extends NavViewport {
constructor(
element: NgElement,
@Ancestor() tabs: Tabs,
@PropertySetter('class.hide') setHidden: Function
) {
super(element)
this.config = Tab.config.invoke(this)
this.setHidden = setHidden
this.setSelected(false)
tabs.addTab(this)
}
setSelected(isSelected) {
this.isSelected = !!isSelected
this.setHidden(!this.isSelected)
}
} }
new IonicComponent(Tab, {
})

View File

@ -1,40 +1,66 @@
import {NgElement, Component, Template, Foreach, Parent} from 'angular2/angular2' import {NgElement, Component, Template, Parent, For} from 'angular2/angular2'
import {NavViewport} from 'ionic2/components'
import {IonicComponent} from 'ionic2/config/component' import {IonicComponent} from 'ionic2/config/component'
@Component({ @Component({
selector: 'ion-tabs', selector: 'ion-tabs',
bind: { bind: {
placement: 'placement' placement: 'placement'
}, }
}) })
@Template({ @Template({
inline: ` inline: `
<div class="toolbar tab-bar toolbar-ios toolbar-bottom"> <div class="toolbar tab-bar toolbar-ios toolbar-bottom">
<div class="tab-bar-content"> <div class="tab-bar-content">
<a class="tab-bar-item tab-active" href="#"> <a *for="#tab of tabs"
Tab 1 class="tab-bar-item tab-active"
</a> [class.active]="tab.isSelected"
<a class="tab-bar-item" href="#"> (click)="onClickTabItem($event, tab)">
Tab 2 {{tab.title}}
</a>
<a class="tab-bar-item" href="#">
Tab 3
</a> </a>
</div> </div>
</div> </div>
<content></content>
`, `,
directives: [] directives: [For]
}) })
export class Tabs extends NavViewport { export class Tabs {
constructor( constructor(
element: NgElement element: NgElement
) { ) {
super()
this.domElement = element.domElement this.domElement = element.domElement
this.domElement.classList.add('pane') this.domElement.classList.add('pane')
this.config = Tabs.config.invoke(this) this.config = Tabs.config.invoke(this)
this.tabs = []
} }
addTab(tab) {
this.tabs.push(tab)
if (this.tabs.length == 1) {
this.select(tab)
}
}
onClickTabItem(ev, tab) {
ev.preventDefault()
if (this.selectedTab !== tab) {
this.select(tab)
} else if (tab._stack.length >= 2) {
while (tab._stack.length > 2) {
tab.pop({ sync: true }) // pop with no animation
}
tab.pop() //pop last one with animation
}
}
select(tab) {
this.tabs.forEach(otherTab => {
otherTab.setSelected(false)
})
tab.setSelected(true)
this.selectedTab = tab
}
} }
new IonicComponent(Tabs, { new IonicComponent(Tabs, {

View File

@ -20,3 +20,7 @@
min-width: 100px; min-width: 100px;
text-align: center; text-align: center;
} }
.tab-bar-item.active {
color: red;
}

View File

@ -0,0 +1,17 @@
import {Component, Template, bootstrap} from 'angular2/angular2'
import {NavViewport} from 'ionic2/components'
import {SignInPage} from 'app/pages/sign-in'
@Component({
selector: '[ion-app]'
})
@Template({
inline: '<ion-nav-viewport [initial]="initial"></ion-nav-viewport>',
directives: [NavViewport]
})
class App {
constructor() {
this.initial = SignInPage
}
}
bootstrap(App)

View File

@ -0,0 +1,5 @@
<ion-view nav-title="Sign In">
<button class="button-positive" (click)="signIn()">
Sign In
</button>
</ion-view>

View File

@ -0,0 +1,21 @@
import {Component, Template, Parent} from 'angular2/angular2'
import {View, NavViewport} from 'ionic2/components'
import {TabsPage} from 'app/pages/tabs'
@Component({
selector: 'sign-in-page'
})
@Template({
url: 'pages/sign-in.html',
directives: [View]
})
export class SignInPage {
constructor(
@Parent() viewport: NavViewport
) {
this.viewport = viewport
}
signIn() {
this.viewport.push(TabsPage)
}
}

View File

@ -0,0 +1,8 @@
<ion-view view-title="Tabs">
<ion-tabs>
<ion-tab [initial]="tab1Initial" tab-title="Tab 1">
</ion-tab>
<ion-tab [initial]="tab2Initial" tab-title="Tab 2">
</ion-tab>
</ion-tabs>
</ion-view>

View File

@ -0,0 +1,73 @@
import {Component, Template, Parent} from 'angular2/angular2'
import {View, Tabs, Tab} from 'ionic2/components'
import {NavViewport} from 'ionic2/components'
@Component({
selector: 'tabs-page'
})
@Template({
url: 'pages/tabs.html',
directives: [Tabs, Tab]
})
export class TabsPage {
constructor() {
this.tab1Initial = Tab1Page1
this.tab2Initial = Tab2Page1
}
}
//
// tab 1
//
@Component({ selector: 't1p1' })
@Template({
inline: '<ion-view>Tab 1 Page 1.<br/><br/><button (click)="next()">Next</button></ion-view>',
directives: [View]
})
class Tab1Page1 {
constructor(@Parent() tab: Tab) {
this.tab = tab
}
next() {
this.tab.push(Tab1Page2)
}
}
@Component({ selector: 't1p2' })
@Template({
inline: '<ion-view>Tab 1<br/>Page 2.<br/></br><button (click)="pop()">Pop</button></ion-view>',
directives: [View]
})
class Tab1Page2 {
constructor(@Parent() tab: Tab) { this.tab = tab }
pop() { this.tab.pop() }
}
//
// tab 2
//
@Component({ selector: 't2p1' })
@Template({
inline: '<ion-view><br/><br/>Tab 2 Page 1. <button (click)="next()">Next</button></ion-view>',
directives: [View]
})
class Tab2Page1 {
constructor(@Parent() tab: Tab) {
this.tab = tab
}
next() {
this.tab.push(Tab2Page2)
}
}
@Component({ selector: 't2p2' })
@Template({
inline: '<ion-view><br/><br/>Tab 2<br/>Page 2. <button (click)="pop()">Pop</button></ion-view>',
directives: [View]
})
class Tab2Page2 {
constructor(@Parent() tab: Tab) { this.tab = tab }
pop() { this.tab.pop() }
}

View File

@ -3,8 +3,11 @@
<ion-tabs> <ion-tabs>
<ion-tab> <ion-tab tab-title="Tab 1">
Tab 1 Content Tab 1 Content<br/>.
</ion-tab>
<ion-tab tab-title="Tab 2">
.<br/>Tab 2 Content
</ion-tab> </ion-tab>
</ion-tabs> </ion-tabs>

View File

@ -1,13 +1,12 @@
import {bootstrap} from 'angular2/core'; import {bootstrap} from 'angular2/core';
import {Component, Template} from 'angular2/angular2'; import {Component, Template} from 'angular2/angular2';
import {View} from 'ionic2/components/view/view'; import {View, Tabs, Tab} from 'ionic2/components';
import {Tabs} from 'ionic2/components/tabs/tabs';
@Component({ selector: '[ion-app]' }) @Component({ selector: '[ion-app]' })
@Template({ @Template({
url: 'main.html', url: 'main.html',
directives: [View, Tabs] directives: [View, Tabs, Tab]
}) })
class IonicApp { class IonicApp {
constructor() { constructor() {

View File

@ -1,5 +1,7 @@
import {NgElement, Component, Template} from 'angular2/angular2' import {NgElement, Component, Template, Ancestor} from 'angular2/angular2'
import {Optional} from 'angular2/src/di/annotations'
import {BackButton} from 'ionic2/components/toolbar/back-button' import {BackButton} from 'ionic2/components/toolbar/back-button'
import {Tabs, NavViewport, NavView} from 'ionic2/components'
import {ComponentConfig} from 'ionic2/config/component-config' import {ComponentConfig} from 'ionic2/config/component-config'
import {raf} from 'ionic2/util/dom' import {raf} from 'ionic2/util/dom'
@ -32,7 +34,10 @@ export let ToolbarConfig = new ComponentConfig('toolbar')
directives: [BackButton] directives: [BackButton]
}) })
export class Toolbar { export class Toolbar {
constructor(@NgElement() ngEle:NgElement, configFactory: ToolbarConfig) { constructor(
@NgElement() ngEle:NgElement,
configFactory: ToolbarConfig
) {
this.domElement = ngEle.domElement this.domElement = ngEle.domElement
this.config = configFactory.create(this); this.config = configFactory.create(this);
@ -76,4 +81,10 @@ export class Toolbar {
}) })
} }
back() {
if (this.viewport && this.viewport._stack.length) {
this.viewport.pop()
}
}
} }

View File

@ -1,7 +1,10 @@
import {NgElement, Component, Template, Parent} from 'angular2/angular2' import {NgElement, Component, Template, Parent, Ancestor} from 'angular2/angular2'
import {Toolbar} from 'ionic2/components/toolbar/toolbar' import {Toolbar} from 'ionic2/components/toolbar/toolbar'
import {ComponentConfig} from 'ionic2/config/component-config' import {ComponentConfig} from 'ionic2/config/component-config'
import {Tabs, NavViewport, NavView} from 'ionic2/components'
import {Optional} from 'angular2/src/di/annotations'
export let ViewConfig = new ComponentConfig('view') export let ViewConfig = new ComponentConfig('view')
@Component({ @Component({