Move sortedDisplayedEntities from ListingComponent to ListingService

This commit is contained in:
Adina Țeudan 2023-02-08 16:22:58 +02:00
parent 40517f145e
commit 610a88570b
14 changed files with 45 additions and 46 deletions

View File

@ -3,8 +3,7 @@ import { INestedFilter } from './models/nested-filter.model';
import { IFilterGroup } from './models/filter-group.model';
import { IFilter } from './models/filter.model';
import { NestedFilter } from './models/nested-filter';
import { IListable } from '../listing';
import { Id } from '../listing/models/trackable';
import { Id, IListable } from '../listing';
function copySettings(oldFilters: INestedFilter[], newFilters: INestedFilter[]) {
if (!oldFilters || !newFilters) {

View File

@ -1,13 +1,12 @@
import { Directive, inject, OnDestroy, TemplateRef, ViewChild } from '@angular/core';
import { combineLatest, Observable } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { map } 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';
import { Id } from './models/trackable';
import { Id, IListable, TableColumnConfig } from './models';
@Directive()
export abstract class ListingComponent<Class extends IListable<PrimaryKey>, PrimaryKey extends Id = Class['id']>
@ -24,26 +23,14 @@ export abstract class ListingComponent<Class extends IListable<PrimaryKey>, Prim
readonly noMatch$ = this.#noMatch$;
// TODO: Move to table content component
readonly noContent$ = this.#noContent$;
readonly sortedDisplayedEntities$ = this.#sortedDisplayedEntities$;
abstract readonly tableColumnConfigs: readonly TableColumnConfig<Class>[];
abstract readonly tableHeaderLabel: string;
@ViewChild('tableItemTemplate') readonly tableItemTemplate?: TemplateRef<unknown>;
get allEntities(): Class[] {
return this.entitiesService.all;
}
get #sortedDisplayedEntities$(): Observable<Class[]> {
const sort = (entities: Class[]) => this.sortingService.defaultSort(entities);
const sortedEntities$ = this.listingService.displayed$.pipe(map(sort));
return this.sortingService.sortingOption$.pipe(
switchMap(() => sortedEntities$),
shareDistinctLast(),
);
}
get #noMatch$(): Observable<boolean> {
return combineLatest([this.entitiesService.allLength$, this.listingService.displayedLength$]).pipe(
map(([hasEntities, hasDisplayedEntities]) => !!hasEntities && !hasDisplayedEntities),

View File

@ -1,10 +1,9 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, Subject } from 'rxjs';
import { filter, map, startWith, tap } from 'rxjs/operators';
import { IListable } from '../models';
import { Id, IListable } from '../models';
import { GenericService, QueryParam } from '../../services';
import { getLength, List, mapEach, shareDistinctLast, shareLast } from '../../utils';
import { Id } from '../models/trackable';
@Injectable()
/**

View File

@ -1,12 +1,12 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { map, switchMap, tap } from 'rxjs/operators';
import { FilterService, getFilteredEntities } from '../../filtering';
import { SearchService } from '../../search';
import { IListable } from '../models';
import { Id, IListable } from '../models';
import { EntitiesService } from './entities.service';
import { any, getLength, shareDistinctLast, shareLast } from '../../utils';
import { Id } from '../models/trackable';
import { SortingService } from '../../sorting';
@Injectable()
export class ListingService<Class extends IListable<PrimaryKey>, PrimaryKey extends Id = Class['id']> {
@ -18,13 +18,16 @@ export class ListingService<Class extends IListable<PrimaryKey>, PrimaryKey exte
readonly selected$: Observable<PrimaryKey[]>;
readonly selectedEntities$: Observable<Class[]>;
readonly selectedLength$: Observable<number>;
readonly sortedDisplayedEntities$: Observable<Class[]>;
private _displayed: Class[] = [];
private readonly _selected$ = new BehaviorSubject<PrimaryKey[]>([]);
readonly #sortedDisplayedEntities$ = new BehaviorSubject<Class[]>([]);
constructor(
protected readonly _filterService: FilterService,
protected readonly _searchService: SearchService<Class>,
protected readonly _entitiesService: EntitiesService<Class, Class>,
protected readonly _sortingService: SortingService<Class>,
) {
this.displayed$ = this._getDisplayed$;
this.displayedLength$ = this.displayed$.pipe(getLength, shareDistinctLast());
@ -39,6 +42,8 @@ export class ListingService<Class extends IListable<PrimaryKey>, PrimaryKey exte
this.areAllSelected$ = this._areAllSelected$;
this.areSomeSelected$ = this._areSomeSelected$;
this.notAllSelected$ = this._notAllSelected$;
this.sortedDisplayedEntities$ = this.#getSortedDisplayedEntities$();
}
get selected(): Class[] {
@ -113,12 +118,17 @@ export class ListingService<Class extends IListable<PrimaryKey>, PrimaryKey exte
this.setSelected(this._displayed);
}
select(entity: Class): void {
const currentEntityIdx = this.selected.indexOf(entity);
select(entity: Class, withShift = false): void {
const currentlySelected = this.selected;
const currentEntityIdx = currentlySelected.indexOf(entity);
if (currentEntityIdx === -1) {
return this.setSelected([...this.selected, entity]);
// Entity is not previously selected, select it
this.setSelected([...currentlySelected, entity]);
} else {
// Entity is previously selected, deselect it
this.setSelected(currentlySelected.slice(0, currentEntityIdx).concat(currentlySelected.slice(currentEntityIdx + 1)));
}
this.setSelected(this.selected.filter((_el, idx) => idx !== currentEntityIdx));
}
deselect(entities: Class | Class[]) {
@ -131,4 +141,16 @@ export class ListingService<Class extends IListable<PrimaryKey>, PrimaryKey exte
const items = this._displayed.filter(item => this.selected.includes(item));
this.setSelected(items);
}
#getSortedDisplayedEntities$(): Observable<Class[]> {
const sort = (entities: Class[]) => this._sortingService.defaultSort(entities);
const sortedEntities$ = this.displayed$.pipe(map(sort));
return this._sortingService.sortingOption$.pipe(
switchMap(() => sortedEntities$),
tap(sortedEntities => {
this.#sortedDisplayedEntities$.next(sortedEntities);
}),
shareDistinctLast(),
);
}
}

View File

@ -1,8 +1,7 @@
import { ChangeDetectionStrategy, Component, Input, Optional } from '@angular/core';
import { SortingOrders, SortingService } from '../../sorting';
import { KeysOf, Required } from '../../utils';
import { IListable } from '../models';
import { Id } from '../models/trackable';
import { Id, IListable } from '../models';
const ifHasRightIcon = <T extends IListable>(thisArg: TableColumnNameComponent<T>) => !!thisArg.rightIcon;

View File

@ -7,7 +7,7 @@
id="virtual-scroll"
iqserHasScrollbar
>
<ng-container *cdkVirtualFor="let entity of listingComponent.sortedDisplayedEntities$ | async; trackBy: trackBy">
<ng-container *cdkVirtualFor="let entity of listingService.sortedDisplayedEntities$ | async; trackBy: trackBy">
<!-- mouseenter and mouseleave triggers change detection event if itemMouse functions are undefined -->
<!-- this little hack below ensures that change detection won't be triggered if functions are undefined -->
<div

View File

@ -3,12 +3,11 @@ import { AfterViewInit, Component, forwardRef, HostListener, Inject, Input, OnDe
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { delay, tap } from 'rxjs/operators';
import { AutoUnsubscribe, trackByFactory } from '../../utils';
import { IListable } from '../models';
import { Id, IListable } from '../models';
import { ListingComponent, ListingService } from '../index';
import { HasScrollbarDirective } from '../../scrollbar';
import { BehaviorSubject } from 'rxjs';
import { HelpModeService } from '../../help-mode';
import { Id } from '../models/trackable';
@Component({
selector: 'iqser-table-content',

View File

@ -29,8 +29,8 @@ export class TableItemComponent<T extends IListable> implements OnChanges {
this.#entityChanged$.next(this.entity);
}
toggleEntitySelected(event: MouseEvent, entity: T): void {
event.stopPropagation();
this.listingService.select(entity);
toggleEntitySelected($event: MouseEvent, entity: T): void {
$event.stopPropagation();
this.listingService.select(entity, $event.shiftKey);
}
}

View File

@ -1,8 +1,7 @@
import { ChangeDetectionStrategy, Component, Input, TemplateRef } from '@angular/core';
import { FilterService } from '../../filtering';
import { EntitiesService, ListingService } from '../services';
import { IListable, ListingMode, ListingModes, TableColumnConfig } from '../models';
import { Id } from '../models/trackable';
import { Id, IListable, ListingMode, ListingModes, TableColumnConfig } from '../models';
@Component({
selector: 'iqser-table-header [tableHeaderLabel]',

View File

@ -13,11 +13,10 @@ import {
ViewChild,
ViewContainerRef,
} from '@angular/core';
import { IListable, ListingModes, TableColumnConfig } from '../models';
import { Id, IListable, ListingModes, TableColumnConfig } from '../models';
import { ListingComponent } from '../listing-component.directive';
import { EntitiesService } from '../services';
import { TableContentComponent } from '../table-content/table-content.component';
import { Id } from '../models/trackable';
const SCROLLBAR_WIDTH = 11;

View File

@ -4,8 +4,7 @@ import { SortingService } from '../sorting';
import { EntitiesService, ListingService } from './services';
import { forwardRef, Provider, Type } from '@angular/core';
import { ListingComponent } from './listing-component.directive';
import { IListable } from './models';
import { Id } from './models/trackable';
import { Id, IListable } from './models';
export const DefaultListingServices: readonly Provider[] = [FilterService, SearchService, SortingService, ListingService] as const;

View File

@ -1,8 +1,7 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { IListable } from '../listing';
import { Id, IListable } from '../listing';
import { shareDistinctLast } from '../utils';
import { Id } from '../listing/models/trackable';
@Injectable()
export class SearchService<T extends IListable<PrimaryKey>, PrimaryKey extends Id = T['id']> {

View File

@ -1,9 +1,8 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, Subject } from 'rxjs';
import { filter, map, startWith } from 'rxjs/operators';
import { Entity } from '../listing';
import { Entity, Id } from '../listing';
import { List, RequiredParam, shareLast, Validate } from '../utils';
import { Id } from '../listing/models/trackable';
import { isArray } from '../permissions';
@Injectable()

View File

@ -3,9 +3,8 @@ import { BehaviorSubject } from 'rxjs';
import { SortingOption } from './models/sorting-option.model';
import { SortingOrder, SortingOrders } from './models/sorting-order.type';
import { KeysOf, shareDistinctLast } from '../utils';
import { IListable } from '../listing';
import { Id, IListable } from '../listing';
import { orderBy } from 'lodash-es';
import { Id } from '../listing/models/trackable';
@Injectable()
export class SortingService<T extends IListable<PrimaryKey>, PrimaryKey extends Id = T['id']> {