forked from EVOgeek/Vmeda.Online
		
	MOBILE-3814 format-text: collapsible item directive replaces maxHeight
This commit is contained in:
		
							parent
							
								
									09cd1af733
								
							
						
					
					
						commit
						112f00bcb5
					
				| @ -8,7 +8,7 @@ | ||||
|                     {{ 'addon.mod_assign.feedbacknotsupported' | translate }} | ||||
|                 </ion-badge> | ||||
|                 <p *ngIf="text"> | ||||
|                     <core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="120" [text]="text" | ||||
|                     <core-format-text [component]="component" [componentId]="assign.cmid" [collapsible-item]="120" [text]="text" | ||||
|                         contextLevel="module" [contextInstanceId]="assign.cmid" [courseId]="assign.course"> | ||||
|                     </core-format-text> | ||||
|                 </p> | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
|                     {{ 'addon.mod_assign.submissionnotsupported' | translate }} | ||||
|                 </ion-badge> | ||||
|                 <p *ngIf="text"> | ||||
|                     <core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="120" [text]="text" | ||||
|                     <core-format-text [component]="component" [componentId]="assign.cmid" [collapsible-item]="120" [text]="text" | ||||
|                         contextLevel="module" [contextInstanceId]="assign.cmid" [courseId]="assign.course"> | ||||
|                     </core-format-text> | ||||
|                 </p> | ||||
|  | ||||
| @ -3,8 +3,8 @@ | ||||
|     <ion-label> | ||||
|         <h2>{{ plugin.name }}</h2> | ||||
|         <p> | ||||
|             <core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="120" [text]="text" contextLevel="module" | ||||
|                 [contextInstanceId]="assign.cmid" [courseId]="assign.course"> | ||||
|             <core-format-text [component]="component" [componentId]="assign.cmid" [collapsible-item]="120" [text]="text" | ||||
|                 contextLevel="module" [contextInstanceId]="assign.cmid" [courseId]="assign.course"> | ||||
|             </core-format-text> | ||||
|         </p> | ||||
|     </ion-label> | ||||
|  | ||||
| @ -4,8 +4,8 @@ | ||||
|         <h2>{{ plugin.name }}</h2> | ||||
|         <p *ngIf="words">{{ 'addon.mod_assign.numwords' | translate: {'$a': words} }}</p> | ||||
|         <p> | ||||
|             <core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="120" [text]="text" contextLevel="module" | ||||
|                 [contextInstanceId]="assign.cmid" [courseId]="assign.course"> | ||||
|             <core-format-text [component]="component" [componentId]="assign.cmid" [collapsible-item]="120" [text]="text" | ||||
|                 contextLevel="module" [contextInstanceId]="assign.cmid" [courseId]="assign.course"> | ||||
|             </core-format-text> | ||||
|         </p> | ||||
|     </ion-label> | ||||
|  | ||||
| @ -87,7 +87,7 @@ | ||||
|                         <ion-label> | ||||
|                             <h3 class="item-heading">{{ 'addon.mod_lesson.question' | translate }}</h3> | ||||
|                             <p> | ||||
|                                 <core-format-text [component]="component" [componentId]="lesson?.coursemodule" [maxHeight]="50" | ||||
|                                 <core-format-text [component]="component" [componentId]="lesson?.coursemodule" [collapsible-item]="50" | ||||
|                                     [text]="page.contents" contextLevel="module" [contextInstanceId]="lesson?.coursemodule" | ||||
|                                     [courseId]="courseId"> | ||||
|                                 </core-format-text> | ||||
|  | ||||
| @ -58,8 +58,8 @@ | ||||
|                 <ion-item class="ion-text-wrap"> | ||||
|                     <ion-label> | ||||
|                         <h2>{{ 'addon.mod_workshop.conclusion' | translate }}</h2> | ||||
|                         <core-format-text [maxHeight]="120" [component]="component" [componentId]="module.id" [text]="workshop!.conclusion" | ||||
|                             contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"> | ||||
|                         <core-format-text [collapsible-item]="120" [component]="component" [componentId]="module.id" | ||||
|                             [text]="workshop!.conclusion" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"> | ||||
|                         </core-format-text> | ||||
|                     </ion-label> | ||||
|                 </ion-item> | ||||
| @ -91,8 +91,8 @@ | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <ion-label> | ||||
|                     <h2>{{ 'addon.mod_workshop.areainstructauthors' | translate }}</h2> | ||||
|                     <core-format-text [maxHeight]="120" [component]="component" [componentId]="module.id" [text]="workshop!.instructauthors" | ||||
|                         contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"> | ||||
|                     <core-format-text [collapsible-item]="120" [component]="component" [componentId]="module.id" | ||||
|                         [text]="workshop!.instructauthors" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"> | ||||
|                     </core-format-text> | ||||
|                 </ion-label> | ||||
|             </ion-item> | ||||
| @ -141,7 +141,7 @@ | ||||
|                 <ion-item class="ion-text-wrap"> | ||||
|                     <ion-label> | ||||
|                         <h2>{{ 'addon.mod_workshop.areainstructreviewers' | translate }}</h2> | ||||
|                         <core-format-text [maxHeight]="120" [component]="component" [componentId]="module.id" | ||||
|                         <core-format-text [collapsible-item]="120" [component]="component" [componentId]="module.id" | ||||
|                             [text]="workshop!.instructreviewers" contextLevel="module" [contextInstanceId]="module.id" | ||||
|                             [courseId]="courseId"> | ||||
|                         </core-format-text> | ||||
|  | ||||
| @ -64,7 +64,7 @@ | ||||
|                 <ion-item class="ion-text-wrap"> | ||||
|                     <ion-label> | ||||
|                         <core-format-text [text]="notification.mobiletext | coreCreateLinks" contextLevel="system" [contextInstanceId]="0" | ||||
|                             [maxHeight]="120"> | ||||
|                             [collapsible-item]="120"> | ||||
|                         </core-format-text> | ||||
|                     </ion-label> | ||||
|                 </ion-item> | ||||
|  | ||||
| @ -46,6 +46,7 @@ export class CoreCollapsibleItemDirective implements OnInit { | ||||
|     protected toggleExpandEnabled = false; | ||||
|     protected expanded = false; | ||||
|     protected maxHeight = defaultMaxHeight; | ||||
|     protected expandedHeight = 0; | ||||
|     protected loadingChangedListener?: CoreEventObserver; | ||||
| 
 | ||||
|     constructor(el: ElementRef<HTMLElement>) { | ||||
| @ -72,9 +73,10 @@ export class CoreCollapsibleItemDirective implements OnInit { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this.element.classList.add('collapsible-item'); | ||||
| 
 | ||||
|         // Calculate the height now.
 | ||||
|         await this.calculateHeight(); | ||||
|         setTimeout(() => this.calculateHeight(), 200); // Try again, sometimes the first calculation is wrong.
 | ||||
| 
 | ||||
|         // Recalculate the height if a parent core-loading displays the content.
 | ||||
|         this.loadingChangedListener = | ||||
| @ -82,7 +84,6 @@ export class CoreCollapsibleItemDirective implements OnInit { | ||||
|                 if (data.loaded && CoreDomUtils.closest(this.element.parentElement, '#' + data.uniqueId)) { | ||||
|                     // The element is inside the loading, re-calculate the height.
 | ||||
|                     await this.calculateHeight(); | ||||
|                     setTimeout(() => this.calculateHeight(), 200); | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
| @ -93,9 +94,15 @@ export class CoreCollapsibleItemDirective implements OnInit { | ||||
|      * @param element Element. | ||||
|      */ | ||||
|     protected async waitFormatTextsRendered(element: Element): Promise<void> { | ||||
|         const formatTexts = Array | ||||
|             .from(element.querySelectorAll('core-format-text')) | ||||
|             .map(element => CoreComponentsRegistry.resolve(element, CoreFormatTextDirective)); | ||||
|         let formatTextElements: HTMLElement[] = []; | ||||
| 
 | ||||
|         if (this.element.tagName == 'CORE-FORMAT-TEXT') { | ||||
|             formatTextElements = [this.element]; | ||||
|         } else { | ||||
|             formatTextElements = Array.from(element.querySelectorAll('core-format-text')); | ||||
|         } | ||||
| 
 | ||||
|         const formatTexts = formatTextElements.map(element => CoreComponentsRegistry.resolve(element, CoreFormatTextDirective)); | ||||
| 
 | ||||
|         await Promise.all(formatTexts.map(formatText => formatText?.rendered())); | ||||
|     } | ||||
| @ -103,22 +110,25 @@ export class CoreCollapsibleItemDirective implements OnInit { | ||||
|     /** | ||||
|      * Calculate the height and check if we need to display show more or not. | ||||
|      */ | ||||
|     protected async calculateHeight(): Promise<void> { | ||||
|         await this.waitFormatTextsRendered(this.element); | ||||
| 
 | ||||
|     protected async calculateHeight(retries = 3): Promise<void> { | ||||
|         // Remove max-height (if any) to calculate the real height.
 | ||||
|         const initialMaxHeight = this.element.style.maxHeight; | ||||
|         this.element.style.maxHeight = 'none'; | ||||
|         this.element.classList.add('collapsible-loading-height'); | ||||
| 
 | ||||
|         await this.waitFormatTextsRendered(this.element); | ||||
| 
 | ||||
|         await CoreUtils.nextTick(); | ||||
| 
 | ||||
|         const height = CoreDomUtils.getElementHeight(this.element) || 0; | ||||
|         this.expandedHeight = CoreDomUtils.getElementHeight(this.element) || 0; | ||||
| 
 | ||||
|         // Restore the max height now.
 | ||||
|         this.element.style.maxHeight = initialMaxHeight; | ||||
|         this.element.classList.remove('collapsible-loading-height'); | ||||
| 
 | ||||
|         // If cannot calculate height, shorten always.
 | ||||
|         this.setExpandButtonEnabled(!height || height >= this.maxHeight); | ||||
|         this.setExpandButtonEnabled(!this.expandedHeight || this.expandedHeight >= this.maxHeight); | ||||
| 
 | ||||
|         if (this.expandedHeight == 0 && retries > 0) { | ||||
|             setTimeout(() => this.calculateHeight(retries - 1), 200); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -163,8 +173,11 @@ export class CoreCollapsibleItemDirective implements OnInit { | ||||
|     protected setMaxHeight(maxHeight?: number): void { | ||||
|         if (maxHeight) { | ||||
|             this.element.style.setProperty('--max-height', maxHeight + buttonHeight + 'px'); | ||||
|         } else if (this.expandedHeight) { | ||||
|             this.element.style.setProperty('--max-height', this.expandedHeight + 'px'); | ||||
|         } else { | ||||
|             this.element.style.removeProperty('--max-height'); | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -195,7 +208,7 @@ export class CoreCollapsibleItemDirective implements OnInit { | ||||
|      * | ||||
|      * @param e Click event. | ||||
|      */ | ||||
|     protected elementClicked(e: MouseEvent): void { | ||||
|     elementClicked(e: MouseEvent): void { | ||||
|         if (e.defaultPrevented) { | ||||
|             // Ignore it if the event was prevented by some other listener.
 | ||||
|             return; | ||||
|  | ||||
| @ -22,10 +22,10 @@ import { | ||||
|     SimpleChange, | ||||
|     Optional, | ||||
|     ViewContainerRef, | ||||
|     ViewChild, | ||||
| } from '@angular/core'; | ||||
| import { IonContent } from '@ionic/angular'; | ||||
| 
 | ||||
| import { CoreEventLoadingChangedData, CoreEventObserver, CoreEvents } from '@singletons/events'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreIframeUtils, CoreIframeUtilsProvider } from '@services/utils/iframe'; | ||||
| @ -40,6 +40,7 @@ import { CoreFilterDelegate } from '@features/filter/services/filter-delegate'; | ||||
| import { CoreFilterHelper } from '@features/filter/services/filter-helper'; | ||||
| import { CoreSubscriptions } from '@singletons/subscriptions'; | ||||
| import { CoreComponentsRegistry } from '@singletons/components-registry'; | ||||
| import { CoreCollapsibleItemDirective } from './collapsible-item'; | ||||
| 
 | ||||
| /** | ||||
|  * Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective | ||||
| @ -55,6 +56,8 @@ import { CoreComponentsRegistry } from '@singletons/components-registry'; | ||||
| }) | ||||
| export class CoreFormatTextDirective implements OnChanges { | ||||
| 
 | ||||
|     @ViewChild(CoreCollapsibleItemDirective) collapsible?: CoreCollapsibleItemDirective; | ||||
| 
 | ||||
|     @Input() text?: string; // The text to format.
 | ||||
|     @Input() siteId?: string; // Site ID to use.
 | ||||
|     @Input() component?: string; // Component for CoreExternalContentDirective.
 | ||||
| @ -73,23 +76,18 @@ export class CoreFormatTextDirective implements OnChanges { | ||||
|     @Input() hideIfEmpty = false; // If true, the tag will contain nothing if text is empty.
 | ||||
| 
 | ||||
|     @Input() fullOnClick?: boolean | string; // @deprecated on 4.0 Won't do anything.
 | ||||
|     @Input() fullTitle?: string; // @deprecated on 4.0 Won't do anything..
 | ||||
|     @Input() fullTitle?: string; // @deprecated on 4.0 Won't do anything.
 | ||||
|     /** | ||||
|      * Max height in pixels to render the content box. It should be 50 at least to make sense. | ||||
|      * Using this parameter will force display: block to calculate height better. | ||||
|      * If you want to avoid this use class="inline" at the same time to use display: inline-block. | ||||
|      */ | ||||
|     @Input() maxHeight?: number; | ||||
|     @Input() maxHeight?: number; // @deprecated on 4.0 Use collapsible-item directive instead.
 | ||||
| 
 | ||||
|     @Output() afterRender: EventEmitter<void>; // Called when the data is rendered.
 | ||||
|     @Output() onClick: EventEmitter<void> = new EventEmitter(); // Called when clicked.
 | ||||
| 
 | ||||
|     protected element: HTMLElement; | ||||
|     protected expanded = false; | ||||
|     protected loadingChangedListener?: CoreEventObserver; | ||||
|     protected emptyText = ''; | ||||
|     protected contentSpan: HTMLElement; | ||||
|     protected toggleExpandEnabled = false; | ||||
| 
 | ||||
|     constructor( | ||||
|         element: ElementRef, | ||||
| @ -122,8 +120,6 @@ export class CoreFormatTextDirective implements OnChanges { | ||||
|      */ | ||||
|     ngOnChanges(changes: { [name: string]: SimpleChange }): void { | ||||
|         if (changes.text || changes.filter || changes.contextLevel || changes.contextInstanceId) { | ||||
|             this.setExpandButtonEnabled(false); | ||||
| 
 | ||||
|             this.formatAndRenderContents(); | ||||
|         } | ||||
|     } | ||||
| @ -269,101 +265,6 @@ export class CoreFormatTextDirective implements OnChanges { | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Calculate the height and check if we need to display show more or not. | ||||
|      */ | ||||
|     protected async calculateHeight(): Promise<void> { | ||||
|         // @todo: Work on calculate this height better.
 | ||||
|         if (!this.maxHeight) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         await this.rendered(); | ||||
| 
 | ||||
|         // Remove max-height (if any) to calculate the real height.
 | ||||
|         const initialMaxHeight = this.element.style.maxHeight; | ||||
|         this.element.style.maxHeight = 'none'; | ||||
| 
 | ||||
|         await CoreUtils.nextTick(); | ||||
| 
 | ||||
|         const height = this.getElementHeight(this.element); | ||||
| 
 | ||||
|         // Restore the max height now.
 | ||||
|         this.element.style.maxHeight = initialMaxHeight; | ||||
| 
 | ||||
|         // If cannot calculate height, shorten always.
 | ||||
|         this.setExpandButtonEnabled(!height || height >= this.maxHeight); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Set max height to element. | ||||
|      * | ||||
|      * @param maxHeight Max height if collapsed or undefined if expanded. | ||||
|      */ | ||||
|     protected setMaxHeight(maxHeight?: number): void { | ||||
|         if (maxHeight) { | ||||
|             this.element.style.setProperty('--max-height', maxHeight + 'px'); | ||||
|         } else { | ||||
|             this.element.style.removeProperty('--max-height'); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets if expand button is enabled or not. | ||||
|      * | ||||
|      * @param enable Wether enable or disable. | ||||
|      */ | ||||
|     protected setExpandButtonEnabled(enable: boolean): void { | ||||
|         this.toggleExpandEnabled = enable; | ||||
|         this.element.classList.toggle('collapsible-enabled', enable); | ||||
| 
 | ||||
|         if (!enable || this.element.querySelector('ion-button.collapsible-toggle'))  { | ||||
|             this.setMaxHeight(!enable || this.expanded? undefined : this.maxHeight); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Add expand/collapse buttons
 | ||||
|         const toggleButton = document.createElement('ion-button'); | ||||
|         toggleButton.classList.add('collapsible-toggle'); | ||||
|         toggleButton.setAttribute('fill', 'clear'); | ||||
| 
 | ||||
|         const toggleText = document.createElement('span'); | ||||
|         toggleText.classList.add('collapsible-toggle-text'); | ||||
|         toggleText.classList.add('sr-only'); | ||||
|         toggleButton.appendChild(toggleText); | ||||
| 
 | ||||
|         const expandArrow = document.createElement('span'); | ||||
|         expandArrow.classList.add('collapsible-toggle-arrow'); | ||||
|         toggleButton.appendChild(expandArrow); | ||||
| 
 | ||||
|         this.element.appendChild(toggleButton); | ||||
| 
 | ||||
|         this.toggleExpand(this.expanded); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Expand or collapse text. | ||||
|      * | ||||
|      * @param expand Wether expand or collapse text. If undefined, will toggle. | ||||
|      */ | ||||
|     protected toggleExpand(expand?: boolean): void { | ||||
|         if (expand === undefined) { | ||||
|             expand = !this.expanded; | ||||
|         } | ||||
|         this.expanded = expand; | ||||
|         this.element.classList.toggle('collapsible-collapsed', !expand); | ||||
|         this.setMaxHeight(!expand? this.maxHeight: undefined); | ||||
| 
 | ||||
|         const toggleButton = this.element.querySelector('ion-button.collapsible-toggle'); | ||||
|         const toggleText = toggleButton?.querySelector('.collapsible-toggle-text'); | ||||
|         if (!toggleButton || !toggleText) { | ||||
|             return; | ||||
|         } | ||||
|         toggleText.innerHTML = expand ? Translate.instant('core.showless') : Translate.instant('core.showmore'); | ||||
|         toggleButton.setAttribute('aria-expanded', expand ? 'true' : 'false'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Listener to call when the element is clicked. | ||||
|      * | ||||
| @ -385,24 +286,18 @@ export class CoreFormatTextDirective implements OnChanges { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (!this.toggleExpandEnabled) { | ||||
|             // Nothing to do on click, just stop.
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         e.preventDefault(); | ||||
|         e.stopPropagation(); | ||||
| 
 | ||||
|         this.toggleExpand(); | ||||
|         this.collapsible?.elementClicked(e); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Finish the rendering, displaying the element again and calling afterRender. | ||||
|      */ | ||||
|     protected finishRender(): void { | ||||
|     protected async finishRender(): Promise<void> { | ||||
|         // Show the element again.
 | ||||
|         this.element.classList.remove('core-format-text-loading'); | ||||
| 
 | ||||
|         await CoreUtils.nextTick(); | ||||
| 
 | ||||
|         // Emit the afterRender output.
 | ||||
|         this.afterRender.emit(); | ||||
|     } | ||||
| @ -413,15 +308,12 @@ export class CoreFormatTextDirective implements OnChanges { | ||||
|     protected async formatAndRenderContents(): Promise<void> { | ||||
|         if (!this.text) { | ||||
|             this.contentSpan.innerHTML = this.emptyText; // Remove current contents.
 | ||||
|             this.finishRender(); | ||||
| 
 | ||||
|             await this.finishRender(); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // In AOT the inputs and ng-reflect aren't in the DOM sometimes. Add them so styles are applied.
 | ||||
|         if (this.maxHeight && !this.element.getAttribute('maxHeight')) { | ||||
|             this.element.setAttribute('maxHeight', String(this.maxHeight)); | ||||
|         } | ||||
|         if (!this.element.getAttribute('singleLine')) { | ||||
|             this.element.setAttribute('singleLine', String(CoreUtils.isTrueOrOne(this.singleLine))); | ||||
|         } | ||||
| @ -434,36 +326,22 @@ export class CoreFormatTextDirective implements OnChanges { | ||||
|         this.element.classList.add('core-disable-media-adapt'); | ||||
| 
 | ||||
|         this.contentSpan.innerHTML = ''; // Remove current contents.
 | ||||
|         if (this.maxHeight && result.div.innerHTML != '') { | ||||
| 
 | ||||
|             // Move the children to the current element to be able to calculate the height.
 | ||||
|             CoreDomUtils.moveChildren(result.div, this.contentSpan); | ||||
|         // Move the children to the current element to be able to calculate the height.
 | ||||
|         CoreDomUtils.moveChildren(result.div, this.contentSpan); | ||||
| 
 | ||||
|             // Calculate the height now.
 | ||||
|             this.calculateHeight(); | ||||
|             setTimeout(() => this.calculateHeight(), 200); // Try again, sometimes the first calculation is wrong.
 | ||||
|         await CoreUtils.nextTick(); | ||||
| 
 | ||||
|             // Add magnifying glasses to images.
 | ||||
|             this.addMagnifyingGlasses(); | ||||
| 
 | ||||
|             if (!this.loadingChangedListener) { | ||||
|                 // Recalculate the height if a parent core-loading displays the content.
 | ||||
|                 this.loadingChangedListener = | ||||
|                     CoreEvents.on(CoreEvents.CORE_LOADING_CHANGED, (data: CoreEventLoadingChangedData) => { | ||||
|                         if (data.loaded && CoreDomUtils.closest(this.element.parentElement, '#' + data.uniqueId)) { | ||||
|                             // The format-text is inside the loading, re-calculate the height.
 | ||||
|                             this.calculateHeight(); | ||||
|                             setTimeout(() => this.calculateHeight(), 200); | ||||
|                         } | ||||
|                     }); | ||||
|             } | ||||
|         } else { | ||||
|             CoreDomUtils.moveChildren(result.div, this.contentSpan); | ||||
| 
 | ||||
|             // Add magnifying glasses to images.
 | ||||
|             this.addMagnifyingGlasses(); | ||||
|         // Use collapsible-item directive instead.
 | ||||
|         if (this.maxHeight && !this.collapsible) { | ||||
|             this.collapsible = new CoreCollapsibleItemDirective(new ElementRef(this.element)); | ||||
|             this.collapsible.height = this.maxHeight; | ||||
|             this.collapsible.ngOnInit(); | ||||
|         } | ||||
| 
 | ||||
|         // Add magnifying glasses to images.
 | ||||
|         this.addMagnifyingGlasses(); | ||||
| 
 | ||||
|         if (result.options.filter) { | ||||
|             // Let filters handle HTML. We do it here because we don't want them to block the render of the text.
 | ||||
|             CoreFilterDelegate.handleHtml( | ||||
| @ -479,7 +357,7 @@ export class CoreFormatTextDirective implements OnChanges { | ||||
|         } | ||||
| 
 | ||||
|         this.element.classList.remove('core-disable-media-adapt'); | ||||
|         this.finishRender(); | ||||
|         await this.finishRender(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| <ion-card *ngIf="description"> | ||||
|     <ion-item class="ion-text-wrap"> | ||||
|         <ion-label> | ||||
|             <core-format-text [text]="description" [component]="component" [componentId]="componentId" [maxHeight]="120" | ||||
|             <core-format-text [text]="description" [component]="component" [componentId]="componentId" [collapsible-item]="120" | ||||
|                 [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId"> | ||||
|             </core-format-text> | ||||
|         </ion-label> | ||||
|  | ||||
| @ -46,7 +46,7 @@ | ||||
| <ion-item class="ion-text-wrap" *ngIf="description"> | ||||
|     <ion-label> | ||||
|         <core-format-text [text]="description" [component]="component" [componentId]="componentId" contextLevel="module" | ||||
|             [contextInstanceId]="module.id" [courseId]="courseId" [maxHeight]="expandDescription ? null : 120"> | ||||
|             [contextInstanceId]="module.id" [courseId]="courseId" [collapsible-item]="expandDescription ? null : 120"> | ||||
|         </core-format-text> | ||||
|     </ion-label> | ||||
| </ion-item> | ||||
|  | ||||
| @ -49,7 +49,7 @@ | ||||
|                     {{ 'core.description' | translate}} | ||||
|                 </p> | ||||
|                 <core-format-text [text]="description" [component]="component" [componentId]="componentId" contextLevel="module" | ||||
|                     [contextInstanceId]="module.id" [courseId]="courseId" [maxHeight]="120"> | ||||
|                     [contextInstanceId]="module.id" [courseId]="courseId" [collapsible-item]="120"> | ||||
|                 </core-format-text> | ||||
|             </ion-label> | ||||
|         </ion-item> | ||||
| @ -169,7 +169,7 @@ | ||||
|                             <ion-label> | ||||
|                                 <p class="item-heading">{{ 'core.grades.feedback' | translate}}</p> | ||||
|                                 <p> | ||||
|                                     <core-format-text [maxHeight]="120" [text]="grade.feedback" contextLevel="course" | ||||
|                                     <core-format-text [collapsible-item]="120" [text]="grade.feedback" contextLevel="course" | ||||
|                                         [contextInstanceId]="courseId"> | ||||
|                                     </core-format-text> | ||||
|                                 </p> | ||||
|  | ||||
| @ -72,7 +72,8 @@ | ||||
|                     <p class="item-heading"> | ||||
|                         {{'core.summary' | translate}} | ||||
|                     </p> | ||||
|                     <core-format-text [text]="course.summary" [maxHeight]="120" contextLevel="course" [contextInstanceId]="course.id"> | ||||
|                     <core-format-text [text]="course.summary" [collapsible-item]="120" contextLevel="course" | ||||
|                         [contextInstanceId]="course.id"> | ||||
|                     </core-format-text> | ||||
|                 </ion-label> | ||||
|             </ion-item> | ||||
| @ -104,7 +105,7 @@ | ||||
|                                 </core-format-text> | ||||
|                             </span><span class="core-customfieldseparator">: </span> | ||||
|                             <span class="core-customfieldvalue"> | ||||
|                                 <core-format-text [text]="field.value" [maxHeight]="120" contextLevel="course" | ||||
|                                 <core-format-text [text]="field.value" [collapsible-item]="120" contextLevel="course" | ||||
|                                     [contextInstanceId]="course.id"> | ||||
|                                 </core-format-text> | ||||
|                             </span> | ||||
|  | ||||
| @ -34,7 +34,7 @@ | ||||
|                         </core-format-text> | ||||
|                     </p> | ||||
|                     <p *ngIf="currentCategory.description"> | ||||
|                         <core-format-text [text]="currentCategory.description" [maxHeight]="120" contextLevel="coursecat" | ||||
|                         <core-format-text [text]="currentCategory.description" [collapsible-item]="120" contextLevel="coursecat" | ||||
|                             [contextInstanceId]="currentCategory.id"></core-format-text> | ||||
|                     </p> | ||||
|                 </ion-label> | ||||
|  | ||||
| @ -55,7 +55,7 @@ | ||||
|                                     </td> | ||||
|                                     <td *ngIf="column.name === 'feedback' && row.feedback !== undefined" | ||||
|                                         class="ion-text-start core-grades-table-feedback" [class.ion-hide-md-down]="column.hiddenPhone"> | ||||
|                                         <core-format-text [maxHeight]="120" [text]="row.feedback" contextLevel="course" | ||||
|                                         <core-format-text [collapsible-item]="120" [text]="row.feedback" contextLevel="course" | ||||
|                                             [contextInstanceId]="courseId"> | ||||
|                                         </core-format-text> | ||||
|                                     </td> | ||||
| @ -124,7 +124,7 @@ | ||||
|                                         <ion-label> | ||||
|                                             <h2>{{ 'core.grades.feedback' | translate}}</h2> | ||||
|                                             <p> | ||||
|                                                 <core-format-text [maxHeight]="120" [text]="row.feedback" contextLevel="course" | ||||
|                                                 <core-format-text [collapsible-item]="120" [text]="row.feedback" contextLevel="course" | ||||
|                                                     [contextInstanceId]="courseId"> | ||||
|                                                 </core-format-text> | ||||
|                                             </p> | ||||
|  | ||||
							
								
								
									
										82
									
								
								src/theme/components/collapsible-item.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/theme/components/collapsible-item.scss
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,82 @@ | ||||
| 
 | ||||
| .collapsible-item { | ||||
|     --display-toggle: none; | ||||
|     --max-height: none; | ||||
| 
 | ||||
|     &.collapsible-loading-height { | ||||
|         display: block !important; | ||||
|         height: auto !important; | ||||
|         --max-height: none !important; | ||||
|         --display-toggle: none !important; | ||||
|     } | ||||
| 
 | ||||
|     .collapsible-toggle { | ||||
|         display: var(--display-toggle); | ||||
|     } | ||||
| 
 | ||||
|     @include media-breakpoint-down(sm) { | ||||
|         &.collapsible-enabled { | ||||
|             position: relative; | ||||
|             padding-bottom: var(--collapsible-min-button-height); // So the Show less button can fit. | ||||
|             --display-toggle: block; | ||||
|             @include core-transition(height max-height, 500ms); | ||||
|             height: calc(var(--max-height, auto)); | ||||
| 
 | ||||
|             .collapsible-toggle { | ||||
|                 position: absolute; | ||||
|                 @include position (null, 0, 0, null); | ||||
|                 text-align: center; | ||||
|                 z-index: 7; | ||||
|                 text-transform: none; | ||||
|                 font-size: 14px; | ||||
|                 font-weight: normal; | ||||
|                 background-color: var(--collapsible-toggle-background); | ||||
|                 color: var(--collapsible-toggle-text); | ||||
|                 min-height: var(--a11y-min-target-size); | ||||
|                 min-width: var(--a11y-min-target-size); | ||||
|                 --border-radius: var(--huge-radius); | ||||
|                 border-radius: var(--border-radius); | ||||
|                 --padding-start: 0px; | ||||
|                 --padding-end: 0px; | ||||
|                 margin: 0px; | ||||
| 
 | ||||
|                 .collapsible-toggle-arrow { | ||||
|                     width: var(--a11y-min-target-size); | ||||
|                     height: var(--a11y-min-target-size); | ||||
| 
 | ||||
|                     background-position: center; | ||||
|                     background-repeat: no-repeat; | ||||
|                     background-size: 14px 14px; | ||||
|                     transform: rotate(-90deg); | ||||
| 
 | ||||
|                     @include core-transition(transform, 500ms); | ||||
| 
 | ||||
|                     @include push-arrow-color(626262, true); | ||||
| 
 | ||||
|                     @include darkmode() { | ||||
|                         @include push-arrow-color(ffffff, true); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             &.collapsible-collapsed { | ||||
|                 overflow: hidden; | ||||
|                 min-height: calc(var(--collapsible-min-button-height) + 12px); | ||||
| 
 | ||||
|                 .collapsible-toggle-arrow { | ||||
|                     transform: rotate(90deg); | ||||
|                 } | ||||
| 
 | ||||
|                 &:before { | ||||
|                     content: ''; | ||||
|                     height: 100%; | ||||
|                     position: absolute; | ||||
|                     @include position(null, 0, 0, 0); | ||||
|                     background: -webkit-linear-gradient(top, rgba(var(--background-gradient-rgb), 0) calc(100% - 56px), rgba(var(--background-gradient-rgb), 1) calc(100% - 5px)); | ||||
|                     background: linear-gradient(to bottom, rgba(var(--background-gradient-rgb), 0) calc(100% - 56px), rgba(var(--background-gradient-rgb), 1) calc(100% - 5px)); | ||||
|                     z-index: 6; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -48,14 +48,6 @@ core-format-text { | ||||
|             opacity: 0; | ||||
|             display: inline; | ||||
|         } | ||||
| 
 | ||||
|         .collapsible-toggle { | ||||
|             display: none !important; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .collapsible-toggle { | ||||
|         display: none; | ||||
|     } | ||||
| 
 | ||||
|     .core-format-text-content { | ||||
| @ -68,33 +60,20 @@ core-format-text { | ||||
|         word-wrap: break-word; | ||||
|     } | ||||
| 
 | ||||
|     &[maxHeight], | ||||
|     &[ng-reflect-max-height] { | ||||
|     &.collapsible-item { | ||||
|         display: block; | ||||
|         position: relative; | ||||
|         width: 100%; | ||||
|         overflow: hidden; | ||||
| 
 | ||||
|         /* Force display inline */ | ||||
|         &.inline { | ||||
|             display: inline-block; | ||||
|             width: auto; | ||||
|         } | ||||
| 
 | ||||
|         // This is to allow clicks in radio/checkbox content. | ||||
|         &.collapsible-enabled { | ||||
|             cursor: pointer; | ||||
|             pointer-events: auto; | ||||
|         cursor: pointer; | ||||
|         pointer-events: auto; | ||||
| 
 | ||||
|             @include collapsible-item(); | ||||
|         .core-format-text-content { | ||||
|             display: block; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @if ($core-format-text-never-shorten) { | ||||
|         &[maxHeight], | ||||
|         &[ng-reflect-max-height] { | ||||
|             &.collapsible-enabled.collapsible-expanded { | ||||
|                 max-height: none !important; | ||||
|         @if ($core-format-text-never-shorten) { | ||||
|             &.collapsible-enabled { | ||||
|                 --display-toggle: none !important; | ||||
|                 --max-height: none !important; | ||||
| 
 | ||||
|                 .collapsible-toggle { | ||||
|                     display: none !important; | ||||
|  | ||||
| @ -226,78 +226,6 @@ | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @mixin collapsible-item() { | ||||
|     --display-toggle: none; | ||||
|     .collapsible-toggle { | ||||
|         display: var(--display-toggle); | ||||
|     } | ||||
| 
 | ||||
|     @include media-breakpoint-down(sm) { | ||||
|         &.collapsible-enabled { | ||||
|             position:relative; | ||||
|             padding-bottom: var(--collapsible-min-button-height); // So the Show less button can fit. | ||||
|             --display-toggle: block; | ||||
| 
 | ||||
|             .collapsible-toggle { | ||||
|                 position: absolute; | ||||
|                 @include position (null, 0, 0, null); | ||||
|                 text-align: center; | ||||
|                 z-index: 7; | ||||
|                 text-transform: none; | ||||
|                 font-size: 14px; | ||||
|                 font-weight: normal; | ||||
|                 background-color: var(--collapsible-toggle-background); | ||||
|                 color: var(--collapsible-toggle-text); | ||||
|                 min-height: var(--a11y-min-target-size); | ||||
|                 min-width: var(--a11y-min-target-size); | ||||
|                 --border-radius: var(--huge-radius); | ||||
|                 border-radius: var(--border-radius); | ||||
|                 --padding-start: 0px; | ||||
|                 --padding-end: 0px; | ||||
|                 margin: 0px; | ||||
| 
 | ||||
|                 .collapsible-toggle-arrow { | ||||
|                     width: var(--a11y-min-target-size); | ||||
|                     height: var(--a11y-min-target-size); | ||||
| 
 | ||||
|                     background-position: center; | ||||
|                     background-repeat: no-repeat; | ||||
|                     background-size: 14px 14px; | ||||
|                     transform: rotate(-90deg); | ||||
| 
 | ||||
|                     @include core-transition(transform, 500ms); | ||||
| 
 | ||||
|                     @include push-arrow-color(626262, true); | ||||
| 
 | ||||
|                     @include darkmode() { | ||||
|                         @include push-arrow-color(ffffff, true); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             &.collapsible-collapsed { | ||||
|                 overflow: hidden; | ||||
|                 min-height: calc(var(--collapsible-min-button-height) + 12px); | ||||
|                 max-height: calc(var(--max-height, auto)); | ||||
| 
 | ||||
|                 .collapsible-toggle-arrow { | ||||
|                     transform: rotate(90deg); | ||||
|                 } | ||||
| 
 | ||||
|                 &:before { | ||||
|                     content: ''; | ||||
|                     height: 100%; | ||||
|                     position: absolute; | ||||
|                     @include position(null, 0, 0, 0); | ||||
|                     background: -webkit-linear-gradient(top, rgba(var(--background-gradient-rgb), 0) calc(100% - 56px), rgba(var(--background-gradient-rgb), 1) calc(100% - 5px)); | ||||
|                     background: linear-gradient(to bottom, rgba(var(--background-gradient-rgb), 0) calc(100% - 56px), rgba(var(--background-gradient-rgb), 1) calc(100% - 5px)); | ||||
|                     z-index: 6; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Color mixins. | ||||
| @function get_brightness($color) { | ||||
|     @return (red($color) + green($color) + blue($color)) / 3; | ||||
|  | ||||
| @ -1419,10 +1419,6 @@ ion-grid.core-no-grid > ion-row { | ||||
|     right: 0; | ||||
| } | ||||
| 
 | ||||
| [collapsible-item] { | ||||
|     @include collapsible-item(); | ||||
| } | ||||
| 
 | ||||
| [collapsible-footer] { | ||||
|     &.footer-collapsed { | ||||
|         --core-collapsible-footer-height: 0; | ||||
|  | ||||
| @ -20,6 +20,7 @@ | ||||
| 
 | ||||
| /* Components */ | ||||
| @import "./components/collapsible-header.scss"; | ||||
| @import "./components/collapsible-item.scss"; | ||||
| @import "./components/format-text.scss"; | ||||
| @import "./components/rubrics.scss"; | ||||
| @import "./components/mod-label.scss"; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user