forked from EVOgeek/Vmeda.Online
		
	MOBILE-3810 core: Collapsible headers
This commit is contained in:
		
							parent
							
								
									57b5266198
								
							
						
					
					
						commit
						d8718c5eaa
					
				| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
| @ -17,7 +17,6 @@ | |||||||
|         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> |         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||||
|     </ion-refresher> |     </ion-refresher> | ||||||
| 
 | 
 | ||||||
|     <addon-mod-book-index [module]="module" [courseId]="courseId" [initialChapterId]="chapterId" |     <addon-mod-book-index [module]="module" [courseId]="courseId" [initialChapterId]="chapterId" (dataRetrieved)="updateData($event)"> | ||||||
|         (dataRetrieved)="updateData($event)"> |  | ||||||
|     </addon-mod-book-index> |     </addon-mod-book-index> | ||||||
| </ion-content> | </ion-content> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -26,9 +26,8 @@ | |||||||
| <core-loading [hideUntil]="loaded"> | <core-loading [hideUntil]="loaded"> | ||||||
| 
 | 
 | ||||||
|     <!-- Activity info. --> |     <!-- Activity info. --> | ||||||
|     <core-course-module-info [module]="module" (completionChanged)="onCompletionChange()" [description]="description" |     <core-course-module-info *ngIf="!subfolder" [module]="module" (completionChanged)="onCompletionChange()" [description]="description" | ||||||
|         [component]="component" [componentId]="componentId" [courseId]="courseId"> |         [component]="component" [componentId]="componentId" [courseId]="courseId"> | ||||||
|         <h3 *ngIf="subfolder" title>{{subfolder.filename}}</h3> |  | ||||||
|     </core-course-module-info> |     </core-course-module-info> | ||||||
| 
 | 
 | ||||||
|     <ion-list *ngIf="contents && (contents.files.length + contents.folders.length > 0)"> |     <ion-list *ngIf="contents && (contents.files.length + contents.folders.length > 0)"> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
| @ -17,7 +17,6 @@ | |||||||
|         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> |         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||||
|     </ion-refresher> |     </ion-refresher> | ||||||
| 
 | 
 | ||||||
