Published
Edited
Oct 2, 2020
Insert cell
Insert cell
v6Demo = makeDemo(`d3 ${d3.version}`, { timeout: 300, tol: 10 })
Insert cell
makeDemo = (title, clickArgs) => {
const cc = cancelableClick({ ...clickArgs});
const div = d3.create("div")
.style("background-color", "purple")
.style("font-family", "sans-serif")
.style("user-select", "none")
.style("color", "white")
.style("padding", "4px")
.style("height", "100px")
.style("width", `${width}px`)
.call(cc);
div.append("h3")
.text(title)
.style("color", "white");
const textContainer = div.append("div")
.style("font", "12px sans-serif")
.text("hello");
const appendText = (text) => () => textContainer.text(textContainer.text() + text);
cc.on("click", appendText(', single'))
cc.on("dblclick", appendText(', double'))
return div.node();
}
Insert cell
cancelableClick = {
// euclidean distance
const dist = (a, b) => {
return Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2));
}

// Method is assumed to be a standard D3 getter-setter:
// If passed with no arguments, gets the value.
// If passed with arguments, sets the value and returns the target.
const rebindMethod = (target, source, method) => {
return (...args) => {
const value = method.apply(source, args);
return value === source ? target : value;
};
}

// Copies a variable number of methods from source to target.
const rebind = (target, source, ...methods) => {
for (let method of methods) {
target[method] = rebindMethod(target, source, source[method]);
}
return target;
}

// see: http://bl.ocks.org/ropeladder/83915942ac42f17c087a82001418f2ee
// based on: http://bl.ocks.org/couchand/6394506
return({ tolerance = 5, timeout = 200} = {}) => {
const dispatcher = d3.dispatch('click', 'dblclick');

const cc = (selection) => {
let downPt;
let lastTs;
let waitId;
let eventArgs;

selection.on('mousedown', (event, ...args) => {
downPt = d3.pointer(event, document.body);
lastTs = Date.now();
eventArgs = [event, ...args];
});

selection.on('click', (e) => {
if (dist(downPt, d3.pointer(e, document.body)) >= tolerance) {
return;
}

if (waitId) {
window.clearTimeout(waitId);
waitId = null;
dispatcher.apply("dblclick", selection, eventArgs);
} else {
waitId = window.setTimeout(
() => {
dispatcher.apply("click", selection, eventArgs);
waitId = null;
},
timeout
);
}
});
};

return rebind(cc, dispatcher, 'on');
}
}
Insert cell
d3 = require('d3')
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more