MOBILE-3833 collapsible: Change collapsible visible strategy
This commit is contained in:
		
							parent
							
								
									e3e54ec194
								
							
						
					
					
						commit
						50c3985822
					
				| @ -683,7 +683,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem | |||||||
|      * Go to search courses. |      * Go to search courses. | ||||||
|      */ |      */ | ||||||
|     async openSearch(): Promise<void> { |     async openSearch(): Promise<void> { | ||||||
|         CoreNavigator.navigateToSitePath('/list', { params : { mode: 'search' } }); |         CoreNavigator.navigateToSitePath('courses/list', { params : { mode: 'search' } }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -50,7 +50,8 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy { | |||||||
|     protected endContentScrollListener?: EventListener; |     protected endContentScrollListener?: EventListener; | ||||||
|     protected resizeListener?: CoreEventObserver; |     protected resizeListener?: CoreEventObserver; | ||||||
|     protected slotPromise?: CoreCancellablePromise<void>; |     protected slotPromise?: CoreCancellablePromise<void>; | ||||||
|     protected calcPending = false; |     protected viewportPromise?: CoreCancellablePromise<void>; | ||||||
|  |     protected loadingHeight = false; | ||||||
|     protected pageDidEnterListener?: EventListener; |     protected pageDidEnterListener?: EventListener; | ||||||
|     protected page?: HTMLElement; |     protected page?: HTMLElement; | ||||||
| 
 | 
 | ||||||
| @ -85,13 +86,14 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy { | |||||||
|      * Calculate the height of the footer. |      * Calculate the height of the footer. | ||||||
|      */ |      */ | ||||||
|     protected async calculateHeight(): Promise<void> { |     protected async calculateHeight(): Promise<void> { | ||||||
|         if (!CoreDom.isElementVisible(this.element)) { |         if (this.loadingHeight) { | ||||||
|             this.calcPending = true; |             // Already calculating, return.
 | ||||||
| 
 |  | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |         this.loadingHeight = true; | ||||||
| 
 | 
 | ||||||
|         this.calcPending = false; |         this.viewportPromise = CoreDom.waitToBeInViewport(this.element); | ||||||
|  |         await this.viewportPromise; | ||||||
| 
 | 
 | ||||||
|         this.element.classList.remove('is-active'); |         this.element.classList.remove('is-active'); | ||||||
|         await CoreUtils.nextTick(); |         await CoreUtils.nextTick(); | ||||||
| @ -110,6 +112,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy { | |||||||
|         this.element.classList.add('is-active'); |         this.element.classList.add('is-active'); | ||||||
| 
 | 
 | ||||||
|         this.setBarHeight(this.initialHeight); |         this.setBarHeight(this.initialHeight); | ||||||
|  |         this.loadingHeight = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -175,9 +178,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy { | |||||||
|         this.page?.addEventListener( |         this.page?.addEventListener( | ||||||
|             'ionViewDidEnter', |             'ionViewDidEnter', | ||||||
|             this.pageDidEnterListener = () => { |             this.pageDidEnterListener = () => { | ||||||
|                 if (this.calcPending) { |  | ||||||
|                 this.calculateHeight(); |                 this.calculateHeight(); | ||||||
|                 } |  | ||||||
|             }, |             }, | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| @ -255,6 +256,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|         this.resizeListener?.off(); |         this.resizeListener?.off(); | ||||||
|         this.slotPromise?.cancel(); |         this.slotPromise?.cancel(); | ||||||
|  |         this.viewportPromise?.cancel(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -13,7 +13,7 @@ | |||||||
| // limitations under the License.
 | // limitations under the License.
 | ||||||
| 
 | 
 | ||||||
| import { Directive, ElementRef, Input, OnChanges, OnDestroy, OnInit, SimpleChange } from '@angular/core'; | import { Directive, ElementRef, Input, OnChanges, OnDestroy, OnInit, SimpleChange } from '@angular/core'; | ||||||
| import { CorePromisedValue } from '@classes/promised-value'; | import { CoreCancellablePromise } from '@classes/cancellable-promise'; | ||||||
| import { CoreLoadingComponent } from '@components/loading/loading'; | import { CoreLoadingComponent } from '@components/loading/loading'; | ||||||
| import { CoreTabsOutletComponent } from '@components/tabs-outlet/tabs-outlet'; | import { CoreTabsOutletComponent } from '@components/tabs-outlet/tabs-outlet'; | ||||||
| import { CoreTabsComponent } from '@components/tabs/tabs'; | import { CoreTabsComponent } from '@components/tabs/tabs'; | ||||||
| @ -69,17 +69,15 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest | |||||||
|     protected content?: HTMLIonContentElement; |     protected content?: HTMLIonContentElement; | ||||||
|     protected contentScrollListener?: EventListener; |     protected contentScrollListener?: EventListener; | ||||||
|     protected endContentScrollListener?: EventListener; |     protected endContentScrollListener?: EventListener; | ||||||
|     protected pageDidEnterListener?: EventListener; |  | ||||||
|     protected resizeListener?: CoreEventObserver; |     protected resizeListener?: CoreEventObserver; | ||||||
|     protected floatingTitle?: HTMLHeadingElement; |     protected floatingTitle?: HTMLHeadingElement; | ||||||
|     protected scrollingHeight?: number; |     protected scrollingHeight?: number; | ||||||
|     protected subscriptions: Subscription[] = []; |     protected subscriptions: Subscription[] = []; | ||||||
|     protected enabled = true; |     protected enabled = true; | ||||||
|     protected isWithinContent = false; |     protected isWithinContent = false; | ||||||
|     protected enteredPromise = new CorePromisedValue<void>(); |  | ||||||
|     protected mutationObserver?: MutationObserver; |     protected mutationObserver?: MutationObserver; | ||||||
|     protected firstEnter = true; |     protected loadingFloatingTitle = false; | ||||||
|     protected initPending = false; |     protected visiblePromise?: CoreCancellablePromise<void>; | ||||||
| 
 | 
 | ||||||
|     constructor(el: ElementRef) { |     constructor(el: ElementRef) { | ||||||
|         this.collapsedHeader = el.nativeElement; |         this.collapsedHeader = el.nativeElement; | ||||||
| @ -106,10 +104,9 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest | |||||||
|         await Promise.all([ |         await Promise.all([ | ||||||
|             this.initializeCollapsedHeader(), |             this.initializeCollapsedHeader(), | ||||||
|             this.initializeExpandedHeader(), |             this.initializeExpandedHeader(), | ||||||
|             await this.enteredPromise, |  | ||||||
|         ]); |         ]); | ||||||
| 
 | 
 | ||||||
|         this.initializeFloatingTitle(); |         await this.initializeFloatingTitle(); | ||||||
|         this.initializeContent(); |         this.initializeContent(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -117,7 +114,7 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest | |||||||
|      * @inheritdoc |      * @inheritdoc | ||||||
|      */ |      */ | ||||||
|     async ngOnChanges(changes: {[name: string]: SimpleChange}): Promise<void> { |     async ngOnChanges(changes: {[name: string]: SimpleChange}): Promise<void> { | ||||||
|         if (changes.collapsible) { |         if (changes.collapsible && !changes.collapsible.firstChange) { | ||||||
|             this.collapsible = !CoreUtils.isFalseOrZero(changes.collapsible.currentValue); |             this.collapsible = !CoreUtils.isFalseOrZero(changes.collapsible.currentValue); | ||||||
|             this.enabled = this.collapsible; |             this.enabled = this.collapsible; | ||||||
| 
 | 
 | ||||||
| @ -141,47 +138,16 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest | |||||||
|         if (this.content && this.endContentScrollListener) { |         if (this.content && this.endContentScrollListener) { | ||||||
|             this.content.removeEventListener('ionScrollEnd', this.endContentScrollListener); |             this.content.removeEventListener('ionScrollEnd', this.endContentScrollListener); | ||||||
|         } |         } | ||||||
|         if (this.page && this.pageDidEnterListener) { |  | ||||||
|             this.page.removeEventListener('ionViewDidEnter', this.pageDidEnterListener); |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         this.resizeListener?.off(); |         this.resizeListener?.off(); | ||||||
|         this.mutationObserver?.disconnect(); |         this.mutationObserver?.disconnect(); | ||||||
|  |         this.visiblePromise?.cancel(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Search the page element, initialize it, and wait until it's ready for the transition to trigger on scroll. |      * Listen to changing events. | ||||||
|      */ |      */ | ||||||
|     protected initializePage(): void { |     protected listenEvents(): void { | ||||||
|         if (!this.collapsedHeader.parentElement) { |  | ||||||
|             throw new Error('[collapsible-header] Couldn\'t get page'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Find element and prepare classes.
 |  | ||||||
|         this.page = this.collapsedHeader.parentElement; |  | ||||||
|         this.page.classList.add('collapsible-header-page'); |  | ||||||
| 
 |  | ||||||
|         this.page.addEventListener( |  | ||||||
|             'ionViewDidEnter', |  | ||||||
|             this.pageDidEnterListener = () => { |  | ||||||
|                 if (this.firstEnter) { |  | ||||||
|                     this.firstEnter = false; |  | ||||||
|                     clearTimeout(timeout); |  | ||||||
|                     this.enteredPromise.resolve(); |  | ||||||
|                 } else if (this.initPending) { |  | ||||||
|                     this.initializeFloatingTitle(); |  | ||||||
|                 } |  | ||||||
|             }, |  | ||||||
|         ); |  | ||||||
| 
 |  | ||||||
|         // Timeout in case event is never fired.
 |  | ||||||
|         const timeout = window.setTimeout(() => { |  | ||||||
|             if (this.firstEnter) { |  | ||||||
|                 this.firstEnter = false; |  | ||||||
|                 this.enteredPromise.reject(new Error('[collapsible-header] Waiting for ionViewDidEnter timeout reached')); |  | ||||||
|             } |  | ||||||
|         }, 5000); |  | ||||||
| 
 |  | ||||||
|         this.resizeListener = CoreDom.onWindowResize(() => { |         this.resizeListener = CoreDom.onWindowResize(() => { | ||||||
|             this.initializeFloatingTitle(); |             this.initializeFloatingTitle(); | ||||||
|         }, 50); |         }, 50); | ||||||
| @ -216,6 +182,19 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Search the page element, initialize it, and wait until it's ready for the transition to trigger on scroll. | ||||||
|  |      */ | ||||||
|  |     protected initializePage(): void { | ||||||
|  |         if (!this.collapsedHeader.parentElement) { | ||||||
|  |             throw new Error('[collapsible-header] Couldn\'t get page'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Find element and prepare classes.
 | ||||||
|  |         this.page = this.collapsedHeader.parentElement; | ||||||
|  |         this.page.classList.add('collapsible-header-page'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Search the collapsed header element, initialize it, and wait until it's ready for the transition to trigger on scroll. |      * Search the collapsed header element, initialize it, and wait until it's ready for the transition to trigger on scroll. | ||||||
|      */ |      */ | ||||||
| @ -253,6 +232,8 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         this.listenEvents(); | ||||||
|  | 
 | ||||||
|         // Initialize from tabs.
 |         // Initialize from tabs.
 | ||||||
|         const tabs = CoreComponentsRegistry.resolve(this.page.querySelector('core-tabs-outlet'), CoreTabsOutletComponent); |         const tabs = CoreComponentsRegistry.resolve(this.page.querySelector('core-tabs-outlet'), CoreTabsOutletComponent); | ||||||
| 
 | 
 | ||||||
| @ -284,21 +265,22 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest | |||||||
|     /** |     /** | ||||||
|      * Initialize a floating title to mimic transitioning the title from one state to the other. |      * Initialize a floating title to mimic transitioning the title from one state to the other. | ||||||
|      */ |      */ | ||||||
|     protected initializeFloatingTitle(): void { |     protected async initializeFloatingTitle(): Promise<void> { | ||||||
|         if (!this.page || !this.expandedHeader) { |         if (!this.page || !this.expandedHeader) { | ||||||
|             throw new Error('[collapsible-header] Couldn\'t create floating title'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (!CoreDom.isElementVisible(this.expandedHeader)) { |  | ||||||
|             this.initPending = true; |  | ||||||
| 
 |  | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         this.initPending = false; |         if (this.loadingFloatingTitle) { | ||||||
|  |             // Already calculating, return.
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         this.loadingFloatingTitle = true; | ||||||
|  | 
 | ||||||
|  |         this.visiblePromise = CoreDom.waitToBeVisible(this.expandedHeader); | ||||||
|  |         await this.visiblePromise; | ||||||
| 
 | 
 | ||||||
|         this.page.classList.remove('collapsible-header-page-is-active'); |         this.page.classList.remove('collapsible-header-page-is-active'); | ||||||
|         CoreUtils.nextTick(); |         await CoreUtils.nextTick(); | ||||||
| 
 | 
 | ||||||
|         // Add floating title and measure initial position.
 |         // Add floating title and measure initial position.
 | ||||||
|         const collapsedHeaderTitle = this.collapsedHeader.querySelector('h1') as HTMLHeadingElement; |         const collapsedHeaderTitle = this.collapsedHeader.querySelector('h1') as HTMLHeadingElement; | ||||||
| @ -370,6 +352,8 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest | |||||||
|         this.collapsedFontStyles = collapsedFontStyles; |         this.collapsedFontStyles = collapsedFontStyles; | ||||||
|         this.expandedFontStyles = expandedFontStyles; |         this.expandedFontStyles = expandedFontStyles; | ||||||
|         this.expandedHeaderHeight = expandedHeaderHeight; |         this.expandedHeaderHeight = expandedHeaderHeight; | ||||||
|  | 
 | ||||||
|  |         this.loadingFloatingTitle = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -55,8 +55,9 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy { | |||||||
|     protected resizeListener?: CoreEventObserver; |     protected resizeListener?: CoreEventObserver; | ||||||
|     protected darkModeListener?: Subscription; |     protected darkModeListener?: Subscription; | ||||||
|     protected domPromise?: CoreCancellablePromise<void>; |     protected domPromise?: CoreCancellablePromise<void>; | ||||||
|  |     protected visiblePromise?: CoreCancellablePromise<void>; | ||||||
|     protected uniqueId: string; |     protected uniqueId: string; | ||||||
|     protected calcPending = false; |     protected loadingHeight = false; | ||||||
|     protected pageDidEnterListener?: EventListener; |     protected pageDidEnterListener?: EventListener; | ||||||
|     protected page?: HTMLElement; |     protected page?: HTMLElement; | ||||||
| 
 | 
 | ||||||
| @ -99,9 +100,7 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy { | |||||||
|         this.page?.addEventListener( |         this.page?.addEventListener( | ||||||
|             'ionViewDidEnter', |             'ionViewDidEnter', | ||||||
|             this.pageDidEnterListener = () => { |             this.pageDidEnterListener = () => { | ||||||
|                 if (this.calcPending) { |  | ||||||
|                 this.calculateHeight(); |                 this.calculateHeight(); | ||||||
|                 } |  | ||||||
|             }, |             }, | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
| @ -143,20 +142,20 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy { | |||||||
|      * Calculate the height and check if we need to display show more or not. |      * Calculate the height and check if we need to display show more or not. | ||||||
|      */ |      */ | ||||||
|     protected async calculateHeight(): Promise<void> { |     protected async calculateHeight(): Promise<void> { | ||||||
|  |         if (this.loadingHeight) { | ||||||
|  |             // Already calculating, return.
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         this.loadingHeight = true; | ||||||
|  | 
 | ||||||
|  |         this.visiblePromise = CoreDom.waitToBeVisible(this.element); | ||||||
|  |         await this.visiblePromise; | ||||||
|  | 
 | ||||||
|         // Remove max-height (if any) to calculate the real height.
 |         // Remove max-height (if any) to calculate the real height.
 | ||||||
|         this.element.classList.add('collapsible-loading-height'); |         this.element.classList.add('collapsible-loading-height'); | ||||||
| 
 | 
 | ||||||
|         await this.waitFormatTextsRendered(); |         await this.waitFormatTextsRendered(); | ||||||
| 
 | 
 | ||||||
|         if (!this.element.clientHeight) { |  | ||||||
|             this.calcPending = true; |  | ||||||
|             this.element.classList.remove('collapsible-loading-height'); |  | ||||||
| 
 |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         this.calcPending = false; |  | ||||||
| 
 |  | ||||||
|         this.expandedHeight = this.element.getBoundingClientRect().height; |         this.expandedHeight = this.element.getBoundingClientRect().height; | ||||||
| 
 | 
 | ||||||
|         // Restore the max height now.
 |         // Restore the max height now.
 | ||||||
| @ -167,6 +166,7 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy { | |||||||
|         this.setExpandButtonEnabled(enable); |         this.setExpandButtonEnabled(enable); | ||||||
|         this.setGradientColor(); |         this.setGradientColor(); | ||||||
| 
 | 
 | ||||||
|  |         this.loadingHeight = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -298,6 +298,7 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy { | |||||||
|         this.resizeListener?.off(); |         this.resizeListener?.off(); | ||||||
|         this.darkModeListener?.unsubscribe(); |         this.darkModeListener?.unsubscribe(); | ||||||
|         this.domPromise?.cancel(); |         this.domPromise?.cancel(); | ||||||
|  |         this.visiblePromise?.cancel(); | ||||||
| 
 | 
 | ||||||
|         if (this.page && this.pageDidEnterListener) { |         if (this.page && this.pageDidEnterListener) { | ||||||
|             this.page.removeEventListener('ionViewDidEnter', this.pageDidEnterListener); |             this.page.removeEventListener('ionViewDidEnter', this.pageDidEnterListener); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user