From 17a9e6d44d55aa048dcc72899974046e1da4c901 Mon Sep 17 00:00:00 2001 From: dustinwag Date: Mon, 28 Mar 2016 14:50:58 -0700 Subject: [PATCH 01/68] feat(confg): create a method to access the global app injector which contains references the bootstr This feature simply sets a reference to the injector on the IonicApp class, so it can be referenced by other components. This is sometimes necessary when injecting providers that depend on other providers. This issue is discussed here https://github.com/angular/angular/issues/4112#issuecomment-139381970, and Brandon Roberts' solution of an appInjector() method has been used to solve a variety of dependency injection conflicts. Unfortunately, it requires access to Angular's bootstrap() method, which Ionic handles in the @App decorator. This fix will create a reference to the appInjector(), so it can be references from within Ionic components. closes #5973 --- ionic/components/app/app.ts | 18 +++++++++++++++++- ionic/decorators/app.ts | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/ionic/components/app/app.ts b/ionic/components/app/app.ts index 530d911247..011c8fc68c 100644 --- a/ionic/components/app/app.ts +++ b/ionic/components/app/app.ts @@ -1,4 +1,4 @@ -import {Injectable, NgZone} from 'angular2/core'; +import {Injectable, Injector, NgZone} from 'angular2/core'; import {Title} from 'angular2/platform/browser'; import {Config} from '../../config/config'; @@ -18,6 +18,7 @@ export class IonicApp { private _title: string = ''; private _titleSrv: Title = new Title(); private _isProd: boolean = false; + private _appInjector: Injector; constructor( private _config: Config, @@ -155,4 +156,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; + } } diff --git a/ionic/decorators/app.ts b/ionic/decorators/app.ts index 8f885d8e86..1b87843ec5 100644 --- a/ionic/decorators/app.ts +++ b/ionic/decorators/app.ts @@ -96,6 +96,7 @@ export function App(args: AppMetadata={}) { bootstrap(cls, providers).then(appRef => { appRef.injector.get(TapClick); let app: IonicApp = appRef.injector.get(IonicApp); + app.setAppInjector(appRef.injector); app.setProd(args.prodMode); }); From a6091bd768236b5ad639646edd47173438d07a78 Mon Sep 17 00:00:00 2001 From: Aziz Abbas Date: Wed, 17 Feb 2016 11:24:23 -0800 Subject: [PATCH 02/68] feat(slides): add ability to slide to specific index --- ionic/components/slides/slides.ts | 7 +++++++ ionic/components/slides/swiper-widget.d.ts | 1 + 2 files changed, 8 insertions(+) diff --git a/ionic/components/slides/slides.ts b/ionic/components/slides/slides.ts index 18a013be16..262131e640 100644 --- a/ionic/components/slides/slides.ts +++ b/ionic/components/slides/slides.ts @@ -606,6 +606,13 @@ export class Slides extends Ion { }); } + /** + * @private + */ + slideTo(slideIndex: number, speed: number, runCallbacks: boolean) { + this.slider.slideTo(slideIndex, speed, runCallbacks); + } + /** * @private */ diff --git a/ionic/components/slides/swiper-widget.d.ts b/ionic/components/slides/swiper-widget.d.ts index 46ba753043..f2a5185de0 100644 --- a/ionic/components/slides/swiper-widget.d.ts +++ b/ionic/components/slides/swiper-widget.d.ts @@ -8,4 +8,5 @@ export declare class Swiper { update(): any; slideNext(): any; slidePrev(): any; + slideTo(slideIndex: number, speed: number, runCallbacks: boolean); } From 0521ce255c228a5e64b57c4fdb12bd25707bb83b Mon Sep 17 00:00:00 2001 From: "Manu Mtz.-Almeida" Date: Sun, 17 Apr 2016 15:56:31 +0200 Subject: [PATCH 03/68] fix(button): buttons don't get activated when ion-label contains exotic elements --- ionic/components/app/normalize.scss | 5 +++++ ionic/components/item/test/buttons/main.html | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ionic/components/app/normalize.scss b/ionic/components/app/normalize.scss index d5597afc11..c6ca4db02b 100644 --- a/ionic/components/app/normalize.scss +++ b/ionic/components/app/normalize.scss @@ -151,6 +151,11 @@ textarea { touch-action: manipulation; } +button ion-label, +a ion-label { + pointer-events: none; +} + button { border: 0; font-family: inherit; diff --git a/ionic/components/item/test/buttons/main.html b/ionic/components/item/test/buttons/main.html index f6a08a6156..d50d7d169a 100644 --- a/ionic/components/item/test/buttons/main.html +++ b/ionic/components/item/test/buttons/main.html @@ -88,8 +88,18 @@ - + + From 8b3b295d74d58c249ad09d60b5397c4cc8f6f54f Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sun, 17 Apr 2016 20:38:06 -0500 Subject: [PATCH 04/68] style(): update css formatting --- ionic/components/app/normalize.scss | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ionic/components/app/normalize.scss b/ionic/components/app/normalize.scss index c6ca4db02b..a4cef5c4a3 100644 --- a/ionic/components/app/normalize.scss +++ b/ionic/components/app/normalize.scss @@ -151,9 +151,10 @@ textarea { touch-action: manipulation; } +a ion-label, button ion-label, -a ion-label { - pointer-events: none; +[tappable] ion-label { + pointer-events: none; } button { From a40c1c3b8d2d35c51d2274b843a40175400ed6d1 Mon Sep 17 00:00:00 2001 From: James Cheuk Date: Mon, 18 Apr 2016 12:49:04 +0800 Subject: [PATCH 05/68] update select.ts docs incorrent closing tag --- ionic/components/select/select.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ionic/components/select/select.ts b/ionic/components/select/select.ts index d264bd9112..9be71ee89b 100644 --- a/ionic/components/select/select.ts +++ b/ionic/components/select/select.ts @@ -64,7 +64,7 @@ const SELECT_VALUE_ACCESSOR = new Provider( * Pepperoni * Sausage * - * + * * ``` * * ### Alert Buttons From f8ea096b05ae04ce2ff4bfb19a815a828c3d92c4 Mon Sep 17 00:00:00 2001 From: mhartington Date: Mon, 18 Apr 2016 10:53:23 -0400 Subject: [PATCH 06/68] docs(list): update doc example --- ionic/components/list/list.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ionic/components/list/list.ts b/ionic/components/list/list.ts index ca2eb1b298..1435db4784 100644 --- a/ionic/components/list/list.ts +++ b/ionic/components/list/list.ts @@ -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(); * } From cef019c66fd927abd793b4d9ad17d91c3eb2827a Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 18 Apr 2016 11:16:42 -0400 Subject: [PATCH 07/68] docs(demos): update show-hide-when demos to include windows also added the code used to the demo --- demos/hide-when/main.html | 31 +++++++++++++++++++++++++------ demos/show-when/main.html | 27 +++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/demos/hide-when/main.html b/demos/hide-when/main.html index 276139286a..bcc127dc9d 100644 --- a/demos/hide-when/main.html +++ b/demos/hide-when/main.html @@ -6,12 +6,28 @@
Hide Icon Per Platform

