MOBILE-3833 loading: Improve loading spinner styles
parent
aa4f1cdee1
commit
cb63541195
|
@ -2,8 +2,4 @@
|
||||||
<ion-spinner color="primary" aria-hidden="true"></ion-spinner>
|
<ion-spinner color="primary" aria-hidden="true"></ion-spinner>
|
||||||
<p class="core-loading-message" *ngIf="message" role="status">{{message}}</p>
|
<p class="core-loading-message" *ngIf="message" role="status">{{message}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div #content class="core-loading-content" [id]="uniqueId" [attr.aria-busy]="!hideUntil" @coreShowHideAnimation
|
<ng-content *ngIf="loaded" @coreShowHideAnimation></ng-content>
|
||||||
[class.opacity-hide]="!hideUntil">
|
|
||||||
<ng-content *ngIf="loaded">
|
|
||||||
</ng-content>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,5 +1,27 @@
|
||||||
@import "~theme/globals";
|
@import "~theme/globals";
|
||||||
|
|
||||||
|
@mixin inline() {
|
||||||
|
min-height: var(--internal-loading-inline-min-height);
|
||||||
|
max-height: 100vh; // In order show it on the page (content will be cut).
|
||||||
|
|
||||||
|
&:not(.core-loading-loaded) {
|
||||||
|
position: relative;
|
||||||
|
--contents-display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.core-loading-container {
|
||||||
|
--loading-background: rgba(var(--loading-background-inline), 0.4);
|
||||||
|
flex-direction: row;
|
||||||
|
height: auto;
|
||||||
|
width: auto;
|
||||||
|
|
||||||
|
.core-loading-message {
|
||||||
|
@include margin(0, 0, 0, 10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
--loading-background: var(--ion-background-color);
|
--loading-background: var(--ion-background-color);
|
||||||
--loading-background-inline: var(--ion-background-color-rgb);
|
--loading-background-inline: var(--ion-background-color-rgb);
|
||||||
|
@ -8,27 +30,28 @@
|
||||||
--loading-inline-margin: 0px;
|
--loading-inline-margin: 0px;
|
||||||
--loading-inline-min-height: 28px;
|
--loading-inline-min-height: 28px;
|
||||||
--internal-loading-inline-min-height: var(--loading-inline-min-height);
|
--internal-loading-inline-min-height: var(--loading-inline-min-height);
|
||||||
--content-display: contents;
|
--loading-display: flex;
|
||||||
|
--loading-display-message: block;
|
||||||
|
--contents-display: contents;
|
||||||
|
|
||||||
position: static;
|
|
||||||
color: var(--loading-text-color);
|
|
||||||
@include core-transition(all, 200ms);
|
@include core-transition(all, 200ms);
|
||||||
|
pointer-events: none;
|
||||||
&.margin {
|
display: var(--contents-display);
|
||||||
--loading-inline-margin: 10px;
|
|
||||||
--internal-loading-inline-min-height: calc(var(--loading-inline-min-height) + var(--loading-inline-margin) + var(--loading-inline-margin));
|
|
||||||
}
|
|
||||||
|
|
||||||
&.core-loading-loaded {
|
&.core-loading-loaded {
|
||||||
|
position: static;
|
||||||
|
pointer-events: auto;
|
||||||
--internal-loading-inline-min-height: 0px;
|
--internal-loading-inline-min-height: 0px;
|
||||||
}
|
|
||||||
|
|
||||||
ion-spinner {
|
&.has-spacer {
|
||||||
--color: var(--loading-spinner);
|
--contents-display: flex;
|
||||||
color: var(--color);
|
min-height: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.core-loading-container {
|
.core-loading-container {
|
||||||
|
pointer-events: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@include position(0, 0, 0, 0);
|
@include position(0, 0, 0, 0);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -36,78 +59,38 @@
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
color: var(--loading-text-color);
|
||||||
background-color: var(--loading-background);
|
background-color: var(--loading-background);
|
||||||
@include core-transition(all, 200ms);
|
@include core-transition(all, 200ms);
|
||||||
display: flex;
|
display: var(--loading-display);
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
|
||||||
|
|
||||||
.core-loading-content {
|
|
||||||
@include core-transition(opacity, 200ms);
|
|
||||||
display: var(--content-display);
|
|
||||||
}
|
|
||||||
|
|
||||||
.core-loading-message {
|
|
||||||
@include margin(10px, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.core-loading-loaded {
|
|
||||||
position: unset;
|
|
||||||
display: contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.core-loading-inline {
|
|
||||||
--loading-background: rgba(var(--loading-background-inline), 0.5);
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
min-height: var(--internal-loading-inline-min-height);
|
|
||||||
|
|
||||||
.core-loading-message {
|
.core-loading-message {
|
||||||
@include margin(0, 0, 0, 10px);
|
@include margin(10px, 0, 0, 0);
|
||||||
|
display: var(--loading-display-message);
|
||||||
}
|
}
|
||||||
|
|
||||||
.core-loading-container {
|
ion-spinner {
|
||||||
flex-direction: row;
|
--color: var(--loading-spinner);
|
||||||
|
color: var(--color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.core-loading-full-height .core-loading-content {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.has-spacer {
|
|
||||||
--content-display: flex;
|
|
||||||
|
|
||||||
.core-loading-content {
|
|
||||||
min-height: 100%;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.list-item-limited-width .core-loading-content {
|
|
||||||
max-width: var(--list-item-max-width);
|
|
||||||
margin-left: auto !important;
|
|
||||||
margin-right: auto !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.safe-area-padding:not(.core-loading-inline) .core-loading-content,
|
|
||||||
&.safe-area-padding-horizontal:not(.core-loading-inline) .core-loading-content {
|
|
||||||
@include safe-area-padding-horizontal(0px, 0px);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.safe-area-padding:not(.core-loading-inline) .core-loading-content {
|
|
||||||
padding-bottom: var(--ion-safe-area-bottom);
|
|
||||||
> * {
|
|
||||||
--ion-safe-area-bottom: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:host-context(ion-item) {
|
|
||||||
&.core-loading-inline {
|
&.core-loading-inline {
|
||||||
position: static;
|
@include inline();
|
||||||
display: block;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force inline on some contexts.
|
||||||
|
:host-context(ion-item),
|
||||||
|
:host-context(core-block) {
|
||||||
|
// Implicit Inline.
|
||||||
|
@include inline();
|
||||||
|
}
|
||||||
|
|
||||||
|
:host-context(.limited-width > ):not([slot]) {
|
||||||
|
--contents-display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, Input, OnInit, OnChanges, SimpleChange, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
|
import { Component, Input, OnInit, OnChanges, SimpleChange, ElementRef, AfterViewInit } from '@angular/core';
|
||||||
|
|
||||||
import { CoreEventLoadingChangedData, CoreEvents } from '@singletons/events';
|
import { CoreEventLoadingChangedData, CoreEvents } from '@singletons/events';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
@ -50,15 +50,14 @@ import { AsyncComponent } from '@classes/async-component';
|
||||||
})
|
})
|
||||||
export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, AsyncComponent {
|
export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, AsyncComponent {
|
||||||
|
|
||||||
@Input() hideUntil: unknown; // Determine when should the contents be shown.
|
@Input() hideUntil = false; // Determine when should the contents be shown.
|
||||||
@Input() message?: string; // Message to show while loading.
|
@Input() message?: string; // Message to show while loading.
|
||||||
@Input() fullscreen = true; // Use the whole screen.
|
@Input() fullscreen = true; // Use the whole screen.
|
||||||
|
|
||||||
@ViewChild('content') content?: ElementRef;
|
|
||||||
|
|
||||||
uniqueId: string;
|
uniqueId: string;
|
||||||
protected element: HTMLElement; // Current element.
|
|
||||||
loaded = false; // Only comes true once.
|
loaded = false; // Only comes true once.
|
||||||
|
|
||||||
|
protected element: HTMLElement; // Current element.
|
||||||
protected onReadyPromise = new CorePromisedValue<void>();
|
protected onReadyPromise = new CorePromisedValue<void>();
|
||||||
|
|
||||||
constructor(element: ElementRef) {
|
constructor(element: ElementRef) {
|
||||||
|
@ -67,6 +66,7 @@ export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, A
|
||||||
|
|
||||||
// Calculate the unique ID.
|
// Calculate the unique ID.
|
||||||
this.uniqueId = 'core-loading-content-' + CoreUtils.getUniqueId('CoreLoadingComponent');
|
this.uniqueId = 'core-loading-content-' + CoreUtils.getUniqueId('CoreLoadingComponent');
|
||||||
|
this.element.setAttribute('id', this.uniqueId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,7 +77,6 @@ export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, A
|
||||||
// Default loading message.
|
// Default loading message.
|
||||||
this.message = Translate.instant('core.loading');
|
this.message = Translate.instant('core.loading');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.element.classList.toggle('core-loading-inline', !this.fullscreen);
|
this.element.classList.toggle('core-loading-inline', !this.fullscreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +84,7 @@ export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, A
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
this.changeState(!!this.hideUntil);
|
this.changeState(this.hideUntil);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,7 +92,7 @@ export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, A
|
||||||
*/
|
*/
|
||||||
ngOnChanges(changes: { [name: string]: SimpleChange }): void {
|
ngOnChanges(changes: { [name: string]: SimpleChange }): void {
|
||||||
if (changes.hideUntil) {
|
if (changes.hideUntil) {
|
||||||
this.changeState(!!this.hideUntil);
|
this.changeState(this.hideUntil);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +104,7 @@ export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, A
|
||||||
*/
|
*/
|
||||||
async changeState(loaded: boolean): Promise<void> {
|
async changeState(loaded: boolean): Promise<void> {
|
||||||
this.element.classList.toggle('core-loading-loaded', loaded);
|
this.element.classList.toggle('core-loading-loaded', loaded);
|
||||||
this.content?.nativeElement.classList.toggle('core-loading-content', loaded);
|
this.element.setAttribute('aria-busy', loaded ? 'false' : 'true');
|
||||||
|
|
||||||
if (!this.loaded && loaded) {
|
if (!this.loaded && loaded) {
|
||||||
this.loaded = true; // Only comes true once.
|
this.loaded = true; // Only comes true once.
|
||||||
|
|
Loading…
Reference in New Issue