Skeleton loader WIP
This commit is contained in:
parent
cb3f1e3eea
commit
6702aea4e3
@ -42,3 +42,23 @@ $sides: (top, bottom, left, right);
|
|||||||
flex: #{$n};
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
import { MatLegacyProgressSpinnerModule as MatProgressSpinnerModule } from '@angular/material/legacy-progress-spinner';
|
import { MatLegacyProgressSpinnerModule as MatProgressSpinnerModule } from '@angular/material/legacy-progress-spinner';
|
||||||
import { SortByPipe } from './sorting';
|
import { SortByPipe } from './sorting';
|
||||||
import { CommonUiOptions, IqserAppConfig, ModuleWithOptions } from './utils';
|
import { CommonUiOptions, IqserAppConfig, ModuleWithOptions } from './utils';
|
||||||
import { HiddenActionComponent, LogoComponent, ToastComponent } from './shared';
|
import { HiddenActionComponent, ToastComponent } from './shared';
|
||||||
import { ConnectionStatusComponent, FullPageErrorComponent } from './error';
|
import { ConnectionStatusComponent, FullPageErrorComponent } from './error';
|
||||||
import { IqserListingModule } from './listing';
|
import { IqserListingModule } from './listing';
|
||||||
import { IqserFiltersModule } from './filtering';
|
import { IqserFiltersModule } from './filtering';
|
||||||
@ -24,6 +24,7 @@ import { MatLegacyTooltipModule as MatTooltipModule } from '@angular/material/le
|
|||||||
import { ApiPathInterceptor, IqserConfigService, IqserUserPreferenceService } from './services';
|
import { ApiPathInterceptor, IqserConfigService, IqserUserPreferenceService } from './services';
|
||||||
import { DefaultUserPreferenceService } from './services/default-user-preference.service';
|
import { DefaultUserPreferenceService } from './services/default-user-preference.service';
|
||||||
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
|
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
|
||||||
|
import { IqserSkeletonModule } from './skeleton/skeleton.module';
|
||||||
|
|
||||||
const matModules = [
|
const matModules = [
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
@ -42,16 +43,10 @@ const modules = [
|
|||||||
IqserInputsModule,
|
IqserInputsModule,
|
||||||
IqserScrollbarModule,
|
IqserScrollbarModule,
|
||||||
IqserEmptyStatesModule,
|
IqserEmptyStatesModule,
|
||||||
|
IqserSkeletonModule,
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
];
|
];
|
||||||
const components = [
|
const components = [ConnectionStatusComponent, FullPageErrorComponent, HiddenActionComponent, ConfirmationDialogComponent, ToastComponent];
|
||||||
ConnectionStatusComponent,
|
|
||||||
FullPageErrorComponent,
|
|
||||||
LogoComponent,
|
|
||||||
HiddenActionComponent,
|
|
||||||
ConfirmationDialogComponent,
|
|
||||||
ToastComponent,
|
|
||||||
];
|
|
||||||
|
|
||||||
const pipes = [SortByPipe];
|
const pipes = [SortByPipe];
|
||||||
|
|
||||||
|
|||||||
@ -2,15 +2,20 @@ import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree } fro
|
|||||||
import { Injectable, InjectionToken, Injector } from '@angular/core';
|
import { Injectable, InjectionToken, Injector } from '@angular/core';
|
||||||
import { firstValueFrom, from, of } from 'rxjs';
|
import { firstValueFrom, from, of } from 'rxjs';
|
||||||
import { LoadingService } from '../loading';
|
import { LoadingService } from '../loading';
|
||||||
|
import { SkeletonService } from './skeleton.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class CompositeRouteGuard implements CanActivate {
|
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> {
|
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
|
||||||
this._loadingService.start();
|
this.#preChecks(route);
|
||||||
|
|
||||||
const routeGuards = <InjectionToken<CanActivate>[]>route.data['routeGuards'];
|
const routeGuards = <InjectionToken<CanActivate>[]>route.data['routeGuards'];
|
||||||
|
|
||||||
@ -27,14 +32,30 @@ export class CompositeRouteGuard implements CanActivate {
|
|||||||
|
|
||||||
const result = await firstValueFrom(canActivateResult);
|
const result = await firstValueFrom(canActivateResult);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
this._loadingService.stop();
|
this.#postChecks(route);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._loadingService.stop();
|
this.#postChecks(route);
|
||||||
|
|
||||||
return true;
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,3 +9,4 @@ export * from './iqser-user-preference.service';
|
|||||||
export * from './language.service';
|
export * from './language.service';
|
||||||
export * from './iqser-config.service';
|
export * from './iqser-config.service';
|
||||||
export * from './api-path.interceptor';
|
export * from './api-path.interceptor';
|
||||||
|
export * from './skeleton.service';
|
||||||
|
|||||||
17
src/lib/services/skeleton.service.ts
Normal file
17
src/lib/services/skeleton.service.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,13 +1,14 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { SideNavComponent, SmallChipComponent, StatusBarComponent } from './index';
|
import { LogoComponent, SideNavComponent, SmallChipComponent, StatusBarComponent } from './index';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { MatLegacyTooltipModule as MatTooltipModule } from '@angular/material/legacy-tooltip';
|
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({
|
@NgModule({
|
||||||
declarations: [...components],
|
declarations: [...components],
|
||||||
exports: [...components],
|
exports: [...components],
|
||||||
imports: [CommonModule, MatTooltipModule],
|
imports: [CommonModule, MatTooltipModule, IqserIconsModule],
|
||||||
})
|
})
|
||||||
export class IqserSharedModule {}
|
export class IqserSharedModule {}
|
||||||
|
|||||||
12
src/lib/skeleton/skeleton.module.ts
Normal file
12
src/lib/skeleton/skeleton.module.ts
Normal 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 {}
|
||||||
3
src/lib/skeleton/skeleton/skeleton.component.html
Normal file
3
src/lib/skeleton/skeleton/skeleton.component.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<ng-container *ngIf="type$ | async as type">
|
||||||
|
<ng-container *ngTemplateOutlet="templates[type]"></ng-container>
|
||||||
|
</ng-container>
|
||||||
5
src/lib/skeleton/skeleton/skeleton.component.scss
Normal file
5
src/lib/skeleton/skeleton/skeleton.component.scss
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
:host {
|
||||||
|
position: absolute;
|
||||||
|
width: 100vw;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
29
src/lib/skeleton/skeleton/skeleton.component.ts
Normal file
29
src/lib/skeleton/skeleton/skeleton.component.ts
Normal 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$;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user