Function: linkCursor()
linkCursor<
R>(root,store,o): () =>void
Defined in: packages/sdvplot/src/interact/cursor.ts:126
Draw the store's cursor (J34: one shared hover VALUE, such as a shot distance) through THIS figure's own scale, and
write the value under the pointer back. Every linked chart follows the same number its own way: a band on share bars
(linear x), the band holding it on FG% bars (a band scale), a band on a side chart (y), a rule on a curve, a ring
around the hoop. Moving inside one snapped bin writes nothing; crossing a bin edge is one update; leaving the figure
(pointerleave, pointercancel) or the axis' range clears the cursor this figure wrote, while the store still holds
it: never one an app store.set or another figure wrote since, nor an equal one set before (the store kept that one,
so this figure wrote none). A cursor never dims marks or filters a table (it is not an id). A store change moves
attributes only: no element is added or removed, so a cursor costs O(1) per figure. The move and press listeners
capture, so a Plot tip that stops a press from reaching other listeners does not stop this one; the leave listeners
do not, so a mark's own pointerleave (the pointer crossing a bar's edge inside one bin) writes nothing. Styled by
--sdv-cursor-color and --sdv-cursor-width. Returns a teardown FUNCTION, not a handle, because teardown is all it
has (as linkSelection's; brushFilter, nearestHover and tooltip return a handle with destroy()). The scales'
pixel ranges are read once, so after a resize, relink. The teardown removes the cursor and its listeners, and clears
the store's cursor when it still holds the one this figure last wrote (as linkSelection's teardown does with its
hover): a chart redrawn under the pointer leaves no cursor that no pointer drives. Throws InputError on a bad
option, in Node too; a no-op without a DOM.
Type Parameters
R
R
Parameters
root
Element
store
o
Returns
() => void
Example
import * as Plot from "@observablehq/plot";
import { createSelection } from "@sportsdataverse/sdvplot";
import { linkCursor } from "@sportsdataverse/sdvplot/interact";
import { fgPctByDistance } from "@sportsdataverse/sdvplot/shots";
// Brooklyn shots from the sportsdataverse-data nba_stats_shots release, 2025-26
const shots = [
{ x_legacy: -1, y_legacy: 7, shot_distance: 1, shot_value: 2, shot_result: "Made" },
{ x_legacy: 0, y_legacy: 0, shot_distance: 0, shot_value: 2, shot_result: "Missed" },
{ x_legacy: -44, y_legacy: 252, shot_distance: 26, shot_value: 3, shot_result: "Made" },
];
const bins = fgPctByDistance(shots, 1, 30);
const svg = Plot.plot({
x: { label: "Shot distance (ft)" },
marks: [Plot.rectY(bins, { x1: "distance", x2: (b) => b.distance + 1, y: "attempts" })],
});
const store = createSelection();
linkCursor(svg, store, {
field: "shot_distance",
shape: { axis: "x", scale: svg.scale("x")!, width: 1 },
snap: (feet) => Math.floor(feet) + 0.5, // one value per 1 ft bin
label: (feet) => [`@ ${Math.floor(feet)} ft`],
});
store.set({ cursor: { field: "shot_distance", value: 26.5 } }); // what a pointer 26.3 ft out writes
svg; // the 26-27 ft band shaded, with its readout