import * as d3 from "d3" ; const getEvent = ( ) : MouseEvent => < MouseEvent > require ( "d3-selection" ) . event ; import { SankeyDiagramLink , SankeyDiagramNode } from "./dataInterfaces" ; import * as sankeyDiagramUtils from "./utils" ; type Selection < T > = d3 . Selection < any , T , any , any > ; import { interactivitySelectionService , interactivityBaseService } from "powerbi-visuals-utils-interactivityutils" ; import SelectableDataPoint = interactivitySelectionService . SelectableDataPoint ; import IInteractiveBehavior = interactivityBaseService . IInteractiveBehavior ; import IInteractivityService = interactivityBaseService . IInteractivityService ; import ISelectionHandler = interactivityBaseService . ISelectionHandler ; import IBehaviorOptions = interactivityBaseService . IBehaviorOptions ; export interface SankeyDiagramBehaviorOptions extends IBehaviorOptions < SelectableDataPoint > { nodes : Selection < SankeyDiagramNode > ; links : Selection < SankeyDiagramLink > ; clearCatcher : Selection < any > ; interactivityService : IInteractivityService < SelectableDataPoint > ; } export class SankeyDiagramBehavior implements IInteractiveBehavior { private behaviorOptions : SankeyDiagramBehaviorOptions ; private selectionHandler : ISelectionHandler ; private selectedDataPoints : SelectableDataPoint [ ] ; public static create ( ) { return new SankeyDiagramBehavior ( ) ; } constructor ( ) { this . createAnEmptySelectedDataPoints ( ) ; } public bindEvents ( behaviorOptions , selectionHandler ) { this . behaviorOptions = behaviorOptions ; this . selectionHandler = selectionHandler ; this . bindClickEventToNodes ( ) ; this . bindClickEventToLinks ( ) ; this . bindClickEventToClearCatcher ( ) ; } private bindClickEventToNodes ( ) { this . behaviorOptions . nodes . on ( "click" , ( node ) => { let selectableDataPoints = node . selectableDataPoints ; if ( node . cloneLink ) { selectableDataPoints = selectableDataPoints . concat ( node . cloneLink . selectableDataPoints ) ; } this . clearSelection ( ) ; if ( ! sankeyDiagramUtils . areDataPointsSelected ( this . selectedDataPoints , selectableDataPoints ) ) { selectableDataPoints . forEach ( ( subDataPoint ) => { this . selectionHandler . handleSelection ( subDataPoint , true ) ; } ) ; this . selectedDataPoints = selectableDataPoints ; } else { this . createAnEmptySelectedDataPoints ( ) ; } } ) ; this . behaviorOptions . nodes . on ( "contextmenu" , ( datum ) => { const event = ( < MouseEvent > getEvent ( ) ) || < MouseEvent > window . event ; if ( event ) { this . selectionHandler . handleContextMenu ( < any > datum , { x : event . clientX , y : event . clientY } ) ; event . preventDefault ( ) ; } } ) ; } private bindClickEventToLinks ( ) { this . behaviorOptions . links . on ( "click" , ( link ) => { this . selectionHandler . handleSelection ( link , getEvent ( ) . ctrlKey ) ; this . createAnEmptySelectedDataPoints ( ) ; } ) ; this . behaviorOptions . links . on ( "contextmenu" , ( datum ) => { const event = ( < MouseEvent > getEvent ( ) ) || < MouseEvent > window . event ; if ( event ) { this . selectionHandler . handleContextMenu ( datum , { x : event . clientX , y : event . clientY } ) ; event . preventDefault ( ) ; } } ) ; } private bindClickEventToClearCatcher ( ) { this . behaviorOptions . clearCatcher . on ( "contextmenu" , ( ) => { const event = ( < MouseEvent > getEvent ( ) ) || < MouseEvent > window . event ; if ( event ) { this . selectionHandler . handleContextMenu ( null , { x : event . clientX , y : event . clientY } ) ; event . preventDefault ( ) ; } } ) ; this . behaviorOptions . clearCatcher . on ( "click" , ( ) => { this . clearSelection ( ) ; this . createAnEmptySelectedDataPoints ( ) ; } ) ; } private clearSelection ( ) { this . selectionHandler . handleClearSelection ( ) ; } private createAnEmptySelectedDataPoints ( ) { this . selectedDataPoints = [ ] ; } public renderSelection ( hasSelection ) { sankeyDiagramUtils . updateFillOpacity ( this . behaviorOptions . links , this . behaviorOptions . interactivityService , hasSelection ) ; sankeyDiagramUtils . updateFillOpacity ( this . behaviorOptions . nodes , this . behaviorOptions . interactivityService , hasSelection ) ; } }