@jbrowse/plugin-linear-genome-view 5.0.0-beta.2 → 5.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@ export default function ExportSvgDialog({ model, handleClose, }: {
5
5
  exportSvg(opts: BaseExportSvgOptions & {
6
6
  trackLabels: TrackLabelMode;
7
7
  showGridlines: boolean;
8
- }): Promise<void>;
8
+ }): Promise<unknown>;
9
9
  };
10
10
  handleClose: () => void;
11
11
  }): import("react").JSX.Element;
@@ -5,7 +5,7 @@ import { assembleLocString, assembleLocStrings } from '@jbrowse/core/util';
5
5
  import { copyText } from '@jbrowse/core/util/copyText';
6
6
  import { makeStyles } from '@jbrowse/core/util/tss-react';
7
7
  import { observer } from 'mobx-react';
8
- import { REF_NAME_LABEL_FONT_SIZE, regionMoveActions, withRegionMoved, withRegionRemoved, withRegionReversed, } from '../util.js';
8
+ import { REF_NAME_LABEL_FONT_SIZE, regionMoveActions, regionRunBounds, setDisplayedRegionsKeepingCenter, withRegionMoved, withRegionRemoved, withRegionReversed, } from '../util.js';
9
9
  const useStyles = makeStyles()(theme => ({
10
10
  refLabel: {
11
11
  // the width the fit test in getScalebarRefNameLabels measures against
@@ -104,6 +104,16 @@ const RefNameMenu = observer(function RefNameMenu({ model, menuState, onClose, }
104
104
  const locString = oneRegion
105
105
  ? assembleLocString(displayedRegions[idx])
106
106
  : assembleLocStrings(labeled);
107
+ // The region this label's blocks were cut out of. Taken over the WHOLE run
108
+ // rather than `labeled`, which the viewport clips: a collapsed-introns view
109
+ // zoomed into two exons would otherwise offer a span that is not the gene's.
110
+ const { first: runFirst, last: runLast } = regionRunBounds(displayedRegions, idx, lastIdx);
111
+ const run = displayedRegions.slice(runFirst, runLast + 1);
112
+ const wholeSpan = {
113
+ ...run[0],
114
+ start: Math.min(...run.map(r => r.start)),
115
+ end: Math.max(...run.map(r => r.end)),
116
+ };
107
117
  return (_jsx(Menu, { anchorEl: menuState.anchorEl, open: true, onClose: onClose, onMenuItemClick: callback => {
108
118
  callback();
109
119
  onClose();
@@ -126,7 +136,31 @@ const RefNameMenu = observer(function RefNameMenu({ model, menuState, onClose, }
126
136
  ? 'Show only this region'
127
137
  : `Show only ${refName}`,
128
138
  onClick: () => {
129
- model.setDisplayedRegions(labeled);
139
+ setDisplayedRegionsKeepingCenter(model, labeled);
140
+ },
141
+ },
142
+ ]
143
+ : []),
144
+ ...(runLast > runFirst
145
+ ? [
146
+ {
147
+ // Names its subject the way the two rows above it do; the span
148
+ // it lands on is not spelled into the label, which would move
149
+ // under the reader as they navigate.
150
+ label: `Show the whole span of ${refName}`,
151
+ // Focused rather than centre-kept: the rejoined region is one
152
+ // the old viewport never saw, so there is no centre to hold,
153
+ // and showing the span is the whole point of the row.
154
+ onClick: () => {
155
+ model.setDisplayedRegions([
156
+ ...displayedRegions.slice(0, runFirst),
157
+ wholeSpan,
158
+ ...displayedRegions.slice(runLast + 1),
159
+ ]);
160
+ model.moveTo({ index: runFirst, offset: 0 }, {
161
+ index: runFirst,
162
+ offset: wholeSpan.end - wholeSpan.start,
163
+ });
130
164
  },
131
165
  },
132
166
  ]
@@ -170,7 +204,7 @@ const RefNameMenu = observer(function RefNameMenu({ model, menuState, onClose, }
170
204
  {
171
205
  label: 'Remove this region from view',
172
206
  onClick: () => {
173
- model.setDisplayedRegions(withRegionRemoved(displayedRegions, idx));
207
+ setDisplayedRegionsKeepingCenter(model, withRegionRemoved(displayedRegions, idx));
174
208
  },
175
209
  },
176
210
  ]
@@ -1,8 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
2
3
  import { ErrorBanner, ResizeHandle } from '@jbrowse/core/ui';
3
4
  import { ErrorBoundary } from '@jbrowse/core/ui/ErrorBoundary';
4
5
  import { cx, makeStyles } from '@jbrowse/core/util/tss-react';
5
6
  import { TrackOverlaySlot } from '@jbrowse/display-ui';
7
+ import { isAlive } from '@jbrowse/mobx-state-tree';
6
8
  import { Paper } from '@mui/material';
7
9
  import { observer } from 'mobx-react';
8
10
  import { RESIZE_HANDLE_HEIGHT, TRACK_OUTLINE_BORDER, TRACK_TOP_GAP, } from '../consts.js';
@@ -16,8 +18,8 @@ const useStyles = makeStyles()({
16
18
  // described in TrackRenderingContainer. flow-root keeps the block formatting
17
19
  // context that `overflow: hidden` used to establish as a side effect (neither
18
20
  // `clip` nor no overflow does), so child margins still can't collapse through
19
- // and shift the track — which the breakpoint split view would pick up, since
20
- // its connector overlay measures trackRefs' getBoundingClientRect().top.
21
+ // and shift the track — which would put the pixels off the model's offset
22
+ // arithmetic, which the breakpoint split view draws its connectors from.
21
23
  root: {
22
24
  marginTop: TRACK_TOP_GAP,
23
25
  position: 'relative',
@@ -63,6 +65,45 @@ const useStyles = makeStyles()({
63
65
  position: 'absolute',
64
66
  },
65
67
  });
68
+ // Measures the in-flow label band, the distance from the Paper's content top to
69
+ // the rendering container, into the model. Observes the Paper: it grows and
70
+ // shrinks with the label, the label setting, and the display, and each of those
71
+ // can move the container. Published after a requestAnimationFrame like
72
+ // useWidthSetter, and cleared on unmount so a removed track leaves no band.
73
+ function useTrackLabelBand(model, trackId) {
74
+ const [paper, setPaper] = useState(null);
75
+ const [container, setContainer] = useState(null);
76
+ useEffect(() => {
77
+ if (paper && container && 'ResizeObserver' in window) {
78
+ let token;
79
+ const publish = () => {
80
+ token = requestAnimationFrame(() => {
81
+ if (isAlive(model)) {
82
+ const paperRect = paper.getBoundingClientRect();
83
+ const band = container.getBoundingClientRect().top -
84
+ paperRect.top -
85
+ paper.clientTop;
86
+ model.setTrackLabelBand(trackId, band);
87
+ }
88
+ });
89
+ };
90
+ publish();
91
+ const observer = new ResizeObserver(publish);
92
+ observer.observe(paper);
93
+ return () => {
94
+ observer.disconnect();
95
+ if (token !== undefined) {
96
+ cancelAnimationFrame(token);
97
+ }
98
+ if (isAlive(model)) {
99
+ model.setTrackLabelBand(trackId, 0);
100
+ }
101
+ };
102
+ }
103
+ return undefined;
104
+ }, [paper, container, model, trackId]);
105
+ return { setPaper, setContainer };
106
+ }
66
107
  const TrackContainer = observer(function TrackContainer({ model, track, }) {
67
108
  const { classes } = useStyles();
68
109
  // an LGV track always holds at least one linear display (activeDisplay =
@@ -79,9 +120,10 @@ const TrackContainer = observer(function TrackContainer({ model, track, }) {
79
120
  const trackLabelStyle = model.effectiveTrackLabels !== 'overlapping' || display.prefersOffset
80
121
  ? classes.trackLabelOffset
81
122
  : classes.trackLabelOverlap;
82
- return (_jsxs(Paper, { className: cx(classes.root, track.pinned ? null : classes.unpinnedTrack), variant: showTrackOutlines ? 'outlined' : undefined, elevation: showTrackOutlines ? undefined : 0, onDragOver: event => {
123
+ const { setPaper, setContainer } = useTrackLabelBand(model, track.trackId);
124
+ return (_jsxs(Paper, { ref: setPaper, className: cx(classes.root, track.pinned ? null : classes.unpinnedTrack), variant: showTrackOutlines ? 'outlined' : undefined, elevation: showTrackOutlines ? undefined : 0, onDragOver: event => {
83
125
  model.onTrackDragOver(track.id, event.clientY);
84
- }, children: [track.pinned ? _jsx(Gridlines, { model: model, offset: outlineOffset }) : null, model.effectiveTrackLabels !== 'hidden' ? (_jsx(TrackLabel, { track: track, className: trackLabelStyle })) : null, _jsx(TrackOverlaySlot, { zIndex: 100, overlayStyle: { left: -outlineOffset }, children: _jsx(ErrorBoundary, { resetKeys: [display.id], FallbackComponent: e => (_jsx(ErrorBanner, { error: e.error, onReset: e.resetErrorBoundary })), children: _jsx(TrackRenderingContainer, { model: model, track: track }) }) }), _jsx(PaddingBlocks, { model: model, offset: outlineOffset }), _jsx(ResizeHandle, { style: { height: RESIZE_HANDLE_HEIGHT }, onDrag: distance => display.resizeHeight(distance),
126
+ }, children: [track.pinned ? _jsx(Gridlines, { model: model, offset: outlineOffset }) : null, model.effectiveTrackLabels !== 'hidden' ? (_jsx(TrackLabel, { track: track, className: trackLabelStyle })) : null, _jsx(TrackOverlaySlot, { zIndex: 100, overlayStyle: { left: -outlineOffset }, children: _jsx(ErrorBoundary, { resetKeys: [display.id], FallbackComponent: e => (_jsx(ErrorBanner, { error: e.error, onReset: e.resetErrorBoundary })), children: _jsx(TrackRenderingContainer, { model: model, track: track, ref: setContainer }) }) }), _jsx(PaddingBlocks, { model: model, offset: outlineOffset }), _jsx(ResizeHandle, { style: { height: RESIZE_HANDLE_HEIGHT }, onDrag: distance => display.resizeHeight(distance),
85
127
  // the drag's shortcut: grow the track by whatever it is scrolled over,
86
128
  // so a pileup showing three rows of forty opens to all forty
87
129
  // Optional: only a display that scrolls its own content has anything
@@ -1,11 +1,11 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { getContainingView } from '@jbrowse/core/util';
3
2
  import { makeStyles } from '@jbrowse/core/util/tss-react';
4
3
  import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
5
4
  import ArrowRightIcon from '@mui/icons-material/ArrowRight';
6
5
  import CloseIcon from '@mui/icons-material/Close';
7
6
  import { IconButton } from '@mui/material';
8
7
  import { observer } from 'mobx-react';
8
+ import { containingLgv } from '../../LinearGenomeView/containingLgv.js';
9
9
  const useStyles = makeStyles()({
10
10
  iconButton: {
11
11
  padding: 0,
@@ -21,7 +21,7 @@ const useStyles = makeStyles()({
21
21
  // write it.
22
22
  export const TrackLabelCloseButton = observer(function TrackLabelCloseButton({ track, }) {
23
23
  const { classes } = useStyles();
24
- const view = getContainingView(track);
24
+ const view = containingLgv(track);
25
25
  return (_jsx(IconButton, { onClick: () => view.hideTrack(track.trackId), className: classes.iconButton, title: "close this track", "data-testid": `track-close-${track.trackId}`, children: _jsx(CloseIcon, { fontSize: "small" }) }));
26
26
  });
27
27
  export const TrackLabelMinimizeButton = observer(function TrackLabelMinimizeButton({ track }) {
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { getContainingView } from '@jbrowse/core/util';
3
2
  import { makeStyles } from '@jbrowse/core/util/tss-react';
4
3
  import DragIcon from '@mui/icons-material/DragIndicator';
5
4
  import { observer } from 'mobx-react';
5
+ import { containingLgv } from '../../LinearGenomeView/containingLgv.js';
6
6
  const useStyles = makeStyles()({
7
7
  dragHandle: {
8
8
  cursor: 'grab',
@@ -15,7 +15,7 @@ const useStyles = makeStyles()({
15
15
  });
16
16
  const TrackLabelDragHandle = observer(function TrackLabelDragHandle({ track, }) {
17
17
  const { classes } = useStyles();
18
- const view = getContainingView(track);
18
+ const view = containingLgv(track);
19
19
  const trackId = track.trackId;
20
20
  return (_jsx("span", { draggable: true, className: classes.dragHandle, onDragStart: event => {
21
21
  const target = event.currentTarget;
@@ -1,10 +1,11 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
3
- import { getContainingView, getSession } from '@jbrowse/core/util';
3
+ import { getSession } from '@jbrowse/core/util';
4
4
  import { makeStyles } from '@jbrowse/core/util/tss-react';
5
5
  import LowPriorityIcon from '@mui/icons-material/LowPriority';
6
6
  import MoreVertIcon from '@mui/icons-material/MoreVert';
7
7
  import { observer } from 'mobx-react';
8
+ import { containingLgv } from '../../LinearGenomeView/containingLgv.js';
8
9
  import { getTrackOrderSubMenu } from './trackLabelMenuItems.js';
9
10
  // IconButton's default 5px padding made this the tallest thing in the label
10
11
  // (27px), so it, not the text or the other controls, set the label height and
@@ -17,7 +18,7 @@ const useStyles = makeStyles()({
17
18
  });
18
19
  const TrackLabelMenu = observer(function TrackLabelMenu({ track, }) {
19
20
  const { classes } = useStyles();
20
- const view = getContainingView(track);
21
+ const view = containingLgv(track);
21
22
  const session = getSession(track);
22
23
  function getMenuItems() {
23
24
  const orderSubMenu = getTrackOrderSubMenu({ view, track });
@@ -1,8 +1,9 @@
1
1
  import type { LinearGenomeViewModel } from '../index.ts';
2
2
  import type { BaseTrackModel } from '@jbrowse/core/pluggableElementTypes/models';
3
3
  type LGV = LinearGenomeViewModel;
4
- declare const TrackRenderingContainer: ({ model, track, }: {
4
+ declare const TrackRenderingContainer: ({ model, track, ref, }: {
5
5
  model: LGV;
6
6
  track: BaseTrackModel;
7
+ ref?: React.Ref<HTMLDivElement>;
7
8
  }) => import("react").JSX.Element;
8
9
  export default TrackRenderingContainer;
@@ -1,5 +1,5 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Suspense, useCallback } from 'react';
2
+ import { Suspense } from 'react';
3
3
  import { LoadingOverlay } from '@jbrowse/core/ui';
4
4
  import { getSession } from '@jbrowse/core/util';
5
5
  import { getTrackName } from '@jbrowse/core/util/tracks';
@@ -72,28 +72,18 @@ const TrackDisplayRegion = observer(function TrackDisplayRegion({ model, track,
72
72
  const loc = model.coarseVisibleLocStrings;
73
73
  return (_jsx("div", { ...divProps, role: "figure", "aria-label": loc ? `${trackName} track, showing ${loc}` : `${trackName} track`, children: children }));
74
74
  });
75
- const TrackRenderingContainer = observer(function TrackRenderingContainer({ model, track, }) {
75
+ const TrackRenderingContainer = observer(function TrackRenderingContainer({ model, track, ref, }) {
76
76
  const { classes } = useStyles();
77
77
  // an LGV track always holds at least one linear display (activeDisplay =
78
78
  // displays[0]); narrow to the linear shape for height/RenderingComponent
79
79
  const display = track.activeDisplay;
80
80
  const { height, RenderingComponent, DisplayBlurb } = display;
81
- const { trackRefs, showTrackOutlines } = model;
81
+ const { showTrackOutlines } = model;
82
82
  const trackId = track.trackId;
83
83
  const minimized = track.minimized;
84
- // callback ref keeps trackRefs in sync as the rendering div mounts/unmounts
85
- // (e.g. on minimize/restore), unlike a useEffect that misses the toggle
86
- const setRef = useCallback((el) => {
87
- if (el) {
88
- trackRefs[trackId] = el;
89
- }
90
- else {
91
- delete trackRefs[trackId];
92
- }
93
- }, [trackRefs, trackId]);
94
- return (_jsx("div", { className: classes.trackRenderingContainer, style: {
84
+ return (_jsx("div", { ref: ref, className: classes.trackRenderingContainer, style: {
95
85
  height: minimized ? MINIMIZED_TRACK_HEIGHT : height,
96
- }, "data-testid": `trackRenderingContainer-${model.id}-${trackId}`, children: !minimized ? (_jsxs(_Fragment, { children: [_jsx(TrackDisplayRegion, { model: model, track: track, ref: setRef, className: classes.renderingComponentContainer, style: { left: showTrackOutlines ? -1 : 0 }, children: _jsx(Suspense, { fallback: _jsx(LoadingOverlay, { isVisible: true, immediate: true }), children: _jsx(RenderingComponent, { model: display, onHorizontalScroll: model.horizontalScroll }) }) }), DisplayBlurb ? (_jsx("div", { style: {
86
+ }, "data-testid": `trackRenderingContainer-${model.id}-${trackId}`, children: !minimized ? (_jsxs(_Fragment, { children: [_jsx(TrackDisplayRegion, { model: model, track: track, className: classes.renderingComponentContainer, style: { left: showTrackOutlines ? -1 : 0 }, children: _jsx(Suspense, { fallback: _jsx(LoadingOverlay, { isVisible: true, immediate: true }), children: _jsx(RenderingComponent, { model: display, onHorizontalScroll: model.horizontalScroll }) }) }), DisplayBlurb ? (_jsx("div", { style: {
97
87
  position: 'absolute',
98
88
  left: 0,
99
89
  top: display.height - 20,
@@ -14,7 +14,7 @@ export const SVG_SCALEBAR_CAP = 5;
14
14
  // TrackContainer lays its Paper out from these same three and nothing else —
15
15
  // the previous spelling had the model on a 3 while the CSS rendered 2 + 1 + 4,
16
16
  // which put every model-derived track offset 5px per track above the pixels.
17
- // The track *label* is deliberately not among them; see `trackChromeHeight`.
17
+ // The track *label* is not among them: it is measured, see `trackLabelBand`.
18
18
  export const RESIZE_HANDLE_HEIGHT = 4;
19
19
  export const TRACK_TOP_GAP = 2;
20
20
  export const TRACK_OUTLINE_BORDER = 1;
@@ -0,0 +1,15 @@
1
+ import type { LinearGenomeViewModel } from './model.ts';
2
+ import type { IStateTreeNode } from '@jbrowse/mobx-state-tree';
3
+ /**
4
+ * The containing view as the linear genome view itself, for a display that
5
+ * needs `pxToBp`, `showTrack`, `navTo` or the view's own chrome settings.
6
+ *
7
+ * The twin of display-kit's `containingHost`, and deliberately not the same
8
+ * function: `RegionHost` is the region contract display-kit reads, and widening
9
+ * it to carry the LGV's own surface is how the display layer would come to
10
+ * depend on the view plugin. A display that wants the view names the view.
11
+ *
12
+ * Components and structural helpers taking duck-typed model shapes keep calling
13
+ * `getContainingView` — the same carve-out `containingHost` documents.
14
+ */
15
+ export declare function containingLgv(self: IStateTreeNode): LinearGenomeViewModel;
@@ -0,0 +1,16 @@
1
+ import { getContainingView } from '@jbrowse/core/util';
2
+ /**
3
+ * The containing view as the linear genome view itself, for a display that
4
+ * needs `pxToBp`, `showTrack`, `navTo` or the view's own chrome settings.
5
+ *
6
+ * The twin of display-kit's `containingHost`, and deliberately not the same
7
+ * function: `RegionHost` is the region contract display-kit reads, and widening
8
+ * it to carry the LGV's own surface is how the display layer would come to
9
+ * depend on the view plugin. A display that wants the view names the view.
10
+ *
11
+ * Components and structural helpers taking duck-typed model shapes keep calling
12
+ * `getContainingView` — the same carve-out `containingHost` documents.
13
+ */
14
+ export function containingLgv(self) {
15
+ return getContainingView(self);
16
+ }
@@ -88,9 +88,7 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
88
88
  readonly effectiveBodyMounted: boolean;
89
89
  menuItems(): MenuItem[];
90
90
  readonly ownTracks: import("@jbrowse/core/util").AbstractTrackModel[];
91
- readonly allViews: import("@jbrowse/core/util").AbstractViewModel[];
92
- } & {
93
- readonly allTracks: import("@jbrowse/core/util").AbstractTrackModel[];
91
+ readonly ownViews: import("@jbrowse/core/util").AbstractViewModel[];
94
92
  } & {
95
93
  setDisplayName(name: string): void;
96
94
  setWidth(newWidth: number): void;
@@ -105,6 +103,15 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
105
103
  } & {
106
104
  afterAttach(): void;
107
105
  } & {
106
+ /**
107
+ * #volatile
108
+ * Height of each track's in-flow label band, measured by
109
+ * TrackContainer as the rendering container's offset inside its Paper.
110
+ * Absent (0) while the label is hidden, overlapping, or not yet
111
+ * measured. Observable, so the offset and height getters re-derive
112
+ * when a band changes.
113
+ */
114
+ trackLabelBands: import("mobx").ObservableMap<string, number>;
108
115
  /**
109
116
  * #volatile
110
117
  */
@@ -125,10 +132,6 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
125
132
  * #volatile
126
133
  */
127
134
  volatileError: unknown;
128
- /**
129
- * #volatile
130
- */
131
- trackRefs: Record<string, HTMLDivElement>;
132
135
  /**
133
136
  * #volatile
134
137
  */
@@ -239,6 +242,12 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
239
242
  */
240
243
  readonly stickyViewHeaders: boolean;
241
244
  } & {
245
+ /**
246
+ * #getter
247
+ * The census entry for this view: the tracks it holds itself. Declared
248
+ * rather than derived — see `BaseViewModel.ownTracks`.
249
+ */
250
+ readonly ownTracks: any[];
242
251
  /**
243
252
  * #getter
244
253
  * Assembly-name prefix for the scalebar refName labels, or undefined for
@@ -657,14 +666,10 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
657
666
  * #getter
658
667
  * A track's full cost beyond its display height.
659
668
  *
660
- * The track *label* is not counted, and cannot be: an offset label is an
661
- * in-flow box whose height is whatever the theme renders a Paper of icon
662
- * buttons at — 31.140625px on the stock theme, and not a number this file
663
- * can derive. So these getters are exact while labels are hidden or
664
- * overlapping, and short by one label box per labelled track otherwise.
665
- * Anything needing the offset to the pixel with labels showing measures
666
- * the DOM; `BreakpointSplitViewOverlay` does, and falls back to this
667
- * arithmetic only for a track with no mounted div.
669
+ * The track *label* is not counted here: an offset label is an in-flow
670
+ * box whose height is whatever the theme renders a Paper of icon buttons
671
+ * at, so it is measured into `trackLabelBands` per track and added by
672
+ * `trackLabelBand` rather than derived.
668
673
  */
669
674
  readonly trackChromeHeight: number;
670
675
  /**
@@ -693,6 +698,12 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
693
698
  * can't diverge from what is on screen.
694
699
  */
695
700
  trackHeight(track: (import("@jbrowse/mobx-state-tree").IMSTArray<import("@jbrowse/mobx-state-tree").IAnyType> & import("@jbrowse/mobx-state-tree").IStateTreeNode<import("@jbrowse/mobx-state-tree").IArrayType<import("@jbrowse/mobx-state-tree").IAnyType>>)[number]): any;
701
+ /**
702
+ * #method
703
+ * the measured in-flow label band above a track's rendering container,
704
+ * 0 until TrackContainer has measured one or when no label is in flow
705
+ */
706
+ trackLabelBand(track: (import("@jbrowse/mobx-state-tree").IMSTArray<import("@jbrowse/mobx-state-tree").IAnyType> & import("@jbrowse/mobx-state-tree").IStateTreeNode<import("@jbrowse/mobx-state-tree").IArrayType<import("@jbrowse/mobx-state-tree").IAnyType>>)[number]): number;
696
707
  /**
697
708
  * #getter
698
709
  */
@@ -712,10 +723,10 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
712
723
  * first, then unpinned), from the same constants TrackContainer lays its
713
724
  * Paper out with. Returns `undefined` if the track is not present.
714
725
  *
715
- * Exact while track labels are hidden or overlapping. With an offset
716
- * label the answer is short by one label box per labelled track above
717
- * this one — see `trackChromeHeight` for why that box is not derivable
718
- * here.
726
+ * Includes each track's measured label band, this track's own included,
727
+ * since an in-flow label sits above the rendering container inside the
728
+ * same Paper. Exact once TrackContainer has measured; before that it is
729
+ * short by the unmeasured bands.
719
730
  */
720
731
  getTrackYOffset(trackId: string): number | undefined;
721
732
  /**
@@ -827,6 +838,7 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
827
838
  /**
828
839
  * #action
829
840
  */
841
+ setTrackLabelBand(trackId: string, band: number): void;
830
842
  setWidth(newWidth: number): void;
831
843
  /**
832
844
  * #action
@@ -1004,9 +1016,10 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
1004
1016
  setLaunch(launch?: LaunchInput<InitState>): void;
1005
1017
  /**
1006
1018
  * #method
1007
- * creates an svg export and save using FileSaver
1019
+ * renders the view to SVG markup, which it returns; saves it through
1020
+ * FileSaver unless `save: false`
1008
1021
  */
1009
- exportSvg(opts?: ExportSvgOptions): Promise<void>;
1022
+ exportSvg(opts?: ExportSvgOptions): Promise<string>;
1010
1023
  } & {
1011
1024
  slide: (viewWidths: number) => void;
1012
1025
  zoom: (targetBpPerPx: number) => void;
@@ -1706,7 +1719,7 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
1706
1719
  * `centerAt`'s animated twin: the same destination, reached along the
1707
1720
  * arc instead of jumped to, at the zoom the view is already on.
1708
1721
  */
1709
- flyToCenter(coord: number, refName: string): void;
1722
+ flyToCenter(coord: number, refName: string, displayedRegionIndex?: number): void;
1710
1723
  } & {
1711
1724
  afterCreate(): void;
1712
1725
  afterAttach(): void;
@@ -1746,9 +1759,7 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
1746
1759
  readonly effectiveBodyMounted: boolean;
1747
1760
  menuItems(): MenuItem[];
1748
1761
  readonly ownTracks: import("@jbrowse/core/util").AbstractTrackModel[];
1749
- readonly allViews: import("@jbrowse/core/util").AbstractViewModel[];
1750
- } & {
1751
- readonly allTracks: import("@jbrowse/core/util").AbstractTrackModel[];
1762
+ readonly ownViews: import("@jbrowse/core/util").AbstractViewModel[];
1752
1763
  } & {
1753
1764
  setDisplayName(name: string): void;
1754
1765
  setWidth(newWidth: number): void;
@@ -1763,6 +1774,15 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
1763
1774
  } & {
1764
1775
  afterAttach(): void;
1765
1776
  } & {
1777
+ /**
1778
+ * #volatile
1779
+ * Height of each track's in-flow label band, measured by
1780
+ * TrackContainer as the rendering container's offset inside its Paper.
1781
+ * Absent (0) while the label is hidden, overlapping, or not yet
1782
+ * measured. Observable, so the offset and height getters re-derive
1783
+ * when a band changes.
1784
+ */
1785
+ trackLabelBands: import("mobx").ObservableMap<string, number>;
1766
1786
  /**
1767
1787
  * #volatile
1768
1788
  */
@@ -1783,10 +1803,6 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
1783
1803
  * #volatile
1784
1804
  */
1785
1805
  volatileError: unknown;
1786
- /**
1787
- * #volatile
1788
- */
1789
- trackRefs: Record<string, HTMLDivElement>;
1790
1806
  /**
1791
1807
  * #volatile
1792
1808
  */
@@ -1897,6 +1913,12 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
1897
1913
  */
1898
1914
  readonly stickyViewHeaders: boolean;
1899
1915
  } & {
1916
+ /**
1917
+ * #getter
1918
+ * The census entry for this view: the tracks it holds itself. Declared
1919
+ * rather than derived — see `BaseViewModel.ownTracks`.
1920
+ */
1921
+ readonly ownTracks: any[];
1900
1922
  /**
1901
1923
  * #getter
1902
1924
  * Assembly-name prefix for the scalebar refName labels, or undefined for
@@ -2315,14 +2337,10 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
2315
2337
  * #getter
2316
2338
  * A track's full cost beyond its display height.
2317
2339
  *
2318
- * The track *label* is not counted, and cannot be: an offset label is an
2319
- * in-flow box whose height is whatever the theme renders a Paper of icon
2320
- * buttons at — 31.140625px on the stock theme, and not a number this file
2321
- * can derive. So these getters are exact while labels are hidden or
2322
- * overlapping, and short by one label box per labelled track otherwise.
2323
- * Anything needing the offset to the pixel with labels showing measures
2324
- * the DOM; `BreakpointSplitViewOverlay` does, and falls back to this
2325
- * arithmetic only for a track with no mounted div.
2340
+ * The track *label* is not counted here: an offset label is an in-flow
2341
+ * box whose height is whatever the theme renders a Paper of icon buttons
2342
+ * at, so it is measured into `trackLabelBands` per track and added by
2343
+ * `trackLabelBand` rather than derived.
2326
2344
  */
2327
2345
  readonly trackChromeHeight: number;
2328
2346
  /**
@@ -2351,6 +2369,12 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
2351
2369
  * can't diverge from what is on screen.
2352
2370
  */
2353
2371
  trackHeight(track: (import("@jbrowse/mobx-state-tree").IMSTArray<import("@jbrowse/mobx-state-tree").IAnyType> & import("@jbrowse/mobx-state-tree").IStateTreeNode<import("@jbrowse/mobx-state-tree").IArrayType<import("@jbrowse/mobx-state-tree").IAnyType>>)[number]): any;
2372
+ /**
2373
+ * #method
2374
+ * the measured in-flow label band above a track's rendering container,
2375
+ * 0 until TrackContainer has measured one or when no label is in flow
2376
+ */
2377
+ trackLabelBand(track: (import("@jbrowse/mobx-state-tree").IMSTArray<import("@jbrowse/mobx-state-tree").IAnyType> & import("@jbrowse/mobx-state-tree").IStateTreeNode<import("@jbrowse/mobx-state-tree").IArrayType<import("@jbrowse/mobx-state-tree").IAnyType>>)[number]): number;
2354
2378
  /**
2355
2379
  * #getter
2356
2380
  */
@@ -2370,10 +2394,10 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
2370
2394
  * first, then unpinned), from the same constants TrackContainer lays its
2371
2395
  * Paper out with. Returns `undefined` if the track is not present.
2372
2396
  *
2373
- * Exact while track labels are hidden or overlapping. With an offset
2374
- * label the answer is short by one label box per labelled track above
2375
- * this one — see `trackChromeHeight` for why that box is not derivable
2376
- * here.
2397
+ * Includes each track's measured label band, this track's own included,
2398
+ * since an in-flow label sits above the rendering container inside the
2399
+ * same Paper. Exact once TrackContainer has measured; before that it is
2400
+ * short by the unmeasured bands.
2377
2401
  */
2378
2402
  getTrackYOffset(trackId: string): number | undefined;
2379
2403
  /**
@@ -2485,6 +2509,7 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
2485
2509
  /**
2486
2510
  * #action
2487
2511
  */
2512
+ setTrackLabelBand(trackId: string, band: number): void;
2488
2513
  setWidth(newWidth: number): void;
2489
2514
  /**
2490
2515
  * #action
@@ -2662,9 +2687,10 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
2662
2687
  setLaunch(launch?: LaunchInput<InitState>): void;
2663
2688
  /**
2664
2689
  * #method
2665
- * creates an svg export and save using FileSaver
2690
+ * renders the view to SVG markup, which it returns; saves it through
2691
+ * FileSaver unless `save: false`
2666
2692
  */
2667
- exportSvg(opts?: ExportSvgOptions): Promise<void>;
2693
+ exportSvg(opts?: ExportSvgOptions): Promise<string>;
2668
2694
  } & {
2669
2695
  slide: (viewWidths: number) => void;
2670
2696
  zoom: (targetBpPerPx: number) => void;
@@ -3364,7 +3390,7 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
3364
3390
  * `centerAt`'s animated twin: the same destination, reached along the
3365
3391
  * arc instead of jumped to, at the zoom the view is already on.
3366
3392
  */
3367
- flyToCenter(coord: number, refName: string): void;
3393
+ flyToCenter(coord: number, refName: string, displayedRegionIndex?: number): void;
3368
3394
  } & {
3369
3395
  afterCreate(): void;
3370
3396
  afterAttach(): void;
@@ -3387,7 +3413,7 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
3387
3413
  showGridlines: boolean;
3388
3414
  labelsVisible: boolean;
3389
3415
  scalebarOnly: boolean;
3390
- launch?: LaunchInput<InitState> | undefined;
3416
+ launch: LaunchInput<InitState> | undefined;
3391
3417
  showCytobands?: false | undefined;
3392
3418
  trackLabels?: string | undefined;
3393
3419
  colorByCDS?: true | undefined;