update to angular 2.0.0-alpha.21, update toolbar system

This commit is contained in:
Andrew Joslin
2015-04-30 10:54:46 -06:00
parent 2715e50988
commit 8a42dde132
24 changed files with 651 additions and 344 deletions

View File

@ -36,9 +36,13 @@ gulp.task('lib', ['fonts', 'dependencies'])
gulp.task('watch', ['default'], function() {
gulp.watch(buildConfig.src.scss, ['sass'])
gulp.watch([].concat(
buildConfig.src.js, buildConfig.src.e2e, buildConfig.src.html,
buildConfig.src.js, buildConfig.src.html,
'scripts/e2e/index.template.html'
), ['e2e'])
gulp.watch([].concat(
buildConfig.src.e2e, buildConfig.src.html,
'scripts/e2e/index.template.html'
), ['ionic-js'])
})
gulp.task('karma', function() {
@ -75,7 +79,7 @@ gulp.task('clean', function(done) {
del([buildConfig.dist], done)
})
gulp.task('e2e', ['ionic-js', 'sass', 'ng2-copy'], function() {
gulp.task('e2e', ['ionic-js', 'sass'], function() {
var indexContents = _.template( fs.readFileSync('scripts/e2e/index.template.html') )({
buildConfig: buildConfig
});

View File

@ -11,14 +11,12 @@ 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' //TODO remove
export * from 'ionic/components/nav/nav'
export * from 'ionic/components/nav/nav-item'
export * from 'ionic/components/nav/decorators'
export * from 'ionic/components/radio/radio-button'
export * from 'ionic/components/radio/radio-group'
export * from 'ionic/components/search-bar/search-bar'
export * from 'ionic/components/scroll/pull-to-refresh'
export * from 'ionic/components/split-view/split-view'
export * from 'ionic/components/switch/switch'
export * from 'ionic/components/tabs/tabs'

View File

@ -32,6 +32,10 @@ html {
position: relative;
width: 100%;
min-height: 50px;
@include flex-order($flex-order-toolbar-top);
}
footer.toolbar-container {
@include flex-order($flex-order-toolbar-bottom);
}
.toolbar {
@ -44,7 +48,7 @@ html {
}
.nav-item-container {
// container of many .nav-pane's, each one containing one view
// container of many .nav-item's, each one containing one view
position: relative;
@include flex(1);
@include flex-order($flex-order-view-content);
@ -58,28 +62,20 @@ html {
@include flex-order($flex-order-view-content);
}
.nav-pane {
// container of one ion-view
position: absolute;
width: 100%;
height: 100%;
background: white;
}
.nav-pane-cover-parent {
// .nav-pane that should completely cover siblings and parent
.nav-item-cover-parent {
// .nav-item that should completely cover siblings and parent
top: -50px;
height: 100vh;
}
ion-view {
// user created view, contained by .nav-pane
.nav-item,
.nav-item-child {
// user created view, contained by .nav-item
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: block;
}
ion-content {

View File

@ -1,4 +1,4 @@
import {Component, View as NgView, NgElement, PropertySetter} from 'angular2/angular2'
import {Component, View as NgView, NgElement} from 'angular2/angular2'
import {IonicComponent} from 'ionic/config/component'
@ -28,15 +28,20 @@ import {IonicComponent} from 'ionic/config/component'
})
export class Checkbox {
constructor(
@NgElement() ngElement: NgElement,
@PropertySetter('attr.role') setAriaRole: Function,
@PropertySetter('attr.aria-checked') setAriaChecked: Function,
@PropertySetter('attr.aria-invalid') setAriaInvalid: Function,
@PropertySetter('attr.aria-disabled') setAriaDisabled: Function
@NgElement() ngElement: NgElement
// @PropertySetter('attr.role') setAriaRole: Function,
// @PropertySetter('attr.aria-checked') setAriaChecked: Function,
// @PropertySetter('attr.aria-invalid') setAriaInvalid: Function,
// @PropertySetter('attr.aria-disabled') setAriaDisabled: Function
) {
this.domElement = ngElement.domElement
this.domElement.classList.add('item')
let setAriaRole = (v) => this.domElement.setAttribute('aria-role', v)
let setAriaChecked = (v) => this.domElement.setAttribute('aria-checked', v)
let setAriaInvalid = (v) => this.domElement.setAttribute('aria-invalid', v)
let setAriaDisabled = (v) => this.domElement.setAttribute('aria-disabled', v)
this.config = Checkbox.config.invoke(this)
setAriaRole('checkbox')

View File

@ -1,4 +1,8 @@
import {NgElement, Component, View as NgView, PropertySetter} from 'angular2/angular2';
import {
NgElement,
Component,
View as NgView,
} from 'angular2/angular2';
@Component({
selector: 'ion-content'
@ -13,6 +17,7 @@ export class Content {
constructor(
@NgElement() element:NgElement
) {
console.log('constructing content', element.domElement);
this.domElement = element.domElement;
this.domElement.classList.add('content');
}

View File

@ -0,0 +1,6 @@
export class NavBarContainer {
}
export class NavBar extends Toolbar {
}

View File

@ -1,20 +1,24 @@
import {NgElement} from 'angular2/angular2';
import * as util from 'ionic/util';
export class NavControllerBase {
/*
* Used be tabs and nav
*/
export class NavBase {
constructor(
element: NgElement
) {
this.domElement = element.domElement
this.domElement.classList.add('nav')
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 = []
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 = []
// The _ng* arrays are what add/remove components from the dom.
// These arrays won't remove a component until they're
// done animating out.
this._ngNavItems = [];
}
containsClass(Class) {
@ -52,7 +56,7 @@ export class NavControllerBase {
let pushedItem = new NavStackData(Class, params)
this._stack.push(pushedItem)
this._ngForLoopArray.push(pushedItem)
this._ngNavItems.push(pushedItem)
return pushedItem.waitForSetup().then(() => {
let current = this._getPrevious(pushedItem)
@ -71,15 +75,19 @@ export class NavControllerBase {
sync: false
})
let current = this._stack.pop()
let dest = this.peek()
let dest = this.last()
dest && dest.enterReverse(opts)
return current && current.leave(opts).then(() => this._destroy(current))
return current && current.leave(opts)
.then(() => this._destroy(current))
}
peek() {
last() {
return this._stack[this._stack.length - 1]
}
length() {
return this._stack.length;
}
popAll() {
while (this._stack.length) {
@ -112,7 +120,7 @@ export class NavControllerBase {
setStack(stack) {
this._stack = stack.slice()
this._ngForLoopArray = stack.slice()
this._ngNavItems = stack.slice()
}
remove(index) {
@ -123,14 +131,19 @@ export class NavControllerBase {
_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.`
`Component "${navItem.Class.name}" was popped from the nav stack, but wer'e keeping its element in the DOM for now because of an ng2 bug.`
);
//util.array.remove(this._ngForLoopArray, navItem)
// util.array.remove(this._ngNavItems, navItem)
}
_getPrevious(item) {
return this._stack[ this._stack.indexOf(item) - 1 ]
}
getToolbars(pos: String) {
let last = this.last();
return last && last.navItem && last.navItem._toolbars[pos] || [];
}
}
class NavStackData {

View File

@ -1,73 +1,132 @@
import {
Component,
DynamicComponent,
Parent,
Decorator,
Ancestor,
NgElement,
DynamicComponentLoader,
ElementRef,
Query,
View as NgView,
} 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 {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) {
if (!this.navItem._toolbars[placement]) {
return console.error(
`Toolbar must have a placement of top or bottom, found toolbar ${toolbar} with placement ${placement}!`
);
}
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
]
})
@NgView({
// 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;
}
set _item(data) {
var navChild = this.dynamicComponentChild;
if (this.initialized) return;
this.initialized = true;
this.Class = data.Class;
util.extend(this.navCtrl.params, data.params || {});
navChild._loader.loadIntoExistingLocation(this.Class, navChild._elementRef).then((instance) => {
this.instance = instance;
data.finishSetup(this, instance);
});
}
}
/**
* 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: 'ion-nav-item',
selector: '.nav-item-child',
properties: {
_item: 'item'
}
})
export class NavItem {
export class NavItemDynamicComponent {
constructor(
@NgElement() element: NgElement,
loader: DynamicComponentLoader,
elementRef: ElementRef
elementRef: ElementRef,
@Ancestor() navItem: NavItem
// 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 = {};
navItem.dynamicComponentChild = this;
}
set _item(data) {
if (this.initialized) return;
this.initialized = true;
this.Class = data.Class;
this._item = data;
if(data.parms) {
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)
// }
}

View File

@ -2,7 +2,7 @@
$transition-duration: 500ms;
.nav-pane {
.nav-item {
display: none;
&.shown {

View File

@ -1,43 +1,62 @@
import {
Component,
View as NgView,
If,
For,
NgElement,
Query,
QueryList,
Descendants
} from 'angular2/angular2';
import {NavControllerBase} from 'ionic/components/nav/nav-controller';
import {NavItem} from 'ionic/components/nav/nav-item';
import {Toolbar} from 'ionic/components/toolbar/toolbar';
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
]
})
@NgView({
template: `
<header class="toolbar-container">
<ion-toolbar class="view-toolbar">
<ion-nav-title>
Test Nonfunctional Toolbar
</ion-nav-title>
</ion-toolbar>
<!-- COLLECTION OF TOOLBARS FOR EACH OF ITS VIEWS WITHIN THIS NAV-VIEWPORT -->
<!-- TOOLBARS FOR EACH VIEW SHOULD HAVE THE SAME CONTEXT AS ITS VIEW -->
</header>
<div class="nav-item-container">
<!-- COLLECTION OF PANES WITHIN THIS NAV-VIEWPORT, EACH PANE AS ONE VIEW -->
<!-- EACH VIEW HAS A TOOLBAR WHICH NEEDS TO HAVE THE SAME CONTEXT -->
<ion-nav-item class="nav-pane" *for="#item of _ngForLoopArray" [item]="item"></section>
<header class="toolbar-container" [class.hide]="getToolbars('top').length == 0">
<div *for="#toolbar of getToolbars('top')" [toolbar-create]="toolbar">
</div>
</header>
<section class="nav-item-container">
<div class="nav-item"
*for="#item of _ngNavItems"
[item]="item"></div>
</section>
<footer class="toolbar-container" [class.hide]="getToolbars('bottom').length == 0">
<div *for="#toolbar of getToolbars('bottom')" [toolbar-create]="toolbar">
</div>
</footer>
`,
directives: [NavItem, For, Toolbar]
directives: [NavItem, For, If, ToolbarContainer]
})
export class Nav extends NavControllerBase {
export class Nav extends NavBase {
constructor(
element: NgElement
element: NgElement,
navInjectable: NavInjectable
) {
super(element);
// Add the nav to navInjectable.
navInjectable.nav = this;
}
}

View File

@ -1,4 +1,4 @@
import {Component, View as NgView, NgElement, PropertySetter} from 'angular2/angular2'
import {Component, View as NgView, NgElement} from 'angular2/angular2'
import {IonicComponent} from 'ionic/config/component'
@ -27,15 +27,20 @@ import {IonicComponent} from 'ionic/config/component'
})
export class Switch {
constructor(
element: NgElement,
@PropertySetter('attr.role') setAriaRole: Function,
@PropertySetter('attr.aria-checked') setChecked: Function,
@PropertySetter('attr.aria-invalid') setInvalid: Function,
@PropertySetter('attr.aria-disabled') setDisabled: Function
element: NgElement
// @PropertySetter('attr.role') setAriaRole: Function,
// @PropertySetter('attr.aria-checked') setChecked: Function,
// @PropertySetter('attr.aria-invalid') setInvalid: Function,
// @PropertySetter('attr.aria-disabled') setDisabled: Function
) {
this.domElement = element.domElement
this.config = Switch.config.invoke(this)
let setAriaRole = (v) => this.domElement.setAttribute('aria-role', v)
let setAriaChecked = (v) => this.domElement.setAttribute('aria-checked', v)
let setAriaInvalid = (v) => this.domElement.setAttribute('aria-invalid', v)
let setAriaDisabled = (v) => this.domElement.setAttribute('aria-disabled', v)
this.domElement.classList.add('item')
setAriaRole('checkbox')

View File

@ -3,14 +3,24 @@ import {
NgElement,
View as NgView,
Ancestor,
PropertySetter,
For
For,
If,
} from 'angular2/angular2';
import {NavControllerBase} from 'ionic/components/nav/nav-controller';
import {bind} from 'angular2/di';
import {NavInjectable} from 'ionic/components/nav/nav';
import {NavBase} from 'ionic/components/nav/nav-base';
import {NavItem} from 'ionic/components/nav/nav-item';
import {Tabs} from 'ionic/components/tabs/tabs';
import * as util from 'ionic/util';
import {IonicComponent} from 'ionic/config/component';
import {ToolbarContainer} from 'ionic/components/toolbar/toolbar';
/*
* See components/nav/nav for an explanation of NavInjectable.
* Allow a tab to publish itself on the injector in a similar manner.
*/
class TabNavInjectable {}
@Component({
selector: 'ion-tab',
@ -19,34 +29,50 @@ import {IonicComponent} from 'ionic/config/component';
icon: 'tab-icon',
initial: 'initial'
},
injectables: [
// Child components will inject `NavInjectable` but it will get the tabs one.
bind(NavInjectable).toClass(TabNavInjectable)
]
})
@NgView({
template: `
<header class="toolbar-container">
<ion-toolbar class="view-toolbar">
<ion-nav-title>
Test Nonfunctional Toolbar
</ion-nav-title>
</ion-toolbar>
</header>
<div class="nav-item-container">
<!-- COLLECTION OF PANES WITHIN THIS NAV-VIEWPORT, EACH PANE AS ONE VIEW -->
<!-- EACH VIEW HAS A TOOLBAR WHICH NEEDS TO HAVE THE SAME CONTEXT -->
<ion-nav-item class="nav-pane" *for="#item of _ngForLoopArray" [item]="item"></section>
<header class="toolbar-container" [class.hide]="nav.getToolbars('top').length == 0">
<div *for="#toolbar of nav.getToolbars('top')" [toolbar-create]="toolbar">
</div>
</header>
<section class="nav-item-container">
<div class="nav-item"
*for="#item of nav._ngNavItems"
[item]="item"></div>
</section>
<footer class="toolbar-container" [class.hide]="nav.getToolbars('bottom').length == 0">
<div *for="#toolbar of nav.getToolbars('bottom')" [toolbar-create]="toolbar">
</div>
</footer>
`,
directives: [For, NavItem]
directives: [NavItem, For, If, ToolbarContainer]
})
export class Tab extends NavControllerBase {
export class Tab {
constructor(
element: NgElement,
@Ancestor() tabs: Tabs,
@PropertySetter('class.hide') setHidden: Function,
@PropertySetter('attr.role') setRole: Function,
@PropertySetter('attr.id') setId: Function,
@PropertySetter('attr.aria-labelledby') setLabelby: Function
navInjectable: NavInjectable,
@Ancestor() tabs: Tabs
// @PropertySetter('class.hide') setHidden: Function,
// @PropertySetter('attr.role') setRole: Function,
// @PropertySetter('attr.id') setId: Function,
// @PropertySetter('attr.aria-labelledby') setLabelby: Function
) {
super(element);
this.nav = new NavBase(element);
this.domElement = element.domElement;
// Allow children to get this nav instance from injecting the NavInjectable
navInjectable.nav = this.nav;
let setHidden = (v) => this.domElement.classList[v?'add':'remove']('hide');
let setRole = (v) => this.domElement.setAttribute('role', v);
let setId = (v) => this.domElement.setAttribute('id', v);
let setLabelby = (v) => this.domElement.setAttribute('aria-labelledby', v);
this.config = Tab.config.invoke(this);
this.setHidden = setHidden
@ -59,6 +85,10 @@ export class Tab extends NavControllerBase {
tabs.addTab(this)
}
set initial(value) {
this.nav.initial = value;
}
setSelected(isSelected) {
this.isSelected = !!isSelected
this.setHidden(!this.isSelected)

View File

@ -1,5 +1,8 @@
import {NgElement, Component, View as NgView, For, PropertySetter} from 'angular2/angular2'
import {IonicComponent} from 'ionic/config/component'
import {NgElement, Component, View as NgView, For, PropertySetter, Query} from 'angular2/angular2';
import {QueryList} from 'angular2/src/core/compiler/query_list';
import {IonicComponent} from 'ionic/config/component';
import {Toolbar} from 'ionic/components/toolbar/toolbar';
import {Tab} from 'ionic/components/tabs/tab';
@Component({
selector: 'ion-tabs',
@ -9,15 +12,25 @@ import {IonicComponent} from 'ionic/config/component'
}
})
@NgView({
//[attr.aria-activedescendant]="'tab-item-' + selectedTab.tabId"
template: `
<header class="toolbar-container">
<!-- COLLECTION OF TOOLBARS FOR EACH VIEW WITHIN EACH TAB-VIEWPORT -->
<!-- TOOLBARS FOR EACH VIEW SHOULD HAVE THE SAME CONTEXT AS ITS VIEW -->
</header>
<!-- TODO: Once the reprojected toolbar preserves the context of the Tabs, then
remove this for loop and use the one in the ion-toolbar below. -->
<button *for="#t of tabs"
role="tab"
class="button button-primary"
[attr.id]="'tab-item-' + t.tabId"
[attr.aria-controls]="'tab-content-' + t.tabId"
[attr.aria-selected]="t.isSelected"
[style.color]="t.isSelected ? 'red' : ''"
(^click)="onClickTabItem($event, t)">
<icon [class-name]="'tab-bar-item-icon ' + t.icon" [hidden]="!t.icon"></icon>
<span class="tab-bar-item-text" [hidden]="!t.title">{{t.title}}</span>
</button>
<nav class="tab-bar-container" role="tablist"
[attr.aria-activedescendant]="'tab-item-' + selectedTab.tabId">
<div class="tab-bar">
<!-- TODO: set the ion-toolbar being on top or bottom depending upon configuration. -->
<header *ion-toolbar class="tab-bar">
The tabbar buttons should be in this bar.
<button *for="#t of tabs"
role="tab"
class="tab-bar-item"
@ -28,48 +41,51 @@ import {IonicComponent} from 'ionic/config/component'
<icon [class-name]="'tab-bar-item-icon ' + t.icon" [hidden]="!t.icon"></icon>
<span class="tab-bar-item-text" [hidden]="!t.title">{{t.title}}</span>
</button>
</div>
</nav>
</header>
<section class="tab-pane-container">
<content></content>
</section>
`,
directives: [For]
directives: [For, Toolbar]
})
export class Tabs {
constructor(
@NgElement() ngElement: NgElement
/*
TODO once QueryList#onChange is fixed, switch to a queryList of tabs, for the
sake of simplicity
@Query(Tab) tabs:QueryList
*/
) {
console.log("Tabs");
this.domElement = ngElement.domElement
this.config = Tabs.config.invoke(this)
this.domElement = ngElement.domElement;
this.config = Tabs.config.invoke(this);
this.tabs = []
this.tabs = [];
}
addTab(tab) {
this.tabs.push(tab)
this.tabs.push(tab);
if (this.tabs.length == 1) {
this.select(tab)
this.select(tab);
}
}
onClickTabItem(ev, tab) {
ev.preventDefault()
if (this.selectedTab !== tab) {
this.select(tab)
} else if (tab._stack.length >= 2) {
tab.popTo(0)
this.select(tab);
} else if (tab.nav._stack.length >= 2) {
tab.nav.popTo(0);
}
}
select(tab) {
this.tabs.forEach(otherTab => {
otherTab.setSelected(false)
otherTab.setSelected(false);
})
tab.setSelected(true)
this.selectedTab = tab
tab.setSelected(true);
this.selectedTab = tab;
}
}

View File

@ -23,17 +23,11 @@ $tab-bar-item-max-width: 160px !default;
@include flex-order($flex-order-tab-bar-bottom);
}
.tab-bar-container {
@include flex-justify-content(center);
position: relative;
background: $tab-bar-background-color;
}
.tab-bar {
@include flex-display();
@include flex-justify-content(center);
height: 100%;
overflow: hidden;
background: $tab-bar-background-color;
}
.tab-bar-item {

View File

@ -1,16 +1,27 @@
<ion-nav-items side="primary">
<button class="button" (click)="instanceCheck()">
<icon class="ion-star"></icon>
<header *ion-toolbar>
<button class="button button-primary" side="primary">
Button 1
</button>
<button class="button" side="primary">
Button 2
</button>
</ion-nav-items>
<ion-content class="padding">
<h1 class="toolbar-title">
My Title
</h1>
<button class="button toolbar-secondary" side="secondary">
Button 3
</button>
</header>
<ion-content>
<p>
<button class="button button-primary" (click)="signIn()">
Sign In
</button>
<button class="button button-primary button-outline" (click)="instanceCheck()">
<button class="button button-primary button-outline">
<icon class="ion-star"></icon>
</button>
</p>
@ -19,6 +30,16 @@
<input>
</p>
<f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f>
<f></f>
<f></f>
<f></f>
<f></f>
<f></f>
<f></f>
<f></f>
<f></f>
<f></f>
<f></f>
<f></f>
</ion-content>

View File

@ -1,27 +1,32 @@
import {Component, View as NgView, Ancestor} from 'angular2/angular2'
import {NavPane, Content, Nav} from 'ionic/ionic'
import {TabsPage} from 'pages/tabs'
import {
Component,
View as NgView,
Ancestor,
} from 'angular2/angular2';
import {
NavItem,
Content,
NavController,
Toolbar,
ToolbarTitle,
} from 'ionic/ionic';
import {TabsPage} from 'pages/tabs';
@Component({
selector: 'sign-in-page'
})
@Component()
@NgView({
templateUrl: 'pages/sign-in.html',
directives: [Content]
directives: [Content, Toolbar, ToolbarTitle]
})
export class SignInPage {
constructor(
nav: Nav
nav: NavController
) {
this.nav = nav;
this.instanceVal = Math.random()
}
signIn() {
console.log('click');
this.nav.push(TabsPage, {
my: 'param'
})
}
instanceCheck() {
window.alert("Instance: " + this.instanceVal)
}
}

View File

@ -2,16 +2,16 @@ import {
Component,
View as NgView,
Parent,
} from 'angular2/angular2'
} from 'angular2/angular2';
import {
View,
Tabs,
Tab,
Aside,
Content,
Nav,
NavItem,
} from 'ionic/ionic'
NavController,
} from 'ionic/ionic';
import {Toolbar, ToolbarTitle} from 'ionic/components/toolbar/toolbar';
@Component({
selector: 'tabs-page'
@ -20,10 +20,10 @@ import {
templateUrl: 'pages/tabs.html',
directives: [Tabs, Tab, View, Content]
})
export class TabsPage {
constructor(
navItem: NavItem,
nav: Nav
nav: NavController
) {
this.tab1Initial = Tab1Page1
this.tab2Initial = Tab2Page1
@ -36,18 +36,22 @@ export class TabsPage {
//
@Component({ selector: 't1p1' })
@NgView({
template: `<ion-content class="padding">
template: `
<header *ion-toolbar>
<h1 class="toolbar-title">Tabs 1 Page 1</h1>
</header>
<ion-content class="padding">
<p>Tab 1 Page 1.</p>
<button class="button button-primary" (click)="next()">
Go to Tab 1, Page 2 (push)
</button>
<f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f>
</ion-content>`,
directives: [View, Content]
directives: [Content, Toolbar, ToolbarTitle]
})
class Tab1Page1 {
// TODO change to 'Nav' injection when we're allowed to inject a tab as a nav.
constructor(nav: Tab) {
constructor(nav: NavController) {
this.nav = nav;
}
next() {
@ -57,19 +61,22 @@ class Tab1Page1 {
@Component({ selector: 't1p2' })
@NgView({
template: `<ion-content class="padding">
template: `
<header *ion-toolbar>
<h1 class="toolbar-title">Tabs 1 Page 2</h1>
</header>
<ion-content class="padding">
<p>Tab 1 Page 2.</p>
<button class="button button-primary" (click)="pop()">
Back to Tab 1, Page 1 (pop)
</button>
<f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f>
</ion-content>
`,
directives: [View, Content]
</ion-content>`,
directives: [Content]
})
class Tab1Page2 {
// TODO change to 'Nav' injection when we're allowed to inject a tab as a nav.
constructor(nav: Tab) {
constructor(nav: NavController) {
this.nav = nav;
}
pop() { this.nav.pop(); }
@ -98,7 +105,7 @@ class Tab1Page2 {
})
class Tab2Page1 {
// TODO change to 'Nav' injection when we're allowed to inject a tab as a nav.
constructor(nav: Tab) {
constructor(nav: NavController) {
this.nav = nav
}
next() {
@ -131,7 +138,7 @@ class Tab2Page1 {
})
class Tab2Page2 {
// TODO change to 'Nav' injection when we're allowed to inject a tab as a nav.
constructor(nav: Tab) {
constructor(nav: NavController) {
this.nav = nav
}
pop() { this.nav.pop() }

View File

@ -1,6 +1,12 @@
// iOS Toolbar
// --------------------------------------------------
$toolbar-order-ios: (
back-button: 1,
primary: 2,
title: 3,
secondary: 4
);
$toolbar-ios-height: 4.4rem !default;
$toolbar-ios-background: #f7f7f8 !default;
@ -16,22 +22,23 @@ $toolbar-ios-button-background-color: transparent !default;
height: $toolbar-ios-height;
background: $toolbar-ios-background;
.toolbar-primary-item {
@include flex-order(2);
.toolbar [side="primary"] {
@include flex-order(map-get($toolbar-order-ios, 'primary'));
}
.toolbar [side="secondary"] {
@include flex-order(map-get($toolbar-order-ios, 'secondary'));
}
.toolbar-title {
@include flex-order(3);
@include flex-order(map-get($toolbar-order-ios, 'title'));
font-size: $toolbar-ios-title-font-size;
text-align: center;
}
.toolbar-inner-title {
opacity: 0; // JS will set to 1 after adjusting alignment
.toolbar-back-button {
@include flex-order(map-get($toolbar-order-ios, 'back-button'));
}
.toolbar-secondary-item {
@include flex-order(4);
.toolbar-title-hide {
opacity: 0;
}
.button {

View File

@ -1,86 +1,201 @@
import {NgElement, Component, View as NgView, Ancestor, Optional} from 'angular2/angular2'
import {BackButton} from 'ionic/components/toolbar/back-button'
import {IonicComponent} from 'ionic/config/component'
import {raf} from 'ionic/util/dom'
import {
NgElement,
Component,
Decorator,
View as NgView,
Viewport,
ViewContainerRef,
onDestroy,
Ancestor,
ElementRef,
onChange,
} from 'angular2/angular2';
import {BackButton} from 'ionic/components/toolbar/back-button';
import {IonicComponent} from 'ionic/config/component';
import {NavController} from 'ionic/components/nav/nav-item';
import {raf} from 'ionic/util/dom';
import {platform} from 'ionic/platform/platform';
@Component({
selector: 'ion-toolbar',
@Viewport({
selector: '[ion-toolbar]',
properties: {
title: 'nav-title'
placement: 'placement'
}
})
@NgView({
template: `
<div class="toolbar-items">
<button class="button back-button toolbar-item" style="display:none"></button>
<div class="toolbar-title">
<div class="toolbar-inner-title">
{{ title }}
<content select="ion-nav-title"></content>
</div>
</div>
<div class="toolbar-item toolbar-primary-item">
<content select="ion-nav-items[side=primary]"></content>
</div>
<div class="toolbar-item toolbar-secondary-item">
<content select="ion-nav-items[side=secondary]"></content>
</div>
</div>
`,
directives: [BackButton]
})
export class Toolbar {
constructor(
@NgElement() ngEle:NgElement
viewContainer: ViewContainerRef,
elementRef: ElementRef,
@Ancestor() navCtrl: NavController,
element: NgElement
// context: Object TODO wait for angular to implement this
) {
this.domElement = ngEle.domElement
this.config = Toolbar.config.invoke(this)
this.viewContainer = viewContainer;
this.elementRef = elementRef;
this.navCtrl = navCtrl;
// TODO: make more better plz
setTimeout(() => {
this.alignTitle()
}, 32)
}
// TODO use config to add these classes
this.viewContainer.domElement.classList.add('toolbar');
this.viewContainer.domElement.classList.add(`toolbar-${platform.getName()}`);
alignTitle() {
let ele = this.domElement
this.titleEle = this.titleEle || ele.querySelector('.toolbar-inner-title')
this.textAlign = this.textAlign || window.getComputedStyle(this.titleEle).textAlign
if (this.textAlign !== 'center') return
// dont bother if the title is already too long
if (this.titleEle.offsetWidth < this.titleEle.scrollWidth) {
if (!this.isTitleVisible) {
this.titleEle.style.opacity = this.isTitleVisible = 1
}
return
}
let leftMargin = this.titleEle.offsetLeft
let rightMargin = ele.offsetWidth - (leftMargin + this.titleEle.offsetWidth)
let centerMargin = leftMargin - rightMargin
this.titleEle.style.margin = `0 ${centerMargin}px 0 0`
raf(() => {
if (this.titleEle.offsetWidth < this.titleEle.scrollWidth) {
this.titleEle.style.margin = ''
this.titleEle.style.textAlign = 'left'
}
if (!this.isTitleVisible) {
this.titleEle.style.opacity = this.isTitleVisible = 1
}
})
}
back() {
if (this.viewport && this.viewport._stack.length) {
this.viewport.pop()
// TODO Make a better way than this
if (/header/i.test(this.viewContainer.domElement.tagName)) {
this.placement = 'top';
} else {
this.placement = 'bottom';
}
}
set placement(pos) {
this.viewContainer.domElement.classList.add(`toolbar-${pos}`);
this._placement = pos;
this.navCtrl.addToolbar(this._placement, this);
this.viewContainer.domElement.setAttribute('placement', pos);
}
onDestroy() {
this.navCtrl.removeToolbar(this);
}
}
new IonicComponent(Toolbar, {})
@Component({
selector: '.toolbar-title',
// events: {
// 'window:resize': 'align()',
// }
})
@NgView({
template: `
<div class="toolbar-inner-title toolbar-title-hide">
<content></content>
</div>`
})
export class ToolbarTitle {
constructor(
element: NgElement
) {
this.domElement = element.domElement;
// TODO find better way to get parent toolbar
let current = this.domElement;
while (current = current.parentNode) {
if (current.classList.contains('toolbar')) {
break;
}
}
this.toolbarElement = current;
this.align();
}
align() {
if (!this.toolbarElement) return;
const toolbarElement = this.toolbarElement;
const titleElement = this._titleElement || (this._titleElement = this.domElement.querySelector('.toolbar-inner-title'));
const style = this._style || (this._style = window.getComputedStyle(titleElement));
const titleOffsetWidth = titleElement.offsetWidth;
const titleOffsetLeft = titleElement.offsetLeft;
const titleScrollWidth = titleElement.scrollWidth;
const toolbarOffsetWidth = toolbarElement.offsetWidth;
//only align if the title is center and if it isn't already overflowing
if (style.textAlign !== 'center' || titleOffsetWidth < titleScrollWidth) {
this._showTitle();
} else {
let rightMargin = toolbarOffsetWidth - (titleOffsetLeft + titleOffsetWidth);
let centerMargin = titleOffsetLeft - rightMargin;
titleElement.style.margin = `0 ${centerMargin}px 0 0`;
raf(() => {
if (titleElement.offsetWidth < titleElement.scrollWidth) {
this.titleElement.style.margin = ''
this.titleElement.style.textAlign = 'left'
}
this._showTitle();
})
}
}
_showTitle() {
if (this._shown) return;
this._shown = true;
this._titleElement.classList.remove('toolbar-title-hide');
}
}
@Decorator({
selector: '[toolbar-create]',
properties: {
'toolbar': 'toolbar-create'
},
})
export class ToolbarContainer {
constructor(
viewContainer: ViewContainerRef,
element: NgElement
) {
this.viewContainer = viewContainer;
this.domElement = element.domElement;
}
set toolbar(bar: Toolbar) {
if (bar) {
// TODO create with correct context
this.viewContainer.create(-1, bar.viewContainer._defaultProtoView, bar.elementRef.elementInjector);
console.log('creating viewportContainer', performance.now())
}
}
}
// @Component({
// selector: 'ion-toolbar',
// properties: {
// title: 'nav-title'
// }
// })
// @NgView({
// template: `
// <div class="toolbar-items">
// <button class="button back-button toolbar-item" style="display:none"></button>
// <div class="toolbar-title">
// <div class="toolbar-inner-title">
// {{ title }}
// <content select=".toolbar-title"></content>
// </div>
// </div>
// <div class="toolbar-item toolbar-primary-item">
// <content select=".primary"></content>
// </div>
// <div class="toolbar-item toolbar-secondary-item">
// <content select=".secondary"></content>
// </div>
// </div>
// `,
// directives: [BackButton]
// })
// export class Toolbar {
// constructor(
// @NgElement() ngEle:NgElement
// ) {
// this.domElement = ngEle.domElement;
// this.config = Toolbar.config.invoke(this);
// // TODO: make more better plz
// setTimeout(() => {
// this.alignTitle()
// }, 32);
// }
// back() {
// if (this.viewport && this.viewport._stack.length) {
// this.viewport.pop()
// }
// }
// }
// new IonicComponent(Toolbar, {})

View File

@ -3,7 +3,16 @@
// --------------------------------------------------
$toolbar-background: #f7f7f8 !default;
$toolbar-order-core: (
back-button: 1,
title: 2,
primary: 3,
secondary: 4
);
$toolbar-primary-flex-order: 1;
$toolbar-primary-flex-order: 1;
$toolbar-title-flex-order: 5;
$toolbar-secondary-flex-order: 10;
.toolbar {
@include flex-display();
@ -13,48 +22,28 @@ $toolbar-background: #f7f7f8 !default;
background: $toolbar-background;
}
.toolbar-top {
.toolbar[placement="top"] {
@include flex-order($flex-order-toolbar-top);
}
.toolbar-bottom {
.toolbar[placement="bottom"] {
@include flex-order($flex-order-toolbar-bottom);
}
.toolbar-items {
@include flex-display();
@include flex(1);
@include flex-justify-content(space-between);
// buttons are primary by default
.toolbar .button,
.toolbar [side="primary"] {
@include flex-order(map-get($toolbar-order-core, 'primary'));
}
.toolbar-item,
.toolbar-item ion-nav-items {
@include flex-display();
@include flex-align-items(center);
}
.toolbar .button {
background: transparent;
border: none;
}
.back-button {
@include flex-display();
@include flex-order(1);
}
.back-button-icon {
margin-right: 5px;
min-width: 12px;
.toolbar [side="secondary"] {
@include flex-order(map-get($toolbar-order-core, 'secondary'));
}
.toolbar-title {
@include flex-display();
@include flex(1);
@include flex-align-items(center);
@include flex-order(2);
@include flex-order(map-get($toolbar-order-core, 'title'));
}
.toolbar-inner-title {
width: 100%;
padding: 0 15px;
@ -62,12 +51,16 @@ $toolbar-background: #f7f7f8 !default;
white-space: nowrap;
text-overflow: ellipsis;
}
.toolbar-primary-item {
@include flex-order(3);
// Override [ion-app] h1 margin-top
h1.toolbar-title {
margin: 0;
}
.toolbar-secondary-item {
@include flex-order(4);
.toolbar-back-button {
@include flex-order(map-get($toolbar-order-core, 'back-button'));
}
.toolbar .button {
background: transparent;
border: none;
}

View File

@ -1,4 +1,5 @@
import {NgElement} from 'angular2/angular2'
// HACKYFILLS (hack + polyfill)
import {NgElement, ViewContainerRef} from 'angular2/angular2'
Object.defineProperties(NgElement.prototype, {
domElement: {
get: function() {
@ -7,6 +8,14 @@ Object.defineProperties(NgElement.prototype, {
}
});
Object.defineProperties(ViewContainerRef.prototype, {
domElement: {
get: function() {
return this._defaultProtoView.render.delegate.element;
}
}
});
export * from 'ionic/components'
export * from 'ionic/platform/platform'
export * from 'ionic/routing/router'

View File

@ -41,7 +41,7 @@
"components/layout/layout",
"components/list/list",
"components/modal/modal",
"components/nav-pane/nav-pane",
"components/nav/nav-item",
"components/radio/radio",
"components/search-bar/search-bar",
"components/switch/switch",

View File

@ -33,7 +33,7 @@
"yargs": "^3.6.0"
},
"dependencies": {
"angular2": "2.0.0-alpha.20",
"angular2": "2.0.0-alpha.21",
"es6-module-loader": "^0.16.5",
"hammerjs": "^2.0.4",
"rtts_assert": "^2.0.0-alpha.20",