Skeleton loader WIP

This commit is contained in:
Adina Țeudan 2022-11-29 03:29:44 +02:00
parent cb3f1e3eea
commit 6702aea4e3
10 changed files with 121 additions and 17 deletions

View File

@ -42,3 +42,23 @@ $sides: (top, bottom, left, right);
flex: #{$n};
}
}
/* SKELETON LINES */
/* sk-w-${n} (in pixels) */
$skeleton-widths: (120, 200, 250, 340);
@each $w in $skeleton-widths {
.sk-w-#{$w} {
max-width: #{$w}px;
}
}
/* sk-h-${n} */
$skeleton-heights: (12, 20, 24);
@each $h in $skeleton-heights {
.sk-h-#{$h} {
height: #{$h}px;
}
}

View File

@ -5,7 +5,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { MatLegacyProgressSpinnerModule as MatProgressSpinnerModule } from '@angular/material/legacy-progress-spinner';
import { SortByPipe } from './sorting';
import { CommonUiOptions, IqserAppConfig, ModuleWithOptions } from './utils';
import { HiddenActionComponent, LogoComponent, ToastComponent } from './shared';
import { HiddenActionComponent, ToastComponent } from './shared';
import { ConnectionStatusComponent, FullPageErrorComponent } from './error';
import { IqserListingModule } from './listing';
import { IqserFiltersModule } from './filtering';
@ -24,6 +24,7 @@ import { MatLegacyTooltipModule as MatTooltipModule } from '@angular/material/le
import { ApiPathInterceptor, IqserConfigService, IqserUserPreferenceService } from './services';
import { DefaultUserPreferenceService } from './services/default-user-preference.service';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { IqserSkeletonModule } from './skeleton/skeleton.module';
const matModules = [
MatIconModule,
@ -42,16 +43,10 @@ const modules = [
IqserInputsModule,
IqserScrollbarModule,
IqserEmptyStatesModule,
IqserSkeletonModule,
HttpClientModule,
];
const components = [
ConnectionStatusComponent,
FullPageErrorComponent,
LogoComponent,
HiddenActionComponent,
ConfirmationDialogComponent,
ToastComponent,
];
const components = [ConnectionStatusComponent, FullPageErrorComponent, HiddenActionComponent, ConfirmationDialogComponent, ToastComponent];
const pipes = [SortByPipe];

View File

@ -2,15 +2,20 @@ import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree } fro
import { Injectable, InjectionToken, Injector } from '@angular/core';
import { firstValueFrom, from, of } from 'rxjs';
import { LoadingService } from '../loading';
import { SkeletonService } from './skeleton.service';
@Injectable({
providedIn: 'root',
})
export class CompositeRouteGuard implements CanActivate {
constructor(protected readonly _injector: Injector, private readonly _loadingService: LoadingService) {}
constructor(
protected readonly _injector: Injector,
private readonly _loadingService: LoadingService,
private readonly _skeletonService: SkeletonService,
) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
this._loadingService.start();
this.#preChecks(route);
const routeGuards = <InjectionToken<CanActivate>[]>route.data['routeGuards'];
@ -27,14 +32,30 @@ export class CompositeRouteGuard implements CanActivate {
const result = await firstValueFrom(canActivateResult);
if (!result) {
this._loadingService.stop();
this.#postChecks(route);
return false;
}
}
}
this._loadingService.stop();
this.#postChecks(route);
return true;
}
#preChecks(route: ActivatedRouteSnapshot): void {
const skeleton = route.data['skeleton'];
if (skeleton) {
this._skeletonService.setType(skeleton);
} else {
this._loadingService.start();
}
}
#postChecks(route: ActivatedRouteSnapshot): void {
if (route.data['skeleton']) {
this._skeletonService.clear();
} else {
this._loadingService.stop();
}
}
}

View File

@ -9,3 +9,4 @@ export * from './iqser-user-preference.service';
export * from './language.service';
export * from './iqser-config.service';
export * from './api-path.interceptor';
export * from './skeleton.service';

View File

@ -0,0 +1,17 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class SkeletonService {
type$ = new BehaviorSubject<string | null>(null);
clear() {
this.type$.next(null);
}
setType(type: string) {
this.type$.next(type);
}
}

View File

@ -1,13 +1,14 @@
import { NgModule } from '@angular/core';
import { SideNavComponent, SmallChipComponent, StatusBarComponent } from './index';
import { LogoComponent, SideNavComponent, SmallChipComponent, StatusBarComponent } from './index';
import { CommonModule } from '@angular/common';
import { MatLegacyTooltipModule as MatTooltipModule } from '@angular/material/legacy-tooltip';
import { IqserIconsModule } from '../icons';
const components = [SmallChipComponent, StatusBarComponent, SideNavComponent];
const components = [SmallChipComponent, StatusBarComponent, SideNavComponent, LogoComponent];
@NgModule({
declarations: [...components],
exports: [...components],
imports: [CommonModule, MatTooltipModule],
imports: [CommonModule, MatTooltipModule, IqserIconsModule],
})
export class IqserSharedModule {}

View File

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SkeletonComponent } from './skeleton/skeleton.component';
import { IqserSharedModule } from '../shared';
import { IqserUsersModule } from '../users';
@NgModule({
declarations: [SkeletonComponent],
exports: [SkeletonComponent],
imports: [CommonModule, IqserSharedModule, IqserUsersModule],
})
export class IqserSkeletonModule {}

View File

@ -0,0 +1,3 @@
<ng-container *ngIf="type$ | async as type">
<ng-container *ngTemplateOutlet="templates[type]"></ng-container>
</ng-container>

View File

@ -0,0 +1,5 @@
:host {
position: absolute;
width: 100vw;
z-index: 2;
}

View File

@ -0,0 +1,29 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input, TemplateRef } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { SkeletonService } from '../../services';
@Component({
selector: 'iqser-skeleton [templates]',
templateUrl: './skeleton.component.html',
styleUrls: ['./skeleton.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SkeletonComponent {
x$ = new BehaviorSubject<string | null>('Dashboard');
@HostBinding('style.display') display = 'block';
@Input() templates: Record<string, TemplateRef<unknown>> = {};
// @HostBinding('style.display') display = 'none';
constructor(private readonly _skeletonService: SkeletonService) {
// this._skeletonService.type$.subscribe(type => {
// this.display = type ? 'block' : 'none';
// });
}
get type$(): BehaviorSubject<string | null> {
return this.x$;
// return this._skeletonService.type$;
}
}