MOBILE-3320 core: Add format text placeholder when loading
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">
|
||||
|
|
|
@ -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…
Reference in New Issue