108 lines
4.1 KiB
TypeScript
108 lines
4.1 KiB
TypeScript
/* eslint-disable @angular-eslint/prefer-on-push-component-change-detection */
|
|
import { AfterViewInit, Component, forwardRef, HostListener, Inject, Input, ViewChild } from '@angular/core';
|
|
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
|
import { delay, tap } from 'rxjs/operators';
|
|
import { trackByFactory, ContextComponent } from '../../utils';
|
|
import { IListable } from '../models';
|
|
import { ListingComponent, ListingService } from '../index';
|
|
import { HasScrollbarDirective } from '../../scrollbar';
|
|
import { BehaviorSubject } from 'rxjs';
|
|
import { HelpModeService } from '../../help-mode';
|
|
|
|
interface TableContentTemplate {
|
|
checkViewportSize: () => void,
|
|
lastScrolledIndex: number,
|
|
hasScrollbarDirective: () => void,
|
|
noContent: boolean,
|
|
sortedDisplayedEntities: any[],
|
|
isHelpModeActive: boolean,
|
|
}
|
|
|
|
@Component({
|
|
selector: 'iqser-table-content',
|
|
templateUrl: './table-content.component.html',
|
|
styleUrls: ['./table-content.component.scss'],
|
|
})
|
|
export class TableContentComponent<T extends IListable> extends ContextComponent<TableContentTemplate> implements AfterViewInit {
|
|
@Input() itemSize!: number;
|
|
@Input() itemMouseEnterFn?: (entity: T) => void;
|
|
@Input() itemMouseLeaveFn?: (entity: T) => void;
|
|
@Input() tableItemClasses?: Record<string, (e: T) => boolean>;
|
|
@Input() selectionEnabled!: boolean;
|
|
readonly trackBy = trackByFactory<T>();
|
|
|
|
@ViewChild(CdkVirtualScrollViewport, { static: true }) readonly scrollViewport!: CdkVirtualScrollViewport;
|
|
@ViewChild(HasScrollbarDirective, { static: true }) readonly hasScrollbarDirective!: HasScrollbarDirective;
|
|
|
|
private _lastScrolledIndex = 0;
|
|
private _multiSelectActive$ = new BehaviorSubject(false);
|
|
|
|
readonly #checkViewportSize$ = this.listingComponent.noContent$.pipe(tap(() => {
|
|
setTimeout(() => {
|
|
this.scrollViewport?.checkViewportSize();
|
|
}, 0);
|
|
}));
|
|
|
|
constructor(
|
|
@Inject(forwardRef(() => ListingComponent)) readonly listingComponent: ListingComponent<T>,
|
|
readonly listingService: ListingService<T>,
|
|
readonly helpModeService: HelpModeService,
|
|
) {
|
|
super();
|
|
}
|
|
|
|
multiSelect(entity: T, $event: MouseEvent): void {
|
|
if (this.selectionEnabled && this._multiSelectActive$.value) {
|
|
$event.stopPropagation();
|
|
this.listingService.select(entity);
|
|
}
|
|
}
|
|
|
|
ngAfterViewInit(): void {
|
|
const lastScrolledIndex$ = this.scrollViewport.scrolledIndexChange.pipe(tap(index => (this._lastScrolledIndex = index)))
|
|
const hasScrollbarDirective$ = this.listingService.displayedLength$
|
|
.pipe(
|
|
delay(100),
|
|
tap(() => this.hasScrollbarDirective.process()),
|
|
)
|
|
|
|
super._initContext({
|
|
checkViewportSize: this.#checkViewportSize$,
|
|
lastScrolledIndex: lastScrolledIndex$,
|
|
hasScrollbarDirective: hasScrollbarDirective$,
|
|
noContent: this.listingComponent.noContent$,
|
|
sortedDisplayedEntities: this.listingComponent.sortedDisplayedEntities$,
|
|
isHelpModeActive: this.helpModeService.isHelpModeActive$,
|
|
})
|
|
}
|
|
|
|
scrollToLastIndex(): void {
|
|
this.scrollViewport.scrollToIndex(this._lastScrolledIndex, 'smooth');
|
|
}
|
|
|
|
getTableItemClasses(entity: T): Record<string, boolean> {
|
|
const classes: Record<string, boolean> = {
|
|
'table-item': true,
|
|
pointer: !!entity.routerLink && entity.routerLink.length > 0,
|
|
};
|
|
for (const key in this.tableItemClasses) {
|
|
if (Object.prototype.hasOwnProperty.call(this.tableItemClasses, key)) {
|
|
classes[key] = this.tableItemClasses[key](entity);
|
|
}
|
|
}
|
|
return classes;
|
|
}
|
|
|
|
@HostListener('window:keydown.control')
|
|
@HostListener('window:keydown.meta')
|
|
private _enableMultiSelect() {
|
|
this._multiSelectActive$.next(true);
|
|
}
|
|
|
|
@HostListener('window:keyup.control')
|
|
@HostListener('window:keyup.meta')
|
|
private _disableMultiSelect() {
|
|
this._multiSelectActive$.next(false);
|
|
}
|
|
}
|