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 @@
+
\ 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;
+}