import { SideObject } from '@popperjs/core' ; import getClippingRect from '@popperjs/core/lib/dom-utils/getClippingRect' ; import getCompositeRect from '@popperjs/core/lib/dom-utils/getCompositeRect' ; import getComputedStyle from '@popperjs/core/lib/dom-utils/getComputedStyle' ; import getDocumentElement from '@popperjs/core/lib/dom-utils/getDocumentElement' ; import getDocumentRect from '@popperjs/core/lib/dom-utils/getDocumentRect' ; import getHTMLElementScroll from '@popperjs/core/lib/dom-utils/getHTMLElementScroll' ; import getLayoutRect from '@popperjs/core/lib/dom-utils/getLayoutRect' ; import getNodeName from '@popperjs/core/lib/dom-utils/getNodeName' ; import getNodeScroll from '@popperjs/core/lib/dom-utils/getNodeScroll' ; import getOffsetParent from '@popperjs/core/lib/dom-utils/getOffsetParent' ; import getParentNode from '@popperjs/core/lib/dom-utils/getParentNode' ; import getViewportRect from '@popperjs/core/lib/dom-utils/getViewportRect' ; import getWindow from '@popperjs/core/lib/dom-utils/getWindow' ; import getWindowScroll from '@popperjs/core/lib/dom-utils/getWindowScroll' ; import getWindowScrollBarX from '@popperjs/core/lib/dom-utils/getWindowScrollBarX' ; import { isHTMLElement } from '@popperjs/core/lib/dom-utils/instanceOf' ; import isScrollParent from '@popperjs/core/lib/dom-utils/isScrollParent' ; import listScrollParents from '@popperjs/core/lib/dom-utils/listScrollParents' ; import ResizeObserver from 'resize-observer-polyfill' ; import { fromEvent , merge , Observable } from 'rxjs' ; import * as op from 'rxjs/operators' ; function isWindow ( arg ) { return arg . toString ( ) === '[object Window]' ; } function isBody ( arg ) { return getNodeName ( arg ) === 'body' ; } function isHtml ( arg ) { return getNodeName ( arg ) === 'html' ; } function isBodyOrHtml ( arg ) { return isBody ( arg ) || isHtml ( arg ) ; } function accumulateScrollOffset ( start , stop , scrollOffsetKey ) { let result = 0 ; let elem = start ; while ( elem != null ) { result += elem [ scrollOffsetKey ] ; if ( elem === stop || ( isWindow ( stop ) && isBodyOrHtml ( elem ) ) ) { break ; } elem = elem . parentElement ; } if ( isWindow ( stop ) ) { result += getWindowScroll ( elem ) [ scrollOffsetKey ] ; } return result ; } /**
 * 获取 target 相对于 base 的布局大小和相对位置。
 * 注意该方法会考虑滚动所带来的影响
 */ function getRelativeLayoutRect ( base , target ) { if ( isWindow ( target ) || getNodeName ( target ) === 'html' ) { return { left : 0 , right : window . innerWidth , top : 0 , bottom : window . innerHeight , } ; } let deltaX = 0 ; let deltaY = 0 ; let elem = target ; while ( elem != null && elem != base ) { deltaY += elem . offsetTop ; deltaX += elem . offsetLeft ; const offsetParent = getOffsetParent ( elem ) as HTMLElement | Window ; deltaY -= accumulateScrollOffset ( elem . parentElement , offsetParent , 'scrollTop' ) ; deltaX -= accumulateScrollOffset ( elem . parentElement , offsetParent , 'scrollLeft' ) ; if ( isWindow ( offsetParent ) ) { break ; } deltaY += offsetParent . clientTop ; deltaX += offsetParent . clientLeft ; elem = offsetParent ; } return { top : deltaY , bottom : deltaY + target . offsetHeight , left : deltaX , right : deltaX + target . offsetWidth , } ; } function findCommonOffsetAncestor ( target , scrollParent ) { if ( isWindow ( scrollParent ) ) { return scrollParent ; } const offsetParents = listOffsetParents ( target ) ; if ( offsetParents . includes ( scrollParent ) ) { return scrollParent ; } return getOffsetParent ( scrollParent ) ; } function getBoundingClientRect ( target ) { if ( domUtils . isWindow ( target ) ) { return { left : 0 , top : 0 , width : window . innerWidth , height : window . innerHeight } ; } else { return target . getBoundingClientRect ( ) ; } } function listOffsetParents ( target ) { const result = [ ] ; let elem = target ; while ( true ) { if ( isWindow ( elem ) ) { break ; } elem = getOffsetParent ( elem ) ; result . push ( elem ) ; } return result ; } function fromScrollEvent ( element ) { return fromEvent ( element , 'scroll' ) ; } function fromPassiveScrollEvent ( element ) { return fromEvent ( element , 'scroll' , { passive : true } ) ; } function fromResizeEvent ( element ) { if ( isWindow ( element ) ) { return fromEvent < Event > ( element , 'resize' ) ; } return new Observable ( ( subscriber ) => { const resizeObserver = new ResizeObserver ( ( entries ) => { subscriber . next ( entries ) ; } ) ; resizeObserver . observe ( element as HTMLElement ) ; return ( ) => { resizeObserver . disconnect ( ) ; } ; } ) ; } function getScrollParent ( elem ) { if ( [ 'html' , 'body' , '#document' ] . includes ( getNodeName ( elem ) ) ) { return getWindow ( elem ) ; } if ( isHTMLElement ( elem ) && isScrollParent ( elem ) ) { return elem ; } return getScrollParent ( getParentNode ( elem ) as HTMLElement ) ; } function getRichVisibleRectsStream ( target ) { const scrollParent = getScrollParent ( target ) ; const commonOffsetAncestor = findCommonOffsetAncestor ( target , scrollParent ) ; return merge ( fromScrollEvent ( scrollParent ) , fromResizeEvent ( scrollParent ) , fromResizeEvent ( target ) ) . pipe ( op . map ( ( ) => ( { targetRect : getRelativeLayoutRect ( commonOffsetAncestor , target ) , scrollParentRect : getRelativeLayoutRect ( commonOffsetAncestor , scrollParent ) , } ) ) , op . map ( ( { scrollParentRect , targetRect } ) => ( { targetRect , scrollParentRect , offsetY : Math . max ( 0 , scrollParentRect . top - targetRect . top ) , offsetX : Math . max ( 0 , scrollParentRect . left - targetRect . left ) , clipRect : { left : Math . max ( targetRect . left , scrollParentRect . left ) , top : Math . max ( targetRect . top , scrollParentRect . top ) , right : Math . min ( targetRect . right , scrollParentRect . right ) , bottom : Math . min ( targetRect . bottom , scrollParentRect . bottom ) , } , } ) ) ) ; } function applyScrollDelta ( scrollParent , delta ) { if ( delta === 0 ) { return ; } if ( isWindow ( scrollParent ) ) { scrollParent . scrollTo ( scrollParent . scrollX , scrollParent . scrollY + delta ) ; } else { scrollParent . scrollTop += delta ; } } export const domUtils = { getClippingRect , getCompositeRect , getComputedStyle , getDocumentElement , getDocumentRect , getHTMLElementScroll , getLayoutRect , getNodeName , getNodeScroll , getOffsetParent : getOffsetParent as ( element : Element ) => Element | Window , getParentNode , getScrollParent , getViewportRect , getWindow : getWindow as ( node : Node ) => Window , getWindowScroll , getWindowScrollBarX , isScrollParent , listScrollParents , isWindow , isHtml , isBody , isBodyOrHtml , getRelativeLayoutRect , findCommonOffsetAncestor , getRichVisibleRectsStream , applyScrollDelta , fromScrollEvent , fromPassiveScrollEvent , fromResizeEvent , accumulateScrollOffset , getBoundingClientRect , } ;