import { tryUntil } from '../async/promise' ; import { getNodeName , getParentNode } from './api' ; export type ScrollStrategy = 'none' | 'native' | 'pseudo' ; const $html = document . documentElement ; const initiatorSet = new Set ( ) ; export abstract class ScrollUtils { /** Check vertical scroll based on content height */ static hasVerticalScroll ( target = $html ) { return target . scrollHeight > target . clientHeight ; } param strategy public static lock ( strategy ? ) { const hasScroll = ScrollUtils . hasVerticalScroll ( ) ; if ( strategy && strategy !== 'none' && hasScroll ) { $html . classList . add ( `esl-${ strategy }-scroll` ) ; } $html . classList . add ( 'esl-disable-scroll' ) ; } /**
     * Enable scroll on the page.
     * */ public static unlock ( ) { $html . classList . remove ( 'esl-disable-scroll' , 'esl-pseudo-scroll' , 'esl-native-scroll' ) ; } param initiator param strategy public static requestLock ( initiator , strategy ? ) { initiator && initiatorSet . add ( initiator ) ; ( initiatorSet . size > 0 ) && ScrollUtils . lock ( strategy ) ; } param initiator param strategy public static requestUnlock ( initiator , strategy ? ) { initiator && initiatorSet . delete ( initiator ) ; ( initiatorSet . size === 0 ) && ScrollUtils . unlock ( ) ; } } param element param list export function getListScrollParents ( element , list = [ ] ) { const scrollParent = getScrollParent ( element ) ; const isBody = scrollParent === element . ownerDocument ?. body ; const target = isBody ? isScrollParent ( scrollParent ) ? scrollParent : [ ] : scrollParent ; const updatedList = list . concat ( target ) ; return isBody ? updatedList : updatedList . concat ( getListScrollParents ( getParentNode ( scrollParent ) ) ) ; } param node export function getScrollParent ( node ) { if ( [ 'html' , 'body' , '#document' ] . indexOf ( getNodeName ( node ) ) >= 0 ) { return node . ownerDocument ?. body as Element ; } if ( node instanceof HTMLElement && isScrollParent ( node as Element ) ) { return node as Element ; } return getScrollParent ( getParentNode ( node as Element ) ) ; } param element export function isScrollParent ( element ) { const { overflow , overflowX , overflowY } = getComputedStyle ( element ) ; return /auto|scroll|overlay|hidden/ . test ( overflow + overflowY + overflowX ) ; } param element param options export function scrollIntoView ( element , options ? ) { let same = 0 ; let lastLeft ; let lastTop ; const check = ( ) : boolean => { const { top , left } = element . getBoundingClientRect ( ) ; if ( top !== lastTop || left !== lastLeft ) { same = 0 ; lastTop = top ; lastLeft = left ; } return same ++ > 2 ; } ; element . scrollIntoView ( options ) ; return tryUntil ( check , 333 , 30 ) ; }