mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-22 21:48:42 +08:00
feat(animations): back button and view animationsa (#13989)
* feat(animations): back button and view animations * chore(): remove unused css * fix(): fix unsaved css * fix(): hide back button when no history
This commit is contained in:
2
packages/core/src/components.d.ts
vendored
2
packages/core/src/components.d.ts
vendored
@ -312,7 +312,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
namespace JSXElements {
|
namespace JSXElements {
|
||||||
export interface IonBackButtonAttributes extends HTMLAttributes {
|
export interface IonBackButtonAttributes extends HTMLAttributes {
|
||||||
|
mode?: 'ios' | 'md';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
@import "./back-button";
|
|
||||||
@import "./back-button.ios.vars";
|
|
||||||
|
|
||||||
// iOS Back Button
|
|
||||||
// --------------------------------------------------
|
|
@ -1,4 +0,0 @@
|
|||||||
@import "../../themes/ionic.globals.ios";
|
|
||||||
|
|
||||||
// iOS Back Button
|
|
||||||
// --------------------------------------------------
|
|
@ -1,22 +0,0 @@
|
|||||||
@import "./back-button";
|
|
||||||
@import "./back-button.md.vars";
|
|
||||||
|
|
||||||
// Material Design Back Button
|
|
||||||
// --------------------------------------------------
|
|
||||||
|
|
||||||
.back-button-md {
|
|
||||||
@include margin(0, 6px);
|
|
||||||
|
|
||||||
min-width: 44px;
|
|
||||||
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button-icon-md {
|
|
||||||
@include margin(0);
|
|
||||||
@include padding(0, 6px);
|
|
||||||
@include text-align(start);
|
|
||||||
|
|
||||||
font-size: 2.4rem;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
@import "../../themes/ionic.globals.md";
|
|
||||||
|
|
||||||
// Material Design Back Button
|
|
||||||
// --------------------------------------------------
|
|
@ -2,14 +2,12 @@
|
|||||||
// Back Button
|
// Back Button
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
// .back-button {
|
|
||||||
// display: none;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .back-button.show-back-button {
|
|
||||||
.back-button {
|
.back-button {
|
||||||
display: inline-block;
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-button.show-back-button {
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-button-text {
|
.back-button-text {
|
||||||
|
@ -1,53 +1,53 @@
|
|||||||
import { Component, Prop } from '@stencil/core';
|
import { Component, Element, Prop, State } from '@stencil/core';
|
||||||
import { Config } from '../../index';
|
import { Config } from '../../index';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
tag: 'ion-back-button',
|
tag: 'ion-back-button',
|
||||||
styleUrls: {
|
styleUrl: 'back-button.scss',
|
||||||
ios: 'back-button.ios.scss',
|
|
||||||
md: 'back-button.md.scss'
|
|
||||||
},
|
|
||||||
host: {
|
host: {
|
||||||
theme: 'back-button'
|
theme: 'back-button'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
export class BackButton {
|
export class BackButton {
|
||||||
private mode: string;
|
|
||||||
|
@State() custom: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mode determines which platform styles to use.
|
||||||
|
* Possible values are: `"ios"` or `"md"`.
|
||||||
|
* For more information, see [Platform Styles](/docs/theming/platform-specific-styles).
|
||||||
|
*/
|
||||||
|
@Prop() mode: 'ios' | 'md';
|
||||||
|
|
||||||
@Prop({ context: 'config' }) config: Config;
|
@Prop({ context: 'config' }) config: Config;
|
||||||
|
|
||||||
|
@Element() el: HTMLElement;
|
||||||
|
|
||||||
|
componentWillLoad() {
|
||||||
|
this.custom = this.el.childElementCount > 0;
|
||||||
|
}
|
||||||
render() {
|
render() {
|
||||||
const iconName = this.config.get('backButtonIcon', this.mode + '-arrow-back');
|
const backButtonIcon = this.config.get('backButtonIcon', 'arrow-back');
|
||||||
const text = this.config.get('backButtonText', 'Back');
|
const backButtonText = this.config.get('backButtonText', 'Back');
|
||||||
|
const buttonColor = this.mode === 'ios' ? 'primary' : '';
|
||||||
|
|
||||||
const iconClass: any = {
|
if (this.custom) {
|
||||||
'back-button-icon': true
|
return (
|
||||||
};
|
<ion-nav-pop>
|
||||||
const textClass: any = {
|
<slot />
|
||||||
'back-button-text': true
|
</ion-nav-pop>
|
||||||
};
|
);
|
||||||
|
} else if (!this.custom) {
|
||||||
if (this.mode) {
|
return (
|
||||||
iconClass['back-button-icon-' + this.mode] = true;
|
<ion-nav-pop>
|
||||||
iconClass['back-button-text-' + this.mode] = true;
|
<ion-button color={buttonColor}>
|
||||||
|
<ion-icon name={backButtonIcon} slot='start' />
|
||||||
|
{backButtonText}
|
||||||
|
</ion-button>
|
||||||
|
</ion-nav-pop>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
|
||||||
<ion-nav-pop>
|
|
||||||
<button>
|
|
||||||
<span class={iconClass}>
|
|
||||||
<slot name='icon'>
|
|
||||||
<ion-icon name={iconName}></ion-icon>
|
|
||||||
</slot>
|
|
||||||
</span>
|
|
||||||
<span class={textClass}>
|
|
||||||
<slot name='text'>
|
|
||||||
{text}
|
|
||||||
</slot>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</ion-nav-pop>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,86 @@
|
|||||||
# ion-back-button
|
# ion-back-button
|
||||||
|
|
||||||
|
A back button is a component that allows you navigate back into app history. To
|
||||||
|
add a back button to your view, all you need is:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<ion-page>
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
</ion-page>
|
||||||
|
```
|
||||||
|
|
||||||
|
The back button component is smart enough to know what to render and what content to show.
|
||||||
|
|
||||||
|
If, however, you want more control over what is shown in the back button, you can pass your own button markup.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<ion-page>
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button>
|
||||||
|
<ion-button>
|
||||||
|
Button Text
|
||||||
|
<ion-icon name="add" slot="start"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
</ion-page>
|
||||||
|
```
|
||||||
|
|
||||||
|
Or no button text at all:
|
||||||
|
|
||||||
|
|
||||||
|
```html
|
||||||
|
<ion-page>
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button>
|
||||||
|
<ion-button>
|
||||||
|
<ion-icon name="add" slot="icon-only"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
</ion-page>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
<!-- Auto Generated Below -->
|
<!-- Auto Generated Below -->
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
#### mode
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The mode determines which platform styles to use.
|
||||||
|
Possible values are: `"ios"` or `"md"`.
|
||||||
|
For more information, see [Platform Styles](/docs/theming/platform-specific-styles).
|
||||||
|
|
||||||
|
|
||||||
|
## Attributes
|
||||||
|
|
||||||
|
#### mode
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The mode determines which platform styles to use.
|
||||||
|
Possible values are: `"ios"` or `"md"`.
|
||||||
|
For more information, see [Platform Styles](/docs/theming/platform-specific-styles).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
|
@ -35,6 +35,9 @@
|
|||||||
firstPage.innerHTML = `
|
firstPage.innerHTML = `
|
||||||
<ion-header>
|
<ion-header>
|
||||||
<ion-toolbar>
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
<ion-title>Page One</ion-title>
|
<ion-title>Page One</ion-title>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
@ -47,7 +50,7 @@
|
|||||||
await nav.push(firstPage);
|
await nav.push(firstPage);
|
||||||
|
|
||||||
// okay cool, we're in the DOM now
|
// okay cool, we're in the DOM now
|
||||||
const button = firstPage.querySelector('ion-button');
|
const button = firstPage.querySelector('.next');
|
||||||
button.addEventListener('click', async () => {
|
button.addEventListener('click', async () => {
|
||||||
await goToPageTwo(nav);
|
await goToPageTwo(nav);
|
||||||
});
|
});
|
||||||
@ -60,14 +63,14 @@
|
|||||||
<ion-header>
|
<ion-header>
|
||||||
<ion-toolbar>
|
<ion-toolbar>
|
||||||
<ion-buttons slot="start">
|
<ion-buttons slot="start">
|
||||||
<ion-back-button>
|
<ion-back-button></ion-back-button>
|
||||||
</ion-back-button>
|
|
||||||
</ion-buttons>
|
</ion-buttons>
|
||||||
<ion-title>Page Two</ion-title>
|
<ion-title>Page Two</ion-title>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content padding>
|
<ion-content padding>
|
||||||
<h1>Page Two</h1>
|
<h1>Page Two</h1>
|
||||||
|
<p>Just an empty <code>ion-back-button</code></p>
|
||||||
<ion-button class="next">Go to Page Three</ion-button>
|
<ion-button class="next">Go to Page Three</ion-button>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
`;
|
`;
|
||||||
@ -89,6 +92,9 @@
|
|||||||
<ion-toolbar>
|
<ion-toolbar>
|
||||||
<ion-buttons slot="start">
|
<ion-buttons slot="start">
|
||||||
<ion-back-button>
|
<ion-back-button>
|
||||||
|
<ion-button>
|
||||||
|
<ion-icon name="add" slot="icon-only"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
</ion-back-button>
|
</ion-back-button>
|
||||||
</ion-buttons>
|
</ion-buttons>
|
||||||
<ion-title>Page Three</ion-title>
|
<ion-title>Page Three</ion-title>
|
||||||
@ -96,6 +102,7 @@
|
|||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content padding>
|
<ion-content padding>
|
||||||
<h1>Page Three</h1>
|
<h1>Page Three</h1>
|
||||||
|
<p>Custom back button</p>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -103,10 +110,10 @@
|
|||||||
await nav.push(thirdPage);
|
await nav.push(thirdPage);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<!-- <style> -->
|
||||||
ion-back-button {
|
<!-- ion-back-button { -->
|
||||||
height: 50px;
|
<!-- height: 50px; -->
|
||||||
background-color: blue;
|
<!-- background-color: blue; -->
|
||||||
}
|
<!-- } -->
|
||||||
</style>
|
<!-- </style> -->
|
||||||
</html>
|
</html>
|
||||||
|
@ -134,7 +134,7 @@ export class Button {
|
|||||||
<span class='button-inner'>
|
<span class='button-inner'>
|
||||||
<slot name='icon-only'></slot>
|
<slot name='icon-only'></slot>
|
||||||
<slot name='start'></slot>
|
<slot name='start'></slot>
|
||||||
<slot></slot>
|
<span class='button-text'><slot></slot></span>
|
||||||
<slot name='end'></slot>
|
<slot name='end'></slot>
|
||||||
</span>
|
</span>
|
||||||
{ this.mode === 'md' && <ion-ripple-effect useTapClick={true} /> }
|
{ this.mode === 'md' && <ion-ripple-effect useTapClick={true} /> }
|
||||||
|
@ -2,13 +2,6 @@ import { Component, Element, Listen } from '@stencil/core';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
tag: 'ion-nav-pop',
|
tag: 'ion-nav-pop',
|
||||||
styleUrls: {
|
|
||||||
ios: 'nav-pop.ios.scss',
|
|
||||||
md: 'nav-pop.md.scss'
|
|
||||||
},
|
|
||||||
host: {
|
|
||||||
theme: 'nav-pop'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
export class NavPop {
|
export class NavPop {
|
||||||
|
|
||||||
|
@ -70,17 +70,15 @@ export function updateZIndex(viewController: ViewController, newZIndex: number)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toggleHidden(element: HTMLElement, isVisible: Boolean, shouldBeVisible: boolean) {
|
export function toggleHidden(element: HTMLElement, shouldBeHidden: boolean) {
|
||||||
if (isVisible !== shouldBeVisible) {
|
element.hidden = shouldBeHidden;
|
||||||
element.hidden = shouldBeVisible;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canNavGoBack(nav: Nav) {
|
export function canNavGoBack(nav: Nav, view?: ViewController) {
|
||||||
if (!nav) {
|
if (!nav) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return !!nav.getPrevious();
|
return nav.getPrevious(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function transitionFactory(animation: Animation): Transition {
|
export function transitionFactory(animation: Animation): Transition {
|
||||||
|
@ -13,7 +13,10 @@ ion-nav {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
background-color: black;
|
||||||
|
|
||||||
contain: layout size style;
|
contain: layout size style;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ion-page {
|
.ion-page {
|
||||||
|
@ -737,7 +737,6 @@ export function loadViewAndTransition(nav: Nav, enteringView: ViewController, le
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function executeAsyncTransition(nav: Nav, transition: Transition, enteringView: ViewController, leavingView: ViewController, delegate: FrameworkDelegate, opts: NavOptions, configShouldAnimate: boolean): Promise<void> {
|
export function executeAsyncTransition(nav: Nav, transition: Transition, enteringView: ViewController, leavingView: ViewController, delegate: FrameworkDelegate, opts: NavOptions, configShouldAnimate: boolean): Promise<void> {
|
||||||
assert(nav.transitioning, 'must be transitioning');
|
assert(nav.transitioning, 'must be transitioning');
|
||||||
nav.transitionId = null;
|
nav.transitionId = null;
|
||||||
@ -746,11 +745,11 @@ export function executeAsyncTransition(nav: Nav, transition: Transition, enterin
|
|||||||
// always ensure the entering view is viewable
|
// always ensure the entering view is viewable
|
||||||
// ******** DOM WRITE ****************
|
// ******** DOM WRITE ****************
|
||||||
// TODO, figure out where we want to read this data from
|
// TODO, figure out where we want to read this data from
|
||||||
enteringView && toggleHidden(enteringView.element, true, true);
|
enteringView && toggleHidden(enteringView.element, false);
|
||||||
|
|
||||||
// always ensure the leaving view is viewable
|
// always ensure the leaving view is viewable
|
||||||
// ******** DOM WRITE ****************
|
// ******** DOM WRITE ****************
|
||||||
leavingView && toggleHidden(leavingView.element, true, true);
|
leavingView && toggleHidden(leavingView.element, false);
|
||||||
|
|
||||||
const isFirstPage = !nav.isViewInitialized && nav.views.length === 1;
|
const isFirstPage = !nav.isViewInitialized && nav.views.length === 1;
|
||||||
const shouldNotAnimate = isFirstPage && !nav.isPortal;
|
const shouldNotAnimate = isFirstPage && !nav.isPortal;
|
||||||
@ -834,15 +833,16 @@ export function transitionFinish(nav: Nav, transition: Transition, delegate: Fra
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function cleanUpView(nav: Nav, delegate: FrameworkDelegate, activeViewController: ViewController): Promise<any> {
|
export function cleanUpView(nav: Nav, delegate: FrameworkDelegate, activeViewController: ViewController): Promise<any> {
|
||||||
|
|
||||||
if (nav.destroyed) {
|
if (nav.destroyed) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeIndex = nav.views.indexOf(activeViewController);
|
const activeIndex = nav.views.indexOf(activeViewController);
|
||||||
const promises: Promise<any>[] = [];
|
const promises: Promise<any>[] = [];
|
||||||
|
|
||||||
for (let i = nav.views.length - 1; i >= 0; i--) {
|
for (let i = nav.views.length - 1; i >= 0; i--) {
|
||||||
const inactiveViewController = nav.views[i];
|
const inactiveViewController = nav.views[i];
|
||||||
|
|
||||||
if (i > activeIndex) {
|
if (i > activeIndex) {
|
||||||
// this view comes after the active view
|
// this view comes after the active view
|
||||||
inactiveViewController.willUnload();
|
inactiveViewController.willUnload();
|
||||||
@ -850,14 +850,14 @@ export function cleanUpView(nav: Nav, delegate: FrameworkDelegate, activeViewCon
|
|||||||
} else if ( i < activeIndex && !nav.isPortal) {
|
} else if ( i < activeIndex && !nav.isPortal) {
|
||||||
// this view comes before the active view
|
// this view comes before the active view
|
||||||
// and it is not a portal then ensure it is hidden
|
// and it is not a portal then ensure it is hidden
|
||||||
toggleHidden(inactiveViewController.element, true, false);
|
toggleHidden(inactiveViewController.element, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - review existing z index code!
|
// TODO - review existing z index code!
|
||||||
}
|
}
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function fireViewWillLifecycles(enteringView: ViewController, leavingView: ViewController) {
|
export function fireViewWillLifecycles(enteringView: ViewController, leavingView: ViewController) {
|
||||||
leavingView && leavingView.willLeave(!enteringView);
|
leavingView && leavingView.willLeave(!enteringView);
|
||||||
enteringView && enteringView.willEnter();
|
enteringView && enteringView.willEnter();
|
||||||
|
@ -11,174 +11,198 @@ const CENTER = '0%';
|
|||||||
const OFF_OPACITY = 0.8;
|
const OFF_OPACITY = 0.8;
|
||||||
const SHOW_BACK_BTN_CSS = 'show-back-button';
|
const SHOW_BACK_BTN_CSS = 'show-back-button';
|
||||||
|
|
||||||
// TODO - make sure this uses the `ion-page` logic from the md transition
|
|
||||||
// DO this later since the transition is broke as a joke anyway
|
|
||||||
// Dan B 1/9/2018
|
|
||||||
|
|
||||||
export function buildIOSTransition(rootTransition: Transition, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions): Promise<Transition> {
|
export function buildIOSTransition(rootTransition: Transition, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions): Promise<Transition> {
|
||||||
|
const componentReadyPromise: Promise<any>[] = [];
|
||||||
rootTransition.enteringView = enteringView;
|
// Let makes sure everything is hydrated and ready to animate
|
||||||
rootTransition.leavingView = leavingView;
|
|
||||||
|
|
||||||
const isRTL = document.dir === 'rtl';
|
|
||||||
const OFF_RIGHT = isRTL ? '-99.5%' : '99.5%';
|
|
||||||
const OFF_LEFT = isRTL ? '33%' : '-33%';
|
|
||||||
|
|
||||||
rootTransition.duration(isDef(opts.duration) ? opts.duration : DURATION);
|
|
||||||
rootTransition.easing(isDef(opts.easing) ? opts.easing : EASING);
|
|
||||||
|
|
||||||
|
|
||||||
rootTransition.addElement(enteringView.element);
|
|
||||||
rootTransition.beforeRemoveClass('hide-page');
|
|
||||||
|
|
||||||
const backDirection = (opts.direction === 'back');
|
|
||||||
|
|
||||||
if (enteringView) {
|
if (enteringView) {
|
||||||
const enteringContent = rootTransition.create();
|
componentReadyPromise.push((enteringView.element as any).componentOnReady());
|
||||||
enteringContent.addElement(enteringView.element.querySelectorAll('ion-header > *:not(ion-toolbar),ion-footer > *'));
|
}
|
||||||
|
if (leavingView) {
|
||||||
|
componentReadyPromise.push((leavingView.element as any).componentOnReady());
|
||||||
|
}
|
||||||
|
|
||||||
rootTransition.add(enteringContent);
|
return Promise.all(componentReadyPromise).then(() => {
|
||||||
|
// Cool we're all hydrated, and can do deep selector
|
||||||
|
rootTransition.enteringView = enteringView;
|
||||||
|
rootTransition.leavingView = leavingView;
|
||||||
|
|
||||||
if (backDirection) {
|
const isRTL = document.dir === 'rtl';
|
||||||
enteringContent.fromTo(TRANSLATEX, OFF_LEFT, CENTER, true).fromTo(OPACITY, OFF_OPACITY, 1, true);
|
const OFF_RIGHT = isRTL ? '-99.5%' : '99.5%';
|
||||||
} else {
|
const OFF_LEFT = isRTL ? '33%' : '-33%';
|
||||||
// entering content, forward direction
|
|
||||||
enteringContent.beforeClearStyles([OPACITY]).fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const enteringToolBarEle = enteringView.element.querySelector('ion-toolbar');
|
rootTransition.duration(isDef(opts.duration) ? opts.duration : DURATION);
|
||||||
if (enteringToolBarEle) {
|
rootTransition.easing(isDef(opts.easing) ? opts.easing : EASING);
|
||||||
const enteringToolBar = rootTransition.create();
|
|
||||||
enteringToolBar.addElement(enteringToolBarEle);
|
|
||||||
|
|
||||||
rootTransition.add(enteringToolBar);
|
|
||||||
|
|
||||||
const enteringTitle = rootTransition.create();
|
rootTransition.addElement(enteringView.element);
|
||||||
enteringTitle.addElement(enteringToolBarEle.querySelector('ion-title'));
|
rootTransition.beforeRemoveClass('hide-page');
|
||||||
const enteringToolBarItems = rootTransition.create();
|
|
||||||
enteringToolBarItems.addElement(enteringToolBarEle.querySelectorAll('ion-buttons,[menuToggle]'));
|
|
||||||
const enteringToolBarBg = rootTransition.create();
|
|
||||||
enteringToolBarBg.addElement(enteringToolBarEle.querySelector('.toolbar-background'));
|
|
||||||
const enteringBackButton = rootTransition.create();
|
|
||||||
enteringBackButton.addElement(enteringToolBarEle.querySelector('.back-button'));
|
|
||||||
|
|
||||||
enteringToolBar
|
const backDirection = (opts.direction === 'back');
|
||||||
.add(enteringTitle)
|
|
||||||
.add(enteringToolBarItems)
|
|
||||||
.add(enteringToolBarBg)
|
|
||||||
.add(enteringBackButton);
|
|
||||||
|
|
||||||
enteringTitle.fromTo(OPACITY, 0.01, 1, true);
|
// setting up enter view
|
||||||
enteringToolBarItems.fromTo(OPACITY, 0.01, 1, true);
|
if (enteringView) {
|
||||||
|
|
||||||
|
const enteringContent = rootTransition.create();
|
||||||
|
enteringContent.addElement(enteringView.element.querySelector('ion-content'));
|
||||||
|
enteringContent.addElement(enteringView.element.querySelectorAll('ion-header > *:not(ion-toolbar),ion-footer > *'));
|
||||||
|
rootTransition.add(enteringContent);
|
||||||
|
|
||||||
if (backDirection) {
|
if (backDirection) {
|
||||||
enteringTitle.fromTo(TRANSLATEX, OFF_LEFT, CENTER, true);
|
enteringContent
|
||||||
|
.beforeClearStyles([OPACITY])
|
||||||
if (canNavGoBack(enteringView.nav)) {
|
.fromTo(TRANSLATEX, OFF_LEFT, CENTER, true)
|
||||||
// back direction, entering page has a back button
|
.fromTo(OPACITY, OFF_OPACITY, 1, true);
|
||||||
enteringBackButton.beforeAddClass(SHOW_BACK_BTN_CSS).fromTo(OPACITY, 0.01, 1, true);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// entering toolbar, forward direction
|
// entering content, forward direction
|
||||||
enteringTitle.fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
enteringContent
|
||||||
|
.beforeClearStyles([OPACITY])
|
||||||
|
.fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
||||||
|
|
||||||
enteringToolBarBg.beforeClearStyles([OPACITY]).fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
}
|
||||||
|
|
||||||
if (canNavGoBack(enteringView.nav)) {
|
const enteringToolBarEle = enteringView.element.querySelector('ion-toolbar');
|
||||||
// forward direction, entering page has a back button
|
if (enteringToolBarEle) {
|
||||||
enteringBackButton.beforeAddClass(SHOW_BACK_BTN_CSS).fromTo(OPACITY, 0.01, 1, true);
|
const enteringToolBar = rootTransition.create();
|
||||||
|
enteringToolBar.addElement(enteringToolBarEle);
|
||||||
|
rootTransition.add(enteringToolBar);
|
||||||
|
|
||||||
|
const enteringTitle = rootTransition.create();
|
||||||
|
enteringTitle.addElement(enteringToolBarEle.querySelector('ion-title'));
|
||||||
|
|
||||||
const enteringBackBtnText = rootTransition.create();
|
const enteringToolBarItems = rootTransition.create();
|
||||||
enteringBackBtnText.addElement(enteringToolBarEle.querySelector('.back-button-text'));
|
enteringToolBarItems.addElement(enteringToolBarEle.querySelectorAll('ion-buttons,[menuToggle]'));
|
||||||
|
|
||||||
enteringBackBtnText.fromTo(TRANSLATEX, (isRTL ? '-100px' : '100px'), '0px');
|
const enteringToolBarBg = rootTransition.create();
|
||||||
enteringToolBar.add(enteringBackBtnText);
|
enteringToolBarBg.addElement(enteringToolBarEle.querySelector('.toolbar-background'));
|
||||||
|
|
||||||
|
const enteringBackButton = rootTransition.create();
|
||||||
|
enteringBackButton.addElement(enteringToolBarEle.querySelector('.back-button'));
|
||||||
|
|
||||||
|
enteringToolBar
|
||||||
|
.add(enteringTitle)
|
||||||
|
.add(enteringToolBarItems)
|
||||||
|
.add(enteringToolBarBg)
|
||||||
|
.add(enteringBackButton);
|
||||||
|
|
||||||
|
enteringTitle.fromTo(OPACITY, 0.01, 1, true);
|
||||||
|
enteringToolBarItems.fromTo(OPACITY, 0.01, 1, true);
|
||||||
|
|
||||||
|
if (backDirection) {
|
||||||
|
enteringTitle.fromTo(TRANSLATEX, OFF_LEFT, CENTER, true);
|
||||||
|
|
||||||
|
if (canNavGoBack(enteringView.nav, enteringView)) {
|
||||||
|
// back direction, entering page has a back button
|
||||||
|
enteringBackButton.beforeAddClass(SHOW_BACK_BTN_CSS).fromTo(OPACITY, 0.01, 1, true);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
enteringBackButton.beforeRemoveClass(SHOW_BACK_BTN_CSS);
|
// entering toolbar, forward direction
|
||||||
|
enteringTitle.fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
||||||
|
|
||||||
|
enteringToolBarBg
|
||||||
|
.beforeClearStyles([OPACITY])
|
||||||
|
.fromTo(TRANSLATEX, OFF_RIGHT, CENTER, true);
|
||||||
|
|
||||||
|
if (canNavGoBack(enteringView.nav, enteringView)) {
|
||||||
|
|
||||||
|
// forward direction, entering page has a back button
|
||||||
|
enteringBackButton
|
||||||
|
.beforeAddClass(SHOW_BACK_BTN_CSS)
|
||||||
|
.fromTo(OPACITY, 0.01, 1, true);
|
||||||
|
|
||||||
|
|
||||||
|
const enteringBackBtnText = rootTransition.create();
|
||||||
|
enteringBackBtnText.addElement(enteringToolBarEle.querySelector('.back-button .button-text'));
|
||||||
|
|
||||||
|
enteringBackBtnText.fromTo(TRANSLATEX, (isRTL ? '-100px' : '100px'), '0px');
|
||||||
|
enteringToolBar.add(enteringBackBtnText);
|
||||||
|
} else {
|
||||||
|
enteringBackButton.beforeRemoveClass(SHOW_BACK_BTN_CSS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// setup leaving view
|
// setup leaving view
|
||||||
if (leavingView) {
|
if (leavingView) {
|
||||||
|
|
||||||
const leavingContent = rootTransition.create();
|
const leavingContent = rootTransition.create();
|
||||||
leavingContent.addElement(leavingView.element);
|
leavingContent.addElement(leavingView.element.querySelector('ion-content'));
|
||||||
leavingContent.addElement(leavingView.element.querySelectorAll('ion-header > *:not(ion-toolbar),ion-footer > *'));
|
leavingContent.addElement(leavingView.element.querySelectorAll('ion-header > *:not(ion-toolbar),ion-footer > *'));
|
||||||
|
rootTransition.add(leavingContent);
|
||||||
rootTransition.add(leavingContent);
|
|
||||||
|
|
||||||
if (backDirection) {
|
|
||||||
// leaving content, back direction
|
|
||||||
leavingContent.beforeClearStyles([OPACITY]).fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// leaving content, forward direction
|
|
||||||
leavingContent
|
|
||||||
.fromTo(TRANSLATEX, CENTER, OFF_LEFT)
|
|
||||||
.fromTo(OPACITY, 1, OFF_OPACITY)
|
|
||||||
.afterClearStyles([TRANSFORM, OPACITY]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const leavingToolBarEle = leavingView.element.querySelector('ion-toolbar');
|
|
||||||
if (leavingToolBarEle) {
|
|
||||||
const leavingToolBar = rootTransition.create();
|
|
||||||
leavingToolBar.addElement(leavingToolBarEle);
|
|
||||||
|
|
||||||
const leavingTitle = rootTransition.create();
|
|
||||||
leavingTitle.addElement(leavingToolBarEle.querySelector('ion-title'));
|
|
||||||
|
|
||||||
const leavingToolBarItems = rootTransition.create();
|
|
||||||
leavingToolBarItems.addElement(leavingToolBarEle.querySelectorAll('ion-buttons,[menuToggle]'));
|
|
||||||
|
|
||||||
const leavingToolBarBg = rootTransition.create();
|
|
||||||
leavingToolBarBg.addElement(leavingToolBarEle.querySelector('.toolbar-background'));
|
|
||||||
|
|
||||||
const leavingBackButton = rootTransition.create();
|
|
||||||
leavingBackButton.addElement(leavingToolBarEle.querySelector('.back-button'));
|
|
||||||
|
|
||||||
leavingToolBar
|
|
||||||
.add(leavingTitle)
|
|
||||||
.add(leavingToolBarItems)
|
|
||||||
.add(leavingBackButton)
|
|
||||||
.add(leavingToolBarBg);
|
|
||||||
|
|
||||||
rootTransition.add(leavingToolBar);
|
|
||||||
|
|
||||||
// fade out leaving toolbar items
|
|
||||||
leavingBackButton.fromTo(OPACITY, 0.99, 0);
|
|
||||||
leavingTitle.fromTo(OPACITY, 0.99, 0);
|
|
||||||
leavingToolBarItems.fromTo(OPACITY, 0.99, 0);
|
|
||||||
|
|
||||||
if (backDirection) {
|
if (backDirection) {
|
||||||
// leaving toolbar, back direction
|
// leaving content, back direction
|
||||||
leavingTitle.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
leavingContent
|
||||||
|
|
||||||
// leaving toolbar, back direction, and there's no entering toolbar
|
|
||||||
// should just slide out, no fading out
|
|
||||||
leavingToolBarBg
|
|
||||||
.beforeClearStyles([OPACITY])
|
.beforeClearStyles([OPACITY])
|
||||||
.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
||||||
|
|
||||||
const leavingBackBtnText = rootTransition.create();
|
|
||||||
leavingBackBtnText.addElement(leavingToolBarEle.querySelector('.back-button-text'));
|
|
||||||
leavingBackBtnText.fromTo(TRANSLATEX, CENTER, (isRTL ? -300 : 300) + 'px');
|
|
||||||
leavingToolBar.add(leavingBackBtnText);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// leaving toolbar, forward direction
|
// leaving content, forward direction
|
||||||
leavingTitle
|
leavingContent
|
||||||
.fromTo(TRANSLATEX, CENTER, OFF_LEFT)
|
.fromTo(TRANSLATEX, CENTER, OFF_LEFT, true)
|
||||||
.afterClearStyles([TRANSFORM]);
|
.fromTo(OPACITY, 1, OFF_OPACITY, true);
|
||||||
|
}
|
||||||
|
|
||||||
leavingBackButton.afterClearStyles([OPACITY]);
|
const leavingToolBarEle = leavingView.element.querySelector('ion-toolbar');
|
||||||
leavingTitle.afterClearStyles([OPACITY]);
|
if (leavingToolBarEle) {
|
||||||
leavingToolBarItems.afterClearStyles([OPACITY]);
|
const leavingToolBar = rootTransition.create();
|
||||||
|
leavingToolBar.addElement(leavingToolBarEle);
|
||||||
|
|
||||||
|
const leavingTitle = rootTransition.create();
|
||||||
|
leavingTitle.addElement(leavingToolBarEle.querySelector('ion-title'));
|
||||||
|
|
||||||
|
const leavingToolBarItems = rootTransition.create();
|
||||||
|
leavingToolBarItems.addElement(leavingToolBarEle.querySelectorAll('ion-buttons,[menuToggle]'));
|
||||||
|
|
||||||
|
const leavingToolBarBg = rootTransition.create();
|
||||||
|
leavingToolBarBg.addElement(leavingToolBarEle.querySelector('.toolbar-background'));
|
||||||
|
|
||||||
|
const leavingBackButton = rootTransition.create();
|
||||||
|
leavingBackButton.addElement(leavingToolBarEle.querySelector('.back-button'));
|
||||||
|
|
||||||
|
leavingToolBar
|
||||||
|
.add(leavingTitle)
|
||||||
|
.add(leavingToolBarItems)
|
||||||
|
.add(leavingBackButton)
|
||||||
|
.add(leavingToolBarBg);
|
||||||
|
|
||||||
|
rootTransition.add(leavingToolBar);
|
||||||
|
|
||||||
|
// fade out leaving toolbar items
|
||||||
|
leavingBackButton.fromTo(OPACITY, 0.99, 0, true);
|
||||||
|
leavingTitle.fromTo(OPACITY, 0.99, 0, true);
|
||||||
|
leavingToolBarItems.fromTo(OPACITY, 0.99, 0, true);
|
||||||
|
|
||||||
|
if (backDirection) {
|
||||||
|
// leaving toolbar, back direction
|
||||||
|
leavingTitle.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
||||||
|
|
||||||
|
// leaving toolbar, back direction, and there's no entering toolbar
|
||||||
|
// should just slide out, no fading out
|
||||||
|
leavingToolBarBg
|
||||||
|
.beforeClearStyles([OPACITY])
|
||||||
|
.fromTo(TRANSLATEX, CENTER, (isRTL ? '-100%' : '100%'));
|
||||||
|
|
||||||
|
const leavingBackBtnText = rootTransition.create();
|
||||||
|
leavingBackBtnText.addElement(leavingToolBarEle.querySelector('.back-button .button-text'));
|
||||||
|
leavingBackBtnText.fromTo(TRANSLATEX, CENTER, (isRTL ? -300 : 300) + 'px');
|
||||||
|
leavingToolBar.add(leavingBackBtnText);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// leaving toolbar, forward direction
|
||||||
|
leavingTitle
|
||||||
|
.fromTo(TRANSLATEX, CENTER, OFF_LEFT)
|
||||||
|
.afterClearStyles([TRANSFORM]);
|
||||||
|
|
||||||
|
leavingBackButton.afterClearStyles([OPACITY]);
|
||||||
|
leavingTitle.afterClearStyles([OPACITY]);
|
||||||
|
leavingToolBarItems.afterClearStyles([OPACITY]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return Promise.resolve(rootTransition);
|
// Return the rootTransition promise
|
||||||
|
return rootTransition;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,15 @@ const SHOW_BACK_BTN_CSS = 'show-back-button';
|
|||||||
|
|
||||||
export function buildMdTransition(rootTransition: Transition, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions): Promise<Transition> {
|
export function buildMdTransition(rootTransition: Transition, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions): Promise<Transition> {
|
||||||
|
|
||||||
|
const componentReadyPromise: Promise<any>[] = [];
|
||||||
|
if (enteringView) {
|
||||||
|
componentReadyPromise.push((enteringView.element as any).componentOnReady());
|
||||||
|
}
|
||||||
|
if (leavingView) {
|
||||||
|
componentReadyPromise.push((leavingView.element as any).componentOnReady());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(componentReadyPromise).then(() => {
|
||||||
rootTransition.enteringView = enteringView;
|
rootTransition.enteringView = enteringView;
|
||||||
rootTransition.leavingView = leavingView;
|
rootTransition.leavingView = leavingView;
|
||||||
|
|
||||||
@ -19,9 +28,12 @@ export function buildMdTransition(rootTransition: Transition, enteringView: View
|
|||||||
|
|
||||||
const backDirection = (opts.direction === 'back');
|
const backDirection = (opts.direction === 'back');
|
||||||
if (enteringView) {
|
if (enteringView) {
|
||||||
|
|
||||||
|
// animate the component itself
|
||||||
if (backDirection) {
|
if (backDirection) {
|
||||||
rootTransition.duration(isDef(opts.duration) ? opts.duration : 200).easing('cubic-bezier(0.47,0,0.745,0.715)');
|
rootTransition.duration(isDef(opts.duration) ? opts.duration : 200).easing('cubic-bezier(0.47,0,0.745,0.715)');
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
rootTransition.duration(isDef(opts.duration) ? opts.duration : 280).easing('cubic-bezier(0.36,0.66,0.04,1)');
|
rootTransition.duration(isDef(opts.duration) ? opts.duration : 280).easing('cubic-bezier(0.36,0.66,0.04,1)');
|
||||||
|
|
||||||
rootTransition
|
rootTransition
|
||||||
@ -29,8 +41,10 @@ export function buildMdTransition(rootTransition: Transition, enteringView: View
|
|||||||
.fromTo('opacity', 0.01, 1, true);
|
.fromTo('opacity', 0.01, 1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Animate toolbar if it's there
|
||||||
const enteringToolbarEle = ionPageElement.querySelector('ion-toolbar');
|
const enteringToolbarEle = ionPageElement.querySelector('ion-toolbar');
|
||||||
if (enteringToolbarEle) {
|
if (enteringToolbarEle) {
|
||||||
|
|
||||||
const enteringToolBar = rootTransition.create();
|
const enteringToolBar = rootTransition.create();
|
||||||
enteringToolBar.addElement(enteringToolbarEle);
|
enteringToolBar.addElement(enteringToolbarEle);
|
||||||
rootTransition.add(enteringToolBar);
|
rootTransition.add(enteringToolBar);
|
||||||
@ -38,8 +52,7 @@ export function buildMdTransition(rootTransition: Transition, enteringView: View
|
|||||||
const enteringBackButton = rootTransition.create();
|
const enteringBackButton = rootTransition.create();
|
||||||
enteringBackButton.addElement(enteringToolbarEle.querySelector('.back-button'));
|
enteringBackButton.addElement(enteringToolbarEle.querySelector('.back-button'));
|
||||||
rootTransition.add(enteringBackButton);
|
rootTransition.add(enteringBackButton);
|
||||||
|
if (canNavGoBack(enteringView.nav, enteringView)) {
|
||||||
if (canNavGoBack(enteringView.nav)) {
|
|
||||||
enteringBackButton.beforeAddClass(SHOW_BACK_BTN_CSS);
|
enteringBackButton.beforeAddClass(SHOW_BACK_BTN_CSS);
|
||||||
} else {
|
} else {
|
||||||
enteringBackButton.beforeRemoveClass(SHOW_BACK_BTN_CSS);
|
enteringBackButton.beforeRemoveClass(SHOW_BACK_BTN_CSS);
|
||||||
@ -56,7 +69,10 @@ export function buildMdTransition(rootTransition: Transition, enteringView: View
|
|||||||
rootTransition.add(leavingPage.fromTo(TRANSLATEY, CENTER, OFF_BOTTOM).fromTo('opacity', 1, 0));
|
rootTransition.add(leavingPage.fromTo(TRANSLATEY, CENTER, OFF_BOTTOM).fromTo('opacity', 1, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve(rootTransition);
|
return rootTransition;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getIonPageElement(element: HTMLElement) {
|
function getIonPageElement(element: HTMLElement) {
|
||||||
|
@ -299,33 +299,33 @@
|
|||||||
// iOS Toolbar Back Button
|
// iOS Toolbar Back Button
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
.back-button-ios {
|
// .back-button-ios {
|
||||||
@include margin(0);
|
// @include margin(0);
|
||||||
|
//
|
||||||
z-index: $z-index-toolbar-buttons;
|
// z-index: $z-index-toolbar-buttons;
|
||||||
overflow: visible;
|
// overflow: visible;
|
||||||
|
//
|
||||||
order: map-get($toolbar-order-ios, back-button);
|
// order: map-get($toolbar-order-ios, back-button);
|
||||||
|
//
|
||||||
min-height: 32px;
|
// min-height: 32px;
|
||||||
|
//
|
||||||
line-height: 1;
|
// line-height: 1;
|
||||||
transform: translateZ(0);
|
// transform: translateZ(0);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
.back-button-icon-ios {
|
// .back-button-icon-ios {
|
||||||
@include margin(-1px, 0, 0, 0);
|
// @include margin(-1px, 0, 0, 0);
|
||||||
|
//
|
||||||
display: inherit;
|
// display: inherit;
|
||||||
|
//
|
||||||
min-width: 18px;
|
// min-width: 18px;
|
||||||
|
//
|
||||||
font-size: 34px;
|
// font-size: 34px;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
.back-button-text-ios {
|
// .back-button-text-ios {
|
||||||
letter-spacing: -.01em;
|
// letter-spacing: -.01em;
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
// iOS Toolbar Menu Toggle
|
// iOS Toolbar Menu Toggle
|
||||||
|
@ -130,20 +130,3 @@ ion-buttons,
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Back Button
|
|
||||||
// --------------------------------------------------
|
|
||||||
|
|
||||||
.back-button {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button.show-back-button {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button-text {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
export function isCordova(): boolean {
|
export function isCordova(): boolean {
|
||||||
const win = window as any;
|
const win = window as any;
|
||||||
return !!(win['cordova'] || win['PhoneGap'] || win['phonegap']);
|
return !!(win['cordova'] || win['PhoneGap'] || win['phonegap'] || win['Capacitor']);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user