1const scrollStrategies = { 2 none: null, 3 close: closeScrollStrategy, 4 block: blockScrollStrategy, 5 reposition: repositionScrollStrategy, 6} 7 8function makeScrollStrategyProps() { 9 return { 10 scrollStrategy: 'block', // [String, Function] 11 } 12} 13 14function useScrollStrategies(props, data) { 15 if (!IN_BROWSER) return 16 17 let scope 18 watchEffect(async () => { 19 scope?.stop() 20 21 if (!(data.isActive.value && props.scrollStrategy)) return 22 23 scope = effectScope() 24 await nextTick() 25 scope.run(() => { 26 if (typeof props.scrollStrategy === 'function') { 27 props.scrollStrategy(data) 28 } else { 29 scrollStrategies[props.scrollStrategy]?.(data) 30 } 31 }) 32 }) 33} 34 35function closeScrollStrategy(data) { 36 function onScroll(e) { 37 data.isActive.value = false 38 } 39 40 bindScroll(data.activatorEl.value ?? data.contentEl.value, onScroll) 41} 42 43function blockScrollStrategy(data) { 44 const scrollElements = [ 45 ...new Set([ 46 ...getScrollParents(data.activatorEl.value), 47 ...getScrollParents(data.contentEl.value), 48 ]), 49 ].filter((el) => !el.classList.contains('x-overlay-scroll-blocked')) 50 const scrollbarWidth = 51 window.innerWidth - document.documentElement.offsetWidth 52 53 const scrollableParent = ((el) => hasScrollbar(el) && el)( 54 data.root.value?.offsetParent || document.documentElement, 55 ) 56 if (scrollableParent) { 57 data.root.value.classList.add('x-overlay--scroll-blocked') 58 } 59 60 scrollElements.forEach((el, i) => { 61 el.style.setProperty('--x-body-scroll-x', convertToUnit(-el.scrollLeft)) 62 el.style.setProperty('--x-body-scroll-y', convertToUnit(-el.scrollTop)) 63 el.style.setProperty('--x-scrollbar-offset', convertToUnit(scrollbarWidth)) 64 el.classList.add('x-overlay-scroll-blocked') 65 }) 66 67 onScopeDispose(() => { 68 scrollElements.forEach((el, i) => { 69 const x = parseFloat(el.style.getPropertyValue('--x-body-scroll-x')) 70 const y = parseFloat(el.style.getPropertyValue('--x-body-scroll-y')) 71 72 el.style.removeProperty('--x-body-scroll-x') 73 el.style.removeProperty('--x-body-scroll-y') 74 el.style.removeProperty('--x-scrollbar-offset') 75 el.classList.remove('x-overlay-scroll-blocked') 76 77 el.scrollLeft = -x 78 el.scrollTop = -y 79 }) 80 if (scrollableParent) { 81 data.root.value.classList.remove('x-overlay--scroll-blocked') 82 } 83 }) 84} 85 86function repositionScrollStrategy(data) { 87 let slow = false 88 let raf = -1 89 90 function update(e) { 91 requestNewFrame(() => { 92 const start = performance.now() 93 data.updateLocation.value?.(e) 94 const time = performance.now() - start 95 slow = time / (1000 / 60) > 2 96 }) 97 } 98 99 bindScroll(data.activatorEl.value ?? data.contentEl.value, (e) => { 100 if (slow) { 101 // If the position calculation is slow, 102 // defer updates until scrolling is finished. 103 // Browsers usually fire one scroll event per frame so 104 // we just wait until we've got two frames without an event 105 cancelAnimationFrame(raf) 106 raf = requestAnimationFrame(() => { 107 raf = requestAnimationFrame(() => { 108 update(e) 109 }) 110 }) 111 } else { 112 update(e) 113 } 114 }) 115} 116 117/** @private */ 118function bindScroll(el, onScroll) { 119 const scrollElements = [document, ...getScrollParents(el)] 120 scrollElements.forEach((el) => { 121 el.addEventListener('scroll', onScroll, { passive: true }) 122 }) 123 124 onScopeDispose(() => { 125 scrollElements.forEach((el) => { 126 el.removeEventListener('scroll', onScroll) 127 }) 128 }) 129}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.