ios.transition-a131cd4d.js 37 KB

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