diff --git a/CHANGELOG.md b/CHANGELOG.md index efe6efb725..12ceba7af4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,30 @@ +## [4.6.1](https://github.com/ionic-team/ionic/compare/v4.6.0...v4.6.1) (2019-07-09) + + +### Bug Fixes + +* **app:** add hydrated to hide white screen with multiple ionic dependencies ([#18649](https://github.com/ionic-team/ionic/issues/18649)) +* **datetime:** datetime no longer reports having a value if none is set ([#18541](https://github.com/ionic-team/ionic/issues/18541)) ([92e0f98](https://github.com/ionic-team/ionic/commit/92e0f98)), closes [#17979](https://github.com/ionic-team/ionic/issues/17979) [#18540](https://github.com/ionic-team/ionic/issues/18540) +* **fab-button:** set opacity on disabled fab button ([#18685](https://github.com/ionic-team/ionic/issues/18685)) ([6042b39](https://github.com/ionic-team/ionic/commit/6042b39)), closes [#18682](https://github.com/ionic-team/ionic/issues/18682) +* **icon:** load icons properly with baseHref ([#18650](https://github.com/ionic-team/ionic/issues/18650)), ([#18637](https://github.com/ionic-team/ionic/issues/18637)) +* **icon:** bind icon name properly ([#18707](https://github.com/ionic-team/ionic/issues/18707)) +* **infinite-scroll:** fix scroll listener ([0d58101](https://github.com/ionic-team/ionic/commit/0d58101)) +* **item:** do not disable entire item if there are multiple inputs ([#18696](https://github.com/ionic-team/ionic/issues/18696)) ([dfa2b13](https://github.com/ionic-team/ionic/commit/dfa2b13)), closes [#18655](https://github.com/ionic-team/ionic/issues/18655) [#18670](https://github.com/ionic-team/ionic/issues/18670) +* **router-link:** add missing target prop ([#18659](https://github.com/ionic-team/ionic/issues/18659)) ([1f51ab2](https://github.com/ionic-team/ionic/commit/1f51ab2)), closes [#18655](https://github.com/ionic-team/ionic/issues/18655) +* **router-outlet:** fix swipe to go back ([b69fb69](https://github.com/ionic-team/ionic/commit/b69fb69)) +* **scss:** copy all scss files ([36a58df](https://github.com/ionic-team/ionic/commit/36a58df)) +* **searchbar:** proper styling after navigating ([#18642](https://github.com/ionic-team/ionic/issues/18642)) +* **slides:** use correct order for pushing slides dynamically ([#18633](https://github.com/ionic-team/ionic/issues/18633)) +* **tabs:** select proper tab by default and do not emit tab change if selectedTab is undefined ([03c834c](https://github.com/ionic-team/ionic/commit/03c834c)) +* **overlay:** make create opts optional ([44c88ad](https://github.com/ionic-team/ionic/commit/44c88ad)) + + +### Performance Improvements + +* **angular:** skip zone ([e059fc8](https://github.com/ionic-team/ionic/commit/e059fc8)) + + + # [4.6.0 Carbon](https://github.com/ionic-team/ionic/compare/v4.5.0...v4.6.0) (2019-06-26) diff --git a/angular/package.json b/angular/package.json index 8f56d7d746..c2757beaa3 100644 --- a/angular/package.json +++ b/angular/package.json @@ -1,6 +1,6 @@ { "name": "@ionic/angular", - "version": "4.6.0", + "version": "4.6.1", "description": "Angular specific wrappers for @ionic/core", "keywords": [ "ionic", @@ -45,7 +45,7 @@ "css/" ], "dependencies": { - "@ionic/core": "4.6.0", + "@ionic/core": "4.6.1", "tslib": "^1.9.3" }, "peerDependencies": { @@ -73,12 +73,12 @@ "@angular/platform-browser": "^7.2.1", "@angular/platform-browser-dynamic": "^7.2.1", "@angular/router": "^7.2.1", - "@types/node": "~10.12.0", + "@types/node": "~12.0.12", "fs-extra": "^7.0.0", "glob": "^7.1.3", "rollup": "^1.1.2", "rollup-plugin-node-resolve": "^4.0.0", - "rxjs": "^6.2.0", + "rxjs": "^6.5.2", "tsickle": "^0.34.0", "tslint": "^5.12.1", "tslint-ionic-rules": "0.0.21", diff --git a/angular/src/app-initialize.ts b/angular/src/app-initialize.ts index f45630042a..65cdadf37b 100644 --- a/angular/src/app-initialize.ts +++ b/angular/src/app-initialize.ts @@ -1,56 +1,42 @@ +import { NgZone } from '@angular/core'; import { applyPolyfills, defineCustomElements } from '@ionic/core/loader'; import { Config } from './providers/config'; import { IonicWindow } from './types/interfaces'; -export function appInitialize(config: Config, doc: Document) { +export function appInitialize(config: Config, doc: Document, zone: NgZone) { return (): any => { const win: IonicWindow | undefined = doc.defaultView as any; if (win) { const Ionic = win.Ionic = win.Ionic || {}; - Ionic.config = config; + Ionic.config = { + ...config, + _zoneGate: (h: any) => zone.run(h) + }; + + const aelFn = '__zone_symbol__addEventListener' in (document.body as any) + ? '__zone_symbol__addEventListener' + : 'addEventListener'; return applyPolyfills().then(() => { return defineCustomElements(win, { exclude: ['ion-tabs', 'ion-tab'], syncQueue: true, - raf: h => (win.__zone_symbol__requestAnimationFrame) ? win.__zone_symbol__requestAnimationFrame(h) : requestAnimationFrame(h), + jmp: (h: any) => zone.runOutsideAngular(h), + raf: h => { + return zone.runOutsideAngular(() => { + return (win.__zone_symbol__requestAnimationFrame) ? win.__zone_symbol__requestAnimationFrame(h) : requestAnimationFrame(h); + }); + }, ael(elm, eventName, cb, opts) { - if ((elm as any).__zone_symbol__addEventListener && skipZone(eventName)) { - (elm as any).__zone_symbol__addEventListener(eventName, cb, opts); - } else { - elm.addEventListener(eventName, cb, opts); - } + (elm as any)[aelFn](eventName, cb, opts); }, rel(elm, eventName, cb, opts) { - if ((elm as any).__zone_symbol__removeEventListener && skipZone(eventName)) { - (elm as any).__zone_symbol__removeEventListener(eventName, cb, opts); - } else { - elm.removeEventListener(eventName, cb, opts); - } + elm.removeEventListener(eventName, cb, opts); } }); }); } }; } - -const SKIP_ZONE = [ - 'scroll', - 'resize', - - 'touchstart', - 'touchmove', - 'touchend', - - 'mousedown', - 'mousemove', - 'mouseup', - - 'ionStyle', -]; - -function skipZone(eventName: string) { - return SKIP_ZONE.indexOf(eventName) >= 0; -} diff --git a/angular/src/directives/control-value-accessors/value-accessor.ts b/angular/src/directives/control-value-accessors/value-accessor.ts index bca308cd7f..9ce45ecf51 100644 --- a/angular/src/directives/control-value-accessors/value-accessor.ts +++ b/angular/src/directives/control-value-accessors/value-accessor.ts @@ -42,16 +42,14 @@ export class ValueAccessor implements ControlValueAccessor { } export function setIonicClasses(element: ElementRef) { - requestAnimationFrame(() => { - const input = element.nativeElement as HTMLElement; - const classes = getClasses(input); - setClasses(input, classes); + const input = element.nativeElement as HTMLElement; + const classes = getClasses(input); + setClasses(input, classes); - const item = input.closest('ion-item'); - if (item) { - setClasses(item, classes); - } - }); + const item = input.closest('ion-item'); + if (item) { + setClasses(item, classes); + } } function getClasses(element: HTMLElement) { diff --git a/angular/src/directives/navigation/ion-router-outlet.ts b/angular/src/directives/navigation/ion-router-outlet.ts index 02701a9a0f..55cf6e3610 100644 --- a/angular/src/directives/navigation/ion-router-outlet.ts +++ b/angular/src/directives/navigation/ion-router-outlet.ts @@ -1,5 +1,5 @@ import { Location } from '@angular/common'; -import { Attribute, ChangeDetectorRef, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, Injector, NgZone, OnDestroy, OnInit, Optional, Output, SkipSelf, ViewContainerRef } from '@angular/core'; +import { Attribute, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, Injector, NgZone, OnDestroy, OnInit, Optional, Output, SkipSelf, ViewContainerRef } from '@angular/core'; import { ActivatedRoute, ChildrenOutletContexts, OutletContext, PRIMARY_OUTLET, Router } from '@angular/router'; import { BehaviorSubject, Observable } from 'rxjs'; import { distinctUntilChanged, filter, switchMap } from 'rxjs/operators'; @@ -57,7 +57,6 @@ export class IonRouterOutlet implements OnDestroy, OnInit { private resolver: ComponentFactoryResolver, @Attribute('name') name: string, @Optional() @Attribute('tabs') tabs: string, - private changeDetector: ChangeDetectorRef, private config: Config, private navCtrl: NavController, commonLocation: Location, @@ -206,12 +205,11 @@ export class IonRouterOutlet implements OnDestroy, OnInit { // Calling `markForCheck` to make sure we will run the change detection when the // `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component. enteringView = this.stackCtrl.createView(this.activated, activatedRoute); + enteringView.ref.changeDetectorRef.detectChanges(); // Store references to the proxy by component this.proxyMap.set(cmpRef.instance, activatedRouteProxy); this.currentActivatedRoute$.next({ component: cmpRef.instance, activatedRoute }); - - this.changeDetector.markForCheck(); } this.activatedView = enteringView; diff --git a/angular/src/directives/navigation/stack-controller.ts b/angular/src/directives/navigation/stack-controller.ts index 89b2778642..ba647815c6 100644 --- a/angular/src/directives/navigation/stack-controller.ts +++ b/angular/src/directives/navigation/stack-controller.ts @@ -44,11 +44,7 @@ export class StackController { getExistingView(activatedRoute: ActivatedRoute): RouteView | undefined { const activatedUrlKey = getUrl(this.router, activatedRoute); - const view = this.views.find(vw => vw.url === activatedUrlKey); - if (view) { - view.ref.changeDetectorRef.reattach(); - } - return view; + return this.views.find(vw => vw.url === activatedUrlKey); } setActive(enteringView: RouteView): Promise { @@ -95,15 +91,15 @@ export class StackController { } const views = this.insertView(enteringView, direction); - return this.wait(async () => { - await this.transition(enteringView, leavingView, animation, this.canGoBack(1), false); - await cleanupAsync(enteringView, views, viewsSnapshot, this.location); - return { - enteringView, - direction, - animation, - tabSwitch - }; + return this.wait(() => { + return this.transition(enteringView, leavingView, animation, this.canGoBack(1), false) + .then(() => cleanupAsync(enteringView, views, viewsSnapshot, this.location)) + .then(() => ({ + enteringView, + direction, + animation, + tabSwitch + })); }); } @@ -138,13 +134,12 @@ export class StackController { }); } - async startBackTransition() { + startBackTransition() { const leavingView = this.activeView; if (leavingView) { const views = this.getStack(leavingView.stackId); const enteringView = views[views.length - 2]; - enteringView.ref.changeDetectorRef.reattach(); - await this.wait(() => { + return this.wait(() => { return this.transition( enteringView, // entering view leavingView, // leaving view @@ -154,6 +149,7 @@ export class StackController { ); }); } + return Promise.resolve(); } endBackTransition(shouldComplete: boolean) { @@ -189,7 +185,7 @@ export class StackController { return this.views.slice(); } - private async transition( + private transition( enteringView: RouteView | undefined, leavingView: RouteView | undefined, direction: 'forward' | 'back' | undefined, @@ -198,8 +194,16 @@ export class StackController { ) { if (this.skipTransition) { this.skipTransition = false; - return; + return Promise.resolve(false); } + if (enteringView) { + enteringView.ref.changeDetectorRef.reattach(); + } + // TODO: disconnect leaving page from change detection to + // reduce jank during the page transition + // if (leavingView) { + // leavingView.ref.changeDetectorRef.detach(); + // } const enteringEl = enteringView ? enteringView.element : undefined; const leavingEl = leavingView ? leavingView.element : undefined; const containerEl = this.containerEl; @@ -209,15 +213,15 @@ export class StackController { containerEl.appendChild(enteringEl); } - await containerEl.componentOnReady(); - await containerEl.commit(enteringEl, leavingEl, { + return this.zone.runOutsideAngular(() => containerEl.commit(enteringEl, leavingEl, { deepWait: true, duration: direction === undefined ? 0 : undefined, direction, showGoBack, progressAnimation - }); + })); } + return Promise.resolve(false); } private async wait(task: () => Promise): Promise { @@ -245,7 +249,6 @@ function cleanup(activeRoute: RouteView, views: RouteView[], viewsSnapshot: Rout .forEach(destroyView); views.forEach(view => { - /** * In the event that a user navigated multiple * times in rapid succession, we want to make sure diff --git a/angular/src/directives/proxies-utils.ts b/angular/src/directives/proxies-utils.ts index 889d35ad1d..6d549650e3 100644 --- a/angular/src/directives/proxies-utils.ts +++ b/angular/src/directives/proxies-utils.ts @@ -6,7 +6,9 @@ export function proxyInputs(Cmp: any, inputs: string[]) { inputs.forEach(item => { Object.defineProperty(Prototype, item, { get() { return this.el[item]; }, - set(val: any) { this.el[item] = val; }, + set(val: any) { + this.z.runOutsideAngular(() => this.el[item] = val); + }, }); }); } @@ -16,7 +18,7 @@ export function proxyMethods(Cmp: any, methods: string[]) { methods.forEach(methodName => { Prototype[methodName] = function() { const args = arguments; - return this.el.componentOnReady().then((el: any) => el[methodName].apply(el, args)); + return this.z.runOutsideAngular(() => this.el[methodName].apply(this.el, args)); }; }); } diff --git a/angular/src/directives/proxies.ts b/angular/src/directives/proxies.ts index 61c6f057f0..f0fef6c748 100644 --- a/angular/src/directives/proxies.ts +++ b/angular/src/directives/proxies.ts @@ -1,15 +1,15 @@ /* tslint:disable */ /* auto-generated angular directive proxies */ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, NgZone } from '@angular/core'; import { proxyInputs, proxyMethods, proxyOutputs } from './proxies-utils'; -import { Components } from '@ionic/core' +import { Components } from '@ionic/core'; export declare interface IonApp extends Components.IonApp {} @Component({ selector: 'ion-app', changeDetection: ChangeDetectionStrategy.OnPush, template: '' }) export class IonApp { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -19,7 +19,7 @@ export declare interface IonAvatar extends Components.IonAvatar {} @Component({ selector: 'ion-avatar', changeDetection: ChangeDetectionStrategy.OnPush, template: '' }) export class IonAvatar { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -29,7 +29,7 @@ export declare interface IonBackButton extends Components.IonBackButton {} @Component({ selector: 'ion-back-button', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['color', 'defaultHref', 'disabled', 'icon', 'mode', 'text', 'type'] }) export class IonBackButton { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -41,7 +41,7 @@ export declare interface IonBackdrop extends Components.IonBackdrop {} export class IonBackdrop { ionBackdropTap!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionBackdropTap']); @@ -53,7 +53,7 @@ export declare interface IonBadge extends Components.IonBadge {} @Component({ selector: 'ion-badge', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['color', 'mode'] }) export class IonBadge { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -66,7 +66,7 @@ export class IonButton { ionFocus!: EventEmitter; ionBlur!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionFocus', 'ionBlur']); @@ -78,7 +78,7 @@ export declare interface IonButtons extends Components.IonButtons {} @Component({ selector: 'ion-buttons', changeDetection: ChangeDetectionStrategy.OnPush, template: '' }) export class IonButtons { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -88,7 +88,7 @@ export declare interface IonCard extends Components.IonCard {} @Component({ selector: 'ion-card', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['button', 'color', 'disabled', 'download', 'href', 'mode', 'rel', 'routerDirection', 'target', 'type'] }) export class IonCard { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -99,7 +99,7 @@ export declare interface IonCardContent extends Components.IonCardContent {} @Component({ selector: 'ion-card-content', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['mode'] }) export class IonCardContent { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -110,7 +110,7 @@ export declare interface IonCardHeader extends Components.IonCardHeader {} @Component({ selector: 'ion-card-header', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['color', 'mode', 'translucent'] }) export class IonCardHeader { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -121,7 +121,7 @@ export declare interface IonCardSubtitle extends Components.IonCardSubtitle {} @Component({ selector: 'ion-card-subtitle', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['color', 'mode'] }) export class IonCardSubtitle { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -132,7 +132,7 @@ export declare interface IonCardTitle extends Components.IonCardTitle {} @Component({ selector: 'ion-card-title', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['color', 'mode'] }) export class IonCardTitle { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -146,7 +146,7 @@ export class IonCheckbox { ionFocus!: EventEmitter; ionBlur!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionChange', 'ionFocus', 'ionBlur']); @@ -158,7 +158,7 @@ export declare interface IonChip extends Components.IonChip {} @Component({ selector: 'ion-chip', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['color', 'mode', 'outline'] }) export class IonChip { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -169,7 +169,7 @@ export declare interface IonCol extends Components.IonCol {} @Component({ selector: 'ion-col', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['offset', 'offsetLg', 'offsetMd', 'offsetSm', 'offsetXl', 'offsetXs', 'pull', 'pullLg', 'pullMd', 'pullSm', 'pullXl', 'pullXs', 'push', 'pushLg', 'pushMd', 'pushSm', 'pushXl', 'pushXs', 'size', 'sizeLg', 'sizeMd', 'sizeSm', 'sizeXl', 'sizeXs'] }) export class IonCol { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -183,7 +183,7 @@ export class IonContent { ionScroll!: EventEmitter; ionScrollEnd!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionScrollStart', 'ionScroll', 'ionScrollEnd']); @@ -200,7 +200,7 @@ export class IonDatetime { ionFocus!: EventEmitter; ionBlur!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionCancel', 'ionChange', 'ionFocus', 'ionBlur']); @@ -213,7 +213,7 @@ export declare interface IonFab extends Components.IonFab {} @Component({ selector: 'ion-fab', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['activated', 'edge', 'horizontal', 'vertical'] }) export class IonFab { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -227,7 +227,7 @@ export class IonFabButton { ionFocus!: EventEmitter; ionBlur!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionFocus', 'ionBlur']); @@ -239,7 +239,7 @@ export declare interface IonFabList extends Components.IonFabList {} @Component({ selector: 'ion-fab-list', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['activated', 'side'] }) export class IonFabList { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -250,7 +250,7 @@ export declare interface IonFooter extends Components.IonFooter {} @Component({ selector: 'ion-footer', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['mode', 'translucent'] }) export class IonFooter { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -261,7 +261,7 @@ export declare interface IonGrid extends Components.IonGrid {} @Component({ selector: 'ion-grid', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['fixed'] }) export class IonGrid { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -272,7 +272,7 @@ export declare interface IonHeader extends Components.IonHeader {} @Component({ selector: 'ion-header', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['mode', 'translucent'] }) export class IonHeader { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -283,7 +283,7 @@ export declare interface IonIcon extends Components.IonIcon {} @Component({ selector: 'ion-icon', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['ariaLabel', 'color', 'flipRtl', 'icon', 'ios', 'lazy', 'md', 'mode', 'name', 'size', 'src'] }) export class IonIcon { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -297,7 +297,7 @@ export class IonImg { ionImgDidLoad!: EventEmitter; ionError!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionImgWillLoad', 'ionImgDidLoad', 'ionError']); @@ -310,7 +310,7 @@ export declare interface IonInfiniteScroll extends Components.IonInfiniteScroll export class IonInfiniteScroll { ionInfinite!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionInfinite']); @@ -323,7 +323,7 @@ export declare interface IonInfiniteScrollContent extends Components.IonInfinite @Component({ selector: 'ion-infinite-scroll-content', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['loadingSpinner', 'loadingText'] }) export class IonInfiniteScrollContent { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -338,7 +338,7 @@ export class IonInput { ionBlur!: EventEmitter; ionFocus!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionInput', 'ionChange', 'ionBlur', 'ionFocus']); @@ -351,7 +351,7 @@ export declare interface IonItem extends Components.IonItem {} @Component({ selector: 'ion-item', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['button', 'color', 'detail', 'detailIcon', 'disabled', 'download', 'href', 'lines', 'mode', 'rel', 'routerDirection', 'target', 'type'] }) export class IonItem { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -362,7 +362,7 @@ export declare interface IonItemDivider extends Components.IonItemDivider {} @Component({ selector: 'ion-item-divider', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['color', 'mode', 'sticky'] }) export class IonItemDivider { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -373,7 +373,7 @@ export declare interface IonItemGroup extends Components.IonItemGroup {} @Component({ selector: 'ion-item-group', changeDetection: ChangeDetectionStrategy.OnPush, template: '' }) export class IonItemGroup { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -383,7 +383,7 @@ export declare interface IonItemOption extends Components.IonItemOption {} @Component({ selector: 'ion-item-option', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['color', 'disabled', 'download', 'expandable', 'href', 'mode', 'rel', 'target', 'type'] }) export class IonItemOption { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -395,7 +395,7 @@ export declare interface IonItemOptions extends Components.IonItemOptions {} export class IonItemOptions { ionSwipe!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionSwipe']); @@ -408,7 +408,7 @@ export declare interface IonItemSliding extends Components.IonItemSliding {} export class IonItemSliding { ionDrag!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionDrag']); @@ -421,7 +421,7 @@ export declare interface IonLabel extends Components.IonLabel {} @Component({ selector: 'ion-label', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['color', 'mode', 'position'] }) export class IonLabel { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -432,7 +432,7 @@ export declare interface IonList extends Components.IonList {} @Component({ selector: 'ion-list', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['inset', 'lines', 'mode'] }) export class IonList { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -444,7 +444,7 @@ export declare interface IonListHeader extends Components.IonListHeader {} @Component({ selector: 'ion-list-header', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['color', 'mode'] }) export class IonListHeader { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -459,7 +459,7 @@ export class IonMenu { ionDidOpen!: EventEmitter; ionDidClose!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionWillOpen', 'ionWillClose', 'ionDidOpen', 'ionDidClose']); @@ -472,7 +472,7 @@ export declare interface IonMenuButton extends Components.IonMenuButton {} @Component({ selector: 'ion-menu-button', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['autoHide', 'color', 'disabled', 'menu', 'type'] }) export class IonMenuButton { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -483,7 +483,7 @@ export declare interface IonMenuToggle extends Components.IonMenuToggle {} @Component({ selector: 'ion-menu-toggle', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['autoHide', 'menu'] }) export class IonMenuToggle { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -496,7 +496,7 @@ export class IonNav { ionNavWillChange!: EventEmitter; ionNavDidChange!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionNavWillChange', 'ionNavDidChange']); @@ -509,7 +509,7 @@ export declare interface IonNavPop extends Components.IonNavPop {} @Component({ selector: 'ion-nav-pop', changeDetection: ChangeDetectionStrategy.OnPush, template: '' }) export class IonNavPop { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -519,7 +519,7 @@ export declare interface IonNavPush extends Components.IonNavPush {} @Component({ selector: 'ion-nav-push', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['component', 'componentProps'] }) export class IonNavPush { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -530,7 +530,7 @@ export declare interface IonNavSetRoot extends Components.IonNavSetRoot {} @Component({ selector: 'ion-nav-set-root', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['component', 'componentProps'] }) export class IonNavSetRoot { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -541,7 +541,7 @@ export declare interface IonNote extends Components.IonNote {} @Component({ selector: 'ion-note', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['color', 'mode'] }) export class IonNote { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -552,7 +552,7 @@ export declare interface IonProgressBar extends Components.IonProgressBar {} @Component({ selector: 'ion-progress-bar', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['buffer', 'color', 'mode', 'reversed', 'type', 'value'] }) export class IonProgressBar { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -566,7 +566,7 @@ export class IonRadio { ionFocus!: EventEmitter; ionBlur!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionSelect', 'ionFocus', 'ionBlur']); @@ -579,7 +579,7 @@ export declare interface IonRadioGroup extends Components.IonRadioGroup {} export class IonRadioGroup { ionChange!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionChange']); @@ -594,7 +594,7 @@ export class IonRange { ionFocus!: EventEmitter; ionBlur!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionChange', 'ionFocus', 'ionBlur']); @@ -609,7 +609,7 @@ export class IonRefresher { ionPull!: EventEmitter; ionStart!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionRefresh', 'ionPull', 'ionStart']); @@ -622,7 +622,7 @@ export declare interface IonRefresherContent extends Components.IonRefresherCont @Component({ selector: 'ion-refresher-content', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['pullingIcon', 'pullingText', 'refreshingSpinner', 'refreshingText'] }) export class IonRefresherContent { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -633,7 +633,7 @@ export declare interface IonReorder extends Components.IonReorder {} @Component({ selector: 'ion-reorder', changeDetection: ChangeDetectionStrategy.OnPush, template: '' }) export class IonReorder { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -644,7 +644,7 @@ export declare interface IonReorderGroup extends Components.IonReorderGroup {} export class IonReorderGroup { ionItemReorder!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionItemReorder']); @@ -657,7 +657,7 @@ export declare interface IonRippleEffect extends Components.IonRippleEffect {} @Component({ selector: 'ion-ripple-effect', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['type'] }) export class IonRippleEffect { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -669,7 +669,7 @@ export declare interface IonRow extends Components.IonRow {} @Component({ selector: 'ion-row', changeDetection: ChangeDetectionStrategy.OnPush, template: '' }) export class IonRow { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -685,7 +685,7 @@ export class IonSearchbar { ionBlur!: EventEmitter; ionFocus!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionInput', 'ionChange', 'ionCancel', 'ionClear', 'ionBlur', 'ionFocus']); @@ -700,7 +700,7 @@ export class IonSegment { ionChange!: EventEmitter; ionStyle!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionChange', 'ionStyle']); @@ -713,7 +713,7 @@ export declare interface IonSegmentButton extends Components.IonSegmentButton {} export class IonSegmentButton { ionSelect!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionSelect']); @@ -729,7 +729,7 @@ export class IonSelect { ionFocus!: EventEmitter; ionBlur!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionChange', 'ionCancel', 'ionFocus', 'ionBlur']); @@ -742,7 +742,7 @@ export declare interface IonSelectOption extends Components.IonSelectOption {} @Component({ selector: 'ion-select-option', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['disabled', 'selected', 'value'] }) export class IonSelectOption { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -753,7 +753,7 @@ export declare interface IonSkeletonText extends Components.IonSkeletonText {} @Component({ selector: 'ion-skeleton-text', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['animated', 'width'] }) export class IonSkeletonText { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -764,7 +764,7 @@ export declare interface IonSlide extends Components.IonSlide {} @Component({ selector: 'ion-slide', changeDetection: ChangeDetectionStrategy.OnPush, template: '' }) export class IonSlide { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -790,7 +790,7 @@ export class IonSlides { ionSlideTouchStart!: EventEmitter; ionSlideTouchEnd!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionSlidesDidLoad', 'ionSlideTap', 'ionSlideDoubleTap', 'ionSlideWillChange', 'ionSlideDidChange', 'ionSlideNextStart', 'ionSlidePrevStart', 'ionSlideNextEnd', 'ionSlidePrevEnd', 'ionSlideTransitionStart', 'ionSlideTransitionEnd', 'ionSlideDrag', 'ionSlideReachStart', 'ionSlideReachEnd', 'ionSlideTouchStart', 'ionSlideTouchEnd']); @@ -803,7 +803,7 @@ export declare interface IonSpinner extends Components.IonSpinner {} @Component({ selector: 'ion-spinner', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['color', 'duration', 'name', 'paused'] }) export class IonSpinner { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -815,7 +815,7 @@ export declare interface IonSplitPane extends Components.IonSplitPane {} export class IonSplitPane { ionSplitPaneVisible!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionSplitPaneVisible']); @@ -827,7 +827,7 @@ export declare interface IonTabBar extends Components.IonTabBar {} @Component({ selector: 'ion-tab-bar', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['color', 'mode', 'selectedTab', 'translucent'] }) export class IonTabBar { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -838,7 +838,7 @@ export declare interface IonTabButton extends Components.IonTabButton {} @Component({ selector: 'ion-tab-button', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['disabled', 'download', 'href', 'layout', 'mode', 'rel', 'selected', 'tab', 'target'] }) export class IonTabButton { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -849,7 +849,7 @@ export declare interface IonText extends Components.IonText {} @Component({ selector: 'ion-text', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['color', 'mode'] }) export class IonText { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -864,7 +864,7 @@ export class IonTextarea { ionBlur!: EventEmitter; ionFocus!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionChange', 'ionInput', 'ionBlur', 'ionFocus']); @@ -877,7 +877,7 @@ export declare interface IonThumbnail extends Components.IonThumbnail {} @Component({ selector: 'ion-thumbnail', changeDetection: ChangeDetectionStrategy.OnPush, template: '' }) export class IonThumbnail { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -887,7 +887,7 @@ export declare interface IonTitle extends Components.IonTitle {} @Component({ selector: 'ion-title', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['color'] }) export class IonTitle { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } @@ -901,7 +901,7 @@ export class IonToggle { ionFocus!: EventEmitter; ionBlur!: EventEmitter; protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionChange', 'ionFocus', 'ionBlur']); @@ -913,7 +913,7 @@ export declare interface IonToolbar extends Components.IonToolbar {} @Component({ selector: 'ion-toolbar', changeDetection: ChangeDetectionStrategy.OnPush, template: '', inputs: ['color', 'mode'] }) export class IonToolbar { protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef) { + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; } diff --git a/angular/src/ionic-module.ts b/angular/src/ionic-module.ts index 6afe231c9c..5d92468a2b 100644 --- a/angular/src/ionic-module.ts +++ b/angular/src/ionic-module.ts @@ -1,5 +1,5 @@ import { CommonModule, DOCUMENT } from '@angular/common'; -import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core'; +import { APP_INITIALIZER, ModuleWithProviders, NgModule, NgZone } from '@angular/core'; import { IonicConfig } from '@ionic/core'; import { appInitialize } from './app-initialize'; @@ -142,7 +142,8 @@ export class IonicModule { multi: true, deps: [ ConfigToken, - DOCUMENT + DOCUMENT, + NgZone ] } ] diff --git a/angular/src/providers/angular-delegate.ts b/angular/src/providers/angular-delegate.ts index eceef7e5b0..36ac4fc001 100644 --- a/angular/src/providers/angular-delegate.ts +++ b/angular/src/providers/angular-delegate.ts @@ -114,20 +114,14 @@ const LIFECYCLES = [ ]; export function bindLifecycleEvents(instance: any, element: HTMLElement) { - const unregisters = LIFECYCLES.map(eventName => { - const handler = (ev: any) => { - if (typeof instance[eventName] === 'function') { - instance[eventName](ev.detail); - } - }; - element.addEventListener(eventName, handler); - return () => { - element.removeEventListener(eventName, handler); - }; - }); - return () => { - unregisters.forEach(fn => fn()); - }; + const unregisters = LIFECYCLES + .filter(eventName => typeof instance[eventName] === 'function') + .map(eventName => { + const handler = (ev: any) => instance[eventName](ev.detail); + element.addEventListener(eventName, handler); + return () => element.removeEventListener(eventName, handler); + }); + return () => unregisters.forEach(fn => fn()); } const NavParamsToken = new InjectionToken('NavParamsToken'); diff --git a/angular/src/util/overlay.ts b/angular/src/util/overlay.ts index 7a5c42e80d..4310d6f243 100644 --- a/angular/src/util/overlay.ts +++ b/angular/src/util/overlay.ts @@ -11,8 +11,9 @@ export class OverlayBaseController implements ControllerShape { it('should go back with ion-button[routerLink][routerDirection=back]', async () => { await element(by.css('#routerLink-back')).click(); - await testBack(); }); it('should go back with a[routerLink][routerDirection=back]', async () => { @@ -139,7 +138,7 @@ describe('router-link', () => { }); async function testForward() { - await waitTime(500); + await waitTime(2500); await testStack('ion-router-outlet', ['app-router-link', 'app-router-link-page']); await testLifeCycle('app-router-link', { ionViewWillEnter: 1, @@ -153,7 +152,6 @@ async function testForward() { ionViewWillLeave: 0, ionViewDidLeave: 0, }); - } async function testRoot() { @@ -165,6 +163,15 @@ async function testRoot() { ionViewWillLeave: 0, ionViewDidLeave: 0, }); + await browser.navigate().back(); + await waitTime(100); + await testStack('ion-router-outlet', ['app-router-link']); + await testLifeCycle('app-router-link', { + ionViewWillEnter: 1, + ionViewDidEnter: 1, + ionViewWillLeave: 0, + ionViewDidLeave: 0, + }); } async function testBack() { diff --git a/angular/test/test-app/package-lock.json b/angular/test/test-app/package-lock.json index beb5de1146..cfef891acd 100644 --- a/angular/test/test-app/package-lock.json +++ b/angular/test/test-app/package-lock.json @@ -796,20 +796,28 @@ } }, "@ionic/angular": { - "version": "4.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-4.0.0-rc.1.tgz", - "integrity": "sha512-BoNynQ7s+9v4D/yOg6Po33c8svL3HLrL623cmU2CeXIh8F7c4DTlyn+vE6x1ifWrlHucLc5KmMCGd5YqzsGfNw==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-4.6.0.tgz", + "integrity": "sha512-T7At4TBHqkNP9zt6nHqgIztOIDB3X/3YojNm5aya/2tlT9mJ+R0DcGBaKD+KOvKmauzIiABs0A3sxFAPZURVCQ==", "requires": { - "@ionic/core": "4.0.0-rc.1", + "@ionic/core": "4.6.0", "tslib": "^1.9.3" } }, "@ionic/core": { - "version": "4.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@ionic/core/-/core-4.0.0-rc.1.tgz", - "integrity": "sha512-HGMjSq0hW7xVczTDib3tJ1aLi6RgE6R3spKWRiEsVvuBz3WGrLAuG6ASFic/U1k5LLG6vyJoWs4qvZ24b3dXag==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@ionic/core/-/core-4.6.0.tgz", + "integrity": "sha512-yE7zVnj8jQYQfFw+oliXgbpxDGYDS8SKDRLo3I0IQWGIn50nFntQVfH+FfaJ6bWexInq+86+dQLDIjCUQUX0PQ==", "requires": { - "ionicons": "4.5.1" + "ionicons": "4.5.10-2", + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + } } }, "@ngtools/webpack": { @@ -5352,9 +5360,9 @@ "dev": true }, "ionicons": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/ionicons/-/ionicons-4.5.1.tgz", - "integrity": "sha512-zqfkjpPKsdzzXePdE03IRw6xt7B6N3fcN/7NepyniuEWhKZLy7YpdZLegEwBmKeciXi7rIcv1O/hHJTdokUwXQ==" + "version": "4.5.10-2", + "resolved": "https://registry.npmjs.org/ionicons/-/ionicons-4.5.10-2.tgz", + "integrity": "sha512-68GMJBezv9ONng8TskjYFrOnCjXzDSdES6q1C9hTJyA9hKViCqaRcDsq3J/w3OukZEq92o2pX2tRwhj+uFgc9g==" }, "ip": { "version": "1.1.5", diff --git a/angular/test/test-app/package.json b/angular/test/test-app/package.json index 4da07e3fd2..32efa32802 100644 --- a/angular/test/test-app/package.json +++ b/angular/test/test-app/package.json @@ -8,8 +8,7 @@ "sync": "sh scripts/sync.sh", "build": "ng build --prod --no-progress", "test": "ng e2e --prod", - "lint": "ng lint", - "postinstall": "npm run sync" + "lint": "ng lint" }, "private": true, "dependencies": { @@ -21,7 +20,7 @@ "@angular/platform-browser": "~7.2.1", "@angular/platform-browser-dynamic": "~7.2.1", "@angular/router": "~7.2.1", - "@ionic/angular": "^4.0.0-rc.1", + "@ionic/angular": "^4.5.0", "core-js": "^2.6.2", "rxjs": "~6.3.3", "tslib": "^1.9.0", diff --git a/angular/test/test-app/src/app/alert/alert.component.ts b/angular/test/test-app/src/app/alert/alert.component.ts index df9d5bb56f..8ff209ab62 100644 --- a/angular/test/test-app/src/app/alert/alert.component.ts +++ b/angular/test/test-app/src/app/alert/alert.component.ts @@ -15,7 +15,16 @@ export class AlertComponent { async openAlert() { const alert = await this.alertCtrl.create({ header: 'Hello', - message: 'Some text' + message: 'Some text', + buttons: [ + { + role: 'cancel', + text: 'Cancel', + handler: () => { + NgZone.assertInAngularZone(); + } + } + ] }); await alert.present(); } diff --git a/angular/test/test-app/src/app/inputs/inputs.component.html b/angular/test/test-app/src/app/inputs/inputs.component.html index da4de839a0..1d9da7bc23 100644 --- a/angular/test/test-app/src/app/inputs/inputs.component.html +++ b/angular/test/test-app/src/app/inputs/inputs.component.html @@ -6,6 +6,7 @@ +

