1- import { Directive , ElementRef , EventEmitter , HostBinding , NgZone , OnDestroy , OnInit , Output } from '@angular/core' ;
1+ import { Directive , ElementRef , EventEmitter , HostBinding , Input , NgZone , OnDestroy , OnInit , Output } from '@angular/core' ;
2+ import { debounceTime , Observable , Subscriber } from 'rxjs' ;
23
34/**
45 * Visibility Observer Directive
@@ -18,7 +19,20 @@ export class VisibilityDirective implements OnInit, OnDestroy {
1819
1920 @Output ( ) visible : EventEmitter < any > = new EventEmitter ( ) ;
2021
21- timeout : any ;
22+ observer ! : ResizeObserver ;
23+
24+ /**
25+ * Throttle time in ms. Will emit this time after the resize.
26+ */
27+ @Input ( ) resizeThrottle = 100 ;
28+ /**
29+ * Emit the initial visibility without waiting throttle time.
30+ */
31+ @Input ( ) emitInitial = true ;
32+
33+ private previousOffsetHeight = 0 ;
34+ private previousOffsetWidth = 0 ;
35+
2236
2337 constructor ( private element : ElementRef , private zone : NgZone ) { }
2438
@@ -27,7 +41,7 @@ export class VisibilityDirective implements OnInit, OnDestroy {
2741 }
2842
2943 ngOnDestroy ( ) : void {
30- clearTimeout ( this . timeout ) ;
44+ this . observer . disconnect ( ) ;
3145 }
3246
3347 onVisibilityChange ( ) : void {
@@ -39,21 +53,28 @@ export class VisibilityDirective implements OnInit, OnDestroy {
3953 }
4054
4155 runCheck ( ) : void {
42- const check = ( ) => {
43- // https://davidwalsh.name/offsetheight-visibility
44- const { offsetHeight, offsetWidth } = this . element . nativeElement ;
45-
46- if ( offsetHeight && offsetWidth ) {
47- clearTimeout ( this . timeout ) ;
48- this . onVisibilityChange ( ) ;
49- } else {
50- clearTimeout ( this . timeout ) ;
51- this . zone . runOutsideAngular ( ( ) => {
52- this . timeout = setTimeout ( ( ) => check ( ) , 50 ) ;
53- } ) ;
54- }
55- } ;
56-
57- this . timeout = setTimeout ( ( ) => check ( ) ) ;
56+ const resizeEvent = new Observable ( ( subscriber : Subscriber < ResizeObserverEntry [ ] > ) => {
57+ this . observer = new ResizeObserver ( _entries => {
58+ const { offsetHeight, offsetWidth } = this . element . nativeElement ;
59+ if ( ( offsetWidth && offsetHeight ) && ( offsetHeight !== this . previousOffsetHeight ) || ( offsetWidth !== this . previousOffsetWidth ) ) {
60+ // First time emit immediately once table is visible
61+ if ( ! this . isVisible && this . emitInitial ) {
62+ this . onVisibilityChange ( ) ;
63+ } else {
64+ subscriber . next ( ) ;
65+ }
66+ }
67+ this . previousOffsetHeight = offsetHeight ;
68+ this . previousOffsetWidth = offsetWidth ;
69+ } ) ;
70+
71+ this . observer . observe ( this . element . nativeElement ) ;
72+ } ) ;
73+
74+ resizeEvent . pipe (
75+ debounceTime ( this . resizeThrottle )
76+ ) . subscribe ( ( ) => {
77+ this . onVisibilityChange ( ) ;
78+ } ) ;
5879 }
5980}
0 commit comments