import React from 'react' ; import { defer , merge , NEVER , Observable , race , Subject , timer } from 'rxjs' ; import * as op from 'rxjs/operators' ; import { PopupInteractionKind } from '../popup' ; type ReactDivMouseEvent = React . MouseEvent < HTMLDivElement > ; type ReactDivFocusEvent = React . FocusEvent < HTMLDivElement > ; const timeout = ( ms ) => timer ( ms ) . pipe ( op . mapTo ( { type : 'timeout' } ) ) ; function hoverTimeout ( hoverDelay , start$ , stop$ ) { return start$ . pipe ( op . switchMapTo ( race ( timeout ( hoverDelay ) , stop$ ) ) , op . filter ( ( result ) => result . type === 'timeout' ) , op . mapTo ( null as void ) ) ; } type HoverInput = { type : 'click-target' ; event : ReactDivMouseEvent ; } | { type : 'enter-target' ; event : ReactDivMouseEvent ; } | { type : 'leave-target' ; event : ReactDivMouseEvent ; } | { type : 'enter-content' ; event : ReactDivMouseEvent ; } | { type : 'leave-content' ; event : ReactDivMouseEvent ; } | { type : 'focus-target' ; event : ReactDivFocusEvent ; } | { type : 'blur-target' ; event : ReactDivFocusEvent ; } ; export class PopupInteractionManager extends Subject < HoverInput > { constructor ( readonly opts , readonly isVisible ) { super ( ) ; this . opts = opts ; this . isVisible = isVisible ; } onTargetFocus = ( event ) => { this . next ( { type : 'focus-target' , event } ) ; } ; onTargetBlur = ( event ) => { this . next ( { type : 'blur-target' , event } ) ; } ; onTargetClick = ( event ) => { this . next ( { type : 'click-target' , event } ) ; } ; onTargetMouseEnter = ( event ) => { this . next ( { type : 'enter-target' , event } ) ; } ; onTargetMouseLeave = ( event ) => { this . next ( { type : 'leave-target' , event } ) ; } ; onContentMouseEnter = ( event ) => { this . next ( { type : 'enter-content' , event } ) ; } ; onContentMouseLeave = ( event ) => { this . next ( { type : 'leave-content' , event } ) ; } ; focusTarget$ = this . pipe ( op . filter ( ( intent ) => intent . type === 'focus-target' ) ) ; blurTarget$ = this . pipe ( op . filter ( ( intent ) => intent . type === 'blur-target' ) ) ; clickTarget$ = this . pipe ( op . filter ( ( intent ) => intent . type === 'click-target' ) ) ; enterTarget$ = this . pipe ( op . filter ( ( intent ) => intent . type === 'enter-target' ) ) ; leaveTarget$ = this . pipe ( op . filter ( ( intent ) => intent . type === 'leave-target' ) ) ; enter$ = this . pipe ( op . filter ( ( intent ) => intent . type === 'enter-target' || intent . type === 'enter-content' ) ) ; leave$ = this . pipe ( op . filter ( ( intent ) => intent . type === 'leave-target' || intent . type === 'leave-content' ) ) ; action$ = defer ( ( ) => { const { hoverDelay , interactionKind } = this . opts ; let open$ ; let close$ ; if ( interactionKind === 'hover' ) { open$ = hoverTimeout ( hoverDelay , this . enter$ , this . leave$ ) ; close$ = hoverTimeout ( hoverDelay , this . leave$ , this . enter$ ) ; } else if ( interactionKind === 'hover-target' ) { open$ = hoverTimeout ( hoverDelay , this . enterTarget$ , this . leaveTarget$ ) ; close$ = hoverTimeout ( hoverDelay , this . leaveTarget$ , this . enterTarget$ ) ; } else { open$ = this . clickTarget$ ; close$ = this . clickTarget$ ; } const openByFocus$ = this . opts . canOpenByFocus ? this . focusTarget$ . pipe ( op . filter ( ( ) => ! this . isVisible ( ) ) , op . map ( ( event ) => ( { type : 'open' , reason : 'focus' } ) ) ) : NEVER ; const closeByFocus$ = this . opts . canCloseByBlur ? this . blurTarget$ . pipe ( op . filter ( ( ) => this . isVisible ( ) ) , op . map ( ( event ) => ( { type : 'close' , reason : 'blur' } ) ) ) : NEVER ; return merge ( openByFocus$ , closeByFocus$ , open$ . pipe ( op . filter ( ( ) => ! this . isVisible ( ) ) , op . map ( ( reason ) => ( { type : 'open' , reason } ) ) ) , close$ . pipe ( op . filter ( ( ) => this . isVisible ( ) ) , op . map ( ( reason ) => ( { type : 'close' , reason } ) ) ) ) ; } ) ; }