Merge branch '2.0' into pr/6203

This commit is contained in:
Brandy Carney
2016-04-21 19:39:57 -04:00
87 changed files with 1897 additions and 524 deletions

View File

@ -29,8 +29,5 @@
"components/virtual-scroll/virtual-scroll";
// Ionicons (to be replaced with SVGs)
$ionicons: true !default;
@if ($ionicons) {
@import "fonts/ionicons";
}
// Ionicons
@import "fonts/ionicons";

View File

@ -27,6 +27,7 @@
"components/select/select.ios",
"components/tabs/tabs.ios",
"components/toggle/toggle.ios",
"components/toast/toast.ios",
"components/toolbar/toolbar.ios";

View File

@ -27,6 +27,7 @@
"components/select/select.md",
"components/tabs/tabs.md",
"components/toggle/toggle.md",
"components/toast/toast.md",
"components/toolbar/toolbar.md";

View File

@ -4,7 +4,6 @@ export * from './components/app/id'
export * from './components/action-sheet/action-sheet'
export * from './components/alert/alert'
export * from './components/badge/badge'
export * from './components/blur/blur'
export * from './components/button/button'
export * from './components/checkbox/checkbox'
export * from './components/content/content'
@ -47,5 +46,6 @@ export * from './components/tabs/tabs'
export * from './components/tabs/tab'
export * from './components/tap-click/tap-click'
export * from './components/toggle/toggle'
export * from './components/toast/toast'
export * from './components/toolbar/toolbar'
export * from './components/virtual-scroll/virtual-scroll'

View File

@ -27,6 +27,7 @@
"components/select/select.wp",
"components/tabs/tabs.wp",
"components/toggle/toggle.wp",
"components/toast/toast.wp",
"components/toolbar/toolbar.wp";

View File

