ios.transition-4047cb68.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. /*!
  2. * (C) Ionic http://ionicframework.com - MIT License
  3. */
  4. import { c as createAnimation } from './animation-8b25e105.js';
  5. import { g as getIonPageElement } from './index-68c0d151.js';
  6. import './index-cfd9c1f2.js';
  7. import './index-a5d50daf.js';
  8. import './index-527b9e34.js';
  9. import './helpers-d94bc8ad.js';
  10. const DURATION = 540;
  11. // TODO(FW-2832): types
  12. const getClonedElement = (tagName) => {
  13. return document.querySelector(`${tagName}.ion-cloned-element`);
  14. };
  15. const shadow = (el) => {
  16. return el.shadowRoot || el;
  17. };
  18. const getLargeTitle = (refEl) => {
  19. const tabs = refEl.tagName === 'ION-TABS' ? refEl : refEl.querySelector('ion-tabs');
  20. const query = 'ion-content ion-header:not(.header-collapse-condense-inactive) ion-title.title-large';
  21. if (tabs != null) {
  22. const activeTab = tabs.querySelector('ion-tab:not(.tab-hidden), .ion-page:not(.ion-page-hidden)');
  23. return activeTab != null ? activeTab.querySelector(query) : null;
  24. }
  25. return refEl.querySelector(query);
  26. };
  27. const getBackButton = (refEl, backDirection) => {
  28. const tabs = refEl.tagName === 'ION-TABS' ? refEl : refEl.querySelector('ion-tabs');
  29. let buttonsList = [];
  30. if (tabs != null) {
  31. const activeTab = tabs.querySelector('ion-tab:not(.tab-hidden), .ion-page:not(.ion-page-hidden)');
  32. if (activeTab != null) {
  33. buttonsList = activeTab.querySelectorAll('ion-buttons');
  34. }
  35. }
  36. else {
  37. buttonsList = refEl.querySelectorAll('ion-buttons');
  38. }
  39. for (const buttons of buttonsList) {
  40. const parentHeader = buttons.closest('ion-header');
  41. const activeHeader = parentHeader && !parentHeader.classList.contains('header-collapse-condense-inactive');
  42. const backButton = buttons.querySelector('ion-back-button');
  43. const buttonsCollapse = buttons.classList.contains('buttons-collapse');
  44. const startSlot = buttons.slot === 'start' || buttons.slot === '';
  45. if (backButton !== null && startSlot && ((buttonsCollapse && activeHeader && backDirection) || !buttonsCollapse)) {
  46. return backButton;
  47. }
  48. }
  49. return null;
  50. };
  51. const createLargeTitleTransition = (rootAnimation, rtl, backDirection, enteringEl, leavingEl) => {
  52. const enteringBackButton = getBackButton(enteringEl, backDirection);
  53. const leavingLargeTitle = getLargeTitle(leavingEl);
  54. const enteringLargeTitle = getLargeTitle(enteringEl);
  55. const leavingBackButton = getBackButton(leavingEl, backDirection);
  56. const shouldAnimationForward = enteringBackButton !== null && leavingLargeTitle !== null && !backDirection;
  57. const shouldAnimationBackward = enteringLargeTitle !== null && leavingBackButton !== null && backDirection;
  58. if (shouldAnimationForward) {
  59. const leavingLargeTitleBox = leavingLargeTitle.getBoundingClientRect();
  60. const enteringBackButtonBox = enteringBackButton.getBoundingClientRect();
  61. const enteringBackButtonTextEl = shadow(enteringBackButton).querySelector('.button-text');
  62. // Text element not rendered if developers pass text="" to the back button
  63. const enteringBackButtonTextBox = enteringBackButtonTextEl === null || enteringBackButtonTextEl === void 0 ? void 0 : enteringBackButtonTextEl.getBoundingClientRect();
  64. const leavingLargeTitleTextEl = shadow(leavingLargeTitle).querySelector('.toolbar-title');
  65. const leavingLargeTitleTextBox = leavingLargeTitleTextEl.getBoundingClientRect();
  66. animateLargeTitle(rootAnimation, rtl, backDirection, leavingLargeTitle, leavingLargeTitleBox, leavingLargeTitleTextBox, enteringBackButtonBox, enteringBackButtonTextEl, enteringBackButtonTextBox);
  67. animateBackButton(rootAnimation, rtl, backDirection, enteringBackButton, enteringBackButtonBox, enteringBackButtonTextEl, enteringBackButtonTextBox, leavingLargeTitle, leavingLargeTitleTextBox);
  68. }
  69. else if (shouldAnimationBackward) {
  70. const enteringLargeTitleBox = enteringLargeTitle.getBoundingClientRect();
  71. const leavingBackButtonBox = leavingBackButton.getBoundingClientRect();
  72. const leavingBackButtonTextEl = shadow(leavingBackButton).querySelector('.button-text');
  73. // Text element not rendered if developers pass text="" to the back button
  74. const leavingBackButtonTextBox = leavingBackButtonTextEl === null || leavingBackButtonTextEl === void 0 ? void 0 : leavingBackButtonTextEl.getBoundingClientRect();
  75. const enteringLargeTitleTextEl = shadow(enteringLargeTitle).querySelector('.toolbar-title');
  76. const enteringLargeTitleTextBox = enteringLargeTitleTextEl.getBoundingClientRect();
  77. animateLargeTitle(rootAnimation, rtl, backDirection, enteringLargeTitle, enteringLargeTitleBox, enteringLargeTitleTextBox, leavingBackButtonBox, leavingBackButtonTextEl, leavingBackButtonTextBox);
  78. animateBackButton(rootAnimation, rtl, backDirection, leavingBackButton, leavingBackButtonBox, leavingBackButtonTextEl, leavingBackButtonTextBox, enteringLargeTitle, enteringLargeTitleTextBox);
  79. }
  80. return {
  81. forward: shouldAnimationForward,
  82. backward: shouldAnimationBackward,
  83. };
  84. };
  85. const animateBackButton = (rootAnimation, rtl, backDirection, backButtonEl, backButtonBox, backButtonTextEl, backButtonTextBox, largeTitleEl, largeTitleTextBox) => {
  86. var _a, _b;
  87. const BACK_BUTTON_START_OFFSET = rtl ? `calc(100% - ${backButtonBox.right + 4}px)` : `${backButtonBox.left - 4}px`;
  88. const TEXT_ORIGIN_X = rtl ? 'right' : 'left';
  89. const ICON_ORIGIN_X = rtl ? 'left' : 'right';
  90. const CONTAINER_ORIGIN_X = rtl ? 'right' : 'left';
  91. let WIDTH_SCALE = 1;
  92. let HEIGHT_SCALE = 1;
  93. let TEXT_START_SCALE = `scale(${HEIGHT_SCALE})`;
  94. const TEXT_END_SCALE = 'scale(1)';
  95. if (backButtonTextEl && backButtonTextBox) {
  96. /**
  97. * When the title and back button texts match then they should overlap during the
  98. * page transition. If the texts do not match up then the back button text scale
  99. * adjusts to not perfectly match the large title text otherwise the proportions
  100. * will be incorrect. When the texts match we scale both the width and height to
  101. * account for font weight differences between the title and back button.
  102. */
  103. const doTitleAndButtonTextsMatch = ((_a = backButtonTextEl.textContent) === null || _a === void 0 ? void 0 : _a.trim()) === ((_b = largeTitleEl.textContent) === null || _b === void 0 ? void 0 : _b.trim());
  104. WIDTH_SCALE = largeTitleTextBox.width / backButtonTextBox.width;
  105. /**
  106. * Subtract an offset to account for slight sizing/padding differences between the
  107. * title and the back button.
  108. */
  109. HEIGHT_SCALE = (largeTitleTextBox.height - LARGE_TITLE_SIZE_OFFSET) / backButtonTextBox.height;
  110. /**
  111. * Even though we set TEXT_START_SCALE to HEIGHT_SCALE above, we potentially need
  112. * to re-compute this here since the HEIGHT_SCALE may have changed.
  113. */
  114. TEXT_START_SCALE = doTitleAndButtonTextsMatch ? `scale(${WIDTH_SCALE}, ${HEIGHT_SCALE})` : `scale(${HEIGHT_SCALE})`;
  115. }
  116. const backButtonIconEl = shadow(backButtonEl).querySelector('ion-icon');
  117. const backButtonIconBox = backButtonIconEl.getBoundingClientRect();
  118. /**
  119. * We need to offset the container by the icon dimensions
  120. * so that the back button text aligns with the large title
  121. * text. Otherwise, the back button icon will align with the
  122. * large title text but the back button text will not.
  123. */
  124. const CONTAINER_START_TRANSLATE_X = rtl
  125. ? `${backButtonIconBox.width / 2 - (backButtonIconBox.right - backButtonBox.right)}px`
  126. : `${backButtonBox.left - backButtonIconBox.width / 2}px`;
  127. const CONTAINER_END_TRANSLATE_X = rtl ? `-${window.innerWidth - backButtonBox.right}px` : `${backButtonBox.left}px`;
  128. /**
  129. * Back button container should be
  130. * aligned to the top of the title container
  131. * so the texts overlap as the back button
  132. * text begins to fade in.
  133. */
  134. const CONTAINER_START_TRANSLATE_Y = `${largeTitleTextBox.top}px`;
  135. /**
  136. * The cloned back button should align exactly with the
  137. * real back button on the entering page otherwise there will
  138. * be a layout shift.
  139. */
  140. const CONTAINER_END_TRANSLATE_Y = `${backButtonBox.top}px`;
  141. /**
  142. * In the forward direction, the cloned back button
  143. * container should translate from over the large title
  144. * to over the back button. In the backward direction,
  145. * it should translate from over the back button to over
  146. * the large title.
  147. */
  148. const FORWARD_CONTAINER_KEYFRAMES = [
  149. { offset: 0, transform: `translate3d(${CONTAINER_START_TRANSLATE_X}, ${CONTAINER_START_TRANSLATE_Y}, 0)` },
  150. { offset: 1, transform: `translate3d(${CONTAINER_END_TRANSLATE_X}, ${CONTAINER_END_TRANSLATE_Y}, 0)` },
  151. ];
  152. const BACKWARD_CONTAINER_KEYFRAMES = [
  153. { offset: 0, transform: `translate3d(${CONTAINER_END_TRANSLATE_X}, ${CONTAINER_END_TRANSLATE_Y}, 0)` },
  154. { offset: 1, transform: `translate3d(${CONTAINER_START_TRANSLATE_X}, ${CONTAINER_START_TRANSLATE_Y}, 0)` },
  155. ];
  156. const CONTAINER_KEYFRAMES = backDirection ? BACKWARD_CONTAINER_KEYFRAMES : FORWARD_CONTAINER_KEYFRAMES;
  157. /**
  158. * In the forward direction, the text in the cloned back button
  159. * should start to be (roughly) the size of the large title
  160. * and then scale down to be the size of the actual back button.
  161. * The text should also translate, but that translate is handled
  162. * by the container keyframes.
  163. */
  164. const FORWARD_TEXT_KEYFRAMES = [
  165. { offset: 0, opacity: 0, transform: TEXT_START_SCALE },
  166. { offset: 1, opacity: 1, transform: TEXT_END_SCALE },
  167. ];
  168. const BACKWARD_TEXT_KEYFRAMES = [
  169. { offset: 0, opacity: 1, transform: TEXT_END_SCALE },
  170. { offset: 1, opacity: 0, transform: TEXT_START_SCALE },
  171. ];
  172. const TEXT_KEYFRAMES = backDirection ? BACKWARD_TEXT_KEYFRAMES : FORWARD_TEXT_KEYFRAMES;
  173. /**
  174. * The icon should scale in/out in the second
  175. * half of the animation. The icon should also
  176. * translate, but that translate is handled by the
  177. * container keyframes.
  178. */
  179. const FORWARD_ICON_KEYFRAMES = [
  180. { offset: 0, opacity: 0, transform: 'scale(0.6)' },
  181. { offset: 0.6, opacity: 0, transform: 'scale(0.6)' },
  182. { offset: 1, opacity: 1, transform: 'scale(1)' },
  183. ];
  184. const BACKWARD_ICON_KEYFRAMES = [
  185. { offset: 0, opacity: 1, transform: 'scale(1)' },
  186. { offset: 0.2, opacity: 0, transform: 'scale(0.6)' },
  187. { offset: 1, opacity: 0, transform: 'scale(0.6)' },
  188. ];
  189. const ICON_KEYFRAMES = backDirection ? BACKWARD_ICON_KEYFRAMES : FORWARD_ICON_KEYFRAMES;
  190. const enteringBackButtonTextAnimation = createAnimation();
  191. const enteringBackButtonIconAnimation = createAnimation();
  192. const enteringBackButtonAnimation = createAnimation();
  193. const clonedBackButtonEl = getClonedElement('ion-back-button');
  194. const clonedBackButtonTextEl = shadow(clonedBackButtonEl).querySelector('.button-text');
  195. const clonedBackButtonIconEl = shadow(clonedBackButtonEl).querySelector('ion-icon');
  196. clonedBackButtonEl.text = backButtonEl.text;
  197. clonedBackButtonEl.mode = backButtonEl.mode;
  198. clonedBackButtonEl.icon = backButtonEl.icon;
  199. clonedBackButtonEl.color = backButtonEl.color;
  200. clonedBackButtonEl.disabled = backButtonEl.disabled;
  201. clonedBackButtonEl.style.setProperty('display', 'block');
  202. clonedBackButtonEl.style.setProperty('position', 'fixed');
  203. enteringBackButtonIconAnimation.addElement(clonedBackButtonIconEl);
  204. enteringBackButtonTextAnimation.addElement(clonedBackButtonTextEl);
  205. enteringBackButtonAnimation.addElement(clonedBackButtonEl);
  206. enteringBackButtonAnimation
  207. .beforeStyles({
  208. position: 'absolute',
  209. top: '0px',
  210. [CONTAINER_ORIGIN_X]: '0px',
  211. })
  212. /**
  213. * The write hooks must be set on this animation as it is guaranteed to run. Other
  214. * animations such as the back button text animation will not run if the back button
  215. * has no visible text.
  216. */
  217. .beforeAddWrite(() => {
  218. backButtonEl.style.setProperty('display', 'none');
  219. clonedBackButtonEl.style.setProperty(TEXT_ORIGIN_X, BACK_BUTTON_START_OFFSET);
  220. })
  221. .afterAddWrite(() => {
  222. backButtonEl.style.setProperty('display', '');
  223. clonedBackButtonEl.style.setProperty('display', 'none');
  224. clonedBackButtonEl.style.removeProperty(TEXT_ORIGIN_X);
  225. })
  226. .keyframes(CONTAINER_KEYFRAMES);
  227. enteringBackButtonTextAnimation
  228. .beforeStyles({
  229. 'transform-origin': `${TEXT_ORIGIN_X} top`,
  230. })
  231. .keyframes(TEXT_KEYFRAMES);
  232. enteringBackButtonIconAnimation
  233. .beforeStyles({
  234. 'transform-origin': `${ICON_ORIGIN_X} center`,
  235. })
  236. .keyframes(ICON_KEYFRAMES);
  237. rootAnimation.addAnimation([
  238. enteringBackButtonTextAnimation,
  239. enteringBackButtonIconAnimation,
  240. enteringBackButtonAnimation,
  241. ]);
  242. };
  243. const animateLargeTitle = (rootAnimation, rtl, backDirection, largeTitleEl, largeTitleBox, largeTitleTextBox, backButtonBox, backButtonTextEl, backButtonTextBox) => {
  244. var _a, _b;
  245. /**
  246. * The horizontal transform origin for the large title
  247. */
  248. const ORIGIN_X = rtl ? 'right' : 'left';
  249. const TITLE_START_OFFSET = rtl ? `calc(100% - ${largeTitleBox.right}px)` : `${largeTitleBox.left}px`;
  250. /**
  251. * The cloned large should align exactly with the
  252. * real large title on the leaving page otherwise there will
  253. * be a layout shift.
  254. */
  255. const START_TRANSLATE_X = '0px';
  256. const START_TRANSLATE_Y = `${largeTitleBox.top}px`;
  257. /**
  258. * How much to offset the large title translation by.
  259. * This accounts for differences in sizing between the large
  260. * title and the back button due to padding and font weight.
  261. */
  262. const LARGE_TITLE_TRANSLATION_OFFSET = 8;
  263. let END_TRANSLATE_X = rtl
  264. ? `-${window.innerWidth - backButtonBox.right - LARGE_TITLE_TRANSLATION_OFFSET}px`
  265. : `${backButtonBox.x + LARGE_TITLE_TRANSLATION_OFFSET}px`;
  266. /**
  267. * How much to scale the large title up/down by.
  268. */
  269. let HEIGHT_SCALE = 0.5;
  270. /**
  271. * The large title always starts full size.
  272. */
  273. const START_SCALE = 'scale(1)';
  274. /**
  275. * By default, we don't worry about having the large title scaled to perfectly
  276. * match the back button because we don't know if the back button's text matches
  277. * the large title's text.
  278. */
  279. let END_SCALE = `scale(${HEIGHT_SCALE})`;
  280. // Text element not rendered if developers pass text="" to the back button
  281. if (backButtonTextEl && backButtonTextBox) {
  282. /**
  283. * The scaled title should (roughly) overlap the back button. This ensures that
  284. * the back button and title overlap during the animation. Note that since both
  285. * elements either fade in or fade out over the course of the animation, neither
  286. * element will be fully visible on top of the other. As a result, the overlap
  287. * does not need to be perfect, so approximate values are acceptable here.
  288. */
  289. END_TRANSLATE_X = rtl
  290. ? `-${window.innerWidth - backButtonTextBox.right - LARGE_TITLE_TRANSLATION_OFFSET}px`
  291. : `${backButtonTextBox.x - LARGE_TITLE_TRANSLATION_OFFSET}px`;
  292. /**
  293. * In the forward direction, the large title should start at its normal size and
  294. * then scale down to be (roughly) the size of the back button on the other view.
  295. * In the backward direction, the large title should start at (roughly) the size
  296. * of the back button and then scale up to its original size.
  297. * Note that since both elements either fade in or fade out over the course of the
  298. * animation, neither element will be fully visible on top of the other. As a result,
  299. * the overlap does not need to be perfect, so approximate values are acceptable here.
  300. */
  301. /**
  302. * When the title and back button texts match then they should overlap during the
  303. * page transition. If the texts do not match up then the large title text scale
  304. * adjusts to not perfectly match the back button text otherwise the proportions
  305. * will be incorrect. When the texts match we scale both the width and height to
  306. * account for font weight differences between the title and back button.
  307. */
  308. const doTitleAndButtonTextsMatch = ((_a = backButtonTextEl.textContent) === null || _a === void 0 ? void 0 : _a.trim()) === ((_b = largeTitleEl.textContent) === null || _b === void 0 ? void 0 : _b.trim());
  309. const WIDTH_SCALE = backButtonTextBox.width / largeTitleTextBox.width;
  310. HEIGHT_SCALE = backButtonTextBox.height / (largeTitleTextBox.height - LARGE_TITLE_SIZE_OFFSET);
  311. /**
  312. * Even though we set TEXT_START_SCALE to HEIGHT_SCALE above, we potentially need
  313. * to re-compute this here since the HEIGHT_SCALE may have changed.
  314. */
  315. END_SCALE = doTitleAndButtonTextsMatch ? `scale(${WIDTH_SCALE}, ${HEIGHT_SCALE})` : `scale(${HEIGHT_SCALE})`;
  316. }
  317. /**
  318. * The midpoints of the back button and the title should align such that the back
  319. * button and title appear to be centered with each other.
  320. */
  321. const backButtonMidPoint = backButtonBox.top + backButtonBox.height / 2;
  322. const titleMidPoint = (largeTitleBox.height * HEIGHT_SCALE) / 2;
  323. const END_TRANSLATE_Y = `${backButtonMidPoint - titleMidPoint}px`;
  324. const BACKWARDS_KEYFRAMES = [
  325. { offset: 0, opacity: 0, transform: `translate3d(${END_TRANSLATE_X}, ${END_TRANSLATE_Y}, 0) ${END_SCALE}` },
  326. { offset: 0.1, opacity: 0 },
  327. { offset: 1, opacity: 1, transform: `translate3d(${START_TRANSLATE_X}, ${START_TRANSLATE_Y}, 0) ${START_SCALE}` },
  328. ];
  329. const FORWARDS_KEYFRAMES = [
  330. {
  331. offset: 0,
  332. opacity: 0.99,
  333. transform: `translate3d(${START_TRANSLATE_X}, ${START_TRANSLATE_Y}, 0) ${START_SCALE}`,
  334. },
  335. { offset: 0.6, opacity: 0 },
  336. { offset: 1, opacity: 0, transform: `translate3d(${END_TRANSLATE_X}, ${END_TRANSLATE_Y}, 0) ${END_SCALE}` },
  337. ];
  338. const KEYFRAMES = backDirection ? BACKWARDS_KEYFRAMES : FORWARDS_KEYFRAMES;
  339. const clonedTitleEl = getClonedElement('ion-title');
  340. const clonedLargeTitleAnimation = createAnimation();
  341. clonedTitleEl.innerText = largeTitleEl.innerText;
  342. clonedTitleEl.size = largeTitleEl.size;
  343. clonedTitleEl.color = largeTitleEl.color;
  344. clonedLargeTitleAnimation.addElement(clonedTitleEl);
  345. clonedLargeTitleAnimation
  346. .beforeStyles({
  347. 'transform-origin': `${ORIGIN_X} top`,
  348. /**
  349. * Since font size changes will cause
  350. * the dimension of the large title to change
  351. * we need to set the cloned title height
  352. * equal to that of the original large title height.
  353. */
  354. height: `${largeTitleBox.height}px`,
  355. display: '',
  356. position: 'relative',
  357. [ORIGIN_X]: TITLE_START_OFFSET,
  358. })
  359. .beforeAddWrite(() => {
  360. largeTitleEl.style.setProperty('opacity', '0');
  361. })
  362. .afterAddWrite(() => {
  363. largeTitleEl.style.setProperty('opacity', '');
  364. clonedTitleEl.style.setProperty('display', 'none');
  365. })
  366. .keyframes(KEYFRAMES);
  367. rootAnimation.addAnimation(clonedLargeTitleAnimation);
  368. };
  369. const iosTransitionAnimation = (navEl, opts) => {
  370. var _a;
  371. try {
  372. const EASING = 'cubic-bezier(0.32,0.72,0,1)';
  373. const OPACITY = 'opacity';
  374. const TRANSFORM = 'transform';
  375. const CENTER = '0%';
  376. const OFF_OPACITY = 0.8;
  377. const isRTL = navEl.ownerDocument.dir === 'rtl';
  378. const OFF_RIGHT = isRTL ? '-99.5%' : '99.5%';
  379. const OFF_LEFT = isRTL ? '33%' : '-33%';
  380. const enteringEl = opts.enteringEl;
  381. const leavingEl = opts.leavingEl;
  382. const backDirection = opts.direction === 'back';
  383. const contentEl = enteringEl.querySelector(':scope > ion-content');
  384. const headerEls = enteringEl.querySelectorAll(':scope > ion-header > *:not(ion-toolbar), :scope > ion-footer > *');
  385. const enteringToolBarEls = enteringEl.querySelectorAll(':scope > ion-header > ion-toolbar');
  386. const rootAnimation = createAnimation();
  387. const enteringContentAnimation = createAnimation();
  388. rootAnimation
  389. .addElement(enteringEl)
  390. .duration(((_a = opts.duration) !== null && _a !== void 0 ? _a : 0) || DURATION)
  391. .easing(opts.easing || EASING)
  392. .fill('both')
  393. .beforeRemoveClass('ion-page-invisible');
  394. // eslint-disable-next-line @typescript-eslint/prefer-optional-chain
  395. if (leavingEl && navEl !== null && navEl !== undefined) {
  396. const navDecorAnimation = createAnimation();
  397. navDecorAnimation.addElement(navEl);
  398. rootAnimation.addAnimation(navDecorAnimation);
  399. }
  400. if (!contentEl && enteringToolBarEls.length === 0 && headerEls.length === 0) {
  401. enteringContentAnimation.addElement(enteringEl.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs')); // REVIEW
  402. }
  403. else {
  404. enteringContentAnimation.addElement(contentEl); // REVIEW
  405. enteringContentAnimation.addElement(headerEls);
  406. }
  407. rootAnimation.addAnimation(enteringContentAnimation);
  408. if (backDirection) {
  409. enteringContentAnimation
  410. .beforeClearStyles([OPACITY])
  411. .fromTo('transform', `translateX(${OFF_LEFT})`, `translateX(${CENTER})`)
  412. .fromTo(OPACITY, OFF_OPACITY, 1);
  413. }
  414. else {
  415. // entering content, forward direction
  416. enteringContentAnimation
  417. .beforeClearStyles([OPACITY])
  418. .fromTo('transform', `translateX(${OFF_RIGHT})`, `translateX(${CENTER})`);
  419. }
  420. if (contentEl) {
  421. const enteringTransitionEffectEl = shadow(contentEl).querySelector('.transition-effect');
  422. if (enteringTransitionEffectEl) {
  423. const enteringTransitionCoverEl = enteringTransitionEffectEl.querySelector('.transition-cover');
  424. const enteringTransitionShadowEl = enteringTransitionEffectEl.querySelector('.transition-shadow');
  425. const enteringTransitionEffect = createAnimation();
  426. const enteringTransitionCover = createAnimation();
  427. const enteringTransitionShadow = createAnimation();
  428. enteringTransitionEffect
  429. .addElement(enteringTransitionEffectEl)
  430. .beforeStyles({ opacity: '1', display: 'block' })
  431. .afterStyles({ opacity: '', display: '' });
  432. enteringTransitionCover
  433. .addElement(enteringTransitionCoverEl) // REVIEW
  434. .beforeClearStyles([OPACITY])
  435. .fromTo(OPACITY, 0, 0.1);
  436. enteringTransitionShadow
  437. .addElement(enteringTransitionShadowEl) // REVIEW
  438. .beforeClearStyles([OPACITY])
  439. .fromTo(OPACITY, 0.03, 0.7);
  440. enteringTransitionEffect.addAnimation([enteringTransitionCover, enteringTransitionShadow]);
  441. enteringContentAnimation.addAnimation([enteringTransitionEffect]);
  442. }
  443. }
  444. const enteringContentHasLargeTitle = enteringEl.querySelector('ion-header.header-collapse-condense');
  445. const { forward, backward } = createLargeTitleTransition(rootAnimation, isRTL, backDirection, enteringEl, leavingEl);
  446. enteringToolBarEls.forEach((enteringToolBarEl) => {
  447. const enteringToolBar = createAnimation();
  448. enteringToolBar.addElement(enteringToolBarEl);
  449. rootAnimation.addAnimation(enteringToolBar);
  450. const enteringTitle = createAnimation();
  451. enteringTitle.addElement(enteringToolBarEl.querySelector('ion-title')); // REVIEW
  452. const enteringToolBarButtons = createAnimation();
  453. const buttons = Array.from(enteringToolBarEl.querySelectorAll('ion-buttons,[menuToggle]'));
  454. const parentHeader = enteringToolBarEl.closest('ion-header');
  455. const inactiveHeader = parentHeader === null || parentHeader === void 0 ? void 0 : parentHeader.classList.contains('header-collapse-condense-inactive');
  456. let buttonsToAnimate;
  457. if (backDirection) {
  458. buttonsToAnimate = buttons.filter((button) => {
  459. const isCollapseButton = button.classList.contains('buttons-collapse');
  460. return (isCollapseButton && !inactiveHeader) || !isCollapseButton;
  461. });
  462. }
  463. else {
  464. buttonsToAnimate = buttons.filter((button) => !button.classList.contains('buttons-collapse'));
  465. }
  466. enteringToolBarButtons.addElement(buttonsToAnimate);
  467. const enteringToolBarItems = createAnimation();
  468. enteringToolBarItems.addElement(enteringToolBarEl.querySelectorAll(':scope > *:not(ion-title):not(ion-buttons):not([menuToggle])'));
  469. const enteringToolBarBg = createAnimation();
  470. enteringToolBarBg.addElement(shadow(enteringToolBarEl).querySelector('.toolbar-background')); // REVIEW
  471. const enteringBackButton = createAnimation();
  472. const backButtonEl = enteringToolBarEl.querySelector('ion-back-button');
  473. if (backButtonEl) {
  474. enteringBackButton.addElement(backButtonEl);
  475. }
  476. enteringToolBar.addAnimation([
  477. enteringTitle,
  478. enteringToolBarButtons,
  479. enteringToolBarItems,
  480. enteringToolBarBg,
  481. enteringBackButton,
  482. ]);
  483. enteringToolBarButtons.fromTo(OPACITY, 0.01, 1);
  484. enteringToolBarItems.fromTo(OPACITY, 0.01, 1);
  485. if (backDirection) {
  486. if (!inactiveHeader) {
  487. enteringTitle
  488. .fromTo('transform', `translateX(${OFF_LEFT})`, `translateX(${CENTER})`)
  489. .fromTo(OPACITY, 0.01, 1);
  490. }
  491. enteringToolBarItems.fromTo('transform', `translateX(${OFF_LEFT})`, `translateX(${CENTER})`);
  492. // back direction, entering page has a back button
  493. enteringBackButton.fromTo(OPACITY, 0.01, 1);
  494. }
  495. else {
  496. // entering toolbar, forward direction
  497. if (!enteringContentHasLargeTitle) {
  498. enteringTitle
  499. .fromTo('transform', `translateX(${OFF_RIGHT})`, `translateX(${CENTER})`)
  500. .fromTo(OPACITY, 0.01, 1);
  501. }
  502. enteringToolBarItems.fromTo('transform', `translateX(${OFF_RIGHT})`, `translateX(${CENTER})`);
  503. enteringToolBarBg.beforeClearStyles([OPACITY, 'transform']);
  504. const translucentHeader = parentHeader === null || parentHeader === void 0 ? void 0 : parentHeader.translucent;
  505. if (!translucentHeader) {
  506. enteringToolBarBg.fromTo(OPACITY, 0.01, 'var(--opacity)');
  507. }
  508. else {
  509. enteringToolBarBg.fromTo('transform', isRTL ? 'translateX(-100%)' : 'translateX(100%)', 'translateX(0px)');
  510. }
  511. // forward direction, entering page has a back button
  512. if (!forward) {
  513. enteringBackButton.fromTo(OPACITY, 0.01, 1);
  514. }
  515. if (backButtonEl && !forward) {
  516. const enteringBackBtnText = createAnimation();
  517. enteringBackBtnText
  518. .addElement(shadow(backButtonEl).querySelector('.button-text')) // REVIEW
  519. .fromTo(`transform`, isRTL ? 'translateX(-100px)' : 'translateX(100px)', 'translateX(0px)');
  520. enteringToolBar.addAnimation(enteringBackBtnText);
  521. }
  522. }
  523. });
  524. // setup leaving view
  525. if (leavingEl) {
  526. const leavingContent = createAnimation();
  527. const leavingContentEl = leavingEl.querySelector(':scope > ion-content');
  528. const leavingToolBarEls = leavingEl.querySelectorAll(':scope > ion-header > ion-toolbar');
  529. const leavingHeaderEls = leavingEl.querySelectorAll(':scope > ion-header > *:not(ion-toolbar), :scope > ion-footer > *');
  530. if (!leavingContentEl && leavingToolBarEls.length === 0 && leavingHeaderEls.length === 0) {
  531. leavingContent.addElement(leavingEl.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs')); // REVIEW
  532. }
  533. else {
  534. leavingContent.addElement(leavingContentEl); // REVIEW
  535. leavingContent.addElement(leavingHeaderEls);
  536. }
  537. rootAnimation.addAnimation(leavingContent);
  538. if (backDirection) {
  539. // leaving content, back direction
  540. leavingContent
  541. .beforeClearStyles([OPACITY])
  542. .fromTo('transform', `translateX(${CENTER})`, isRTL ? 'translateX(-100%)' : 'translateX(100%)');
  543. const leavingPage = getIonPageElement(leavingEl);
  544. rootAnimation.afterAddWrite(() => {
  545. if (rootAnimation.getDirection() === 'normal') {
  546. leavingPage.style.setProperty('display', 'none');
  547. }
  548. });
  549. }
  550. else {
  551. // leaving content, forward direction
  552. leavingContent
  553. .fromTo('transform', `translateX(${CENTER})`, `translateX(${OFF_LEFT})`)
  554. .fromTo(OPACITY, 1, OFF_OPACITY);
  555. }
  556. if (leavingContentEl) {
  557. const leavingTransitionEffectEl = shadow(leavingContentEl).querySelector('.transition-effect');
  558. if (leavingTransitionEffectEl) {
  559. const leavingTransitionCoverEl = leavingTransitionEffectEl.querySelector('.transition-cover');
  560. const leavingTransitionShadowEl = leavingTransitionEffectEl.querySelector('.transition-shadow');
  561. const leavingTransitionEffect = createAnimation();
  562. const leavingTransitionCover = createAnimation();
  563. const leavingTransitionShadow = createAnimation();
  564. leavingTransitionEffect
  565. .addElement(leavingTransitionEffectEl)
  566. .beforeStyles({ opacity: '1', display: 'block' })
  567. .afterStyles({ opacity: '', display: '' });
  568. leavingTransitionCover
  569. .addElement(leavingTransitionCoverEl) // REVIEW
  570. .beforeClearStyles([OPACITY])
  571. .fromTo(OPACITY, 0.1, 0);
  572. leavingTransitionShadow
  573. .addElement(leavingTransitionShadowEl) // REVIEW
  574. .beforeClearStyles([OPACITY])
  575. .fromTo(OPACITY, 0.7, 0.03);
  576. leavingTransitionEffect.addAnimation([leavingTransitionCover, leavingTransitionShadow]);
  577. leavingContent.addAnimation([leavingTransitionEffect]);
  578. }
  579. }
  580. leavingToolBarEls.forEach((leavingToolBarEl) => {
  581. const leavingToolBar = createAnimation();
  582. leavingToolBar.addElement(leavingToolBarEl);
  583. const leavingTitle = createAnimation();
  584. leavingTitle.addElement(leavingToolBarEl.querySelector('ion-title')); // REVIEW
  585. const leavingToolBarButtons = createAnimation();
  586. const buttons = leavingToolBarEl.querySelectorAll('ion-buttons,[menuToggle]');
  587. const parentHeader = leavingToolBarEl.closest('ion-header');
  588. const inactiveHeader = parentHeader === null || parentHeader === void 0 ? void 0 : parentHeader.classList.contains('header-collapse-condense-inactive');
  589. const buttonsToAnimate = Array.from(buttons).filter((button) => {
  590. const isCollapseButton = button.classList.contains('buttons-collapse');
  591. return (isCollapseButton && !inactiveHeader) || !isCollapseButton;
  592. });
  593. leavingToolBarButtons.addElement(buttonsToAnimate);
  594. const leavingToolBarItems = createAnimation();
  595. const leavingToolBarItemEls = leavingToolBarEl.querySelectorAll(':scope > *:not(ion-title):not(ion-buttons):not([menuToggle])');
  596. if (leavingToolBarItemEls.length > 0) {
  597. leavingToolBarItems.addElement(leavingToolBarItemEls);
  598. }
  599. const leavingToolBarBg = createAnimation();
  600. leavingToolBarBg.addElement(shadow(leavingToolBarEl).querySelector('.toolbar-background')); // REVIEW
  601. const leavingBackButton = createAnimation();
  602. const backButtonEl = leavingToolBarEl.querySelector('ion-back-button');
  603. if (backButtonEl) {
  604. leavingBackButton.addElement(backButtonEl);
  605. }
  606. leavingToolBar.addAnimation([
  607. leavingTitle,
  608. leavingToolBarButtons,
  609. leavingToolBarItems,
  610. leavingBackButton,
  611. leavingToolBarBg,
  612. ]);
  613. rootAnimation.addAnimation(leavingToolBar);
  614. // fade out leaving toolbar items
  615. leavingBackButton.fromTo(OPACITY, 0.99, 0);
  616. leavingToolBarButtons.fromTo(OPACITY, 0.99, 0);
  617. leavingToolBarItems.fromTo(OPACITY, 0.99, 0);
  618. if (backDirection) {
  619. if (!inactiveHeader) {
  620. // leaving toolbar, back direction
  621. leavingTitle
  622. .fromTo('transform', `translateX(${CENTER})`, isRTL ? 'translateX(-100%)' : 'translateX(100%)')
  623. .fromTo(OPACITY, 0.99, 0);
  624. }
  625. leavingToolBarItems.fromTo('transform', `translateX(${CENTER})`, isRTL ? 'translateX(-100%)' : 'translateX(100%)');
  626. leavingToolBarBg.beforeClearStyles([OPACITY, 'transform']);
  627. // leaving toolbar, back direction, and there's no entering toolbar
  628. // should just slide out, no fading out
  629. const translucentHeader = parentHeader === null || parentHeader === void 0 ? void 0 : parentHeader.translucent;
  630. if (!translucentHeader) {
  631. leavingToolBarBg.fromTo(OPACITY, 'var(--opacity)', 0);
  632. }
  633. else {
  634. leavingToolBarBg.fromTo('transform', 'translateX(0px)', isRTL ? 'translateX(-100%)' : 'translateX(100%)');
  635. }
  636. if (backButtonEl && !backward) {
  637. const leavingBackBtnText = createAnimation();
  638. leavingBackBtnText
  639. .addElement(shadow(backButtonEl).querySelector('.button-text')) // REVIEW
  640. .fromTo('transform', `translateX(${CENTER})`, `translateX(${(isRTL ? -124 : 124) + 'px'})`);
  641. leavingToolBar.addAnimation(leavingBackBtnText);
  642. }
  643. }
  644. else {
  645. // leaving toolbar, forward direction
  646. if (!inactiveHeader) {
  647. leavingTitle
  648. .fromTo('transform', `translateX(${CENTER})`, `translateX(${OFF_LEFT})`)
  649. .fromTo(OPACITY, 0.99, 0)
  650. .afterClearStyles([TRANSFORM, OPACITY]);
  651. }
  652. leavingToolBarItems
  653. .fromTo('transform', `translateX(${CENTER})`, `translateX(${OFF_LEFT})`)
  654. .afterClearStyles([TRANSFORM, OPACITY]);
  655. leavingBackButton.afterClearStyles([OPACITY]);
  656. leavingTitle.afterClearStyles([OPACITY]);
  657. leavingToolBarButtons.afterClearStyles([OPACITY]);
  658. }
  659. });
  660. }
  661. return rootAnimation;
  662. }
  663. catch (err) {
  664. throw err;
  665. }
  666. };
  667. /**
  668. * The scale of the back button during the animation
  669. * is computed based on the scale of the large title
  670. * and vice versa. However, we need to account for slight
  671. * variations in the size of the large title due to
  672. * padding and font weight. This value should be used to subtract
  673. * a small amount from the large title height when computing scales
  674. * to get more accurate scale results.
  675. */
  676. const LARGE_TITLE_SIZE_OFFSET = 10;
  677. export { iosTransitionAnimation, shadow };