module powerbi . extensibility . visual { import Selection = d3 . Selection ; import UpdateSelection = d3 . selection . Update ; import ISQExpr = powerbi . data . ISQExpr ; import Selector = powerbi . data . Selector ; import ISemanticFilter = data . ISemanticFilter ; import ISelectionHandler = powerbi . extensibility . utils . interactivity . ISelectionHandler ; import SelectableDataPoint = powerbi . extensibility . utils . interactivity . SelectableDataPoint ; import IInteractiveBehavior = powerbi . extensibility . utils . interactivity . IInteractiveBehavior ; import IInteractivityService = powerbi . extensibility . utils . interactivity . IInteractivityService ; export interface ChicletSlicerBehaviorOptions { slicerItemContainers : Selection < SelectableDataPoint > ; slicerItemLabels : Selection < any > ; slicerItemInputs : Selection < any > ; slicerClear : Selection < any > ; dataPoints : ChicletSlicerDataPoint [ ] ; interactivityService : IInteractivityService ; slicerSettings : ChicletSlicerSettings ; identityFields : ISQExpr [ ] ; isHighContrastMode : boolean ; } export class ChicletSlicerWebBehavior implements IInteractiveBehavior { private slicers : Selection < SelectableDataPoint > ; private slicerItemLabels : Selection < any > ; private slicerItemInputs : Selection < any > ; private interactivityService : IInteractivityService ; private slicerSettings : ChicletSlicerSettings ; private options : ChicletSlicerBehaviorOptions ; private selectionHandler : ISelectionHandler ; /**
         * Public for testability.
         */ public dataPoints : ChicletSlicerDataPoint [ ] ; public bindEvents ( options , selectionHandler ) { const slicers = this . slicers = options . slicerItemContainers , slicerClear = options . slicerClear ; this . slicerItemLabels = options . slicerItemLabels ; this . slicerItemInputs = options . slicerItemInputs ; this . dataPoints = options . dataPoints ; this . interactivityService = options . interactivityService ; this . slicerSettings = options . slicerSettings ; this . options = options ; this . selectionHandler = selectionHandler ; this . loadSelection ( ) ; slicers . on ( "mouseover" , ( dataPoint ) => { if ( dataPoint . selectable ) { dataPoint . mouseOver = true ; dataPoint . mouseOut = false ; this . renderMouseover ( ) ; } } ) ; slicers . on ( "mouseout" , ( dataPoint ) => { if ( dataPoint . selectable ) { dataPoint . mouseOver = false ; dataPoint . mouseOut = true ; this . renderMouseover ( ) ; } } ) ; slicers . on ( "click" , ( dataPoint , index ) => { if ( ! dataPoint . selectable ) { return ; } ( d3 . event as MouseEvent ) . preventDefault ( ) ; let settings = this . slicerSettings ; let multiselect = settings . general . multiselect ; let selectedIndexes = jQuery . map ( this . dataPoints , ( dataPoint , index ) => { if ( dataPoint . selected ) { return index ; } ; } ) ; if ( settings . general . forcedSelection && selectedIndexes . length === 1 ) { let availableDataPoints = jQuery . map ( this . dataPoints , ( dataPoint , index ) => { if ( ! dataPoint . filtered ) { return dataPoint ; } ; } ) ; if ( availableDataPoints [ index ] && this . dataPoints [ selectedIndexes [ 0 ] ] . identity === availableDataPoints [ index ] . identity ) { return ; } } if ( ( d3 . event as MouseEvent ) . altKey && multiselect ) { let selIndex = selectedIndexes . length > 0 ? ( selectedIndexes [ selectedIndexes . length - 1 ] ) : 0 ; if ( selIndex > index ) { [ index , selIndex ] = [ selIndex , index ] ; } selectionHandler . handleClearSelection ( ) ; for ( let i = selIndex ; i <= index ; i ++ ) { selectionHandler . handleSelection ( this . dataPoints [ i ] , true ) ; } } else if ( ( ( ( d3 . event as MouseEvent ) . ctrlKey || ( d3 . event as MouseEvent ) . metaKey ) ) || multiselect ) { selectionHandler . handleSelection ( dataPoint , true ) ; } else { selectionHandler . handleSelection ( dataPoint , false ) ; } this . saveSelection ( ) ; } ) ; slicerClear . on ( "click" , ( d ) => { const settings = this . slicerSettings ; if ( settings . general . forcedSelection ) { return false ; } selectionHandler . handleClearSelection ( ) ; this . saveSelection ( ) ; } ) ; this . forceSelection ( ) ; } private forceSelection ( ) { if ( ! this . slicerSettings . general . forcedSelection ) { return ; } const isSelected = _ . some ( this . dataPoints , ( dataPoint ) => dataPoint . selected ) ; if ( ! isSelected ) { for ( let i = 0 ; i < this . dataPoints . length ; i ++ ) { const dataPoint = this . dataPoints [ i ] ; if ( dataPoint . selectable && ! dataPoint . filtered ) { this . selectionHandler . handleSelection ( dataPoint , false ) ; this . saveSelection ( ) ; break ; } } } } public loadSelection ( ) { this . interactivityService . applySelectionFromFilter ( this . slicerSettings . general . filter ) ; } public saveSelection ( ) { this . selectionHandler . applySelectionFilter ( ) ; } public renderSelection ( hasSelection ) { if ( ! hasSelection && ! this . interactivityService . isSelectionModeInverted ( ) ) { this . slicers . style ( "background" , this . slicerSettings . slicerText . unselectedColor ) ; } else { this . styleSlicerInputs ( this . slicers , hasSelection ) ; } } private renderMouseover ( ) { this . slicerItemLabels . style ( { "color" : ( dataPoint ) => { if ( dataPoint . mouseOver ) { return this . slicerSettings . slicerText . hoverColor ; } if ( dataPoint . mouseOut ) { return this . slicerSettings . slicerText . fontColor ; } } , "opacity" : ( dataPoint ) => { if ( dataPoint . selectable ) { if ( dataPoint . mouseOver ) { return this . options . isHighContrastMode ? ChicletSlicer . HoveredTextOpacity : ChicletSlicer . DefaultOpacity ; } } return ChicletSlicer . DefaultOpacity ; } } ) ; } public styleSlicerInputs ( slicers , hasSelection ) { let settings = this . slicerSettings , isHighContrastMode = this . options . isHighContrastMode ; slicers . each ( function ( dataPoint ) { d3 . select ( this ) . style ( { "background" : dataPoint . selectable ? ( dataPoint . selected ? settings . slicerText . selectedColor : settings . slicerText . unselectedColor ) : settings . slicerText . disabledColor , "opacity" : ( ) => { if ( isHighContrastMode ) { let opacity = dataPoint . selectable ? ( dataPoint . selected ? ChicletSlicer . DefaultOpacity : ChicletSlicer . DimmedOpacity ) : ChicletSlicer . DisabledOpacity ; return opacity ; } return ChicletSlicer . DefaultOpacity ; } } ) ; d3 . select ( this ) . classed ( "slicerItem-disabled" , ! dataPoint . selectable ) ; } ) ; } } }