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' ;
2
3
3
4
/**
4
5
* Visibility Observer Directive
@@ -18,7 +19,20 @@ export class VisibilityDirective implements OnInit, OnDestroy {
18
19
19
20
@Output ( ) visible : EventEmitter < any > = new EventEmitter ( ) ;
20
21
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
+
22
36
23
37
constructor ( private element : ElementRef , private zone : NgZone ) { }
24
38
@@ -27,7 +41,7 @@ export class VisibilityDirective implements OnInit, OnDestroy {
27
41
}
28
42
29
43
ngOnDestroy ( ) : void {
30
- clearTimeout ( this . timeout ) ;
44
+ this . observer . disconnect ( ) ;
31
45
}
32
46
33
47
onVisibilityChange ( ) : void {
@@ -39,21 +53,28 @@ export class VisibilityDirective implements OnInit, OnDestroy {
39
53
}
40
54
41
55
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
+ } ) ;
58
79
}
59
80
}
0 commit comments