diff --git a/src/interactions/brush.js b/src/interactions/brush.js index b1292bb3db..89021f0e89 100644 --- a/src/interactions/brush.js +++ b/src/interactions/brush.js @@ -32,6 +32,7 @@ export class Brush extends Mark { if (type === "start" && !clearing) { target = event.sourceEvent?.currentTarget ?? this; currentNode = _brushNodes.indexOf(target); + if (event.sourceEvent) context.ownerSVGElement.classList.add("no-tip"); if (!clearing) { clearing = true; selectAll(_brushNodes.filter((_, i) => i !== currentNode)).call(_brush.move, null); @@ -46,6 +47,7 @@ export class Brush extends Mark { if (selection === null) { if (type === "end") { + context.ownerSVGElement.classList.remove("no-tip"); for (let i = 0; i < _brushNodes.length; ++i) { inactive.update(true, i); ctx.update(false, i); diff --git a/src/interactions/pointer.js b/src/interactions/pointer.js index f0c0f765b0..5d553fbb3e 100644 --- a/src/interactions/pointer.js +++ b/src/interactions/pointer.js @@ -140,7 +140,8 @@ function pointerK(kx, ky, {x, y, px, py, maxRadius = 40, channels, render, ...op // squashed, selecting primarily on the dominant dimension. Across facets, // use unsquashed distance to determine the winner. function pointermove(event) { - if (state.sticky || (event.pointerType === "mouse" && event.buttons === 1)) return; // dragging + if (state.sticky) return; + if (event.pointerType === "mouse" && event.buttons === 1) return void update(null); // hide tip during drag let [xp, yp] = pointof(event); (xp -= tx), (yp -= ty); // correct for facets and band scales const kpx = xp < dimensions.marginLeft || xp > dimensions.width - dimensions.marginRight ? 1 : kx; @@ -166,6 +167,7 @@ function pointerK(kx, ky, {x, y, px, py, maxRadius = 40, channels, render, ...op if (i == null) return; // not pointing if (state.sticky && state.roots.some((r) => r?.contains(event.target))) return; // stay sticky if (state.sticky) (state.sticky = false), state.renders.forEach((r) => r(null)); // clear all pointers + else if (svg.classList.contains("no-tip")) return void update(null); // cancel tip on brush start else (state.sticky = true), render(i); event.stopImmediatePropagation(); // suppress other pointers } diff --git a/test/output/brushDotTip.svg b/test/output/brushDotTip.svg new file mode 100644 index 0000000000..abfb11e928 --- /dev/null +++ b/test/output/brushDotTip.svg @@ -0,0 +1,416 @@ + + + + + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + + + ↑ culmen_depth_mm + + + + 35 + 40 + 45 + 50 + 55 + + + culmen_length_mm → + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plots/brush.ts b/test/plots/brush.ts index 2419f4b2ed..2d8a5efb65 100644 --- a/test/plots/brush.ts +++ b/test/plots/brush.ts @@ -290,3 +290,19 @@ export async function brushSimple() { plot.oninput = oninput; return html`
${plot}${textarea}
`; } + +export async function brushDotTip() { + const penguins = await d3.csv("data/penguins.csv", d3.autoType); + const brush = new Plot.Brush(); + const xy = {x: "culmen_length_mm" as const, y: "culmen_depth_mm" as const}; + const plot = Plot.plot({ + marks: [ + brush, + Plot.dot(penguins, brush.inactive({...xy, fill: "species", r: 2})), + Plot.dot(penguins, brush.context({...xy, fill: "#ccc", r: 2})), + Plot.dot(penguins, brush.focus({...xy, fill: "species", r: 3, tip: true})) + ] + }); + brush.move({x1: 36, x2: 48, y1: 15, y2: 20}); + return plot; +}