common-ui/src/lib/listing/listing-component.directive.ts
2021-09-28 16:16:12 +03:00

75 lines
3.0 KiB
TypeScript

import { Directive, Injector, OnDestroy } from '@angular/core';
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { distinctUntilChanged, map, switchMap } from 'rxjs/operators';
import { FilterService } from '../filtering';
import { SortingService } from '../sorting';
import { AutoUnsubscribe } from '../utils';
import { SearchService } from '../search';
import { EntitiesService } from './services';
import { IListable, TableColumnConfig } from './models';
export const DefaultListingServices = [FilterService, SearchService, EntitiesService, SortingService] 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 noMatch$ = this._noMatch$;
readonly noContent$ = this._noContent$;
readonly sortedDisplayedEntities$ = this._sortedDisplayedEntities$;
readonly listingMode$: Observable<ListingMode>;
/**
* @deprecated Use routerLink getter from IListable
*/
readonly routerLinkFn?: (entity: T) => string | string[];
abstract readonly tableColumnConfigs: readonly TableColumnConfig<T>[];
abstract readonly tableHeaderLabel: string;
private readonly _listingMode$ = new BehaviorSubject<ListingMode>(ListingModes.table);
protected constructor(protected readonly _injector: Injector) {
super();
this.listingMode$ = this._listingMode$.asObservable();
}
get allEntities(): T[] {
return this.entitiesService.all;
}
set listingMode(listingMode: ListingMode) {
this._listingMode$.next(listingMode);
}
private get _sortedDisplayedEntities$(): Observable<readonly T[]> {
const sort = (entities: T[]) => this.sortingService.defaultSort(entities);
const sortedEntities = () => this.entitiesService.displayed$.pipe(map(sort));
return this.sortingService.sortingOption$.pipe(switchMap(sortedEntities));
}
private get _noMatch$(): Observable<boolean> {
return combineLatest([this.entitiesService.allLength$, this.entitiesService.displayedLength$]).pipe(
map(([hasEntities, hasDisplayedEntities]) => !!hasEntities && !hasDisplayedEntities),
distinctUntilChanged()
);
}
private get _noContent$(): Observable<boolean> {
return combineLatest([this._noMatch$, this.entitiesService.noData$]).pipe(
map(([noMatch, noData]) => noMatch || noData),
distinctUntilChanged()
);
}
toggleEntitySelected(event: MouseEvent, entity: T): void {
event.stopPropagation();
this.entitiesService.select(entity);
}
isSelected(entity: T): boolean {
return this.entitiesService.isSelected(entity);
}
}