@@ -3,96 +3,143 @@ function _toConsumableArray(arr) {
3
3
for ( var i = 0 , arr2 = Array ( arr . length ) ; i < arr . length ; i ++ ) {
4
4
arr2 [ i ] = arr [ i ] ;
5
5
}
6
+
6
7
return arr2 ;
7
8
} else {
8
9
return Array . from ( arr ) ;
9
10
}
10
- }
11
+ } // Older browsers don't support event options, feature detect it.
12
+ // Adopted and modified solution from Bohdan Didukh (2017)
13
+ // https://stackoverflow.com/questions/41594997/ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain-scroll-posi
14
+
15
+
11
16
var hasPassiveEvents = false ;
17
+
12
18
if ( typeof window !== 'undefined' ) {
13
19
var passiveTestOptions = {
14
20
get passive ( ) {
15
21
hasPassiveEvents = true ;
16
22
return undefined ;
17
23
}
24
+
18
25
} ;
19
26
window . addEventListener ( 'testPassive' , null , passiveTestOptions ) ;
20
27
window . removeEventListener ( 'testPassive' , null , passiveTestOptions ) ;
21
28
}
29
+
22
30
var isIosDevice = typeof window !== 'undefined' && window . navigator && window . navigator . platform && ( / i P ( a d | h o n e | o d ) / . test ( window . navigator . platform ) || window . navigator . platform === 'MacIntel' && window . navigator . maxTouchPoints > 1 ) ;
23
31
var locks = [ ] ;
24
32
var documentListenerAdded = false ;
25
33
var initialClientY = - 1 ;
26
34
var previousBodyOverflowSetting = void 0 ;
27
- var previousBodyPaddingRight = void 0 ;
35
+ var previousBodyPaddingRight = void 0 ; // returns true if `el` should be allowed to receive touchmove events.
36
+
28
37
var allowTouchMove = function allowTouchMove ( el ) {
29
38
return locks . some ( function ( lock ) {
30
39
if ( lock . options . allowTouchMove && lock . options . allowTouchMove ( el ) ) {
31
40
return true ;
32
41
}
42
+
33
43
return false ;
34
44
} ) ;
35
45
} ;
46
+
36
47
var preventDefault = function preventDefault ( rawEvent ) {
37
- var e = rawEvent || window . event ;
48
+ var e = rawEvent || window . event ; // For the case whereby consumers adds a touchmove event listener to document.
49
+ // Recall that we do document.addEventListener('touchmove', preventDefault, { passive: false })
50
+ // in disableBodyScroll - so if we provide this opportunity to allowTouchMove, then
51
+ // the touchmove event on document will break.
52
+
38
53
if ( allowTouchMove ( e . target ) ) {
39
54
return true ;
40
- }
55
+ } // Do not prevent if the event has more than one touch (usually meaning this is a multi touch gesture like pinch to zoom).
56
+
57
+
41
58
if ( e . touches . length > 1 ) return true ;
42
59
if ( e . preventDefault ) e . preventDefault ( ) ;
43
60
return false ;
44
61
} ;
62
+
45
63
var setOverflowHidden = function setOverflowHidden ( options ) {
64
+ // Setting overflow on body/documentElement synchronously in Desktop Safari slows down
65
+ // the responsiveness for some reason. Setting within a setTimeout fixes this.
46
66
setTimeout ( function ( ) {
67
+ // If previousBodyPaddingRight is already set, don't set it again.
47
68
if ( previousBodyPaddingRight === undefined ) {
48
69
var _reserveScrollBarGap = ! ! options && options . reserveScrollBarGap === true ;
70
+
49
71
var scrollBarGap = window . innerWidth - document . documentElement . clientWidth ;
72
+
50
73
if ( _reserveScrollBarGap && scrollBarGap > 0 ) {
51
74
previousBodyPaddingRight = document . body . style . paddingRight ;
52
75
document . body . style . paddingRight = scrollBarGap + 'px' ;
53
76
}
54
- }
77
+ } // If previousBodyOverflowSetting is already set, don't set it again.
78
+
79
+
55
80
if ( previousBodyOverflowSetting === undefined ) {
56
81
previousBodyOverflowSetting = document . body . style . overflow ;
57
82
document . body . style . overflow = 'hidden' ;
58
83
}
59
84
} ) ;
60
85
} ;
86
+
61
87
var restoreOverflowSetting = function restoreOverflowSetting ( ) {
88
+ // Setting overflow on body/documentElement synchronously in Desktop Safari slows down
89
+ // the responsiveness for some reason. Setting within a setTimeout fixes this.
62
90
setTimeout ( function ( ) {
63
91
if ( previousBodyPaddingRight !== undefined ) {
64
- document . body . style . paddingRight = previousBodyPaddingRight ;
92
+ document . body . style . paddingRight = previousBodyPaddingRight ; // Restore previousBodyPaddingRight to undefined so setOverflowHidden knows it
93
+ // can be set again.
94
+
65
95
previousBodyPaddingRight = undefined ;
66
96
}
97
+
67
98
if ( previousBodyOverflowSetting !== undefined ) {
68
- document . body . style . overflow = previousBodyOverflowSetting ;
99
+ document . body . style . overflow = previousBodyOverflowSetting ; // Restore previousBodyOverflowSetting to undefined
100
+ // so setOverflowHidden knows it can be set again.
101
+
69
102
previousBodyOverflowSetting = undefined ;
70
103
}
71
104
} ) ;
72
- } ;
105
+ } ; // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions
106
+
107
+
73
108
var isTargetElementTotallyScrolled = function isTargetElementTotallyScrolled ( targetElement ) {
74
109
return targetElement ? targetElement . scrollHeight - targetElement . scrollTop <= targetElement . clientHeight : false ;
75
110
} ;
111
+
76
112
var handleScroll = function handleScroll ( event , targetElement ) {
77
113
var clientY = event . targetTouches [ 0 ] . clientY - initialClientY ;
114
+
78
115
if ( allowTouchMove ( event . target ) ) {
79
116
return false ;
80
117
}
118
+
81
119
if ( targetElement && targetElement . scrollTop === 0 && clientY > 0 ) {
120
+ // element is at the top of its scroll.
82
121
return preventDefault ( event ) ;
83
122
}
123
+
84
124
if ( isTargetElementTotallyScrolled ( targetElement ) && clientY < 0 ) {
125
+ // element is at the bottom of its scroll.
85
126
return preventDefault ( event ) ;
86
127
}
128
+
87
129
event . stopPropagation ( ) ;
88
130
return true ;
89
131
} ;
132
+
90
133
var disableBodyScroll = function disableBodyScroll ( targetElement , options ) {
91
134
if ( isIosDevice ) {
135
+ // targetElement must be provided, and disableBodyScroll must not have been
136
+ // called on this targetElement before.
92
137
if ( ! targetElement ) {
138
+ // eslint-disable-next-line no-console
93
139
console . error ( 'disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.' ) ;
94
140
return ;
95
141
}
142
+
96
143
if ( targetElement && ! locks . some ( function ( lock ) {
97
144
return lock . targetElement === targetElement ;
98
145
} ) ) {
@@ -101,16 +148,21 @@ var disableBodyScroll = function disableBodyScroll(targetElement, options) {
101
148
options : options || { }
102
149
} ;
103
150
locks = [ ] . concat ( _toConsumableArray ( locks ) , [ lock ] ) ;
151
+
104
152
targetElement . ontouchstart = function ( event ) {
105
153
if ( event . targetTouches . length === 1 ) {
154
+ // detect single touch.
106
155
initialClientY = event . targetTouches [ 0 ] . clientY ;
107
156
}
108
157
} ;
158
+
109
159
targetElement . ontouchmove = function ( event ) {
110
160
if ( event . targetTouches . length === 1 ) {
161
+ // detect single touch.
111
162
handleScroll ( event , targetElement ) ;
112
163
}
113
164
} ;
165
+
114
166
if ( ! documentListenerAdded ) {
115
167
document . addEventListener ( 'touchmove' , preventDefault , hasPassiveEvents ? {
116
168
passive : false
@@ -129,24 +181,29 @@ var disableBodyScroll = function disableBodyScroll(targetElement, options) {
129
181
} ;
130
182
var clearAllBodyScrollLocks = function clearAllBodyScrollLocks ( ) {
131
183
if ( isIosDevice ) {
184
+ // Clear all locks ontouchstart/ontouchmove handlers, and the references.
132
185
locks . forEach ( function ( lock ) {
133
186
lock . targetElement . ontouchstart = null ;
134
187
lock . targetElement . ontouchmove = null ;
135
188
} ) ;
189
+
136
190
if ( documentListenerAdded ) {
137
191
document . removeEventListener ( 'touchmove' , preventDefault , hasPassiveEvents ? {
138
192
passive : false
139
193
} : undefined ) ;
140
194
documentListenerAdded = false ;
141
195
}
142
- locks = [ ] ;
196
+
197
+ locks = [ ] ; // Reset initial clientY.
198
+
143
199
initialClientY = - 1 ;
144
200
} else {
145
201
restoreOverflowSetting ( ) ;
146
202
locks = [ ] ;
147
203
}
148
204
} ;
149
205
206
+ //
150
207
let modalStack = [ ] ;
151
208
const TransitionState = {
152
209
Enter : 'enter' ,
@@ -223,56 +280,72 @@ var script = {
223
280
isComponentReadyToBeDestroyed ( ) {
224
281
return this . overlayTransitionState === TransitionState . Leave && this . modalTransitionState === TransitionState . Leave ;
225
282
}
283
+
226
284
} ,
227
285
watch : {
228
286
value ( value ) {
229
287
this . mounted ( value ) ;
288
+
230
289
if ( value === false ) {
231
290
this . close ( ) ;
232
291
}
233
292
} ,
293
+
234
294
lockScroll : 'handleLockScroll' ,
295
+
235
296
hideOverlay ( value ) {
236
297
if ( this . value ) {
237
298
! value && this . appendOverlay ( ) ;
238
299
}
239
300
} ,
301
+
240
302
attach ( ) {
241
303
this . mounted ( this . value ) ;
242
304
} ,
305
+
243
306
isComponentReadyToBeDestroyed ( isReady ) {
244
307
if ( isReady ) {
245
308
this . visible = false ;
246
309
}
247
310
}
311
+
248
312
} ,
313
+
249
314
mounted ( ) {
250
315
this . mounted ( this . value ) ;
251
316
} ,
317
+
252
318
beforeDestroy ( ) {
253
319
this . close ( ) ;
254
320
} ,
321
+
255
322
methods : {
256
323
mounted ( value ) {
257
324
if ( value ) {
258
325
let target = this . getAttachElement ( ) ;
326
+
259
327
if ( target ) {
260
328
target . appendChild ( this . $el ) ;
261
329
let index = modalStack . findIndex ( vm => vm === this ) ;
330
+
262
331
if ( index !== - 1 ) {
332
+ // if this is already exist in modalStack, delete it
263
333
modalStack . splice ( index , 1 ) ;
264
334
}
335
+
265
336
modalStack . push ( this ) ;
266
337
this . handleLockScroll ( ) ;
267
338
modalStack . filter ( vm => vm !== this ) . forEach ( vm => {
268
339
if ( vm . getAttachElement ( ) === target ) {
340
+ // if vm and this have the same attach element
269
341
vm . visibility . overlay = false ;
270
342
}
271
343
} ) ;
272
344
} else if ( target !== false ) {
273
345
console . warn ( 'Unable to locate target ' . concat ( this . attach || 'body' ) ) ;
274
346
return ;
275
347
}
348
+
276
349
this . visible = true ;
277
350
this . $nextTick ( ( ) => {
278
351
this . startTransitionEnter ( ) ;
@@ -281,77 +354,102 @@ var script = {
281
354
this . lockScroll && clearAllBodyScrollLocks ( ) ;
282
355
}
283
356
} ,
357
+
284
358
close ( ) {
285
359
let index = modalStack . findIndex ( vm => vm === this ) ;
360
+
286
361
if ( index !== - 1 ) {
362
+ // remove this in modalStack
287
363
modalStack . splice ( index , 1 ) ;
288
364
}
365
+
289
366
if ( modalStack . length > 0 ) {
367
+ // If there are still nested modals opened
290
368
const $_vm = modalStack [ modalStack . length - 1 ] ;
291
369
$_vm . handleLockScroll ( ) ;
292
370
! $_vm . hideOverlay && $_vm . appendOverlay ( ) ;
293
371
} else {
372
+ // If the closed modal is the last one
294
373
this . lockScroll && clearAllBodyScrollLocks ( ) ;
295
374
}
375
+
296
376
this . startTransitionLeave ( ) ;
297
377
} ,
378
+
298
379
startTransitionEnter ( ) {
299
380
this . visibility . overlay = true ;
300
381
this . visibility . modal = true ;
301
382
} ,
383
+
302
384
startTransitionLeave ( ) {
303
385
this . visibility . overlay = false ;
304
386
this . visibility . modal = false ;
305
387
} ,
388
+
306
389
appendOverlay ( ) {
307
390
this . visibility . overlay = true ;
308
391
} ,
392
+
309
393
handleLockScroll ( ) {
310
394
this . lockScroll ? disableBodyScroll ( this . $refs . vfmContent ) : clearAllBodyScrollLocks ( ) ;
311
395
} ,
396
+
312
397
getAttachElement ( ) {
313
398
let target ;
399
+
314
400
if ( this . attach === false ) {
315
401
target = false ;
316
402
} else if ( typeof this . attach === 'string' ) {
403
+ // CSS selector
317
404
if ( window ) {
318
405
target = window . document . querySelector ( this . attach ) ;
319
406
} else {
320
407
target = false ;
321
408
}
322
409
} else {
410
+ // DOM Element
323
411
target = this . attach ;
324
412
}
413
+
325
414
return target ;
326
415
} ,
416
+
327
417
beforeOverlayEnter ( ) {
328
418
this . overlayTransitionState = TransitionState . Entering ;
329
419
} ,
420
+
330
421
afterOverlayEnter ( ) {
331
422
this . overlayTransitionState = TransitionState . Enter ;
332
423
} ,
424
+
333
425
beforeOverlayLeave ( ) {
334
426
this . overlayTransitionState = TransitionState . Leaving ;
335
427
} ,
428
+
336
429
afterOverlayLeave ( ) {
337
430
this . overlayTransitionState = TransitionState . Leave ;
338
431
} ,
432
+
339
433
beforeModalEnter ( ) {
340
434
this . $emit ( 'before-open' ) ;
341
435
this . modalTransitionState = TransitionState . Entering ;
342
436
} ,
437
+
343
438
afterModalEnter ( ) {
344
439
this . modalTransitionState = TransitionState . Enter ;
345
440
this . $emit ( 'opened' ) ;
346
441
} ,
442
+
347
443
beforeModalLeave ( ) {
348
444
this . $emit ( 'before-close' ) ;
349
445
this . modalTransitionState = TransitionState . Leaving ;
350
446
} ,
447
+
351
448
afterModalLeave ( ) {
352
449
this . modalTransitionState = TransitionState . Leave ;
353
450
this . $emit ( 'closed' ) ;
354
451
}
452
+
355
453
}
356
454
} ;
357
455
0 commit comments