Change Detections: {{counter()}}

@@ -89,7 +90,7 @@ {{range}} - + Range Mirror diff --git a/angular/test/test-app/src/app/inputs/inputs.component.ts b/angular/test/test-app/src/app/inputs/inputs.component.ts index 93f6ee32f4..78f14765a0 100644 --- a/angular/test/test-app/src/app/inputs/inputs.component.ts +++ b/angular/test/test-app/src/app/inputs/inputs.component.ts @@ -12,6 +12,7 @@ export class InputsComponent { toggle = true; select = 'nes'; range = 10; + changes = 0; setValues() { console.log('set values'); @@ -32,4 +33,8 @@ export class InputsComponent { this.select = undefined; this.range = undefined; } + counter() { + this.changes++; + return Math.floor(this.changes / 2); + } } diff --git a/angular/test/test-app/src/app/router-link/router-link.component.html b/angular/test/test-app/src/app/router-link/router-link.component.html index 5ac6b632d3..ea6b97eb44 100644 --- a/angular/test/test-app/src/app/router-link/router-link.component.html +++ b/angular/test/test-app/src/app/router-link/router-link.component.html @@ -11,6 +11,7 @@

ionViewDidEnter: {{didEnter}}

ionViewWillLeave: {{willLeave}}

ionViewDidLeave: {{didLeave}}

+

Change Detections: {{counter()}}

ion-button[routerLink] @@ -26,7 +27,7 @@

- +

- +
diff --git a/angular/test/test-app/src/app/router-link/router-link.component.ts b/angular/test/test-app/src/app/router-link/router-link.component.ts index 4d17e02a37..6c7d94adcf 100644 --- a/angular/test/test-app/src/app/router-link/router-link.component.ts +++ b/angular/test/test-app/src/app/router-link/router-link.component.ts @@ -13,6 +13,7 @@ export class RouterLinkComponent implements OnInit { didEnter = 0; willLeave = 0; didLeave = 0; + changes = 0; constructor( private navCtrl: NavController, @@ -35,6 +36,11 @@ export class RouterLinkComponent implements OnInit { this.navCtrl.navigateRoot('/router-link-page'); } + counter() { + this.changes++; + return Math.floor(this.changes / 2); + } + ngOnInit() { NgZone.assertInAngularZone(); this.onInit++; diff --git a/core/README.md b/core/README.md index 2dc3009bbc..701a58490b 100644 --- a/core/README.md +++ b/core/README.md @@ -23,9 +23,9 @@ The Ionic Core package contains the Web Components that make up the reusable UI Easiest way to start using Ionic Core is by adding a script tag to the CDN: ```html - - - + + + ``` Any Ionic component added to the webpage will automatically load. This includes writing the component tag directly in HTML, or using JavaScript such as `document.createElement('ion-toggle')`. diff --git a/core/api.txt b/core/api.txt index 8c02cb0477..14cb78051c 100644 --- a/core/api.txt +++ b/core/api.txt @@ -930,12 +930,14 @@ ion-router-link,prop,color,string | undefined,undefined,false,false ion-router-link,prop,href,string | undefined,undefined,false,false ion-router-link,prop,rel,string | undefined,undefined,false,false ion-router-link,prop,routerDirection,"back" | "forward" | "root",'forward',false,false +ion-router-link,prop,target,string | undefined,undefined,false,false ion-router-link,css-prop,--background ion-router-link,css-prop,--color ion-router-outlet,shadow ion-router-outlet,prop,animated,boolean,true,false,false ion-router-outlet,prop,animation,((Animation: Animation, baseEl: any, opts?: any) => Promise) | undefined,undefined,false,false +ion-router-outlet,prop,mode,"ios" | "md",getIonMode(this),false,false ion-row,shadow diff --git a/core/package.json b/core/package.json index 63d2fb131c..25aa36ff1a 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@ionic/core", - "version": "4.6.0", + "version": "4.6.1", "description": "Base components for Ionic", "keywords": [ "ionic", @@ -30,11 +30,11 @@ "loader/" ], "dependencies": { - "ionicons": "4.5.10-2", + "ionicons": "4.6.1", "tslib": "^1.10.0" }, "devDependencies": { - "@stencil/core": "1.1.2", + "@stencil/core": "1.1.5", "@stencil/sass": "1.0.0", "@types/jest": "24.0.13", "@types/node": "10.12.18", diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 10c45f24e1..525e3f684b 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -2016,6 +2016,10 @@ export namespace Components { * When using a router, it specifies the transition direction when navigating to another page using `href`. */ 'routerDirection': RouterDirection; + /** + * Specifies where to display the linked URL. Only applies when an `href` is provided. Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`. + */ + 'target': string | undefined; } interface IonRouterOutlet { /** @@ -2029,6 +2033,10 @@ export namespace Components { 'commit': (enteringEl: HTMLElement, leavingEl: HTMLElement | undefined, opts?: RouterOutletOptions | undefined) => Promise; 'delegate'?: FrameworkDelegate; 'getRouteId': () => Promise; + /** + * The mode determines which platform styles to use. + */ + 'mode': "ios" | "md"; 'setRouteId': (id: string, params: ComponentProps | undefined, direction: RouterDirection) => Promise; 'swipeHandler'?: SwipeGestureHandler; } @@ -5244,6 +5252,10 @@ declare namespace LocalJSX { * When using a router, it specifies the transition direction when navigating to another page using `href`. */ 'routerDirection'?: RouterDirection; + /** + * Specifies where to display the linked URL. Only applies when an `href` is provided. Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`. + */ + 'target'?: string | undefined; } interface IonRouterOutlet extends JSXBase.HTMLAttributes { /** @@ -5254,6 +5266,10 @@ declare namespace LocalJSX { * By default `ion-nav` animates transition between pages based in the mode (ios or material design). However, this property allows to create custom transition using `AnimateBuilder` functions. */ 'animation'?: AnimationBuilder; + /** + * The mode determines which platform styles to use. + */ + 'mode'?: "ios" | "md"; } interface IonRow extends JSXBase.HTMLAttributes {} interface IonSearchbar extends JSXBase.HTMLAttributes { diff --git a/core/src/components/action-sheet/action-sheet.tsx b/core/src/components/action-sheet/action-sheet.tsx index 0b567c9f81..403c74cdd3 100644 --- a/core/src/components/action-sheet/action-sheet.tsx +++ b/core/src/components/action-sheet/action-sheet.tsx @@ -2,7 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Listen, Me import { getIonMode } from '../../global/ionic-global'; import { ActionSheetButton, Animation, AnimationBuilder, CssClassMap, OverlayEventDetail, OverlayInterface } from '../../interface'; -import { BACKDROP, dismiss, eventMethod, isCancel, present } from '../../utils/overlays'; +import { BACKDROP, dismiss, eventMethod, isCancel, present, safeCall } from '../../utils/overlays'; import { getClassMap } from '../../utils/theme'; import { iosEnterAnimation } from './animations/ios.enter'; @@ -169,17 +169,13 @@ export class ActionSheet implements ComponentInterface, OverlayInterface { } private async callButtonHandler(button: ActionSheetButton | undefined) { - if (button && button.handler) { + if (button) { // a handler has been provided, execute it // pass the handler the values from the inputs - try { - const rtn = await button.handler(); - if (rtn === false) { - // if the return value of the handler is false then do not dismiss - return false; - } - } catch (e) { - console.error(e); + const rtn = await safeCall(button.handler); + if (rtn === false) { + // if the return value of the handler is false then do not dismiss + return false; } } return true; diff --git a/core/src/components/alert/alert.tsx b/core/src/components/alert/alert.tsx index f5d306ff35..e52dc95ea4 100644 --- a/core/src/components/alert/alert.tsx +++ b/core/src/components/alert/alert.tsx @@ -2,7 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Listen, Me import { getIonMode } from '../../global/ionic-global'; import { AlertButton, AlertInput, Animation, AnimationBuilder, CssClassMap, OverlayEventDetail, OverlayInterface } from '../../interface'; -import { BACKDROP, dismiss, eventMethod, isCancel, present } from '../../utils/overlays'; +import { BACKDROP, dismiss, eventMethod, isCancel, present, safeCall } from '../../utils/overlays'; import { sanitizeDOMString } from '../../utils/sanitization'; import { getClassMap } from '../../utils/theme'; @@ -223,17 +223,13 @@ export class Alert implements ComponentInterface, OverlayInterface { input.checked = input === selectedInput; } this.activeId = selectedInput.id; - if (selectedInput.handler) { - selectedInput.handler(selectedInput); - } + safeCall(selectedInput.handler, selectedInput); this.el.forceUpdate(); } private cbClick(selectedInput: AlertInput) { selectedInput.checked = !selectedInput.checked; - if (selectedInput.handler) { - selectedInput.handler(selectedInput); - } + safeCall(selectedInput.handler, selectedInput); this.el.forceUpdate(); } @@ -254,7 +250,7 @@ export class Alert implements ComponentInterface, OverlayInterface { if (button && button.handler) { // a handler has been provided, execute it // pass the handler the values from the inputs - const returnData = button.handler(data); + const returnData = safeCall(button.handler, data); if (returnData === false) { // if the return value of the handler is false then do not dismiss return false; diff --git a/core/src/components/datetime/datetime.tsx b/core/src/components/datetime/datetime.tsx index 389d7a88bc..1cf77789f7 100644 --- a/core/src/components/datetime/datetime.tsx +++ b/core/src/components/datetime/datetime.tsx @@ -565,8 +565,7 @@ export class Datetime implements ComponentInterface { } private hasValue(): boolean { - const val = this.datetimeValue; - return Object.keys(val).length > 0; + return this.text !== undefined; } private setFocus() { diff --git a/core/src/components/datetime/test/basic/index.html b/core/src/components/datetime/test/basic/index.html index 01b9f4b069..3cf5be322a 100644 --- a/core/src/components/datetime/test/basic/index.html +++ b/core/src/components/datetime/test/basic/index.html @@ -31,6 +31,16 @@ Default + + + Default with floating label + + + + + Placeholder with floating label + + Max diff --git a/core/src/components/fab-button/fab-button.scss b/core/src/components/fab-button/fab-button.scss index ae24110897..4ca8ec024c 100755 --- a/core/src/components/fab-button/fab-button.scss +++ b/core/src/components/fab-button/fab-button.scss @@ -132,7 +132,7 @@ // -------------------------------------------------- :host(.fab-button-disabled) { - --opacity: .5; + opacity: .5; pointer-events: none; } diff --git a/core/src/components/infinite-scroll/infinite-scroll.tsx b/core/src/components/infinite-scroll/infinite-scroll.tsx index 6e9b07f541..e28fe6b597 100644 --- a/core/src/components/infinite-scroll/infinite-scroll.tsx +++ b/core/src/components/infinite-scroll/infinite-scroll.tsx @@ -29,7 +29,8 @@ export class InfiniteScroll implements ComponentInterface { @Prop() threshold = '15%'; @Watch('threshold') - protected thresholdChanged(val: string) { + protected thresholdChanged() { + const val = this.threshold; if (val.lastIndexOf('%') > -1) { this.thrPx = 0; this.thrPc = (parseFloat(val) / 100); @@ -53,10 +54,12 @@ export class InfiniteScroll implements ComponentInterface { @Watch('disabled') protected disabledChanged() { - if (this.disabled) { + const disabled = this.disabled; + if (disabled) { this.isLoading = false; this.isBusy = false; } + this.enableScrollEvents(!disabled); } /** @@ -79,7 +82,8 @@ export class InfiniteScroll implements ComponentInterface { await contentEl.componentOnReady(); this.scrollEl = await contentEl.getScrollElement(); } - this.thresholdChanged(this.threshold); + this.thresholdChanged(); + this.disabledChanged(); if (this.position === 'top') { writeTask(() => { if (this.scrollEl) { @@ -90,6 +94,7 @@ export class InfiniteScroll implements ComponentInterface { } componentDidUnload() { + this.enableScrollEvents(false); this.scrollEl = undefined; } @@ -199,16 +204,26 @@ export class InfiniteScroll implements ComponentInterface { ); } + private enableScrollEvents(shouldListen: boolean) { + if (this.scrollEl) { + if (shouldListen) { + this.scrollEl.addEventListener('scroll', this.onScroll); + } else { + this.scrollEl.removeEventListener('scroll', this.onScroll); + } + } + } + render() { const mode = getIonMode(this); + const disabled = this.disabled; return ( ); } diff --git a/core/src/components/item/item.scss b/core/src/components/item/item.scss index e8449a0711..bd1f279bd5 100644 --- a/core/src/components/item/item.scss +++ b/core/src/components/item/item.scss @@ -149,7 +149,7 @@ // Item: Disabled // -------------------------------------------------- -:host(.item-interactive-disabled) { +:host(.item-interactive-disabled:not(.item-multiple-inputs)) { cursor: default; pointer-events: none; } @@ -397,7 +397,9 @@ button, a { // Multiple inputs in an item should have the input // cover relative to themselves instead of the item +:host(.item-multiple-inputs) ::slotted(ion-checkbox), :host(.item-multiple-inputs) ::slotted(ion-datetime), +:host(.item-multiple-inputs) ::slotted(ion-radio), :host(.item-multiple-inputs) ::slotted(ion-select) { position: relative; } diff --git a/core/src/components/item/item.tsx b/core/src/components/item/item.tsx index 7c14b21eb0..618a273505 100644 --- a/core/src/components/item/item.tsx +++ b/core/src/components/item/item.tsx @@ -128,18 +128,26 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac } componentDidLoad() { - // Check for multiple inputs to change the position to relative - const inputs = this.el.querySelectorAll('ion-select, ion-datetime'); - this.multipleInputs = inputs.length > 1 ? true : false; + // The following elements have a clickable cover that is relative to the entire item + const covers = this.el.querySelectorAll('ion-checkbox, ion-datetime, ion-select, ion-radio'); + + // The following elements can accept focus alongside the previous elements + // therefore if these elements are also a child of item, we don't want the + // input cover on top of those interfering with their clicks + const inputs = this.el.querySelectorAll('ion-input, ion-range, ion-searchbar, ion-segment, ion-textarea, ion-toggle'); + + // Check for multiple inputs to change the position of the input cover to relative + // for all of the covered inputs above + this.multipleInputs = covers.length + inputs.length > 1; } - // If the item contains an input including a radio, checkbox, datetime, etc. - // then the item will have a clickable input cover that should - // get the hover, focused and activated states UNLESS it has multiple - // inputs, then those need to individually get the click + // If the item contains an input including a checkbox, datetime, select, or radio + // then the item will have a clickable input cover that covers the item + // that should get the hover, focused and activated states UNLESS it has multiple + // inputs, then those need to individually get each click private hasCover(): boolean { const inputs = this.el.querySelectorAll('ion-checkbox, ion-datetime, ion-select, ion-radio'); - return inputs.length > 0 && !this.multipleInputs; + return inputs.length === 1 && !this.multipleInputs; } // If the item has an href or button property it will render a native diff --git a/core/src/components/item/test/disabled/e2e.ts b/core/src/components/item/test/disabled/e2e.ts new file mode 100644 index 0000000000..8d774b7215 --- /dev/null +++ b/core/src/components/item/test/disabled/e2e.ts @@ -0,0 +1,19 @@ +import { newE2EPage } from '@stencil/core/testing'; + +test('item: disabled', async () => { + const page = await newE2EPage({ + url: '/src/components/item/test/disabled?ionic:_testing=true' + }); + + const compare = await page.compareScreenshot(); + expect(compare).toMatchScreenshot(); +}); + +test('item: disabled-rtl', async () => { + const page = await newE2EPage({ + url: '/src/components/item/test/disabled?ionic:_testing=true&rtl=true' + }); + + const compare = await page.compareScreenshot(); + expect(compare).toMatchScreenshot(); +}); diff --git a/core/src/components/item/test/disabled/index.html b/core/src/components/item/test/disabled/index.html new file mode 100644 index 0000000000..c7d78c9392 --- /dev/null +++ b/core/src/components/item/test/disabled/index.html @@ -0,0 +1,197 @@ + + + + + + Item - Disabled + + + + + + + + + + + + + Item: Disabled + + + + + + + Single Input Disabled Items + + + + Disabled Item + + + + Disabled Item Button + + + + Disabled Item Anchor + + + + Disabled Datetime + + + + + Disabled Select + + No Game Console + NES + Nintendo64 + PlayStation + Sega Genesis + Sega Saturn + SNES + + + + + Disabled Input + + + + + Disabled Toggle + + + + + Disabled Checkbox + + + + + Disabled Range + + + + + + + Multiple Input Disabled Items + + + + + + + + + + + + + + + + + + + + + + + + + Checkbox + Radio + + + + + + + Checkbox + Radio + + + + Disabled Selects + + January + February + March + + + 1990 + 1991 + 1992 + 1993 + 1994 + 1995 + 1996 + 1997 + + + + + + Checkbox + Range + + + + + + Checkbox + Toggle + + + + + + Checkbox + Buttons + Default + Buttons + + + + Disabled Input + + + + Friends + + + Enemies + + + + + + + Disabled Checkbox + + + + + + + + + Toggle + + + + + + + + diff --git a/core/src/components/label/label.scss b/core/src/components/label/label.scss index d2d00d4a54..394281aaa2 100644 --- a/core/src/components/label/label.scss +++ b/core/src/components/label/label.scss @@ -34,7 +34,7 @@ white-space: normal; } -:host-context(.item-interactive-disabled) { +:host-context(.item-interactive-disabled:not(.item-multiple-inputs)) { cursor: default; opacity: .3; pointer-events: none; diff --git a/core/src/components/picker/picker.tsx b/core/src/components/picker/picker.tsx index dd72851c34..34fa951701 100644 --- a/core/src/components/picker/picker.tsx +++ b/core/src/components/picker/picker.tsx @@ -2,7 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Listen, Me import { getIonMode } from '../../global/ionic-global'; import { Animation, AnimationBuilder, CssClassMap, OverlayEventDetail, OverlayInterface, PickerButton, PickerColumn } from '../../interface'; -import { dismiss, eventMethod, present } from '../../utils/overlays'; +import { dismiss, eventMethod, present, safeCall } from '../../utils/overlays'; import { getClassMap } from '../../utils/theme'; import { iosEnterAnimation } from './animations/ios.enter'; @@ -175,17 +175,9 @@ export class Picker implements ComponentInterface, OverlayInterface { // } // keep the time of the most recent button click - let shouldDismiss = true; - - if (button.handler) { - // a handler has been provided, execute it - // pass the handler the values from the inputs - if (button.handler(this.getSelected()) === false) { - // if the return value of the handler is false then do not dismiss - shouldDismiss = false; - } - } - + // a handler has been provided, execute it + // pass the handler the values from the inputs + const shouldDismiss = safeCall(button.handler, this.getSelected()) !== false; if (shouldDismiss) { return this.dismiss(); } diff --git a/core/src/components/router-link/readme.md b/core/src/components/router-link/readme.md index be83f2fdd5..c65c3af851 100644 --- a/core/src/components/router-link/readme.md +++ b/core/src/components/router-link/readme.md @@ -15,6 +15,7 @@ The router link component is used for navigating to a specified link. Similar to | `href` | `href` | Contains a URL or a URL fragment that the hyperlink points to. If this property is set, an anchor tag will be rendered. | `string \| undefined` | `undefined` | | `rel` | `rel` | Specifies the relationship of the target object to the link object. The value is a space-separated list of [link types](https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types). | `string \| undefined` | `undefined` | | `routerDirection` | `router-direction` | When using a router, it specifies the transition direction when navigating to another page using `href`. | `"back" \| "forward" \| "root"` | `'forward'` | +| `target` | `target` | Specifies where to display the linked URL. Only applies when an `href` is provided. Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`. | `string \| undefined` | `undefined` | ## CSS Custom Properties diff --git a/core/src/components/router-link/router-link.tsx b/core/src/components/router-link/router-link.tsx index 2b9e224844..66dd51a6df 100644 --- a/core/src/components/router-link/router-link.tsx +++ b/core/src/components/router-link/router-link.tsx @@ -36,6 +36,13 @@ export class RouterLink implements ComponentInterface { */ @Prop() routerDirection: RouterDirection = 'forward'; + /** + * Specifies where to display the linked URL. + * Only applies when an `href` is provided. + * Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`. + */ + @Prop() target: string | undefined; + private onClick = (ev: Event) => { openURL(this.href, ev, this.routerDirection); } @@ -44,7 +51,8 @@ export class RouterLink implements ComponentInterface { const mode = getIonMode(this); const attrs = { href: this.href, - rel: this.rel + rel: this.rel, + target: this.target }; return (

- Underline Router Link + External Router Link

diff --git a/core/src/components/router-outlet/readme.md b/core/src/components/router-outlet/readme.md index 3797c8cfd5..6c7184f082 100644 --- a/core/src/components/router-outlet/readme.md +++ b/core/src/components/router-outlet/readme.md @@ -30,10 +30,11 @@ For handling Router Guards, the older `ionViewCanEnter` and `ionViewCanLeave` ha ## Properties -| Property | Attribute | Description | Type | Default | -| ----------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ----------- | -| `animated` | `animated` | If `true`, the router-outlet should animate the transition of components. | `boolean` | `true` | -| `animation` | -- | By default `ion-nav` animates transition between pages based in the mode (ios or material design). However, this property allows to create custom transition using `AnimateBuilder` functions. | `((Animation: Animation, baseEl: any, opts?: any) => Promise) \| undefined` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ----------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------ | +| `animated` | `animated` | If `true`, the router-outlet should animate the transition of components. | `boolean` | `true` | +| `animation` | -- | By default `ion-nav` animates transition between pages based in the mode (ios or material design). However, this property allows to create custom transition using `AnimateBuilder` functions. | `((Animation: Animation, baseEl: any, opts?: any) => Promise) \| undefined` | `undefined` | +| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `getIonMode(this)` | ---------------------------------------------- diff --git a/core/src/components/router-outlet/route-outlet.tsx b/core/src/components/router-outlet/route-outlet.tsx index 459c623179..ac6c78b345 100644 --- a/core/src/components/router-outlet/route-outlet.tsx +++ b/core/src/components/router-outlet/route-outlet.tsx @@ -21,6 +21,11 @@ export class RouterOutlet implements ComponentInterface, NavOutlet { @Element() el!: HTMLElement; + /** + * The mode determines which platform styles to use. + */ + @Prop({ mutable: true }) mode = getIonMode(this); + /** @internal */ @Prop() delegate?: FrameworkDelegate; @@ -147,8 +152,7 @@ export class RouterOutlet implements ComponentInterface, NavOutlet { // emit nav will change event this.ionNavWillChange.emit(); - const mode = getIonMode(this); - const { el } = this; + const { el, mode } = this; const animated = this.animated && config.getBoolean('animated', true); const animationBuilder = this.animation || opts.animationBuilder || config.get('navAnimation'); diff --git a/core/src/components/select-popover/select-popover.tsx b/core/src/components/select-popover/select-popover.tsx index 51b6b2d554..0bfc9fa196 100644 --- a/core/src/components/select-popover/select-popover.tsx +++ b/core/src/components/select-popover/select-popover.tsx @@ -2,6 +2,7 @@ import { Component, ComponentInterface, Listen, Prop, h } from '@stencil/core'; import { getIonMode } from '../../global/ionic-global'; import { SelectPopoverOption } from '../../interface'; +import { safeCall } from '../../utils/overlays'; /** * @internal @@ -28,8 +29,8 @@ export class SelectPopover implements ComponentInterface { @Listen('ionSelect') onSelect(ev: any) { const option = this.options.find(o => o.value === ev.target.value); - if (option && option.handler) { - option.handler(); + if (option) { + safeCall(option.handler); } } diff --git a/core/src/components/tab-bar/tab-bar.tsx b/core/src/components/tab-bar/tab-bar.tsx index 75edacc204..2cb82d7da5 100644 --- a/core/src/components/tab-bar/tab-bar.tsx +++ b/core/src/components/tab-bar/tab-bar.tsx @@ -34,9 +34,11 @@ export class TabBar implements ComponentInterface { @Prop() selectedTab?: string; @Watch('selectedTab') selectedTabChanged() { - this.ionTabBarChanged.emit({ - tab: this.selectedTab - }); + if (this.selectedTab !== undefined) { + this.ionTabBarChanged.emit({ + tab: this.selectedTab + }); + } } /** diff --git a/core/src/components/toast/toast.tsx b/core/src/components/toast/toast.tsx index 847a1fc238..726d2cf238 100644 --- a/core/src/components/toast/toast.tsx +++ b/core/src/components/toast/toast.tsx @@ -2,7 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Method, Pr import { getIonMode } from '../../global/ionic-global'; import { Animation, AnimationBuilder, Color, CssClassMap, OverlayEventDetail, OverlayInterface, ToastButton } from '../../interface'; -import { dismiss, eventMethod, isCancel, present } from '../../utils/overlays'; +import { dismiss, eventMethod, isCancel, present, safeCall } from '../../utils/overlays'; import { sanitizeDOMString } from '../../utils/sanitization'; import { createColorClasses, getClassMap } from '../../utils/theme'; @@ -212,7 +212,7 @@ export class Toast implements ComponentInterface, OverlayInterface { // a handler has been provided, execute it // pass the handler the values from the inputs try { - const rtn = await button.handler(); + const rtn = await safeCall(button.handler); if (rtn === false) { // if the return value of the handler is false then do not dismiss return false; diff --git a/core/src/utils/config.ts b/core/src/utils/config.ts index 7ab0c29f13..bfd3fcc58d 100644 --- a/core/src/utils/config.ts +++ b/core/src/utils/config.ts @@ -177,6 +177,7 @@ export interface IonicConfig { persistConfig?: boolean; _forceStatusbarPadding?: boolean; _testing?: boolean; + _zoneGate?: (h: () => any) => any; } export function setupConfig(config: IonicConfig) { diff --git a/core/src/utils/overlays.ts b/core/src/utils/overlays.ts index 5d6ca6e48b..9d64289a03 100644 --- a/core/src/utils/overlays.ts +++ b/core/src/utils/overlays.ts @@ -203,16 +203,6 @@ const overlayAnimation = async ( return hasCompleted; }; -export const autoFocus = (containerEl: HTMLElement): HTMLElement | undefined => { - const focusableEls = containerEl.querySelectorAll('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"]'); - if (focusableEls.length > 0) { - const el = focusableEls[0] as HTMLInputElement; - el.focus(); - return el; - } - return undefined; -}; - export const eventMethod = (element: HTMLElement, eventName: string): Promise => { let resolve: (detail: T) => void; const promise = new Promise(r => resolve = r); @@ -244,4 +234,20 @@ const isDescendant = (parent: HTMLElement, child: HTMLElement | null) => { return false; }; +const defaultGate = (h: any) => h(); + +export const safeCall = (handler: any, arg?: any) => { + if (typeof handler === 'function') { + const jmp = config.get('_zoneGate', defaultGate); + return jmp(() => { + try { + return handler(arg); + } catch (e) { + console.error(e); + } + }); + } + return undefined; +}; + export const BACKDROP = 'backdrop'; diff --git a/core/stencil.config.ts b/core/stencil.config.ts index 0342f9e646..fa42e5a204 100644 --- a/core/stencil.config.ts +++ b/core/stencil.config.ts @@ -10,7 +10,7 @@ export const config: Config = { { components: ['ion-action-sheet'] }, { components: ['ion-alert'] }, { components: ['ion-back-button'] }, - { components: ['ion-app', 'ion-buttons', 'ion-content', 'ion-footer', 'ion-header', 'ion-title', 'ion-toolbar'] }, + { components: ['ion-app', 'ion-router-outlet', 'ion-buttons', 'ion-content', 'ion-footer', 'ion-header', 'ion-title', 'ion-toolbar'] }, { components: ['ion-avatar', 'ion-badge', 'ion-thumbnail'] }, { components: ['ion-backdrop'] }, { components: ['ion-button', 'ion-icon'] }, @@ -37,7 +37,7 @@ export const config: Config = { { components: ['ion-refresher', 'ion-refresher-content'] }, { components: ['ion-reorder', 'ion-reorder-group'] }, { components: ['ion-ripple-effect'] }, - { components: ['ion-anchor', 'ion-router', 'ion-route', 'ion-route-redirect', 'ion-router-link', 'ion-router-outlet'] }, + { components: ['ion-router', 'ion-route', 'ion-route-redirect', 'ion-router-link'] }, { components: ['ion-searchbar'] }, { components: ['ion-segment', 'ion-segment-button'] }, { components: ['ion-select', 'ion-select-option', 'ion-select-popover'] }, @@ -50,7 +50,10 @@ export const config: Config = { { components: ['ion-toast'] }, { components: ['ion-toggle'] }, { components: ['ion-virtual-scroll'] }, + + // Deprecated { components: [ + 'ion-anchor', 'ion-action-sheet-controller', 'ion-alert-controller', 'ion-loading-controller', @@ -66,7 +69,10 @@ export const config: Config = { outputTargets: [ { type: 'dist', - esmLoaderPath: '../loader' + esmLoaderPath: '../loader', + copy: [ + { src: '**/*.scss' } + ] }, // { // type: 'experimental-dist-module', diff --git a/docs/package.json b/docs/package.json index cb73824145..a5b32a3ddb 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "@ionic/docs", - "version": "4.6.0", + "version": "4.6.1", "description": "Pre-packaged API documentation for the Ionic docs.", "main": "core.json", "files": [