@ionic/core
Version:
Base components for Ionic
114 lines (113 loc) • 5.6 kB
JavaScript
/*!
* (C) Ionic http://ionicframework.com - MIT License
*/
import { createAnimation } from "../../../utils/animation/animation";
import { getElementRoot } from "../../../utils/helpers";
import { SwipeToCloseDefaults } from "../gestures/swipe-to-close";
import { createSheetLeaveAnimation } from "./sheet";
const createLeaveAnimation = () => {
const backdropAnimation = createAnimation().fromTo('opacity', 'var(--backdrop-opacity)', 0);
const wrapperAnimation = createAnimation().fromTo('transform', 'translateY(0vh)', 'translateY(100vh)');
return { backdropAnimation, wrapperAnimation };
};
/**
* iOS Modal Leave Animation
*/
export const iosLeaveAnimation = (baseEl, opts, duration = 500) => {
const { presentingEl, currentBreakpoint, expandToScroll } = opts;
const root = getElementRoot(baseEl);
const { wrapperAnimation, backdropAnimation } = currentBreakpoint !== undefined ? createSheetLeaveAnimation(opts) : createLeaveAnimation();
backdropAnimation.addElement(root.querySelector('ion-backdrop'));
wrapperAnimation.addElement(root.querySelectorAll('.modal-wrapper, .modal-shadow')).beforeStyles({ opacity: 1 });
const baseAnimation = createAnimation('leaving-base')
.addElement(baseEl)
.easing('cubic-bezier(0.32,0.72,0,1)')
.duration(duration)
.addAnimation(wrapperAnimation)
.beforeAddWrite(() => {
if (expandToScroll) {
// Scroll can only be done when the modal is fully expanded.
return;
}
/**
* If expandToScroll is disabled, we need to swap
* the visibility to the original, so the footer
* dismisses with the modal and doesn't stay
* until the modal is removed from the DOM.
*/
const ionFooter = baseEl.querySelector('ion-footer');
if (ionFooter) {
const clonedFooter = baseEl.shadowRoot.querySelector('ion-footer');
ionFooter.style.removeProperty('display');
ionFooter.removeAttribute('aria-hidden');
clonedFooter.style.setProperty('display', 'none');
clonedFooter.setAttribute('aria-hidden', 'true');
const page = baseEl.querySelector('.ion-page');
page.style.removeProperty('padding-bottom');
}
});
if (presentingEl) {
const isMobile = window.innerWidth < 768;
const hasCardModal = presentingEl.tagName === 'ION-MODAL' && presentingEl.presentingElement !== undefined;
const presentingElRoot = getElementRoot(presentingEl);
const presentingAnimation = createAnimation()
.beforeClearStyles(['transform'])
.afterClearStyles(['transform'])
.onFinish((currentStep) => {
// only reset background color if this is the last card-style modal
if (currentStep !== 1) {
return;
}
presentingEl.style.setProperty('overflow', '');
const numModals = Array.from(bodyEl.querySelectorAll('ion-modal:not(.overlay-hidden)')).filter((m) => m.presentingElement !== undefined).length;
if (numModals <= 1) {
bodyEl.style.setProperty('background-color', '');
}
});
const bodyEl = document.body;
if (isMobile) {
const transformOffset = !CSS.supports('width', 'max(0px, 1px)') ? '30px' : 'max(30px, var(--ion-safe-area-top))';
const modalTransform = hasCardModal ? '-10px' : transformOffset;
const toPresentingScale = SwipeToCloseDefaults.MIN_PRESENTING_SCALE;
const finalTransform = `translateY(${modalTransform}) scale(${toPresentingScale})`;
presentingAnimation.addElement(presentingEl).keyframes([
{ offset: 0, filter: 'contrast(0.85)', transform: finalTransform, borderRadius: '10px 10px 0 0' },
{ offset: 1, filter: 'contrast(1)', transform: 'translateY(0px) scale(1)', borderRadius: '0px' },
]);
baseAnimation.addAnimation(presentingAnimation);
}
else {
baseAnimation.addAnimation(backdropAnimation);
if (!hasCardModal) {
wrapperAnimation.fromTo('opacity', '1', '0');
}
else {
const toPresentingScale = hasCardModal ? SwipeToCloseDefaults.MIN_PRESENTING_SCALE : 1;
const finalTransform = `translateY(-10px) scale(${toPresentingScale})`;
presentingAnimation
.addElement(presentingElRoot.querySelector('.modal-wrapper'))
.afterStyles({
transform: 'translate3d(0, 0, 0)',
})
.keyframes([
{ offset: 0, filter: 'contrast(0.85)', transform: finalTransform },
{ offset: 1, filter: 'contrast(1)', transform: 'translateY(0) scale(1)' },
]);
const shadowAnimation = createAnimation()
.addElement(presentingElRoot.querySelector('.modal-shadow'))
.afterStyles({
transform: 'translateY(0) scale(1)',
})
.keyframes([
{ offset: 0, opacity: '0', transform: finalTransform },
{ offset: 1, opacity: '1', transform: 'translateY(0) scale(1)' },
]);
baseAnimation.addAnimation([presentingAnimation, shadowAnimation]);
}
}
}
else {
baseAnimation.addAnimation(backdropAnimation);
}
return baseAnimation;
};