@@ -4,92 +4,184 @@ import { inject as service } from '@ember/service';
44import { DEBUG } from '@glimmer/env' ;
55import Modifier from 'ember-modifier' ;
66import deepEqual from 'fast-deep-equal' ;
7+ import { registerDestructor } from '@ember/destroyable' ;
8+ import { macroCondition , dependencySatisfies } from '@embroider/macros' ;
79
810const WATCHED_ELEMENTS = DEBUG ? new WeakSet ( ) : undefined ;
911
10- export default class InViewportModifier extends Modifier {
11- @service inViewport ;
12+ let modifier ;
1213
13- name = 'in-viewport' ;
14+ if ( macroCondition ( dependencySatisfies ( 'ember-modifier' , '>=3.2.0 || 4.x' ) ) ) {
15+ modifier = class InViewportModifier extends Modifier {
16+ @service inViewport ;
1417
15- lastOptions ;
18+ name = 'in-viewport' ;
1619
17- get options ( ) {
18- // eslint-disable-next-line no-unused-vars
19- const { onEnter, onExit, ...options } = this . args . named ;
20- return options ;
21- }
20+ lastOptions ;
21+ element = null ;
2222
23- get hasStaleOptions ( ) {
24- return ! deepEqual ( this . options , this . lastOptions ) ;
25- }
23+ modify ( element , positional , named ) {
24+ this . element = element ;
25+ this . positional = positional ;
26+ this . named = named ;
27+ this . validateArguments ( ) ;
2628
27- validateArguments ( ) {
28- assert (
29- `'{{in-viewport}}' does not accept positional parameters. Specify listeners via 'onEnter' / 'onExit'.` ,
30- this . args . positional . length === 0
31- ) ;
32- assert (
33- `'{{in-viewport}}' either expects 'onEnter', 'onExit' or both to be present.` ,
34- typeof this . args . named . onEnter === 'function' ||
35- typeof this . args . named . onExit === 'function'
36- ) ;
37- }
29+ if ( ! this . didSetup ) {
30+ this . setupWatcher ( element ) ;
31+ registerDestructor ( ( ) => this . destroyWatcher ( element ) ) ;
32+ } else if ( this . hasStaleOptions ) {
33+ this . destroyWatcher ( element ) ;
34+ this . setupWatcher ( element ) ;
35+ }
36+ }
37+
38+ get options ( ) {
39+ // eslint-disable-next-line no-unused-vars
40+ const { onEnter, onExit, ...options } = this . named ;
41+ return options ;
42+ }
43+
44+ get hasStaleOptions ( ) {
45+ return ! deepEqual ( this . options , this . lastOptions ) ;
46+ }
47+
48+ validateArguments ( ) {
49+ assert (
50+ `'{{in-viewport}}' does not accept positional parameters. Specify listeners via 'onEnter' / 'onExit'.` ,
51+ this . positional . length === 0
52+ ) ;
53+ assert (
54+ `'{{in-viewport}}' either expects 'onEnter', 'onExit' or both to be present.` ,
55+ typeof this . named . onEnter === 'function' ||
56+ typeof this . named . onExit === 'function'
57+ ) ;
58+ }
59+
60+ @action
61+ onEnter ( ...args ) {
62+ if ( this . named . onEnter ) {
63+ this . named . onEnter . call ( null , this . element , ...args ) ;
64+ }
65+
66+ if ( ! this . options . viewportSpy ) {
67+ this . inViewport . stopWatching ( this . element ) ;
68+ }
69+ }
3870
39- @action
40- onEnter ( ...args ) {
41- if ( this . args . named . onEnter ) {
42- this . args . named . onEnter . call ( null , this . element , ...args ) ;
71+ @action
72+ onExit ( ...args ) {
73+ if ( this . named . onExit ) {
74+ this . named . onExit . call ( null , this . element , ...args ) ;
75+ }
4376 }
4477
45- if ( ! this . options . viewportSpy ) {
78+ setupWatcher ( element ) {
79+ assert (
80+ `'${ element } ' is already being watched. Make sure that '{{in-viewport}}' is only used once on this element and that you are not calling 'inViewport.watchElement(element)' in other places.` ,
81+ ! WATCHED_ELEMENTS . has ( element )
82+ ) ;
83+ if ( DEBUG ) WATCHED_ELEMENTS . add ( element ) ;
84+ this . inViewport . watchElement (
85+ element ,
86+ this . options ,
87+ this . onEnter ,
88+ this . onExit
89+ ) ;
90+ this . lastOptions = this . options ;
91+ }
92+
93+ destroyWatcher ( element ) {
94+ if ( DEBUG ) WATCHED_ELEMENTS . delete ( element ) ;
95+ this . inViewport . stopWatching ( element ) ;
96+ }
97+ } ;
98+ } else {
99+ modifier = class InViewportModifier extends Modifier {
100+ @service inViewport ;
101+
102+ name = 'in-viewport' ;
103+
104+ lastOptions ;
105+
106+ get options ( ) {
107+ // eslint-disable-next-line no-unused-vars
108+ const { onEnter, onExit, ...options } = this . args . named ;
109+ return options ;
110+ }
111+
112+ get hasStaleOptions ( ) {
113+ return ! deepEqual ( this . options , this . lastOptions ) ;
114+ }
115+
116+ validateArguments ( ) {
117+ assert (
118+ `'{{in-viewport}}' does not accept positional parameters. Specify listeners via 'onEnter' / 'onExit'.` ,
119+ this . args . positional . length === 0
120+ ) ;
121+ assert (
122+ `'{{in-viewport}}' either expects 'onEnter', 'onExit' or both to be present.` ,
123+ typeof this . args . named . onEnter === 'function' ||
124+ typeof this . args . named . onExit === 'function'
125+ ) ;
126+ }
127+
128+ @action
129+ onEnter ( ...args ) {
130+ if ( this . args . named . onEnter ) {
131+ this . args . named . onEnter . call ( null , this . element , ...args ) ;
132+ }
133+
134+ if ( ! this . options . viewportSpy ) {
135+ this . inViewport . stopWatching ( this . element ) ;
136+ }
137+ }
138+
139+ @action
140+ onExit ( ...args ) {
141+ if ( this . args . named . onExit ) {
142+ this . args . named . onExit . call ( null , this . element , ...args ) ;
143+ }
144+ }
145+
146+ setupWatcher ( ) {
147+ assert (
148+ `'${ this . element } ' is already being watched. Make sure that '{{in-viewport}}' is only used once on this element and that you are not calling 'inViewport.watchElement(element)' in other places.` ,
149+ ! WATCHED_ELEMENTS . has ( this . element )
150+ ) ;
151+ if ( DEBUG ) WATCHED_ELEMENTS . add ( this . element ) ;
152+ this . inViewport . watchElement (
153+ this . element ,
154+ this . options ,
155+ this . onEnter ,
156+ this . onExit
157+ ) ;
158+ this . lastOptions = this . options ;
159+ }
160+
161+ destroyWatcher ( ) {
162+ if ( DEBUG ) WATCHED_ELEMENTS . delete ( this . element ) ;
46163 this . inViewport . stopWatching ( this . element ) ;
47164 }
48- }
49-
50- @action
51- onExit ( ...args ) {
52- if ( this . args . named . onExit ) {
53- this . args . named . onExit . call ( null , this . element , ...args ) ;
54- }
55- }
56-
57- setupWatcher ( ) {
58- assert (
59- `'${ this . element } ' is already being watched. Make sure that '{{in-viewport}}' is only used once on this element and that you are not calling 'inViewport.watchElement(element)' in other places.` ,
60- ! WATCHED_ELEMENTS . has ( this . element )
61- ) ;
62- if ( DEBUG ) WATCHED_ELEMENTS . add ( this . element ) ;
63- this . inViewport . watchElement (
64- this . element ,
65- this . options ,
66- this . onEnter ,
67- this . onExit
68- ) ;
69- this . lastOptions = this . options ;
70- }
71-
72- destroyWatcher ( ) {
73- if ( DEBUG ) WATCHED_ELEMENTS . delete ( this . element ) ;
74- this . inViewport . stopWatching ( this . element ) ;
75- }
76-
77- didInstall ( ) {
78- this . setupWatcher ( ) ;
79- }
80-
81- didUpdateArguments ( ) {
82- if ( this . hasStaleOptions ) {
83- this . destroyWatcher ( ) ;
165+
166+ didInstall ( ) {
84167 this . setupWatcher ( ) ;
85168 }
86- }
87169
88- didReceiveArguments ( ) {
89- this . validateArguments ( ) ;
90- }
170+ didUpdateArguments ( ) {
171+ if ( this . hasStaleOptions ) {
172+ this . destroyWatcher ( ) ;
173+ this . setupWatcher ( ) ;
174+ }
175+ }
91176
92- willRemove ( ) {
93- this . destroyWatcher ( ) ;
94- }
177+ didReceiveArguments ( ) {
178+ this . validateArguments ( ) ;
179+ }
180+
181+ willRemove ( ) {
182+ this . destroyWatcher ( ) ;
183+ }
184+ } ;
95185}
186+
187+ export default modifier ;
0 commit comments