-
Notifications
You must be signed in to change notification settings - Fork 11
Initial draft for new loaders using gifs #1071
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
7d84a9d
5b1e115
9569dfa
3ef430d
752b534
579ae96
ede6f6e
ff67f22
aa03ccb
b34414a
b7444b2
0470ca4
d51d783
c78b305
416837b
76d5ffd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| export const enum ImagesAssetPath { | ||
| ErrorPage = 'assets/images/error-page.svg', | ||
| LoaderSmallCircle = 'assets/images/loader-small.gif', | ||
| LoaderLargeSquare = 'assets/images/loader-large.gif', | ||
| LoaderHorizontal = 'assets/images/loader-horizontal.gif' | ||
| } | ||
|
|
||
| export const enum LoaderTypes { | ||
|
||
| SmallCircle = 'small-circle', | ||
| Horizontal = 'horizontal', | ||
| LargeSquare = 'large-square' | ||
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,9 +9,11 @@ import { | |
| TemplateRef, | ||
| ViewContainerRef | ||
| } from '@angular/core'; | ||
| import { LoaderTypes } from '@hypertrace/assets-library'; | ||
| import { Observable, ReplaySubject } from 'rxjs'; | ||
| import { LoadAsyncContext, LoadAsyncService } from './load-async.service'; | ||
| import { | ||
| LOADER_TYPE, | ||
| ASYNC_WRAPPER_PARAMETERS$, | ||
| LoadAsyncWrapperComponent, | ||
| LoadAsyncWrapperParameters | ||
|
|
@@ -23,26 +25,19 @@ import { | |
| export class LoadAsyncDirective implements OnChanges, OnDestroy { | ||
| @Input('htLoadAsync') | ||
| public data$?: Observable<unknown>; | ||
| @Input() | ||
|
||
| public htLoadAsyncLoaderType!: LoaderTypes; | ||
|
|
||
| private readonly wrapperParamsSubject: ReplaySubject<LoadAsyncWrapperParameters> = new ReplaySubject(1); | ||
| private readonly wrapperInjector: Injector; | ||
| private wrapperInjector!: Injector; | ||
|
||
| private wrapperView?: ComponentRef<LoadAsyncWrapperComponent>; | ||
|
|
||
| public constructor( | ||
| private readonly viewContainer: ViewContainerRef, | ||
| private readonly componentFactoryResolver: ComponentFactoryResolver, | ||
| private readonly loadAsyncService: LoadAsyncService, | ||
| public readonly templateRef: TemplateRef<LoadAsyncContext> | ||
| ) { | ||
| this.wrapperInjector = Injector.create({ | ||
| providers: [ | ||
| { | ||
| provide: ASYNC_WRAPPER_PARAMETERS$, | ||
| useValue: this.wrapperParamsSubject.asObservable() | ||
| } | ||
| ], | ||
| parent: this.viewContainer.injector | ||
| }); | ||
| } | ||
| ) {} | ||
|
|
||
| public ngOnChanges(): void { | ||
| if (this.data$) { | ||
|
|
@@ -65,6 +60,23 @@ export class LoadAsyncDirective implements OnChanges, OnDestroy { | |
| private buildWrapperView(): ComponentRef<LoadAsyncWrapperComponent> { | ||
| const componentFactory = this.componentFactoryResolver.resolveComponentFactory(LoadAsyncWrapperComponent); | ||
|
|
||
| // Second param for structural directive is undefined until this.data$ is defined | ||
| // So putting this assignment in constuctor will not work | ||
| // This will execute only once as this method is called only | ||
| this.wrapperInjector = Injector.create({ | ||
|
||
| providers: [ | ||
| { | ||
| provide: ASYNC_WRAPPER_PARAMETERS$, | ||
| useValue: this.wrapperParamsSubject.asObservable() | ||
| }, | ||
| { | ||
| provide: LOADER_TYPE, | ||
| useValue: this.htLoadAsyncLoaderType | ||
| } | ||
| ], | ||
| parent: this.viewContainer.injector | ||
| }); | ||
|
|
||
| return this.viewContainer.createComponent(componentFactory, undefined, this.wrapperInjector); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,38 @@ | ||
| import { ChangeDetectionStrategy, Component } from '@angular/core'; | ||
| import { IconType } from '@hypertrace/assets-library'; | ||
| import { IconSize } from '../../icon/icon-size'; | ||
| import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core'; | ||
| import { ImagesAssetPath, LoaderTypes } from '@hypertrace/assets-library'; | ||
| import { assertUnreachable } from '@hypertrace/common'; | ||
|
|
||
| @Component({ | ||
| selector: 'ht-loader', | ||
| styleUrls: ['./loader.component.scss'], | ||
| template: ` | ||
| <div class="ht-loader"> | ||
| <ht-icon icon="${IconType.Loading}" size="${IconSize.Large}"></ht-icon> | ||
| <img [ngClass]="[this.type]" [src]="this.imagePath" /> | ||
anandtiwary marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| </div> | ||
| `, | ||
| changeDetection: ChangeDetectionStrategy.OnPush | ||
| }) | ||
| export class LoaderComponent {} | ||
| export class LoaderComponent implements OnChanges { | ||
| @Input() | ||
| public type: LoaderTypes = LoaderTypes.Horizontal; | ||
| public imagePath: ImagesAssetPath = ImagesAssetPath.LoaderHorizontal; | ||
|
|
||
| public ngOnChanges(): void { | ||
| // Unfortunatly passing undefined is overwriting default value | ||
| this.type = this.type ?? LoaderTypes.Horizontal; | ||
|
||
| this.imagePath = this.getImagePathFromType(); | ||
| } | ||
|
|
||
| private getImagePathFromType(): ImagesAssetPath { | ||
| switch (this.type) { | ||
| case LoaderTypes.Horizontal: | ||
| return ImagesAssetPath.LoaderHorizontal; | ||
| case LoaderTypes.LargeSquare: | ||
| return ImagesAssetPath.LoaderLargeSquare; | ||
| case LoaderTypes.SmallCircle: | ||
| return ImagesAssetPath.LoaderSmallCircle; | ||
| default: | ||
| return assertUnreachable(this.type); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. may be we can leave
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, No. When a consumer is passing a wrong value it should throw an error, on the other hand, if the consumer is not passing a value it should show default value. |
||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| import { ChangeDetectionStrategy, Component, Inject, InjectionToken, TemplateRef } from '@angular/core'; | ||
| import { IconType } from '@hypertrace/assets-library'; | ||
| import { IconType, LoaderTypes } from '@hypertrace/assets-library'; | ||
| import { Observable } from 'rxjs'; | ||
| import { switchMap, tap } from 'rxjs/operators'; | ||
| import { LoadAsyncStateType } from '../load-async-state.type'; | ||
|
|
@@ -9,12 +9,13 @@ export const ASYNC_WRAPPER_PARAMETERS$ = new InjectionToken<Observable<LoadAsync | |
| 'ASYNC_WRAPPER_PARAMETERS$' | ||
| ); | ||
|
|
||
| export const LOADER_TYPE = new InjectionToken<string>('LOADER_TYPE'); | ||
| @Component({ | ||
| selector: 'ht-load-async-wrapper', | ||
| template: ` | ||
| <div *ngIf="this.state$ | async as state" class="fill-container" [ngSwitch]="state.type"> | ||
| <ng-container *ngSwitchCase="'${LoadAsyncStateType.Loading}'"> | ||
| <ht-loader></ht-loader> | ||
| <ht-loader [type]="this.loaderType"></ht-loader> | ||
| </ng-container> | ||
| <ng-container *ngSwitchCase="'${LoadAsyncStateType.Success}'"> | ||
| <ng-container *ngTemplateOutlet="this.content; context: state.context"></ng-container> | ||
|
|
@@ -36,15 +37,20 @@ export class LoadAsyncWrapperComponent { | |
| public icon?: IconType; | ||
| public title?: string; | ||
| public description: string = ''; | ||
| public loaderType: LoaderTypes; | ||
|
|
||
| public content?: TemplateRef<LoadAsyncContext>; | ||
|
|
||
| public constructor(@Inject(ASYNC_WRAPPER_PARAMETERS$) parameters$: Observable<LoadAsyncWrapperParameters>) { | ||
| public constructor( | ||
| @Inject(ASYNC_WRAPPER_PARAMETERS$) parameters$: Observable<LoadAsyncWrapperParameters>, | ||
| @Inject(LOADER_TYPE) loaderType: LoaderTypes | ||
| ) { | ||
| this.state$ = parameters$.pipe( | ||
| tap(params => (this.content = params.content)), | ||
| switchMap(parameter => parameter.state$), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As per prev comments, we can add loadertype as part of LoadingState. We wouldn't need to store another subject here. We can reference it from the state object directly. |
||
| tap(state => this.updateMessage(state.type, (state as Partial<ErrorAsyncState>).description)) | ||
| ); | ||
| this.loaderType = loaderType; | ||
| } | ||
|
|
||
| private updateMessage(stateType: LoadAsyncStateType, description: string = ''): void { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.