diff --git a/src/addons/messages/pages/discussion/discussion.page.ts b/src/addons/messages/pages/discussion/discussion.page.ts index bdff8c4f9..ca9028029 100644 --- a/src/addons/messages/pages/discussion/discussion.page.ts +++ b/src/addons/messages/pages/discussion/discussion.page.ts @@ -495,7 +495,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView /** * Set the new message badge number and set scroll listener if needed. * - * @param addMessages NUmber of messages still to be read. + * @param addMessages Number of messages still to be read. */ protected setNewMessagesBadge(addMessages: number): void { if (this.newMessages == 0 && addMessages > 0) { diff --git a/src/core/features/course/components/module-navigation/module-navigation.ts b/src/core/features/course/components/module-navigation/module-navigation.ts index dfb9602a4..c6cc18ae9 100644 --- a/src/core/features/course/components/module-navigation/module-navigation.ts +++ b/src/core/features/course/components/module-navigation/module-navigation.ts @@ -23,6 +23,7 @@ import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; +import { CoreMath } from '@singletons/math'; /** * Component to show a button to go to the next resource/activity. @@ -50,6 +51,8 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { protected initialHeight = 0; protected initialPaddingBottom = 0; protected previousTop = 0; + protected previousHeight = 0; + protected stickTimeout?: number; protected content?: HTMLIonContentElement | null; protected completionObserver: CoreEventObserver; @@ -102,6 +105,7 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { } // Set a minimum height value. this.initialHeight = this.initialHeight || 56; + this.previousHeight = this.initialHeight; this.content = this.element.closest('ion-content'); @@ -118,6 +122,8 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { return; } + this.content.classList.add('has-core-course-module-navigation'); + // Move element to the nearest ion-content if it's not the parent. if (this.element.parentElement?.nodeName != 'ION-CONTENT') { this.content.appendChild(this.element); @@ -135,7 +141,7 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { return; } - this.onScroll(e.detail.scrollTop, scroll.scrollHeight - scroll.offsetHeight); + this.onScroll(e.detail, scroll); }); } @@ -304,19 +310,21 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { /** * On scroll function. * - * @param top Scroll top measure. - * @param maxScroll Scroll height. + * @param scrollDetail Scroll detail object. + * @param scrollElement Scroll element to calculate maxScroll. */ - protected onScroll(top: number, maxScroll: number): void { - if (top == 0 || top == maxScroll) { + protected onScroll(scrollDetail: ScrollDetail, scrollElement: HTMLElement): void { + const maxScroll = scrollElement.scrollHeight - scrollElement.offsetHeight; + if (scrollDetail.scrollTop <= 0 || scrollDetail.scrollTop >= maxScroll) { // Reset. this.setBarHeight(this.initialHeight); } else { - const diffHeight = this.element.clientHeight - (top - this.previousTop); - this.setBarHeight(diffHeight); - } + let newHeight = this.previousHeight - (scrollDetail.scrollTop - this.previousTop); + newHeight = CoreMath.clamp(newHeight, 0, this.initialHeight); - this.previousTop = top; + this.setBarHeight(newHeight); + } + this.previousTop = scrollDetail.scrollTop; } /** @@ -325,14 +333,20 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { * @param height The new bar height. */ protected setBarHeight(height: number): void { - if (height <= 0) { - height = 0; - } else if (height > this.initialHeight) { - height = this.initialHeight; + if (this.stickTimeout) { + clearTimeout(this.stickTimeout); } - this.element.style.opacity = height == 0 ? '0' : '1'; + this.element.style.opacity = height <= 0 ? '0' : '1'; this.content?.style.setProperty('--core-course-module-navigation-height', height + 'px'); + this.previousHeight = height; + + if (height > 0 && height < this.initialHeight) { + // Finish opening or closing the bar. + const newHeight = height < this.initialHeight / 2 ? 0 : this.initialHeight; + + this.stickTimeout = window.setTimeout(() => this.setBarHeight(newHeight), 500); + } } } diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index 51f3cb7f2..d13635f92 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -459,7 +459,7 @@ body.core-iframe-fullscreen ion-router-outlet { @supports (padding-left: constant(safe-area-inset-left)) { --ion-safe-area-left: constant(safe-area-inset-left); } - + @supports (padding-left: env(safe-area-inset-left)) { --ion-safe-area-left: env(safe-area-inset-left); } @@ -539,7 +539,7 @@ body.core-iframe-fullscreen ion-router-outlet { .core-modal-lateral-#{$breakpoint} { --ion-safe-area-left: 0px; --ion-safe-area-right: 0px; - + .modal-wrapper { position: absolute; @include position(0 !important, 0 !important, 0 !important, unset !important); @@ -554,7 +554,7 @@ body.core-iframe-fullscreen ion-router-outlet { } } } - + } // Hidden submit button. @@ -911,9 +911,9 @@ ion-fab[core-fab] { ion-fab-button::part(close-icon) { display: none; } -} +} -core-course-module-navigation + ion-fab { +ion-content.has-core-course-module-navigation ion-fab { bottom: calc(var(--core-course-module-navigation-height, 0px) + 10px); @include core-transition(all, 200ms); }