Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Legend events #2581

Merged
merged 9 commits into from
May 1, 2018
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