@ -91,7 +91,7 @@ ion-action-sheet {
.action-sheet-selected {
font-weight: bold;
background: white;
background: #fff;
}
.action-sheet-destructive {

View File

@ -70,6 +70,10 @@ $action-sheet-md-icon-margin: 0 28px 0 0 !default;
&:last-child .action-sheet-button {
margin-bottom: $action-sheet-md-group-margin-bottom;
}
.button-inner {
justify-content: flex-start;
}
}
.action-sheet-selected {

View File

@ -75,6 +75,62 @@ import {ViewController} from '../nav/view-controller';
* }
* ```
*
*
* ### Dismissing And Async Navigation
*
* After an action sheet has been dismissed, the app may need to also transition
* to another page depending on the handler's logic. However, because multiple
* transitions were fired at roughly the same time, it's difficult for the
* nav controller to cleanly animate multiple transitions that may
* have been kicked off asynchronously. This is further described in the
* [`Nav Transition Promises`](../../nav/NavController) section. For action sheets,
* this means it's best to wait for the action sheet to finish its transition
* out before starting a new transition on the same nav controller.
*
* In the example below, after the button has been clicked, its handler
* waits on async operation to complete, *then* it uses `pop` to navigate
* back a page in the same stack. The potential problem is that the async operation
* may have been completed before the action sheet has even finished its transition
* out. In this case, it's best to ensure the action sheet has finished its transition
* out first, *then* start the next transition.
*
* ```ts
* let actionSheet = ActionSheet.create({
* title: 'Hello',
* buttons: [{
* text: 'Ok',
* handler: () => {
* // user has clicked the action sheet button
* // begin the action sheet's dimiss transition
* let navTransition = actionSheet.dismiss();
*
* // start some async method
* someAsyncOperation().then(() => {
* // once the async operation has completed
* // then run the next nav transition after the
* // first transition has finished animating out
*
* navTransition.then(() => {
* this.nav.pop();
* });
* });
* return false;
* }
* }]
* });
*
* this.nav.present(actionSheet);
* ```
*
* It's important to note that the handler returns `false`. A feature of
* button handlers is that they automatically dismiss the action sheet when their button
* was clicked, however, we'll need more control regarding the transition. Because
* the handler returns `false`, then the action sheet does not automatically dismiss
* itself. Instead, you now have complete control of when the action sheet has finished
* transitioning, and the ability to wait for the action sheet to finish transitioning
* out before starting a new transition.
*
*
* @demo /docs/v2/demos/action-sheet/
* @see {@link /docs/v2/components#action-sheets ActionSheet Component Docs}
*/
@ -166,14 +222,14 @@ export class ActionSheet extends ViewController {
'<div class="action-sheet-group">' +
'<div class="action-sheet-title" id="{{hdrId}}" *ngIf="d.title">{{d.title}}</div>' +
'<div class="action-sheet-sub-title" id="{{descId}}" *ngIf="d.subTitle">{{d.subTitle}}</div>' +
'<button (click)="click(b)" *ngFor="#b of d.buttons" class="action-sheet-button disable-hover" [ngClass]="b.cssClass">' +
'<button category="action-sheet-button" (click)="click(b)" *ngFor="#b of d.buttons" class="disable-hover" [ngClass]="b.cssClass">' +
'<ion-icon [name]="b.icon" *ngIf="b.icon" class="action-sheet-icon"></ion-icon> ' +
'{{b.text}}' +
'<ion-button-effect></ion-button-effect>' +
'</button>' +
'</div>' +
'<div class="action-sheet-group" *ngIf="d.cancelButton">' +
'<button (click)="click(d.cancelButton)" class="action-sheet-button action-sheet-cancel disable-hover" [ngClass]="d.cancelButton.cssClass">' +
'<button category="action-sheet-button" (click)="click(d.cancelButton)" class="action-sheet-cancel disable-hover" [ngClass]="d.cancelButton.cssClass">' +
'<ion-icon [name]="d.cancelButton.icon" *ngIf="d.cancelButton.icon" class="action-sheet-icon"></ion-icon> ' +
'{{d.cancelButton.text}}' +
'<ion-button-effect></ion-button-effect>' +
@ -234,7 +290,7 @@ class ActionSheetCmp {
// deprecated warning
if (button.style) {
console.warn('Alert "style" property has been renamed to "role"');
console.warn('Action sheet "style" property has been renamed to "role"');
button.role = button.style;
}

View File

@ -73,6 +73,10 @@ $action-sheet-wp-icon-margin: 0 16px 0 0 !default;
&:last-child .action-sheet-button {
margin-bottom: $action-sheet-wp-group-margin-bottom;
}
.button-inner {
justify-content: flex-start;
}
}
.action-sheet-selected {

View File

@ -58,7 +58,7 @@ $alert-ios-radio-label-padding: 13px !default;
$alert-ios-radio-min-width: 30px !default;
$alert-ios-radio-icon-top: 13px !default;
$alert-ios-radio-icon-top: -7px !default;
$alert-ios-radio-icon-left: 7px !default;
$alert-ios-radio-icon-width: 6px !default;
$alert-ios-radio-icon-height: 12px !default;

View File

@ -57,7 +57,7 @@ $alert-md-list-border-bottom: $alert-md-list-border-top !default
$alert-md-radio-label-padding: 13px 26px !default;
$alert-md-radio-top: 13px !default;
$alert-md-radio-top: 0 !default;
$alert-md-radio-left: 13px !default;
$alert-md-radio-width: 16px !default;
$alert-md-radio-height: 16px !default;
@ -78,7 +78,7 @@ $alert-md-radio-icon-transition: transform 280ms cubic-bezier(.4, 0
$alert-md-checkbox-label-padding: 13px 26px !default;
$alert-md-checkbox-top: 13px !default;
$alert-md-checkbox-top: 0 !default;
$alert-md-checkbox-left: 13px !default;
$alert-md-checkbox-width: 16px !default;
$alert-md-checkbox-height: 16px !default;
@ -337,4 +337,8 @@ $alert-md-checkbox-icon-transform: rotate(45deg) !default;
&.activated {
background-color: $alert-md-button-background-color-activated;
}
.button-inner {
justify-content: $alert-md-button-group-justify-content;
}
}

View File

@ -128,6 +128,62 @@ import {ViewController} from '../nav/view-controller';
* }
* ```
*
*
* ### Dismissing And Async Navigation
*
* After an alert has been dismissed, the app may need to also transition
* to another page depending on the handler's logic. However, because multiple
* transitions were fired at roughly the same time, it's difficult for the
* nav controller to cleanly animate multiple transitions that may
* have been kicked off asynchronously. This is further described in the
* [`Nav Transition Promises`](../../nav/NavController) section. For alerts,
* this means it's best to wait for the alert to finish its transition
* out before starting a new transition on the same nav controller.
*
* In the example below, after the alert button has been clicked, its handler
* waits on async operation to complete, *then* it uses `pop` to navigate
* back a page in the same stack. The potential problem is that the async operation
* may have been completed before the alert has even finished its transition
* out. In this case, it's best to ensure the alert has finished its transition
* out first, *then* start the next transition.
*
* ```ts
* let alert = Alert.create({
* title: 'Hello',
* buttons: [{
* text: 'Ok',
* handler: () => {
* // user has clicked the alert button
* // begin the alert's dimiss transition
* let navTransition = alert.dismiss();
*
* // start some async method
* someAsyncOperation().then(() => {
* // once the async operation has completed
* // then run the next nav transition after the
* // first transition has finished animating out
*
* navTransition.then(() => {
* this.nav.pop();
* });
* });
* return false;
* }
* }]
* });
*
* this.nav.present(alert);
* ```
*
* It's important to note that the handler returns `false`. A feature of
* button handlers is that they automatically dismiss the alert when their button
* was clicked, however, we'll need more control regarding the transition. Because
* the handler returns `false`, then the alert does not automatically dismiss
* itself. Instead, you now have complete control of when the alert has finished
* transitioning, and the ability to wait for the alert to finish transitioning
* out before starting a new transition.
*
*
* @demo /docs/v2/demos/alert/
*/
export class Alert extends ViewController {
@ -242,7 +298,7 @@ export class Alert extends ViewController {
* | cssClass | `string` | An additional CSS class for the button |
* | role | `string` | The buttons role, null or `cancel` |
*
* @param {object} opts Alert. See the tabel above
* @param {object} opts Alert. See the table above
*/
static create(opts: AlertOptions = {}) {
return new Alert(opts);
@ -267,7 +323,7 @@ export class Alert extends ViewController {
'<template ngSwitchWhen="radio">' +
'<div class="alert-radio-group" role="radiogroup" [attr.aria-labelledby]="hdrId" [attr.aria-activedescendant]="activeId">' +
'<button *ngFor="#i of d.inputs" (click)="rbClick(i)" [attr.aria-checked]="i.checked" [attr.id]="i.id" class="alert-tappable alert-radio" role="radio">' +
'<button category="alert-radio-button" *ngFor="#i of d.inputs" (click)="rbClick(i)" [attr.aria-checked]="i.checked" [attr.id]="i.id" class="alert-tappable alert-radio" role="radio">' +
'<div class="alert-radio-icon"><div class="alert-radio-inner"></div></div>' +
'<div class="alert-radio-label">' +
'{{i.label}}' +
@ -278,7 +334,7 @@ export class Alert extends ViewController {
'<template ngSwitchWhen="checkbox">' +
'<div class="alert-checkbox-group">' +
'<button *ngFor="#i of d.inputs" (click)="cbClick(i)" [attr.aria-checked]="i.checked" class="alert-tappable alert-checkbox" role="checkbox">' +
'<button category="alert-checkbox-button" *ngFor="#i of d.inputs" (click)="cbClick(i)" [attr.aria-checked]="i.checked" class="alert-tappable alert-checkbox" role="checkbox">' +
'<div class="alert-checkbox-icon"><div class="alert-checkbox-inner"></div></div>' +
'<div class="alert-checkbox-label">' +
'{{i.label}}' +
@ -297,7 +353,7 @@ export class Alert extends ViewController {
'</div>' +
'<div class="alert-button-group" [ngClass]="{vertical: d.buttons.length>2}">' +
'<button *ngFor="#b of d.buttons" (click)="btnClick(b)" [ngClass]="b.cssClass" class="alert-button">' +
'<button category="alert-button" *ngFor="#b of d.buttons" (click)="btnClick(b)" [ngClass]="b.cssClass">' +
'{{b.text}}' +
'<ion-button-effect></ion-button-effect>' +
'</button>' +

View File

@ -59,7 +59,7 @@ $alert-wp-radio-border-color: $input-wp-border-color !default;
$alert-wp-radio-label-padding: 13px 26px !default;
$alert-wp-radio-top: 13px !default;
$alert-wp-radio-top: 0 !default;
$alert-wp-radio-left: 13px !default;
$alert-wp-radio-width: 16px !default;
$alert-wp-radio-height: 16px !default;
@ -76,7 +76,7 @@ $alert-wp-radio-icon-border-radius: $alert-wp-radio-border-radius !de
$alert-wp-checkbox-label-padding: 13px 26px !default;
$alert-wp-checkbox-top: 13px !default;
$alert-wp-checkbox-top: 0 !default;
$alert-wp-checkbox-left: 13px !default;
$alert-wp-checkbox-width: 16px !default;
$alert-wp-checkbox-height: 16px !default;

View File

@ -1,4 +1,4 @@
import { Alert, NavController, App, Page } from 'ionic-angular/index';
import { Alert, Loading, NavController, App, Page } from 'ionic-angular/index';
import { FORM_DIRECTIVES, FormBuilder, ControlGroup, Validators } from 'angular2/common';
@ -51,6 +51,9 @@ export class E2EPage {
</button>
</div>
</form>
<p>
<button block (click)="doFastPop()">Fast Loading Dismiss, Nav Pop</button>
</p>
</ion-content>
`
})
@ -107,6 +110,46 @@ class AnotherPage {
this.nav.present(alert);
}
doFastPop() {
let alert = Alert.create({
title: 'Async Nav Transition',
message: 'This is an example of dismissing an alert, then quickly starting another transition on the same nav controller.',
buttons: [{
text: 'Ok',
handler: () => {
// present a loading indicator
let loading = Loading.create({
content: 'Loading...'
});
this.nav.present(loading);
// start an async operation
setTimeout(() => {
// the async operation has completed
// dismiss the loading indicator
loading.dismiss();
// begin dismissing the alert
alert.dismiss().then(() => {
// after the alert has been dismissed
// then you can do another nav transition
this.nav.pop();
});
}, 100);
// return false so the alert doesn't automatically
// dismissed itself. Instead we're manually
// handling the dismiss logic above so that we
// can wait for the alert to finish it's dismiss
// transition before starting another nav transition
// on the same nav controller
return false;
}
}]
});
this.nav.present(alert);
}
}

View File

@ -1,4 +1,4 @@
import {Injectable} from 'angular2/core';
import {Injectable, Injector} from 'angular2/core';
import {Title} from 'angular2/platform/browser';
import {Config} from '../../config/config';
@ -19,6 +19,7 @@ export class IonicApp {
private _titleSrv: Title = new Title();
private _isProd: boolean = false;
private _rootNav: any = null;
private _appInjector: Injector;
constructor(
private _config: Config,
@ -199,4 +200,19 @@ export class IonicApp {
return this._cmps[id];
}
/**
* Set the global app injector that contains references to all of the instantiated providers
* @param injector
*/
setAppInjector(injector: Injector) {
this._appInjector = injector;
}
/**
* Get an instance of the global app injector that contains references to all of the instantiated providers
* @returns {Injector}
*/
getAppInjector(): Injector {
return this._appInjector;
}
}

View File

@ -3,6 +3,7 @@ import {AppViewManager, ElementRef, Directive, Renderer, Input} from 'angular2/c
import {IonicApp} from './app';
/**
* @private
* @name Id
* @description
* The `id` attribute is an easy way to identify unique components in an app and access them

View File

@ -151,6 +151,12 @@ textarea {
touch-action: manipulation;
}
a ion-label,
button ion-label,
[tappable] ion-label {
pointer-events: none;
}
button {
border: 0;
font-family: inherit;

View File

@ -89,9 +89,10 @@ describe('IonicApp', () => {
var app: IonicApp;
var config: Config;
var platform: Platform;
var _cd: any;
function mockNav(): Nav {
return new Nav(null,null,null,config,null,null,null,null,null,null);
return new Nav(null, null, null, config, null, null, null, null, null, null);
}
function mockTabs(): Tabs {
@ -99,13 +100,17 @@ describe('IonicApp', () => {
}
function mockTab(parentTabs: Tabs): Tab {
return new Tab(parentTabs,null,config,null,null,null,null,null,null);
return new Tab(parentTabs, app, config, null, null, null, null, null, null, _cd);
}
beforeEach(() => {
config = new Config();
platform = new Platform();
app = new IonicApp(config, null, platform);
_cd = {
reattach: function(){},
detach: function(){}
};
});
});

View File

@ -1,26 +0,0 @@
import {Directive, Renderer, ElementRef} from 'angular2/core';
/**
* @name Blur
* @description
* The blur attribute applies the CSS blur attribute to an element. Safari only.
*
* @usage
* ```html
* <ion-card blur>
* This card will blur the content behind it.
* </ion-card>
* ```
*
* @demo /docs/v2/demos/blur/
* @private
*/
@Directive({
selector: '[blur]'
})
export class Blur {
constructor(private _elementRef: ElementRef, private _renderer: Renderer) {
_renderer.setElementStyle(_elementRef.nativeElement, '-webkit-backdrop-filter', 'blur(10px)');
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 517 KiB

View File

@ -1,10 +0,0 @@
import {App} from 'ionic-angular';
@App({
templateUrl: 'main.html'
})
class E2EApp {
constructor() {
}
}

View File

@ -1,34 +0,0 @@
<ion-toolbar>
<ion-title>Blur</ion-title>
</ion-toolbar>
<ion-content>
<div class="blur-content">
<ion-card blur class="blur-card">
<ion-card-header>
Card
</ion-card-header>
<ion-card-content>
This card will blur the content behind it.
</ion-card-content>
</ion-card>
</div>
</ion-content>
<style>
.blur-content {
position: absolute;
text-align:center;
background: url('bg.jpg') no-repeat transparent;
background-size: cover;
height: 100%;
width: 100%;
}
.blur-card,
.blur-card ion-card-header {
background: transparent;
color: white;
}
</style>

View File

@ -58,6 +58,11 @@ export class Button {
*/
isItem: boolean;
/**
* @input {string} The category of the button.
*/
@Input() category: string;
/**
* @input {string} Large button.
*/
@ -185,6 +190,16 @@ export class Button {
this._readAttrs(element);
}
/**
* @private
*/
ngOnInit() {
// If the button has a role applied to it
if (this.category) {
this.setRole(this.category);
}
}
/**
* @private
*/

View File

@ -55,6 +55,7 @@ const CHECKBOX_VALUE_ACCESSOR = new Provider(
'</div>' +
'<button role="checkbox" ' +
'type="button" ' +
'category="item-cover" ' +
'[id]="id" ' +
'[attr.aria-checked]="_checked" ' +
'[attr.aria-labelledby]="_labelId" ' +

View File

@ -268,7 +268,7 @@ export class Content extends Ion {
* @returns {Promise} Returns a promise which is resolved when the scroll has completed.
*/
scrollToTop(duration: number = 300) {
return this.scrollTo(0, 0, duration);
return this._scroll.scrollToTop(duration);
}
/**
@ -287,6 +287,15 @@ export class Content extends Ion {
this._scroll.setTop(top);
}
/**
* Scroll to the bottom of the content component.
* @param {number} [duration] Duration of the scroll animation in milliseconds. Defaults to `300`.
* @returns {Promise} Returns a promise which is resolved when the scroll has completed.
*/
scrollToBottom(duration: number = 300) {
return this._scroll.scrollToBottom(duration);
}
/**
* @private
*/

View File

@ -434,7 +434,7 @@ export class InputBase {
/**
* @private
* Angular2 Forms API method called by the the view (NgControl) to register
* Angular2 Forms API method called by the view (NgControl) to register
* the onTouched event handler that marks model (Control) as touched.
* @param {Function} fn onTouched event handler.
*/

View File

@ -44,7 +44,7 @@ export class NativeInput {
function docTouchEnd(ev) {
var tapped: HTMLElement = ev.target;
if (tapped && self.element()) {
if (tapped.tagName !== "INPUT" && tapped.tagName !== "TEXTAREA" && !tapped.classList.contains('input-cover')) {
if (tapped.tagName !== 'INPUT' && tapped.tagName !== 'TEXTAREA' && !tapped.classList.contains('input-cover')) {
self.element().blur();
}
}
@ -211,4 +211,4 @@ export class NextInput {
this.focused.emit(true);
}
}
}

View File

@ -88,8 +88,18 @@
<button outline item-right (click)="testClick($event)">View</button>
</ion-item>
<button ion-item *ngFor="#data of [0,1,2,3,4]; #i = index" [class.activated]="i == 1">
<button ion-item *ngFor="#data of [0,1,2,3,4]; #i = index" [class.activated]="i == 1" (click)="testClick($event)">
<ion-avatar item-left>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==">
</ion-avatar>
<h3>ng-for {{i}}</h3>
<ion-badge item-right>260k</ion-badge>
</button>
</ion-content>
<style>
img {
height: 100px;
}
</style>

View File

@ -7,9 +7,9 @@ import {Directive, ElementRef, Renderer, Input, Optional, Attribute} from 'angul
* Labels are placed inside of an `ion-item` element and can be used
* to describe an `ion-input`, `ion-toggle`, `ion-checkbox`, and more.
*
* @property [fixed] - a persistant label that sits next the the input
* @property [floating] - a label that will float about the input if the input is empty of looses focus
* @property [stacked] - A stacked label will always appear on top of the input
* @property [fixed] - A persistant label that sits next the input.
* @property [floating] - A label that will float about the input if the input is empty of looses focus.
* @property [stacked] - A stacked label will always appear on top of the input.
*
* @usage

View File

@ -52,11 +52,12 @@ export class List extends Ion {
* Enable sliding items if your page has them
*
* ```ts
* import {Page, List} from 'ionic-angular';
* import {ViewChild} from 'angular2/core';
* @Page...
* export class MyClass {
* constructor(app: IonicApp){
* this.app = app;
* this.list = this.app.getComponent('my-list');
* }
* @ViewChild(List) list: List;
* constructor(){}
* stopSliding(){
* this.list.enableSlidingItems(false);
* }
@ -86,13 +87,12 @@ export class List extends Ion {
* Enable sliding items if your page has
*
* ```ts
* import {Page, List} from 'ionic-angular';
* import {ViewChild} from 'angular2/core';
* @Page...
* export class MyClass {
* constructor(app: IonicApp){
* this.app = app;
* this.list = this.app.getComponent('my-list');
* }
* // Here we have some method that will close the items
* // when called
* @ViewChild(List) list: List;
* constructor(){}
* closeItmes(){
* this.list.closeSlidingItems();
* }

View File

@ -36,7 +36,9 @@ import {ViewController} from '../nav/view-controller';
* the `duration` of the loading options. By default the loading indicator
* will show even during page changes, but this can be disabled by setting
* `dismissOnPageChange` to `true`. To dismiss the loading indicator after
* creation, call the `dismiss()` method on the Loading instance.
* creation, call the `dismiss()` method on the Loading instance. The
* `onDismiss` function can be called to perform an action after the loading
* indicator is dismissed.
*
* ### Limitations
* The element is styled to appear on top of other content by setting its
@ -71,6 +73,10 @@ import {ViewController} from '../nav/view-controller';
* duration: 5000
* });
*
* loading.onDismiss(() => {
* console.log('Dismissed loading');
* });
*
* this.nav.present(loading);
* }
*

View File

@ -13,6 +13,10 @@ class E2EPage {
duration: 1000
});
loading.onDismiss(() => {
console.log('Dismissed loading');
});
this.nav.present(loading);
}

View File

@ -37,7 +37,7 @@ import {ViewController} from './view-controller';
* specific NavController, most times you will inject and use a reference to the
* nearest NavController to manipulate the navigation stack.
*
* <h3 id="injecting_nav_controller">Injecting NavController</h3>
* ### Injecting NavController
* Injecting NavController will always get you an instance of the nearest
* NavController, regardless of whether it is a Tab or a Nav.
*
@ -58,7 +58,8 @@ import {ViewController} from './view-controller';
* }
* ```
*
* <h2 id="creating_pages">Page creation</h2>
*
* ## Page creation
* _For more information on the `@Page` decorator see the [@Page API
* reference](../../../decorators/Page/)._
*
@ -73,7 +74,7 @@ import {ViewController} from './view-controller';
* [pop()](#pop) or [setRoot()](#setRoot)).
*
*
* <h2 id="Lifecycle">Lifecycle events</h2>
* ## Lifecycle events
* Lifecycle events are fired during various stages of navigation. They can be
* defined in any `@Page` decorated component class.
*
@ -91,15 +92,53 @@ import {ViewController} from './view-controller';
* }
* ```
*
* | Page Event | Description |
* |--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
* | `onPageLoaded` | Runs when the page has loaded. This event only happens once per page being created and added to the DOM. If a page leaves but is cached, then this event will not fire again on a subsequent viewing. The `onPageLoaded` event is good place to put your setup code for the page. |
* | `onPageWillEnter` | Runs when the page is about to enter and become the active page. |
* | `onPageDidEnter` | Runs when the page has fully entered and is now the active page. This event will fire, whether it was the first load or a cached page. |
* | `onPageWillLeave` | Runs when the page is about to leave and no longer be the active page. |
* | `onPageDidLeave` | Runs when the page has finished leaving and is no longer the active page. |
* | `onPageWillUnload` | Runs when the page is about to be destroyed and have its elements removed. |
* | `onPageDidUnload` | Runs after the page has been destroyed and its elements have been removed.
*
*
* - `onPageLoaded` - Runs when the page has loaded. This event only happens once per page being created and added to the DOM. If a page leaves but is cached, then this event will not fire again on a subsequent viewing. The `onPageLoaded` event is good place to put your setup code for the page.
* - `onPageWillEnter` - Runs when the page is about to enter and become the active page.
* - `onPageDidEnter` - Runs when the page has fully entered and is now the active page. This event will fire, whether it was the first load or a cached page.
* - `onPageWillLeave` - Runs when the page is about to leave and no longer be the active page.
* - `onPageDidLeave` - Runs when the page has finished leaving and is no longer the active page.
* - `onPageWillUnload` - Runs when the page is about to be destroyed and have its elements removed.
* - `onPageDidUnload` - Runs after the page has been destroyed and its elements have been removed.
* ## Nav Transition Promises
*
* Navigation transitions are asynchronous, meaning they take a few moments to finish, and
* the duration of a transition could be any number. In most cases the async nature of a
* transition doesn't cause any problems and the nav controller is pretty good about handling
* which transition was the most recent when multiple transitions have been kicked off.
* However, when an app begins firing off many transitions, on the same stack at
* *roughly* the same time, the nav controller can start to get lost as to which transition
* should be finishing, and which transitions should not be animated.
*
* In cases where an app's navigation can be altered by other async tasks, which may or
* may not take a long time, it's best to rely on each nav transition's returned
* promise. So instead of firing and forgetting multiple `push` or `pop` nav transitions,
* it's better to fire the next nav transition when the previous one has finished.
*
* In the example below, after the async operation has completed, we then want to transition
* to another page. Where the potential problem comes in, is that if the async operation
* completed 100ms after the first transition started, then kicking off another transition
* halfway through the first transition ends up with a janky animation. Instead, it's best
* to always ensure the first transition has already finished before starting the next.
*
* ```ts
* // begin the first transition
* let navTransition = this.nav.push(SomePage);
*
* // start an async call, we're not sure how long it'll take
* someAsyncOperation().then(() => {
* // incase the async operation completed faster than the time
* // it took to finish the first transition, this logic should
* // always ensure that the previous transition has resolved
* // first before kicking off the next transition
* navTransition.then(() => {
* this.nav.push(AnotherPage);
* });
* });
* ```
*
* @see {@link /docs/v2/components#navigation Navigation Component Docs}
*/
@ -178,6 +217,9 @@ export class NavController extends Ion {
]);
}
/**
* @private
*/
setPortal(val: Portal) {
this._portal = val;
}
@ -1357,7 +1399,7 @@ export class NavController extends Ion {
}
this._views.length = 0;
if (this.parent) {
if (this.parent && this.parent.unregisterChildNav) {
this.parent.unregisterChildNav(this);
}
}

View File

@ -1,13 +1,12 @@
import {Component, Type, ViewChild} from 'angular2/core';
import {App, NavController, Alert, Content} from 'ionic-angular';
import {Page, Config, IonicApp} from 'ionic-angular';
import {NavParams, ViewController, IONIC_DIRECTIVES} from 'ionic-angular';;
import {NavParams, ViewController} from 'ionic-angular';;
@Component({
selector: 'my-cmp',
template: `<p>My Custom Component Test <ion-icon name="star"></ion-icon></p>`,
directives: [IONIC_DIRECTIVES]
template: `<p>My Custom Component Test <ion-icon name="star"></ion-icon></p>`
})
class MyCmpTest{}
@ -49,6 +48,7 @@ class MyCmpTest{}
<button ion-item (click)="quickPush()">New push during transition</button>
<button ion-item (click)="quickPop()">New pop during transition</button>
<button ion-item (click)="reload()">Reload</button>
<button ion-item (click)="scrollToBottom()">Scroll to bottom</button>
<button *ngFor="#i of pages" ion-item (click)="pushPrimaryHeaderPage()">Page {{i}}</button>
<button ion-item (click)="content.scrollToTop()">Scroll to top</button>
</ion-list>
@ -122,6 +122,10 @@ class FirstPage {
scrollToTop() {
this.content.scrollToTop();
}
scrollToBottom() {
this.content.scrollToBottom(1000);
}
}

View File

@ -91,7 +91,7 @@ class ToolbarBackground {
selector: 'ion-navbar',
template:
'<div class="toolbar-background"></div>' +
'<button class="back-button bar-button bar-button-default" [hidden]="_hideBb">' +
'<button category="bar-button" class="back-button" [hidden]="_hideBb">' +
'<span class="button-inner">' +
'<ion-icon class="back-button-icon" [name]="_bbIcon"></ion-icon>' +
'<span class="back-button-text">' +

View File

@ -37,6 +37,7 @@ import {RadioGroup} from './radio-group';
'</div>' +
'<button role="radio" ' +
'type="button" ' +
'category="item-cover" ' +
'[id]="id" ' +
'[attr.aria-checked]="_checked" ' +
'[attr.aria-labelledby]="_labelId" ' +

View File

@ -65,7 +65,7 @@ const SELECT_VALUE_ACCESSOR = new Provider(
* <ion-option>Pepperoni</ion-option>
* <ion-option>Sausage</ion-option>
* </ion-select>
* <ion-item>
* </ion-item>
* ```
*
* ### Alert Buttons
@ -109,6 +109,7 @@ const SELECT_VALUE_ACCESSOR = new Provider(
'</div>' +
'<button aria-haspopup="true" ' +
'[id]="id" ' +
'category="item-cover" ' +
'[attr.aria-labelledby]="_labelId" ' +
'[attr.aria-disabled]="_disabled" ' +
'class="item-cover">' +
@ -255,7 +256,7 @@ export class Select {
this.onChange(input.value);
this.change.emit(input.value);
}
}
};
}));
alertOptions.cssClass = 'select-action-sheet';
@ -273,7 +274,7 @@ export class Select {
label: input.text,
value: input.value,
checked: input.checked
}
};
});
// create the alert instance from our built up alertOptions

View File

@ -5,10 +5,10 @@
// Applied by the showWhen directive
.hidden-show-when {
display: none;
display: none !important;
}
// Applied by the hideWhen directive
.hidden-hide-when {
display: none;
display: none !important;
}

View File

@ -62,7 +62,15 @@ export class DisplayWhen {
* @description
* The `showWhen` attribute takes a string that represents a platform or screen orientation.
* The element the attribute is added to will only be shown when that platform or screen orientation is active.
* Complements the [hideWhen attribute](../HideWhen).
*
* Complements the [hideWhen attribute](../HideWhen). If the `showWhen` attribute is used on an
* element that also has the `hideWhen` attribute, the element will not show if `hideWhen` evaluates
* to `true` or `showWhen` evaluates to `false`. If the `hidden` attribute is also added, the element
* will not show if `hidden` evaluates to `true`.
*
* View the [Platform API docs](../../../platform/Platform) for more information on the different
* platforms you can use.
*
* @usage
* ```html
* <div showWhen="android">
@ -87,6 +95,7 @@ export class DisplayWhen {
* ```
* @demo /docs/v2/demos/show-when/
* @see {@link ../HideWhen HideWhen API Docs}
* @see {@link ../../../platform/Platform Platform API Docs}
*/
@Directive({
selector: '[showWhen]',
@ -111,7 +120,15 @@ export class ShowWhen extends DisplayWhen {
* @description
* The `hideWhen` attribute takes a string that represents a plaform or screen orientation.
* The element the attribute is added to will only be hidden when that platform or screen orientation is active.
* Complements the [showWhen attribute](../ShowWhen).
*
* Complements the [showWhen attribute](../ShowWhen). If the `hideWhen` attribute is used on an
* element that also has the `showWhen` attribute, the element will not show if `hideWhen` evaluates
* to `true` or `showWhen` evaluates to `false`. If the `hidden` attribute is also added, the element
* will not show if `hidden` evaluates to `true`.
*
* View the [Platform API docs](../../../platform/Platform) for more information on the different
* platforms you can use.
*
* @usage
* ```html
* <div hideWhen="android">
@ -137,7 +154,8 @@ export class ShowWhen extends DisplayWhen {
*
* @demo /docs/v2/demos/hide-when/
* @see {@link ../ShowWhen ShowWhen API Docs}
*/
* @see {@link ../../../platform/Platform Platform API Docs}
*/
@Directive({
selector: '[hideWhen]',
host: {

View File

@ -1,8 +1,18 @@
<ion-toolbar><ion-title>Show/Hide When</ion-title></ion-toolbar>
<ion-toolbar>
<ion-title>Show/Hide When</ion-title>
<ion-buttons end>
<button showWhen="ios">iOS</button>
<button showWhen="windows">Windows</button>
<button showWhen="android">Android</button>
</ion-buttons>
</ion-toolbar>
<ion-content padding>
<button showWhen="ios">iOS</button>
<button showWhen="windows">Windows</button>
<button showWhen="android">Android</button>
<p showWhen="ios" style="background:blue; color:white">
showWhen="ios"

View File

@ -1,10 +1,5 @@
@import "../../globals.core";
ion-swipe-slides {
display: block;
width: 100%;
height: 100%;
}
/**
* Swiper 3.1.2

View File

@ -7,7 +7,7 @@ import {Gesture} from '../../gestures/gesture';
import {DragGesture} from '../../gestures/drag-gesture';
import {dom} from '../../util';
import {CSS} from '../../util/dom';
import {debounce, isTrueProperty, defaults} from '../../util/util';
import {debounce, isTrueProperty, isPresent, defaults} from '../../util/util';
import {Swiper} from './swiper-widget';
import {Scroll} from '../scroll/scroll';
@ -16,38 +16,159 @@ import {Scroll} from '../scroll/scroll';
/**
* @name Slides
* @description
* Slides is a slide box implementation based on Swiper.js
* The Slides component is a multi-section container. Each section can be swiped
* or dragged between. It contains any number of [Slide](../Slide) components.
*
*
* ### Creating
* You should use a template to create slides and listen to slide events. The template
* should contain the slide container, an `<ion-slides>` element, and any number of
* [Slide](../Slide) components, written as `<ion-slide>`. Any configuration of the
* slides should be passed in the `options` property of the `<ion-slides>` element.
* You can listen to events such as the slide changing by placing the event on the
* `<ion-slides>` element. See [Usage](#usage) below for more information on
* creating slides.
*
*
* ### Configuring
* There are several configuration options that can be passed to Slides. These should
* be passed in the `options` property of the `<ion-slides>` element upon creation.
* You can allow the slides to loop around from the last to the first, set autoplay
* on the slides so it will automatically switch between them, and more.
*
* Properties to pass in options:
*
* | Property | Type | Default | Description |
* |-----------------------|-----------|----------------|--------------------------------------------------------------------------------------------|
* | autoplay | `number` | - | Delay between transitions (in ms). If this parameter is not passed, autoplay is disabled. |
* | direction | `string` | 'horizontal' | Swipe direction: 'horizontal' or 'vertical'. |
* | initialSlide | `number` | 0 | Index number of initial slide |
* | loop | `boolean` | false | Whether to continuously loop from the last slide to the first slide. |
* | pager | `boolean` | false | Show the pagination bullets. |
* | speed | `number` | 300 | Duration of transition between slides (in ms). |
*
* See [Usage](#usage) below for more information on configuring slides.
*
*
* ### Navigating
* After creating and configuring the slides, you can navigate between them
* by swiping or calling methods on the `Slides` instance. You can call `slideTo()` to
* navigate to a specific slide, or `slideNext()` to change to the slide that follows
* the active slide. All of the [methods](#instance-members) provided by the `Slides`
* instance are listed below. See [Usage](#usage) below for more information on
* navigating between slides.
*
*
* ### Limitations
* The Slides component wraps the [Swiper](http://www.idangero.us/swiper/) component
* built by iDangero.us. This means that all of the Swiper API isn't exposed on the
* Slides component. See the [`getSlider()`](#getSlider) method for information on
* getting the `Swiper` instance and using its methods directly.
*
*
* @usage
* ```ts
* @Page({
* template: `
* <ion-slides pager (change)="onSlideChanged($event)" (move)="onSlideMove($event)">
* <ion-slide>
* <h3>Thank you for choosing the Awesome App!</h3>
* <p>
* The number one app for everything awesome.
* </p>
* </ion-slide>
* <ion-slide>
* <h3>Using Awesome</h3>
* <div id="list">
* <h5>Just three steps:</h5>
* <ol>
* <li>Be awesome</li>
* <li>Stay awesome</li>
* <li>There is no step 3</li>
* </ol>
* </div>
* </ion-slide>
* <ion-slide>
* <h3>Any questions?</h3>
* </ion-slide>
* </ion-slides>
* `
*})
*
*```
* You can add slides to a `@Page` using the following template:
*
* ```html
* <ion-slides>
* <ion-slide>
* <h1>Slide 1</h1>
* </ion-slide>
* <ion-slide>
* <h1>Slide 2</h1>
* </ion-slide>
* <ion-slide>
* <h1>Slide 3</h1>
* </ion-slide>
* </ion-slides>
* ```
*
* To add [options](#configuring), we will define them in `mySlideOptions` in our class `MyPage`:
*
* ```ts
* import {Page, Slides} from 'ionic-angular';
*
* @Page({
* templateUrl: 'my-page.html'
* })
* class MyPage {
* mySlideOptions = {
* initialSlide: 1,
* loop: true
* };
* }
* ```
*
* This is setting the second slide as the initial slide on load, since
* the `initialSlide` begins at `0`. We are also setting `loop` to true which
* allows us to swipe from the last slide to the first continuously. Then,
* we will pass `mySlideOptions` in the `options` property of the `<ion-slides>`
* element. We are using [property binding](https://angular.io/docs/ts/latest/guide/template-syntax.html#!#property-binding)
* on `options` because `mySlideOptions` is an expression:
*
* ```html
* <ion-slides [options]="mySlideOptions">
* ```
*
* To grab a reference to the Slides, we will add a [local template variable](https://angular.io/docs/ts/latest/guide/template-syntax.html#!#local-vars)
* to `<ion-slides>` called `mySlider`:
*
* ```html
* <ion-slides #mySlider [options]="mySlideOptions">
* ```
*
* Next, we can use `ViewChild` to assign the Slides instance to `slider`:
*
* ```ts
* import {ViewChild} from 'angular2/core';
*
* class MyPage {
* @ViewChild('mySlider') slider: Slides;
*
* ...
* }
* ```
*
* Now we can call any of the `Slider` [methods]((#instance-members)),
* for example we can use the Slider's `slideTo()` method in order to
* navigate to a specific slide on a button click. Below we call the
* `goToSlide()` method and it navigates to the 3rd slide:
*
* ```ts
* class MyPage {
* ...
*
* goToSlide() {
* this.slider.slideTo(2, 500);
* }
* }
* ```
*
* We can also add events to listen to on the `<ion-slides>` element.
* Let's add the `didChange` event and call a method when the slide changes:
*
* ```html
* <ion-slides #mySlider (didChange)="onSlideChanged()" [options]="mySlideOptions">
* ```
*
* In our class, we add the `onSlideChanged()` method which gets the active
* index and prints it:
*
* ```ts
* class MyPage {
* ...
*
* onSlideChanged() {
* let currentIndex = this.slider.getActiveIndex();
* console.log("Current index is", currentIndex);
* }
* }
* ```
*
* For all of the methods you can call on the `Slider` instance, see the
* [Instance Members](#instance-members).
*
* @demo /docs/v2/demos/slides/
* @see {@link /docs/v2/components#slides Slides Component Docs}
*
@ -72,6 +193,9 @@ import {Scroll} from '../scroll/scroll';
'<div [class.hide]="!showPager" class="swiper-pagination"></div>' +
'</div>',
directives: [NgClass],
host: {
'[class]': 'slideId'
},
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
})
@ -82,6 +206,16 @@ export class Slides extends Ion {
*/
public rapidUpdate: Function;
/**
* @private
*/
private id: number;
/**
* @private
*/
private slideId: string;
/**
* @private
*/
@ -156,54 +290,64 @@ export class Slides extends Ion {
};
/**
* @input {boolean} Whether the slide should show the pager or not
*/
@Input() pager: any;
/**
* @input {any} Any slider options you want to configure, see swiper parameters: http://www.idangero.us/swiper/api/
* @input {Object} Any configuration for the slides
*/
@Input() options: any;
/**
* @input {number} Whether or not the slider can zoom in or out
* @private Deprecated
*/
@Input() pager: any;
/**
* @private Deprecated
*/
@Input() zoom: any;
/**
* @input {number} how long it should take to zoom a slide
* @private Deprecated
*/
@Input() zoomDuration: any;
/**
* @input {number} the max scale an slide can be zoomed
* @private Deprecated
*/
@Input() zoomMax: any;
/**
* @output {any} expression to evaluate when a slide has been changed
*/
@Output() change: EventEmitter<any> = new EventEmitter();
/**
* @output {any} expression to evaluate when a slide change starts
* @private Deprecated
*/
@Output() slideChangeStart: EventEmitter<any> = new EventEmitter();
/**
* @output {any} expression to evaluate when a slide moves
* @private Deprecated
*/
@Output() change: EventEmitter<any> = new EventEmitter();
/**
* @output {any} Expression to evaluate when a slide change starts.
*/
@Output() willChange: EventEmitter<any> = new EventEmitter();
/**
* @output {any} Expression to evaluate when a slide change ends.
*/
@Output() didChange: EventEmitter<any> = new EventEmitter();
/**
* @output {any} Expression to evaluate when a slide moves.
*/
@Output() move: EventEmitter<any> = new EventEmitter();
/**
* @private
* @param {ElementRef} elementRef TODO
*/
constructor(elementRef: ElementRef) {
super(elementRef);
this.rapidUpdate = debounce(() => {
this.update();
}, 10);
this.id = ++slidesId;
this.slideId = 'slides-' + this.id;
}
/**
@ -214,10 +358,32 @@ export class Slides extends Ion {
this.options = {};
}
this.showPager = isTrueProperty(this.pager);
if (isPresent(this.pager)) {
// beta.5 2016-04-18 deprecated warning
// Pager should be passed as an option
console.warn('The "pager" attribute has been deprecated. Please pass it in options.');
// Remove this with the deprecation warning
this.showPager = isTrueProperty(this.pager);
}
if (isPresent(this.zoom)) {
// beta.5 2016-04-18 deprecated warning
// Zoom should be passed as an option
console.warn('The "zoom" attribute has been deprecated. Please pass it in options.');
}
// Deprecated 04-18 beta.5
console.warn('The "slideChangeStart" event has been deprecated. Please use "willChange" instead. Ignore this if you aren\'t using it.');
console.warn('The "change" event has been deprecated. Please use "didChange" instead. Ignore this if you aren\'t using it.');
if (isPresent(this.options.pager)) {
this.showPager = isTrueProperty(this.options.pager);
}
let paginationId = '.' + this.slideId + ' .swiper-pagination';
var options = defaults({
pagination: '.swiper-pagination',
pagination: paginationId
}, this.options);
options.onTap = (swiper, e) => {
@ -241,11 +407,17 @@ export class Slides extends Ion {
return this.options.onTransitionEnd && this.options.onTransitionEnd(swiper, e);
};
options.onSlideChangeStart = (swiper) => {
// TODO deprecated 2016-04-18
this.slideChangeStart.emit(swiper);
this.willChange.emit(swiper);
return this.options.onSlideChangeStart && this.options.onSlideChangeStart(swiper);
};
options.onSlideChangeEnd = (swiper) => {
// TODO deprecated 2016-04-18
this.change.emit(swiper);
this.didChange.emit(swiper);
return this.options.onSlideChangeEnd && this.options.onSlideChangeEnd(swiper);
};
options.onLazyImageLoad = (swiper, slide, img) => {
@ -259,6 +431,7 @@ export class Slides extends Ion {
return this.options.onSliderMove && this.options.onSliderMove(swiper, e);
};
setTimeout(() => {
var swiper = new Swiper(this.getNativeElement().children[0], options);
this.slider = swiper;
@ -266,7 +439,7 @@ export class Slides extends Ion {
/*
* TODO: Finish this
if (util.isTrueProperty(this.zoom)) {
if (isTrueProperty(this.zoom)) {
this.enableZoom = true;
setTimeout(() => {
this.initZoom();
@ -290,7 +463,6 @@ export class Slides extends Ion {
* @private
*/
onDoubleTap(swiper, e) {
this.toggleZoom(swiper, e);
}
/**
@ -602,58 +774,98 @@ export class Slides extends Ion {
this.slider.update();
// Don't allow pager to show with > 10 slides
if (this.slider.slides.length > 10) {
if (this.length() > 10) {
this.showPager = false;
}
});
}
/**
* @private
* Transition to the specified slide.
*
* @param {number} index The index number of the slide.
* @param {number} speed Transition duration (in ms). Optional.
* @param {boolean} runCallbacks Whether or not to emit the `willChange`/`didChange` events. Optional. Default true.
*/
next() {
this.slider.slideNext();
slideTo(index: number, speed: number, runCallbacks: boolean) {
this.slider.slideTo(index, speed, runCallbacks);
}
/**
* @private
* Transition to the next slide.
*
* @param {number} speed Transition duration (in ms). Optional.
* @param {boolean} runCallbacks Whether or not to emit the `willChange`/`didChange` events. Optional. Default true.
*/
prev() {
this.slider.slidePrev();
slideNext(speed: number, runCallbacks: boolean) {
this.slider.slideNext(runCallbacks, speed);
}
/**
* @private
* Transition to the previous slide.
*
* @param {number} speed Transition duration (in ms). Optional.
* @param {boolean} runCallbacks Whether or not to emit the `willChange`/`didChange` events. Optional. Default true.
*/
getIndex(): number {
slidePrev(speed: number, runCallbacks: boolean) {
this.slider.slidePrev(runCallbacks, speed);
}
/**
* Get the index of the active slide.
*
* @returns {number} The index number of the current slide.
*/
getActiveIndex(): number {
return this.slider.activeIndex;
}
/**
* @private
* Get the index of the previous slide.
*
* @returns {number} The index number of the previous slide.
*/
getNumSlides(): number {
getPreviousIndex(): number {
return this.slider.previousIndex;
}
/**
* Get the total number of slides.
*
* @returns {number} The total number of slides.
*/
length(): number {
return this.slider.slides.length;
}
/**
* @private
* Get whether or not the current slide is the last slide.
*
* @returns {boolean} If the slide is the last slide or not.
*/
isAtEnd(): boolean {
isEnd(): boolean {
return this.slider.isEnd;
}
/**
* @private
* Get whether or not the current slide is the first slide.
*
* @returns {boolean} If the slide is the first slide or not.
*/
isAtBeginning(): boolean {
isBeginning(): boolean {
return this.slider.isBeginning;
}
/**
* @private
* Get the `Swiper` instance.
*
* The Slides component wraps the `Swiper` component built by iDangero.us. See the
* [Swiper API Docs](http://idangero.us/swiper/api/) for information on using
* the `Swiper` instance directly.
*
* @returns {Swiper}
*/
getSliderWidget() {
getSlider() {
return this.slider;
}
}
@ -661,7 +873,11 @@ export class Slides extends Ion {
/**
* @name Slide
* @description
* `ion-slide` is a child component of `ion-slides` and is where all your individule slide content will be rendered too.
* The Slide component is a child component of [Slides](../Slides). The template
* should be written as `ion-slide`. Any slide content should be written
* in this component and it should be used in conjunction with [Slides](../Slides).
*
* See the [Slides API Docs](../Slides) for more usage information.
*
* @demo /docs/v2/demos/slides/
* @see {@link /docs/v2/api/components/slides/Slides/ Slides API Docs}
@ -706,3 +922,5 @@ export class Slide {
}
})
export class SlideLazy {}
let slidesId = -1;

View File

@ -3,9 +3,11 @@ export declare class Swiper {
constructor(container: HTMLElement, params: any);
slides: Array<HTMLElement>;
activeIndex: number;
previousIndex: number;
isEnd: boolean;
isBeginning: boolean;
update(): any;
slideNext(): any;
slidePrev(): any;
slideNext(runCallbacks: boolean, speed: number);
slidePrev(runCallbacks: boolean, speed: number);
slideTo(slideIndex: number, speed: number, runCallbacks: boolean);
}

View File

@ -0,0 +1,53 @@
import {ViewChild} from 'angular2/core';
import {App, Page, Slides} from 'ionic-angular';
@App({
templateUrl: 'main.html'
})
class MyPage {
@ViewChild('mySlider') slider: Slides;
mySlideOptions = {
initialSlide: 1,
loop: false
};
ngAfterViewInit() {
}
onSlideChanged() {
let previousIndex = this.slider.getPreviousIndex();
let currentIndex = this.slider.getActiveIndex();
console.log("Previous index is", previousIndex, "Current index is", currentIndex);
}
goToPrevSlide() {
this.slider.slidePrev();
}
goToNextSlide() {
this.slider.slideNext();
}
goToSlide(index) {
this.slider.slideTo(index);
}
getIndex() {
let index = this.slider.getActiveIndex();
console.log("Current Index is", index);
}
getLength() {
let length = this.slider.length();
console.log("Current Length is", length);
}
}
@App({
template: `<ion-nav [root]="root"></ion-nav>`
})
class E2EApp {
root: Page = MyPage;
}

View File

@ -0,0 +1,23 @@
<ion-slides #mySlider [options]="mySlideOptions" (didChange)="onSlideChanged()">
<ion-slide padding>
<h1>Slide 1</h1>
<button block (click)="goToPrevSlide()">Navigate Back</button>
<button block (click)="goToNextSlide()">Navigate Forward</button>
<button block (click)="goToSlide(2)">Navigate to 3rd Slide</button>
<button block (click)="getLength()">Get Slide Length</button>
<button block (click)="getIndex()">Get Index</button>
</ion-slide>
<ion-slide padding>
<h1>Slide 2</h1>
<button block (click)="goToPrevSlide()">Navigate Back</button>
<button block (click)="goToNextSlide()">Navigate Forward</button>
<button block (click)="goToSlide(2)">Navigate to 3rd Slide</button>
<button block (click)="getIndex()">Get Index</button>
</ion-slide>
<ion-slide padding>
<h1>Slide 3</h1>
<button block (click)="goToPrevSlide()">Navigate Back</button>
<button block (click)="goToNextSlide()">Navigate Forward</button>
<button block (click)="getIndex()">Get Index</button>
</ion-slide>
</ion-slides>

View File

@ -8,7 +8,7 @@
</ion-navbar>
<ion-content>
<ion-slides pager [options]="mySlideOptions" (change)="onSlideChanged($event)" (slideChangeStart)="onSlideChangeStart($event)" (move)="onSlideMove($event)">
<ion-slides pager [options]="mySlideOptions" (didChange)="onSlideChanged($event)" (willChange)="onSlideChangeStart($event)" (move)="onSlideMove($event)">
<ion-slide>
<h3>Thank you for choosing the Awesome App!</h3>
<div id="logo">

View File

@ -35,7 +35,10 @@ class MyApp {
onSlideChanged(slider: Slides) {
console.log('Slide changed', slider);
console.log("active index", slider.activeIndex);
}
ngAfterViewInit() {
console.log(this.loopSlider);
}
}

View File

@ -1,5 +1,5 @@
<div class="slides-div">
<ion-slides [options]="myTopSlideOptions" #loopSlider (change)="onSlideChanged($event)" pager>
<ion-slides [options]="myTopSlideOptions" #loopSlider (didChange)="onSlideChanged($event)" pager>
<ion-slide *ngFor="#slide of slides" [ngClass]="slide.class">
Loop {{ slide.name }}
</ion-slide>
@ -8,7 +8,7 @@
<div class="slides-div">
<ion-slides (change)="onSlideChanged($event)" pager>
<ion-slides (didChange)="onSlideChanged($event)" pager>
<ion-slide *ngFor="#slide of slides" [ngClass]="slide.class">
Don't Loop {{ slide.name }}
</ion-slide>

View File

@ -90,7 +90,7 @@ tabbar {
ion-badge,
ion-icon,
span {
opacity: 0.4;
opacity: .4;
}
}

View File

@ -287,9 +287,30 @@ export class Tabs extends Ion {
* @private
*/
ngAfterContentInit() {
let preloadTabs = (isBlank(this.preloadTabs) ? this._config.getBoolean('preloadTabs') : isTrueProperty(this.preloadTabs));
// get the selected index
let selectedIndex = this.selectedIndex ? parseInt(this.selectedIndex, 10) : 0;
let preloadTabs = (isBlank(this.preloadTabs) ? this._config.getBoolean('preloadTabs') : isTrueProperty(this.preloadTabs));
// ensure the selectedIndex isn't a hidden or disabled tab
// also find the first available index incase we need it later
let availableIndex = -1;
this._tabs.forEach((tab, index) => {
if (tab.enabled && tab.show && availableIndex < 0) {
// we know this tab index is safe to show
availableIndex = index;
}
if (index === selectedIndex && (!tab.enabled || !tab.show)) {
// the selectedIndex is not safe to show
selectedIndex = -1;
}
});
if (selectedIndex < 0) {
// the selected index wasn't safe to show
// instead use an available index found to be safe to show
selectedIndex = availableIndex;
}
this._tabs.forEach((tab, index) => {
if (index === selectedIndex) {

View File

@ -81,7 +81,7 @@ tabbar {
// Windows Tabbar Color Mixin
// --------------------------------------------------
@mixin tabbar-wp($color-name, $color-base) {
@mixin tabbar-wp($color-name, $color-base, $color-contrast) {
ion-tabs[#{$color-name}] tabbar {
background-color: $color-base;
@ -104,5 +104,5 @@ tabbar {
// --------------------------------------------------
@each $color-name, $color-base, $color-contrast in get-colors($colors-wp) {
@include tabbar-wp($color-name, $color-base);
@include tabbar-wp($color-name, $color-base, $color-contrast);
}

View File

@ -0,0 +1,8 @@
it('should open toast', function() {
element(by.css('.e2eOpenToast')).click();
});
it('should close with backdrop click', function() {
element(by.css('.backdrop')).click();
});

View File

@ -0,0 +1,84 @@
import {App, Page, Toast, NavController} from 'ionic-angular';
@Page({
template: `
<ion-navbar *navbar>
<ion-title>Another Page</ion-title>
</ion-navbar>
<ion-content padding>
<p>This is another page to show that the toast stays.</p>
</ion-content>
`
})
class AnotherPage {
}
@Page({
templateUrl: 'main.html'
})
class E2EPage {
constructor(private nav: NavController) { }
showToast() {
const toast = Toast.create({
message: 'User was created successfully',
showCloseButton: true,
enableBackdropDismiss: false
});
toast.onDismiss(() => {
console.log('Dismissed toast');
});
this.nav.present(toast);
setTimeout(() => {
this.nav.push(AnotherPage);
}, 1000);
}
showLongToast() {
const toast = Toast.create({
message: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea voluptatibus quibusdam eum nihil optio, ullam accusamus magni, nobis suscipit reprehenderit, sequi quam amet impedit. Accusamus dolorem voluptates laborum dolor obcaecati.',
});
toast.onDismiss(this.dismissHandler);
this.nav.present(toast);
}
showDismissDurationToast() {
const toast = Toast.create({
message: 'I am dismissed after 1.5 seconds',
duration: 1500
});
toast.onDismiss(this.dismissHandler);
this.nav.present(toast);
}
showToastWithCloseButton() {
const toast = Toast.create({
message: 'Your internet connection appears to be offline. Data integrity is not gauranteed.',
showCloseButton: true,
closeButtonText: 'Ok'
});
toast.onDismiss(this.dismissHandler);
this.nav.present(toast);
}
private dismissHandler(toast: Toast) {
console.info('Toast onDismiss()');
}
}
@App({
template: '<ion-nav [root]="root"></ion-nav>'
})
class E2EApp {
root = E2EPage;
constructor() {
}
}

View File

@ -0,0 +1,23 @@
<ion-navbar *navbar>
<ion-title>Toasts</ion-title>
</ion-navbar>
<ion-content padding>
<button block (click)="showToast()">Show Toast and Navigate</button>
<button block (click)="showLongToast()">Show Long Toast</button>
<br />
<button block (click)="showDismissDurationToast()">Custom (1.5s) Duration</button>
<button block (click)="showToastWithCloseButton()" class="e2eOpenToast">With closeButtonText</button>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed lacinia purus ac turpis fermentum, nec accumsan nulla rutrum. Aenean lorem est, luctus id iaculis ac, ultricies quis odio. Aenean imperdiet imperdiet ex et vehicula. Suspendisse vulputate turpis quis ultricies porttitor. Proin malesuada tortor at libero laoreet, eu eleifend enim pulvinar. Nulla facilisi. Fusce sit amet mauris mauris. Mauris consequat libero sed egestas tincidunt.
</p>
<p>
In felis augue, sagittis id dui ac, tempor luctus turpis. Vestibulum nec urna vitae nisl malesuada lacinia ut sit amet orci. Suspendisse sed mauris vitae mauris porttitor pulvinar. Donec quis ante id dui cursus malesuada ut nec magna. Vestibulum venenatis efficitur urna, quis tempus quam. Curabitur id elementum eros, at euismod nisl. Aliquam ultricies imperdiet arcu id consequat. Aliquam erat volutpat. Nam quis laoreet dui. Donec eget neque non leo porta scelerisque. In blandit placerat nibh, ut viverra nisi feugiat a. Pellentesque semper, ligula et tincidunt egestas, urna arcu pellentesque massa, vitae accumsan ligula velit vitae sem. Nulla porta est id ligula viverra, ut placerat quam auctor. Morbi eget efficitur nibh.
</p>
<p>
Aenean viverra commodo enim eget interdum. Donec condimentum tincidunt sollicitudin. Curabitur malesuada est elementum lectus sodales, vitae eleifend massa dignissim. Pellentesque nec diam dapibus purus vulputate pharetra at id nunc. Vivamus dapibus sed turpis in facilisis. Nulla sollicitudin lacus sem, vel fringilla neque accumsan non. Suspendisse non congue turpis, id mattis ex. Nam sit amet diam quis neque convallis aliquet quis et lorem. Donec sit amet libero sit amet nisl mollis vehicula nec id eros. Curabitur rutrum condimentum porta. Donec pellentesque consectetur lacus. Etiam maximus ante vitae varius eleifend. Integer ac justo sem. Morbi iaculis vel urna in tempus. Aenean at rhoncus nulla.
</p>
</ion-content>

View File

@ -0,0 +1,52 @@
@import "../../globals.ios";
@import "./toast";
// iOS Toast
// --------------------------------------------------
$toast-ios-text-align: left !default;
$toast-ios-background: rgba(0, 0, 0, .9) !default;
$toast-ios-border-radius: .65rem !default;
$toast-ios-title-color: #fff !default;
$toast-ios-title-font-size: 1.4rem !default;
$toast-ios-title-padding: 1.5rem !default;
ion-toast {
position: absolute;
top: 0;
left: 0;
z-index: $z-index-overlay;
display: block;
width: $toast-width;
height: $toast-width;
}
.toast-wrapper {
position: absolute;
right: 10px;
bottom: 10px;
left: 10px;
z-index: $z-index-overlay-wrapper;
display: block;
margin: auto;
max-width: $toast-max-width;
border-radius: $toast-ios-border-radius;
background: $toast-ios-background;
transform: translate3d(0, 100%, 0);
}
.toast-message {
padding: $toast-ios-title-padding;
font-size: $toast-ios-title-font-size;
color: $toast-ios-title-color;
}

View File

@ -0,0 +1,51 @@
@import "../../globals.md";
@import "./toast";
// Material Design Toast
// --------------------------------------------------
$toast-md-text-align: left !default;
$toast-md-background: #333 !default;
$toast-md-group-margin-bottom: 8px !default;
$toast-md-title-color: #fff !default;
$toast-md-title-font-size: 1.5rem !default;
$toast-md-title-padding: 19px 16px 17px !default;
ion-toast {
position: absolute;
top: 0;
left: 0;
z-index: $z-index-overlay;
display: block;
width: $toast-width;
height: $toast-width;
}
.toast-wrapper {
position: absolute;
right: 0;
bottom: 0;
left: 0;
z-index: $z-index-overlay-wrapper;
display: block;
margin: auto;
width: $toast-width;
max-width: $toast-max-width;
background: $toast-md-background;
transform: translate3d(0, 100%, 0);
}
.toast-message {
padding: $toast-md-title-padding;
font-size: $toast-md-title-font-size;
color: $toast-md-title-color;
}

View File

@ -0,0 +1,52 @@
@import "../../globals.core";
// Toast
// --------------------------------------------------
$toast-width: 100% !default;
$toast-max-width: 700px !default;
ion-toast {
position: absolute;
top: 0;
left: 0;
z-index: $z-index-overlay;
display: block;
width: $toast-width;
height: $toast-width;
}
.toast-container {
display: flex;
align-items: center;
button {
padding: 19px 16px 17px;
font-size: 1.5rem;
}
}
.toast-message {
flex: 1;
}
.toast-wrapper {
position: absolute;
right: 0;
bottom: 0;
left: 0;
z-index: $z-index-overlay-wrapper;
display: block;
margin: auto;
max-width: $toast-max-width;
transform: translate3d(0, 100%, 0);
}

View File

@ -0,0 +1,322 @@
import {Component, ElementRef, Renderer, Output, EventEmitter} from 'angular2/core';
import {NgClass, NgIf, NgFor} from 'angular2/common';
import {Button} from '../button/button';
import {Icon} from '../icon/icon';
import {ActionSheet, ActionSheetOptions} from '../action-sheet/action-sheet';
import {Animation} from '../../animations/animation';
import {Transition, TransitionOptions} from '../../transitions/transition';
import {Config} from '../../config/config';
import {isPresent} from '../../util/util';
import {NavParams} from '../nav/nav-params';
import {NavController} from '../nav/nav-controller';
import {ViewController} from '../nav/view-controller';
/**
* @name Toast
* @description
* A Toast is a subtle notification that appears at the bottom of the
* screen. It can be used to provide feedback about an operation or to
* display a system message. The toast appears on top of the app's content,
* and can be dismissed by the app to resume user interaction with
* the app. It includes a backdrop, which can optionally be clicked to
* dismiss the toast.
*
* ### Creating
* All of the toast options should be passed in the first argument of
* the create method: `Toast.create(opts)`. The message to display should be
* passed in the `message` property. The `showCloseButton` option can be set to
* true in order to display a close button on the toast. See the [create](#create)
* method below for all available options.
*
* ### Dismissing
* The toast can be dismissed automatically after a specific amount of time
* by passing the number of milliseconds to display it in the `duration` of
* the toast options. It can also be dismissed by clicking on the backdrop,
* unless `enableBackdropDismiss` is set to `false` upon creation. If `showCloseButton`
* is set to true, then the close button will dismiss the toast. To dismiss
* the toast after creation, call the `dismiss()` method on the Toast instance.
* The `onDismiss` function can be called to perform an action after the toast
* is dismissed.
*
* @usage
* ```ts
* constructor(nav: NavController) {
* this.nav = nav;
* }
*
* presentToast() {
* let toast = Toast.create({
* message: 'User was added successfully',
* duration: 3000
* });
*
* toast.onDismiss(() => {
* console.log('Dismissed toast');
* });
*
* this.nav.present(toast);
* }
* ```
*
* @demo /docs/v2/demos/toast/
*/
export class Toast extends ViewController {
constructor(opts: ToastOptions = {}) {
opts.enableBackdropDismiss = isPresent(opts.enableBackdropDismiss) ? !!opts.enableBackdropDismiss : true;
opts.dismissOnPageChange = isPresent(opts.dismissOnPageChange) ? !!opts.dismissOnPageChange : false;
super(ToastCmp, opts);
this.viewType = 'toast';
this.isOverlay = true;
this.usePortal = true;
// by default, toasts should not fire lifecycle events of other views
// for example, when an toast enters, the current active view should
// not fire its lifecycle events because it's not conceptually leaving
this.fireOtherLifecycles = false;
}
/**
* @private
*/
getTransitionName(direction: string) {
let key = 'toast' + (direction === 'back' ? 'Leave' : 'Enter');
return this._nav && this._nav.config.get(key);
}
/**
* @param {string} message Toast message content
*/
setMessage(message: string) {
this.data.message = message;
}
/**
*
* Toast options
*
* | Property | Type | Default | Description |
* |-----------------------|-----------|-----------------|---------------------------------------------------------------------------------------------------------------|
* | message | `string` | - | The message for the toast. Long strings will wrap and the toast container will expand. |
* | duration | `number` | - | How many milliseconds to wait before hiding the toast. By default, it will show until `dismiss()` is called. |
* | cssClass | `string` | - | Any additional class for custom styles. |
* | showCloseButton | `boolean` | false | Whether or not to show a button to close the toast. |
* | closeButtonText | `string` | "Close" | Text to display in the close button. |
* | enableBackdropDismiss | `boolean` | true | Whether the toast should be dismissed by tapping the backdrop. |
* | dismissOnPageChange | `boolean` | false | Whether to dismiss the toast when navigating to a new page. |
*
* @param {object} opts Toast options. See the above table for available options.
*/
static create(opts: ToastOptions = {}) {
return new Toast(opts);
}
}
/**
* @private
*/
@Component({
selector: 'ion-toast',
template: `
<div (click)="bdClick()" tappable disable-activated class="backdrop" role="presentation"></div>
<div class="toast-wrapper">
<div class="toast-container">
<div class="toast-message" id="{{hdrId}}" *ngIf="d.message">{{d.message}}</div>
<button clear class="toast-button" *ngIf="d.showCloseButton" (click)="cbClick()">
{{ d.closeButtonText || 'Close' }}
<ion-button-effect></ion-button-effect>
</button>
</div>
</div>
`,
host: {
'role': 'dialog',
'[attr.aria-labelledby]': 'hdrId',
'[attr.aria-describedby]': 'descId'
},
directives: [NgIf, Icon, Button]
})
class ToastCmp {
private d: any;
private descId: string;
private hdrId: string;
private created: number;
private id: number;
private dismissTimeout: number = undefined;
constructor(
private _nav: NavController,
private _viewCtrl: ViewController,
private _config: Config,
private _elementRef: ElementRef,
params: NavParams,
renderer: Renderer
) {
this.d = params.data;
this.created = Date.now();
if (this.d.cssClass) {
renderer.setElementClass(_elementRef.nativeElement, this.d.cssClass, true);
}
this.id = (++toastIds);
if (this.d.message) {
this.hdrId = 'toast-hdr-' + this.id;
}
}
onPageDidEnter() {
const { activeElement }: any = document;
if (activeElement) {
activeElement.blur();
}
let focusableEle = this._elementRef.nativeElement.querySelector('button');
if (focusableEle) {
focusableEle.focus();
}
// if there's a `duration` set, automatically dismiss.
if (this.d.duration) {
this.dismissTimeout =
setTimeout(() => {
this.dismiss('backdrop');
}, this.d.duration);
}
}
bdClick() {
if (this.isEnabled() && this.d.enableBackdropDismiss) {
this.dismiss('backdrop');
}
}
cbClick() {
if (this.isEnabled()) {
this.dismiss('close');
}
}
dismiss(role): Promise<any> {
clearTimeout(this.dismissTimeout);
this.dismissTimeout = undefined;
return this._viewCtrl.dismiss(null, role);
}
isEnabled() {
let tm = this._config.getNumber('overlayCreatedDiff', 750);
return (this.created + tm < Date.now());
}
}
export interface ToastOptions {
message?: string;
cssClass?: string;
duration?: number;
showCloseButton?: boolean;
closeButtonText?: string;
enableBackdropDismiss?: boolean;
dismissOnPageChange?: boolean;
}
class ToastSlideIn extends Transition {
constructor(enteringView, leavingView, opts: TransitionOptions) {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let wrapper = new Animation(ele.querySelector('.toast-wrapper'));
wrapper.fromTo('translateY', '120%', '0%');
this.easing('cubic-bezier(.36,.66,.04,1)').duration(400).add(wrapper);
}
}
class ToastSlideOut extends Transition {
constructor(enteringView: ViewController, leavingView: ViewController, opts: TransitionOptions) {
super(opts);
let ele = leavingView.pageRef().nativeElement;
let wrapper = new Animation(ele.querySelector('.toast-wrapper'));
wrapper.fromTo('translateY', '0%', '120%');
this.easing('cubic-bezier(.36,.66,.04,1)').duration(300).add(wrapper);
}
}
class ToastMdSlideIn extends Transition {
constructor(enteringView: ViewController, leavingView: ViewController, opts: TransitionOptions) {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let wrapper = new Animation(ele.querySelector('.toast-wrapper'));
backdrop.fromTo('opacity', 0, 0);
wrapper.fromTo('translateY', '120%', '0%');
this.easing('cubic-bezier(.36,.66,.04,1)').duration(400).add(backdrop).add(wrapper);
}
}
class ToastMdSlideOut extends Transition {
constructor(enteringView: ViewController, leavingView: ViewController, opts: TransitionOptions) {
super(opts);
let ele = leavingView.pageRef().nativeElement;
let wrapper = new Animation(ele.querySelector('.toast-wrapper'));
let backdrop = new Animation(ele.querySelector('.backdrop'));
wrapper.fromTo('translateY', '0%', '120%');
backdrop.fromTo('opacity', 0, 0);
this.easing('cubic-bezier(.36,.66,.04,1)').duration(450).add(backdrop).add(wrapper);
}
}
class ToastWpPopIn extends Transition {
constructor(enteringView: ViewController, leavingView: ViewController, opts: TransitionOptions) {
super(opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let wrapper = new Animation(ele.querySelector('.toast-wrapper'));
wrapper.fromTo('opacity', '0.01', '1').fromTo('scale', '1.3', '1');
backdrop.fromTo('opacity', 0, 0);
this.easing('cubic-bezier(0,0 0.05,1)').duration(200).add(backdrop).add(wrapper);
}
}
class ToastWpPopOut extends Transition {
constructor(enteringView: ViewController, leavingView: ViewController, opts: TransitionOptions) {
super(opts);
let ele = leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('.backdrop'));
let wrapper = new Animation(ele.querySelector('.toast-wrapper'));
wrapper.fromTo('opacity', '1', '0').fromTo('scale', '1', '1.3');
backdrop.fromTo('opacity', 0, 0);
this.easing('ease-out').duration(150).add(backdrop).add(wrapper);
}
}
Transition.register('toast-slide-in', ToastSlideIn);
Transition.register('toast-slide-out', ToastSlideOut);
Transition.register('toast-md-slide-in', ToastMdSlideIn);
Transition.register('toast-md-slide-out', ToastMdSlideOut);
Transition.register('toast-wp-slide-out', ToastWpPopOut);
Transition.register('toast-wp-slide-in', ToastWpPopIn);
let toastIds = -1;

View File

@ -0,0 +1,44 @@
@import "../../globals.wp";
@import "./toast";
// Windows Phone Toast
// --------------------------------------------------
$toast-wp-text-align: left !default;
$toast-wp-background: rgba(0, 0, 0, 1) !default;
$toast-wp-border-radius: 0 !default;
$toast-wp-button-color: #fff !default;
$toast-wp-title-color: #fff !default;
$toast-wp-title-font-size: 1.4rem !default;
$toast-wp-title-padding: 1.5rem !default;
.toast-wrapper {
position: absolute;
bottom: 0;
left: 0;
z-index: $z-index-overlay-wrapper;
display: block;
margin: auto;
max-width: $toast-max-width;
border-radius: $toast-wp-border-radius;
background: $toast-wp-background;
transform: translate3d(0, 100%, 0);
}
.toast-message {
padding: $toast-wp-title-padding;
font-size: $toast-wp-title-font-size;
color: $toast-wp-title-color;
}
.toast-button {
color: $toast-wp-button-color;
}

View File

@ -58,6 +58,7 @@ const TOGGLE_VALUE_ACCESSOR = new Provider(
'</div>' +
'<button role="checkbox" ' +
'type="button" ' +
'category="item-cover" ' +
'[id]="id" ' +
'[attr.aria-checked]="_checked" ' +
'[attr.aria-labelledby]="_labelId" ' +

View File

@ -128,7 +128,7 @@ ion-toggle {
// Windows Color Mixin
// --------------------------------------------------
@mixin toggle-theme-wp($color-name, $color-base) {
@mixin toggle-theme-wp($color-name, $color-base, $color-contrast) {
ion-toggle[#{$color-name}] {
@ -151,6 +151,6 @@ ion-toggle {
@each $color-name, $color-base, $color-contrast in get-colors($colors-wp) {
@include toggle-theme-wp($color-name, $color-base);
@include toggle-theme-wp($color-name, $color-base, $color-contrast);
}

View File

@ -73,6 +73,7 @@ export function postBootstrap(appRef: ComponentRef, prodMode: boolean) {
platform.setZone(appRef.injector.get(NgZone));
platform.prepareReady();
app.setProd(prodMode);
app.setAppInjector(appRef.injector);
}

View File

@ -6,7 +6,6 @@ import {MenuToggle} from '../components/menu/menu-toggle';
import {MenuClose} from '../components/menu/menu-close';
import {Badge} from '../components/badge/badge';
import {Button} from '../components/button/button';
import {Blur} from '../components/blur/blur';
import {Content} from '../components/content/content';
import {Img} from '../components/img/img';
import {Scroll} from '../components/scroll/scroll';
@ -44,79 +43,102 @@ import {ShowWhen, HideWhen} from '../components/show-hide-when/show-hide-when';
/**
* @name IONIC_DIRECTIVES
* @private
* @description
* The core Ionic directives as well as Angular's CORE_DIRECTIVES and
* FORM_DIRECTIVES. Automatically available in every [@Page](../Page/) template.
* The core Ionic directives as well as Angular's `CORE_DIRECTIVES` and `FORM_DIRECTIVES` are
* avaialbe automatically when you bootstrap your app with the `@App` decorator. This means
* if you are using custom components you no longer need to import `IONIC_DIRECTIVES` as they
* are part of the `@App`s default directives.
*
* **Angular**
* If you would like to **not** have them included by default, you would need to bootstrap
* the app differently.
*
* Instead of starting your app like so:
*
* ```typescript
* @App({
* template: "<ion-nav></ion-nav>"
* })
*
* export class MyApp{
*
* }
* ```
*
* We would use Angulars default way of bootstrap an app, import `IONIC_DIRECTIVES` and `ionicProviders`, then
* declare `ionicProviders` as a dependencey.
*
* ```typescript
* import {IONIC_DIRECTIVES, ionicProviders} from 'ionic-angular';
* import {bootstrap} from 'angular2/platform/browser';
*
* @Component({
* //default selector, and theme.
* directives: [IONIC_DIRECTIVES]
* })
* class App {}
*
* bootstrap(App,ionicProviders())
* ```
*
*
*
* #### Angular
* - CORE_DIRECTIVES
* - FORM_DIRECTIVES
*
* **Content**
* - Menu
* - MenuToggle
* - MenuClose
*
* - Button
* - Blur
* - Content
* - Scroll
* - InfiniteScroll
* - InfiniteScrollContent
* - Refresher
* - RefresherContent
*
* **Lists**
* - List
* - ListHeader
* - Item
* - ItemSliding
* - VirtualScroll
* - VirtualFor
*
* **Slides**
* - Slides
* - Slide
* - SlideLazy
*
* **Tabs**
* - Tabs
* - Tab
*
* **Toolbar**
* - Toolbar
* - ToolbarTitle
* - ToolbarItem
*
* **Media**
* - Icon
* - Spinner
*
* **Forms**
* - Searchbar
* - Segment
* - SegmentButton
* - Checkbox
* - RadioGroup
* - RadioButton
* - Select
* - Option
* - Toggle
* - TextInput
* - Label
*
* **Nav**
* - Nav
* - NavbarTemplate
* - Navbar
* - NavPush
* - NavPop
* - NavRouter
* - IdRef
*
* - ShowWhen
* - HideWhen
* #### Ionic
* - Menu
* - MenuToggle
* - MenuClose
* - Badge
* - Button
* - Content
* - Scroll
* - InfiniteScroll
* - InfiniteScrollContent
* - Refresher
* - RefresherContent
* - Img
* - List
* - ListHeader
* - Item
* - ItemSliding
* - VirtualScroll
* - VirtualItem
* - VirtualHeader
* - VirtualFooter
* - Slides
* - Slide
* - SlideLazy
* - Tabs
* - Tab
* - Toolbar
* - ToolbarTitle
* - ToolbarItem
* - Icon
* - Spinner
* - Searchbar
* - SearchbarInput
* - Segment
* - SegmentButton
* - Checkbox
* - RadioGroup
* - RadioButton
* - Select
* - Option
* - Toggle
* - TextArea
* - TextInput
* - Label
* - Nav
* - NavbarTemplate
* - Navbar
* - NavPush
* - NavPop
* - NavRouter
* - IdRef
* - ShowWhen
* - HideWhen
*/
export const IONIC_DIRECTIVES = [
// Angular
@ -130,7 +152,6 @@ export const IONIC_DIRECTIVES = [
Badge,
Button,
Blur,
Content,
Scroll,
InfiniteScroll,

View File

@ -9,6 +9,9 @@ Config.setModeConfig('ios', {
actionSheetEnter: 'action-sheet-slide-in',
actionSheetLeave: 'action-sheet-slide-out',
toastEnter: 'toast-slide-in',
toastLeave: 'toast-slide-out',
alertEnter: 'alert-pop-in',
alertLeave: 'alert-pop-out',
@ -41,6 +44,9 @@ Config.setModeConfig('md', {
actionSheetEnter: 'action-sheet-md-slide-in',
actionSheetLeave: 'action-sheet-md-slide-out',
toastEnter: 'toast-md-slide-in',
toastLeave: 'toast-md-slide-out',
alertEnter: 'alert-md-pop-in',
alertLeave: 'alert-md-pop-out',
@ -76,6 +82,9 @@ Config.setModeConfig('wp', {
actionSheetEnter: 'action-sheet-wp-slide-in',
actionSheetLeave: 'action-sheet-wp-slide-out',
toastEnter: 'toast-wp-slide-in',
toastLeave: 'toast-wp-slide-out',
alertEnter: 'alert-wp-pop-in',
alertLeave: 'alert-wp-pop-out',

View File

@ -1,4 +1,4 @@
import {Component, ChangeDetectionStrategy, ViewEncapsulation, enableProdMode, Type} from 'angular2/core';
import {Component, ChangeDetectionStrategy, ViewEncapsulation, enableProdMode, Type, provide, PLATFORM_DIRECTIVES} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {ionicProviders, postBootstrap} from '../config/bootstrap';
import {IONIC_DIRECTIVES} from '../config/directives';
@ -41,6 +41,9 @@ export interface AppMetadata {
* property that has an inline template, or a `templateUrl` property that points
* to an external html template. The `@App` decorator runs the Angular bootstrapping
* process automatically, however you can bootstrap your app separately if you prefer.
* Additionally, `@App` will automatically bootstrap with all of Ionic's
* core components, meaning they won't all have to be individually imported and added
* to each component's `directives` property.
*
* @usage
* ```ts
@ -71,9 +74,6 @@ export function App(args: AppMetadata = {}) {
args.selector = 'ion-app';
// auto add Ionic directives
args.directives = args.directives ? args.directives.concat(IONIC_DIRECTIVES) : IONIC_DIRECTIVES;
// if no template was provided, default so it has a root <ion-nav>
if (!args.templateUrl && !args.template) {
args.template = '<ion-nav></ion-nav>';
@ -88,6 +88,12 @@ export function App(args: AppMetadata = {}) {
// define array of bootstrap providers
let providers = ionicProviders(args).concat(args.providers || []);
// auto add Ionic directives
let directives = args.directives ? args.directives.concat(IONIC_DIRECTIVES) : IONIC_DIRECTIVES;
// automatically provide all of Ionic's directives to every component
providers.push(provide(PLATFORM_DIRECTIVES, {useValue: [directives], multi: true}));
if (args.prodMode) {
enableProdMode();
}

View File

@ -1,5 +1,4 @@
import {Component, ChangeDetectionStrategy, ViewEncapsulation, Type} from 'angular2/core';
import {IONIC_DIRECTIVES} from '../config/directives';
const _reflect: any = Reflect;
@ -33,12 +32,15 @@ export interface PageMetadata {
* @description
*
* The Page decorator indicates that the decorated class is an Ionic
* navigation component, meaning it can be navigated to using a NavController.
* navigation component, meaning it can be navigated to using a
* [NavController](../../nav/NavController).
*
* Pages have all `IONIC_DIRECTIVES`, which include all Ionic components and directives,
* as well as Angular's [CORE_DIRECTIVES](https://angular.io/docs/js/latest/api/core/CORE_DIRECTIVES-const.html)
* and [FORM_DIRECTIVES](https://angular.io/docs/js/latest/api/core/FORM_DIRECTIVES-const.html),
* already provided to them, so you only need to supply custom components and directives to your pages:
* Since the app has already been bootstrapped with Ionic's core directives, it
* is not needed to include `IONIC_DIRECTIVES` in the directives property. Additionally,
* Angular's [CORE_DIRECTIVES](https://angular.io/docs/ts/latest/api/common/CORE_DIRECTIVES-let.html)
* and [FORM_DIRECTIVES](https://angular.io/docs/ts/latest/api/common/FORM_DIRECTIVES-let.html),
* are also already provided, so you only need to supply any custom components and directives
* to your pages:
*
* @usage
*
@ -53,44 +55,7 @@ export interface PageMetadata {
* class MyPage {}
* ```
*
* Here [Content](../../../components/content/Content/) will load because
* it is in `IONIC_DIRECTIVES`, so there is no need to add a `directives` array.
*
*
* Say you built a custom component that uses the already existing Ionic component.
* In this case, you would add `IONIC_DIRECTIVES` to your directives array.
*
* ```ts
* import {IONIC_DIRECTIVES} from 'ionic-angular';
* @Component({
* selector: 'my-component'
* template: `<div class="my-style">
* <ion-checkbox></ion-checkbox>
* </div>`,
* directives: [IONIC_DIRECTIVES]
* })
* class MyCustomCheckbox {}
*```
* Alternatively, you could:
*
* ```ts
* import {Checkbox, Icon} from 'ionic-angular'
* ```
*
* along with any other components and add them individually:
*
* ```
* @Component({
* ...
* directives: [Checkbox, Icon]
* })
* ```
*
* However, using IONIC_DIRECTIVES will always *Just Work* with no
* performance overhead, so there is really no reason to not always use it.
*
* Pages have their content automatically wrapped in `<ion-view>`, so although
* Pages have their content automatically wrapped in `<ion-page>`, so although
* you may see these tags if you inspect your markup, you don't need to include
* them in your templates.
*
@ -99,7 +64,6 @@ export interface PageMetadata {
export function Page(config: PageMetadata) {
return function(cls) {
config.selector = 'ion-page';
config.directives = config.directives ? config.directives.concat(IONIC_DIRECTIVES) : IONIC_DIRECTIVES;
config.host = config.host || {};
config.host['[hidden]'] = '_hidden';
config.host['[class.tab-subpage]'] = '_tabSubPage';

View File

@ -152,9 +152,7 @@ export class Platform {
* }
* ```
*
* @param {string} [platformName] optional platformName
* @returns {object} An object with various platform info
*
* @returns {object} An object containing all of the platforms and their versions.
*/
versions(): {[name: string]: PlatformVersion} {
// get all the platforms that have a valid parsed version

View File

@ -104,6 +104,18 @@ export class ScrollView {
});
}
scrollToTop(duration: number): Promise<any> {
return this.scrollTo(0, 0, duration);
}
scrollToBottom(duration: number): Promise<any> {
let y = 0;
if (this._el) {
y = this._el.scrollHeight - this._el.clientHeight;
}
return this.scrollTo(0, y, duration);
}
stop() {
this.isPlaying = false;
}