MOBILE-3833 loading: Improve loading spinner styles

main
Pau Ferrer Ocaña 2022-03-24 16:08:17 +01:00
parent aa4f1cdee1
commit cb63541195
3 changed files with 64 additions and 86 deletions

View File

@ -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>

View File

@ -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;
}

View File

@ -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.