forked from CIT/Vmeda.Online
		
	MOBILE-3320 core: Add format text placeholder when loading
This commit is contained in:
		
							parent
							
								
									530ac1a109
								
							
						
					
					
						commit
						64dd36a5e2
					
				@ -72,6 +72,7 @@ export class CoreFormatTextDirective implements OnChanges {
 | 
			
		||||
    @Input() wsNotFiltered?: boolean | string; // If true it means the WS didn't filter the text for some reason.
 | 
			
		||||
    @Input() captureLinks?: boolean; // Whether links should tried to be opened inside the app. Defaults to true.
 | 
			
		||||
    @Input() openLinksInApp?: boolean; // Whether links should be opened in InAppBrowser.
 | 
			
		||||
    @Input() hideIfEmpty = false; // If true, the tag will contain nothing if text is empty.
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Max height in pixels to render the content box. It should be 50 at least to make sense.
 | 
			
		||||
@ -86,6 +87,8 @@ export class CoreFormatTextDirective implements OnChanges {
 | 
			
		||||
    protected element: HTMLElement;
 | 
			
		||||
    protected showMoreDisplayed = false;
 | 
			
		||||
    protected loadingChangedListener?: CoreEventObserver;
 | 
			
		||||
    protected emptyText = '';
 | 
			
		||||
    protected contentSpan: HTMLElement;
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
        element: ElementRef,
 | 
			
		||||
@ -93,9 +96,20 @@ export class CoreFormatTextDirective implements OnChanges {
 | 
			
		||||
        protected viewContainerRef: ViewContainerRef,
 | 
			
		||||
        protected sanitizer: DomSanitizer,
 | 
			
		||||
    ) {
 | 
			
		||||
 | 
			
		||||
        this.element = element.nativeElement;
 | 
			
		||||
        this.element.classList.add('opacity-hide'); // Hide contents until they're treated.
 | 
			
		||||
        this.element.classList.add('core-format-text-loading'); // Hide contents until they're treated.
 | 
			
		||||
 | 
			
		||||
        const placeholder = document.createElement('span');
 | 
			
		||||
        placeholder.classList.add('core-format-text-loader');
 | 
			
		||||
        this.element.appendChild(placeholder);
 | 
			
		||||
 | 
			
		||||
        this.contentSpan = document.createElement('span');
 | 
			
		||||
        this.contentSpan.classList.add('core-format-text-content');
 | 
			
		||||
        this.element.appendChild(this.contentSpan);
 | 
			
		||||
 | 
			
		||||
        this.emptyText = this.hideIfEmpty ? '' : ' ';
 | 
			
		||||
        this.contentSpan.innerHTML = this.emptyText;
 | 
			
		||||
 | 
			
		||||
        this.afterRender = new EventEmitter<void>();
 | 
			
		||||
 | 
			
		||||
        this.element.addEventListener('click', this.elementClicked.bind(this));
 | 
			
		||||
@ -183,7 +197,7 @@ export class CoreFormatTextDirective implements OnChanges {
 | 
			
		||||
     * Add magnifying glass icons to view adapted images at full size.
 | 
			
		||||
     */
 | 
			
		||||
    addMagnifyingGlasses(): void {
 | 
			
		||||
        const imgs = Array.from(this.element.querySelectorAll('.core-adapted-img-container > img'));
 | 
			
		||||
        const imgs = Array.from(this.contentSpan.querySelectorAll('.core-adapted-img-container > img'));
 | 
			
		||||
        if (!imgs.length) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@ -339,7 +353,7 @@ export class CoreFormatTextDirective implements OnChanges {
 | 
			
		||||
     */
 | 
			
		||||
    protected finishRender(): void {
 | 
			
		||||
        // Show the element again.
 | 
			
		||||
        this.element.classList.remove('opacity-hide');
 | 
			
		||||
        this.element.classList.remove('core-format-text-loading');
 | 
			
		||||
        // Emit the afterRender output.
 | 
			
		||||
        this.afterRender.emit();
 | 
			
		||||
    }
 | 
			
		||||
@ -349,7 +363,7 @@ export class CoreFormatTextDirective implements OnChanges {
 | 
			
		||||
     */
 | 
			
		||||
    protected async formatAndRenderContents(): Promise<void> {
 | 
			
		||||
        if (!this.text) {
 | 
			
		||||
            this.element.innerHTML = ''; // Remove current contents.
 | 
			
		||||
            this.contentSpan.innerHTML = this.emptyText; // Remove current contents.
 | 
			
		||||
            this.finishRender();
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
@ -370,12 +384,12 @@ export class CoreFormatTextDirective implements OnChanges {
 | 
			
		||||
        // Disable media adapt to correctly calculate the height.
 | 
			
		||||
        this.element.classList.add('core-disable-media-adapt');
 | 
			
		||||
 | 
			
		||||
        this.element.innerHTML = ''; // Remove current contents.
 | 
			
		||||
        this.contentSpan.innerHTML = ''; // Remove current contents.
 | 
			
		||||
        if (this.maxHeight && result.div.innerHTML != '' &&
 | 
			
		||||
                (this.fullOnClick || (window.innerWidth < 576 || window.innerHeight < 576))) { // Don't collapse in big screens.
 | 
			
		||||
 | 
			
		||||
            // Move the children to the current element to be able to calculate the height.
 | 
			
		||||
            CoreDomUtils.moveChildren(result.div, this.element);
 | 
			
		||||
            CoreDomUtils.moveChildren(result.div, this.contentSpan);
 | 
			
		||||
 | 
			
		||||
            // Calculate the height now.
 | 
			
		||||
            this.calculateHeight();
 | 
			
		||||
@ -396,7 +410,7 @@ export class CoreFormatTextDirective implements OnChanges {
 | 
			
		||||
                    });
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            CoreDomUtils.moveChildren(result.div, this.element);
 | 
			
		||||
            CoreDomUtils.moveChildren(result.div, this.contentSpan);
 | 
			
		||||
 | 
			
		||||
            // Add magnifying glasses to images.
 | 
			
		||||
            this.addMagnifyingGlasses();
 | 
			
		||||
@ -405,7 +419,7 @@ export class CoreFormatTextDirective implements OnChanges {
 | 
			
		||||
        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(
 | 
			
		||||
                this.element,
 | 
			
		||||
                this.contentSpan,
 | 
			
		||||
                result.filters,
 | 
			
		||||
                this.viewContainerRef,
 | 
			
		||||
                result.options,
 | 
			
		||||
 | 
			
		||||
@ -66,7 +66,7 @@ describe('CoreFormatTextDirective', () => {
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Assert
 | 
			
		||||
        const text = fixture.nativeElement.querySelector('core-format-text');
 | 
			
		||||
        const text = fixture.nativeElement.querySelector('core-format-text .core-format-text-content');
 | 
			
		||||
        expect(text).not.toBeNull();
 | 
			
		||||
        expect(text.innerHTML).toEqual(sentence);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -142,7 +142,10 @@
 | 
			
		||||
        </ion-item-divider>
 | 
			
		||||
 | 
			
		||||
        <ion-item class="ion-text-wrap" *ngIf="section.summary">
 | 
			
		||||
            <core-format-text [text]="section.summary" contextLevel="course" [contextInstanceId]="course?.id"></core-format-text>
 | 
			
		||||
            <ion-label>
 | 
			
		||||
                <core-format-text [text]="section.summary" contextLevel="course" [contextInstanceId]="course?.id">
 | 
			
		||||
                </core-format-text>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
 | 
			
		||||
        <ng-container *ngFor="let module of section.modules">
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								src/theme/bootstrap.scss
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								src/theme/bootstrap.scss
									
									
									
									
										vendored
									
									
								
							@ -9,6 +9,7 @@
 | 
			
		||||
    clip: rect(0, 0, 0, 0);
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
    border: 0;
 | 
			
		||||
    display: block !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.sr-only-focusable:active, .sr-only-focusable:focus {
 | 
			
		||||
 | 
			
		||||
@ -2,22 +2,70 @@
 | 
			
		||||
/** Styles of elements inside the directive should be placed in format-text.scss */
 | 
			
		||||
@import "~theme/globals";
 | 
			
		||||
 | 
			
		||||
:root {
 | 
			
		||||
    --background: var(--background, #{$ion-item-background});
 | 
			
		||||
    --background-gradient-rgb: var(--background-rgb, #{color-to-rgb-list($ion-item-background)});
 | 
			
		||||
    --viewer-icon-background: rgba(255, 255, 255, .5);
 | 
			
		||||
core-format-text {
 | 
			
		||||
    --core-format-text-background: var(--background, #{$ion-item-background});
 | 
			
		||||
    --core-format-text-background-gradient-rgb: var(--background-rgb, #{color-to-rgb-list($ion-item-background)});
 | 
			
		||||
    --core-format-text-viewer-icon-background: rgba(255, 255, 255, .5);
 | 
			
		||||
    --core-format-text-loader-shine: 251,251,251;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
:root body.dark {
 | 
			
		||||
    --background: var(--background, #{$ion-item-background-dark});
 | 
			
		||||
    --background-gradient-rgb: var(--background-rgb, #{color-to-rgb-list($ion-item-background-dark)});
 | 
			
		||||
    --viewer-icon-background: rgba(0, 0, 0, .5);
 | 
			
		||||
body.dark core-format-text {
 | 
			
		||||
    --core-format-text-background: var(--background, #{$ion-item-background-dark});
 | 
			
		||||
    --core-format-text-background-gradient-rgb: var(--background-rgb, #{color-to-rgb-list($ion-item-background-dark)});
 | 
			
		||||
    --core-format-text-viewer-icon-background: rgba(0, 0, 0, .5);
 | 
			
		||||
    --core-format-text-loader-shine: 90,90,90;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
core-format-text {
 | 
			
		||||
    user-select: text;
 | 
			
		||||
    word-break: break-word;
 | 
			
		||||
    word-wrap: break-word;
 | 
			
		||||
    display: contents;
 | 
			
		||||
 | 
			
		||||
    .core-format-text-loader {
 | 
			
		||||
        opacity: 0;
 | 
			
		||||
        @include core-transition(opacity, 200ms);
 | 
			
		||||
        display: contents;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.core-format-text-loading {
 | 
			
		||||
        position: relative;
 | 
			
		||||
        width: 100%;
 | 
			
		||||
        height: 100%;
 | 
			
		||||
        opacity: 1;
 | 
			
		||||
        background-color: rgba(0,0,0,.1);
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
        border-radius: 5px;
 | 
			
		||||
        display: block;
 | 
			
		||||
 | 
			
		||||
        .core-format-text-loader {
 | 
			
		||||
            position: absolute;
 | 
			
		||||
            left: -45%;
 | 
			
		||||
            height: 100%;
 | 
			
		||||
            width: 45%;
 | 
			
		||||
            background-image: -webkit-linear-gradient(to left, rgba(var(--core-format-text-loader-shine), .05), rgba(var(--core-format-text-loader-shine), .3), rgba(var(--core-format-text-loader-shine), .6), rgba(var(--core-format-text-loader-shine), .3), rgba(var(--core-format-text-loader-shine), .05));
 | 
			
		||||
            background-image: linear-gradient(to left, rgba(var(--core-format-text-loader-shine), .05), rgba(var(--core-format-text-loader-shine), .3), rgba(var(--core-format-text-loader-shine), .6), rgba(var(--core-format-text-loader-shine), .3), rgba(var(--core-format-text-loader-shine), .05));
 | 
			
		||||
            animation: loading 1s infinite;
 | 
			
		||||
            opacity: 1;
 | 
			
		||||
            display: inline;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .core-format-text-content {
 | 
			
		||||
            opacity: 0;
 | 
			
		||||
            display: inline;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .core-show-more {
 | 
			
		||||
            display: none !important;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .core-format-text-content {
 | 
			
		||||
        opacity: 1;
 | 
			
		||||
        @include core-transition(opacity, 200ms);
 | 
			
		||||
 | 
			
		||||
        display: contents;
 | 
			
		||||
        user-select: text;
 | 
			
		||||
        word-break: break-word;
 | 
			
		||||
        word-wrap: break-word;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &[maxHeight],
 | 
			
		||||
    &[ng-reflect-max-height] {
 | 
			
		||||
@ -57,7 +105,7 @@ core-format-text {
 | 
			
		||||
                    position: absolute;
 | 
			
		||||
                    @include position(null, 0, 0, null);
 | 
			
		||||
                    z-index: 7;
 | 
			
		||||
                    background-color: var(--background);
 | 
			
		||||
                    background-color: var(--core-format-text-background);
 | 
			
		||||
                    color: var(--text-color);
 | 
			
		||||
                    @include padding(null, null, null, 10px);
 | 
			
		||||
                    margin: 0;
 | 
			
		||||
@ -68,10 +116,8 @@ core-format-text {
 | 
			
		||||
                    height: 100%;
 | 
			
		||||
                    position: absolute;
 | 
			
		||||
                    @include position(null, 0, 0, 0);
 | 
			
		||||
                    background: -moz-linear-gradient(top, rgba(var(--background-gradient-rgb), 0) calc(100% - 50px), rgba(var(--background-gradient-rgb), 1) calc(100% - 15px));
 | 
			
		||||
                    background: -webkit-gradient(left top, left bottom, color-stop(calc(100% - 50px), rgba(var(--background-gradient-rgb), 0)), color-stop(calc(100% - 15px), rgba(var(--background-gradient-rgb), 1)));
 | 
			
		||||
                    background: -webkit-linear-gradient(top, rgba(var(--background-gradient-rgb), 0) calc(100% - 50px), rgba(var(--background-gradient-rgb), 1) calc(100% - 15px));
 | 
			
		||||
                    background: linear-gradient(to bottom, rgba(var(--background-gradient-rgb), 0) calc(100% - 50px), rgba(var(--background-gradient-rgb), 1) calc(100% - 15px));
 | 
			
		||||
                    background: -webkit-linear-gradient(top, rgba(var(--core-format-text-background-gradient-rgb), 0) calc(100% - 50px), rgba(var(--core-format-text-background-gradient-rgb), 1) calc(100% - 15px));
 | 
			
		||||
                    background: linear-gradient(to bottom, rgba(var(--core-format-text-background-gradient-rgb), 0) calc(100% - 50px), rgba(var(--core-format-text-background-gradient-rgb), 1) calc(100% - 15px));
 | 
			
		||||
                    z-index: 6;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -115,15 +161,15 @@ core-format-text {
 | 
			
		||||
    .core-adapted-img-container {
 | 
			
		||||
        position: relative;
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        width: 100%;
 | 
			
		||||
        max-width: 100%;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .core-image-viewer-icon {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        @include position(null, 10px, 10px, null);
 | 
			
		||||
        color: var(--black);
 | 
			
		||||
        color: var(--ion-text-color);
 | 
			
		||||
        border-radius: 5px;
 | 
			
		||||
        background-color: var(--viewer-icon-background);
 | 
			
		||||
        background-color: var(--core-format-text-viewer-icon-background);
 | 
			
		||||
        display: flex;
 | 
			
		||||
 | 
			
		||||
        width: var(--a11y-min-target-size);
 | 
			
		||||
@ -141,3 +187,13 @@ core-format-text {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@keyframes loading {
 | 
			
		||||
    0% {
 | 
			
		||||
        left: -45%;
 | 
			
		||||
    }
 | 
			
		||||
    100% {
 | 
			
		||||
        left: 100%;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user