Skip to main content

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​

SelectionStore<R>

o​

LinkCursorOptions

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