In this example we're using the hideWhen directive to decide whether to hide an icon based on the platform.

- - - + + + - + <ion-icon name="logo-apple" hideWhen="android,windows"></ion-icon> + + + + + + + + <ion-icon name="logo-android" hideWhen="ios,windows"></ion-icon> + + + + + + + + <ion-icon name="logo-windows" hideWhen="ios,android"></ion-icon> @@ -20,7 +36,10 @@ .hide-when-demo ion-col { background: #f8f8f8; border: 1px solid #ddd; - margin: 5px; + margin: 1%; + max-width: 48%; + flex: 0 0 48%; + min-height: 120px; } .hide-when-demo code { @@ -28,6 +47,6 @@ } .hide-when-demo ion-icon { - font-size: 200px; + font-size: 100px; } diff --git a/demos/show-when/main.html b/demos/show-when/main.html index 34de682560..0407f2d803 100644 --- a/demos/show-when/main.html +++ b/demos/show-when/main.html @@ -6,12 +6,28 @@
Show Icon Per Platform

In this example we're using the showWhen directive to decide whether to show an icon based on the platform.

- + + + + + <ion-icon name="logo-apple" showWhen="ios"></ion-icon> + + + + - + <ion-icon name="logo-android" showWhen="android"></ion-icon> + + + + + + + + <ion-icon name="logo-windows" showWhen="windows"></ion-icon> @@ -20,7 +36,10 @@ .show-when-demo ion-col { background: #f8f8f8; border: 1px solid #ddd; - margin: 5px; + margin: 1%; + max-width: 48%; + flex: 0 0 48%; + min-height: 120px; } .show-when-demo code { @@ -28,6 +47,6 @@ } .show-when-demo ion-icon { - font-size: 200px; + font-size: 100px; } From 370054a071dd6bd7afbfdc6984dc7c6209ee4995 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 18 Apr 2016 11:18:52 -0400 Subject: [PATCH 08/68] docs(showHideWhen): update API docs for showWhen/hideWhen include links to the platform API docs and a little more information on the directives closes driftyco/ionic-site#503 --- .../show-hide-when/show-hide-when.ts | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/ionic/components/show-hide-when/show-hide-when.ts b/ionic/components/show-hide-when/show-hide-when.ts index 28b88b779d..42831b7aa8 100644 --- a/ionic/components/show-hide-when/show-hide-when.ts +++ b/ionic/components/show-hide-when/show-hide-when.ts @@ -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 *
@@ -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 *
@@ -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: { From faf634ceabd8b6dcb713d66538163da69c579a89 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 18 Apr 2016 11:22:09 -0400 Subject: [PATCH 09/68] docs(demos): update platform demo so iOS is first --- demos/platform/main.html | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/demos/platform/main.html b/demos/platform/main.html index 252c4cfbfd..9a39319c12 100644 --- a/demos/platform/main.html +++ b/demos/platform/main.html @@ -9,14 +9,6 @@ Name Value - - -
platform.is('android')
-
- -
{{isAndroid}}
-
-
platform.is('ios')
@@ -25,6 +17,14 @@
{{isIos}}
+ + +
platform.is('android')
+
+ +
{{isAndroid}}
+
+
platform.is('windows')
From b3b553e50f1cb2e225346cc677bc2aeb1aa02ad3 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 18 Apr 2016 11:38:55 -0400 Subject: [PATCH 10/68] docs(show-hide-when): fix link to platform docs --- ionic/components/show-hide-when/show-hide-when.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ionic/components/show-hide-when/show-hide-when.ts b/ionic/components/show-hide-when/show-hide-when.ts index 42831b7aa8..ead749f783 100644 --- a/ionic/components/show-hide-when/show-hide-when.ts +++ b/ionic/components/show-hide-when/show-hide-when.ts @@ -68,7 +68,7 @@ export class DisplayWhen { * 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 + * View the [Platform API docs](../../../platform/Platform) for more information on the different * platforms you can use. * * @usage @@ -95,7 +95,7 @@ export class DisplayWhen { * ``` * @demo /docs/v2/demos/show-when/ * @see {@link ../HideWhen HideWhen API Docs} - * @see {@link ../../platform/Platform Platform API Docs} + * @see {@link ../../../platform/Platform Platform API Docs} */ @Directive({ selector: '[showWhen]', @@ -126,7 +126,7 @@ export class ShowWhen extends DisplayWhen { * 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 + * View the [Platform API docs](../../../platform/Platform) for more information on the different * platforms you can use. * * @usage @@ -154,7 +154,7 @@ export class ShowWhen extends DisplayWhen { * * @demo /docs/v2/demos/hide-when/ * @see {@link ../ShowWhen ShowWhen API Docs} - * @see {@link ../../platform/Platform Platform API Docs} + * @see {@link ../../../platform/Platform Platform API Docs} */ @Directive({ selector: '[hideWhen]', From 7263728da11cb03c30d500cf41cb0b1947f37b2e Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 18 Apr 2016 13:43:53 -0400 Subject: [PATCH 11/68] fix(slides): add id to the slide component to grab the correct pagination fixes #5745 references #5508 --- ionic/components/slides/slides.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/ionic/components/slides/slides.ts b/ionic/components/slides/slides.ts index e71acaef71..6a1711ed5b 100644 --- a/ionic/components/slides/slides.ts +++ b/ionic/components/slides/slides.ts @@ -72,6 +72,9 @@ import {Scroll} from '../scroll/scroll'; '
' + '
', directives: [NgClass], + host: { + '[id]': 'slideId' + }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, }) @@ -82,6 +85,16 @@ export class Slides extends Ion { */ public rapidUpdate: Function; + /** + * @private + */ + private id: number; + + /** + * @private + */ + private slideId: string; + /** * @private */ @@ -204,6 +217,9 @@ export class Slides extends Ion { this.rapidUpdate = debounce(() => { this.update(); }, 10); + + this.id = ++slidesId; + this.slideId = 'slides-' + this.id; } /** @@ -216,8 +232,10 @@ export class Slides extends Ion { this.showPager = isTrueProperty(this.pager); + let paginationId = '#' + this.slideId + ' .swiper-pagination'; + var options = defaults({ - pagination: '.swiper-pagination', + pagination: paginationId, }, this.options); options.onTap = (swiper, e) => { @@ -706,3 +724,5 @@ export class Slide { } }) export class SlideLazy {} + +let slidesId = -1; From 3fb79cf99d369ceb80d7f7a10312c5db2f9c1c4d Mon Sep 17 00:00:00 2001 From: Dave Ackerman Date: Tue, 22 Mar 2016 12:29:14 -0700 Subject: [PATCH 12/68] feat(): add toast component --- ionic/components.core.scss | 1 + ionic/components.ios.scss | 1 + ionic/components.md.scss | 1 + ionic/components.ts | 1 + ionic/components/toast/test/basic/e2e.ts | 8 + ionic/components/toast/test/basic/index.ts | 78 ++++++ ionic/components/toast/test/basic/main.html | 11 + ionic/components/toast/toast.ios.scss | 39 +++ ionic/components/toast/toast.md.scss | 41 +++ ionic/components/toast/toast.scss | 43 ++++ ionic/components/toast/toast.ts | 263 ++++++++++++++++++++ ionic/config/modes.ts | 9 + 12 files changed, 496 insertions(+) create mode 100644 ionic/components/toast/test/basic/e2e.ts create mode 100644 ionic/components/toast/test/basic/index.ts create mode 100644 ionic/components/toast/test/basic/main.html create mode 100644 ionic/components/toast/toast.ios.scss create mode 100644 ionic/components/toast/toast.md.scss create mode 100644 ionic/components/toast/toast.scss create mode 100644 ionic/components/toast/toast.ts diff --git a/ionic/components.core.scss b/ionic/components.core.scss index 7252f4a8ad..e3a102a431 100644 --- a/ionic/components.core.scss +++ b/ionic/components.core.scss @@ -26,6 +26,7 @@ "components/show-hide-when/show-hide-when", "components/slides/slides", "components/spinner/spinner", + "components/toast/toast", "components/virtual-scroll/virtual-scroll"; diff --git a/ionic/components.ios.scss b/ionic/components.ios.scss index aa02af24f1..b37c65aff0 100644 --- a/ionic/components.ios.scss +++ b/ionic/components.ios.scss @@ -27,6 +27,7 @@ "components/select/select.ios", "components/tabs/tabs.ios", "components/toggle/toggle.ios", + "components/toast/toast.ios", "components/toolbar/toolbar.ios"; diff --git a/ionic/components.md.scss b/ionic/components.md.scss index 91911f4c62..4515f65aa1 100644 --- a/ionic/components.md.scss +++ b/ionic/components.md.scss @@ -27,6 +27,7 @@ "components/select/select.md", "components/tabs/tabs.md", "components/toggle/toggle.md", + "components/toast/toast.md", "components/toolbar/toolbar.md"; diff --git a/ionic/components.ts b/ionic/components.ts index 86455e54bb..a961e2ab84 100644 --- a/ionic/components.ts +++ b/ionic/components.ts @@ -47,5 +47,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' diff --git a/ionic/components/toast/test/basic/e2e.ts b/ionic/components/toast/test/basic/e2e.ts new file mode 100644 index 0000000000..cffef5a0be --- /dev/null +++ b/ionic/components/toast/test/basic/e2e.ts @@ -0,0 +1,8 @@ + +it('should open action sheet', function() { + element(by.css('.e2eOpenActionSheet')).click(); +}); + +it('should close with backdrop click', function() { + element(by.css('.backdrop')).click(); +}); diff --git a/ionic/components/toast/test/basic/index.ts b/ionic/components/toast/test/basic/index.ts new file mode 100644 index 0000000000..b6341523ff --- /dev/null +++ b/ionic/components/toast/test/basic/index.ts @@ -0,0 +1,78 @@ +import {App, Page, Toast, NavController, Platform} from 'ionic-angular'; + +@Page({ + templateUrl: 'main.html' +}) +class E2EPage { + constructor( + private nav: NavController, + private platform: Platform) + {} + + showToast() { + const toast = Toast.create({ + message: 'User was created successfully', + }); + + this.nav.present(toast); + } + + 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.', + }); + + this.nav.present(toast); + } + + showDismissDurationToast() { + const toast = Toast.create({ + message: 'I am dismissed after 1.5 seconds', + duration: 1500 + }); + + 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' + }); + + this.nav.present(toast); + } + +} + +@Page({ + template: ` + + + + + Toast + + + Hi, I'm Bob, and I'm a modal. + + ` +}) +class ToastPage { + constructor(private viewCtrl: ViewController) { } + + dismiss() { + this.viewCtrl.dismiss(); + } +} + + +@App({ + template: '' +}) +class E2EApp { + constructor() { + this.root = E2EPage; + } +} diff --git a/ionic/components/toast/test/basic/main.html b/ionic/components/toast/test/basic/main.html new file mode 100644 index 0000000000..f6407aa6d2 --- /dev/null +++ b/ionic/components/toast/test/basic/main.html @@ -0,0 +1,11 @@ + + Toasts + + + + + +
+ + +
diff --git a/ionic/components/toast/toast.ios.scss b/ionic/components/toast/toast.ios.scss new file mode 100644 index 0000000000..1c6badde01 --- /dev/null +++ b/ionic/components/toast/toast.ios.scss @@ -0,0 +1,39 @@ +// iOS Toast +// -------------------------------------------------- +$toast-ios-text-align: left !default; +$toast-ios-background: rgba(0, 0, 0, 0.70) !default; +$toast-ios-border-radius: 0.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 { + display: block; + height: $toast-width; + left: 0; + position: absolute; + top: 0; + width: $toast-width; + z-index: $z-index-overlay; +} + +.toast-wrapper { + background: $toast-ios-background; + border-radius: $toast-ios-border-radius; + bottom: 10px; + display: block; + left: 10px; + margin: auto; + max-width: $toast-max-width; + position: absolute; + right: 10px; + transform: translate3d(0, 100%, 0); + z-index: $z-index-overlay-wrapper; +} + +.toast-message { + color: $toast-ios-title-color; + font-size: $toast-ios-title-font-size; + padding: $toast-ios-title-padding; +} diff --git a/ionic/components/toast/toast.md.scss b/ionic/components/toast/toast.md.scss new file mode 100644 index 0000000000..a50b34b29f --- /dev/null +++ b/ionic/components/toast/toast.md.scss @@ -0,0 +1,41 @@ +@import "../../globals.md"; + +// Material Design Toast +// -------------------------------------------------- +$toast-md-text-align: left !default; +$toast-md-background: #333333 !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 { + display: block; + height: $toast-width; + left: 0; + position: absolute; + top: 0; + width: $toast-width; + z-index: $z-index-overlay; +} + +.toast-wrapper { + background: $toast-md-background; + bottom: 0; + display: block; + left: 0; + margin: auto; + max-width: $toast-max-width; + position: absolute; + right: 0; + transform: translate3d(0, 100%, 0); + width: $toast-width; + z-index: $z-index-overlay-wrapper; +} + +.toast-message { + color: $toast-md-title-color; + font-size: $toast-md-title-font-size; + padding: $toast-md-title-padding; +} diff --git a/ionic/components/toast/toast.scss b/ionic/components/toast/toast.scss new file mode 100644 index 0000000000..3a09c7954e --- /dev/null +++ b/ionic/components/toast/toast.scss @@ -0,0 +1,43 @@ +@import "../../globals.ios"; + +// Action Sheet +// -------------------------------------------------- + +$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 { + font-size: 1.5rem; + padding: 19px 16px 17px; + } +} + +.toast-message { + flex: 1; +} + +.toast-wrapper { + bottom: 0; + display: block; + left: 0; + margin: auto; + max-width: $toast-max-width; + position: absolute; + right: 0; + transform: translate3d(0, 100%, 0); + z-index: $z-index-overlay-wrapper; +} diff --git a/ionic/components/toast/toast.ts b/ionic/components/toast/toast.ts new file mode 100644 index 0000000000..b7d216b600 --- /dev/null +++ b/ionic/components/toast/toast.ts @@ -0,0 +1,263 @@ +import {Component, ElementRef, Renderer} 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 + * An Toast is a small message that appears in the lower part of the screen. + * It's useful for displaying success messages, error messages, etc. + * `title`, `subTitle` and `message`. + * + * @usage + * ```ts + * constructor(nav: NavController) { + * this.nav = nav; + * } + * + * presentToast() { + * let toast = Toast.create({ + * message: 'User was added successfully', + * duration: 3000 + * }); + * this.nav.present(toast); + * } + * ``` + * + * @demo /docs/v2/demos/toast/ + */ +export class Toast extends ViewController { + + constructor(opts: ToastOptions = {}) { + console.log('Toast Constructor'); + opts.enableBackdropDismiss = isPresent(opts.enableBackdropDismiss) ? !!opts.enableBackdropDismiss : true; + + super(ToastCmp, opts); + + this.viewType = 'toast'; + this.isOverlay = false; + + // 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 | Description | + * |-----------------------|-----------|--------------------------------------------------------------------------- | + * | message | `string` | The message for the toast | + * | duration | `number` | The amount of time in milliseconds the toast should appear (optional) | + * | cssClass | `string` | Any additional class for the toast (optional) | + * | showCloseButton | `boolean` | Whether or not to show an optional button to close the toast. (optional) | + * | closeButtonText | `string` | Text to display in the close button. (optional) | + * | enableBackdropDismiss | `boolean` | Whether the the toast should be dismissed by tapping the backdrop (optional) | + * + * @param {object} opts Toast. See the tabel above + */ + static create(opts: ToastOptions = {}) { + return new Toast(opts); + } + +} + + +/** +* @private +*/ +@Component({ + selector: 'ion-toast', + template: ` + +
+
+
{{d.message}}
+ +
+
+ `, + 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 id: number; + private created: number; + + 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 = 'acst-hdr-' + this.id; + } + } + + onPageDidEnter() { + let activeElement: any = document.activeElement; + if (document.activeElement) { + activeElement.blur(); + } + + let focusableEle = this._elementRef.nativeElement.querySelector('button'); + + if (focusableEle) { + focusableEle.focus(); + } + + // if there's a `duration` set, automatically dismiss. + setTimeout(() => this.dismiss('backdrop'), this.d.duration ? this.d.duration : 3000) + + } + + click(button, dismissDelay?) { + if (!this.isEnabled()) { + return; + } + let shouldDismiss = true; + + if (shouldDismiss) { + setTimeout(() => { + this.dismiss(button.role); + }, dismissDelay || this._config.get('pageTransitionDelay')); + } + } + + bdClick() { + if (this.isEnabled() && this.d.enableBackdropDismiss) { + this.dismiss('backdrop'); + } + } + + dismiss(role): Promise { + return this._viewCtrl.dismiss(null, role); + } + + isEnabled() { + let tm = this._config.getNumber('overlayCreatedDiff', 750); + return (this.created + tm < Date.now()); + } + +} + +export interface ToastOptions { + title?: string; + cssClass?: string; + buttons?: Array; + duration?: number, + showCloseButton?: boolean; + closeButtonText?: string; + enableBackdropDismiss?: 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', '100%', '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%', '100%'); + 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', '100%', '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%', '100%'); + backdrop.fromTo('opacity', 0, 0); + this.easing('cubic-bezier(.36,.66,.04,1)').duration(450).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); + + +let toastIds = -1; diff --git a/ionic/config/modes.ts b/ionic/config/modes.ts index 590952479a..55901bfcf8 100644 --- a/ionic/config/modes.ts +++ b/ionic/config/modes.ts @@ -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', From de8cc08261f5cb75b17c0d2e89dd79f1d6350bca Mon Sep 17 00:00:00 2001 From: Dave Ackerman Date: Thu, 24 Mar 2016 14:15:59 -0700 Subject: [PATCH 13/68] clearTimeout after toast is dismissed, add WP styles --- ionic/components.wp.scss | 1 + ionic/components/toast/test/basic/index.ts | 23 ----------- ionic/components/toast/toast.ts | 47 +++++++++++++++++++++- ionic/components/toast/toast.wp.scss | 33 +++++++++++++++ 4 files changed, 79 insertions(+), 25 deletions(-) create mode 100644 ionic/components/toast/toast.wp.scss diff --git a/ionic/components.wp.scss b/ionic/components.wp.scss index 5cdf5f81d1..e1e423801f 100644 --- a/ionic/components.wp.scss +++ b/ionic/components.wp.scss @@ -27,6 +27,7 @@ "components/select/select.wp", "components/tabs/tabs.wp", "components/toggle/toggle.wp", + "components/toast/toast.wp", "components/toolbar/toolbar.wp"; diff --git a/ionic/components/toast/test/basic/index.ts b/ionic/components/toast/test/basic/index.ts index b6341523ff..27d12585af 100644 --- a/ionic/components/toast/test/basic/index.ts +++ b/ionic/components/toast/test/basic/index.ts @@ -43,31 +43,8 @@ class E2EPage { this.nav.present(toast); } - } -@Page({ - template: ` - - - - - Toast - - - Hi, I'm Bob, and I'm a modal. - - ` -}) -class ToastPage { - constructor(private viewCtrl: ViewController) { } - - dismiss() { - this.viewCtrl.dismiss(); - } -} - - @App({ template: '' }) diff --git a/ionic/components/toast/toast.ts b/ionic/components/toast/toast.ts index b7d216b600..5d177c882b 100644 --- a/ionic/components/toast/toast.ts +++ b/ionic/components/toast/toast.ts @@ -121,6 +121,7 @@ class ToastCmp { private hdrId: string; private id: number; private created: number; + private dismissTimeout: number = undefined; constructor( private _nav: NavController, @@ -158,8 +159,7 @@ class ToastCmp { } // if there's a `duration` set, automatically dismiss. - setTimeout(() => this.dismiss('backdrop'), this.d.duration ? this.d.duration : 3000) - + this.dismissTimeout = setTimeout(() => this.dismiss('backdrop'), this.d.duration ? this.d.duration : 3000) } click(button, dismissDelay?) { @@ -182,6 +182,8 @@ class ToastCmp { } dismiss(role): Promise { + clearTimeout(this.dismissTimeout); + this.dismissTimeout = undefined; return this._viewCtrl.dismiss(null, role); } @@ -254,10 +256,51 @@ class ToastMdSlideOut extends Transition { } } +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; diff --git a/ionic/components/toast/toast.wp.scss b/ionic/components/toast/toast.wp.scss new file mode 100644 index 0000000000..86c82ebcd5 --- /dev/null +++ b/ionic/components/toast/toast.wp.scss @@ -0,0 +1,33 @@ +// 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 { + background: $toast-wp-background; + border-radius: $toast-wp-border-radius; + bottom: 0; + display: block; + left: 0; + margin: auto; + max-width: $toast-max-width; + position: absolute; + transform: translate3d(0, 100%, 0); + z-index: $z-index-overlay-wrapper; +} + +.toast-message { + color: $toast-wp-title-color; + font-size: $toast-wp-title-font-size; + padding: $toast-wp-title-padding; +} + +.toast-button { + color: $toast-wp-button-color; +} From 8b55812ec086b3469fad4d7d077fa0006d1f9a81 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 18 Apr 2016 17:32:48 -0400 Subject: [PATCH 14/68] docs(demos): remove user agent from platform demo --- demos/platform/index.ts | 2 -- demos/platform/main.html | 8 -------- 2 files changed, 10 deletions(-) diff --git a/demos/platform/index.ts b/demos/platform/index.ts index 9d386daa91..f44bff5517 100644 --- a/demos/platform/index.ts +++ b/demos/platform/index.ts @@ -7,12 +7,10 @@ class ApiDemoApp { isIos: boolean; isAndroid: boolean; isWindows: boolean; - userAgent: string; constructor(platform: Platform) { this.isIos = platform.is('ios'); this.isAndroid = platform.is('android'); this.isWindows = platform.is('windows'); - this.userAgent = platform.userAgent(); } } diff --git a/demos/platform/main.html b/demos/platform/main.html index 9a39319c12..ee64912631 100644 --- a/demos/platform/main.html +++ b/demos/platform/main.html @@ -33,14 +33,6 @@
{{isWindows}}
- - -
platform.userAgent()
-
- -
{{userAgent}}
-
-
From 58570f2da5cd9b20739881a7adaf54d316c2902d Mon Sep 17 00:00:00 2001 From: mhartington Date: Mon, 18 Apr 2016 17:36:50 -0400 Subject: [PATCH 15/68] docs(id): make private for now --- ionic/components/app/id.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ionic/components/app/id.ts b/ionic/components/app/id.ts index cc5f83e604..b070d4ac83 100644 --- a/ionic/components/app/id.ts +++ b/ionic/components/app/id.ts @@ -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 From 0a83f2f26026fdf1519688a3ac15c05d51373766 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Mon, 18 Apr 2016 17:30:50 -0500 Subject: [PATCH 16/68] feat(directives): auto provide IONIC_DIRECTIVES to all components Closes #6092 --- ionic/components/nav/test/basic/index.ts | 5 +- ionic/decorators/app.ts | 14 +++-- ionic/decorators/page.ts | 54 ++++--------------- .../generators/component/component.tmpl.js | 4 +- .../generators/component/component.tmpl.ts | 4 +- 5 files changed, 23 insertions(+), 58 deletions(-) diff --git a/ionic/components/nav/test/basic/index.ts b/ionic/components/nav/test/basic/index.ts index ba4fb24999..5b52e09d47 100644 --- a/ionic/components/nav/test/basic/index.ts +++ b/ionic/components/nav/test/basic/index.ts @@ -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: `

My Custom Component Test

`, - directives: [IONIC_DIRECTIVES] + template: `

My Custom Component Test

` }) class MyCmpTest{} diff --git a/ionic/decorators/app.ts b/ionic/decorators/app.ts index 896d599e5a..a946eb1d4c 100644 --- a/ionic/decorators/app.ts +++ b/ionic/decorators/app.ts @@ -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 if (!args.templateUrl && !args.template) { args.template = ''; @@ -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(); } diff --git a/ionic/decorators/page.ts b/ionic/decorators/page.ts index 3eaff8175b..f00e16c020 100644 --- a/ionic/decorators/page.ts +++ b/ionic/decorators/page.ts @@ -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: `
- * - *
`, - * 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 ``, so although + * Pages have their content automatically wrapped in ``, 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'; diff --git a/tooling/generators/component/component.tmpl.js b/tooling/generators/component/component.tmpl.js index a1285b9835..d13ca07ecd 100644 --- a/tooling/generators/component/component.tmpl.js +++ b/tooling/generators/component/component.tmpl.js @@ -1,5 +1,4 @@ import {Component} from 'angular2/core'; -import {IONIC_DIRECTIVES} from 'ionic-angular'; /* Generated class for the <%= jsClassName %> component. @@ -9,8 +8,7 @@ import {IONIC_DIRECTIVES} from 'ionic-angular'; */ @Component({ selector: '<%= fileName %>', - templateUrl: 'build/<%= directory %>/<%= fileName %>/<%= fileName %>.html', - directives: [IONIC_DIRECTIVES] // makes all Ionic directives available to your component + templateUrl: 'build/<%= directory %>/<%= fileName %>/<%= fileName %>.html' }) export class <%= jsClassName %> { constructor() { diff --git a/tooling/generators/component/component.tmpl.ts b/tooling/generators/component/component.tmpl.ts index a1285b9835..d13ca07ecd 100644 --- a/tooling/generators/component/component.tmpl.ts +++ b/tooling/generators/component/component.tmpl.ts @@ -1,5 +1,4 @@ import {Component} from 'angular2/core'; -import {IONIC_DIRECTIVES} from 'ionic-angular'; /* Generated class for the <%= jsClassName %> component. @@ -9,8 +8,7 @@ import {IONIC_DIRECTIVES} from 'ionic-angular'; */ @Component({ selector: '<%= fileName %>', - templateUrl: 'build/<%= directory %>/<%= fileName %>/<%= fileName %>.html', - directives: [IONIC_DIRECTIVES] // makes all Ionic directives available to your component + templateUrl: 'build/<%= directory %>/<%= fileName %>/<%= fileName %>.html' }) export class <%= jsClassName %> { constructor() { From f5b9474452b4fefc9bf408e38a0df1c21d137a8a Mon Sep 17 00:00:00 2001 From: Dave Ackerman Date: Mon, 18 Apr 2016 15:36:05 -0700 Subject: [PATCH 17/68] some docs updates, cleanup --- ionic/components/toast/test/basic/index.ts | 33 ++++++++++------ ionic/components/toast/test/basic/main.html | 2 + ionic/components/toast/toast.ts | 44 +++++++-------------- 3 files changed, 38 insertions(+), 41 deletions(-) diff --git a/ionic/components/toast/test/basic/index.ts b/ionic/components/toast/test/basic/index.ts index 27d12585af..51934592fe 100644 --- a/ionic/components/toast/test/basic/index.ts +++ b/ionic/components/toast/test/basic/index.ts @@ -1,19 +1,21 @@ -import {App, Page, Toast, NavController, Platform} from 'ionic-angular'; +import {App, Page, Toast, NavController} from 'ionic-angular'; @Page({ templateUrl: 'main.html' }) class E2EPage { - constructor( - private nav: NavController, - private platform: Platform) - {} + + private dismissMessage: string; + + + constructor(private nav: NavController) { } showToast() { const toast = Toast.create({ message: 'User was created successfully', }); + toast.onDismiss(this.dismissHandler); this.nav.present(toast); } @@ -22,16 +24,17 @@ class E2EPage { 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 - }); - - this.nav.present(toast); + const toast = Toast.create({ + message: 'I am dismissed after 1.5 seconds', + duration: 1500 + }); + toast.onDismiss(this.dismissHandler); + this.nav.present(toast); } showToastWithCloseButton() { @@ -40,9 +43,15 @@ class E2EPage { showCloseButton: true, closeButtonText: 'Ok' }); - + toast.onDismiss(this.dismissHandler); this.nav.present(toast); } + + private dismissHandler(toast: Toast) { + console.info('Toast onDismiss()'); + + } + } @App({ diff --git a/ionic/components/toast/test/basic/main.html b/ionic/components/toast/test/basic/main.html index f6407aa6d2..e3d4c63eb9 100644 --- a/ionic/components/toast/test/basic/main.html +++ b/ionic/components/toast/test/basic/main.html @@ -8,4 +8,6 @@
+ + {{ dismissMessage }} diff --git a/ionic/components/toast/toast.ts b/ionic/components/toast/toast.ts index 5d177c882b..3bd0285712 100644 --- a/ionic/components/toast/toast.ts +++ b/ionic/components/toast/toast.ts @@ -1,4 +1,4 @@ -import {Component, ElementRef, Renderer} from 'angular2/core'; +import {Component, ElementRef, Renderer, Output, EventEmitter} from 'angular2/core'; import {NgClass, NgIf, NgFor} from 'angular2/common'; import {Button} from '../button/button'; @@ -18,7 +18,6 @@ import {ViewController} from '../nav/view-controller'; * @description * An Toast is a small message that appears in the lower part of the screen. * It's useful for displaying success messages, error messages, etc. - * `title`, `subTitle` and `message`. * * @usage * ```ts @@ -40,11 +39,10 @@ import {ViewController} from '../nav/view-controller'; export class Toast extends ViewController { constructor(opts: ToastOptions = {}) { - console.log('Toast Constructor'); opts.enableBackdropDismiss = isPresent(opts.enableBackdropDismiss) ? !!opts.enableBackdropDismiss : true; - super(ToastCmp, opts); + this.viewType = 'toast'; this.isOverlay = false; @@ -54,6 +52,8 @@ export class Toast extends ViewController { this.fireOtherLifecycles = false; } + + /** * @private */ @@ -75,14 +75,14 @@ export class Toast extends ViewController { * * | Property | Type | Description | * |-----------------------|-----------|--------------------------------------------------------------------------- | - * | message | `string` | The message for the toast | - * | duration | `number` | The amount of time in milliseconds the toast should appear (optional) | - * | cssClass | `string` | Any additional class for the toast (optional) | - * | showCloseButton | `boolean` | Whether or not to show an optional button to close the toast. (optional) | - * | closeButtonText | `string` | Text to display in the close button. (optional) | - * | enableBackdropDismiss | `boolean` | Whether the the toast should be dismissed by tapping the backdrop (optional) | + * | message | `string` | The message for the toast. Long strings will wrap and the toast container will expand. **(required)** | + * | duration | `number` | The amount of time in milliseconds the toast should appear *(optional)* | + * | cssClass | `string` | Any additional class for the toast *(optional)* | + * | showCloseButton | `boolean` | Whether or not to show an optional button to close the toast. *(optional)* | + * | closeButtonText | `string` | Text to display in the close button. *(optional)* | + * | enableBackdropDismiss | `boolean` | Whether the the toast should be dismissed by tapping the backdrop *(optional)* | * - * @param {object} opts Toast. See the tabel above + * @param {object} ToastOptions Toast. See the above table for available options. */ static create(opts: ToastOptions = {}) { return new Toast(opts); @@ -119,7 +119,6 @@ class ToastCmp { private d: any; private descId: string; private hdrId: string; - private id: number; private created: number; private dismissTimeout: number = undefined; @@ -139,16 +138,14 @@ class ToastCmp { renderer.setElementClass(_elementRef.nativeElement, this.d.cssClass, true); } - this.id = (++toastIds); - if (this.d.message) { this.hdrId = 'acst-hdr-' + this.id; } } onPageDidEnter() { - let activeElement: any = document.activeElement; - if (document.activeElement) { + const { activeElement }: any = document; + if (activeElement) { activeElement.blur(); } @@ -267,11 +264,7 @@ class ToastWpPopIn extends Transition { 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); + this.easing('cubic-bezier(0,0 0.05,1)').duration(200).add(backdrop).add(wrapper); } } @@ -286,11 +279,7 @@ class ToastWpPopOut extends Transition { 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); + this.easing('ease-out').duration(150).add(backdrop).add(wrapper); } } @@ -301,6 +290,3 @@ 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; From 1c755dcb34792e57a3ccdfb5b173707b732fc0d2 Mon Sep 17 00:00:00 2001 From: perry Date: Mon, 18 Apr 2016 17:36:43 -0500 Subject: [PATCH 18/68] chore(dgeni): differentiate properties v methods. Closes driftyco/ionic-site#498 --- scripts/docs/processors/collect-inputs-outputs.js | 6 ++++++ scripts/docs/templates/common.template.html | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/scripts/docs/processors/collect-inputs-outputs.js b/scripts/docs/processors/collect-inputs-outputs.js index f92c14ce23..c8b382e650 100644 --- a/scripts/docs/processors/collect-inputs-outputs.js +++ b/scripts/docs/processors/collect-inputs-outputs.js @@ -12,6 +12,12 @@ module.exports = function collectInputsOutputs() { memberLoop: for (var i in doc.members) { + + // identify properties to differentiate from methods + if (typeof doc.members[i].parameters == 'undefined') { + doc.members[i].isProperty = true; + } + if (doc.members[i].decorators && doc.members[i].decorators.length) { decoratorLoop: diff --git a/scripts/docs/templates/common.template.html b/scripts/docs/templates/common.template.html index 15ca4cba13..0a43975412 100644 --- a/scripts/docs/templates/common.template.html +++ b/scripts/docs/templates/common.template.html @@ -128,11 +128,11 @@ angular_controller: APIDemoCtrl <@ endif @> <@- macro functionSyntax(fn) @> <@- set sep = joiner(', ') -@> - <$ fn.name $>(<@- for param in fn.params @><$ sep() $> + <$ fn.name $><@- if not fn.isProperty @>(<@ endif -@><@- for param in fn.params @><$ sep() $> <@- if param.type.optional @>[<@ endif -@> <$ param.name $> <@- if param.type.optional @>]<@ endif -@> - <@ endfor @>) + <@ endfor @><@- if not fn.isProperty @>)<@ endif -@> <@ if fn.alias @>(alias: <$ fn.alias $>)<@ endif @> <@ endmacro -@> @@ -249,7 +249,7 @@ Improve this doc <@- if doc.statics.length -@> -

Static Methods

+

Static Members

<@- for method in doc.statics @><@ if not method.internal @>

<$ functionSyntax(method) $>

@@ -283,7 +283,7 @@ Improve this doc <@- if doc.members and doc.members.length @> -

Instance Methods

+

Instance Members

<@- for method in doc.members @>
From bef4a67025c76af80b5482a2878244152a1b9288 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Mon, 18 Apr 2016 21:22:39 -0500 Subject: [PATCH 19/68] feat(content): add scrollToBottom --- ionic/components/content/content.ts | 11 ++++++++++- ionic/components/nav/test/basic/index.ts | 5 +++++ ionic/util/scroll-view.ts | 12 ++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/ionic/components/content/content.ts b/ionic/components/content/content.ts index 667d13cd2c..cda57396d7 100644 --- a/ionic/components/content/content.ts +++ b/ionic/components/content/content.ts @@ -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 */ diff --git a/ionic/components/nav/test/basic/index.ts b/ionic/components/nav/test/basic/index.ts index 5b52e09d47..cb70681b7c 100644 --- a/ionic/components/nav/test/basic/index.ts +++ b/ionic/components/nav/test/basic/index.ts @@ -48,6 +48,7 @@ class MyCmpTest{} + @@ -121,6 +122,10 @@ class FirstPage { scrollToTop() { this.content.scrollToTop(); } + + scrollToBottom() { + this.content.scrollToBottom(1000); + } } diff --git a/ionic/util/scroll-view.ts b/ionic/util/scroll-view.ts index 212acae63f..c0ab1e7d28 100644 --- a/ionic/util/scroll-view.ts +++ b/ionic/util/scroll-view.ts @@ -104,6 +104,18 @@ export class ScrollView { }); } + scrollToTop(duration: number): Promise { + return this.scrollTo(0, 0, duration); + } + + scrollToBottom(duration: number): Promise { + let y = 0; + if (this._el) { + y = this._el.scrollHeight - this._el.clientHeight; + } + return this.scrollTo(0, y, duration); + } + stop() { this.isPlaying = false; } From 5aa816946a0cc5782aff54cc2c9d70ade2c5f0d8 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Mon, 18 Apr 2016 23:50:26 -0500 Subject: [PATCH 20/68] docs(nav): add Nav Transition Promises section --- ionic/components/nav/nav-controller.ts | 41 ++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/ionic/components/nav/nav-controller.ts b/ionic/components/nav/nav-controller.ts index 1cc3c3176e..9f2c3640ec 100644 --- a/ionic/components/nav/nav-controller.ts +++ b/ionic/components/nav/nav-controller.ts @@ -101,6 +101,44 @@ import {ViewController} from './view-controller'; * - `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 on has finished. + * + * In the example below, after we receive some data asynchronously, we then want transition + * to another page. Where the problem comes in, is that if we received the data 200ms after + * the first transition started, then kicking off another transition halfway through + * the first transition ends up with a janky transition. 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 + * getSomeAsyncData().then(() => { + * // incase we received the data faster than the time it + * // took to finish the first transition, this logic should + * // always wait 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} */ export class NavController extends Ion { @@ -178,6 +216,9 @@ export class NavController extends Ion { ]); } + /** + * @private + */ setPortal(val: Portal) { this._portal = val; } From 28eb819a0e1e2a29f56c11b8c21bae7c5eb27bd7 Mon Sep 17 00:00:00 2001 From: mhartington Date: Tue, 19 Apr 2016 10:24:49 -0400 Subject: [PATCH 21/68] docs(navController): update life cylce hooks --- ionic/components/nav/nav-controller.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/ionic/components/nav/nav-controller.ts b/ionic/components/nav/nav-controller.ts index 9f2c3640ec..ba002c3e2d 100644 --- a/ionic/components/nav/nav-controller.ts +++ b/ionic/components/nav/nav-controller.ts @@ -91,16 +91,17 @@ 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 * From 98e0c233403213dbda174e54238710876597f6a3 Mon Sep 17 00:00:00 2001 From: mhartington Date: Tue, 19 Apr 2016 10:36:53 -0400 Subject: [PATCH 22/68] docs(navController): update links References https://github.com/driftyco/ionic-site/issues/554 --- ionic/components/nav/nav-controller.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ionic/components/nav/nav-controller.ts b/ionic/components/nav/nav-controller.ts index ba002c3e2d..d9e993032d 100644 --- a/ionic/components/nav/nav-controller.ts +++ b/ionic/components/nav/nav-controller.ts @@ -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. * - *

Injecting NavController

+ * ### Injecting NavController * Injecting NavController will always get you an instance of the nearest * NavController, regardless of whether it is a Tab or a Nav. * @@ -57,8 +57,7 @@ import {ViewController} from './view-controller'; * } * } * ``` - * - *

Page creation

+ * ## Page creation * _For more information on the `@Page` decorator see the [@Page API * reference](../../../decorators/Page/)._ * @@ -72,8 +71,7 @@ import {ViewController} from './view-controller'; * example). They are destroyed when removed from the navigation stack (on * [pop()](#pop) or [setRoot()](#setRoot)). * - * - *

Lifecycle events

+ * ## Lifecycle events * Lifecycle events are fired during various stages of navigation. They can be * defined in any `@Page` decorated component class. * From f1b6d93dce7269d347ce25ffd589b2a73aa2df94 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 19 Apr 2016 09:58:10 -0500 Subject: [PATCH 23/68] docs(alert): dismissing and async navigation --- ionic/components/alert/alert.ts | 56 ++++++++++++++++++++ ionic/components/alert/test/dismiss/index.ts | 45 +++++++++++++++- 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/ionic/components/alert/alert.ts b/ionic/components/alert/alert.ts index 6554984b6b..6803dbfa5e 100644 --- a/ionic/components/alert/alert.ts +++ b/ionic/components/alert/alert.ts @@ -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(() => { + * // 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 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 { diff --git a/ionic/components/alert/test/dismiss/index.ts b/ionic/components/alert/test/dismiss/index.ts index d882d44f19..dc99facae1 100644 --- a/ionic/components/alert/test/dismiss/index.ts +++ b/ionic/components/alert/test/dismiss/index.ts @@ -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 {
+

+ +

` }) @@ -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); + } + } From 83af18b87eba686c24ff937a43927e2c0cbd810c Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 19 Apr 2016 10:17:42 -0500 Subject: [PATCH 24/68] docs(actionsheet): dismissing and async navigation --- ionic/components/action-sheet/action-sheet.ts | 58 ++++++++++++++++++- ionic/components/alert/alert.ts | 2 +- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/ionic/components/action-sheet/action-sheet.ts b/ionic/components/action-sheet/action-sheet.ts index 6479980f95..54b73c336d 100644 --- a/ionic/components/action-sheet/action-sheet.ts +++ b/ionic/components/action-sheet/action-sheet.ts @@ -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 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} */ @@ -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; } diff --git a/ionic/components/alert/alert.ts b/ionic/components/alert/alert.ts index 6803dbfa5e..fc294eed3b 100644 --- a/ionic/components/alert/alert.ts +++ b/ionic/components/alert/alert.ts @@ -158,7 +158,7 @@ import {ViewController} from '../nav/view-controller'; * let navTransition = alert.dismiss(); * * // start some async method - * someAsyncOperation(() => { + * someAsyncOperation().then(() => { * // once the async operation has completed * // then run the next nav transition after the * // first transition has finished animating out From 8eedb2b463b67737db294091897e6f7cba3a42b1 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 19 Apr 2016 10:38:37 -0500 Subject: [PATCH 25/68] docs(nav): update nav transition promises docs --- ionic/components/nav/nav-controller.ts | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/ionic/components/nav/nav-controller.ts b/ionic/components/nav/nav-controller.ts index d9e993032d..4ad642ce1b 100644 --- a/ionic/components/nav/nav-controller.ts +++ b/ionic/components/nav/nav-controller.ts @@ -57,6 +57,8 @@ import {ViewController} from './view-controller'; * } * } * ``` + * + * * ## Page creation * _For more information on the `@Page` decorator see the [@Page API * reference](../../../decorators/Page/)._ @@ -71,6 +73,7 @@ import {ViewController} from './view-controller'; * example). They are destroyed when removed from the navigation stack (on * [pop()](#pop) or [setRoot()](#setRoot)). * + * * ## Lifecycle events * Lifecycle events are fired during various stages of navigation. They can be * defined in any `@Page` decorated component class. @@ -100,8 +103,7 @@ import {ViewController} from './view-controller'; * | `onPageDidUnload` | Runs after the page has been destroyed and its elements have been removed. * * - * - * ### Nav Transition Promises + * ## 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 @@ -114,23 +116,23 @@ import {ViewController} from './view-controller'; * 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 on has finished. + * it's better to fire the next nav transition when the previous one has finished. * - * In the example below, after we receive some data asynchronously, we then want transition - * to another page. Where the problem comes in, is that if we received the data 200ms after - * the first transition started, then kicking off another transition halfway through - * the first transition ends up with a janky transition. Instead, it's best to always - * ensure the first transition has already finished before starting the next. + * 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 - * getSomeAsyncData().then(() => { - * // incase we received the data faster than the time it - * // took to finish the first transition, this logic should - * // always wait that the previous transition has resolved + * 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); From 8d8cc4c097c1ea79f9141d5abb8f82e92cc329d9 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 19 Apr 2016 11:27:34 -0500 Subject: [PATCH 26/68] fix(tabs): do not init w/ tab that is hidden or disabled Closes #6226 --- ionic/components/tabs/tabs.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/ionic/components/tabs/tabs.ts b/ionic/components/tabs/tabs.ts index 38408acabd..65edb2f2ba 100644 --- a/ionic/components/tabs/tabs.ts +++ b/ionic/components/tabs/tabs.ts @@ -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) { From 902fbd0f62109b6fd0841cf6e1b4b90ea39a75cb Mon Sep 17 00:00:00 2001 From: Mike Hartington Date: Tue, 19 Apr 2016 14:18:40 -0400 Subject: [PATCH 27/68] docs(ionicDirectives): add docs for IONIC_DIRECTIVES --- ionic/config/directives.ts | 160 +++++++++++++++++++++---------------- 1 file changed, 92 insertions(+), 68 deletions(-) diff --git a/ionic/config/directives.ts b/ionic/config/directives.ts index 8db68a0a71..a2e1e64bff 100644 --- a/ionic/config/directives.ts +++ b/ionic/config/directives.ts @@ -44,79 +44,103 @@ 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: "" + * }) + * + * 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 + * - Blur + * - 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 From 20feed0cc843b3a680a357db290efb7b02c7ac30 Mon Sep 17 00:00:00 2001 From: Perry Govier Date: Tue, 19 Apr 2016 15:04:07 -0500 Subject: [PATCH 28/68] chore(dgeni): allow markdown in the return description --- scripts/docs/templates/common.template.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/docs/templates/common.template.html b/scripts/docs/templates/common.template.html index 0a43975412..d2aad1b011 100644 --- a/scripts/docs/templates/common.template.html +++ b/scripts/docs/templates/common.template.html @@ -142,7 +142,7 @@ angular_controller: APIDemoCtrl <@ endif @> <@- endmacro -@> <@- macro typeInfo(fn) -@> - <$ typeList(fn.typeList) $> <$ fn.description $> + <$ typeList(fn.typeList) $> <$ fn.description | marked $> <@- endmacro -@> <@ block body @> From 381a650f8ee4cd6f0b3dd512d818f7cc9159b96d Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 19 Apr 2016 17:34:51 -0400 Subject: [PATCH 29/68] test(slides): update slides tests with new API and add new one references #5508 --- .../slides/test/controller/index.ts | 52 +++++++++++++++++++ .../slides/test/controller/main.html | 16 ++++++ ionic/components/slides/test/intro/main.html | 2 +- ionic/components/slides/test/loop/index.ts | 5 +- ionic/components/slides/test/loop/main.html | 4 +- 5 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 ionic/components/slides/test/controller/index.ts create mode 100644 ionic/components/slides/test/controller/main.html diff --git a/ionic/components/slides/test/controller/index.ts b/ionic/components/slides/test/controller/index.ts new file mode 100644 index 0000000000..42ddeed8ca --- /dev/null +++ b/ionic/components/slides/test/controller/index.ts @@ -0,0 +1,52 @@ +import {ViewChild} from 'angular2/core'; +import {App, Slides} from 'ionic-angular'; + + +@App({ + templateUrl: 'main.html' +}) +class MyApp { + mySlideOptions: any; + @ViewChild(Slides) slider: Slides; + + constructor() { + console.log("here"); + this.mySlideOptions = { + initialSlide: 1, + autoplay: 1000 + }; + } + + ngAfterViewInit() { + + } + + onSlideChanged() { + console.log("Slide Changed"); + let isEnd = this.slider.isEnd(); + console.log("This is the last slide?", isEnd); + } + + goToPrevSlide() { + this.slider.slidePrev(5000, false); + } + + goToNextSlide() { + this.slider.slideNext(); + } + + goToSlide(index) { + console.log(index); + this.slider.slideTo(index, 500, false); + } + + getIndex() { + let index = this.slider.getActiveIndex(); + console.log("Current Index is", index); + } + + getLength() { + let length = this.slider.length(); + console.log("Current Length is", length); + } +} diff --git a/ionic/components/slides/test/controller/main.html b/ionic/components/slides/test/controller/main.html new file mode 100644 index 0000000000..5ba590e94b --- /dev/null +++ b/ionic/components/slides/test/controller/main.html @@ -0,0 +1,16 @@ + + +

Slide 1

+ + +
+ +

Slide 2

+ + +
+ +

Slide 3

+ +
+
diff --git a/ionic/components/slides/test/intro/main.html b/ionic/components/slides/test/intro/main.html index 33cb3a55e9..f2a1e4d888 100644 --- a/ionic/components/slides/test/intro/main.html +++ b/ionic/components/slides/test/intro/main.html @@ -8,7 +8,7 @@ - +

Thank you for choosing the Awesome App!