Skip to content

Commit

Permalink
Merge pull request #2581 from plotly/legend-events
Browse files Browse the repository at this point in the history
Legend events
  • Loading branch information
etpinard authored May 1, 2018
2 parents a22c0d5 + 05e1b6b commit ef6b259
Show file tree
Hide file tree
Showing 4 changed files with 350 additions and 64 deletions.
62 changes: 43 additions & 19 deletions src/components/legend/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var d3 = require('d3');
var Lib = require('../../lib');
var Plots = require('../../plots/plots');
var Registry = require('../../registry');
var Events = require('../../lib/events');
var dragElement = require('../dragelement');
var Drawing = require('../drawing');
var Color = require('../color');
Expand Down Expand Up @@ -347,22 +348,53 @@ module.exports = function draw(gd) {
e.clientY >= bbox.top && e.clientY <= bbox.bottom);
});
if(clickedTrace.size() > 0) {
if(numClicks === 1) {
legend._clickTimeout = setTimeout(function() {
handleClick(clickedTrace, gd, numClicks);
}, DBLCLICKDELAY);
} else if(numClicks === 2) {
if(legend._clickTimeout) {
clearTimeout(legend._clickTimeout);
}
handleClick(clickedTrace, gd, numClicks);
}
clickOrDoubleClick(gd, legend, clickedTrace, numClicks, e);
}
}
});
}
};

function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) {
var trace = legendItem.data()[0][0].trace;

var evtData = {
event: evt,
node: legendItem.node(),
curveNumber: trace.index,
expandedIndex: trace._expandedIndex,
data: gd.data,
layout: gd.layout,
frames: gd._transitionData._frames,
config: gd._context,
fullData: gd._fullData,
fullLayout: gd._fullLayout
};

if(trace._group) {
evtData.group = trace._group;
}
if(trace.type === 'pie') {
evtData.label = legendItem.datum()[0].label;
}

var clickVal = Events.triggerHandler(gd, 'plotly_legendclick', evtData);
if(clickVal === false) return;

if(numClicks === 1) {
legend._clickTimeout = setTimeout(function() {
handleClick(legendItem, gd, numClicks);
}, DBLCLICKDELAY);
}
else if(numClicks === 2) {
if(legend._clickTimeout) clearTimeout(legend._clickTimeout);
gd._legendMouseDownTime = 0;

var dblClickVal = Events.triggerHandler(gd, 'plotly_legenddoubleclick', evtData);
if(dblClickVal !== false) handleClick(legendItem, gd, numClicks);
}
}

function drawTexts(g, gd, maxLength) {
var legendItem = g.data()[0][0];
var fullLayout = gd._fullLayout;
Expand Down Expand Up @@ -460,15 +492,7 @@ function setupTraceToggle(g, gd) {
numClicks = Math.max(numClicks - 1, 1);
}

if(numClicks === 1) {
legend._clickTimeout = setTimeout(function() { handleClick(g, gd, numClicks); }, DBLCLICKDELAY);
} else if(numClicks === 2) {
if(legend._clickTimeout) {
clearTimeout(legend._clickTimeout);
}
gd._legendMouseDownTime = 0;
handleClick(g, gd, numClicks);
}
clickOrDoubleClick(gd, legend, g, numClicks, d3.event);
});
}

Expand Down
60 changes: 34 additions & 26 deletions src/lib/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ var Events = {
plotObj.removeAllListeners = ev.removeAllListeners.bind(ev);

/*
* Create funtions for managing internal events. These are *only* triggered
* Create functions for managing internal events. These are *only* triggered
* by the mirroring of external events via the emit function.
*/
plotObj._internalOn = internalEv.on.bind(internalEv);
Expand All @@ -85,20 +85,17 @@ var Events = {
},

/*
* This function behaves like jQueries triggerHandler. It calls
* This function behaves like jQuery's triggerHandler. It calls
* all handlers for a particular event and returns the return value
* of the LAST handler. This function also triggers jQuery's
* triggerHandler for backwards compatibility.
*
* Note: triggerHandler has been recommended for deprecation in v2.0.0,
* so the additional behavior of triggerHandler triggering internal events
* is deliberate excluded in order to avoid reinforcing more usage.
*/
triggerHandler: function(plotObj, event, data) {
var jQueryHandlerValue;
var nodeEventHandlerValue;

/*
* If Jquery exists run all its handlers for this event and
* If jQuery exists run all its handlers for this event and
* collect the return value of the LAST handler function
*/
if(typeof jQuery !== 'undefined') {
Expand All @@ -114,30 +111,41 @@ var Events = {
var handlers = ev._events[event];
if(!handlers) return jQueryHandlerValue;

/*
* handlers can be function or an array of functions
*/
if(typeof handlers === 'function') handlers = [handlers];
var lastHandler = handlers.pop();

/*
* Call all the handlers except the last one.
*/
for(var i = 0; i < handlers.length; i++) {
handlers[i](data);
// making sure 'this' is the EventEmitter instance
function apply(handler) {
// The 'once' case, we can't just call handler() as we need
// the return value here. So,
// - remove handler
// - call listener and grab return value!
// - stash 'fired' key to not call handler twice
if(handler.listener) {
ev.removeListener(event, handler.listener);
if(!handler.fired) {
handler.fired = true;
return handler.listener.apply(ev, [data]);
}
} else {
return handler.apply(ev, [data]);
}
}

/*
* Now call the final handler and collect its value
*/
nodeEventHandlerValue = lastHandler(data);
// handlers can be function or an array of functions
handlers = Array.isArray(handlers) ? handlers : [handlers];

var i;
for(i = 0; i < handlers.length - 1; i++) {
apply(handlers[i]);
}
// now call the final handler and collect its value
nodeEventHandlerValue = apply(handlers[i]);

/*
* Return either the jquery handler value if it exists or the
* nodeEventHandler value. Jquery event value superceeds nodejs
* events for backwards compatability reasons.
* Return either the jQuery handler value if it exists or the
* nodeEventHandler value. jQuery event value supersedes nodejs
* events for backwards compatibility reasons.
*/
return jQueryHandlerValue !== undefined ? jQueryHandlerValue :
return jQueryHandlerValue !== undefined ?
jQueryHandlerValue :
nodeEventHandlerValue;
},

Expand Down
19 changes: 19 additions & 0 deletions test/jasmine/tests/events_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,25 @@ describe('Events', function() {
expect(eventBaton).toBe(3);
expect(result).toBe('pong');
});

it('works with *once* event handlers', function() {
var eventBaton = 0;

Events.init(plotDiv);

plotDiv.once('ping', function() {
eventBaton++;
return 'pong';
});

var result = Events.triggerHandler(plotDiv, 'ping');
expect(result).toBe('pong');
expect(eventBaton).toBe(1);

var nop = Events.triggerHandler(plotDiv, 'ping');
expect(nop).toBeUndefined();
expect(eventBaton).toBe(1);
});
});

describe('purge', function() {
Expand Down
Loading

0 comments on commit ef6b259

Please sign in to comment.