Track Surface
Lanes stacked over one shared window.
Preview
The two surfaces above use different axis names, so they move independently. Give them the same name and they lock together.
About
Thirteen viewers share a horizontal base-pair axis, and stacking them — genes above coverage above variants, all showing the same span — is the whole idiom of genome browsing. It only works if they agree on the window, and they didn't: each kept its own, so pan and zoom existed in two of the thirteen and stacking meant threading a {start, end} pair and two callbacks through both. A track surface takes an axis name instead of a window; every surface with the same name moves together through the substrate's viewport channel, with no props between them. Lanes carry their own y domain, because a coverage depth and a log2 ratio are not the same scale.
Usage
import { TrackSurface, useTrackSurface } from "@/components/ui/track-surface"
const surface = useTrackSurface({
axis: "chr7",
bounds: [0, 159_345_973],
initial: { start: 55_000_000, end: 55_300_000 },
lanes: [
{ name: "genes", height: 54, draw: drawGenes, deps: [genes] },
{ name: "coverage", height: 70, yDomain: [0, 200], yAxis: true,
draw: drawCoverage, deps: [bins] },
],
})
<TrackSurface surface={surface} title="chr7" />components/ui/track-surface.tsx