sync with master

This commit is contained in:
Liam DeBeasi
2019-07-22 13:07:23 -04:00
25 changed files with 559 additions and 161 deletions

View File

@@ -3,6 +3,7 @@ import { applyPolyfills, defineCustomElements } from '@ionic/core/loader';
import { Config } from './providers/config';
import { IonicWindow } from './types/interfaces';
import { raf } from './util/util';
export function appInitialize(config: Config, doc: Document, zone: NgZone) {
return (): any => {
@@ -23,12 +24,8 @@ export function appInitialize(config: Config, doc: Document, zone: NgZone) {
return defineCustomElements(win, {
exclude: ['ion-tabs', 'ion-tab'],
syncQueue: true,
raf,
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) {
(elm as any)[aelFn](eventName, cb, opts);
},

View File

@@ -1,6 +1,8 @@
import { ElementRef, HostListener } from '@angular/core';
import { ControlValueAccessor } from '@angular/forms';
import { raf } from '../../util/util';
export class ValueAccessor implements ControlValueAccessor {
private onChange: (value: any) => void = () => {/**/};
@@ -42,14 +44,16 @@ export class ValueAccessor implements ControlValueAccessor {
}
export function setIonicClasses(element: ElementRef) {
const input = element.nativeElement as HTMLElement;
const classes = getClasses(input);
setClasses(input, classes);
raf(() => {
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) {

View File

@@ -205,7 +205,6 @@ 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);

View File

@@ -31,7 +31,7 @@ export class StackController {
createView(ref: ComponentRef<any>, activatedRoute: ActivatedRoute): RouteView {
const url = getUrl(this.router, activatedRoute);
const element = (ref && ref.location && ref.location.nativeElement) as HTMLElement;
const unlistenEvents = bindLifecycleEvents(ref.instance, element);
const unlistenEvents = bindLifecycleEvents(this.zone, ref.instance, element);
return {
id: this.nextId++,
stackId: computeStackId(this.tabsPrefix, url),
@@ -90,16 +90,28 @@ export class StackController {
}
}
const reused = this.views.includes(enteringView);
const views = this.insertView(enteringView, direction);
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
}));
// Trigger change detection before transition starts
// This will call ngOnInit() the first time too, just after the view
// was attached to the dom, but BEFORE the transition starts
if (!reused) {
enteringView.ref.changeDetectorRef.detectChanges();
}
// Wait until previous transitions finish
return this.zone.runOutsideAngular(() => {
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
}));
});
});
}
@@ -199,11 +211,11 @@ export class StackController {
if (enteringView) {
enteringView.ref.changeDetectorRef.reattach();
}
// TODO: disconnect leaving page from change detection to
// disconnect leaving page from change detection to
// reduce jank during the page transition
// if (leavingView) {
// leavingView.ref.changeDetectorRef.detach();
// }
if (leavingView) {
leavingView.ref.changeDetectorRef.detach();
}
const enteringEl = enteringView ? enteringView.element : undefined;
const leavingEl = leavingView ? leavingView.element : undefined;
const containerEl = this.containerEl;
@@ -213,13 +225,13 @@ export class StackController {
containerEl.appendChild(enteringEl);
}
return this.zone.runOutsideAngular(() => containerEl.commit(enteringEl, leavingEl, {
return containerEl.commit(enteringEl, leavingEl, {
deepWait: true,
duration: direction === undefined ? 0 : undefined,
direction,
showGoBack,
progressAnimation
}));
});
}
return Promise.resolve(false);
}

View File

