function hasScrollableParent(target: HTMLElement): boolean { let el: HTMLElement | null = target; while (el && el !== document.body) { const { overflowY } = window.getComputedStyle(el); if ((overflowY === 'auto' || overflowY === 'scroll') && el.scrollHeight > el.clientHeight) { return true; } el = el.parentElement; } return false; } function preventTouchScroll(e: TouchEvent) { if (!hasScrollableParent(e.target as HTMLElement)) { e.preventDefault(); } } /** Управляет глобальной блокировкой прокрутки с счётчиком вложенности; корректно блокирует тач-скролл на мобильных. */ export function useGlobalScrollLock() { const lockCount = useState('global-scroll-lock-count', () => 0); function scrollLock() { lockCount.value++; if (lockCount.value === 1 && import.meta.client) { document.body.style.overflow = 'hidden'; document.addEventListener('touchmove', preventTouchScroll, { passive: false }); } } function scrollUnlock() { lockCount.value--; if (lockCount.value <= 0) { lockCount.value = 0; if (import.meta.client) { document.body.style.overflow = ''; document.removeEventListener('touchmove', preventTouchScroll); } } } return { scrollLock, scrollUnlock }; }