common-ui/src/lib/listing/listing-component.directive.ts
2021-11-17 16:13:35 +02:00

69 lines
2.9 KiB
TypeScript

import { Directive, Injector, OnDestroy, TemplateRef, ViewChild } from '@angular/core';
import { combineLatest, Observable } from 'rxjs';
import { map, switchMapTo } from 'rxjs/operators';
import { FilterService } from '../filtering';
import { SortingService } from '../sorting';
import { AutoUnsubscribe, shareDistinctLast } from '../utils';
import { SearchService } from '../search';
import { EntitiesService, ListingService } from './services';
import { IListable, TableColumnConfig } from './models';
export const DefaultListingServicesTmp = [FilterService, SearchService, SortingService, ListingService] as const;
export const DefaultListingServices = [...DefaultListingServicesTmp, EntitiesService] as const;
@Directive()
export abstract class ListingComponent<T extends IListable> extends AutoUnsubscribe implements OnDestroy {
readonly filterService = this._injector.get(FilterService);
readonly searchService = this._injector.get<SearchService<T>>(SearchService);
readonly sortingService = this._injector.get<SortingService<T>>(SortingService);
readonly entitiesService = this._injector.get<EntitiesService<T>>(EntitiesService);
readonly listingService = this._injector.get<ListingService<T>>(ListingService);
readonly noMatch$ = this._noMatch$;
readonly noContent$ = this._noContent$;
readonly sortedDisplayedEntities$ = this._sortedDisplayedEntities$;
abstract readonly tableColumnConfigs: readonly TableColumnConfig<T>[];
abstract readonly tableHeaderLabel: string;
@ViewChild('tableItemTemplate') readonly tableItemTemplate?: TemplateRef<unknown>;
@ViewChild('workflowItemTemplate') readonly workflowItemTemplate?: TemplateRef<unknown>;
protected constructor(protected readonly _injector: Injector) {
super();
}
get allEntities(): T[] {
return this.entitiesService.all;
}
private get _sortedDisplayedEntities$(): Observable<T[]> {
const sort = (entities: T[]) => this.sortingService.defaultSort(entities);
const sortedEntities$ = this.listingService.displayed$.pipe(map(sort));
return this.sortingService.sortingOption$.pipe(switchMapTo(sortedEntities$), shareDistinctLast());
}
private get _noMatch$(): Observable<boolean> {
return combineLatest([this.entitiesService.allLength$, this.listingService.displayedLength$]).pipe(
map(([hasEntities, hasDisplayedEntities]) => !!hasEntities && !hasDisplayedEntities),
shareDistinctLast(),
);
}
private get _noContent$(): Observable<boolean> {
return combineLatest([this._noMatch$, this.entitiesService.noData$]).pipe(
map(([noMatch, noData]) => noMatch || noData),
shareDistinctLast(),
);
}
toggleEntitySelected(event: MouseEvent, entity: T): void {
event.stopPropagation();
this.listingService.select(entity);
}
cast(entity: unknown): T {
return entity as T;
}
}