@@ -34,10 +34,10 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
) {}
attachViewToDom(container: any, component: any, params?: any, cssClasses?: string[]): Promise<any> {
return new Promise(resolve => {
this.zone.run(() => {
return this.zone.run(() => {
return new Promise(resolve => {
const el = attachView(
this.resolver, this.injector, this.location, this.appRef,
this.zone, this.resolver, this.injector, this.location, this.appRef,
this.elRefMap, this.elEventsMap,
container, component, params, cssClasses
);
@@ -47,8 +47,8 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
}
removeViewFromDom(_container: any, component: any): Promise<void> {
return new Promise(resolve => {
this.zone.run(() => {
return this.zone.run(() => {
return new Promise(resolve => {
const componentRef = this.elRefMap.get(component);
if (componentRef) {
componentRef.destroy();
@@ -66,6 +66,7 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
}
export function attachView(
zone: NgZone,
resolver: ComponentFactoryResolver,
injector: Injector,
location: ViewContainerRef | undefined,
@@ -93,7 +94,7 @@ export function attachView(
hostElement.classList.add(clazz);
}
}
const unbindEvents = bindLifecycleEvents(instance, hostElement);
const unbindEvents = bindLifecycleEvents(zone, instance, hostElement);
container.appendChild(hostElement);
if (!location) {
@@ -113,15 +114,17 @@ const LIFECYCLES = [
LIFECYCLE_WILL_UNLOAD
];
export function bindLifecycleEvents(instance: any, element: HTMLElement) {
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());
export function bindLifecycleEvents(zone: NgZone, instance: any, element: HTMLElement) {
return zone.run(() => {
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<any>('NavParamsToken');

View File

@@ -1,5 +1,10 @@
import { HTMLStencilElement } from '../types/interfaces';
export const raf = (h: any) => {
const win = window as any;
return (win.__zone_symbol__requestAnimationFrame) ? win.__zone_symbol__requestAnimationFrame(h) : requestAnimationFrame(h);
};
export function proxyMethod(ctrlName: string, doc: Document, methodName: string, ...args: any[]) {
const controller = ensureElementInBody(ctrlName, doc);
return controller.componentOnReady()

View File

@@ -1,5 +1,5 @@
import { browser, element, by, protractor } from 'protractor';
import { waitTime, testStack, testLifeCycle, handleErrorMessages } from './utils';
import { waitTime, testStack, testLifeCycle, handleErrorMessages, getText } from './utils';
const EC = protractor.ExpectedConditions;
@@ -23,12 +23,13 @@ describe('router-link params and fragments', () => {
it('should return to a page with preserved query param and fragment', async () => {
await browser.get('/router-link?ionic:_testing=true');
await waitTime(30);
await element(by.css('#queryParamsFragment')).click();
await waitTime(200);
await waitTime(400);
await element(by.css('#goToPage3')).click();
browser.wait(EC.urlContains('router-link-page3'), 5000);
await waitTime(200);
await waitTime(400);
await element(by.css('#goBackFromPage3')).click();
@@ -38,6 +39,7 @@ describe('router-link params and fragments', () => {
it('should preserve query param and fragment with defaultHref string', async () => {
await browser.get('/router-link-page3?ionic:_testing=true');
await waitTime(30);
await element(by.css('#goBackFromPage3')).click();
@@ -71,18 +73,6 @@ describe('router-link', () => {
it('should go forward with ion-button[routerLink]', async () => {
await element(by.css('#routerLink')).click();
await testForward();
// test go back
await element(by.css('ion-back-button')).click();
await waitTime(500);
await testStack('ion-router-outlet', ['app-router-link']);
await testLifeCycle('app-router-link', {
ionViewWillEnter: 2,
ionViewDidEnter: 2,
ionViewWillLeave: 1,
ionViewDidLeave: 1,
});
});
it('should go forward with a[routerLink]', async () => {
@@ -140,18 +130,23 @@ describe('router-link', () => {
async function testForward() {
await waitTime(2500);
await testStack('ion-router-outlet', ['app-router-link', 'app-router-link-page']);
await testLifeCycle('app-router-link', {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 1,
ionViewDidLeave: 1,
});
await testLifeCycle('app-router-link-page', {
ionViewWillEnter: 1,
ionViewDidEnter: 1,
ionViewWillLeave: 0,
ionViewDidLeave: 0,
});
expect(await getText(`app-router-link-page #canGoBack`)).toEqual('true');
await browser.navigate().back();
await waitTime(100);
await testStack('ion-router-outlet', ['app-router-link']);
await testLifeCycle('app-router-link', {
ionViewWillEnter: 2,
ionViewDidEnter: 2,
ionViewWillLeave: 1,
ionViewDidLeave: 1,
});
}
async function testRoot() {
@@ -163,6 +158,8 @@ async function testRoot() {
ionViewWillLeave: 0,
ionViewDidLeave: 0,
});
expect(await getText(`app-router-link-page #canGoBack`)).toEqual('false');
await browser.navigate().back();
await waitTime(100);
await testStack('ion-router-outlet', ['app-router-link']);
@@ -183,4 +180,15 @@ async function testBack() {
ionViewWillLeave: 0,
ionViewDidLeave: 0,
});
expect(await getText(`app-router-link-page #canGoBack`)).toEqual('false');
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,
});
}

View File

@@ -48,11 +48,19 @@ export function handleErrorMessages() {
export async function testLifeCycle(selector: string, expected: LifeCycleCount) {
await waitTime(50);
expect(await getText(`${selector} #ngOnInit`)).toEqual('1');
expect(await getText(`${selector} #ionViewWillEnter`)).toEqual(expected.ionViewWillEnter.toString());
expect(await getText(`${selector} #ionViewDidEnter`)).toEqual(expected.ionViewDidEnter.toString());
expect(await getText(`${selector} #ionViewWillLeave`)).toEqual(expected.ionViewWillLeave.toString());
expect(await getText(`${selector} #ionViewDidLeave`)).toEqual(expected.ionViewDidLeave.toString());
const results = await Promise.all([
getText(`${selector} #ngOnInit`),
getText(`${selector} #ionViewWillEnter`),
getText(`${selector} #ionViewDidEnter`),
getText(`${selector} #ionViewWillLeave`),
getText(`${selector} #ionViewDidLeave`),
]);
expect(results[0]).toEqual('1');
expect(results[1]).toEqual(expected.ionViewWillEnter.toString());
expect(results[2]).toEqual(expected.ionViewDidEnter.toString());
expect(results[3]).toEqual(expected.ionViewWillLeave.toString());
expect(results[4]).toEqual(expected.ionViewDidLeave.toString());
}
export async function testStack(selector: string, expected: string[]) {

View File

@@ -1,10 +1,10 @@
<ion-header>
<ion-toolbar>
<ion-title>
Modal test
Alert test
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<ion-button (click)="openAlert()" id="action-button">Open Alert</ion-button>
<p>Change Detections: <span id="counter">{{counter()}}</span></p>
</ion-content>

View File

@@ -8,10 +8,17 @@ import { NavComponent } from '../nav/nav.component';
})
export class AlertComponent {
changes = 0;
constructor(
private alertCtrl: AlertController
) { }
counter() {
this.changes++;
return Math.floor(this.changes / 2);
}
async openAlert() {
const alert = await this.alertCtrl.create({
header: 'Hello',

View File

@@ -9,6 +9,7 @@
<ion-content padding>
<p>ngOnInit: <span id="ngOnInit">{{onInit}}</span></p>
<p>canGoBack: <span id="canGoBack">{{canGoBack}}</span></p>
<p>ionViewWillEnter: <span id="ionViewWillEnter">{{willEnter}}</span></p>
<p>ionViewDidEnter: <span id="ionViewDidEnter">{{didEnter}}</span></p>
<p>ionViewWillLeave: <span id="ionViewWillLeave">{{willLeave}}</span></p>

View File

@@ -1,4 +1,5 @@
import { Component, OnInit, NgZone } from '@angular/core';
import { IonRouterOutlet } from '@ionic/angular';
@Component({
selector: 'app-router-link-page',
@@ -11,9 +12,15 @@ export class RouterLinkPageComponent implements OnInit {
didEnter = 0;
willLeave = 0;
didLeave = 0;
canGoBack: boolean = null;
constructor(
private ionRouterOutlet: IonRouterOutlet
) {}
ngOnInit() {
NgZone.assertInAngularZone();
this.canGoBack = this.ionRouterOutlet.canGoBack();
this.onInit++;
}
@@ -21,10 +28,16 @@ export class RouterLinkPageComponent implements OnInit {
if (this.onInit !== 1) {
throw new Error('ngOnInit was not called');
}
if (this.canGoBack !== this.ionRouterOutlet.canGoBack()) {
throw new Error('canGoBack() changed');
}
NgZone.assertInAngularZone();
this.willEnter++;
}
ionViewDidEnter() {
if (this.canGoBack !== this.ionRouterOutlet.canGoBack()) {
throw new Error('canGoBack() changed');
}
NgZone.assertInAngularZone();
this.didEnter++;
}

View File

@@ -7,8 +7,6 @@ export default {
format: 'es'
},
plugins: [
resolve({
module: true
})
resolve()
]
};

View File

@@ -5,4 +5,8 @@
document.documentElement.setAttribute('dir', 'rtl');
}
window.Ionic = window.Ionic || {};
window.Ionic.config = window.Ionic.config || {};
window.Ionic.config.experimentalTransitionShadow = true;
})();

View File

@@ -15,15 +15,19 @@ export class App implements ComponentInterface {
componentDidLoad() {
rIC(() => {
const isHybrid = isPlatform(window, 'hybrid');
if (!config.getBoolean('_testing')) {
import('../../utils/tap-click').then(module => module.startTapClick(config));
}
if (config.getBoolean('statusTap', isPlatform(window, 'hybrid'))) {
if (config.getBoolean('statusTap', isHybrid)) {
import('../../utils/status-tap').then(module => module.startStatusTap());
}
if (config.getBoolean('inputShims', needInputShims())) {
import('../../utils/input-shims/input-shims').then(module => module.startInputShims(config));
}
if (config.getBoolean('hardwareBackButton', isHybrid)) {
import('../../utils/hardware-back-button').then(module => module.startHardwareBackButton());
}
import('../../utils/focus-visible').then(module => module.startFocusVisible());
});

View File

@@ -118,3 +118,47 @@
:host(.content-sizing) .inner-scroll {
position: relative;
}
.transition-effect {
position: absolute;
/* stylelint-disable property-blacklist */
left: -100%;
/* stylelint-enable property-blacklist */
width: 100%;
height: 100%;
opacity: 0;
}
.transition-cover {
position: absolute;
/* stylelint-disable property-blacklist */
right: 0;
/* stylelint-enable property-blacklist */
width: 100%;
height: 100%;
background: black;
opacity: 0.1;
}
.transition-shadow {
display: block;
position: absolute;
/* stylelint-disable property-blacklist */
right: 0;
/* stylelint-enable property-blacklist */
width: 10px;
height: 100%;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAgCAYAAAAIXrg4AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTE3MDgzRkQ5QTkyMTFFOUEwNzQ5MkJFREE1NUY2MjQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTE3MDgzRkU5QTkyMTFFOUEwNzQ5MkJFREE1NUY2MjQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoxMTcwODNGQjlBOTIxMUU5QTA3NDkyQkVEQTU1RjYyNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoxMTcwODNGQzlBOTIxMUU5QTA3NDkyQkVEQTU1RjYyNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PmePEuQAAABNSURBVHjaYvz//z8DIxAwMDAwATGMhmFmPDQuOSZks0AMmoJBaQHjkPfB0Lfg/2gQjVow+HPy/yHvg9GiYjQfjMbBqAWjFgy/4hogwADYqwdzxy5BuwAAAABJRU5ErkJggg==);
background-repeat: repeat-y;
background-size: 10px 16px;
}

View File

@@ -1,5 +1,6 @@
import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Listen, Method, Prop, h, readTask } from '@stencil/core';
import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global';
import { Color, ScrollBaseDetail, ScrollDetail } from '../../interface';
import { isPlatform } from '../../utils/platform';
@@ -301,6 +302,8 @@ export class Content implements ComponentInterface {
const mode = getIonMode(this);
const { scrollX, scrollY, forceOverscroll } = this;
const transitionShadow = (mode === 'ios' && config.getBoolean('experimentalTransitionShadow', false));
this.resize();
return (
@@ -328,6 +331,12 @@ export class Content implements ComponentInterface {
>
<slot></slot>
</div>
{transitionShadow ? (
<div class="transition-effect">
<div class="transition-cover"></div>
<div class="transition-shadow"></div>
</div>
) : null}
<slot name="fixed"></slot>
</Host>
);
@@ -370,6 +379,8 @@ const updateScrollDetail = (
const prevT = detail.timeStamp;
const currentX = el.scrollLeft;
const currentY = el.scrollTop;
const timeDelta = timestamp - prevT;
if (shouldStart) {
// remember the start positions
detail.startTimeStamp = timestamp;
@@ -383,7 +394,6 @@ const updateScrollDetail = (
detail.deltaX = currentX - detail.startX;
detail.deltaY = currentY - detail.startY;
const timeDelta = timestamp - prevT;
if (timeDelta > 0 && timeDelta < 100) {
const velocityX = (currentX - prevX) / timeDelta;
const velocityY = (currentY - prevY) / timeDelta;

View File

@@ -154,4 +154,4 @@
</ion-app>
</body>
</html>
</html>

View File

@@ -10,6 +10,9 @@
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script> <script>
window.Ionic = window.Ionic || {};
window.Ionic.config = window.Ionic.config || {};
window.Ionic.config.experimentalTransitionShadow = true;
class PageRoot extends HTMLElement {
connectedCallback() {
this.innerHTML = `

View File

@@ -193,10 +193,12 @@ export class Textarea implements ComponentInterface {
this.ionInputDidLoad.emit();
}
// TODO: performance hit, this cause layout thrashing
private runAutoGrow() {
const nativeInput = this.nativeInput;
if (nativeInput && this.autoGrow) {
readTask(() => {
nativeInput.style.height = 'inherit';
nativeInput.style.height = nativeInput.scrollHeight + 'px';
});
}

View File

@@ -1,6 +1,6 @@
// TODO: Add validation
// TODO: More tests
let counter = 0;
export interface Animation {
parentAnimation: Animation | undefined;
elements: HTMLElement[];
@@ -91,7 +91,8 @@ const animationEnd = (el: HTMLElement | null, callback: (ev?: TransitionEvent) =
};
const supportsWebAnimations = (): boolean => {
return !!(window as any).Animation;
return false;
//return !!(window as any).Animation;
};
const generateKeyframeString = (name: string | undefined, keyframes: any[] = []): string => {
@@ -101,25 +102,35 @@ const generateKeyframeString = (name: string | undefined, keyframes: any[] = [])
keyframes.forEach(keyframe => {
const offset = keyframe.offset;
delete keyframe.offset;
const frameString = [];
for (const property in keyframe) {
if (keyframe.hasOwnProperty(property)) {
if (keyframe.hasOwnProperty(property) && property !== 'offset') {
frameString.push(`${property}: ${keyframe[property]};`);
}
}
keyframeString.push(`${offset * 100}% { ${frameString.join(' ')} }`);
});
keyframeString.push('}')
return keyframeString.join(' ');
};
const createKeyframeStylesheet = (keyframeString: string): HTMLElement => {
const createKeyframeStylesheet = (name: string | undefined, keyframeString: string, element: HTMLElement): HTMLElement | undefined => {
const stylesheetId = `ion-${name}`;
const stylesheet = document.createElement('style');
stylesheet.id = stylesheetId;
stylesheet.appendChild(document.createTextNode(keyframeString));
document.querySelector('head')!.appendChild(stylesheet);
const rootNode = (element.getRootNode() as any);
const styleContainer = (rootNode.head || rootNode);
const existingStylesheet = rootNode.querySelector(`#${stylesheetId}`);
if (existingStylesheet) { return }
styleContainer.appendChild(stylesheet);
return stylesheet;
};
@@ -148,7 +159,7 @@ export const createAnimation = (animationNameValue: string | undefined): Animati
let initialized = false;
let stylesheet: HTMLElement | undefined;
let stylesheets: HTMLElement[] | undefined = [];
let parentAnimation: Animation | undefined;
@@ -218,10 +229,11 @@ export const createAnimation = (animationNameValue: string | undefined): Animati
};
const cleanUpStyleSheets = () => {
if (stylesheet) {
stylesheets.forEach(stylesheet => {
stylesheet.parentNode!.removeChild(stylesheet);
stylesheet = undefined;
}
});
stylesheets = [];
};
/**
@@ -358,6 +370,7 @@ export const createAnimation = (animationNameValue: string | undefined): Animati
const name = (animationName: string): Animation => {
_name = animationName;
counter += 1;
return generatePublicAPI();
};
@@ -507,26 +520,32 @@ export const createAnimation = (animationNameValue: string | undefined): Animati
};
const initializeCSSAnimation = () => {
if (!stylesheet) {
stylesheet = createKeyframeStylesheet(generateKeyframeString(_name, _keyframes));
}
elements.forEach(element => {
element.style.setProperty('animation-name', _name || null);
element.style.setProperty('animation-duration', (getDuration() !== undefined) ? `${getDuration()}ms` : null);
element.style.setProperty('animation-timing-function', getEasing() || null);
element.style.setProperty('animation-delay', (getDelay() !== undefined) ? `${getDelay()}ms` : null);
element.style.setProperty('animation-fill-mode', getFill() || null);
element.style.setProperty('animation-direction', getDirection() || null);
let iterationsCount = null;
if (getIterations() !== undefined) {
iterationsCount = (getIterations() === Infinity) ? 'infinite' : getIterations()!.toString();
if (_keyframes.length > 0) {
const stylesheet = createKeyframeStylesheet(_name, generateKeyframeString(_name, _keyframes), element);
if (stylesheet) {
stylesheets.push(stylesheet);
}
element.style.setProperty('animation-name', _name || null);
element.style.setProperty('animation-duration', (getDuration() !== undefined) ? `${getDuration()}ms` : null);
element.style.setProperty('animation-timing-function', getEasing() || null);
element.style.setProperty('animation-delay', (getDelay() !== undefined) ? `${getDelay()}ms` : null);
element.style.setProperty('animation-fill-mode', getFill() || null);
element.style.setProperty('animation-direction', getDirection() || null);
let iterationsCount = null;
if (getIterations() !== undefined) {
iterationsCount = (getIterations() === Infinity) ? 'infinite' : getIterations()!.toString();
}
element.style.setProperty('animation-iteration-countion', iterationsCount);
element.style.setProperty('animation-play-state', 'paused');
}
element.style.setProperty('animation-iteration-countion', iterationsCount);
element.style.setProperty('animation-play-state', 'paused');
});
if (elements.length > 0) {
animationEnd(elements[0], () => {
@@ -586,7 +605,7 @@ export const createAnimation = (animationNameValue: string | undefined): Animati
childAnimations.forEach(animation => {
animation.progressStep(step);
});
if (getDuration() !== undefined) {
if (supportsWebAnimations()) {
webAnimations.forEach(animation => {
@@ -597,8 +616,10 @@ export const createAnimation = (animationNameValue: string | undefined): Animati
const animationDuration = `-${getDuration()! * step}ms`;
elements.forEach(element => {
(element as HTMLElement).style.animationDelay = animationDuration;
(element as HTMLElement).style.animationPlayState = 'paused';
if (_keyframes.length > 0) {
(element as HTMLElement).style.animationDelay = animationDuration;
(element as HTMLElement).style.animationPlayState = 'paused';
}
});
}
}
@@ -625,6 +646,9 @@ export const createAnimation = (animationNameValue: string | undefined): Animati
step;
shouldForceLinearEasing = false;
// temp
//play();
return generatePublicAPI();
};
@@ -653,18 +677,19 @@ export const createAnimation = (animationNameValue: string | undefined): Animati
childAnimations.forEach(animation => {
animation.play();
});
if (!initialized) {
initializeAnimation();
}
if (supportsWebAnimations()) {
webAnimations.forEach(animation => {
animation.play();
});
} else {
elements.forEach(element => {
(element as HTMLElement).style.animationPlayState = 'running';
if (_keyframes.length > 0) {
(element as HTMLElement).style.animationPlayState = 'running';}
});
}
@@ -706,23 +731,25 @@ export const createAnimation = (animationNameValue: string | undefined): Animati
};
const to = (property: string, value: any): Animation => {
const keyframeValues = getKeyframes();
const lastFrame = keyframeValues[keyframeValues.length - 1];
if (lastFrame != null && (lastFrame.offset === undefined || lastFrame.offset === 1)) {
lastFrame[property] = value;
} else {
const object: any = {
offset: 1
};
object[property] = value;
_keyframes = [
..._keyframes,
object
];
}
return generatePublicAPI();
};
@@ -785,10 +812,15 @@ export const createAnimation = (animationNameValue: string | undefined): Animati
progressEnd
};
};
animationNameValue;
name(`ion-animation-${counter}`);
/*
if (animationNameValue !== undefined) {
name(animationNameValue);
}
*/
return generatePublicAPI();
};

View File

@@ -0,0 +1,97 @@
<!DOCTYPE html>
<html dir="ltr">
<head>
<meta charset="UTF-8">
<title>Animation - Basic</title>
<meta name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<script type="module">
import { createAnimation } from '../../../../dist/collection/utils/animation/animation.js';
const cover = document.querySelectorAll('.cover');
const square = document.querySelectorAll('.square');
const rootAnimation = createAnimation();
const coverAnimation = createAnimation();
const squareAnimation = createAnimation();
rootAnimation
.duration(500)
.easing('ease-in-out');
coverAnimation
.addElement(cover)
.beforeStyles({ opacity: '1' })
.afterStyles({ opacity: '' });
squareAnimation
.addElement(square)
.beforeClearStyles(['opacity'])
.fromTo('opacity', 0.70, 0.03);
rootAnimation.addAnimation(coverAnimation);
coverAnimation.addAnimation([squareAnimation]);
let counter = 0;
let init = false;
document.querySelector('ion-button').addEventListener('click', () => {
if (counter === 100) {
counter -= 5;
} else {
counter += 5;
}
if (!init) {
rootAnimation.progressStart();
init = true;
}
rootAnimation.progressStep(counter / 100);
})
</script>
<style>
.cover {
opacity: 0;
}
.square {
width: 100px;
height: 100px;
background: rgba(0, 0, 255, 0.5);
text-align: center;
line-height: 100px;
margin-left: 25px;
margin-top: 25px;
margin-bottom: 25px;
opacity: 0;
}
</style>
</head>
<body
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Animations</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<div class="ion-padding">
<ion-button>play step</ion-button>
<div class="cover">
<div class="square">Hello</div>
</div>
</div>
</ion-content>
</ion-app>
</body>
</html>

View File

@@ -165,6 +165,11 @@ export interface IonicConfig {
*/
pickerLeave?: AnimationBuilder;
/**
* EXPERIMENTAL: Adds a page shadow to transitioning pages on iOS. Disabled by default.
*/
experimentalTransitionShadow?: boolean;
// PRIVATE configs
keyboardHeight?: number;
inputShims?: boolean;

View File

@@ -2,20 +2,19 @@ import { Animation } from '../../interface';
import { Animation as AnimationNew, createAnimation } from '../animation/animation';
import { TransitionOptions } from '../transition';
const DURATION = 500;
const EASING = 'cubic-bezier(0.36,0.66,0.04,1)';
const OPACITY = 'opacity';
const TRANSFORM = 'transform';
const TRANSLATEX = 'translateX';
const CENTER = '0%';
const OFF_OPACITY = 0.8;
export const shadow = <T extends Element>(el: T): ShadowRoot | T => {
return el.shadowRoot || el;
};
export const newIosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptions): Promise<AnimationNew> => {
try {
const DURATION = 540;
const EASING = 'cubic-bezier(0.32,0.72,0,1)';
const OPACITY = 'opacity';
const TRANSFORM = 'transform';
const CENTER = '0%';
const OFF_OPACITY = 0.8;
const isRTL = (navEl.ownerDocument as any).dir === 'rtl';
const OFF_RIGHT = isRTL ? '-99.5%' : '99.5%';
const OFF_LEFT = isRTL ? '33%' : '-33%';
@@ -61,7 +60,37 @@ export const newIosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOp
// entering content, forward direction
enteringContentAnimation
.beforeClearStyles([OPACITY])
.fromTo('transform', `translateX(${OFF_RIGHT})`, `translateX(${CENTER})`);
.fromTo('transform', `translateX(${OFF_RIGHT})`, `translateX(${CENTER})`);
}
if (contentEl) {
const enteringTransitionEffectEl = shadow(contentEl).querySelector('.transition-effect');
if (enteringTransitionEffectEl) {
const enteringTransitionCoverEl = enteringTransitionEffectEl.querySelector('.transition-cover');
const enteringTransitionShadowEl = enteringTransitionEffectEl.querySelector('.transition-shadow');
const enteringTransitionEffect = createAnimation('ios-entering-transition-effect');
const enteringTransitionCover = createAnimation('ios-entering-transition-cover');
const enteringTransitionShadow = createAnimation('ios-entering-transition-shadow');
enteringTransitionEffect
.addElement(enteringTransitionEffectEl)
.beforeStyles({ opacity: '1' })
.afterStyles({ opacity: '' });
enteringTransitionCover
.addElement(enteringTransitionCoverEl)
.beforeClearStyles([OPACITY])
.fromTo(OPACITY, 0, 0.1);
enteringTransitionShadow
.addElement(enteringTransitionShadowEl)
.beforeClearStyles([OPACITY])
.fromTo(OPACITY, 0.03, 0.70);
enteringTransitionEffect.addAnimation([enteringTransitionCover, enteringTransitionShadow]);
enteringContentAnimation.addAnimation([enteringTransitionEffect]);
}
}
enteringToolBarEls.forEach((enteringToolBarEl, i) => {
@@ -127,8 +156,10 @@ export const newIosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOp
// setup leaving view
if (leavingEl) {
const leavingContent = createAnimation(`ios-leaving-content-animation`);
leavingContent.addElement(leavingEl.querySelector(':scope > ion-content'));
let leavingContent = createAnimation(`ios-leaving-content-animation`);
const leavingContentEl = leavingEl.querySelector(':scope > ion-content');
leavingContent.addElement(leavingContentEl);
leavingContent.addElement(leavingEl.querySelectorAll(':scope > ion-header > *:not(ion-toolbar), :scope > ion-footer > *'));
rootAnimation.addAnimation(leavingContent);
@@ -144,6 +175,37 @@ export const newIosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOp
.fromTo('transform', `translateX(${CENTER})`, `translateX(${OFF_LEFT})`)
.fromTo(OPACITY, 1, OFF_OPACITY);
}
if (leavingContentEl) {
const leavingTransitionEffectEl = shadow(leavingContentEl).querySelector('.transition-effect');
if (leavingTransitionEffectEl) {
const leavingTransitionCoverEl = leavingTransitionEffectEl.querySelector('.transition-cover');
const leavingTransitionShadowEl = leavingTransitionEffectEl.querySelector('.transition-shadow');
const leavingTransitionEffect = createAnimation('ios-leaving-transition-effect');
const leavingTransitionCover = createAnimation('ios-leaving-transition-cover');
const leavingTransitionShadow = createAnimation('ios-leaving-transition-shadow');
leavingTransitionEffect
.addElement(leavingTransitionEffectEl)
.beforeStyles({ opacity: '1' })
.afterStyles({ opacity: '' });
leavingTransitionCover
.addElement(leavingTransitionCoverEl)
.beforeClearStyles([OPACITY])
.fromTo(OPACITY, 0.1, 0);
leavingTransitionShadow
.addElement(leavingTransitionShadowEl)
.beforeClearStyles([OPACITY])
.fromTo(OPACITY, 0.70, 0.03);
leavingTransitionEffect.addAnimation([leavingTransitionCover, leavingTransitionShadow]);
leavingContent.addAnimation([leavingTransitionEffect]);
}
}
const leavingToolBarEls = leavingEl.querySelectorAll(':scope > ion-header > ion-toolbar');
leavingToolBarEls.forEach((leavingToolBarEl, i) => {
@@ -192,7 +254,7 @@ export const newIosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOp
.fromTo(OPACITY, 1, 0.01);
if (backButtonEl) {
const leavingBackBtnText = createAnimation('ios-leaving-toolbar-${i}-back-button-text');
const leavingBackBtnText = createAnimation(`ios-leaving-toolbar-${i}-back-button-text`);
leavingBackBtnText.addElement(shadow(backButtonEl).querySelector('.button-text'));
leavingBackBtnText.fromTo('transform', `translateX(${CENTER})`, `translateX(${(isRTL ? -124 : 124) + 'px'})`);
leavingToolBar.addAnimation(leavingBackBtnText);
@@ -222,12 +284,25 @@ export const newIosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOp
export const iosTransitionAnimation = (AnimationC: Animation, navEl: HTMLElement, opts: TransitionOptions): Promise<Animation> => {
const DURATION = 540;
const EASING = 'cubic-bezier(0.32,0.72,0,1)';
const OPACITY = 'opacity';
const TRANSFORM = 'transform';
const TRANSLATEX = 'translateX';
const CENTER = '0%';
const OFF_OPACITY = 0.8;
const backDirection = (opts.direction === 'back');
const isRTL = (navEl.ownerDocument as any).dir === 'rtl';
const OFF_RIGHT = isRTL ? '-99.5%' : '99.5%';
const OFF_LEFT = isRTL ? '33%' : '-33%';
const enteringEl = opts.enteringEl;
const leavingEl = opts.leavingEl;
const contentEl = enteringEl.querySelector(':scope > ion-content');
const headerEls = enteringEl.querySelectorAll(':scope > ion-header > *:not(ion-toolbar), :scope > ion-footer > *');
const enteringToolBarEls = enteringEl.querySelectorAll(':scope > ion-header > ion-toolbar');
const enteringContent = new AnimationC();
const rootTransition = new AnimationC();
rootTransition
@@ -244,18 +319,12 @@ export const iosTransitionAnimation = (AnimationC: Animation, navEl: HTMLElement
rootTransition.add(navDecor);
}
const backDirection = (opts.direction === 'back');
// setting up enter view
const contentEl = enteringEl.querySelector(':scope > ion-content');
const headerEls = enteringEl.querySelectorAll(':scope > ion-header > *:not(ion-toolbar), :scope > ion-footer > *');
const enteringToolBarEls = enteringEl.querySelectorAll(':scope > ion-header > ion-toolbar');
const enteringContent = new AnimationC();
if (!contentEl && enteringToolBarEls.length === 0 && headerEls.length === 0) {
enteringContent.addElement(enteringEl.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs'));
} else {
enteringContent.addElement(contentEl);
enteringContent.addElement(headerEls);
enteringContent
.addElement(contentEl)
.addElement(headerEls);
}
rootTransition.add(enteringContent);
@@ -270,28 +339,61 @@ export const iosTransitionAnimation = (AnimationC: Animation, navEl: HTMLElement
enteringContent
.beforeClearStyles([OPACITY])
.fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
if (contentEl) {
const enteringTransitionEffectEl = shadow(contentEl).querySelector('.transition-effect');
if (enteringTransitionEffectEl) {
const enteringTransitionCoverEl = enteringTransitionEffectEl.querySelector('.transition-cover');
const enteringTransitionShadowEl = enteringTransitionEffectEl.querySelector('.transition-shadow');
const enteringTransitionEffect = new AnimationC();
const enteringTransitionCover = new AnimationC();
const enteringTransitionShadow = new AnimationC();
enteringTransitionEffect
.addElement(enteringTransitionEffectEl)
.beforeStyles({ opacity: '1' })
.afterStyles({ opacity: '' });
enteringTransitionCover
.addElement(enteringTransitionCoverEl)
.beforeClearStyles([OPACITY])
.fromTo(OPACITY, 0, 0.1, true);
enteringTransitionShadow
.addElement(enteringTransitionShadowEl)
.beforeClearStyles([OPACITY])
.fromTo(OPACITY, 0.70, 0.03, true);
enteringContent
.add(enteringTransitionEffect)
.add(enteringTransitionCover)
.add(enteringTransitionShadow);
}
}
}
enteringToolBarEls.forEach(enteringToolBarEl => {
const enteringToolBar = new AnimationC();
const enteringTitle = new AnimationC();
const enteringToolBarButtons = new AnimationC();
const enteringToolBarItems = new AnimationC();
const enteringToolBarBg = new AnimationC();
const enteringBackButton = new AnimationC();
const backButtonEl = enteringToolBarEl.querySelector('ion-back-button');
enteringToolBar.addElement(enteringToolBarEl);
rootTransition.add(enteringToolBar);
const enteringTitle = new AnimationC();
enteringTitle.addElement(enteringToolBarEl.querySelector('ion-title'));
const enteringToolBarButtons = new AnimationC();
enteringToolBarButtons.addElement(enteringToolBarEl.querySelectorAll('ion-buttons,[menuToggle]'));
const enteringToolBarItems = new AnimationC();
enteringToolBarItems.addElement(enteringToolBarEl.querySelectorAll(':scope > *:not(ion-title):not(ion-buttons):not([menuToggle])'));
const enteringToolBarBg = new AnimationC();
enteringToolBarBg.addElement(shadow(enteringToolBarEl).querySelector('.toolbar-background'));
const enteringBackButton = new AnimationC();
const backButtonEl = enteringToolBarEl.querySelector('ion-back-button');
if (backButtonEl) {
enteringBackButton.addElement(backButtonEl);
}
@@ -340,10 +442,13 @@ export const iosTransitionAnimation = (AnimationC: Animation, navEl: HTMLElement
// setup leaving view
if (leavingEl) {
const leavingContent = new AnimationC();
leavingContent.addElement(leavingEl.querySelector(':scope > ion-content'));
leavingContent.addElement(leavingEl.querySelectorAll(':scope > ion-header > *:not(ion-toolbar), :scope > ion-footer > *'));
const leavingContentEl = leavingEl.querySelector(':scope > ion-content');
leavingContent
.addElement(leavingContentEl)
.addElement(leavingEl.querySelectorAll(':scope > ion-header > *:not(ion-toolbar), :scope > ion-footer > *'));
rootTransition.add(leavingContent);
if (backDirection) {
@@ -352,6 +457,38 @@ export const iosTransitionAnimation = (AnimationC: Animation, navEl: HTMLElement
.beforeClearStyles([OPACITY])
.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
if (leavingContentEl) {
const leavingTransitionEffectEl = shadow(leavingContentEl).querySelector('.transition-effect');
if (leavingTransitionEffectEl) {
const leavingTransitionCoverEl = leavingTransitionEffectEl.querySelector('.transition-cover');
const leavingTransitionShadowEl = leavingTransitionEffectEl.querySelector('.transition-shadow');
const leavingTransitionEffect = new AnimationC();
const leavingTransitionCover = new AnimationC();
const leavingTransitionShadow = new AnimationC();
leavingTransitionEffect
.addElement(leavingTransitionEffectEl)
.beforeStyles({ opacity: '1' })
.afterStyles({ opacity: '' });
leavingTransitionCover
.addElement(leavingTransitionCoverEl)
.beforeClearStyles([OPACITY])
.fromTo(OPACITY, 0.1, 0, true);
leavingTransitionShadow
.addElement(leavingTransitionShadowEl)
.beforeClearStyles([OPACITY])
.fromTo(OPACITY, 0.70, 0.03, true);
leavingContent
.add(leavingTransitionEffect)
.add(leavingTransitionCover)
.add(leavingTransitionShadow);
}
}
} else {
// leaving content, forward direction
leavingContent
@@ -362,25 +499,26 @@ export const iosTransitionAnimation = (AnimationC: Animation, navEl: HTMLElement
const leavingToolBarEls = leavingEl.querySelectorAll(':scope > ion-header > ion-toolbar');
leavingToolBarEls.forEach(leavingToolBarEl => {
const leavingToolBar = new AnimationC();
leavingToolBar.addElement(leavingToolBarEl);
const leavingTitle = new AnimationC();
leavingTitle.addElement(leavingToolBarEl.querySelector('ion-title'));
const leavingToolBarButtons = new AnimationC();
leavingToolBarButtons.addElement(leavingToolBarEl.querySelectorAll('ion-buttons,[menuToggle]'));
const leavingToolBarItems = new AnimationC();
const leavingToolBarItemEls = leavingToolBarEl.querySelectorAll(':scope > *:not(ion-title):not(ion-buttons):not([menuToggle])');
const leavingToolBarBg = new AnimationC();
const leavingBackButton = new AnimationC();
const backButtonEl = leavingToolBarEl.querySelector('ion-back-button');
leavingToolBar.addElement(leavingToolBarEl);
leavingTitle.addElement(leavingToolBarEl.querySelector('ion-title'));
leavingToolBarButtons.addElement(leavingToolBarEl.querySelectorAll('ion-buttons,[menuToggle]'));
if (leavingToolBarItemEls.length > 0) {
leavingToolBarItems.addElement(leavingToolBarItemEls);
}
const leavingToolBarBg = new AnimationC();
leavingToolBarBg.addElement(shadow(leavingToolBarEl).querySelector('.toolbar-background'));
const leavingBackButton = new AnimationC();
const backButtonEl = leavingToolBarEl.querySelector('ion-back-button');
if (backButtonEl) {
leavingBackButton.addElement(backButtonEl);
}
@@ -423,6 +561,7 @@ export const iosTransitionAnimation = (AnimationC: Animation, navEl: HTMLElement
leavingTitle
.fromTo(TRANSLATEX, CENTER, OFF_LEFT)
.afterClearStyles([TRANSFORM]);
leavingToolBarItems
.fromTo(TRANSLATEX, CENTER, OFF_LEFT)
.afterClearStyles([TRANSFORM, OPACITY]);
@@ -433,6 +572,7 @@ export const iosTransitionAnimation = (AnimationC: Animation, navEl: HTMLElement
}
});
}
// Return the rootTransition promise
return Promise.resolve(rootTransition);
};

View File

@@ -2,12 +2,11 @@ import { Animation } from '../../interface';
import { Animation as AnimationNew, createAnimation } from '../animation/animation';
import { TransitionOptions } from '../transition';
const TRANSLATEY = 'translateY';
const OFF_BOTTOM = '40px';
const CENTER = '0px';
export const newMdTransitionAnimation = (opts: TransitionOptions): AnimationNew => {
try {
const OFF_BOTTOM = '40px';
const CENTER = '0px';
const rootAnimation = createAnimation('md-root-animation');
const enteringEl = opts.enteringEl;
@@ -61,18 +60,21 @@ export const newMdTransitionAnimation = (opts: TransitionOptions): AnimationNew
};
export const mdTransitionAnimation = (AnimationC: Animation, _: HTMLElement, opts: TransitionOptions): Promise<Animation> => {
const TRANSLATEY = 'translateY';
const OFF_BOTTOM = '40px';
const CENTER = '0px';
const backDirection = (opts.direction === 'back');
const enteringEl = opts.enteringEl;
const leavingEl = opts.leavingEl;
const ionPageElement = getIonPageElement(enteringEl);
const enteringToolbarEle = ionPageElement.querySelector('ion-toolbar');
const rootTransition = new AnimationC();
rootTransition
.addElement(ionPageElement)
.beforeRemoveClass('ion-page-invisible');
const backDirection = (opts.direction === 'back');
// animate the component itself
if (backDirection) {
rootTransition
@@ -88,7 +90,6 @@ export const mdTransitionAnimation = (AnimationC: Animation, _: HTMLElement, opt
}
// Animate toolbar if it's there
const enteringToolbarEle = ionPageElement.querySelector('ion-toolbar');
if (enteringToolbarEle) {
const enteringToolBar = new AnimationC();
enteringToolBar.addElement(enteringToolbarEle);
@@ -118,6 +119,7 @@ const getIonPageElement = (element: HTMLElement) => {
if (element.classList.contains('ion-page')) {
return element;
}
const ionPage = element.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs');
if (ionPage) {
return ionPage;