import { Overlay } from '../overlay' ; import { batchedUpdates } from './batchUpdate' ; /**
 管理一个 portalContainer 上的多个 overlay 实例
 - 支持容器内按下`Escape`来关闭最近打开的浮层
 - 支持在容器内点击浮层外部区块来关闭最近打开的浮层
 - 支持浮层关闭事件的防抖，避免 `props.onRequestClose` 在不期望的情况下被调用
*/ export class OverlayManager { static REX_OVERFLOW_HIDDEN_CLS = 'rex-overflow-hidden' ; static REX_OVERFLOW_FORCE_HIDDEN_CLS = 'rex-overflow-force-hidden' ; constructor ( readonly portalContainer ) { this . portalContainer = portalContainer ; } readonly stack : Overlay [ ] = [ ] ; private disableScrollCount = 0 ; private forceDisableScrollCount = 0 ; private getLastOpenOverlay ( ) { return this . stack [ this . stack . length - 1 ] ; } add ( overlay ) { requestAnimationFrame ( ( ) => { this . stack . push ( overlay ) ; this . portalContainer . dataset . rexOverlayCount = String ( this . stack . length ) ; if ( overlay . props . disableScroll === true ) { this . disableScrollCount += 1 ; if ( this . disableScrollCount === 1 ) { this . portalContainer . classList . add ( OverlayManager . REX_OVERFLOW_HIDDEN_CLS ) ; } } else if ( overlay . props . disableScroll === 'force' ) { this . forceDisableScrollCount += 1 ; if ( this . forceDisableScrollCount === 1 ) { this . portalContainer . classList . add ( OverlayManager . REX_OVERFLOW_FORCE_HIDDEN_CLS ) ; } } if ( this . stack . length === 1 ) { this . setupEventListeners ( ) ; } } ) ; } delete ( overlay ) { const index = this . stack . indexOf ( overlay ) ; if ( index === - 1 ) { return ; } this . stack . splice ( index , 1 ) ; this . portalContainer . dataset . rexOverlayCount = String ( this . stack . length ) ; if ( overlay . props . disableScroll === true ) { this . disableScrollCount -= 1 ; if ( this . disableScrollCount === 0 ) { this . portalContainer . classList . remove ( OverlayManager . REX_OVERFLOW_HIDDEN_CLS ) ; } } else if ( overlay . props . disableScroll === 'force' ) { this . forceDisableScrollCount -= 1 ; if ( this . forceDisableScrollCount === 0 ) { this . portalContainer . classList . remove ( OverlayManager . REX_OVERFLOW_FORCE_HIDDEN_CLS ) ; } } if ( this . stack . length === 0 ) { this . disposeEventListeners ( ) ; } } private setupEventListeners ( ) { document . addEventListener ( 'keydown' , this . handleKeydown ) ; document . addEventListener ( 'click' , this . handleClick , { capture : true } ) ; } private disposeEventListeners ( ) { document . removeEventListener ( 'keydown' , this . handleKeydown ) ; document . removeEventListener ( 'click' , this . handleClick ) ; } private handleKeydown = ( e ) => { batchedUpdates ( ( ) => { const targetNode = e . target as Node ; if ( ! this . portalContainer . contains ( targetNode ) ) { return ; } if ( e . key === 'Escape' ) { const lastOpenOverlay = this . getLastOpenOverlay ( ) ; if ( lastOpenOverlay == null ) { return ; } const { canCloseByEsc , onRequestClose } = lastOpenOverlay . props ; if ( canCloseByEsc ) { onRequestClose ?. ( 'Escape' ) ; } } } ) ; } ; private handleClick = ( e ) => { batchedUpdates ( ( ) => { const targetNode = e . target as Node ; if ( ! this . portalContainer . contains ( targetNode ) ) { return ; } const lastOpenOverlay = this . getLastOpenOverlay ( ) ; if ( lastOpenOverlay == null ) { return ; } const { canCloseByOutSideClick , onRequestClose } = lastOpenOverlay . props ; if ( canCloseByOutSideClick && ! lastOpenOverlay . isInsideClick ( targetNode ) ) { onRequestClose ?. ( 'outside-click' ) ; } } ) ; } ; }