UNPKG

@ionic/core

Version:
676 lines (673 loc) • 37 kB
/*! * (C) Ionic http://ionicframework.com - MIT License */ import { c as createAnimation } from './animation.js'; import { g as getIonPageElement } from './index2.js'; const DURATION = 540; // TODO(FW-2832): types const getClonedElement = (tagName) => { return document.querySelector(`${tagName}.ion-cloned-element`); }; const shadow = (el) => { return el.shadowRoot || el; }; const getLargeTitle = (refEl) => { const tabs = refEl.tagName === 'ION-TABS' ? refEl : refEl.querySelector('ion-tabs'); const query = 'ion-content ion-header:not(.header-collapse-condense-inactive) ion-title.title-large'; if (tabs != null) { const activeTab = tabs.querySelector('ion-tab:not(.tab-hidden), .ion-page:not(.ion-page-hidden)'); return activeTab != null ? activeTab.querySelector(query) : null; } return refEl.querySelector(query); }; const getBackButton = (refEl, backDirection) => { const tabs = refEl.tagName === 'ION-TABS' ? refEl : refEl.querySelector('ion-tabs'); let buttonsList = []; if (tabs != null) { const activeTab = tabs.querySelector('ion-tab:not(.tab-hidden), .ion-page:not(.ion-page-hidden)'); if (activeTab != null) { buttonsList = activeTab.querySelectorAll('ion-buttons'); } } else { buttonsList = refEl.querySelectorAll('ion-buttons'); } for (const buttons of buttonsList) { const parentHeader = buttons.closest('ion-header'); const activeHeader = parentHeader && !parentHeader.classList.contains('header-collapse-condense-inactive'); const backButton = buttons.querySelector('ion-back-button'); const buttonsCollapse = buttons.classList.contains('buttons-collapse'); const startSlot = buttons.slot === 'start' || buttons.slot === ''; if (backButton !== null && startSlot && ((buttonsCollapse && activeHeader && backDirection) || !buttonsCollapse)) { return backButton; } } return null; }; const createLargeTitleTransition = (rootAnimation, rtl, backDirection, enteringEl, leavingEl) => { const enteringBackButton = getBackButton(enteringEl, backDirection); const leavingLargeTitle = getLargeTitle(leavingEl); const enteringLargeTitle = getLargeTitle(enteringEl); const leavingBackButton = getBackButton(leavingEl, backDirection); const shouldAnimationForward = enteringBackButton !== null && leavingLargeTitle !== null && !backDirection; const shouldAnimationBackward = enteringLargeTitle !== null && leavingBackButton !== null && backDirection; if (shouldAnimationForward) { const leavingLargeTitleBox = leavingLargeTitle.getBoundingClientRect(); const enteringBackButtonBox = enteringBackButton.getBoundingClientRect(); const enteringBackButtonTextEl = shadow(enteringBackButton).querySelector('.button-text'); // Text element not rendered if developers pass text="" to the back button const enteringBackButtonTextBox = enteringBackButtonTextEl === null || enteringBackButtonTextEl === void 0 ? void 0 : enteringBackButtonTextEl.getBoundingClientRect(); const leavingLargeTitleTextEl = shadow(leavingLargeTitle).querySelector('.toolbar-title'); const leavingLargeTitleTextBox = leavingLargeTitleTextEl.getBoundingClientRect(); animateLargeTitle(rootAnimation, rtl, backDirection, leavingLargeTitle, leavingLargeTitleBox, leavingLargeTitleTextBox, enteringBackButtonBox, enteringBackButtonTextEl, enteringBackButtonTextBox); animateBackButton(rootAnimation, rtl, backDirection, enteringBackButton, enteringBackButtonBox, enteringBackButtonTextEl, enteringBackButtonTextBox, leavingLargeTitle, leavingLargeTitleTextBox); } else if (shouldAnimationBackward) { const enteringLargeTitleBox = enteringLargeTitle.getBoundingClientRect(); const leavingBackButtonBox = leavingBackButton.getBoundingClientRect(); const leavingBackButtonTextEl = shadow(leavingBackButton).querySelector('.button-text'); // Text element not rendered if developers pass text="" to the back button const leavingBackButtonTextBox = leavingBackButtonTextEl === null || leavingBackButtonTextEl === void 0 ? void 0 : leavingBackButtonTextEl.getBoundingClientRect(); const enteringLargeTitleTextEl = shadow(enteringLargeTitle).querySelector('.toolbar-title'); const enteringLargeTitleTextBox = enteringLargeTitleTextEl.getBoundingClientRect(); animateLargeTitle(rootAnimation, rtl, backDirection, enteringLargeTitle, enteringLargeTitleBox, enteringLargeTitleTextBox, leavingBackButtonBox, leavingBackButtonTextEl, leavingBackButtonTextBox); animateBackButton(rootAnimation, rtl, backDirection, leavingBackButton, leavingBackButtonBox, leavingBackButtonTextEl, leavingBackButtonTextBox, enteringLargeTitle, enteringLargeTitleTextBox); } return { forward: shouldAnimationForward, backward: shouldAnimationBackward, }; }; const animateBackButton = (rootAnimation, rtl, backDirection, backButtonEl, backButtonBox, backButtonTextEl, backButtonTextBox, largeTitleEl, largeTitleTextBox) => { var _a, _b; const BACK_BUTTON_START_OFFSET = rtl ? `calc(100% - ${backButtonBox.right + 4}px)` : `${backButtonBox.left - 4}px`; const TEXT_ORIGIN_X = rtl ? 'right' : 'left'; const ICON_ORIGIN_X = rtl ? 'left' : 'right'; const CONTAINER_ORIGIN_X = rtl ? 'right' : 'left'; let WIDTH_SCALE = 1; let HEIGHT_SCALE = 1; let TEXT_START_SCALE = `scale(${HEIGHT_SCALE})`; const TEXT_END_SCALE = 'scale(1)'; if (backButtonTextEl && backButtonTextBox) { /** * When the title and back button texts match then they should overlap during the * page transition. If the texts do not match up then the back button text scale * adjusts to not perfectly match the large title text otherwise the proportions * will be incorrect. When the texts match we scale both the width and height to * account for font weight differences between the title and back button. */ const doTitleAndButtonTextsMatch = ((_a = backButtonTextEl.textContent) === null || _a === void 0 ? void 0 : _a.trim()) === ((_b = largeTitleEl.textContent) === null || _b === void 0 ? void 0 : _b.trim()); WIDTH_SCALE = largeTitleTextBox.width / backButtonTextBox.width; /** * Subtract an offset to account for slight sizing/padding differences between the * title and the back button. */ HEIGHT_SCALE = (largeTitleTextBox.height - LARGE_TITLE_SIZE_OFFSET) / backButtonTextBox.height; /** * Even though we set TEXT_START_SCALE to HEIGHT_SCALE above, we potentially need * to re-compute this here since the HEIGHT_SCALE may have changed. */ TEXT_START_SCALE = doTitleAndButtonTextsMatch ? `scale(${WIDTH_SCALE}, ${HEIGHT_SCALE})` : `scale(${HEIGHT_SCALE})`; } const backButtonIconEl = shadow(backButtonEl).querySelector('ion-icon'); const backButtonIconBox = backButtonIconEl.getBoundingClientRect(); /** * We need to offset the container by the icon dimensions * so that the back button text aligns with the large title * text. Otherwise, the back button icon will align with the * large title text but the back button text will not. */ const CONTAINER_START_TRANSLATE_X = rtl ? `${backButtonIconBox.width / 2 - (backButtonIconBox.right - backButtonBox.right)}px` : `${backButtonBox.left - backButtonIconBox.width / 2}px`; const CONTAINER_END_TRANSLATE_X = rtl ? `-${window.innerWidth - backButtonBox.right}px` : `${backButtonBox.left}px`; /** * Back button container should be * aligned to the top of the title container * so the texts overlap as the back button * text begins to fade in. */ const CONTAINER_START_TRANSLATE_Y = `${largeTitleTextBox.top}px`; /** * The cloned back button should align exactly with the * real back button on the entering page otherwise there will * be a layout shift. */ const CONTAINER_END_TRANSLATE_Y = `${backButtonBox.top}px`; /** * In the forward direction, the cloned back button * container should translate from over the large title * to over the back button. In the backward direction, * it should translate from over the back button to over * the large title. */ const FORWARD_CONTAINER_KEYFRAMES = [ { offset: 0, transform: `translate3d(${CONTAINER_START_TRANSLATE_X}, ${CONTAINER_START_TRANSLATE_Y}, 0)` }, { offset: 1, transform: `translate3d(${CONTAINER_END_TRANSLATE_X}, ${CONTAINER_END_TRANSLATE_Y}, 0)` }, ]; const BACKWARD_CONTAINER_KEYFRAMES = [ { offset: 0, transform: `translate3d(${CONTAINER_END_TRANSLATE_X}, ${CONTAINER_END_TRANSLATE_Y}, 0)` }, { offset: 1, transform: `translate3d(${CONTAINER_START_TRANSLATE_X}, ${CONTAINER_START_TRANSLATE_Y}, 0)` }, ]; const CONTAINER_KEYFRAMES = backDirection ? BACKWARD_CONTAINER_KEYFRAMES : FORWARD_CONTAINER_KEYFRAMES; /** * In the forward direction, the text in the cloned back button * should start to be (roughly) the size of the large title * and then scale down to be the size of the actual back button. * The text should also translate, but that translate is handled * by the container keyframes. */ const FORWARD_TEXT_KEYFRAMES = [ { offset: 0, opacity: 0, transform: TEXT_START_SCALE }, { offset: 1, opacity: 1, transform: TEXT_END_SCALE }, ]; const BACKWARD_TEXT_KEYFRAMES = [ { offset: 0, opacity: 1, transform: TEXT_END_SCALE }, { offset: 1, opacity: 0, transform: TEXT_START_SCALE }, ]; const TEXT_KEYFRAMES = backDirection ? BACKWARD_TEXT_KEYFRAMES : FORWARD_TEXT_KEYFRAMES; /** * The icon should scale in/out in the second * half of the animation. The icon should also * translate, but that translate is handled by the * container keyframes. */ const FORWARD_ICON_KEYFRAMES = [ { offset: 0, opacity: 0, transform: 'scale(0.6)' }, { offset: 0.6, opacity: 0, transform: 'scale(0.6)' }, { offset: 1, opacity: 1, transform: 'scale(1)' }, ]; const BACKWARD_ICON_KEYFRAMES = [ { offset: 0, opacity: 1, transform: 'scale(1)' }, { offset: 0.2, opacity: 0, transform: 'scale(0.6)' }, { offset: 1, opacity: 0, transform: 'scale(0.6)' }, ]; const ICON_KEYFRAMES = backDirection ? BACKWARD_ICON_KEYFRAMES : FORWARD_ICON_KEYFRAMES; const enteringBackButtonTextAnimation = createAnimation(); const enteringBackButtonIconAnimation = createAnimation(); const enteringBackButtonAnimation = createAnimation(); const clonedBackButtonEl = getClonedElement('ion-back-button'); const clonedBackButtonTextEl = shadow(clonedBackButtonEl).querySelector('.button-text'); const clonedBackButtonIconEl = shadow(clonedBackButtonEl).querySelector('ion-icon'); clonedBackButtonEl.text = backButtonEl.text; clonedBackButtonEl.mode = backButtonEl.mode; clonedBackButtonEl.icon = backButtonEl.icon; clonedBackButtonEl.color = backButtonEl.color; clonedBackButtonEl.disabled = backButtonEl.disabled; clonedBackButtonEl.style.setProperty('display', 'block'); clonedBackButtonEl.style.setProperty('position', 'fixed'); enteringBackButtonIconAnimation.addElement(clonedBackButtonIconEl); enteringBackButtonTextAnimation.addElement(clonedBackButtonTextEl); enteringBackButtonAnimation.addElement(clonedBackButtonEl); enteringBackButtonAnimation .beforeStyles({ position: 'absolute', top: '0px', [CONTAINER_ORIGIN_X]: '0px', }) /** * The write hooks must be set on this animation as it is guaranteed to run. Other * animations such as the back button text animation will not run if the back button * has no visible text. */ .beforeAddWrite(() => { backButtonEl.style.setProperty('display', 'none'); clonedBackButtonEl.style.setProperty(TEXT_ORIGIN_X, BACK_BUTTON_START_OFFSET); }) .afterAddWrite(() => { backButtonEl.style.setProperty('display', ''); clonedBackButtonEl.style.setProperty('display', 'none'); clonedBackButtonEl.style.removeProperty(TEXT_ORIGIN_X); }) .keyframes(CONTAINER_KEYFRAMES); enteringBackButtonTextAnimation .beforeStyles({ 'transform-origin': `${TEXT_ORIGIN_X} top`, }) .keyframes(TEXT_KEYFRAMES); enteringBackButtonIconAnimation .beforeStyles({ 'transform-origin': `${ICON_ORIGIN_X} center`, }) .keyframes(ICON_KEYFRAMES); rootAnimation.addAnimation([ enteringBackButtonTextAnimation, enteringBackButtonIconAnimation, enteringBackButtonAnimation, ]); }; const animateLargeTitle = (rootAnimation, rtl, backDirection, largeTitleEl, largeTitleBox, largeTitleTextBox, backButtonBox, backButtonTextEl, backButtonTextBox) => { var _a, _b; /** * The horizontal transform origin for the large title */ const ORIGIN_X = rtl ? 'right' : 'left'; const TITLE_START_OFFSET = rtl ? `calc(100% - ${largeTitleBox.right}px)` : `${largeTitleBox.left}px`; /** * The cloned large should align exactly with the * real large title on the leaving page otherwise there will * be a layout shift. */ const START_TRANSLATE_X = '0px'; const START_TRANSLATE_Y = `${largeTitleBox.top}px`; /** * How much to offset the large title translation by. * This accounts for differences in sizing between the large * title and the back button due to padding and font weight. */ const LARGE_TITLE_TRANSLATION_OFFSET = 8; let END_TRANSLATE_X = rtl ? `-${window.innerWidth - backButtonBox.right - LARGE_TITLE_TRANSLATION_OFFSET}px` : `${backButtonBox.x + LARGE_TITLE_TRANSLATION_OFFSET}px`; /** * How much to scale the large title up/down by. */ let HEIGHT_SCALE = 0.5; /** * The large title always starts full size. */ const START_SCALE = 'scale(1)'; /** * By default, we don't worry about having the large title scaled to perfectly * match the back button because we don't know if the back button's text matches * the large title's text. */ let END_SCALE = `scale(${HEIGHT_SCALE})`; // Text element not rendered if developers pass text="" to the back button if (backButtonTextEl && backButtonTextBox) { /** * The scaled title should (roughly) overlap the back button. This ensures that * the back button and title overlap during the animation. Note that since both * elements either fade in or fade out over the course of the animation, neither * element will be fully visible on top of the other. As a result, the overlap * does not need to be perfect, so approximate values are acceptable here. */ END_TRANSLATE_X = rtl ? `-${window.innerWidth - backButtonTextBox.right - LARGE_TITLE_TRANSLATION_OFFSET}px` : `${backButtonTextBox.x - LARGE_TITLE_TRANSLATION_OFFSET}px`; /** * In the forward direction, the large title should start at its normal size and * then scale down to be (roughly) the size of the back button on the other view. * In the backward direction, the large title should start at (roughly) the size * of the back button and then scale up to its original size. * Note that since both elements either fade in or fade out over the course of the * animation, neither element will be fully visible on top of the other. As a result, * the overlap does not need to be perfect, so approximate values are acceptable here. */ /** * When the title and back button texts match then they should overlap during the * page transition. If the texts do not match up then the large title text scale * adjusts to not perfectly match the back button text otherwise the proportions * will be incorrect. When the texts match we scale both the width and height to * account for font weight differences between the title and back button. */ const doTitleAndButtonTextsMatch = ((_a = backButtonTextEl.textContent) === null || _a === void 0 ? void 0 : _a.trim()) === ((_b = largeTitleEl.textContent) === null || _b === void 0 ? void 0 : _b.trim()); const WIDTH_SCALE = backButtonTextBox.width / largeTitleTextBox.width; HEIGHT_SCALE = backButtonTextBox.height / (largeTitleTextBox.height - LARGE_TITLE_SIZE_OFFSET); /** * Even though we set TEXT_START_SCALE to HEIGHT_SCALE above, we potentially need * to re-compute this here since the HEIGHT_SCALE may have changed. */ END_SCALE = doTitleAndButtonTextsMatch ? `scale(${WIDTH_SCALE}, ${HEIGHT_SCALE})` : `scale(${HEIGHT_SCALE})`; } /** * The midpoints of the back button and the title should align such that the back * button and title appear to be centered with each other. */ const backButtonMidPoint = backButtonBox.top + backButtonBox.height / 2; const titleMidPoint = (largeTitleBox.height * HEIGHT_SCALE) / 2; const END_TRANSLATE_Y = `${backButtonMidPoint - titleMidPoint}px`; const BACKWARDS_KEYFRAMES = [ { offset: 0, opacity: 0, transform: `translate3d(${END_TRANSLATE_X}, ${END_TRANSLATE_Y}, 0) ${END_SCALE}` }, { offset: 0.1, opacity: 0 }, { offset: 1, opacity: 1, transform: `translate3d(${START_TRANSLATE_X}, ${START_TRANSLATE_Y}, 0) ${START_SCALE}` }, ]; const FORWARDS_KEYFRAMES = [ { offset: 0, opacity: 0.99, transform: `translate3d(${START_TRANSLATE_X}, ${START_TRANSLATE_Y}, 0) ${START_SCALE}`, }, { offset: 0.6, opacity: 0 }, { offset: 1, opacity: 0, transform: `translate3d(${END_TRANSLATE_X}, ${END_TRANSLATE_Y}, 0) ${END_SCALE}` }, ]; const KEYFRAMES = backDirection ? BACKWARDS_KEYFRAMES : FORWARDS_KEYFRAMES; const clonedTitleEl = getClonedElement('ion-title'); const clonedLargeTitleAnimation = createAnimation(); clonedTitleEl.innerText = largeTitleEl.innerText; clonedTitleEl.size = largeTitleEl.size; clonedTitleEl.color = largeTitleEl.color; clonedLargeTitleAnimation.addElement(clonedTitleEl); clonedLargeTitleAnimation .beforeStyles({ 'transform-origin': `${ORIGIN_X} top`, /** * Since font size changes will cause * the dimension of the large title to change * we need to set the cloned title height * equal to that of the original large title height. */ height: `${largeTitleBox.height}px`, display: '', position: 'relative', [ORIGIN_X]: TITLE_START_OFFSET, }) .beforeAddWrite(() => { largeTitleEl.style.setProperty('opacity', '0'); }) .afterAddWrite(() => { largeTitleEl.style.setProperty('opacity', ''); clonedTitleEl.style.setProperty('display', 'none'); }) .keyframes(KEYFRAMES); rootAnimation.addAnimation(clonedLargeTitleAnimation); }; const iosTransitionAnimation = (navEl, opts) => { var _a; try { 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.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 backDirection = opts.direction === 'back'; 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 rootAnimation = createAnimation(); const enteringContentAnimation = createAnimation(); rootAnimation .addElement(enteringEl) .duration(((_a = opts.duration) !== null && _a !== void 0 ? _a : 0) || DURATION) .easing(opts.easing || EASING) .fill('both') .beforeRemoveClass('ion-page-invisible'); // eslint-disable-next-line @typescript-eslint/prefer-optional-chain if (leavingEl && navEl !== null && navEl !== undefined) { const navDecorAnimation = createAnimation(); navDecorAnimation.addElement(navEl); rootAnimation.addAnimation(navDecorAnimation); } if (!contentEl && enteringToolBarEls.length === 0 && headerEls.length === 0) { enteringContentAnimation.addElement(enteringEl.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs')); // REVIEW } else { enteringContentAnimation.addElement(contentEl); // REVIEW enteringContentAnimation.addElement(headerEls); } rootAnimation.addAnimation(enteringContentAnimation); if (backDirection) { enteringContentAnimation .beforeClearStyles([OPACITY]) .fromTo('transform', `translateX(${OFF_LEFT})`, `translateX(${CENTER})`) .fromTo(OPACITY, OFF_OPACITY, 1); } else { // entering content, forward direction enteringContentAnimation .beforeClearStyles([OPACITY]) .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(); const enteringTransitionCover = createAnimation(); const enteringTransitionShadow = createAnimation(); enteringTransitionEffect .addElement(enteringTransitionEffectEl) .beforeStyles({ opacity: '1', display: 'block' }) .afterStyles({ opacity: '', display: '' }); enteringTransitionCover .addElement(enteringTransitionCoverEl) // REVIEW .beforeClearStyles([OPACITY]) .fromTo(OPACITY, 0, 0.1); enteringTransitionShadow .addElement(enteringTransitionShadowEl) // REVIEW .beforeClearStyles([OPACITY]) .fromTo(OPACITY, 0.03, 0.7); enteringTransitionEffect.addAnimation([enteringTransitionCover, enteringTransitionShadow]); enteringContentAnimation.addAnimation([enteringTransitionEffect]); } } const enteringContentHasLargeTitle = enteringEl.querySelector('ion-header.header-collapse-condense'); const { forward, backward } = createLargeTitleTransition(rootAnimation, isRTL, backDirection, enteringEl, leavingEl); enteringToolBarEls.forEach((enteringToolBarEl) => { const enteringToolBar = createAnimation(); enteringToolBar.addElement(enteringToolBarEl); rootAnimation.addAnimation(enteringToolBar); const enteringTitle = createAnimation(); enteringTitle.addElement(enteringToolBarEl.querySelector('ion-title')); // REVIEW const enteringToolBarButtons = createAnimation(); const buttons = Array.from(enteringToolBarEl.querySelectorAll('ion-buttons,[menuToggle]')); const parentHeader = enteringToolBarEl.closest('ion-header'); const inactiveHeader = parentHeader === null || parentHeader === void 0 ? void 0 : parentHeader.classList.contains('header-collapse-condense-inactive'); let buttonsToAnimate; if (backDirection) { buttonsToAnimate = buttons.filter((button) => { const isCollapseButton = button.classList.contains('buttons-collapse'); return (isCollapseButton && !inactiveHeader) || !isCollapseButton; }); } else { buttonsToAnimate = buttons.filter((button) => !button.classList.contains('buttons-collapse')); } enteringToolBarButtons.addElement(buttonsToAnimate); const enteringToolBarItems = createAnimation(); enteringToolBarItems.addElement(enteringToolBarEl.querySelectorAll(':scope > *:not(ion-title):not(ion-buttons):not([menuToggle])')); const enteringToolBarBg = createAnimation(); enteringToolBarBg.addElement(shadow(enteringToolBarEl).querySelector('.toolbar-background')); // REVIEW const enteringBackButton = createAnimation(); const backButtonEl = enteringToolBarEl.querySelector('ion-back-button'); if (backButtonEl) { enteringBackButton.addElement(backButtonEl); } enteringToolBar.addAnimation([ enteringTitle, enteringToolBarButtons, enteringToolBarItems, enteringToolBarBg, enteringBackButton, ]); enteringToolBarButtons.fromTo(OPACITY, 0.01, 1); enteringToolBarItems.fromTo(OPACITY, 0.01, 1); if (backDirection) { if (!inactiveHeader) { enteringTitle .fromTo('transform', `translateX(${OFF_LEFT})`, `translateX(${CENTER})`) .fromTo(OPACITY, 0.01, 1); } enteringToolBarItems.fromTo('transform', `translateX(${OFF_LEFT})`, `translateX(${CENTER})`); // back direction, entering page has a back button enteringBackButton.fromTo(OPACITY, 0.01, 1); } else { // entering toolbar, forward direction if (!enteringContentHasLargeTitle) { enteringTitle .fromTo('transform', `translateX(${OFF_RIGHT})`, `translateX(${CENTER})`) .fromTo(OPACITY, 0.01, 1); } enteringToolBarItems.fromTo('transform', `translateX(${OFF_RIGHT})`, `translateX(${CENTER})`); enteringToolBarBg.beforeClearStyles([OPACITY, 'transform']); const translucentHeader = parentHeader === null || parentHeader === void 0 ? void 0 : parentHeader.translucent; if (!translucentHeader) { enteringToolBarBg.fromTo(OPACITY, 0.01, 'var(--opacity)'); } else { enteringToolBarBg.fromTo('transform', isRTL ? 'translateX(-100%)' : 'translateX(100%)', 'translateX(0px)'); } // forward direction, entering page has a back button if (!forward) { enteringBackButton.fromTo(OPACITY, 0.01, 1); } if (backButtonEl && !forward) { const enteringBackBtnText = createAnimation(); enteringBackBtnText .addElement(shadow(backButtonEl).querySelector('.button-text')) // REVIEW .fromTo(`transform`, isRTL ? 'translateX(-100px)' : 'translateX(100px)', 'translateX(0px)'); enteringToolBar.addAnimation(enteringBackBtnText); } } }); // setup leaving view if (leavingEl) { const leavingContent = createAnimation(); const leavingContentEl = leavingEl.querySelector(':scope > ion-content'); const leavingToolBarEls = leavingEl.querySelectorAll(':scope > ion-header > ion-toolbar'); const leavingHeaderEls = leavingEl.querySelectorAll(':scope > ion-header > *:not(ion-toolbar), :scope > ion-footer > *'); if (!leavingContentEl && leavingToolBarEls.length === 0 && leavingHeaderEls.length === 0) { leavingContent.addElement(leavingEl.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs')); // REVIEW } else { leavingContent.addElement(leavingContentEl); // REVIEW leavingContent.addElement(leavingHeaderEls); } rootAnimation.addAnimation(leavingContent); if (backDirection) { // leaving content, back direction leavingContent .beforeClearStyles([OPACITY]) .fromTo('transform', `translateX(${CENTER})`, isRTL ? 'translateX(-100%)' : 'translateX(100%)'); const leavingPage = getIonPageElement(leavingEl); rootAnimation.afterAddWrite(() => { if (rootAnimation.getDirection() === 'normal') { leavingPage.style.setProperty('display', 'none'); } }); } else { // leaving content, forward direction leavingContent .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(); const leavingTransitionCover = createAnimation(); const leavingTransitionShadow = createAnimation(); leavingTransitionEffect .addElement(leavingTransitionEffectEl) .beforeStyles({ opacity: '1', display: 'block' }) .afterStyles({ opacity: '', display: '' }); leavingTransitionCover .addElement(leavingTransitionCoverEl) // REVIEW .beforeClearStyles([OPACITY]) .fromTo(OPACITY, 0.1, 0); leavingTransitionShadow .addElement(leavingTransitionShadowEl) // REVIEW .beforeClearStyles([OPACITY]) .fromTo(OPACITY, 0.7, 0.03); leavingTransitionEffect.addAnimation([leavingTransitionCover, leavingTransitionShadow]); leavingContent.addAnimation([leavingTransitionEffect]); } } leavingToolBarEls.forEach((leavingToolBarEl) => { const leavingToolBar = createAnimation(); leavingToolBar.addElement(leavingToolBarEl); const leavingTitle = createAnimation(); leavingTitle.addElement(leavingToolBarEl.querySelector('ion-title')); // REVIEW const leavingToolBarButtons = createAnimation(); const buttons = leavingToolBarEl.querySelectorAll('ion-buttons,[menuToggle]'); const parentHeader = leavingToolBarEl.closest('ion-header'); const inactiveHeader = parentHeader === null || parentHeader === void 0 ? void 0 : parentHeader.classList.contains('header-collapse-condense-inactive'); const buttonsToAnimate = Array.from(buttons).filter((button) => { const isCollapseButton = button.classList.contains('buttons-collapse'); return (isCollapseButton && !inactiveHeader) || !isCollapseButton; }); leavingToolBarButtons.addElement(buttonsToAnimate); const leavingToolBarItems = createAnimation(); const leavingToolBarItemEls = leavingToolBarEl.querySelectorAll(':scope > *:not(ion-title):not(ion-buttons):not([menuToggle])'); if (leavingToolBarItemEls.length > 0) { leavingToolBarItems.addElement(leavingToolBarItemEls); } const leavingToolBarBg = createAnimation(); leavingToolBarBg.addElement(shadow(leavingToolBarEl).querySelector('.toolbar-background')); // REVIEW const leavingBackButton = createAnimation(); const backButtonEl = leavingToolBarEl.querySelector('ion-back-button'); if (backButtonEl) { leavingBackButton.addElement(backButtonEl); } leavingToolBar.addAnimation([ leavingTitle, leavingToolBarButtons, leavingToolBarItems, leavingBackButton, leavingToolBarBg, ]); rootAnimation.addAnimation(leavingToolBar); // fade out leaving toolbar items leavingBackButton.fromTo(OPACITY, 0.99, 0); leavingToolBarButtons.fromTo(OPACITY, 0.99, 0); leavingToolBarItems.fromTo(OPACITY, 0.99, 0); if (backDirection) { if (!inactiveHeader) { // leaving toolbar, back direction leavingTitle .fromTo('transform', `translateX(${CENTER})`, isRTL ? 'translateX(-100%)' : 'translateX(100%)') .fromTo(OPACITY, 0.99, 0); } leavingToolBarItems.fromTo('transform', `translateX(${CENTER})`, isRTL ? 'translateX(-100%)' : 'translateX(100%)'); leavingToolBarBg.beforeClearStyles([OPACITY, 'transform']); // leaving toolbar, back direction, and there's no entering toolbar // should just slide out, no fading out const translucentHeader = parentHeader === null || parentHeader === void 0 ? void 0 : parentHeader.translucent; if (!translucentHeader) { leavingToolBarBg.fromTo(OPACITY, 'var(--opacity)', 0); } else { leavingToolBarBg.fromTo('transform', 'translateX(0px)', isRTL ? 'translateX(-100%)' : 'translateX(100%)'); } if (backButtonEl && !backward) { const leavingBackBtnText = createAnimation(); leavingBackBtnText .addElement(shadow(backButtonEl).querySelector('.button-text')) // REVIEW .fromTo('transform', `translateX(${CENTER})`, `translateX(${(isRTL ? -124 : 124) + 'px'})`); leavingToolBar.addAnimation(leavingBackBtnText); } } else { // leaving toolbar, forward direction if (!inactiveHeader) { leavingTitle .fromTo('transform', `translateX(${CENTER})`, `translateX(${OFF_LEFT})`) .fromTo(OPACITY, 0.99, 0) .afterClearStyles([TRANSFORM, OPACITY]); } leavingToolBarItems .fromTo('transform', `translateX(${CENTER})`, `translateX(${OFF_LEFT})`) .afterClearStyles([TRANSFORM, OPACITY]); leavingBackButton.afterClearStyles([OPACITY]); leavingTitle.afterClearStyles([OPACITY]); leavingToolBarButtons.afterClearStyles([OPACITY]); } }); } return rootAnimation; } catch (err) { throw err; } }; /** * The scale of the back button during the animation * is computed based on the scale of the large title * and vice versa. However, we need to account for slight * variations in the size of the large title due to * padding and font weight. This value should be used to subtract * a small amount from the large title height when computing scales * to get more accurate scale results. */ const LARGE_TITLE_SIZE_OFFSET = 10; export { iosTransitionAnimation, shadow };