116 lines
4.5 KiB
TypeScript
116 lines
4.5 KiB
TypeScript
/* eslint-disable @angular-eslint/prefer-on-push-component-change-detection */
|
|
import { CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
|
import { AsyncPipe, NgClass } from '@angular/common';
|
|
import { AfterViewInit, Component, forwardRef, HostListener, Inject, Input, OnDestroy, Optional, ViewChild } from '@angular/core';
|
|
import { RouterLink } from '@angular/router';
|
|
import { BehaviorSubject } from 'rxjs';
|
|
import { delay, tap } from 'rxjs/operators';
|
|
import { HasScrollbarDirective } from '../../directives/has-scrollbar.directive';
|
|
import { HelpModeService } from '../../help-mode/help-mode.service';
|
|
import { SnakeCasePipe } from '../../pipes/snake-case.pipe';
|
|
import { AutoUnsubscribe } from '../../utils/auto-unsubscribe.directive';
|
|
import { trackByFactory } from '../../utils/functions';
|
|
import { ListingComponent } from '../listing-component.directive';
|
|
import { IListable } from '../models/listable';
|
|
import { Id } from '../models/trackable';
|
|
import { ListingService } from '../services/listing.service';
|
|
import { TableItemComponent } from './table-item/table-item.component';
|
|
|
|
@Component({
|
|
selector: 'iqser-table-content',
|
|
templateUrl: './table-content.component.html',
|
|
styleUrls: ['./table-content.component.scss'],
|
|
standalone: true,
|
|
imports: [
|
|
CdkVirtualScrollViewport,
|
|
AsyncPipe,
|
|
CdkFixedSizeVirtualScroll,
|
|
HasScrollbarDirective,
|
|
CdkVirtualForOf,
|
|
SnakeCasePipe,
|
|
NgClass,
|
|
RouterLink,
|
|
TableItemComponent,
|
|
],
|
|
})
|
|
export class TableContentComponent<Class extends IListable<PrimaryKey>, PrimaryKey extends Id = Class['id']>
|
|
extends AutoUnsubscribe
|
|
implements OnDestroy, AfterViewInit
|
|
{
|
|
private _lastScrolledIndex = 0;
|
|
private _multiSelectActive$ = new BehaviorSubject(false);
|
|
@Input() itemSize!: number;
|
|
@Input() itemMouseEnterFn?: (entity: Class) => void;
|
|
@Input() itemMouseLeaveFn?: (entity: Class) => void;
|
|
@Input() tableItemClasses?: Record<string, (e: Class) => boolean>;
|
|
@Input() selectionEnabled!: boolean;
|
|
@Input() rowIdPrefix: string = 'item';
|
|
@Input() namePropertyKey?: string;
|
|
readonly trackBy = trackByFactory<Class>();
|
|
@ViewChild(CdkVirtualScrollViewport, { static: true }) readonly scrollViewport!: CdkVirtualScrollViewport;
|
|
@ViewChild(HasScrollbarDirective, { static: true }) readonly hasScrollbarDirective!: HasScrollbarDirective;
|
|
|
|
constructor(
|
|
@Inject(forwardRef(() => ListingComponent)) readonly listingComponent: ListingComponent<Class>,
|
|
readonly listingService: ListingService<Class>,
|
|
@Optional() readonly helpModeService: HelpModeService,
|
|
) {
|
|
super();
|
|
this.addSubscription = this.listingComponent.noContent$.subscribe(() => {
|
|
setTimeout(() => {
|
|
this.scrollViewport?.checkViewportSize();
|
|
}, 0);
|
|
});
|
|
}
|
|
|
|
multiSelect(entity: Class, $event: MouseEvent): void {
|
|
if (this.selectionEnabled && this._multiSelectActive$.value) {
|
|
$event.stopPropagation();
|
|
this.listingService.select(entity, $event.shiftKey);
|
|
}
|
|
}
|
|
|
|
ngAfterViewInit(): void {
|
|
this.addSubscription = this.scrollViewport.scrolledIndexChange.pipe(tap(index => (this._lastScrolledIndex = index))).subscribe();
|
|
this.addSubscription = this.listingService.displayedLength$
|
|
.pipe(
|
|
delay(100),
|
|
tap(() => this.hasScrollbarDirective.process()),
|
|
)
|
|
.subscribe();
|
|
}
|
|
|
|
scrollToLastIndex(): void {
|
|
this.scrollViewport.scrollToIndex(this._lastScrolledIndex, 'smooth');
|
|
}
|
|
|
|
getTableItemClasses(entity: Class): Record<string, boolean> {
|
|
const classes: Record<string, boolean> = {
|
|
'table-item': true,
|
|
'cursor-default': !entity.routerLink,
|
|
};
|
|
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')
|
|
@HostListener('window:keydown.shift')
|
|
private _enableMultiSelect() {
|
|
this._multiSelectActive$.next(true);
|
|
}
|
|
|
|
@HostListener('window:focus')
|
|
@HostListener('window:blur')
|
|
@HostListener('window:keyup.control')
|
|
@HostListener('window:keyup.meta')
|
|
@HostListener('window:keyup.shift')
|
|
private _disableMultiSelect() {
|
|
this._multiSelectActive$.next(false);
|
|
}
|
|
}
|