-
-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathbehavior.ts
137 lines (127 loc) · 4.83 KB
/
behavior.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import { interactivityBaseService } from 'powerbi-visuals-utils-interactivityutils';
import IBehaviorOptions = interactivityBaseService.IBehaviorOptions;
import BaseDataPoint = interactivityBaseService.BaseDataPoint;
import IInteractiveBehavior = interactivityBaseService.IInteractiveBehavior;
import ISelectionHandler = interactivityBaseService.ISelectionHandler;
import { IHtmlEntry, IViewModel } from './view-model';
import { VisualConstants } from './visual-constants';
import { shouldDimPoint } from './domain-utils';
/**
* Behavior options for interactivity.
*/
export interface IHtmlBehaviorOptions<
SelectableDataPoint extends BaseDataPoint
> extends IBehaviorOptions<SelectableDataPoint> {
// Elements denoting a selectable data point in the visual
pointSelection: d3.Selection<HTMLDivElement, IHtmlEntry, any, any>;
// Element performing the role of clear-catcher (clears selection)
clearCatcherSelection: d3.Selection<HTMLDivElement, any, any, any>;
// Visual ViewModel
viewModel: IViewModel;
}
/**
* Used to control and bind visual interaction and behavior.
*/
export class BehaviorManager<SelectableDataPoint extends BaseDataPoint>
implements IInteractiveBehavior {
// Interactivity options
protected options: IHtmlBehaviorOptions<SelectableDataPoint>;
// Handles selection event delegation to the visual host
protected selectionHandler: ISelectionHandler;
/**
* Apply click behavior to selections as necessary.
*/
protected bindClick() {
const {
pointSelection,
viewModel: { hasCrossFiltering }
} = this.options;
pointSelection.on('click', (event, d) =>
hasCrossFiltering ? this.handleSelectionClick(event, d) : null
);
}
/**
* Apply context menu behavior to selections as necessary.
*/
protected bindContextMenu() {
const { pointSelection, clearCatcherSelection } = this.options;
pointSelection.on('contextmenu', (event, d) =>
this.handleContextMenu(event, d)
);
clearCatcherSelection.on('contextmenu', event =>
this.handleContextMenu(event, null)
);
}
/**
* Abstraction of common click event handling for a `SelectableDataPoint`
*
* @param event - click event
* @param d - datum from selection
*/
private handleSelectionClick(event: MouseEvent, d: IHtmlEntry) {
event.preventDefault();
event.stopPropagation();
this.selectionHandler.handleSelection(d, event.ctrlKey);
}
/**
* Abstraction of common context menu event handling for a `SelectableDataPoint`.
*
* @param event - click event
* @param d - datum from selection
*/
handleContextMenu(event: MouseEvent, d: IHtmlEntry) {
event.preventDefault();
event.stopPropagation();
event &&
this.selectionHandler.handleContextMenu(d, {
x: event.clientX,
y: event.clientY
});
}
/**
* Apply click behavior to the clear-catcher (clearing active selections if clicked).
*/
protected bindClearCatcher() {
const {
clearCatcherSelection,
viewModel: { hasCrossFiltering }
} = this.options;
clearCatcherSelection.on('click', event => {
if (hasCrossFiltering) {
event.preventDefault();
event.stopPropagation();
const mouseEvent: MouseEvent = <MouseEvent>event;
mouseEvent && this.selectionHandler.handleClearSelection();
}
});
}
/**
* Ensure that class has necessary options and tooling to perform interactivity/behavior requirements as needed.
*
* @param options - interactivity & behavior options
* @param selectionHandler - selection handler instance
*/
public bindEvents(
options: IHtmlBehaviorOptions<SelectableDataPoint>,
selectionHandler: ISelectionHandler
): void {
this.options = options;
this.selectionHandler = selectionHandler;
this.bindClick();
this.bindContextMenu();
this.bindClearCatcher();
}
/**
* Handle visual effects on selection and interactivity events.
*
* @param hasSelection - whether visual has selection state or not
*/
public renderSelection(hasSelection: boolean): void {
const { pointSelection, viewModel } = this.options;
// Update viewModel selection state to match current state
viewModel.hasSelection = hasSelection;
pointSelection.classed(VisualConstants.dom.unselectedClassSelector, d =>
shouldDimPoint(hasSelection, d.selected)
);
}
}