import { isBrowser , BridgeEvents } from '@vue-devtools/shared-utils' ; import { BackendContext , DevtoolsBackend } from '@vue-devtools/app-backend-api' ; import { highlight , unHighlight } from './highlighter' ; import { ComponentInstance } from '@vue/devtools-api' ; export default class ComponentPicker { ctx : BackendContext ; selectedInstance : ComponentInstance ; selectedBackend : DevtoolsBackend ; constructor ( ctx ) { this . ctx = ctx ; this . bindMethods ( ) ; } /**
     * Adds event listeners for mouseover and mouseup
     */ startSelecting ( ) { if ( ! isBrowser ) return ; window . addEventListener ( 'mouseover' , this . elementMouseOver , true ) ; window . addEventListener ( 'click' , this . elementClicked , true ) ; window . addEventListener ( 'mouseout' , this . cancelEvent , true ) ; window . addEventListener ( 'mouseenter' , this . cancelEvent , true ) ; window . addEventListener ( 'mouseleave' , this . cancelEvent , true ) ; window . addEventListener ( 'mousedown' , this . cancelEvent , true ) ; window . addEventListener ( 'mouseup' , this . cancelEvent , true ) ; } /**
     * Removes event listeners
     */ stopSelecting ( ) { if ( ! isBrowser ) return ; window . removeEventListener ( 'mouseover' , this . elementMouseOver , true ) ; window . removeEventListener ( 'click' , this . elementClicked , true ) ; window . removeEventListener ( 'mouseout' , this . cancelEvent , true ) ; window . removeEventListener ( 'mouseenter' , this . cancelEvent , true ) ; window . removeEventListener ( 'mouseleave' , this . cancelEvent , true ) ; window . removeEventListener ( 'mousedown' , this . cancelEvent , true ) ; window . removeEventListener ( 'mouseup' , this . cancelEvent , true ) ; unHighlight ( ) ; } /**
     * Highlights a component on element mouse over
     */ async elementMouseOver ( e ) { this . cancelEvent ( e ) ; const el = e . target ; if ( el ) { await this . selectElementComponent ( el ) ; } unHighlight ( ) ; if ( this . selectedInstance ) { highlight ( this . selectedInstance , this . selectedBackend , this . ctx ) ; } } async selectElementComponent ( el ) { for ( const backend of this . ctx . backends ) { const instance = await backend . api . getElementComponent ( el ) ; if ( instance ) { this . selectedInstance = instance ; this . selectedBackend = backend ; return ; } } this . selectedInstance = null ; this . selectedBackend = null ; } /**
     * Selects an instance in the component view
     */ async elementClicked ( e ) { this . cancelEvent ( e ) ; if ( this . selectedInstance && this . selectedBackend ) { const parentInstances = await this . selectedBackend . api . walkComponentParents ( this . selectedInstance ) ; this . ctx . bridge . send ( BridgeEvents . TO_FRONT_COMPONENT_PICK , { id : this . selectedInstance . __VUE_DEVTOOLS_UID__ , parentIds : parentInstances . map ( i => i . __VUE_DEVTOOLS_UID__ ) } ) ; } else { this . ctx . bridge . send ( BridgeEvents . TO_FRONT_COMPONENT_PICK_CANCELED , null ) ; } this . stopSelecting ( ) ; } /**
     * Cancel a mouse event
     */ cancelEvent ( e ) { e . stopImmediatePropagation ( ) ; e . preventDefault ( ) ; } /**
     * Bind class methods to the class scope to avoid rebind for event listeners
     */ bindMethods ( ) { this . startSelecting = this . startSelecting . bind ( this ) ; this . stopSelecting = this . stopSelecting . bind ( this ) ; this . elementMouseOver = this . elementMouseOver . bind ( this ) ; this . elementClicked = this . elementClicked . bind ( this ) ; } }