From 49ee0615119fd5bdbb75f8a66fc9504cf2a17995 Mon Sep 17 00:00:00 2001 From: sumeyyeKurtulus Date: Fri, 13 Sep 2024 10:50:59 +0300 Subject: [PATCH] update: datatable directive to handle loading indicator --- .../ngx-datatable-default.directive.ts | 95 +++++++++++++++++-- 1 file changed, 87 insertions(+), 8 deletions(-) diff --git a/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-default.directive.ts b/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-default.directive.ts index eb781666c1..8f0f49d79e 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-default.directive.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-default.directive.ts @@ -1,34 +1,63 @@ import { DOCUMENT } from '@angular/common'; -import { AfterViewInit, Directive, HostBinding, Inject, Input, OnDestroy } from '@angular/core'; +import { + AfterViewInit, + Directive, + effect, + ElementRef, + HostBinding, + inject, + Inject, + input, + Input, + OnDestroy, + OnInit, + Renderer2, + ViewContainerRef, +} from '@angular/core'; import { ColumnMode, DatatableComponent, ScrollerComponent } from '@swimlane/ngx-datatable'; import { fromEvent, Subscription } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; +import { SpinnerComponent } from '../components'; @Directive({ // eslint-disable-next-line @angular-eslint/directive-selector selector: 'ngx-datatable[default]', - standalone:true, + standalone: true, exportAs: 'ngxDatatableDefault', }) -export class NgxDatatableDefaultDirective implements AfterViewInit, OnDestroy { - private subscription = new Subscription(); +export class NgxDatatableDefaultDirective implements OnInit, AfterViewInit, OnDestroy { + @Input() class = 'material bordered'; + loadingIndicator = input(true); - private resizeDiff = 0; + private _loading = true; + private mutationObserver: MutationObserver | null = null; - @Input() class = 'material bordered'; + private subscription = new Subscription(); + private resizeDiff = 0; + private elRef = inject(ElementRef); + private renderer = inject(Renderer2); + private viewContainerRef = inject(ViewContainerRef); @HostBinding('class') get classes(): string { return `ngx-datatable ${this.class}`; } - constructor(private table: DatatableComponent, @Inject(DOCUMENT) private document: MockDocument) { + constructor( + private table: DatatableComponent, + @Inject(DOCUMENT) private document: MockDocument, + ) { this.table.columnMode = ColumnMode.force; this.table.footerHeight = 50; this.table.headerHeight = 50; this.table.rowHeight = 'auto'; this.table.scrollbarH = true; this.table.virtualization = false; + + effect(() => { + this._loading = this.loadingIndicator(); + this.updateLoadingIndicator(); + }); } private fixHorizontalGap(scroller: ScrollerComponent) { @@ -60,17 +89,67 @@ export class NgxDatatableDefaultDirective implements AfterViewInit, OnDestroy { this.subscription.add(subscription); } + private observeMutations() { + this.mutationObserver = new MutationObserver(() => { + if (this._loading) { + this.updateLoadingIndicator(); + } + }); + + this.mutationObserver.observe(this.elRef.nativeElement, { + childList: true, + subtree: true, + }); + } + + private updateLoadingIndicator() { + const progressElement = this.elRef.nativeElement.querySelector('datatable-progress'); + + if (this._loading) { + if (progressElement) { + this.renderer.removeChild(progressElement.parentNode, progressElement); + this.addSpinner(progressElement); + } + } else { + this.removeSpinner(); + } + } + + private addSpinner(placeholder: Comment) { + this.viewContainerRef.clear(); + + const spinnerRef = this.viewContainerRef.createComponent(SpinnerComponent); + + this.renderer.insertBefore( + placeholder.parentNode, + spinnerRef.location.nativeElement, + placeholder, + ); + this.renderer.removeChild(placeholder.parentNode, placeholder); + } + + private removeSpinner() { + this.viewContainerRef.clear(); + } + + ngOnInit() { + this.observeMutations(); + } + ngAfterViewInit() { this.fixStyleOnWindowResize(); } ngOnDestroy() { this.subscription.unsubscribe(); + if (this.mutationObserver) { + this.mutationObserver.disconnect(); + } } } // fix: https://github.com/angular/angular/issues/20351 -interface MockDocument { +interface MockDocument extends ParentNode { body: MockBody; documentElement: MockDocumentElement; }