Skip to content

Commit e23a577

Browse files
committed
move attachFxHandlers to fx.js
1 parent 088b0e0 commit e23a577

File tree

4 files changed

+347
-330
lines changed

4 files changed

+347
-330
lines changed

src/traces/sunburst/fx.js

+342
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
/**
2+
* Copyright 2012-2019, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
var d3 = require('d3');
12+
var Registry = require('../../registry');
13+
var appendArrayPointValue = require('../../components/fx/helpers').appendArrayPointValue;
14+
var Fx = require('../../components/fx');
15+
var Lib = require('../../lib');
16+
var Events = require('../../lib/events');
17+
18+
var helpers = require('./helpers');
19+
var pieHelpers = require('../pie/helpers');
20+
21+
var formatValue = pieHelpers.formatPieValue;
22+
var formatPercent = pieHelpers.formatPiePercent;
23+
24+
module.exports = function attachFxHandlers(sliceTop, entry, gd, cd, styleOne, constants) {
25+
var cd0 = cd[0];
26+
var trace = cd0.trace;
27+
var hierarchy = cd0.hierarchy;
28+
29+
var isSunburst = trace.type === 'sunburst';
30+
var isTreemap = trace.type === 'treemap';
31+
32+
// hover state vars
33+
// have we drawn a hover label, so it should be cleared later
34+
if(!('_hasHoverLabel' in trace)) trace._hasHoverLabel = false;
35+
// have we emitted a hover event, so later an unhover event should be emitted
36+
// note that click events do not depend on this - you can still get them
37+
// with hovermode: false or if you were earlier dragging, then clicked
38+
// in the same slice that you moused up in
39+
if(!('_hasHoverEvent' in trace)) trace._hasHoverEvent = false;
40+
41+
var onMouseOver = function(pt) {
42+
var fullLayoutNow = gd._fullLayout;
43+
44+
if(gd._dragging || fullLayoutNow.hovermode === false) return;
45+
46+
var traceNow = gd._fullData[trace.index];
47+
var cdi = pt.data.data;
48+
var ptNumber = cdi.i;
49+
50+
var _cast = function(astr) {
51+
return Lib.castOption(traceNow, ptNumber, astr);
52+
};
53+
54+
var hovertemplate = _cast('hovertemplate');
55+
var hoverinfo = Fx.castHoverinfo(traceNow, fullLayoutNow, ptNumber);
56+
var separators = fullLayoutNow.separators;
57+
58+
if(hovertemplate || (hoverinfo && hoverinfo !== 'none' && hoverinfo !== 'skip')) {
59+
var hoverCenterX;
60+
var hoverCenterY;
61+
if(isSunburst) {
62+
hoverCenterX = cd0.cx + pt.pxmid[0] * (1 - pt.rInscribed);
63+
hoverCenterY = cd0.cy + pt.pxmid[1] * (1 - pt.rInscribed);
64+
}
65+
if(isTreemap) {
66+
hoverCenterX = pt._hoverX;
67+
hoverCenterY = pt._hoverY;
68+
}
69+
70+
var hoverPt = {};
71+
var parts = [];
72+
var thisText = [];
73+
var hasFlag = function(flag) { return parts.indexOf(flag) !== -1; };
74+
var getVal = function(d) { return d.hasOwnProperty('v') ? d.v : d.value; };
75+
76+
if(hoverinfo) {
77+
parts = hoverinfo === 'all' ?
78+
traceNow._module.attributes.hoverinfo.flags :
79+
hoverinfo.split('+');
80+
}
81+
82+
hoverPt.label = helpers.getLabelStr(cdi.label);
83+
if(hasFlag('label') && hoverPt.label) thisText.push(hoverPt.label);
84+
85+
if(cdi.hasOwnProperty('v')) {
86+
hoverPt.value = cdi.v;
87+
hoverPt.valueLabel = formatValue(hoverPt.value, separators);
88+
if(hasFlag('value')) thisText.push(hoverPt.valueLabel);
89+
}
90+
91+
if(pt.parent) {
92+
hoverPt.currentPath = pt.currentPath = helpers.getPath(pt.parent.data);
93+
if(hasFlag('current path')) {
94+
thisText.push(hoverPt.currentPath);
95+
}
96+
}
97+
98+
var tx;
99+
var prevTx;
100+
var insertPercent = function() {
101+
if(tx !== prevTx) { // no need to add redundant info
102+
thisText.push(tx);
103+
prevTx = tx;
104+
}
105+
};
106+
107+
var val = getVal(cdi);
108+
109+
var ref2 = pt.parent;
110+
if(ref2 && getVal(ref2)) {
111+
hoverPt.percentParent = pt.percentParent = val / getVal(ref2);
112+
hoverPt.parentLabel = pt.parentLabel = helpers.getLabelString(ref2.data.data.label);
113+
if(hasFlag('percent parent')) {
114+
tx = formatPercent(hoverPt.percentParent, separators) + ' of ' + hoverPt.parentLabel;
115+
insertPercent();
116+
}
117+
}
118+
119+
var ref1 = entry;
120+
if(ref1 && getVal(ref1)) {
121+
hoverPt.percentVisible = pt.percentVisible = val / getVal(ref1);
122+
hoverPt.visibleLabel = pt.visibleLabel = helpers.getLabelString(ref1.data.data.label);
123+
if(hasFlag('percent visible')) {
124+
tx = formatPercent(hoverPt.percentVisible, separators) + ' of ' + hoverPt.visibleLabel;
125+
insertPercent();
126+
}
127+
}
128+
129+
var ref0 = hierarchy;
130+
if(ref0 && getVal(ref0)) {
131+
hoverPt.percentRoot = pt.percentRoot = val / getVal(ref0);
132+
hoverPt.rootLabel = pt.rootLabel = helpers.getLabelString(ref0.data.data.label);
133+
if(hasFlag('percent root')) {
134+
tx = formatPercent(hoverPt.percentRoot, separators) + ' of ' + hoverPt.rootLabel;
135+
insertPercent();
136+
}
137+
}
138+
139+
hoverPt.text = _cast('hovertext') || _cast('text');
140+
if(hasFlag('text')) {
141+
tx = hoverPt.text;
142+
if(Lib.isValidTextValue(tx)) thisText.push(tx);
143+
}
144+
145+
var hoverItems = {
146+
trace: traceNow,
147+
y: hoverCenterY,
148+
text: thisText.join('<br>'),
149+
name: (hovertemplate || hasFlag('name')) ? traceNow.name : undefined,
150+
color: _cast('hoverlabel.bgcolor') || cdi.color,
151+
borderColor: _cast('hoverlabel.bordercolor'),
152+
fontFamily: _cast('hoverlabel.font.family'),
153+
fontSize: _cast('hoverlabel.font.size'),
154+
fontColor: _cast('hoverlabel.font.color'),
155+
nameLength: _cast('hoverlabel.namelength'),
156+
textAlign: _cast('hoverlabel.align'),
157+
hovertemplate: hovertemplate,
158+
hovertemplateLabels: hoverPt,
159+
eventData: [makeEventData(pt, traceNow)]
160+
};
161+
162+
if(isSunburst) {
163+
hoverItems.x0 = hoverCenterX - pt.rInscribed * pt.rpx1;
164+
hoverItems.x1 = hoverCenterX + pt.rInscribed * pt.rpx1;
165+
hoverItems.idealAlign = pt.pxmid[0] < 0 ? 'left' : 'right';
166+
}
167+
if(isTreemap) {
168+
hoverItems.x = hoverCenterX;
169+
hoverItems.idealAlign = hoverCenterX < 0 ? 'left' : 'right';
170+
}
171+
172+
Fx.loneHover(hoverItems, {
173+
container: fullLayoutNow._hoverlayer.node(),
174+
outerContainer: fullLayoutNow._paper.node(),
175+
gd: gd
176+
});
177+
178+
trace._hasHoverLabel = true;
179+
}
180+
181+
if(isTreemap) {
182+
var slice = sliceTop.select('path.surface');
183+
styleOne(slice, pt, traceNow, true);
184+
}
185+
186+
trace._hasHoverEvent = true;
187+
gd.emit('plotly_hover', {
188+
points: [makeEventData(pt, traceNow)],
189+
event: d3.event
190+
});
191+
};
192+
193+
var onMouseOut = function(evt) {
194+
var fullLayoutNow = gd._fullLayout;
195+
var traceNow = gd._fullData[trace.index];
196+
var pt = d3.select(this).datum();
197+
198+
if(trace._hasHoverEvent) {
199+
evt.originalEvent = d3.event;
200+
gd.emit('plotly_unhover', {
201+
points: [makeEventData(pt, traceNow)],
202+
event: d3.event
203+
});
204+
trace._hasHoverEvent = false;
205+
}
206+
207+
if(trace._hasHoverLabel) {
208+
Fx.loneUnhover(fullLayoutNow._hoverlayer.node());
209+
trace._hasHoverLabel = false;
210+
}
211+
212+
if(isTreemap) {
213+
var slice = sliceTop.select('path.surface');
214+
styleOne(slice, pt, traceNow, false);
215+
}
216+
};
217+
218+
var onClick = function(pt) {
219+
// TODO: this does not support right-click. If we want to support it, we
220+
// would likely need to change pie to use dragElement instead of straight
221+
// mapbox event binding. Or perhaps better, make a simple wrapper with the
222+
// right mousedown, mousemove, and mouseup handlers just for a left/right click
223+
// mapbox would use this too.
224+
var fullLayoutNow = gd._fullLayout;
225+
var traceNow = gd._fullData[trace.index];
226+
227+
var clickVal = Events.triggerHandler(gd, 'plotly_' + trace.type + 'click', {
228+
points: [makeEventData(pt, traceNow)],
229+
event: d3.event
230+
});
231+
232+
// 'regular' click event when sunburst/treemap click is disabled or when
233+
// clicking on leaves or the hierarchy root
234+
if(
235+
clickVal === false ||
236+
isSunburst && (
237+
helpers.isHierarchyRoot(pt) ||
238+
helpers.isLeaf(pt)
239+
)
240+
) {
241+
if(fullLayoutNow.hovermode) {
242+
gd._hoverdata = [makeEventData(pt, traceNow)];
243+
Fx.click(gd, d3.event);
244+
}
245+
return;
246+
}
247+
248+
// skip if triggered from dragging a nearby cartesian subplot
249+
if(gd._dragging) return;
250+
251+
// skip during transitions, to avoid potential bugs
252+
// we could remove this check later
253+
if(gd._transitioning) return;
254+
255+
// store 'old' level in guiEdit stash, so that subsequent Plotly.react
256+
// calls with the same uirevision can start from the same entry
257+
Registry.call('_storeDirectGUIEdit', traceNow, fullLayoutNow._tracePreGUI[traceNow.uid], {
258+
level: traceNow.level
259+
});
260+
261+
var id = helpers.getPtId(pt);
262+
var isEntry = helpers.isEntry(pt);
263+
264+
if(isTreemap) {
265+
var zoomOut = true;
266+
var redirectId = pt._redirect;
267+
if(redirectId === undefined) {
268+
redirectId = id;
269+
if(!isEntry) zoomOut = false;
270+
}
271+
272+
traceNow._clickedInfo = {
273+
id: redirectId,
274+
zoomOut: zoomOut
275+
};
276+
}
277+
278+
var nextEntry = isEntry ?
279+
helpers.findEntryWithChild(hierarchy, id) :
280+
helpers.findEntryWithLevel(hierarchy, id);
281+
282+
var frame = {
283+
data: [{level: helpers.getPtId(nextEntry)}],
284+
traces: [trace.index]
285+
};
286+
287+
var animOpts = {
288+
frame: {
289+
redraw: false,
290+
duration: constants.CLICK_TRANSITION_TIME
291+
},
292+
transition: {
293+
duration: constants.CLICK_TRANSITION_TIME,
294+
easing: constants.CLICK_TRANSITION_EASING
295+
},
296+
mode: 'immediate',
297+
fromcurrent: true
298+
};
299+
300+
Fx.loneUnhover(fullLayoutNow._hoverlayer.node());
301+
Registry.call('animate', gd, frame, animOpts);
302+
/*
303+
.then(function() {
304+
// TODO: fixup hover position
305+
onMouseOver(pt);
306+
});
307+
*/
308+
};
309+
310+
sliceTop.on('mouseover', onMouseOver);
311+
sliceTop.on('mouseout', onMouseOut);
312+
sliceTop.on('click', onClick);
313+
};
314+
315+
function makeEventData(pt, trace) {
316+
var cdi = pt.data.data;
317+
318+
var out = {
319+
curveNumber: trace.index,
320+
pointNumber: cdi.i,
321+
data: trace._input,
322+
fullData: trace,
323+
324+
// TODO more things like 'children', 'siblings', 'hierarchy?
325+
};
326+
327+
[ // TODO: read these from (sunburst | treemap) trace constants
328+
'parentLabel',
329+
'visibleLabel',
330+
'rootLabel',
331+
'percentParent',
332+
'percentVisible',
333+
'percentRoot',
334+
'currentPath'
335+
].forEach(function(key) {
336+
if(key in pt) out[key] = pt[key];
337+
});
338+
339+
appendArrayPointValue(out, trace, cdi.i);
340+
341+
return out;
342+
}

0 commit comments

Comments
 (0)