From 7ce3959b66a08e980c7dac3bb7d7df6bf0ae874e Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 5 Oct 2021 16:44:23 -0400 Subject: [PATCH] feat(header, footer): add ios fading header style (#24011) --- angular/src/directives/proxies.ts | 4 +- core/api.txt | 3 +- core/src/components.d.ts | 16 ++- core/src/components/footer/footer.ios.scss | 9 +- core/src/components/footer/footer.tsx | 68 +++++++++- core/src/components/footer/footer.utils.ts | 36 +++++ core/src/components/footer/readme.md | 43 +++++- core/src/components/footer/test/fade/e2e.ts | 10 ++ .../components/footer/test/fade/index.html | 89 ++++++++++++ core/src/components/footer/usage/angular.md | 7 + .../src/components/footer/usage/javascript.md | 7 + core/src/components/footer/usage/react.md | 7 + core/src/components/footer/usage/stencil.md | 9 +- core/src/components/footer/usage/vue.md | 7 + core/src/components/header/header.ios.scss | 17 ++- core/src/components/header/header.tsx | 69 ++++++---- core/src/components/header/header.utils.ts | 56 ++++++-- core/src/components/header/readme.md | 127 +++++++++++++++++- core/src/components/header/test/fade/e2e.ts | 10 ++ .../components/header/test/fade/index.html | 89 ++++++++++++ core/src/components/header/usage/angular.md | 15 +++ .../src/components/header/usage/javascript.md | 15 +++ core/src/components/header/usage/react.md | 15 +++ core/src/components/header/usage/stencil.md | 17 ++- core/src/components/header/usage/vue.md | 15 +++ core/src/components/toolbar/toolbar.scss | 3 +- packages/vue/src/proxies.ts | 1 + 27 files changed, 704 insertions(+), 60 deletions(-) create mode 100644 core/src/components/footer/footer.utils.ts create mode 100644 core/src/components/footer/test/fade/e2e.ts create mode 100644 core/src/components/footer/test/fade/index.html create mode 100644 core/src/components/header/test/fade/e2e.ts create mode 100644 core/src/components/header/test/fade/index.html 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 + + +
+
+ + diff --git a/core/src/components/footer/usage/angular.md b/core/src/components/footer/usage/angular.md index a3bcfe851e..ae8a6cc576 100644 --- a/core/src/components/footer/usage/angular.md +++ b/core/src/components/footer/usage/angular.md @@ -13,4 +13,11 @@ Footer + + + + + Footer + + ``` diff --git a/core/src/components/footer/usage/javascript.md b/core/src/components/footer/usage/javascript.md index a3bcfe851e..ae8a6cc576 100644 --- a/core/src/components/footer/usage/javascript.md +++ b/core/src/components/footer/usage/javascript.md @@ -13,4 +13,11 @@ Footer + + + + + Footer + + ``` diff --git a/core/src/components/footer/usage/react.md b/core/src/components/footer/usage/react.md index c4e6fef6f4..abaeadeff5 100644 --- a/core/src/components/footer/usage/react.md +++ b/core/src/components/footer/usage/react.md @@ -18,6 +18,13 @@ export const FooterExample: React.FC = () => ( Footer + + {/*-- Fade Footer --*/} + + + Footer + + ); ``` diff --git a/core/src/components/footer/usage/stencil.md b/core/src/components/footer/usage/stencil.md index 84ef348a27..d399618fb0 100644 --- a/core/src/components/footer/usage/stencil.md +++ b/core/src/components/footer/usage/stencil.md @@ -10,7 +10,7 @@ export class FooterExample { return [ , - // Footer without a border + {/*-- Footer without a border --*/} Footer - No Border @@ -21,6 +21,13 @@ export class FooterExample { Footer + , + + {/*-- Fade Footer --*/} + + + Footer + ]; } diff --git a/core/src/components/footer/usage/vue.md b/core/src/components/footer/usage/vue.md index 903749618d..a830b4b73d 100644 --- a/core/src/components/footer/usage/vue.md +++ b/core/src/components/footer/usage/vue.md @@ -14,6 +14,13 @@ Footer + + + + + Footer + + + + + + + + +
+ + + Mailboxes + + + + + + Mailboxes + + +
+
+
+
+
+
+
+
+
+
+
+ + + Updated Just Now + + +
+
+ + diff --git a/core/src/components/header/usage/angular.md b/core/src/components/header/usage/angular.md index 7b43b5d997..e91326f263 100644 --- a/core/src/components/header/usage/angular.md +++ b/core/src/components/header/usage/angular.md @@ -26,4 +26,19 @@ + + + + + Header + + + + + + + Header + + + ``` diff --git a/core/src/components/header/usage/javascript.md b/core/src/components/header/usage/javascript.md index 7b43b5d997..a78b678802 100644 --- a/core/src/components/header/usage/javascript.md +++ b/core/src/components/header/usage/javascript.md @@ -26,4 +26,19 @@ + + + + + Header + + + + + + + Header + + + ``` diff --git a/core/src/components/header/usage/react.md b/core/src/components/header/usage/react.md index 683f6e9adf..219a7a713a 100644 --- a/core/src/components/header/usage/react.md +++ b/core/src/components/header/usage/react.md @@ -31,6 +31,21 @@ export const HeaderExample: React.FC = () => ( + + {/*-- Fade Header with collapse header --*/} + + + Header + + + + + + + Header + + + ); ``` \ No newline at end of file diff --git a/core/src/components/header/usage/stencil.md b/core/src/components/header/usage/stencil.md index 8abf3618b2..70b267a538 100644 --- a/core/src/components/header/usage/stencil.md +++ b/core/src/components/header/usage/stencil.md @@ -21,7 +21,7 @@ export class HeaderExample { , - // Header without a border + {/*-- Header without a border --*/} Header - No Border @@ -34,6 +34,21 @@ export class HeaderExample { My Navigation Bar + , + + {/*-- Fade Header with collapse header --*/} + + + Header + + + + + + + Header + + ]; } diff --git a/core/src/components/header/usage/vue.md b/core/src/components/header/usage/vue.md index 9b3dd5d42c..3f55a81c2d 100644 --- a/core/src/components/header/usage/vue.md +++ b/core/src/components/header/usage/vue.md @@ -27,6 +27,21 @@ + + + + + Header + + + + + + + Header + + +