Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 73d96f2

Browse files
temp: remove scroll-lock, add body-scroll-lock back
1 parent 9d355ed commit 73d96f2

File tree

6 files changed

+287
-76
lines changed

6 files changed

+287
-76
lines changed

‎packages/vue-final-modal/package.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,18 @@
3232
"devDependencies": {
3333
"@cypress/vue": "^5.0.5",
3434
"@release-it/conventional-changelog": "^5.1.1",
35-
"@types/scroll-lock": "^2.1.3",
3635
"@vueuse/core": "^10.7.1",
3736
"@vueuse/integrations": "^10.7.1",
3837
"cypress": "^13.6.0",
3938
"focus-trap": "^7.5.4",
4039
"release-it": "^16.1.3",
41-
"scroll-lock": "^2.1.5",
4240
"vite-plugin-dts": "^3.6.3",
4341
"vue": "3.3.7"
4442
},
4543
"peerDependencies": {
4644
"@vueuse/core": ">=10.0.0",
4745
"@vueuse/integrations": ">=10.0.0",
4846
"focus-trap": ">=7.2.0",
49-
"scroll-lock": ">=2.1.5",
5047
"vue": ">=3.0.0"
5148
},
5249
"homepage": "https://vue-final-modal.org/",

‎packages/vue-final-modal/src/components/VueFinalModal/VueFinalModal.vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useTransition } from './useTransition'
55
import { useToClose } from './useToClose'
66
import { useModelValue } from './useModelValue'
77
import { useFocusTrap } from './useFocusTrap'
8-
import { useScrollLock } from './useScrollLock'
8+
import { useLockScroll } from './useBodyScrollLock'
99
import { useZIndex } from './useZIndex'
1010
import { vVisible } from './vVisible'
1111
import { useInternalExposed } from './useInternalExposed'
@@ -46,7 +46,7 @@ const vfmContentEl = ref<HTMLDivElement>()
4646
4747
const { focus, blur } = useFocusTrap(props, { focusEl: vfmRootEl })
4848
const { modelValueLocal } = useModelValue(props, emit, { open, close })
49-
const { disablePageScroll, enablePageScroll } = useScrollLock(props, {
49+
const { disableBodyScroll, enableBodyScroll } = useLockScroll(props, {
5050
lockScrollEl: vfmRootEl,
5151
modelValueLocal,
5252
})
@@ -77,7 +77,7 @@ onMounted(() => {
7777
})
7878
7979
onBeforeUnmount(() => {
80-
enablePageScroll()
80+
enableBodyScroll()
8181
arrayRemoveItem(modals, modalExposed)
8282
arrayRemoveItem(openedModals, modalExposed)
8383
blur()
@@ -86,7 +86,7 @@ onBeforeUnmount(() => {
8686
8787
function onEntering() {
8888
nextTick(() => {
89-
disablePageScroll()
89+
disableBodyScroll()
9090
focus()
9191
})
9292
}
@@ -100,7 +100,7 @@ function onEnter() {
100100
function onLeave() {
101101
arrayRemoveItem(openedModals, modalExposed)
102102
resetZIndex()
103-
enablePageScroll()
103+
enableBodyScroll()
104104
emit('closed')
105105
// eslint-disable-next-line vue/custom-event-name-casing
106106
emit('_closed')
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
import type { Ref } from 'vue'
2+
import { onBeforeUnmount, watch } from 'vue'
3+
import type VueFinalModal from './VueFinalModal.vue'
4+
5+
type BodyScrollOptions = {
6+
reserveScrollBarGap?: boolean
7+
allowTouchMove?: (el?: null | HTMLElement) => boolean
8+
}
9+
10+
type Lock = {
11+
targetElement: HTMLElement
12+
options?: BodyScrollOptions
13+
}
14+
15+
// stolen from body-scroll-lock
16+
17+
// Older browsers don't support event options, feature detect it.
18+
let hasPassiveEvents = false
19+
if (typeof window !== 'undefined') {
20+
const passiveTestOptions = {
21+
get passive() {
22+
hasPassiveEvents = true
23+
return undefined
24+
},
25+
}
26+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
27+
// @ts-expect-error
28+
window.addEventListener('testPassive', null, passiveTestOptions)
29+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
30+
// @ts-expect-error
31+
window.removeEventListener('testPassive', null, passiveTestOptions)
32+
}
33+
34+
const isIosDevice
35+
= typeof window !== 'undefined'
36+
&& window.navigator
37+
&& window.navigator.platform
38+
&& (/iP(ad|hone|od)/.test(window.navigator.platform)
39+
|| (window.navigator.platform === 'MacIntel' && window.navigator.maxTouchPoints > 1))
40+
41+
let locks: Lock[] = []
42+
let documentListenerAdded = false
43+
let clientY = 0
44+
let initialClientY = -1
45+
let previousBodyOverflowSetting: undefined | string
46+
let previousBodyPaddingRight: undefined | string
47+
48+
const hasScrollbar = (el: HTMLElement) => {
49+
if (!el || el.nodeType !== Node.ELEMENT_NODE)
50+
return false
51+
52+
const style = window.getComputedStyle(el)
53+
return ['auto', 'scroll'].includes(style.overflowY) && el.scrollHeight > el.clientHeight
54+
}
55+
56+
const shouldScroll = (el: HTMLElement, delta: number) => {
57+
if (el.scrollTop === 0 && delta < 0)
58+
return false
59+
if (el.scrollTop + el.clientHeight + delta >= el.scrollHeight && delta > 0)
60+
return false
61+
return true
62+
}
63+
64+
const composedPath = (el: null | HTMLElement) => {
65+
const path = []
66+
while (el) {
67+
path.push(el)
68+
if (el.classList.contains('vfm'))
69+
return path
70+
el = el.parentElement
71+
}
72+
return path
73+
}
74+
75+
const hasAnyScrollableEl = (el: HTMLElement | null, delta: number) => {
76+
let hasAnyScrollableEl = false
77+
const path = composedPath(el)
78+
path.forEach((el) => {
79+
if (hasScrollbar(el) && shouldScroll(el, delta))
80+
hasAnyScrollableEl = true
81+
})
82+
return hasAnyScrollableEl
83+
}
84+
85+
// returns true if `el` should be allowed to receive touchmove events.
86+
const allowTouchMove = (el: HTMLElement | null) => locks.some(() => hasAnyScrollableEl(el, -clientY))
87+
88+
const preventDefault = (rawEvent: TouchEvent) => {
89+
const e = rawEvent || window.event
90+
91+
// For the case whereby consumers adds a touchmove event listener to document.
92+
// Recall that we do document.addEventListener('touchmove', preventDefault, { passive: false })
93+
// in disableBodyScroll - so if we provide this opportunity to allowTouchMove, then
94+
// the touchmove event on document will break.
95+
if (allowTouchMove(e.target as HTMLElement | null))
96+
return true
97+
98+
// Do not prevent if the event has more than one touch (usually meaning this is a multi touch gesture like pinch to zoom).
99+
if (e.touches.length > 1)
100+
return true
101+
102+
if (e.preventDefault)
103+
e.preventDefault()
104+
105+
return false
106+
}
107+
108+
const setOverflowHidden = (options?: BodyScrollOptions) => {
109+
// If previousBodyPaddingRight is already set, don't set it again.
110+
if (previousBodyPaddingRight === undefined) {
111+
const reserveScrollBarGap = !!options && options.reserveScrollBarGap === true
112+
const scrollBarGap = window.innerWidth - document.documentElement.clientWidth
113+
114+
if (reserveScrollBarGap && scrollBarGap > 0) {
115+
const computedBodyPaddingRight = parseInt(getComputedStyle(document.body).getPropertyValue('padding-right'), 10)
116+
previousBodyPaddingRight = document.body.style.paddingRight
117+
document.body.style.paddingRight = `${computedBodyPaddingRight + scrollBarGap}px`
118+
}
119+
}
120+
// If previousBodyOverflowSetting is already set, don't set it again.
121+
if (previousBodyOverflowSetting === undefined) {
122+
previousBodyOverflowSetting = document.body.style.overflow
123+
document.body.style.overflow = 'hidden'
124+
}
125+
}
126+
127+
const restoreOverflowSetting = () => {
128+
if (previousBodyPaddingRight !== undefined) {
129+
document.body.style.paddingRight = previousBodyPaddingRight
130+
131+
// Restore previousBodyPaddingRight to undefined so setOverflowHidden knows it
132+
// can be set again.
133+
previousBodyPaddingRight = undefined
134+
}
135+
136+
if (previousBodyOverflowSetting !== undefined) {
137+
document.body.style.overflow = previousBodyOverflowSetting
138+
139+
// Restore previousBodyOverflowSetting to undefined
140+
// so setOverflowHidden knows it can be set again.
141+
previousBodyOverflowSetting = undefined
142+
}
143+
}
144+
// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions
145+
const isTargetElementTotallyScrolled = (targetElement: HTMLElement) =>
146+
targetElement ? targetElement.scrollHeight - targetElement.scrollTop <= targetElement.clientHeight : false
147+
148+
const handleScroll = (event: TouchEvent, targetElement: HTMLElement) => {
149+
clientY = event.targetTouches[0].clientY - initialClientY
150+
151+
if (allowTouchMove(event.target as HTMLElement | null))
152+
return false
153+
154+
if (targetElement && targetElement.scrollTop === 0 && clientY > 0) {
155+
// element is at the top of its scroll.
156+
return preventDefault(event)
157+
}
158+
159+
if (isTargetElementTotallyScrolled(targetElement) && clientY < 0) {
160+
// element is at the bottom of its scroll.
161+
return preventDefault(event)
162+
}
163+
164+
event.stopPropagation()
165+
return true
166+
}
167+
168+
export const disableBodyScroll = (targetElement?: HTMLElement, options?: BodyScrollOptions) => {
169+
// targetElement must be provided
170+
if (!targetElement) {
171+
console.error(
172+
'disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.',
173+
)
174+
return
175+
}
176+
177+
// disableBodyScroll must not have been called on this targetElement before
178+
if (locks.some(lock => lock.targetElement === targetElement))
179+
return
180+
181+
const lock = {
182+
targetElement,
183+
options: options || {},
184+
}
185+
186+
locks = [...locks, lock]
187+
188+
if (isIosDevice) {
189+
targetElement.ontouchstart = (event: TouchEvent) => {
190+
if (event.targetTouches.length === 1) {
191+
// detect single touch.
192+
initialClientY = event.targetTouches[0].clientY
193+
}
194+
}
195+
targetElement.ontouchmove = (event: TouchEvent) => {
196+
if (event.targetTouches.length === 1) {
197+
// detect single touch.
198+
handleScroll(event, targetElement)
199+
}
200+
}
201+
202+
if (!documentListenerAdded) {
203+
document.addEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined)
204+
documentListenerAdded = true
205+
}
206+
}
207+
else {
208+
setOverflowHidden(options)
209+
}
210+
}
211+
212+
export const enableBodyScroll = (targetElement?: HTMLElement) => {
213+
if (!targetElement) {
214+
console.error(
215+
'enableBodyScroll unsuccessful - targetElement must be provided when calling enableBodyScroll on IOS devices.',
216+
)
217+
return
218+
}
219+
220+
locks = locks.filter(lock => lock.targetElement !== targetElement)
221+
222+
if (isIosDevice) {
223+
targetElement.ontouchstart = null
224+
targetElement.ontouchmove = null
225+
226+
if (documentListenerAdded && locks.length === 0) {
227+
document.removeEventListener('touchmove', preventDefault, (hasPassiveEvents ? { passive: false } : undefined) as any)
228+
documentListenerAdded = false
229+
}
230+
}
231+
else if (!locks.length) {
232+
restoreOverflowSetting()
233+
}
234+
}
235+
236+
export function useLockScroll(props: InstanceType<typeof VueFinalModal>['$props'], options: {
237+
lockScrollEl: Ref<undefined | HTMLElement>
238+
modelValueLocal: Ref<boolean>
239+
}) {
240+
const { lockScrollEl, modelValueLocal } = options
241+
242+
let _lockScrollEl: HTMLElement
243+
watch(lockScrollEl, (val) => {
244+
if (val)
245+
_lockScrollEl = val
246+
}, { immediate: true })
247+
248+
watch(() => props.lockScroll, (val) => {
249+
val ? _disableBodyScroll() : _enableBodyScroll()
250+
})
251+
252+
onBeforeUnmount(() => {
253+
_enableBodyScroll()
254+
})
255+
256+
function _enableBodyScroll() {
257+
_lockScrollEl && enableBodyScroll(_lockScrollEl)
258+
}
259+
260+
function _disableBodyScroll() {
261+
if (!modelValueLocal.value)
262+
return
263+
props.lockScroll && _lockScrollEl
264+
&& disableBodyScroll(_lockScrollEl, {
265+
reserveScrollBarGap: props.reserveScrollBarGap,
266+
allowTouchMove: (el) => {
267+
while (el && el !== document.body) {
268+
if (el.getAttribute('vfm-scroll-lock-ignore') !== null)
269+
return true
270+
271+
el = el.parentElement
272+
}
273+
return false
274+
},
275+
})
276+
}
277+
278+
return {
279+
enableBodyScroll: _enableBodyScroll,
280+
disableBodyScroll: _disableBodyScroll,
281+
}
282+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /