Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2785,6 +2785,7 @@ ion-toggle,part,track

ion-toolbar,shadow
ion-toolbar,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
ion-toolbar,prop,hideOnScroll,boolean,false,false,false
ion-toolbar,prop,mode,"ios" | "md",undefined,false,false
ion-toolbar,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-toolbar,prop,titlePlacement,"center" | "end" | "start" | undefined,undefined,false,false
Expand Down
15 changes: 13 additions & 2 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1517,7 +1517,7 @@ export namespace Components {
}
interface IonHeader {
/**
* Describes the scroll effect that will be applied to the header. Only applies when the theme is `"ios"`. 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 when the theme is `"ios"`. Typically used for [Collapsible Large Titles](https://ionicframework.com/docs/api/title#collapsible-large-titles) When any child `ion-toolbar` has `hideOnScroll` set to `true`, the collapse behavior is skipped and `hideOnScroll` takes precedence.
*/
"collapse"?: 'condense' | 'fade';
/**
Expand Down Expand Up @@ -4505,6 +4505,11 @@ export namespace Components {
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
"color"?: Color;
/**
* If `true`, the toolbar will be hidden when the user scrolls down and shown when the user scrolls up. Applies when the toolbar is inside an `ion-header` or `ion-footer` and a sibling `ion-content` exists on the same page. The header slides up and fades; the footer slides down. `ion-content` scroll insets are coordinated per edge so top and bottom toolbars can hide independently. When the theme is `"ios"`, this takes precedence over `ion-header[collapse="condense" | "fade"]` if both are set on the same header.
* @default false
*/
"hideOnScroll": boolean;
/**
* The mode determines the platform behaviors of the component.
*/
Expand Down Expand Up @@ -7565,7 +7570,7 @@ declare namespace LocalJSX {
}
interface IonHeader {
/**
* Describes the scroll effect that will be applied to the header. Only applies when the theme is `"ios"`. 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 when the theme is `"ios"`. Typically used for [Collapsible Large Titles](https://ionicframework.com/docs/api/title#collapsible-large-titles) When any child `ion-toolbar` has `hideOnScroll` set to `true`, the collapse behavior is skipped and `hideOnScroll` takes precedence.
*/
"collapse"?: 'condense' | 'fade';
/**
Expand Down Expand Up @@ -10691,6 +10696,11 @@ declare namespace LocalJSX {
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
"color"?: Color;
/**
* If `true`, the toolbar will be hidden when the user scrolls down and shown when the user scrolls up. Applies when the toolbar is inside an `ion-header` or `ion-footer` and a sibling `ion-content` exists on the same page. The header slides up and fades; the footer slides down. `ion-content` scroll insets are coordinated per edge so top and bottom toolbars can hide independently. When the theme is `"ios"`, this takes precedence over `ion-header[collapse="condense" | "fade"]` if both are set on the same header.
* @default false
*/
"hideOnScroll"?: boolean;
/**
* The mode determines the platform behaviors of the component.
*/
Expand Down Expand Up @@ -11506,6 +11516,7 @@ declare namespace LocalJSX {
interface IonToolbarAttributes {
"color": Color;
"titlePlacement": 'start' | 'center' | 'end';
"hideOnScroll": boolean;
}

interface IntrinsicElements {
Expand Down
87 changes: 80 additions & 7 deletions core/src/components/content/content.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import "../../themes/native/native.globals";
@use "../../themes/native/native.globals" as globals;

// Content
// --------------------------------------------------
Expand All @@ -19,8 +19,8 @@
* @prop --offset-top: Offset top of the content
* @prop --offset-bottom: Offset bottom of the content
*/
--background: #{$background-color};
--color: #{$text-color};
--background: #{globals.$background-color};
--color: #{globals.$text-color};
--padding-top: 0px;
--padding-bottom: 0px;
--padding-start: 0px;
Expand All @@ -44,7 +44,7 @@
padding: 0 !important;
/* stylelint-enable */

font-family: $font-family-base;
font-family: globals.$font-family-base;

contain: size style;
}
Expand All @@ -55,16 +55,16 @@
}

#background-content {
@include position(calc(var(--offset-top) * -1), 0px, calc(var(--offset-bottom) * -1), 0px);
@include globals.position(calc(var(--offset-top) * -1), 0px, calc(var(--offset-bottom) * -1), 0px);

position: absolute;

background: var(--background);
}

.inner-scroll {
@include position(calc(var(--offset-top) * -1), 0px, calc(var(--offset-bottom) * -1), 0px);
@include padding(
@include globals.position(calc(var(--offset-top) * -1), 0px, calc(var(--offset-bottom) * -1), 0px);
@include globals.padding(
calc(var(--padding-top) + var(--offset-top)),
var(--padding-end),
calc(
Expand Down Expand Up @@ -145,6 +145,79 @@
top: -1px;
}

// Toolbar hide on scroll — ion-content coordination
// --------------------------------------------------
// Applied when `ion-toolbar` sets `hideOnScroll` on a page with sibling
// `ion-content`. Header/footer overlay the viewport; scroll insets use
// `--toolbar-hide-offset-*` (measured by ion-toolbar) and animatable
// `--toolbar-hide-inset-*` so padding eases without layout jumps. Top and
// bottom edges are independent (separate classes and CSS variables).

@property --toolbar-hide-inset-top {
syntax: "<length>";
inherits: true;
initial-value: 0px;
}

@property --toolbar-hide-inset-bottom {
syntax: "<length>";
inherits: true;
initial-value: 0px;
}

:host(.content-toolbar-hide-offset-top),
:host(.content-toolbar-hide-offset-bottom) {
#background-content,
.inner-scroll {
@include globals.position(0, 0, 0, 0);
}
}

// Top inset (header toolbar)
:host(.content-toolbar-hide-offset-top) {
--toolbar-hide-inset-top: var(--toolbar-hide-offset-top, 0px);

transition: --toolbar-hide-inset-top var(--token-transition-time-300, 300ms)
var(--token-transition-curve-quick, cubic-bezier(0, 0, 0.2, 1));
}

:host(.content-toolbar-hide-offset-top.content-header-scroll-hidden) {
--toolbar-hide-inset-top: 0px;

transition: --toolbar-hide-inset-top var(--token-transition-time-200, 200ms)
var(--token-transition-curve-base, cubic-bezier(0.4, 0, 1, 1));
}

:host(.content-toolbar-hide-offset-top) .inner-scroll {
padding-top: calc(var(--padding-top) + var(--toolbar-hide-inset-top, 0px));
}

:host(.content-toolbar-hide-offset-top) #background-content {
padding-top: var(--toolbar-hide-inset-top, 0px);
}

// Bottom inset (footer toolbar)
:host(.content-toolbar-hide-offset-bottom) {
--toolbar-hide-inset-bottom: var(--toolbar-hide-offset-bottom, 0px);

transition: --toolbar-hide-inset-bottom var(--token-transition-time-200, 200ms)
var(--token-transition-curve-spring, cubic-bezier(0.16, 1, 0.3, 1));
}

:host(.content-toolbar-hide-offset-bottom.content-footer-scroll-hidden) {
--toolbar-hide-inset-bottom: 0px;

transition: --toolbar-hide-inset-bottom var(--token-transition-time-350, 350ms)
var(--token-transition-curve-base, cubic-bezier(0.4, 0, 1, 1));
}

:host(.content-toolbar-hide-offset-bottom) .inner-scroll {
padding-bottom: calc(
var(--padding-bottom) + var(--keyboard-offset) + var(--toolbar-hide-inset-bottom, 0px) +
var(--internal-content-safe-area-padding-bottom, 0px)
);
}

:host(.content-sizing) {
display: flex;

Expand Down
23 changes: 21 additions & 2 deletions core/src/components/footer/footer.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import "../../themes/native/native.globals";
@use "../../themes/native/native.globals" as globals;

// Footer
// --------------------------------------------------
Expand All @@ -11,9 +11,28 @@ ion-footer {

width: 100%;

z-index: $z-index-toolbar;
z-index: globals.$z-index-toolbar;
}

ion-footer.footer-toolbar-padding ion-toolbar:last-of-type {
padding-bottom: var(--ion-safe-area-bottom, 0);
}

// Footer Hide on Scroll
// --------------------------------------------------
// Added by `ion-toolbar` when `hideOnScroll` is enabled. Overlays `ion-content`.

ion-footer.ion-footer-hide-on-scroll {
@include globals.position(null, 0, 0, 0);
position: absolute;

transition: transform var(--token-transition-time-200, 200ms)
var(--token-transition-curve-spring, cubic-bezier(0.16, 1, 0.3, 1));
}

ion-footer.ion-footer-hide-on-scroll.footer-scroll-hidden {
transform: translateY(calc(100% + var(--ion-safe-area-bottom, 0)));

transition: transform var(--token-transition-time-350, 350ms)
var(--token-transition-curve-base, cubic-bezier(0.4, 0, 1, 1));
}
23 changes: 23 additions & 0 deletions core/src/components/header/header.common.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@use "../../themes/native/native.globals" as globals;

// Header
// --------------------------------------------------

Expand All @@ -13,3 +15,24 @@ ion-header {
ion-header ion-toolbar:first-of-type {
padding-top: var(--ion-safe-area-top, 0);
}

// Header Hide on Scroll
// --------------------------------------------------
// Added by `ion-toolbar` when `hideOnScroll` is enabled. Overlays `ion-content`.

ion-header.ion-header-hide-on-scroll {
@include globals.position(0, 0, null, 0);
position: absolute;

transition: transform var(--token-transition-time-300, 300ms)
var(--token-transition-curve-quick, cubic-bezier(0, 0, 0.2, 1));

z-index: 10;
}

ion-header.ion-header-hide-on-scroll.header-scroll-hidden {
transform: translateY(calc(-100% - var(--ion-safe-area-top, 0)));

transition: transform var(--token-transition-time-200, 200ms)
var(--token-transition-curve-base, cubic-bezier(0.4, 0, 1, 1));
}
13 changes: 13 additions & 0 deletions core/src/components/header/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ export class Header implements ComponentInterface {
* Only applies when the theme is `"ios"`.
*
* Typically used for [Collapsible Large Titles](https://ionicframework.com/docs/api/title#collapsible-large-titles)
*
* When any child `ion-toolbar` has `hideOnScroll` set to `true`, the collapse
* behavior is skipped and `hideOnScroll` takes precedence.
*/
@Prop() collapse?: 'condense' | 'fade';

Expand Down Expand Up @@ -80,13 +83,23 @@ export class Header implements ComponentInterface {
this.destroyCollapsibleHeader();
}

private hasHideOnScrollToolbar(): boolean {
return Array.from(this.el.querySelectorAll('ion-toolbar')).some((toolbar) => toolbar.hideOnScroll);
}

private async checkCollapsibleHeader() {
const theme = getIonTheme(this);

if (theme !== 'ios') {
return;
}

// Destroy the collapsible header when `hideOnScroll` is present.
if (this.hasHideOnScrollToolbar()) {
this.destroyCollapsibleHeader();
return;
}

const { collapse } = this;
const hasCondense = collapse === 'condense';
const hasFade = collapse === 'fade';
Expand Down
Loading
Loading