From d8718c5eaae68dc3f48b83d10ce995746c45b1af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Fri, 19 Nov 2021 13:20:00 +0100 Subject: [PATCH] MOBILE-3810 core: Collapsible headers --- src/addons/mod/assign/pages/index/index.html | 2 +- src/addons/mod/book/pages/index/index.html | 5 +- src/addons/mod/chat/pages/index/index.html | 2 +- src/addons/mod/choice/pages/index/index.html | 2 +- src/addons/mod/data/pages/index/index.html | 2 +- .../mod/feedback/pages/index/index.html | 2 +- .../index/addon-mod-folder-index.html | 3 +- src/addons/mod/folder/pages/index/index.html | 2 +- src/addons/mod/forum/pages/index/index.html | 2 +- .../mod/glossary/pages/index/index.html | 2 +- .../mod/h5pactivity/pages/index/index.html | 2 +- src/addons/mod/imscp/pages/index/index.html | 2 +- src/addons/mod/lesson/pages/index/index.html | 5 +- src/addons/mod/lti/pages/index/index.html | 2 +- src/addons/mod/page/pages/index/index.html | 2 +- src/addons/mod/quiz/pages/index/index.html | 2 +- .../mod/resource/pages/index/index.html | 5 +- src/addons/mod/scorm/pages/index/index.html | 2 +- src/addons/mod/survey/pages/index/index.html | 2 +- src/addons/mod/url/pages/index/index.html | 2 +- src/addons/mod/wiki/components/index/index.ts | 19 +- src/addons/mod/wiki/pages/index/index.html | 6 +- src/addons/mod/wiki/pages/index/index.ts | 7 +- .../mod/workshop/pages/index/index.html | 2 +- src/core/classes/tabs.ts | 6 +- .../components/split-view/split-view.scss | 3 +- src/core/directives/collapsible-header.ts | 257 ++++++++++++++++++ src/core/directives/directives.module.ts | 3 + .../module-info/core-course-module-info.html | 6 +- .../core-course-unsupported-module.html | 6 +- .../unsupported-module.html | 2 +- src/theme/globals.mixins.scss | 12 +- src/theme/theme.base.scss | 138 +++++++--- 33 files changed, 424 insertions(+), 93 deletions(-) create mode 100644 src/core/directives/collapsible-header.ts diff --git a/src/addons/mod/assign/pages/index/index.html b/src/addons/mod/assign/pages/index/index.html index 8e1a710bc..c64cbdfdd 100644 --- a/src/addons/mod/assign/pages/index/index.html +++ b/src/addons/mod/assign/pages/index/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/addons/mod/book/pages/index/index.html b/src/addons/mod/book/pages/index/index.html index 495b0b976..2d7faf457 100644 --- a/src/addons/mod/book/pages/index/index.html +++ b/src/addons/mod/book/pages/index/index.html @@ -1,4 +1,4 @@ - + @@ -17,7 +17,6 @@ - + diff --git a/src/addons/mod/chat/pages/index/index.html b/src/addons/mod/chat/pages/index/index.html index b228480b8..de71fbf74 100644 --- a/src/addons/mod/chat/pages/index/index.html +++ b/src/addons/mod/chat/pages/index/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/addons/mod/choice/pages/index/index.html b/src/addons/mod/choice/pages/index/index.html index 9497c99fb..6f8b727cd 100644 --- a/src/addons/mod/choice/pages/index/index.html +++ b/src/addons/mod/choice/pages/index/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/addons/mod/data/pages/index/index.html b/src/addons/mod/data/pages/index/index.html index 669549eb4..5d0caa151 100644 --- a/src/addons/mod/data/pages/index/index.html +++ b/src/addons/mod/data/pages/index/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/addons/mod/feedback/pages/index/index.html b/src/addons/mod/feedback/pages/index/index.html index f7c6c173a..8eb3987ad 100644 --- a/src/addons/mod/feedback/pages/index/index.html +++ b/src/addons/mod/feedback/pages/index/index.html @@ -1,4 +1,4 @@ - + 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 e44e8a1b7..cb1093a10 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 @@ -26,9 +26,8 @@ - -

