diff --git a/angular/src/directives/proxies.ts b/angular/src/directives/proxies.ts
index 4642e4101a..3b060d6c9c 100644
--- a/angular/src/directives/proxies.ts
+++ b/angular/src/directives/proxies.ts
@@ -519,13 +519,13 @@ export class IonFabList {
export declare interface IonFooter extends Components.IonFooter {}
@ProxyCmp({
- inputs: ['mode', 'translucent']
+ inputs: ['collapse', 'mode', 'translucent']
})
@Component({
selector: 'ion-footer',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '',
- inputs: ['mode', 'translucent']
+ inputs: ['collapse', 'mode', 'translucent']
})
export class IonFooter {
protected el: HTMLElement;
diff --git a/core/api.txt b/core/api.txt
index dc5f6f5d5f..dd0de21130 100644
--- a/core/api.txt
+++ b/core/api.txt
@@ -463,6 +463,7 @@ ion-fab-list,prop,activated,boolean,false,false,false
ion-fab-list,prop,side,"bottom" | "end" | "start" | "top",'bottom',false,false
ion-footer,none
+ion-footer,prop,collapse,"fade" | undefined,undefined,false,false
ion-footer,prop,mode,"ios" | "md",undefined,false,false
ion-footer,prop,translucent,boolean,false,false,false
@@ -482,7 +483,7 @@ ion-grid,css-prop,--ion-grid-width-xl
ion-grid,css-prop,--ion-grid-width-xs
ion-header,none
-ion-header,prop,collapse,"condense" | undefined,undefined,false,false
+ion-header,prop,collapse,"condense" | "fade" | undefined,undefined,false,false
ion-header,prop,mode,"ios" | "md",undefined,false,false
ion-header,prop,translucent,boolean,false,false,false
diff --git a/core/src/components.d.ts b/core/src/components.d.ts
index 89a6560b2f..7932a1dc35 100644
--- a/core/src/components.d.ts
+++ b/core/src/components.d.ts
@@ -920,6 +920,10 @@ export namespace Components {
"side": 'start' | 'end' | 'top' | 'bottom';
}
interface IonFooter {
+ /**
+ * Describes the scroll effect that will be applied to the footer. Only applies in iOS mode.
+ */
+ "collapse"?: 'fade';
/**
* The mode determines which platform styles to use.
*/
@@ -937,9 +941,9 @@ export namespace Components {
}
interface IonHeader {
/**
- * Describes the scroll effect that will be applied to the header `condense` only applies in iOS mode. Typically used for [Collapsible Large Titles](https://ionicframework.com/docs/api/title#collapsible-large-titles)
+ * Describes the scroll effect that will be applied to the header. Only applies in iOS mode. Typically used for [Collapsible Large Titles](https://ionicframework.com/docs/api/title#collapsible-large-titles)
*/
- "collapse"?: 'condense';
+ "collapse"?: 'condense' | 'fade';
/**
* The mode determines which platform styles to use.
*/
@@ -4606,6 +4610,10 @@ declare namespace LocalJSX {
"side"?: 'start' | 'end' | 'top' | 'bottom';
}
interface IonFooter {
+ /**
+ * Describes the scroll effect that will be applied to the footer. Only applies in iOS mode.
+ */
+ "collapse"?: 'fade';
/**
* The mode determines which platform styles to use.
*/
@@ -4623,9 +4631,9 @@ declare namespace LocalJSX {
}
interface IonHeader {
/**
- * Describes the scroll effect that will be applied to the header `condense` only applies in iOS mode. Typically used for [Collapsible Large Titles](https://ionicframework.com/docs/api/title#collapsible-large-titles)
+ * Describes the scroll effect that will be applied to the header. Only applies in iOS mode. Typically used for [Collapsible Large Titles](https://ionicframework.com/docs/api/title#collapsible-large-titles)
*/
- "collapse"?: 'condense';
+ "collapse"?: 'condense' | 'fade';
/**
* The mode determines which platform styles to use.
*/
diff --git a/core/src/components/footer/footer.ios.scss b/core/src/components/footer/footer.ios.scss
index 67ff5b4818..7c59624efe 100644
--- a/core/src/components/footer/footer.ios.scss
+++ b/core/src/components/footer/footer.ios.scss
@@ -23,4 +23,11 @@
.footer-ios.ion-no-border ion-toolbar:first-of-type {
--border-width: 0;
-}
\ No newline at end of file
+}
+
+// iOS Footer - Collapse Fade
+// --------------------------------------------------
+
+.footer-collapse-fade ion-toolbar {
+ --opacity-scale: inherit;
+}
diff --git a/core/src/components/footer/footer.tsx b/core/src/components/footer/footer.tsx
index 3eb0369d89..7719bd4718 100644
--- a/core/src/components/footer/footer.tsx
+++ b/core/src/components/footer/footer.tsx
@@ -1,6 +1,9 @@
-import { Component, ComponentInterface, Host, Prop, h } from '@stencil/core';
+import { Component, ComponentInterface, Element, Host, Prop, h } from '@stencil/core';
import { getIonMode } from '../../global/ionic-global';
+import { componentOnReady } from '../../utils/helpers';
+
+import { handleFooterFade } from './footer.utils';
/**
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
@@ -13,6 +16,16 @@ import { getIonMode } from '../../global/ionic-global';
}
})
export class Footer implements ComponentInterface {
+ private scrollEl?: HTMLElement;
+ private contentScrollCallback: any;
+
+ @Element() el!: HTMLIonFooterElement;
+
+ /**
+ * Describes the scroll effect that will be applied to the footer.
+ * Only applies in iOS mode.
+ */
+ @Prop() collapse?: 'fade';
/**
* If `true`, the footer will be translucent.
@@ -24,9 +37,56 @@ export class Footer implements ComponentInterface {
*/
@Prop() translucent = false;
- render() {
+ componentDidLoad() {
+ this.checkCollapsibleFooter();
+ }
+
+ componentDidUpdate() {
+ this.checkCollapsibleFooter();
+ }
+
+ private checkCollapsibleFooter = () => {
+ const mode = getIonMode(this);
+ if (mode !== 'ios') { return; }
+
+ const { collapse } = this;
+ const hasFade = collapse === 'fade';
+
+ this.destroyCollapsibleFooter();
+
+ if (hasFade) {
+ const pageEl = this.el.closest('ion-app,ion-page,.ion-page,page-inner');
+ const contentEl = (pageEl) ? pageEl.querySelector('ion-content') : null;
+
+ this.setupFadeFooter(contentEl);
+ }
+ }
+
+ private setupFadeFooter = async (contentEl: HTMLIonContentElement | null) => {
+ if (!contentEl) { console.error('ion-footer requires a content to collapse. Make sure there is an ion-content.'); return; }
+
+ await new Promise(resolve => componentOnReady(contentEl, resolve));
+ const scrollEl = this.scrollEl = await contentEl.getScrollElement();
+
+ /**
+ * Handle fading of toolbars on scroll
+ */
+ this.contentScrollCallback = () => { handleFooterFade(scrollEl, this.el); };
+ scrollEl.addEventListener('scroll', this.contentScrollCallback);
+
+ handleFooterFade(scrollEl, this.el);
+ }
+
+ private destroyCollapsibleFooter() {
+ if (this.scrollEl && this.contentScrollCallback) {
+ this.scrollEl.removeEventListener('scroll', this.contentScrollCallback);
+ this.contentScrollCallback = undefined;
+ }
+ }
+
+ render() {
+ const { translucent, collapse } = this;
const mode = getIonMode(this);
- const translucent = this.translucent;
return (
{ mode === 'ios' && translucent &&
diff --git a/core/src/components/footer/footer.utils.ts b/core/src/components/footer/footer.utils.ts
new file mode 100644
index 0000000000..60c22c4ef9
--- /dev/null
+++ b/core/src/components/footer/footer.utils.ts
@@ -0,0 +1,36 @@
+import { readTask, writeTask } from '@stencil/core';
+
+import { clamp } from '../../utils/helpers';
+
+export const handleFooterFade = (scrollEl: HTMLElement, baseEl: HTMLElement) => {
+ readTask(() => {
+ const scrollTop = scrollEl.scrollTop;
+ const maxScroll = scrollEl.scrollHeight - scrollEl.clientHeight;
+
+ /**
+ * Toolbar background will fade
+ * out over fadeDuration in pixels.
+ */
+ const fadeDuration = 10;
+
+ /**
+ * Begin fading out maxScroll - 30px
+ * from the bottom of the content.
+ * Also determine how close we are
+ * to starting the fade. If we are
+ * before the starting point, the
+ * scale value will get clamped to 0.
+ * If we are after the maxScroll (rubber
+ * band scrolling), the scale value will
+ * get clamped to 1.
+ */
+ const fadeStart = maxScroll - fadeDuration;
+ const distanceToStart = scrollTop - fadeStart;
+
+ const scale = clamp(0, 1 - (distanceToStart / fadeDuration), 1);
+
+ writeTask(() => {
+ baseEl.style.setProperty('--opacity-scale', scale.toString());
+ })
+ });
+}
diff --git a/core/src/components/footer/readme.md b/core/src/components/footer/readme.md
index d94dfdb2aa..3525b110c5 100644
--- a/core/src/components/footer/readme.md
+++ b/core/src/components/footer/readme.md
@@ -3,6 +3,10 @@
Footer is a root component of a page that sits at the bottom of the page.
Footer can be a wrapper for ion-toolbar to make sure the content area is sized correctly.
+## Fade Footer
+
+The `collapse` property can be set to `'fade'` on a page's `ion-footer` to have the background color of the toolbars fade in as users scroll. This provides the same fade effect that is found in many native iOS applications.
+
@@ -25,6 +29,13 @@ Footer can be a wrapper for ion-toolbar to make sure the content area is sized c
Footer
+
+
+
+
+ Footer
+
+
```
@@ -50,6 +61,13 @@ export const FooterExample: React.FC = () => (
Footer
+
+ {/*-- Fade Footer --*/}
+
+
+ Footer
+
+
>
);
```
@@ -69,7 +87,7 @@ export class FooterExample {
return [
,
- // Footer without a border
+ {/*-- Footer without a border --*/}
Footer - No Border
@@ -80,6 +98,13 @@ export class FooterExample {
Footer
+ ,
+
+ {/*-- Fade Footer --*/}
+
+
+ Footer
+
];
}
@@ -105,6 +130,13 @@ export class FooterExample {
Footer
+
+
+
+
+ Footer
+
+
+
+
+
+
+
+
+
+
+
+ Mailboxes
+
+
+
+
+
+ Mailboxes
+
+
+
+
+
+
+ Updated Just Now
+
+
+
+
+
+