Skip to content

Commit dbcc6a2

Browse files
committed
pointer comments
1 parent d2c1c02 commit dbcc6a2

File tree

1 file changed

+30
-7
lines changed

1 file changed

+30
-7
lines changed

src/interactions/pointer.js

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@ import {applyFrameAnchor} from "../style.js";
33

44
function pointerK(kx, ky, {x, y, px, py, maxRadius = 40, channels, ...options} = {}) {
55
maxRadius = +maxRadius;
6+
// When px or py is used, register an extra channel that the pointer
7+
// interaction can use to control which point is focused; this allows pointing
8+
// to function independently of where the downstream mark (e.g., a tip) is
9+
// displayed. Also default x or y to null to disable maybeTuple etc.
610
if (px != null) (x ??= null), (channels = {...channels, px: {value: px, scale: "x"}});
711
if (py != null) (y ??= null), (channels = {...channels, py: {value: py, scale: "y"}});
8-
const stateBySvg = new WeakMap();
12+
const states = new WeakMap();
913
return {
1014
x,
1115
y,
@@ -17,18 +21,35 @@ function pointerK(kx, ky, {x, y, px, py, maxRadius = 40, channels, ...options} =
1721

1822
// Isolate state per-pointer, per-plot; if the pointer is reused by
1923
// multiple marks, they will share the same state (e.g., sticky modality).
20-
let state = stateBySvg.get(svg);
21-
if (!state) stateBySvg.set(svg, (state = {sticky: false, roots: [], renders: []}));
24+
let state = states.get(svg);
25+
if (!state) states.set(svg, (state = {sticky: false, roots: [], renders: []}));
26+
27+
// This serves as a unique identifier of the rendered mark per-plot; it is
28+
// used to record the currently-rendered elements (state.roots) so that we
29+
// can tell when a rendered element is clicked on.
2230
let renderIndex = state.renders.push(render) - 1;
2331

24-
const faceted = index.fi != null;
25-
const facetState = faceted ? (state.facetState ??= new Map()) : null;
32+
// For faceting, we want to compute the local coordinates of each point,
33+
// which means subtracting out the facet translation, if any. (It’s
34+
// tempting to do this using the local coordinates in SVG, but that’s
35+
// complicated by mark-specific transforms such as dx and dy.)
2636
const tx = scales.fx ? scales.fx(index.fx) - dimensions.marginLeft : 0;
2737
const ty = scales.fy ? scales.fy(index.fy) - dimensions.marginTop : 0;
38+
39+
// For faceting, we also need to record the closest point per facet, since
40+
// each facet has its own pointer event listeners; we only want the
41+
// closest point across facets to be visible.
42+
const faceted = index.fi != null;
43+
const facetState = faceted ? (state.facetState ??= new Map()) : null;
44+
45+
// The order of precedence when determining the point position is: px &
46+
// py; the middle of x1 & y1 and x2 & y2; or lastly x & y. If any
47+
// dimension is unspecified, we fallback to the frame anchor.
2848
const {x: X, y: Y, x1: X1, y1: Y1, x2: X2, y2: Y2, px: PX, py: PY} = values;
2949
const [cx, cy] = applyFrameAnchor(this, dimensions);
3050
const px = PX ? (i) => PX[i] : X2 ? (i) => (X1[i] + X2[i]) / 2 : X ? (i) => X[i] : () => cx;
3151
const py = PY ? (i) => PY[i] : Y2 ? (i) => (Y1[i] + Y2[i]) / 2 : Y ? (i) => Y[i] : () => cy;
52+
3253
let i; // currently focused index
3354
let g; // currently rendered mark
3455

@@ -57,14 +78,16 @@ function pointerK(kx, ky, {x, y, px, py, maxRadius = 40, channels, ...options} =
5778
if (faceted) (I.fx = index.fx), (I.fy = index.fy), (I.fi = index.fi);
5879
const r = mark.render(I, scales, values, dimensions, context);
5980
if (g) {
81+
// When faceting, preserve swapped mark and facet transforms; also
82+
// remove ARIA attributes since these are promoted to the parent. This
83+
// is perhaps brittle in that it depends on how Plot renders facets,
84+
// but it produces a cleaner and more accessible SVG structure.
6085
if (faceted) {
61-
// when faceting, preserve swapped mark and facet transforms
6286
const p = g.parentNode;
6387
const ft = g.getAttribute("transform");
6488
const mt = r.getAttribute("transform");
6589
ft ? r.setAttribute("transform", ft) : r.removeAttribute("transform");
6690
mt ? p.setAttribute("transform", mt) : p.removeAttribute("transform");
67-
// also remove ARIA attributes since these are promoted to the parent
6891
r.removeAttribute("aria-label");
6992
r.removeAttribute("aria-description");
7093
r.removeAttribute("aria-hidden");

0 commit comments

Comments
 (0)