{{subfolder.filename}}

diff --git a/src/addons/mod/folder/pages/index/index.html b/src/addons/mod/folder/pages/index/index.html index 198e37e5f..a3d8d8626 100644 --- a/src/addons/mod/folder/pages/index/index.html +++ b/src/addons/mod/folder/pages/index/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/addons/mod/forum/pages/index/index.html b/src/addons/mod/forum/pages/index/index.html index c98bfc3a1..c72371b3f 100644 --- a/src/addons/mod/forum/pages/index/index.html +++ b/src/addons/mod/forum/pages/index/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/addons/mod/glossary/pages/index/index.html b/src/addons/mod/glossary/pages/index/index.html index 2183e2bf6..c1b066a21 100644 --- a/src/addons/mod/glossary/pages/index/index.html +++ b/src/addons/mod/glossary/pages/index/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/addons/mod/h5pactivity/pages/index/index.html b/src/addons/mod/h5pactivity/pages/index/index.html index 2f3d19f98..1001692cf 100644 --- a/src/addons/mod/h5pactivity/pages/index/index.html +++ b/src/addons/mod/h5pactivity/pages/index/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/addons/mod/imscp/pages/index/index.html b/src/addons/mod/imscp/pages/index/index.html index fd8b7e083..98a8c22b2 100644 --- a/src/addons/mod/imscp/pages/index/index.html +++ b/src/addons/mod/imscp/pages/index/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/addons/mod/lesson/pages/index/index.html b/src/addons/mod/lesson/pages/index/index.html index 4c5603526..ba95ea502 100644 --- a/src/addons/mod/lesson/pages/index/index.html +++ b/src/addons/mod/lesson/pages/index/index.html @@ -1,4 +1,4 @@ - + @@ -17,7 +17,6 @@ - + diff --git a/src/addons/mod/lti/pages/index/index.html b/src/addons/mod/lti/pages/index/index.html index bd9a3da32..f742d5232 100644 --- a/src/addons/mod/lti/pages/index/index.html +++ b/src/addons/mod/lti/pages/index/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/addons/mod/page/pages/index/index.html b/src/addons/mod/page/pages/index/index.html index 464a81a0b..a5db017ea 100644 --- a/src/addons/mod/page/pages/index/index.html +++ b/src/addons/mod/page/pages/index/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/addons/mod/quiz/pages/index/index.html b/src/addons/mod/quiz/pages/index/index.html index 9fcb5b5c2..2cbf6b449 100644 --- a/src/addons/mod/quiz/pages/index/index.html +++ b/src/addons/mod/quiz/pages/index/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/addons/mod/resource/pages/index/index.html b/src/addons/mod/resource/pages/index/index.html index 0a03b540b..d86648348 100644 --- a/src/addons/mod/resource/pages/index/index.html +++ b/src/addons/mod/resource/pages/index/index.html @@ -1,4 +1,4 @@ - + @@ -14,8 +14,7 @@ - diff --git a/src/addons/mod/scorm/pages/index/index.html b/src/addons/mod/scorm/pages/index/index.html index 8b0a62177..2e3a4ded7 100644 --- a/src/addons/mod/scorm/pages/index/index.html +++ b/src/addons/mod/scorm/pages/index/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/addons/mod/survey/pages/index/index.html b/src/addons/mod/survey/pages/index/index.html index e4c9e1717..8d0ffd272 100644 --- a/src/addons/mod/survey/pages/index/index.html +++ b/src/addons/mod/survey/pages/index/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/addons/mod/url/pages/index/index.html b/src/addons/mod/url/pages/index/index.html index 6b546f69f..b93cbc153 100644 --- a/src/addons/mod/url/pages/index/index.html +++ b/src/addons/mod/url/pages/index/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/addons/mod/wiki/components/index/index.ts b/src/addons/mod/wiki/components/index/index.ts index 8bbd2a3a8..1ae8303a8 100644 --- a/src/addons/mod/wiki/components/index/index.ts +++ b/src/addons/mod/wiki/components/index/index.ts @@ -135,6 +135,8 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp } if (!this.wiki) { + CoreNavigator.back(); + return; } @@ -143,7 +145,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp await AddonModWiki.logView(this.wiki.id, this.wiki.name); CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); - } catch (error) { + } catch { // Ignore errors. } } else { @@ -210,7 +212,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp /** * @inheritdoc */ - protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { + protected async fetchContent(refresh = false, sync = false, showErrors = false): Promise { try { // Get the wiki instance. this.wiki = await AddonModWiki.getWiki(this.courseId, this.module.id); @@ -219,6 +221,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp // Page not loaded yet, emit the data to update the page title. this.dataRetrieved.emit(this.wiki); } + AddonModWiki.wikiPageOpened(this.wiki.id, this.currentPath); if (sync) { @@ -299,14 +302,14 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp // No page ID but we received a title. This means we're trying to load an offline page. try { - const title = this.pageTitle || this.wiki!.firstpagetitle!; + const title = this.pageTitle || this.wiki?.firstpagetitle || ''; const offlinePage = await AddonModWikiOffline.getNewPage( title, - this.currentSubwiki!.id, - this.currentSubwiki!.wikiid, - this.currentSubwiki!.userid, - this.currentSubwiki!.groupid, + this.currentSubwiki?.id, + this.currentSubwiki?.wikiid, + this.currentSubwiki?.userid, + this.currentSubwiki?.groupid, ); this.pageIsOffline = true; @@ -321,7 +324,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp this.currentPage = data.pageId; // Stop listening for new page events. - this.newPageObserver!.off(); + this.newPageObserver?.off(); this.newPageObserver = undefined; await this.showLoadingAndFetch(true, false); diff --git a/src/addons/mod/wiki/pages/index/index.html b/src/addons/mod/wiki/pages/index/index.html index fab061635..e8db7c3b0 100644 --- a/src/addons/mod/wiki/pages/index/index.html +++ b/src/addons/mod/wiki/pages/index/index.html @@ -1,4 +1,4 @@ - + @@ -7,6 +7,10 @@ +

+ + +

diff --git a/src/addons/mod/wiki/pages/index/index.ts b/src/addons/mod/wiki/pages/index/index.ts index e98a10c9f..0d374feaa 100644 --- a/src/addons/mod/wiki/pages/index/index.ts +++ b/src/addons/mod/wiki/pages/index/index.ts @@ -47,8 +47,6 @@ export class AddonModWikiIndexPage extends CoreCourseModuleMainActivityPage + diff --git a/src/core/classes/tabs.ts b/src/core/classes/tabs.ts index d2b593398..b03b8f827 100644 --- a/src/core/classes/tabs.ts +++ b/src/core/classes/tabs.ts @@ -26,7 +26,7 @@ import { SimpleChange, } from '@angular/core'; import { IonSlides } from '@ionic/angular'; -import { BackButtonEvent } from '@ionic/core'; +import { BackButtonEvent, ScrollDetail } from '@ionic/core'; import { Subscription } from 'rxjs'; import { Platform, Translate } from '@singletons'; @@ -625,8 +625,8 @@ export class CoreTabsBaseComponent implements OnInit, Aft content.scrollEvents = true; this.scrollElements[id] = scroll; - content.addEventListener('ionScroll', (e: CustomEvent): void => { - this.showHideTabs(parseInt(e.detail.scrollTop, 10), scroll); + content.addEventListener('ionScroll', (e: CustomEvent): void => { + this.showHideTabs(e.detail.scrollTop, scroll); }); } diff --git a/src/core/components/split-view/split-view.scss b/src/core/components/split-view/split-view.scss index 1524a38c6..deb84173f 100644 --- a/src/core/components/split-view/split-view.scss +++ b/src/core/components/split-view/split-view.scss @@ -15,14 +15,13 @@ left: 0; display: flex; position: absolute; - inset: 0; flex-direction: row; flex-wrap: nowrap; contain: strict; .menu, .content-outlet { - top: 0; + top: var(--offset-top); right: 0; bottom: 0; left: 0; diff --git a/src/core/directives/collapsible-header.ts b/src/core/directives/collapsible-header.ts new file mode 100644 index 000000000..11191f7c6 --- /dev/null +++ b/src/core/directives/collapsible-header.ts @@ -0,0 +1,257 @@ +// (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 } from '@angular/core'; +import { ScrollDetail } from '@ionic/core'; +import { CoreUtils } from '@services/utils/utils'; +import { Platform } from '@singletons'; +import { CoreEventObserver, CoreEvents } from '@singletons/events'; +import { CoreMath } from '@singletons/math'; + +/** + * Directive to make ion-header collapsible. + * Ion content should have h1 tag inside. + * + * Example usage: + * + * + */ +@Directive({ + selector: 'ion-header[collapsible]', +}) +export class CoreCollapsibleHeaderDirective implements OnDestroy { + + protected scrollElement?: HTMLElement; + protected loadingObserver: CoreEventObserver; + protected content?: HTMLIonContentElement | null; + protected header: HTMLIonHeaderElement; + protected titleTopDifference = 1; + protected h1StartDifference = 0; + protected headerH1FontSize = 0; + protected contentH1FontSize = 0; + protected headerSubHeadingFontSize = 0; + protected contentSubHeadingFontSize = 0; + protected subHeadingStartDifference = 0; + + constructor(el: ElementRef) { + this.header = el.nativeElement; + + this.loadingObserver = CoreEvents.on(CoreEvents.CORE_LOADING_CHANGED, async (data) => { + const loadingId = await this.getLoadingId(); + if (loadingId && data.loaded && data.uniqueId == loadingId) { + // Remove event when loading is done. + this.loadingObserver.off(); + + // Wait to render. + await CoreUtils.nextTick(); + this.setupRealTitle(); + } + }); + } + + /** + * Gets the loading content id to wait for the loading to finish. + * + * @TODO: If no core-loading is present, load directly. Take into account content needs to be initialized. + * + * @return Promise resolved with Loading Id, if any. + */ + protected async getLoadingId(): Promise { + if (!this.content) { + this.content = this.header.parentElement?.querySelector('ion-content:not(.disable-scroll-y)'); + + if (!this.content) { + this.cannotCollapse(); + + return; + } + } + + return this.content.querySelector('core-loading .core-loading-content')?.id; + } + + /** + * Call this function when header is not collapsible. + */ + protected cannotCollapse(): void { + this.loadingObserver.off(); + this.header.classList.add('core-header-collapsed'); + } + + /** + * Gets the real title on ion content to watch scroll. + * + * @return Promise resolved when done. + */ + protected async setupRealTitle(): Promise { + + if (!this.content) { + this.cannotCollapse(); + + return; + } + + const title = this.content.querySelector('.collapsible-title, h1'); + const contentH1 = this.content.querySelector('h1'); + const headerH1 = this.header.querySelector('h1'); + if (!title || !contentH1 || !headerH1) { + this.cannotCollapse(); + + return; + } + + this.titleTopDifference = contentH1.getBoundingClientRect().top - headerH1.getBoundingClientRect().top; + + // Split view part. + const contentAux = this.header.parentElement?.querySelector('ion-content.disable-scroll-y'); + if (contentAux) { + if (contentAux.querySelector('core-split-view.menu-and-content')) { + this.cannotCollapse(); + title.remove(); + + return; + } + contentAux.style.setProperty('--offset-top', this.header.clientHeight + 'px'); + } + + const headerH1Styles = getComputedStyle(headerH1); + const contentH1Styles = getComputedStyle(contentH1); + + if (Platform.isRTL) { + this.h1StartDifference = contentH1.getBoundingClientRect().right - + (headerH1.getBoundingClientRect().right - parseFloat(headerH1Styles.paddingRight)); + } else { + this.h1StartDifference = contentH1.getBoundingClientRect().left - + (headerH1.getBoundingClientRect().left + parseFloat(headerH1Styles.paddingLeft)); + } + + this.headerH1FontSize = parseFloat(headerH1Styles.fontSize); + this.contentH1FontSize = parseFloat(contentH1Styles.fontSize); + + // Transfer font styles. + Array.from(headerH1Styles).forEach((styleName) => { + if (styleName != 'font-size' && (styleName.startsWith('font-') || styleName.startsWith('letter-'))) { + contentH1.style.setProperty(styleName, headerH1Styles.getPropertyValue(styleName)); + } + }); + contentH1.style.setProperty( + '--max-width', + (parseFloat(headerH1Styles.width) + -parseFloat(headerH1Styles.paddingLeft) + -parseFloat(headerH1Styles.paddingRight) + +'px'), + ); + + contentH1.setAttribute('aria-hidden', 'true'); + + // Add something under the hood to change the page background. + let color = getComputedStyle(title).getPropertyValue('backgroundColor').trim(); + if (color == '') { + color = getComputedStyle(title).getPropertyValue('--background').trim(); + } + + const underHeader = document.createElement('div'); + underHeader.classList.add('core-underheader'); + underHeader.style.setProperty('height', this.header.clientHeight + 'px'); + underHeader.style.setProperty('background', color); + this.content.shadowRoot?.querySelector('#background-content')?.prepend(underHeader); + + this.content.style.setProperty('--offset-top', this.header.clientHeight + 'px'); + + // Subheading. + const headerSubHeading = this.header.querySelector('h2,.subheading'); + const contentSubHeading = title.querySelector('h2,.subheading'); + if (headerSubHeading && contentSubHeading) { + const headerSubHeadingStyles = getComputedStyle(headerSubHeading); + this.headerSubHeadingFontSize = parseFloat(headerSubHeadingStyles.fontSize); + + const contentSubHeadingStyles = getComputedStyle(contentSubHeading); + this.contentSubHeadingFontSize = parseFloat(contentSubHeadingStyles.fontSize); + + if (Platform.isRTL) { + this.subHeadingStartDifference = contentSubHeading.getBoundingClientRect().right - + (headerSubHeading.getBoundingClientRect().right - parseFloat(headerSubHeadingStyles.paddingRight)); + } else { + this.subHeadingStartDifference = contentSubHeading.getBoundingClientRect().left - + (headerSubHeading.getBoundingClientRect().left + parseFloat(headerSubHeadingStyles.paddingLeft)); + } + + contentSubHeading.setAttribute('aria-hidden', 'true'); + } + + this.content.scrollEvents = true; + this.content.addEventListener('ionScroll', (e: CustomEvent): void => { + this.onScroll(title, contentH1, contentSubHeading, e.detail); + }); + } + + /** + * On scroll function. + * + * @param title Title on ion content. + * @param contentH1 Heading 1 of title, if found. + * @param scrollDetail Event details. + */ + protected onScroll( + title: HTMLElement, + contentH1: HTMLElement, + contentSubheading: HTMLElement | null, + scrollDetail: ScrollDetail, + ): void { + const progress = CoreMath.clamp(scrollDetail.scrollTop / this.titleTopDifference, 0, 1); + const collapsed = progress >= 1; + + // Check total collapse. + this.header.classList.toggle('core-header-collapsed', collapsed); + title.classList.toggle('collapsible-title-collapsed', collapsed); + title.classList.toggle('collapsible-title-collapse-started', scrollDetail.scrollTop > 0); + + if (collapsed) { + contentH1.style.transform = 'translateX(-' + this.h1StartDifference + 'px)'; + contentH1.style.setProperty('font-size', this.headerH1FontSize + 'px'); + + if (contentSubheading) { + contentSubheading.style.transform = 'translateX(-' + this.subHeadingStartDifference + 'px)'; + contentSubheading.style.setProperty('font-size', this.headerSubHeadingFontSize + 'px'); + } + + return; + } + + // Zoom font-size out. + const newFontSize = this.contentH1FontSize - ((this.contentH1FontSize - this.headerH1FontSize) * progress); + contentH1.style.setProperty('font-size', newFontSize + 'px'); + + // Move. + const newStart = - this.h1StartDifference * progress; + contentH1.style.transform = 'translateX(' + newStart + 'px)'; + + if (contentSubheading) { + const newFontSize = this.contentSubHeadingFontSize - + ((this.contentSubHeadingFontSize - this.headerSubHeadingFontSize) * progress); + contentSubheading.style.setProperty('font-size', newFontSize + 'px'); + + const newStart = - this.subHeadingStartDifference * progress; + contentSubheading.style.transform = 'translateX(' + newStart + 'px)'; + } + } + + /** + * @inheritdoc + */ + ngOnDestroy(): void { + this.loadingObserver.off(); + } + +} diff --git a/src/core/directives/directives.module.ts b/src/core/directives/directives.module.ts index 84a102748..e1d6dc959 100644 --- a/src/core/directives/directives.module.ts +++ b/src/core/directives/directives.module.ts @@ -27,6 +27,7 @@ import { CoreUserLinkDirective } from './user-link'; import { CoreAriaButtonClickDirective } from './aria-button'; import { CoreOnResizeDirective } from './on-resize'; import { CoreDownloadFileDirective } from './download-file'; +import { CoreCollapsibleHeaderDirective } from './collapsible-header'; @NgModule({ declarations: [ @@ -43,6 +44,7 @@ import { CoreDownloadFileDirective } from './download-file'; CoreAriaButtonClickDirective, CoreOnResizeDirective, CoreDownloadFileDirective, + CoreCollapsibleHeaderDirective, ], exports: [ CoreAutoFocusDirective, @@ -58,6 +60,7 @@ import { CoreDownloadFileDirective } from './download-file'; CoreAriaButtonClickDirective, CoreOnResizeDirective, CoreDownloadFileDirective, + CoreCollapsibleHeaderDirective, ], }) export class CoreDirectivesModule {} diff --git a/src/core/features/course/components/module-info/core-course-module-info.html b/src/core/features/course/components/module-info/core-course-module-info.html index a169e3604..7afb15368 100644 --- a/src/core/features/course/components/module-info/core-course-module-info.html +++ b/src/core/features/course/components/module-info/core-course-module-info.html @@ -1,12 +1,12 @@ - + -

+

-

+
diff --git a/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html b/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html index 4e27e6c44..6b7fe1848 100644 --- a/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html +++ b/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html @@ -1,7 +1,7 @@ -
- - + + +

{{ 'core.whoops' | translate }}

{{ 'core.uhoh' | translate }}

diff --git a/src/core/features/course/pages/unsupported-module/unsupported-module.html b/src/core/features/course/pages/unsupported-module/unsupported-module.html index 417963a21..c5e22195d 100644 --- a/src/core/features/course/pages/unsupported-module/unsupported-module.html +++ b/src/core/features/course/pages/unsupported-module/unsupported-module.html @@ -1,4 +1,4 @@ - + diff --git a/src/theme/globals.mixins.scss b/src/theme/globals.mixins.scss index c871f18b3..84089a4dd 100644 --- a/src/theme/globals.mixins.scss +++ b/src/theme/globals.mixins.scss @@ -23,12 +23,12 @@ // border: var(--a11y-focus-width) solid var(--a11y-focus-color); } -@mixin core-transition($where: all, $time: 500ms) { - -webkit-transition: $where $time ease-in-out; - -moz-transition: $where $time ease-in-out; - -ms-transition: $where $time ease-in-out; - -o-transition: $where $time ease-in-out; - transition: $where $time ease-in-out; +@mixin core-transition($property: all, $duration: 500ms, $timing-function: ease-in-out) { + -webkit-transition: $property $duration $timing-function; + -moz-transition: $property $duration $timing-function; + -ms-transition: $property $duration $timing-function; + -o-transition: $property $duration $timing-function; + transition: $property $duration $timing-function; } @mixin push-arrow-color($color: dedede, $flip-rtl: false) { diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index 12b4b04a1..4b9278132 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -119,45 +119,50 @@ body { } } -// Some styles taken from ion-title -ion-header h1, -ion-header h2 { - display: block; - transform: translateZ(0); - --color: initial; - color: var(--color); - margin: 0; - width: 100%; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - pointer-events: auto; +ion-header ion-title{ + h1, h2, .subheading { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + margin: 0; + } .filter_mathjaxloader_equation div { display: inline !important; } } -ion-app.md ion-header h1, -ion-app.md ion-header h2 { +ion-app.md ion-header ion-title{ @include padding(0, 20px); - font-size: 20px; - font-weight: 500; - letter-spacing: .0125em; + + h1, h2, .subheading { + font-size: 20px; + font-weight: 500; + letter-spacing: .0125em; + } + + h1 + h2, + h1 + .subheading { + font-size: 14px; + font-weight: 400; + } } -ion-app.ios ion-header h1, -ion-app.ios ion-header h2 { - @include position(0, null, null, 0); - @include padding(0, 90px, 0); +ion-app.ios ion-header ion-title { + display: flex; + flex-direction: column; + justify-content: center; - position: absolute; - text-align: center; - font-size: 17px; - font-weight: 600; - line-height: var(--core-header-toolbar-height); - box-sizing: border-box; - pointer-events: none; + h1, h2, .subheading { + font-size: 17px; + font-weight: 600; + } + + h1 + h2, + h1 + .subheading { + font-size: 14px; + font-weight: 400; + } } @@ -901,9 +906,13 @@ ion-back-button.md::part(text) { display: none; } -// Hide close button because when present is read on voice over. -ion-fab[core-fab] ion-fab-button::part(close-icon) { - display: none; +ion-fab[core-fab] { + position: fixed; + + // Hide close button because when present is read on voice over. + ion-fab-button::part(close-icon) { + display: none; + } } .core-media-adapt-width { @@ -1166,4 +1175,67 @@ iframe { ion-grid.core-no-grid > ion-row { display: block; -} \ No newline at end of file +} + +ion-header[collapsible] { + @include core-transition(all, 500ms); + + ion-title { + @include core-transition(opacity, 0ms); + } + + &:not(.core-header-collapsed) { + ion-toolbar { + --core-header-toolbar-background: rgba(255, 255, 255, 0); + --core-header-toolbar-border-width: 0; + } + + ion-title, &::after { + opacity: 0; + z-index: 0; + } + } +} + +.collapsible-title { + overflow: visible; + *, h1, h2, .subheading { + @include core-transition(all, 200ms, linear); + } + + ion-label { + overflow: visible !important; + } + + h1, h2, .subheading { + --max-width: none; + } +} + +ion-app.ios .collapsible-title h1 { + font-weight: 600; // Default heading weight. +} +ion-app.md .collapsible-title h1 { + font-weight: 500; // Default heading weight. + letter-spacing: .0125em; +} + +.collapsible-title.collapsible-title-collapsed { + ion-label, h1, h2, .subheading { + opacity: 0; + } +} + +.collapsible-title.collapsible-title-collapse-started { + * { + opacity: 0; + } + ion-label, h1, h2, .subheading { + opacity: 1; + } + h1, h2, .subheading { + max-width: var(--max-width); + white-space: nowrap; + overflow: hidden; + } +}