From 294c94b934643761448d6b66f5513a97042322a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 24 Feb 2022 09:55:40 +0100 Subject: [PATCH 1/3] MOBILE-3996 navbar: Update style on navigation bar --- .../mod/book/pages/contents/contents.html | 5 ++-- .../mod/book/pages/contents/contents.ts | 2 -- .../navigation-bar/core-navigation-bar.html | 30 +++++++------------ .../navigation-bar/navigation-bar.scss | 25 +++++++++++----- .../navigation-bar/navigation-bar.ts | 22 ++++---------- .../progress-bar/core-progress-bar.html | 2 +- .../components/progress-bar/progress-bar.ts | 5 ++-- .../core-course-module-navigation.html | 4 +-- .../module-navigation/module-navigation.scss | 8 ++--- .../module-navigation/module-navigation.ts | 4 +-- src/theme/theme.base.scss | 4 +-- src/theme/theme.dark.scss | 2 ++ src/theme/theme.light.scss | 4 +-- 13 files changed, 49 insertions(+), 68 deletions(-) diff --git a/src/addons/mod/book/pages/contents/contents.html b/src/addons/mod/book/pages/contents/contents.html index 6e6334aab..9cb91f696 100644 --- a/src/addons/mod/book/pages/contents/contents.html +++ b/src/addons/mod/book/pages/contents/contents.html @@ -31,9 +31,8 @@ - + diff --git a/src/addons/mod/book/pages/contents/contents.ts b/src/addons/mod/book/pages/contents/contents.ts index 36af98196..21436d557 100644 --- a/src/addons/mod/book/pages/contents/contents.ts +++ b/src/addons/mod/book/pages/contents/contents.ts @@ -62,7 +62,6 @@ export class AddonModBookContentsPage implements OnInit, OnDestroy { warning = ''; displayNavBar = true; navigationItems: CoreNavigationBarItem[] = []; - displayTitlesInNavBar = false; slidesOpts: CoreSwipeSlidesOptions = { autoHeight: true, scrollOnChange: 'top', @@ -135,7 +134,6 @@ export class AddonModBookContentsPage implements OnInit, OnDestroy { const downloadResult = await this.downloadResourceIfNeeded(module, refresh); this.displayNavBar = book.navstyle != AddonModBookNavStyle.TOC_ONLY; - this.displayTitlesInNavBar = book.navstyle == AddonModBookNavStyle.TEXT; this.title = book.name; // Get contents. No need to refresh, it has been done in downloadResourceIfNeeded. diff --git a/src/core/components/navigation-bar/core-navigation-bar.html b/src/core/components/navigation-bar/core-navigation-bar.html index 3f5b6df49..087ffbdf9 100644 --- a/src/core/components/navigation-bar/core-navigation-bar.html +++ b/src/core/components/navigation-bar/core-navigation-bar.html @@ -1,27 +1,17 @@ - - - + + - - + - - -

{{currentIndex + 1}} / {{items.length}}

-
+ + + - - - - + + +
diff --git a/src/core/components/navigation-bar/navigation-bar.scss b/src/core/components/navigation-bar/navigation-bar.scss index b79c274dd..a007f12b3 100644 --- a/src/core/components/navigation-bar/navigation-bar.scss +++ b/src/core/components/navigation-bar/navigation-bar.scss @@ -1,15 +1,24 @@ -:host { - --background: var(--core-course-module-navigation-background); +@import "~theme/globals"; +:host { + --height: var(--core-navigation-height, var(--core-navigation-max-height)); + --background: var(--core-navigation-background); + --button-vertical-margin: 2px; + + height: var(--height); width: 100%; background-color: var(--background); display: block; + bottom: 0; + z-index: 3; + border-top: 1px solid var(--stroke); - .core-navigation-bar-arrow { - text-transform: none; - max-width: 100%; - ion-icon { - flex-shrink: 0; - } + @include core-transition(all, 200ms); + + ion-button, + ::ng-deep ion-button { + margin-top: var(--button-vertical-margin); + margin-bottom: var(--button-vertical-margin); } + } diff --git a/src/core/components/navigation-bar/navigation-bar.ts b/src/core/components/navigation-bar/navigation-bar.ts index 92681ea06..4f1f76b80 100644 --- a/src/core/components/navigation-bar/navigation-bar.ts +++ b/src/core/components/navigation-bar/navigation-bar.ts @@ -32,7 +32,6 @@ import { Translate } from '@singletons'; export class CoreNavigationBarComponent implements OnChanges { @Input() items: CoreNavigationBarItem[] = []; // List of items. - @Input() showTitles = false; // Display titles on buttons. @Input() previousTranslate = 'core.previous'; // Previous translatable text, can admit $a variable. @Input() nextTranslate = 'core.next'; // Next translatable text, can admit $a variable. @Input() component?: string; // Component the bar belongs to. @@ -46,6 +45,8 @@ export class CoreNavigationBarComponent implements OnChanges { previousIndex = -1; // Previous item index. If -1, the previous arrow won't be shown. nextIndex = -1; // Next item index. If -1, the next arrow won't be shown. currentIndex = 0; + progress = 0; + progressText = ''; // Function to call when arrow is clicked. Will receive as a param the item to load. @Output() action: EventEmitter = new EventEmitter(); @@ -63,6 +64,9 @@ export class CoreNavigationBarComponent implements OnChanges { return; } + this.progress = ((this.currentIndex + 1) / this.items.length) * 100; + this.progressText = `${this.currentIndex + 1} / ${this.items.length}`; + this.nextIndex = this.items[this.currentIndex + 1]?.enabled ? this.currentIndex + 1 : -1; if (this.nextIndex >= 0) { this.nextTitle = Translate.instant(this.nextTranslate, { $a: this.items[this.nextIndex].title || '' }); @@ -88,22 +92,6 @@ export class CoreNavigationBarComponent implements OnChanges { this.action.emit(this.items[itemIndex].item); } - /** - * Navigate to an item with the range component. - * - * @param target: Element changed. - */ - navigateOnRange(target: HTMLIonRangeElement): void { - const selectedIndex = target.value as number; // Single value, use number. - if (!this.items[selectedIndex].enabled) { - target.value = this.currentIndex; - - return; - } - - this.navigate(selectedIndex); - } - } export type CoreNavigationBarItem = { diff --git a/src/core/components/progress-bar/core-progress-bar.html b/src/core/components/progress-bar/core-progress-bar.html index dd3335981..dc7dc744e 100644 --- a/src/core/components/progress-bar/core-progress-bar.html +++ b/src/core/components/progress-bar/core-progress-bar.html @@ -4,7 +4,7 @@
{{ a11yText | translate }} - {{ 'core.percentagenumber' | translate: {$a: text} }} + {{ text }}
diff --git a/src/core/components/progress-bar/progress-bar.ts b/src/core/components/progress-bar/progress-bar.ts index 78d74f287..357d948e5 100644 --- a/src/core/components/progress-bar/progress-bar.ts +++ b/src/core/components/progress-bar/progress-bar.ts @@ -86,7 +86,7 @@ export class CoreProgressBarComponent implements OnInit, OnChanges { this.progress = Math.floor(this.progress); if (!this.textSupplied) { - this.text = String(this.progress); + this.text = Translate.instant('core.percentagenumber', { $a: this.progress }); } this.width = DomSanitizer.bypassSecurityTrustStyle(this.progress + '%'); @@ -94,8 +94,7 @@ export class CoreProgressBarComponent implements OnInit, OnChanges { } if (changes.text || changes.progress || changes.a11yText) { - this.progressBarValueText = (this.a11yText ? Translate.instant(this.a11yText) + ' ' : '') + - Translate.instant('core.percentagenumber', { $a: this.text }); + this.progressBarValueText = (this.a11yText ? Translate.instant(this.a11yText) + ' ' : '') + this.text; } } diff --git a/src/core/features/course/components/module-navigation/core-course-module-navigation.html b/src/core/features/course/components/module-navigation/core-course-module-navigation.html index 203fc8f2a..3beaf4af2 100644 --- a/src/core/features/course/components/module-navigation/core-course-module-navigation.html +++ b/src/core/features/course/components/module-navigation/core-course-module-navigation.html @@ -1,7 +1,7 @@ - @@ -14,7 +14,7 @@ - diff --git a/src/core/features/course/components/module-navigation/module-navigation.scss b/src/core/features/course/components/module-navigation/module-navigation.scss index 767ea4da6..48713703f 100644 --- a/src/core/features/course/components/module-navigation/module-navigation.scss +++ b/src/core/features/course/components/module-navigation/module-navigation.scss @@ -1,8 +1,8 @@ @import "~theme/globals"; :host { - --height: var(--core-course-module-navigation-height, var(--core-course-module-navigation-max-height)); - --background: var(--core-course-module-navigation-background); + --height: var(--core-navigation-height, var(--core-navigation-max-height)); + --background: var(--core-navigation-background); --button-vertical-margin: 2px; height: var(--height); @@ -25,10 +25,6 @@ margin-top: var(--button-vertical-margin); margin-bottom: var(--button-vertical-margin); } - - .core-course-module-navigation-arrow { - min-width: 48px; - } } :host-context(.core-iframe-fullscreen) { 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 3a566c701..5b6a4062f 100644 --- a/src/core/features/course/components/module-navigation/module-navigation.ts +++ b/src/core/features/course/components/module-navigation/module-navigation.ts @@ -128,7 +128,7 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { return; } - this.content.classList.add('has-core-course-module-navigation'); + this.content.classList.add('has-core-navigation'); // Move element to the nearest ion-content if it's not the parent. if (this.element.parentElement?.nodeName != 'ION-CONTENT') { @@ -347,7 +347,7 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { } this.element.style.opacity = height <= 0 ? '0' : '1'; - this.content?.style.setProperty('--core-course-module-navigation-height', height + 'px'); + this.content?.style.setProperty('--core-navigation-height', height + 'px'); this.previousHeight = height; if (height > 0 && height < this.initialHeight) { diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index 461393019..e2fbd0a46 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -1124,8 +1124,8 @@ ion-fab[core-fab] { } } -ion-content.has-core-course-module-navigation ion-fab { - bottom: calc(var(--core-course-module-navigation-height, 0px) + 10px); +ion-content.has-core-navigation ion-fab { + bottom: calc(var(--core-navigation-height, 0px) + 10px); @include core-transition(all, 200ms); } diff --git a/src/theme/theme.dark.scss b/src/theme/theme.dark.scss index 6f0743f7c..b5be2494e 100644 --- a/src/theme/theme.dark.scss +++ b/src/theme/theme.dark.scss @@ -129,6 +129,8 @@ --core-send-message-input-background: var(--gray-900); --core-send-message-input-color: var(--white); + --core-navigation-background: var(--contrast-background); + --addon-messages-message-bg: var(--gray-800); --addon-messages-message-activated-bg: var(--gray-700); --addon-messages-message-note-text: var(--subdued-text-color); diff --git a/src/theme/theme.light.scss b/src/theme/theme.light.scss index 32b099920..58a0cc3a5 100644 --- a/src/theme/theme.light.scss +++ b/src/theme/theme.light.scss @@ -310,8 +310,8 @@ --core-courseimage-on-course-size: 72px; --core-courseimage-radius: var(--medium-radius); - --core-course-module-navigation-max-height: 48px; - --core-course-module-navigation-background: var(--contrast-background); + --core-navigation-max-height: 48px; + --core-navigation-background: var(--contrast-background); --core-user-menu-site-logo-max-height: 32px; From 533fe8e1b443968ab41c99bba2b984fd29530885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 24 Feb 2022 10:42:12 +0100 Subject: [PATCH 2/3] MOBILE-3996 navbar: Add navbar at the bottom which disappears on scroll --- .../mod/book/pages/contents/contents.html | 12 +- .../mod/book/pages/contents/contents.scss | 8 +- .../index/addon-mod-imscp-index.html | 7 +- src/addons/mod/scorm/pages/player/player.html | 7 +- src/core/classes/tabs.ts | 3 +- .../navigation-bar/navigation-bar.scss | 6 +- .../navigation-bar/navigation-bar.ts | 126 +++++++++++++++++- src/theme/theme.base.scss | 12 +- 8 files changed, 163 insertions(+), 18 deletions(-) diff --git a/src/addons/mod/book/pages/contents/contents.html b/src/addons/mod/book/pages/contents/contents.html index 9cb91f696..a25501544 100644 --- a/src/addons/mod/book/pages/contents/contents.html +++ b/src/addons/mod/book/pages/contents/contents.html @@ -21,9 +21,8 @@ - +
- @@ -31,10 +30,6 @@ - - -
@@ -49,4 +44,9 @@
+ + + diff --git a/src/addons/mod/book/pages/contents/contents.scss b/src/addons/mod/book/pages/contents/contents.scss index de654bcac..823126ed0 100644 --- a/src/addons/mod/book/pages/contents/contents.scss +++ b/src/addons/mod/book/pages/contents/contents.scss @@ -1,6 +1,12 @@ :host { + core-loading ::ng-deep .core-loading-content { + min-height: 100%; + display: flex; + flex-direction: column; + } + .core-swipe-slides-container { - ion-card, core-navigation-bar { + ion-card { flex: none; } } diff --git a/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html b/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html index a3ce89920..ea00f00ce 100644 --- a/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html +++ b/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html @@ -10,7 +10,7 @@ - + @@ -24,12 +24,13 @@
- -
+ + + diff --git a/src/addons/mod/scorm/pages/player/player.html b/src/addons/mod/scorm/pages/player/player.html index 73bb4de59..f50283787 100644 --- a/src/addons/mod/scorm/pages/player/player.html +++ b/src/addons/mod/scorm/pages/player/player.html @@ -20,13 +20,14 @@ - - - +

{{ errorMessage | translate }}

+ + +
diff --git a/src/core/classes/tabs.ts b/src/core/classes/tabs.ts index 76527de07..ca644c64e 100644 --- a/src/core/classes/tabs.ts +++ b/src/core/classes/tabs.ts @@ -85,7 +85,8 @@ export class CoreTabsBaseComponent implements OnInit, Aft protected firstSelectedTab?: string; // ID of the first selected tab to control history. protected backButtonFunction: (event: BackButtonEvent) => void; protected languageChangedSubscription?: Subscription; - protected isInTransition = false; // Weather Slides is in transition. + // Swiper 6 documentation: https://swiper6.vercel.app/ + protected isInTransition = false; // Wether Slides is in transition. protected slidesSwiper: any; // eslint-disable-line @typescript-eslint/no-explicit-any protected slidesSwiperLoaded = false; protected scrollElements: Record = {}; // Scroll elements for each loaded tab. diff --git a/src/core/components/navigation-bar/navigation-bar.scss b/src/core/components/navigation-bar/navigation-bar.scss index a007f12b3..42a6491d7 100644 --- a/src/core/components/navigation-bar/navigation-bar.scss +++ b/src/core/components/navigation-bar/navigation-bar.scss @@ -20,5 +20,9 @@ margin-top: var(--button-vertical-margin); margin-bottom: var(--button-vertical-margin); } - +} + +:host-context(.core-iframe-fullscreen) { + opacity: 0 !important; + height: 0 !important; } diff --git a/src/core/components/navigation-bar/navigation-bar.ts b/src/core/components/navigation-bar/navigation-bar.ts index 4f1f76b80..4fa88cfe6 100644 --- a/src/core/components/navigation-bar/navigation-bar.ts +++ b/src/core/components/navigation-bar/navigation-bar.ts @@ -12,8 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChange } from '@angular/core'; +import { Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, Output, SimpleChange } from '@angular/core'; +import { IonContent } from '@ionic/angular'; +import { ScrollDetail } from '@ionic/core'; +import { CoreUtils } from '@services/utils/utils'; import { Translate } from '@singletons'; +import { CoreMath } from '@singletons/math'; /** * Component to show a "bar" with arrows to navigate forward/backward and an slider to move around. @@ -29,7 +33,7 @@ import { Translate } from '@singletons'; templateUrl: 'core-navigation-bar.html', styleUrls: ['navigation-bar.scss'], }) -export class CoreNavigationBarComponent implements OnChanges { +export class CoreNavigationBarComponent implements OnDestroy, OnChanges { @Input() items: CoreNavigationBarItem[] = []; // List of items. @Input() previousTranslate = 'core.previous'; // Previous translatable text, can admit $a variable. @@ -48,9 +52,118 @@ export class CoreNavigationBarComponent implements OnChanges { progress = 0; progressText = ''; + protected element: HTMLElement; + protected initialHeight = 0; + protected initialPaddingBottom = 0; + protected previousTop = 0; + protected previousHeight = 0; + protected stickTimeout?: number; + protected content?: HTMLIonContentElement | null; + // Function to call when arrow is clicked. Will receive as a param the item to load. @Output() action: EventEmitter = new EventEmitter(); + constructor(el: ElementRef, protected ionContent: IonContent) { + this.element = el.nativeElement; + this.element.setAttribute('slot', 'fixed'); // Just in case somebody forgets to add it. + } + + /** + * Setup scroll event listener. + * + * @param retries Number of retries left. + */ + protected async listenScrollEvents(retries = 3): Promise { + // Already initialized. + if (this.initialHeight > 0) { + return; + } + + this.initialHeight = this.element.getBoundingClientRect().height; + + if (this.initialHeight == 0 && retries > 0) { + await CoreUtils.nextTicks(50); + + this.listenScrollEvents(retries - 1); + + return; + } + // Set a minimum height value. + this.initialHeight = this.initialHeight || 48; + this.previousHeight = this.initialHeight; + + this.content = this.element.closest('ion-content'); + + if (!this.content) { + return; + } + + this.content.classList.add('has-core-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); + } + + // Set a padding to not overlap elements. + this.initialPaddingBottom = parseFloat(this.content.style.getPropertyValue('--padding-bottom') || '0'); + this.content.style.setProperty('--padding-bottom', this.initialPaddingBottom + this.initialHeight + 'px'); + const scroll = await this.content.getScrollElement(); + this.content.scrollEvents = true; + + this.setBarHeight(this.initialHeight); + this.content.addEventListener('ionScroll', (e: CustomEvent): void => { + if (!this.content) { + return; + } + + this.onScroll(e.detail, scroll); + }); + + } + + /** + * On scroll function. + * + * @param scrollDetail Scroll detail object. + * @param scrollElement Scroll element to calculate 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 { + let newHeight = this.previousHeight - (scrollDetail.scrollTop - this.previousTop); + newHeight = CoreMath.clamp(newHeight, 0, this.initialHeight); + + this.setBarHeight(newHeight); + } + this.previousTop = scrollDetail.scrollTop; + } + + /** + * Sets the bar height. + * + * @param height The new bar height. + */ + protected setBarHeight(height: number): void { + if (this.stickTimeout) { + clearTimeout(this.stickTimeout); + } + + this.element.style.opacity = height <= 0 ? '0' : '1'; + this.content?.style.setProperty('--core-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); + } + } + /** * @inheritdoc */ @@ -76,6 +189,8 @@ export class CoreNavigationBarComponent implements OnChanges { if (this.previousIndex >= 0) { this.previousTitle = Translate.instant(this.previousTranslate, { $a: this.items[this.previousIndex].title || '' }); } + + this.listenScrollEvents(); } /** @@ -92,6 +207,13 @@ export class CoreNavigationBarComponent implements OnChanges { this.action.emit(this.items[itemIndex].item); } + /** + * @inheritdoc + */ + async ngOnDestroy(): Promise { + this.content?.style.setProperty('--padding-bottom', this.initialPaddingBottom + 'px'); + } + } export type CoreNavigationBarItem = { diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index e2fbd0a46..822b5b991 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -1540,9 +1540,19 @@ body.core-iframe-fullscreen ion-content { .core-swipe-slides-container { display: flex; flex-direction: column; - height: 100%; + flex-grow: 1; + min-height: 100%; core-swipe-slides { + display: flex; + flex-direction: column; flex-grow: 1; + + ion-slides { + flex-grow: 1; + max-width: 100%; + } } + + } From b5e20713183d1dd390e3762d074f1f586df31b93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 24 Feb 2022 14:27:39 +0100 Subject: [PATCH 3/3] MOBILE-3996 navbar: Move collapsible footer feature to a new directive --- .../index/addon-mod-assign-index.html | 2 +- .../components/index/index.html | 2 +- .../index/addon-mod-book-index.html | 2 +- .../mod/book/pages/contents/contents.html | 2 +- .../index/addon-mod-chat-index.html | 2 +- .../index/addon-mod-choice-index.html | 2 +- .../index/addon-mod-data-index.html | 2 +- .../index/addon-mod-feedback-index.html | 2 +- .../index/addon-mod-folder-index.html | 2 +- .../mod/forum/components/index/index.html | 2 +- .../index/addon-mod-glossary-index.html | 2 +- .../index/addon-mod-h5pactivity-index.html | 2 +- .../index/addon-mod-imscp-index.html | 5 +- .../index/addon-mod-lesson-index.html | 2 +- .../components/index/addon-mod-lti-index.html | 2 +- .../index/addon-mod-page-index.html | 2 +- .../index/addon-mod-quiz-index.html | 2 +- .../index/addon-mod-resource-index.html | 2 +- .../index/addon-mod-scorm-index.html | 2 +- src/addons/mod/scorm/pages/player/player.html | 3 +- .../index/addon-mod-survey-index.html | 2 +- .../components/index/addon-mod-url-index.html | 2 +- .../index/addon-mod-wiki-index.html | 2 +- .../index/addon-mod-workshop-index.html | 2 +- .../navigation-bar/navigation-bar.scss | 11 +- .../navigation-bar/navigation-bar.ts | 130 +------------- src/core/directives/collapsible-footer.ts | 158 ++++++++++++++++++ src/core/directives/directives.module.ts | 3 + .../module-navigation/module-navigation.scss | 11 +- .../module-navigation/module-navigation.ts | 116 ------------- .../pages/module-preview/module-preview.html | 2 +- .../core-siteplugins-module-index.html | 2 +- src/theme/theme.base.scss | 26 ++- src/theme/theme.dark.scss | 2 + src/theme/theme.light.scss | 4 +- 35 files changed, 226 insertions(+), 291 deletions(-) create mode 100644 src/core/directives/collapsible-footer.ts diff --git a/src/addons/mod/assign/components/index/addon-mod-assign-index.html b/src/addons/mod/assign/components/index/addon-mod-assign-index.html index 1f7633bd4..a11a4b319 100644 --- a/src/addons/mod/assign/components/index/addon-mod-assign-index.html +++ b/src/addons/mod/assign/components/index/addon-mod-assign-index.html @@ -127,6 +127,6 @@
- diff --git a/src/addons/mod/bigbluebuttonbn/components/index/index.html b/src/addons/mod/bigbluebuttonbn/components/index/index.html index 911bc6123..f17bc8bf5 100644 --- a/src/addons/mod/bigbluebuttonbn/components/index/index.html +++ b/src/addons/mod/bigbluebuttonbn/components/index/index.html @@ -112,6 +112,6 @@ - diff --git a/src/addons/mod/book/components/index/addon-mod-book-index.html b/src/addons/mod/book/components/index/addon-mod-book-index.html index 410dd43cc..cdd809546 100644 --- a/src/addons/mod/book/components/index/addon-mod-book-index.html +++ b/src/addons/mod/book/components/index/addon-mod-book-index.html @@ -41,6 +41,6 @@ - diff --git a/src/addons/mod/book/pages/contents/contents.html b/src/addons/mod/book/pages/contents/contents.html index a25501544..5089297cf 100644 --- a/src/addons/mod/book/pages/contents/contents.html +++ b/src/addons/mod/book/pages/contents/contents.html @@ -45,7 +45,7 @@
- diff --git a/src/addons/mod/chat/components/index/addon-mod-chat-index.html b/src/addons/mod/chat/components/index/addon-mod-chat-index.html index 7296cddb5..52c2a5cc4 100644 --- a/src/addons/mod/chat/components/index/addon-mod-chat-index.html +++ b/src/addons/mod/chat/components/index/addon-mod-chat-index.html @@ -30,6 +30,6 @@
- diff --git a/src/addons/mod/choice/components/index/addon-mod-choice-index.html b/src/addons/mod/choice/components/index/addon-mod-choice-index.html index 1eebfa01c..6ef6a30cc 100644 --- a/src/addons/mod/choice/components/index/addon-mod-choice-index.html +++ b/src/addons/mod/choice/components/index/addon-mod-choice-index.html @@ -135,7 +135,7 @@
- diff --git a/src/addons/mod/data/components/index/addon-mod-data-index.html b/src/addons/mod/data/components/index/addon-mod-data-index.html index 6f15f5132..40b51ef5a 100644 --- a/src/addons/mod/data/components/index/addon-mod-data-index.html +++ b/src/addons/mod/data/components/index/addon-mod-data-index.html @@ -120,7 +120,7 @@ - diff --git a/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html b/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html index 3abdff768..4ffa61d9a 100644 --- a/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html +++ b/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html @@ -35,7 +35,7 @@ - diff --git a/src/addons/mod/folder/components/index/addon-mod-folder-index.html b/src/addons/mod/folder/components/index/addon-mod-folder-index.html index 82cb66284..fd09820a8 100644 --- a/src/addons/mod/folder/components/index/addon-mod-folder-index.html +++ b/src/addons/mod/folder/components/index/addon-mod-folder-index.html @@ -32,6 +32,6 @@ - diff --git a/src/addons/mod/forum/components/index/index.html b/src/addons/mod/forum/components/index/index.html index 61ff3501f..e49167d13 100644 --- a/src/addons/mod/forum/components/index/index.html +++ b/src/addons/mod/forum/components/index/index.html @@ -114,7 +114,7 @@ - diff --git a/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html b/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html index c973a95a3..d391f6172 100644 --- a/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html +++ b/src/addons/mod/glossary/components/index/addon-mod-glossary-index.html @@ -72,7 +72,7 @@ - diff --git a/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html b/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html index deed6960a..0ef0f7b2d 100644 --- a/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html +++ b/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html @@ -68,6 +68,6 @@ - diff --git a/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html b/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html index ea00f00ce..608162e22 100644 --- a/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html +++ b/src/addons/mod/imscp/components/index/addon-mod-imscp-index.html @@ -29,8 +29,9 @@ - + - diff --git a/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html b/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html index d79ecd5a1..188c0ca97 100644 --- a/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html +++ b/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html @@ -279,6 +279,6 @@ - diff --git a/src/addons/mod/lti/components/index/addon-mod-lti-index.html b/src/addons/mod/lti/components/index/addon-mod-lti-index.html index 1452e0c18..217ee88b2 100644 --- a/src/addons/mod/lti/components/index/addon-mod-lti-index.html +++ b/src/addons/mod/lti/components/index/addon-mod-lti-index.html @@ -21,6 +21,6 @@ - diff --git a/src/addons/mod/page/components/index/addon-mod-page-index.html b/src/addons/mod/page/components/index/addon-mod-page-index.html index 83536200d..e995cc5a0 100644 --- a/src/addons/mod/page/components/index/addon-mod-page-index.html +++ b/src/addons/mod/page/components/index/addon-mod-page-index.html @@ -32,6 +32,6 @@ - diff --git a/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html b/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html index 59961b31c..5b80f4744 100644 --- a/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html +++ b/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html @@ -206,6 +206,6 @@ - diff --git a/src/addons/mod/resource/components/index/addon-mod-resource-index.html b/src/addons/mod/resource/components/index/addon-mod-resource-index.html index 1efbca3f9..0fd7713b7 100644 --- a/src/addons/mod/resource/components/index/addon-mod-resource-index.html +++ b/src/addons/mod/resource/components/index/addon-mod-resource-index.html @@ -97,6 +97,6 @@ - diff --git a/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html b/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html index 96329ba19..49c22490a 100644 --- a/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html +++ b/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html @@ -217,6 +217,6 @@ - diff --git a/src/addons/mod/scorm/pages/player/player.html b/src/addons/mod/scorm/pages/player/player.html index f50283787..895ae27f0 100644 --- a/src/addons/mod/scorm/pages/player/player.html +++ b/src/addons/mod/scorm/pages/player/player.html @@ -28,6 +28,7 @@

{{ errorMessage | translate }}

- + diff --git a/src/addons/mod/survey/components/index/addon-mod-survey-index.html b/src/addons/mod/survey/components/index/addon-mod-survey-index.html index 8c99699fc..6fd4d8906 100644 --- a/src/addons/mod/survey/components/index/addon-mod-survey-index.html +++ b/src/addons/mod/survey/components/index/addon-mod-survey-index.html @@ -131,6 +131,6 @@ - diff --git a/src/addons/mod/url/components/index/addon-mod-url-index.html b/src/addons/mod/url/components/index/addon-mod-url-index.html index 0153bcdf7..9bd9966eb 100644 --- a/src/addons/mod/url/components/index/addon-mod-url-index.html +++ b/src/addons/mod/url/components/index/addon-mod-url-index.html @@ -46,6 +46,6 @@ - diff --git a/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html b/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html index 342e83895..f7208d4ce 100644 --- a/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html +++ b/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html @@ -71,7 +71,7 @@ - diff --git a/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html b/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html index ebb5b73a3..adc5c72e4 100644 --- a/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html +++ b/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html @@ -233,6 +233,6 @@ - diff --git a/src/core/components/navigation-bar/navigation-bar.scss b/src/core/components/navigation-bar/navigation-bar.scss index 42a6491d7..6579ff761 100644 --- a/src/core/components/navigation-bar/navigation-bar.scss +++ b/src/core/components/navigation-bar/navigation-bar.scss @@ -1,7 +1,7 @@ @import "~theme/globals"; :host { - --height: var(--core-navigation-height, var(--core-navigation-max-height)); + --height: var(--core-navigation-max-height); --background: var(--core-navigation-background); --button-vertical-margin: 2px; @@ -9,20 +9,11 @@ width: 100%; background-color: var(--background); display: block; - bottom: 0; - z-index: 3; border-top: 1px solid var(--stroke); - @include core-transition(all, 200ms); - ion-button, ::ng-deep ion-button { margin-top: var(--button-vertical-margin); margin-bottom: var(--button-vertical-margin); } } - -:host-context(.core-iframe-fullscreen) { - opacity: 0 !important; - height: 0 !important; -} diff --git a/src/core/components/navigation-bar/navigation-bar.ts b/src/core/components/navigation-bar/navigation-bar.ts index 4fa88cfe6..c749dfa5f 100644 --- a/src/core/components/navigation-bar/navigation-bar.ts +++ b/src/core/components/navigation-bar/navigation-bar.ts @@ -12,18 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, Output, SimpleChange } from '@angular/core'; -import { IonContent } from '@ionic/angular'; -import { ScrollDetail } from '@ionic/core'; -import { CoreUtils } from '@services/utils/utils'; +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChange } from '@angular/core'; import { Translate } from '@singletons'; -import { CoreMath } from '@singletons/math'; /** - * Component to show a "bar" with arrows to navigate forward/backward and an slider to move around. + * Component to show a "bar" with arrows to navigate forward/backward and an progressbar to see the status. * * This directive will show two arrows at the left and right of the screen to navigate to previous/next item when clicked. - * If no previous/next item is defined, that arrow won't be shown. + * If no previous/next item is defined, that arrow will be disabled. * * Example usage: * @@ -33,7 +29,7 @@ import { CoreMath } from '@singletons/math'; templateUrl: 'core-navigation-bar.html', styleUrls: ['navigation-bar.scss'], }) -export class CoreNavigationBarComponent implements OnDestroy, OnChanges { +export class CoreNavigationBarComponent implements OnChanges { @Input() items: CoreNavigationBarItem[] = []; // List of items. @Input() previousTranslate = 'core.previous'; // Previous translatable text, can admit $a variable. @@ -52,118 +48,9 @@ export class CoreNavigationBarComponent implements OnDestroy, OnChanges { progress = 0; progressText = ''; - protected element: HTMLElement; - protected initialHeight = 0; - protected initialPaddingBottom = 0; - protected previousTop = 0; - protected previousHeight = 0; - protected stickTimeout?: number; - protected content?: HTMLIonContentElement | null; - // Function to call when arrow is clicked. Will receive as a param the item to load. @Output() action: EventEmitter = new EventEmitter(); - constructor(el: ElementRef, protected ionContent: IonContent) { - this.element = el.nativeElement; - this.element.setAttribute('slot', 'fixed'); // Just in case somebody forgets to add it. - } - - /** - * Setup scroll event listener. - * - * @param retries Number of retries left. - */ - protected async listenScrollEvents(retries = 3): Promise { - // Already initialized. - if (this.initialHeight > 0) { - return; - } - - this.initialHeight = this.element.getBoundingClientRect().height; - - if (this.initialHeight == 0 && retries > 0) { - await CoreUtils.nextTicks(50); - - this.listenScrollEvents(retries - 1); - - return; - } - // Set a minimum height value. - this.initialHeight = this.initialHeight || 48; - this.previousHeight = this.initialHeight; - - this.content = this.element.closest('ion-content'); - - if (!this.content) { - return; - } - - this.content.classList.add('has-core-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); - } - - // Set a padding to not overlap elements. - this.initialPaddingBottom = parseFloat(this.content.style.getPropertyValue('--padding-bottom') || '0'); - this.content.style.setProperty('--padding-bottom', this.initialPaddingBottom + this.initialHeight + 'px'); - const scroll = await this.content.getScrollElement(); - this.content.scrollEvents = true; - - this.setBarHeight(this.initialHeight); - this.content.addEventListener('ionScroll', (e: CustomEvent): void => { - if (!this.content) { - return; - } - - this.onScroll(e.detail, scroll); - }); - - } - - /** - * On scroll function. - * - * @param scrollDetail Scroll detail object. - * @param scrollElement Scroll element to calculate 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 { - let newHeight = this.previousHeight - (scrollDetail.scrollTop - this.previousTop); - newHeight = CoreMath.clamp(newHeight, 0, this.initialHeight); - - this.setBarHeight(newHeight); - } - this.previousTop = scrollDetail.scrollTop; - } - - /** - * Sets the bar height. - * - * @param height The new bar height. - */ - protected setBarHeight(height: number): void { - if (this.stickTimeout) { - clearTimeout(this.stickTimeout); - } - - this.element.style.opacity = height <= 0 ? '0' : '1'; - this.content?.style.setProperty('--core-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); - } - } - /** * @inheritdoc */ @@ -189,8 +76,6 @@ export class CoreNavigationBarComponent implements OnDestroy, OnChanges { if (this.previousIndex >= 0) { this.previousTitle = Translate.instant(this.previousTranslate, { $a: this.items[this.previousIndex].title || '' }); } - - this.listenScrollEvents(); } /** @@ -207,13 +92,6 @@ export class CoreNavigationBarComponent implements OnDestroy, OnChanges { this.action.emit(this.items[itemIndex].item); } - /** - * @inheritdoc - */ - async ngOnDestroy(): Promise { - this.content?.style.setProperty('--padding-bottom', this.initialPaddingBottom + 'px'); - } - } export type CoreNavigationBarItem = { diff --git a/src/core/directives/collapsible-footer.ts b/src/core/directives/collapsible-footer.ts new file mode 100644 index 000000000..71fd0b7fd --- /dev/null +++ b/src/core/directives/collapsible-footer.ts @@ -0,0 +1,158 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Directive, ElementRef, OnDestroy, OnInit } from '@angular/core'; +import { ScrollDetail } from '@ionic/core'; +import { IonContent } from '@ionic/angular'; +import { CoreUtils } from '@services/utils/utils'; +import { CoreMath } from '@singletons/math'; + +/** + * Directive to make an element fixed at the bottom collapsible when scrolling. + * + * Example usage: + * + *
+ */ +@Directive({ + selector: '[collapsible-footer]', +}) +export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy { + + protected element: HTMLElement; + protected initialHeight = 0; + protected initialPaddingBottom = 0; + protected previousTop = 0; + protected previousHeight = 0; + protected stickTimeout?: number; + protected content?: HTMLIonContentElement | null; + + constructor(el: ElementRef, protected ionContent: IonContent) { + this.element = el.nativeElement; + this.element.setAttribute('slot', 'fixed'); // Just in case somebody forgets to add it. + } + + /** + * Setup scroll event listener. + * + * @param retries Number of retries left. + */ + protected async listenScrollEvents(retries = 5): Promise { + // Already initialized. + if (this.initialHeight > 0) { + return; + } + + this.initialHeight = this.element.getBoundingClientRect().height; + + if (this.initialHeight == 0 && retries > 0) { + await CoreUtils.nextTicks(50); + + this.listenScrollEvents(retries - 1); + + return; + } + + // Set a minimum height value. + this.initialHeight = this.initialHeight || 48; + this.previousHeight = this.initialHeight; + + this.content = this.element.closest('ion-content'); + + if (!this.content) { + return; + } + + this.content.classList.add('has-collapsible-footer'); + + // 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); + } + + // Set a padding to not overlap elements. + this.initialPaddingBottom = parseFloat(this.content.style.getPropertyValue('--padding-bottom') || '0'); + this.content.style.setProperty('--padding-bottom', this.initialPaddingBottom + this.initialHeight + 'px'); + const scroll = await this.content.getScrollElement(); + this.content.scrollEvents = true; + + this.setBarHeight(this.initialHeight); + this.content.addEventListener('ionScroll', (e: CustomEvent): void => { + if (!this.content) { + return; + } + + this.onScroll(e.detail, scroll); + }); + + } + + /** + * On scroll function. + * + * @param scrollDetail Scroll detail object. + * @param scrollElement Scroll element to calculate 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 { + let newHeight = this.previousHeight - (scrollDetail.scrollTop - this.previousTop); + newHeight = CoreMath.clamp(newHeight, 0, this.initialHeight); + + this.setBarHeight(newHeight); + } + this.previousTop = scrollDetail.scrollTop; + } + + /** + * Sets the bar height. + * + * @param height The new bar height. + */ + protected setBarHeight(height: number): void { + if (this.stickTimeout) { + clearTimeout(this.stickTimeout); + } + + this.element.classList.toggle('footer-collapsed', height <= 0); + this.element.classList.toggle('footer-expanded', height >= this.initialHeight); + this.content?.style.setProperty('--core-collapsible-footer-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); + } + } + + /** + * @inheritdoc + */ + ngOnInit(): void { + this.listenScrollEvents(); + } + + /** + * @inheritdoc + */ + async ngOnDestroy(): Promise { + this.content?.style.setProperty('--padding-bottom', this.initialPaddingBottom + 'px'); + } + +} diff --git a/src/core/directives/directives.module.ts b/src/core/directives/directives.module.ts index 42a2b3c79..9f518532f 100644 --- a/src/core/directives/directives.module.ts +++ b/src/core/directives/directives.module.ts @@ -30,6 +30,7 @@ import { CoreDownloadFileDirective } from './download-file'; import { CoreCollapsibleHeaderDirective } from './collapsible-header'; import { CoreSwipeNavigationDirective } from './swipe-navigation'; import { CoreCollapsibleItemDirective } from './collapsible-item'; +import { CoreCollapsibleFooterDirective } from './collapsible-footer'; @NgModule({ declarations: [ @@ -49,6 +50,7 @@ import { CoreCollapsibleItemDirective } from './collapsible-item'; CoreCollapsibleHeaderDirective, CoreSwipeNavigationDirective, CoreCollapsibleItemDirective, + CoreCollapsibleFooterDirective, ], exports: [ CoreAutoFocusDirective, @@ -67,6 +69,7 @@ import { CoreCollapsibleItemDirective } from './collapsible-item'; CoreCollapsibleHeaderDirective, CoreSwipeNavigationDirective, CoreCollapsibleItemDirective, + CoreCollapsibleFooterDirective, ], }) export class CoreDirectivesModule {} diff --git a/src/core/features/course/components/module-navigation/module-navigation.scss b/src/core/features/course/components/module-navigation/module-navigation.scss index 48713703f..7ae45b850 100644 --- a/src/core/features/course/components/module-navigation/module-navigation.scss +++ b/src/core/features/course/components/module-navigation/module-navigation.scss @@ -1,7 +1,7 @@ @import "~theme/globals"; :host { - --height: var(--core-navigation-height, var(--core-navigation-max-height)); + --height: var(--core-navigation-max-height); --background: var(--core-navigation-background); --button-vertical-margin: 2px; @@ -9,12 +9,8 @@ width: 100%; background-color: var(--background); display: block; - bottom: 0; - z-index: 3; border-top: 1px solid var(--stroke); - @include core-transition(all, 200ms); - core-loading { text-align: center; --loading-inline-min-height: var(--height); @@ -27,11 +23,6 @@ } } -:host-context(.core-iframe-fullscreen) { - opacity: 0 !important; - height: 0 !important; -} - :host-context(core-course-format.core-course-format-singleactivity) { opacity: 0 !important; height: 0 !important; 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 5b6a4062f..af20ac47e 100644 --- a/src/core/features/course/components/module-navigation/module-navigation.ts +++ b/src/core/features/course/components/module-navigation/module-navigation.ts @@ -17,13 +17,11 @@ import { CoreCourse, CoreCourseProvider, CoreCourseWSSection } from '@features/c import { CoreCourseModuleCompletionData, CoreCourseModuleData } from '@features/course/services/course-helper'; import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate'; import { IonContent } from '@ionic/angular'; -import { ScrollDetail } from '@ionic/core'; import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; 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. @@ -51,21 +49,11 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { loaded = false; showCompletion = false; // Whether to show completion. - protected element: HTMLElement; - protected initialHeight = 0; - protected initialPaddingBottom = 0; - protected previousTop = 0; - protected previousHeight = 0; - protected stickTimeout?: number; - protected content?: HTMLIonContentElement | null; protected completionObserver: CoreEventObserver; constructor(el: ElementRef, protected ionContent: IonContent) { const siteId = CoreSites.getCurrentSiteId(); - this.element = el.nativeElement; - this.element.setAttribute('slot', 'fixed'); - this.completionObserver = CoreEvents.on(CoreEvents.COMPLETION_MODULE_VIEWED, async (data) => { if (data && data.courseId == this.courseId) { // Check if now there's a next module. @@ -88,76 +76,14 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { await this.setNextAndPreviousModules(CoreSitesReadingStrategy.PREFER_CACHE); } finally { this.loaded = true; - - await CoreUtils.nextTicks(50); - this.listenScrollEvents(); } } - /** - * Setup scroll event listener. - * - * @param retries Number of retries left. - */ - protected async listenScrollEvents(retries = 3): Promise { - this.initialHeight = this.element.getBoundingClientRect().height; - - if (this.initialHeight == 0 && retries > 0) { - await CoreUtils.nextTicks(50); - - this.listenScrollEvents(retries - 1); - - return; - } - // Set a minimum height value. - this.initialHeight = this.initialHeight || 48; - this.previousHeight = this.initialHeight; - - this.content = this.element.closest('ion-content'); - - if (!this.content) { - return; - } - - // Special case where there's no navigation. - const courseFormat = this.element.closest('core-course-format.core-course-format-singleactivity'); - if (courseFormat) { - this.element.remove(); - this.ngOnDestroy(); - - return; - } - - this.content.classList.add('has-core-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); - } - - // Set a padding to not overlap elements. - this.initialPaddingBottom = parseFloat(this.content.style.getPropertyValue('--padding-bottom') || '0'); - this.content.style.setProperty('--padding-bottom', this.initialPaddingBottom + this.initialHeight + 'px'); - const scroll = await this.content.getScrollElement(); - this.content.scrollEvents = true; - - this.setBarHeight(this.initialHeight); - this.content.addEventListener('ionScroll', (e: CustomEvent): void => { - if (!this.content) { - return; - } - - this.onScroll(e.detail, scroll); - }); - - } - /** * @inheritdoc */ async ngOnDestroy(): Promise { this.completionObserver.off(); - this.content?.style.setProperty('--padding-bottom', this.initialPaddingBottom + 'px'); } /** @@ -316,46 +242,4 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy { } } - /** - * On scroll function. - * - * @param scrollDetail Scroll detail object. - * @param scrollElement Scroll element to calculate 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 { - let newHeight = this.previousHeight - (scrollDetail.scrollTop - this.previousTop); - newHeight = CoreMath.clamp(newHeight, 0, this.initialHeight); - - this.setBarHeight(newHeight); - } - this.previousTop = scrollDetail.scrollTop; - } - - /** - * Sets the bar height. - * - * @param height The new bar height. - */ - protected setBarHeight(height: number): void { - if (this.stickTimeout) { - clearTimeout(this.stickTimeout); - } - - this.element.style.opacity = height <= 0 ? '0' : '1'; - this.content?.style.setProperty('--core-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/core/features/course/pages/module-preview/module-preview.html b/src/core/features/course/pages/module-preview/module-preview.html index 010351d87..60efcbee6 100644 --- a/src/core/features/course/pages/module-preview/module-preview.html +++ b/src/core/features/course/pages/module-preview/module-preview.html @@ -44,6 +44,6 @@ - diff --git a/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html b/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html index 94574fcbe..b47edea6c 100644 --- a/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html +++ b/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html @@ -14,5 +14,5 @@ (onLoadingContent)="contentLoading()"> - + diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index 822b5b991..8261707ae 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -1124,7 +1124,7 @@ ion-fab[core-fab] { } } -ion-content.has-core-navigation ion-fab { +ion-content.has-collapsible-footer ion-fab { bottom: calc(var(--core-navigation-height, 0px) + 10px); @include core-transition(all, 200ms); } @@ -1442,6 +1442,30 @@ ion-grid.core-no-grid > ion-row { @include collapsible-item(); } +[collapsible-footer] { + &.footer-collapsed { + --core-collapsible-footer-height: 0; + opacity: 0; + } + &.footer-expanded { + --core-collapsible-footer-height: auto; + } + + width: 100%; + bottom: 0; + z-index: 3; + height: var(--core-collapsible-footer-height, auto); + background-color: var(--core-collapsible-footer-background); + display: block; + border-top: 1px solid var(--stroke); + @include core-transition(all, 200ms); +} + +.core-iframe-fullscreen [collapsible-footer] { + opacity: 0 !important; + height: 0 !important; +} + ion-header.no-title { --core-header-toolbar-border-width: 0; --core-header-toolbar-background: transparent; diff --git a/src/theme/theme.dark.scss b/src/theme/theme.dark.scss index b5be2494e..358d341f1 100644 --- a/src/theme/theme.dark.scss +++ b/src/theme/theme.dark.scss @@ -131,6 +131,8 @@ --core-navigation-background: var(--contrast-background); + --core-collapsible-footer-background: var(--contrast-background); + --addon-messages-message-bg: var(--gray-800); --addon-messages-message-activated-bg: var(--gray-700); --addon-messages-message-note-text: var(--subdued-text-color); diff --git a/src/theme/theme.light.scss b/src/theme/theme.light.scss index 58a0cc3a5..34fa46c45 100644 --- a/src/theme/theme.light.scss +++ b/src/theme/theme.light.scss @@ -310,9 +310,11 @@ --core-courseimage-on-course-size: 72px; --core-courseimage-radius: var(--medium-radius); - --core-navigation-max-height: 48px; + --core-navigation-height: 48px; --core-navigation-background: var(--contrast-background); + --core-collapsible-footer-background: var(--contrast-background); + --core-user-menu-site-logo-max-height: 32px; --addon-calendar-today-border-color: var(--primary);