MOBILE-3565 components: Implement loading component

main
Dani Palou 2020-10-19 10:34:00 +02:00
parent 072c05c268
commit a6bf6afe7a
4 changed files with 204 additions and 1 deletions

View File

@ -13,18 +13,24 @@
// limitations under the License.
import { NgModule } from '@angular/core';
import { IonicModule } from '@ionic/angular';
import { CoreIconComponent } from './icon/icon';
import { CoreLoadingComponent } from './loading/loading';
import { CoreShowPasswordComponent } from './show-password/show-password';
@NgModule({
declarations: [
CoreIconComponent,
CoreLoadingComponent,
CoreShowPasswordComponent,
],
imports: [],
imports: [
IonicModule,
],
exports: [
CoreIconComponent,
CoreLoadingComponent,
CoreShowPasswordComponent,
],
})

View File

@ -0,0 +1,10 @@
<div class="core-loading-container" *ngIf="!hideUntil" role="status"> <!-- @todo [@coreShowHideAnimation] -->
<span class="core-loading-spinner">
<ion-spinner></ion-spinner>
<p class="core-loading-message" *ngIf="message" role="status">{{message}}</p>
</span>
</div>
<div #content class="core-loading-content" [id]="uniqueId" [attr.aria-busy]="hideUntil">
<ng-content *ngIf="hideUntil">
</ng-content> <!-- @todo [@coreShowHideAnimation] -->
</div>

View File

@ -0,0 +1,67 @@
ion-app.app-root {
core-loading {
// @todo @include core-transition(height, 200ms);
.core-loading-container {
width: 100%;
text-align: center;
padding-top: 10px;
clear: both;
/* @todo @include darkmode() {
color: $core-dark-text-color;
} */
}
.core-loading-content {
display: inline;
padding-bottom: 1px; /* This makes height be real */
}
&.core-loading-noheight .core-loading-content {
height: auto;
}
&.safe-area-page {
padding-left: 0 !important;
padding-right: 0 !important;
> .core-loading-content > *:not[padding],
> .core-loading-content-loading > *:not[padding] {
// @todo @include safe-area-padding-horizontal(0px, 0px);
}
}
}
.scroll-content > core-loading,
ion-content > .scroll-content > core-loading,
core-tab core-loading,
.core-loading-center {
position: static !important;
}
.scroll-content > core-loading,
ion-content > .scroll-content > core-loading,
core-tab core-loading,
.core-loading-center,
core-loading.core-loading-loaded {
position: relative;
> .core-loading-container {
position: absolute;
// @todo @include position(0, 0, 0, 0);
display: table;
height: 100%;
width: 100%;
z-index: 1;
margin: 0;
padding: 0;
clear: both;
.core-loading-spinner {
display: table-cell;
text-align: center;
vertical-align: middle;
}
}
}
}

View File

@ -0,0 +1,120 @@
// (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 { Component, Input, OnInit, OnChanges, SimpleChange, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { CoreEvents, CoreEventsProvider } from '@services/events';
import { CoreUtils } from '@services/utils/utils';
import { Translate } from '@singletons/core.singletons';
/**
* Component to show a loading spinner and message while data is being loaded.
*
* It will show a spinner with a message and hide all the content until 'hideUntil' variable is set to a truthy value (!!hideUntil).
* If 'message' isn't set, default message "Loading" is shown.
* 'message' attribute accepts hardcoded strings, variables, filters, etc. E.g. [message]="'core.loading' | translate".
*
* Usage:
* <core-loading [message]="loadingMessage" [hideUntil]="dataLoaded">
* <!-- CONTENT TO HIDE UNTIL LOADED -->
* </core-loading>
*
* IMPORTANT: Due to how ng-content works in Angular, the content of core-loading will be executed as soon as your view
* is loaded, even if the content hidden. So if you have the following code:
* <core-loading [hideUntil]="dataLoaded"><my-component></my-component></core-loading>
*
* The component "my-component" will be initialized immediately, even if dataLoaded is false, but it will be hidden. If you want
* your component to be initialized only if dataLoaded is true, then you should use ngIf:
* <core-loading [hideUntil]="dataLoaded"><my-component *ngIf="dataLoaded"></my-component></core-loading>
*/
@Component({
selector: 'core-loading',
templateUrl: 'core-loading.html',
styleUrls: ['loading.scss'],
// @todo animations: [coreShowHideAnimation],
})
export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit {
@Input() hideUntil: unknown; // Determine when should the contents be shown.
@Input() message?: string; // Message to show while loading.
@ViewChild('content') content: ElementRef;
protected uniqueId: string;
protected element: HTMLElement; // Current element.
constructor(element: ElementRef) {
this.element = element.nativeElement;
}
/**
* Component being initialized.
*/
ngOnInit(): void {
// Calculate the unique ID.
this.uniqueId = 'core-loading-content-' + CoreUtils.instance.getUniqueId('CoreLoadingComponent');
if (!this.message) {
// Default loading message.
this.message = Translate.instance.instant('core.loading');
}
}
/**
* View has been initialized.
*/
ngAfterViewInit(): void {
// Add class if loaded on init.
if (this.hideUntil) {
this.element.classList.add('core-loading-loaded');
this.content?.nativeElement.classList.add('core-loading-content');
} else {
this.content?.nativeElement.classList.remove('core-loading-content');
this.content?.nativeElement.classList.add('core-loading-content-loading');
}
}
/**
* Component input changed.
*
* @param changes Changes.
*/
ngOnChanges(changes: { [name: string]: SimpleChange }): void {
if (changes.hideUntil) {
if (this.hideUntil) {
setTimeout(() => {
// Content is loaded so, center the spinner on the content itself.
this.element.classList.add('core-loading-loaded');
setTimeout(() => {
// Change CSS to force calculate height.
this.content?.nativeElement.classList.add('core-loading-content');
this.content?.nativeElement.classList.remove('core-loading-content-loading');
}, 500);
});
} else {
this.element.classList.remove('core-loading-loaded');
this.content?.nativeElement.classList.remove('core-loading-content');
this.content?.nativeElement.classList.add('core-loading-content-loading');
}
// Trigger the event after a timeout since the elements inside ngIf haven't been added to DOM yet.
setTimeout(() => {
CoreEvents.instance.trigger(CoreEventsProvider.CORE_LOADING_CHANGED, {
loaded: !!this.hideUntil,
uniqueId: this.uniqueId,
});
});
}
}
}