@@ -3,9 +3,13 @@ import {applyFrameAnchor} from "../style.js";
3
3
4
4
function pointerK ( kx , ky , { x, y, px, py, maxRadius = 40 , channels, ...options } = { } ) {
5
5
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.
6
10
if ( px != null ) ( x ??= null ) , ( channels = { ...channels , px : { value : px , scale : "x" } } ) ;
7
11
if ( py != null ) ( y ??= null ) , ( channels = { ...channels , py : { value : py , scale : "y" } } ) ;
8
- const stateBySvg = new WeakMap ( ) ;
12
+ const states = new WeakMap ( ) ;
9
13
return {
10
14
x,
11
15
y,
@@ -17,18 +21,35 @@ function pointerK(kx, ky, {x, y, px, py, maxRadius = 40, channels, ...options} =
17
21
18
22
// Isolate state per-pointer, per-plot; if the pointer is reused by
19
23
// 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.
22
30
let renderIndex = state . renders . push ( render ) - 1 ;
23
31
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.)
26
36
const tx = scales . fx ? scales . fx ( index . fx ) - dimensions . marginLeft : 0 ;
27
37
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.
28
48
const { x : X , y : Y , x1 : X1 , y1 : Y1 , x2 : X2 , y2 : Y2 , px : PX , py : PY } = values ;
29
49
const [ cx , cy ] = applyFrameAnchor ( this , dimensions ) ;
30
50
const px = PX ? ( i ) => PX [ i ] : X2 ? ( i ) => ( X1 [ i ] + X2 [ i ] ) / 2 : X ? ( i ) => X [ i ] : ( ) => cx ;
31
51
const py = PY ? ( i ) => PY [ i ] : Y2 ? ( i ) => ( Y1 [ i ] + Y2 [ i ] ) / 2 : Y ? ( i ) => Y [ i ] : ( ) => cy ;
52
+
32
53
let i ; // currently focused index
33
54
let g ; // currently rendered mark
34
55
@@ -57,14 +78,16 @@ function pointerK(kx, ky, {x, y, px, py, maxRadius = 40, channels, ...options} =
57
78
if ( faceted ) ( I . fx = index . fx ) , ( I . fy = index . fy ) , ( I . fi = index . fi ) ;
58
79
const r = mark . render ( I , scales , values , dimensions , context ) ;
59
80
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.
60
85
if ( faceted ) {
61
- // when faceting, preserve swapped mark and facet transforms
62
86
const p = g . parentNode ;
63
87
const ft = g . getAttribute ( "transform" ) ;
64
88
const mt = r . getAttribute ( "transform" ) ;
65
89
ft ? r . setAttribute ( "transform" , ft ) : r . removeAttribute ( "transform" ) ;
66
90
mt ? p . setAttribute ( "transform" , mt ) : p . removeAttribute ( "transform" ) ;
67
- // also remove ARIA attributes since these are promoted to the parent
68
91
r . removeAttribute ( "aria-label" ) ;
69
92
r . removeAttribute ( "aria-description" ) ;
70
93
r . removeAttribute ( "aria-hidden" ) ;
0 commit comments