import { AfterViewInit, Directive, ElementRef, Input, NgZone, OnChanges, OnDestroy } from '@angular/core'; import { BooleanInput, InputBoolean } from '@delon/util'; import { fromEvent, Subscription } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; declare var Masonry: any; @Directive({ selector: '[masonry]', exportAs: 'masonryComp' }) export class MasonryDirective implements AfterViewInit, OnChanges, OnDestroy { static ngAcceptInputType_disabled: BooleanInput; private masonry: any; private observer?: MutationObserver; private resize$: Subscription | null = null; @Input('masonry') options: any; @Input() @InputBoolean() disabled = false; constructor(private el: ElementRef, private zone: NgZone) {} private outsideRender(cb: () => void): void { this.zone.runOutsideAngular(() => cb()); } init(): void { this.destroy(); this.outsideRender(() => { this.masonry = new Masonry(this.el.nativeElement, { originLeft: true, transitionDuration: '0.3s', itemSelector: '.masonry__thm', columnWidth: '.masonry__sizer', ...this.options }); }); } reload(): void { this.outsideRender(() => { if (this.disabled) { return; } this.masonry.reloadItems(); this.masonry.layout(); }); } private destroy(): void { this.zone.runOutsideAngular(() => { if (this.masonry) { this.masonry.destroy(); } }); } private initElChange(): void { if (this.observer || typeof MutationObserver === 'undefined') { return; } this.zone.runOutsideAngular(() => { this.observer = new MutationObserver(() => this.reload()); this.observer.observe(this.el.nativeElement, { childList: true, subtree: true }); }); } ngAfterViewInit(): void { this.initElChange(); this.resize$ = fromEvent(window, 'resize') .pipe(debounceTime(50)) .subscribe(() => this.reload()); } ngOnChanges(): void { this.init(); } ngOnDestroy(): void { this.destroy(); if (this.observer) { this.observer.disconnect(); } if (this.resize$) { this.resize$.unsubscribe(); } } }