|     <addon-mod-lesson-index [module]="module" [courseId]="courseId" [group]="group" [action]="action" |     <addon-mod-lesson-index [module]="module" [courseId]="courseId" [group]="group" [action]="action" (dataRetrieved)="updateData($event)"> | ||||||
|         (dataRetrieved)="updateData($event)"> |  | ||||||
|     </addon-mod-lesson-index> |     </addon-mod-lesson-index> | ||||||
| </ion-content> | </ion-content> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
| @ -14,8 +14,7 @@ | |||||||
|     </ion-toolbar> |     </ion-toolbar> | ||||||
| </ion-header> | </ion-header> | ||||||
| <ion-content> | <ion-content> | ||||||
|     <ion-refresher slot="fixed" |     <ion-refresher slot="fixed" [disabled]="!activityComponent?.loaded || activityComponent?.mode == 'iframe'" | ||||||
|         [disabled]="!activityComponent?.loaded || activityComponent?.mode == 'iframe'" |  | ||||||
|         (ionRefresh)="activityComponent?.doRefresh($event.target)"> |         (ionRefresh)="activityComponent?.doRefresh($event.target)"> | ||||||
|         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> |         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||||
|     </ion-refresher> |     </ion-refresher> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -135,6 +135,8 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (!this.wiki) { |         if (!this.wiki) { | ||||||
|  |             CoreNavigator.back(); | ||||||
|  | 
 | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -143,7 +145,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp | |||||||
|                 await AddonModWiki.logView(this.wiki.id, this.wiki.name); |                 await AddonModWiki.logView(this.wiki.id, this.wiki.name); | ||||||
| 
 | 
 | ||||||
|                 CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); |                 CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata); | ||||||
|             } catch (error) { |             } catch { | ||||||
|                 // Ignore errors.
 |                 // Ignore errors.
 | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
| @ -210,7 +212,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp | |||||||
|     /** |     /** | ||||||
|      * @inheritdoc |      * @inheritdoc | ||||||
|      */ |      */ | ||||||
|     protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<void> { |     protected async fetchContent(refresh = false, sync = false, showErrors = false): Promise<void> { | ||||||
|         try { |         try { | ||||||
|             // Get the wiki instance.
 |             // Get the wiki instance.
 | ||||||
|             this.wiki = await AddonModWiki.getWiki(this.courseId, this.module.id); |             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.
 |                 // Page not loaded yet, emit the data to update the page title.
 | ||||||
|                 this.dataRetrieved.emit(this.wiki); |                 this.dataRetrieved.emit(this.wiki); | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|             AddonModWiki.wikiPageOpened(this.wiki.id, this.currentPath); |             AddonModWiki.wikiPageOpened(this.wiki.id, this.currentPath); | ||||||
| 
 | 
 | ||||||
|             if (sync) { |             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.
 |         // No page ID but we received a title. This means we're trying to load an offline page.
 | ||||||
|         try { |         try { | ||||||
|             const title = this.pageTitle || this.wiki!.firstpagetitle!; |             const title = this.pageTitle || this.wiki?.firstpagetitle || ''; | ||||||
| 
 | 
 | ||||||
|             const offlinePage = await AddonModWikiOffline.getNewPage( |             const offlinePage = await AddonModWikiOffline.getNewPage( | ||||||
|                 title, |                 title, | ||||||
|                 this.currentSubwiki!.id, |                 this.currentSubwiki?.id, | ||||||
|                 this.currentSubwiki!.wikiid, |                 this.currentSubwiki?.wikiid, | ||||||
|                 this.currentSubwiki!.userid, |                 this.currentSubwiki?.userid, | ||||||
|                 this.currentSubwiki!.groupid, |                 this.currentSubwiki?.groupid, | ||||||
|             ); |             ); | ||||||
| 
 | 
 | ||||||
|             this.pageIsOffline = true; |             this.pageIsOffline = true; | ||||||
| @ -321,7 +324,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp | |||||||
|                     this.currentPage = data.pageId; |                     this.currentPage = data.pageId; | ||||||
| 
 | 
 | ||||||
|                     // Stop listening for new page events.
 |                     // Stop listening for new page events.
 | ||||||
|                     this.newPageObserver!.off(); |                     this.newPageObserver?.off(); | ||||||
|                     this.newPageObserver = undefined; |                     this.newPageObserver = undefined; | ||||||
| 
 | 
 | ||||||
|                     await this.showLoadingAndFetch(true, false); |                     await this.showLoadingAndFetch(true, false); | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
| @ -7,6 +7,10 @@ | |||||||
|             <core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId"> |             <core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId"> | ||||||
|             </core-format-text> |             </core-format-text> | ||||||
|         </h1> |         </h1> | ||||||
|  |         <p> | ||||||
|  |             <core-format-text [text]="pageTitle" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId"> | ||||||
|  |             </core-format-text> | ||||||
|  |         </p> | ||||||
| 
 | 
 | ||||||
|         <ion-buttons slot="end"> |         <ion-buttons slot="end"> | ||||||
|             <!-- The buttons defined by the component will be added in here. --> |             <!-- The buttons defined by the component will be added in here. --> | ||||||
|  | |||||||
| @ -47,8 +47,6 @@ export class AddonModWikiIndexPage extends CoreCourseModuleMainActivityPage<Addo | |||||||
|         this.subwikiId = CoreNavigator.getRouteNumberParam('subwikiId'); |         this.subwikiId = CoreNavigator.getRouteNumberParam('subwikiId'); | ||||||
|         this.userId = CoreNavigator.getRouteNumberParam('userId'); |         this.userId = CoreNavigator.getRouteNumberParam('userId'); | ||||||
|         this.groupId = CoreNavigator.getRouteNumberParam('groupId'); |         this.groupId = CoreNavigator.getRouteNumberParam('groupId'); | ||||||
| 
 |  | ||||||
|         this.title = this.pageTitle || this.module.name; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -57,10 +55,9 @@ export class AddonModWikiIndexPage extends CoreCourseModuleMainActivityPage<Addo | |||||||
|     updateData(data: { name: string } | string): void { |     updateData(data: { name: string } | string): void { | ||||||
|         if (typeof data == 'string') { |         if (typeof data == 'string') { | ||||||
|             // We received the title to display.
 |             // We received the title to display.
 | ||||||
|             this.title = data; |             this.pageTitle = data; | ||||||
|         } else { |         } else { | ||||||
|             // We received a wiki instance.
 |             super.updateData(data); | ||||||
|             this.title = this.pageTitle || data.name || this.title; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -26,7 +26,7 @@ import { | |||||||
|     SimpleChange, |     SimpleChange, | ||||||
| } from '@angular/core'; | } from '@angular/core'; | ||||||
| import { IonSlides } from '@ionic/angular'; | import { IonSlides } from '@ionic/angular'; | ||||||
| import { BackButtonEvent } from '@ionic/core'; | import { BackButtonEvent, ScrollDetail } from '@ionic/core'; | ||||||
| import { Subscription } from 'rxjs'; | import { Subscription } from 'rxjs'; | ||||||
| 
 | 
 | ||||||
| import { Platform, Translate } from '@singletons'; | import { Platform, Translate } from '@singletons'; | ||||||
| @ -625,8 +625,8 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft | |||||||
| 
 | 
 | ||||||
|         content.scrollEvents = true; |         content.scrollEvents = true; | ||||||
|         this.scrollElements[id] = scroll; |         this.scrollElements[id] = scroll; | ||||||
|         content.addEventListener('ionScroll', (e: CustomEvent): void => { |         content.addEventListener('ionScroll', (e: CustomEvent<ScrollDetail>): void => { | ||||||
|             this.showHideTabs(parseInt(e.detail.scrollTop, 10), scroll); |             this.showHideTabs(e.detail.scrollTop, scroll); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -15,14 +15,13 @@ | |||||||
|     left: 0; |     left: 0; | ||||||
|     display: flex; |     display: flex; | ||||||
|     position: absolute; |     position: absolute; | ||||||
|     inset: 0; |  | ||||||
|     flex-direction: row; |     flex-direction: row; | ||||||
|     flex-wrap: nowrap; |     flex-wrap: nowrap; | ||||||
|     contain: strict; |     contain: strict; | ||||||
| 
 | 
 | ||||||
|     .menu, |     .menu, | ||||||
|     .content-outlet { |     .content-outlet { | ||||||
|         top: 0; |         top: var(--offset-top); | ||||||
|         right: 0; |         right: 0; | ||||||
|         bottom: 0; |         bottom: 0; | ||||||
|         left: 0; |         left: 0; | ||||||
|  | |||||||
							
								
								
									
										257
									
								
								src/core/directives/collapsible-header.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										257
									
								
								src/core/directives/collapsible-header.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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: | ||||||
|  |  * | ||||||
|  |  * <ion-header collapsible> | ||||||
|  |  */ | ||||||
|  | @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<string | undefined> { | ||||||
|  |         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<void> { | ||||||
|  | 
 | ||||||
|  |         if (!this.content) { | ||||||
|  |             this.cannotCollapse(); | ||||||
|  | 
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const title = this.content.querySelector<HTMLElement>('.collapsible-title, h1'); | ||||||
|  |         const contentH1 = this.content.querySelector<HTMLElement>('h1'); | ||||||
|  |         const headerH1 = this.header.querySelector<HTMLElement>('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<HTMLElement>('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<HTMLElement>('h2,.subheading'); | ||||||
|  |         const contentSubHeading = title.querySelector<HTMLElement>('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<ScrollDetail>): 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(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -27,6 +27,7 @@ import { CoreUserLinkDirective } from './user-link'; | |||||||
| import { CoreAriaButtonClickDirective } from './aria-button'; | import { CoreAriaButtonClickDirective } from './aria-button'; | ||||||
| import { CoreOnResizeDirective } from './on-resize'; | import { CoreOnResizeDirective } from './on-resize'; | ||||||
| import { CoreDownloadFileDirective } from './download-file'; | import { CoreDownloadFileDirective } from './download-file'; | ||||||
|  | import { CoreCollapsibleHeaderDirective } from './collapsible-header'; | ||||||
| 
 | 
 | ||||||
| @NgModule({ | @NgModule({ | ||||||
|     declarations: [ |     declarations: [ | ||||||
| @ -43,6 +44,7 @@ import { CoreDownloadFileDirective } from './download-file'; | |||||||
|         CoreAriaButtonClickDirective, |         CoreAriaButtonClickDirective, | ||||||
|         CoreOnResizeDirective, |         CoreOnResizeDirective, | ||||||
|         CoreDownloadFileDirective, |         CoreDownloadFileDirective, | ||||||
|  |         CoreCollapsibleHeaderDirective, | ||||||
|     ], |     ], | ||||||
|     exports: [ |     exports: [ | ||||||
|         CoreAutoFocusDirective, |         CoreAutoFocusDirective, | ||||||
| @ -58,6 +60,7 @@ import { CoreDownloadFileDirective } from './download-file'; | |||||||
|         CoreAriaButtonClickDirective, |         CoreAriaButtonClickDirective, | ||||||
|         CoreOnResizeDirective, |         CoreOnResizeDirective, | ||||||
|         CoreDownloadFileDirective, |         CoreDownloadFileDirective, | ||||||
|  |         CoreCollapsibleHeaderDirective, | ||||||
|     ], |     ], | ||||||
| }) | }) | ||||||
| export class CoreDirectivesModule {} | export class CoreDirectivesModule {} | ||||||
|  | |||||||
| @ -1,12 +1,12 @@ | |||||||
| <ion-item class="ion-text-wrap" lines="none"> | <ion-item class="ion-text-wrap collapsible-title" lines="none"> | ||||||
|     <core-mod-icon slot="start" [modicon]="modicon" [modname]="module.modname" [componentId]="module.instance"> |     <core-mod-icon slot="start" [modicon]="modicon" [modname]="module.modname" [componentId]="module.instance"> | ||||||
|     </core-mod-icon> |     </core-mod-icon> | ||||||
|     <ion-label> |     <ion-label> | ||||||
|         <h2> |         <h1> | ||||||
|             <core-format-text [text]="module.name" contextLevel="module" [component]="component" [componentId]="componentId" |             <core-format-text [text]="module.name" contextLevel="module" [component]="component" [componentId]="componentId" | ||||||
|                 [contextInstanceId]="module.id" [courseId]="courseId"> |                 [contextInstanceId]="module.id" [courseId]="courseId"> | ||||||
|             </core-format-text> |             </core-format-text> | ||||||
|         </h2> |         </h1> | ||||||
|         <ng-content select="[title]"></ng-content> |         <ng-content select="[title]"></ng-content> | ||||||
|     </ion-label> |     </ion-label> | ||||||
| </ion-item> | </ion-item> | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| <div class="ion-padding"> | <core-course-module-info [description]="module?.description" [courseId]="courseId" [module]="module"> | ||||||
|     <core-course-module-info [description]="module?.description" [courseId]="courseId" [module]="module"> | </core-course-module-info> | ||||||
|     </core-course-module-info> |  | ||||||
| 
 | 
 | ||||||
|  | <div class="ion-padding"> | ||||||
|     <h2 *ngIf="!isDisabledInSite && isSupportedByTheApp">{{ 'core.whoops' | translate }}</h2> |     <h2 *ngIf="!isDisabledInSite && isSupportedByTheApp">{{ 'core.whoops' | translate }}</h2> | ||||||
|     <h2 *ngIf="isDisabledInSite || !isSupportedByTheApp">{{ 'core.uhoh' | translate }}</h2> |     <h2 *ngIf="isDisabledInSite || !isSupportedByTheApp">{{ 'core.uhoh' | translate }}</h2> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <ion-header> | <ion-header collapsible> | ||||||
|     <ion-toolbar> |     <ion-toolbar> | ||||||
|         <ion-buttons slot="start"> |         <ion-buttons slot="start"> | ||||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> |             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||||
|  | |||||||
| @ -23,12 +23,12 @@ | |||||||
|     // border: var(--a11y-focus-width) solid var(--a11y-focus-color); |     // border: var(--a11y-focus-width) solid var(--a11y-focus-color); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @mixin core-transition($where: all, $time: 500ms) { | @mixin core-transition($property: all, $duration: 500ms, $timing-function: ease-in-out) { | ||||||
|     -webkit-transition: $where $time ease-in-out; |     -webkit-transition: $property $duration $timing-function; | ||||||
|     -moz-transition: $where $time ease-in-out; |     -moz-transition: $property $duration $timing-function; | ||||||
|     -ms-transition: $where $time ease-in-out; |     -ms-transition: $property $duration $timing-function; | ||||||
|     -o-transition: $where $time ease-in-out; |     -o-transition: $property $duration $timing-function; | ||||||
|     transition: $where $time ease-in-out; |     transition: $property $duration $timing-function; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @mixin push-arrow-color($color: dedede, $flip-rtl: false) { | @mixin push-arrow-color($color: dedede, $flip-rtl: false) { | ||||||
|  | |||||||
| @ -119,45 +119,50 @@ body { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Some styles taken from ion-title | ion-header ion-title{ | ||||||
| ion-header h1, |     h1, h2, .subheading { | ||||||
| ion-header h2 { |         text-overflow: ellipsis; | ||||||
|     display: block; |         white-space: nowrap; | ||||||
|     transform: translateZ(0); |         overflow: hidden; | ||||||
|     --color: initial; |         margin: 0; | ||||||
|     color: var(--color); |     } | ||||||
|     margin: 0; |  | ||||||
|     width: 100%; |  | ||||||
|     text-overflow: ellipsis; |  | ||||||
|     white-space: nowrap; |  | ||||||
|     overflow: hidden; |  | ||||||
|     pointer-events: auto; |  | ||||||
| 
 | 
 | ||||||
|     .filter_mathjaxloader_equation div { |     .filter_mathjaxloader_equation div { | ||||||
|         display: inline !important; |         display: inline !important; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ion-app.md ion-header h1, | ion-app.md ion-header ion-title{ | ||||||
| ion-app.md ion-header h2 { |  | ||||||
|     @include padding(0, 20px); |     @include padding(0, 20px); | ||||||
|     font-size: 20px; | 
 | ||||||
|     font-weight: 500; |     h1, h2, .subheading { | ||||||
|     letter-spacing: .0125em; |         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 ion-title { | ||||||
| ion-app.ios ion-header h2 { |     display: flex; | ||||||
|     @include position(0, null, null, 0); |     flex-direction: column; | ||||||
|     @include padding(0, 90px, 0); |     justify-content: center; | ||||||
| 
 | 
 | ||||||
|     position: absolute; |     h1, h2, .subheading { | ||||||
|     text-align: center; |         font-size: 17px; | ||||||
|     font-size: 17px; |         font-weight: 600; | ||||||
|     font-weight: 600; |     } | ||||||
|     line-height: var(--core-header-toolbar-height); | 
 | ||||||
|     box-sizing: border-box; |     h1 + h2, | ||||||
|     pointer-events: none; |     h1 + .subheading { | ||||||
|  |         font-size: 14px; | ||||||
|  |         font-weight: 400; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -901,9 +906,13 @@ ion-back-button.md::part(text) { | |||||||
|     display: none; |     display: none; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Hide close button because when present is read on voice over. | ion-fab[core-fab] { | ||||||
| ion-fab[core-fab] ion-fab-button::part(close-icon) { |     position: fixed; | ||||||
|     display: none; |      | ||||||
|  |     // Hide close button because when present is read on voice over. | ||||||
|  |     ion-fab-button::part(close-icon) { | ||||||
|  |         display: none; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .core-media-adapt-width { | .core-media-adapt-width { | ||||||
| @ -1166,4 +1175,67 @@ iframe { | |||||||
| 
 | 
 | ||||||
| ion-grid.core-no-grid > ion-row { | ion-grid.core-no-grid > ion-row { | ||||||
|     display: block; |     display: block; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | 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; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user