ion-footer.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /*!
  2. * (C) Ionic http://ionicframework.com - MIT License
  3. */
  4. import { readTask, writeTask, proxyCustomElement, HTMLElement, h, Host } from '@stencil/core/internal/client';
  5. import { f as findIonContent, p as printIonContentErrorMsg, g as getScrollElement } from './index8.js';
  6. import { c as createKeyboardController } from './keyboard-controller.js';
  7. import { b as getIonMode } from './ionic-global.js';
  8. import { k as clamp } from './helpers.js';
  9. const handleFooterFade = (scrollEl, baseEl) => {
  10. readTask(() => {
  11. const scrollTop = scrollEl.scrollTop;
  12. const maxScroll = scrollEl.scrollHeight - scrollEl.clientHeight;
  13. /**
  14. * Toolbar background will fade
  15. * out over fadeDuration in pixels.
  16. */
  17. const fadeDuration = 10;
  18. /**
  19. * Begin fading out maxScroll - 30px
  20. * from the bottom of the content.
  21. * Also determine how close we are
  22. * to starting the fade. If we are
  23. * before the starting point, the
  24. * scale value will get clamped to 0.
  25. * If we are after the maxScroll (rubber
  26. * band scrolling), the scale value will
  27. * get clamped to 1.
  28. */
  29. const fadeStart = maxScroll - fadeDuration;
  30. const distanceToStart = scrollTop - fadeStart;
  31. const scale = clamp(0, 1 - distanceToStart / fadeDuration, 1);
  32. writeTask(() => {
  33. baseEl.style.setProperty('--opacity-scale', scale.toString());
  34. });
  35. });
  36. };
  37. const footerIosCss = "ion-footer{display:block;position:relative;-ms-flex-order:1;order:1;width:100%;z-index:10}ion-footer.footer-toolbar-padding ion-toolbar:last-of-type{padding-bottom:var(--ion-safe-area-bottom, 0)}.footer-ios ion-toolbar:first-of-type{--border-width:0.55px 0 0}@supports ((-webkit-backdrop-filter: blur(0)) or (backdrop-filter: blur(0))){.footer-background{left:0;right:0;top:0;bottom:0;position:absolute;-webkit-backdrop-filter:saturate(180%) blur(20px);backdrop-filter:saturate(180%) blur(20px)}.footer-translucent-ios ion-toolbar{--opacity:.8}}.footer-ios.ion-no-border ion-toolbar:first-of-type{--border-width:0}.footer-collapse-fade ion-toolbar{--opacity-scale:inherit}";
  38. const IonFooterIosStyle0 = footerIosCss;
  39. const footerMdCss = "ion-footer{display:block;position:relative;-ms-flex-order:1;order:1;width:100%;z-index:10}ion-footer.footer-toolbar-padding ion-toolbar:last-of-type{padding-bottom:var(--ion-safe-area-bottom, 0)}.footer-md{-webkit-box-shadow:0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12);box-shadow:0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12)}.footer-md.ion-no-border{-webkit-box-shadow:none;box-shadow:none}";
  40. const IonFooterMdStyle0 = footerMdCss;
  41. const Footer = /*@__PURE__*/ proxyCustomElement(class Footer extends HTMLElement {
  42. constructor() {
  43. super();
  44. this.__registerHost();
  45. this.keyboardCtrl = null;
  46. this.checkCollapsibleFooter = () => {
  47. const mode = getIonMode(this);
  48. if (mode !== 'ios') {
  49. return;
  50. }
  51. const { collapse } = this;
  52. const hasFade = collapse === 'fade';
  53. this.destroyCollapsibleFooter();
  54. if (hasFade) {
  55. const pageEl = this.el.closest('ion-app,ion-page,.ion-page,page-inner');
  56. const contentEl = pageEl ? findIonContent(pageEl) : null;
  57. if (!contentEl) {
  58. printIonContentErrorMsg(this.el);
  59. return;
  60. }
  61. this.setupFadeFooter(contentEl);
  62. }
  63. };
  64. this.setupFadeFooter = async (contentEl) => {
  65. const scrollEl = (this.scrollEl = await getScrollElement(contentEl));
  66. /**
  67. * Handle fading of toolbars on scroll
  68. */
  69. this.contentScrollCallback = () => {
  70. handleFooterFade(scrollEl, this.el);
  71. };
  72. scrollEl.addEventListener('scroll', this.contentScrollCallback);
  73. handleFooterFade(scrollEl, this.el);
  74. };
  75. this.keyboardVisible = false;
  76. this.collapse = undefined;
  77. this.translucent = false;
  78. }
  79. componentDidLoad() {
  80. this.checkCollapsibleFooter();
  81. }
  82. componentDidUpdate() {
  83. this.checkCollapsibleFooter();
  84. }
  85. async connectedCallback() {
  86. this.keyboardCtrl = await createKeyboardController(async (keyboardOpen, waitForResize) => {
  87. /**
  88. * If the keyboard is hiding, then we need to wait
  89. * for the webview to resize. Otherwise, the footer
  90. * will flicker before the webview resizes.
  91. */
  92. if (keyboardOpen === false && waitForResize !== undefined) {
  93. await waitForResize;
  94. }
  95. this.keyboardVisible = keyboardOpen; // trigger re-render by updating state
  96. });
  97. }
  98. disconnectedCallback() {
  99. if (this.keyboardCtrl) {
  100. this.keyboardCtrl.destroy();
  101. }
  102. }
  103. destroyCollapsibleFooter() {
  104. if (this.scrollEl && this.contentScrollCallback) {
  105. this.scrollEl.removeEventListener('scroll', this.contentScrollCallback);
  106. this.contentScrollCallback = undefined;
  107. }
  108. }
  109. render() {
  110. const { translucent, collapse } = this;
  111. const mode = getIonMode(this);
  112. const tabs = this.el.closest('ion-tabs');
  113. const tabBar = tabs === null || tabs === void 0 ? void 0 : tabs.querySelector(':scope > ion-tab-bar');
  114. return (h(Host, { key: 'ddc228f1a1e7fa4f707dccf74db2490ca3241137', role: "contentinfo", class: {
  115. [mode]: true,
  116. // Used internally for styling
  117. [`footer-${mode}`]: true,
  118. [`footer-translucent`]: translucent,
  119. [`footer-translucent-${mode}`]: translucent,
  120. ['footer-toolbar-padding']: !this.keyboardVisible && (!tabBar || tabBar.slot !== 'bottom'),
  121. [`footer-collapse-${collapse}`]: collapse !== undefined,
  122. } }, mode === 'ios' && translucent && h("div", { key: 'e16ed4963ff94e06de77eb8038201820af73937c', class: "footer-background" }), h("slot", { key: 'f186934febf85d37133d9351a96c1a64b0a4b203' })));
  123. }
  124. get el() { return this; }
  125. static get style() { return {
  126. ios: IonFooterIosStyle0,
  127. md: IonFooterMdStyle0
  128. }; }
  129. }, [36, "ion-footer", {
  130. "collapse": [1],
  131. "translucent": [4],
  132. "keyboardVisible": [32]
  133. }]);
  134. function defineCustomElement$1() {
  135. if (typeof customElements === "undefined") {
  136. return;
  137. }
  138. const components = ["ion-footer"];
  139. components.forEach(tagName => { switch (tagName) {
  140. case "ion-footer":
  141. if (!customElements.get(tagName)) {
  142. customElements.define(tagName, Footer);
  143. }
  144. break;
  145. } });
  146. }
  147. const IonFooter = Footer;
  148. const defineCustomElement = defineCustomElement$1;
  149. export { IonFooter, defineCustomElement };