@jbrowse/plugin-linear-genome-view 5.0.0-beta.1 → 5.0.0-beta.11

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.
Files changed (148) hide show
  1. package/esm/FeatureTrack/configSchema.d.ts +142 -50
  2. package/esm/LaunchLinearGenomeView/index.js +2 -13
  3. package/esm/LinearGenomeView/afterAttach.d.ts +5 -5
  4. package/esm/LinearGenomeView/afterAttach.js +75 -62
  5. package/esm/LinearGenomeView/components/CenterLine.js +5 -3
  6. package/esm/LinearGenomeView/components/CrisprGuidePanel.js +3 -5
  7. package/esm/LinearGenomeView/components/ExportSvgDialog.d.ts +3 -2
  8. package/esm/LinearGenomeView/components/ExportSvgDialog.js +6 -2
  9. package/esm/LinearGenomeView/components/GetSequenceDialog.js +2 -2
  10. package/esm/LinearGenomeView/components/Header.js +11 -1
  11. package/esm/LinearGenomeView/components/HeaderClearHighlightButton.js +2 -2
  12. package/esm/LinearGenomeView/components/HeaderPanControls.js +2 -1
  13. package/esm/LinearGenomeView/components/HeaderZoomControls.js +4 -34
  14. package/esm/LinearGenomeView/components/Highlight.js +7 -7
  15. package/esm/LinearGenomeView/components/HighlightBand.d.ts +9 -1
  16. package/esm/LinearGenomeView/components/HighlightBand.js +15 -3
  17. package/esm/LinearGenomeView/components/HighlightChip.d.ts +1 -2
  18. package/esm/LinearGenomeView/components/HighlightChip.js +5 -7
  19. package/esm/LinearGenomeView/components/HoverPositionHighlight.js +3 -3
  20. package/esm/LinearGenomeView/components/ImportForm.js +2 -2
  21. package/esm/LinearGenomeView/components/LinearGenomeView.js +3 -3
  22. package/esm/LinearGenomeView/components/LinearGenomeViewContainer.js +3 -4
  23. package/esm/LinearGenomeView/components/MiniControls.js +3 -2
  24. package/esm/LinearGenomeView/components/OverviewHighlight.js +3 -3
  25. package/esm/LinearGenomeView/components/OverviewHighlightBand.js +2 -3
  26. package/esm/LinearGenomeView/components/OverviewRubberband.js +20 -45
  27. package/esm/LinearGenomeView/components/OverviewScalebar.js +4 -5
  28. package/esm/LinearGenomeView/components/OverviewScalebarPolygon.js +5 -3
  29. package/esm/LinearGenomeView/components/PaddingBlocks.d.ts +1 -1
  30. package/esm/LinearGenomeView/components/PaddingBlocks.js +14 -9
  31. package/esm/LinearGenomeView/components/RangeSelectOverlay.js +5 -6
  32. package/esm/LinearGenomeView/components/ResizeAllTracksHandle.d.ts +5 -0
  33. package/esm/LinearGenomeView/components/ResizeAllTracksHandle.js +71 -0
  34. package/esm/LinearGenomeView/components/Rubberband.d.ts +1 -2
  35. package/esm/LinearGenomeView/components/Rubberband.js +3 -2
  36. package/esm/LinearGenomeView/components/SVGHighlightBand.js +2 -3
  37. package/esm/LinearGenomeView/components/Scalebar.d.ts +2 -6
  38. package/esm/LinearGenomeView/components/Scalebar.js +8 -5
  39. package/esm/LinearGenomeView/components/ScalebarRefNameLabels.js +49 -34
  40. package/esm/LinearGenomeView/components/SearchBox.js +8 -7
  41. package/esm/LinearGenomeView/components/SearchResultsDialog.d.ts +2 -1
  42. package/esm/LinearGenomeView/components/SearchResultsDialog.js +2 -2
  43. package/esm/LinearGenomeView/components/SearchResultsTable.d.ts +2 -1
  44. package/esm/LinearGenomeView/components/SearchResultsTable.js +12 -29
  45. package/esm/LinearGenomeView/components/SequenceSearchDialog.d.ts +2 -4
  46. package/esm/LinearGenomeView/components/TrackContainer.js +63 -9
  47. package/esm/LinearGenomeView/components/TrackLabel.js +10 -3
  48. package/esm/LinearGenomeView/components/TrackLabelButtons.js +2 -2
  49. package/esm/LinearGenomeView/components/TrackLabelDragHandle.js +2 -2
  50. package/esm/LinearGenomeView/components/TrackLabelMenu.js +3 -2
  51. package/esm/LinearGenomeView/components/TrackRenderingContainer.d.ts +4 -1
  52. package/esm/LinearGenomeView/components/TrackRenderingContainer.js +11 -23
  53. package/esm/LinearGenomeView/components/TracksContainer.js +10 -22
  54. package/esm/LinearGenomeView/components/VerticalGuide.d.ts +1 -2
  55. package/esm/LinearGenomeView/components/VerticalGuide.js +3 -16
  56. package/esm/LinearGenomeView/components/ZoomButton.js +6 -1
  57. package/esm/LinearGenomeView/components/ZoomTransform.js +4 -0
  58. package/esm/LinearGenomeView/components/fetchSequence.d.ts +1 -2
  59. package/esm/LinearGenomeView/components/recentLocationsMenu.js +10 -0
  60. package/esm/LinearGenomeView/components/searchModes.d.ts +1 -1
  61. package/esm/LinearGenomeView/components/searchModes.js +1 -1
  62. package/esm/LinearGenomeView/components/useRangeSelect.d.ts +0 -3
  63. package/esm/LinearGenomeView/components/useRangeSelect.js +20 -59
  64. package/esm/LinearGenomeView/components/useSideScroll.d.ts +16 -2
  65. package/esm/LinearGenomeView/components/useSideScroll.js +153 -103
  66. package/esm/LinearGenomeView/components/util.d.ts +4 -0
  67. package/esm/LinearGenomeView/components/util.js +4 -2
  68. package/esm/LinearGenomeView/consts.d.ts +2 -0
  69. package/esm/LinearGenomeView/consts.js +3 -1
  70. package/esm/LinearGenomeView/containingLgv.d.ts +21 -0
  71. package/esm/LinearGenomeView/containingLgv.js +23 -0
  72. package/esm/LinearGenomeView/headerFit.d.ts +4 -2
  73. package/esm/LinearGenomeView/headerFit.js +20 -7
  74. package/esm/LinearGenomeView/index.d.ts +1 -5
  75. package/esm/LinearGenomeView/index.js +10 -7
  76. package/esm/LinearGenomeView/keyboardHandler.d.ts +2 -0
  77. package/esm/LinearGenomeView/keyboardHandler.js +15 -1
  78. package/esm/LinearGenomeView/launchKeys.d.ts +2 -0
  79. package/esm/LinearGenomeView/launchKeys.js +27 -0
  80. package/esm/LinearGenomeView/lazyDialogs.d.ts +2 -4
  81. package/esm/LinearGenomeView/lazyDialogs.js +1 -0
  82. package/esm/LinearGenomeView/linkedViewSync.d.ts +3 -11
  83. package/esm/LinearGenomeView/linkedViewSync.js +27 -43
  84. package/esm/LinearGenomeView/menuItems.d.ts +16 -18
  85. package/esm/LinearGenomeView/menuItems.js +242 -151
  86. package/esm/LinearGenomeView/model.d.ts +2027 -200
  87. package/esm/LinearGenomeView/model.js +560 -395
  88. package/esm/LinearGenomeView/sharedScaleContainer.d.ts +1 -1
  89. package/esm/LinearGenomeView/showRegionsWithUndo.js +7 -1
  90. package/esm/LinearGenomeView/stickyChrome.d.ts +5 -9
  91. package/esm/LinearGenomeView/stickyChrome.js +5 -9
  92. package/esm/LinearGenomeView/svgcomponents/SVGGridlines.js +4 -4
  93. package/esm/LinearGenomeView/svgcomponents/SVGHeader.d.ts +4 -1
  94. package/esm/LinearGenomeView/svgcomponents/SVGHeader.js +8 -21
  95. package/esm/LinearGenomeView/svgcomponents/SVGHighlights.js +8 -8
  96. package/esm/LinearGenomeView/svgcomponents/SVGHighlightsOverlay.js +3 -3
  97. package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +7 -11
  98. package/esm/LinearGenomeView/svgcomponents/SVGRegionSeparators.js +2 -5
  99. package/esm/LinearGenomeView/svgcomponents/SVGRowHeader.js +5 -6
  100. package/esm/LinearGenomeView/svgcomponents/SVGRuler.js +9 -10
  101. package/esm/LinearGenomeView/svgcomponents/SVGScalebar.js +3 -3
  102. package/esm/LinearGenomeView/svgcomponents/SVGStackedRow.d.ts +27 -0
  103. package/esm/LinearGenomeView/svgcomponents/SVGStackedRow.js +12 -0
  104. package/esm/LinearGenomeView/svgcomponents/SVGTrackLabel.d.ts +2 -1
  105. package/esm/LinearGenomeView/svgcomponents/SVGTrackLabel.js +29 -16
  106. package/esm/LinearGenomeView/svgcomponents/SVGTracks.d.ts +4 -4
  107. package/esm/LinearGenomeView/svgcomponents/SVGTracks.js +6 -5
  108. package/esm/LinearGenomeView/svgcomponents/SVGView.d.ts +1 -1
  109. package/esm/LinearGenomeView/svgcomponents/SVGView.js +1 -1
  110. package/esm/LinearGenomeView/svgcomponents/figureContract.js +14 -14
  111. package/esm/LinearGenomeView/svgcomponents/renderViewTracks.d.ts +1 -0
  112. package/esm/LinearGenomeView/svgcomponents/renderViewTracks.js +1 -1
  113. package/esm/LinearGenomeView/svgcomponents/useViewSvgFigure.js +50 -68
  114. package/esm/LinearGenomeView/svgcomponents/util.d.ts +20 -8
  115. package/esm/LinearGenomeView/svgcomponents/util.js +50 -29
  116. package/esm/LinearGenomeView/types.d.ts +8 -11
  117. package/esm/LinearGenomeView/util.d.ts +60 -45
  118. package/esm/LinearGenomeView/util.js +131 -160
  119. package/esm/MultiLevelRubberband/MultiLevelRubberband.js +7 -4
  120. package/esm/MultiLevelRubberband/index.d.ts +2 -0
  121. package/esm/MultiLevelRubberband/index.js +2 -0
  122. package/esm/MultiLevelRubberband/menuItems.d.ts +34 -0
  123. package/esm/MultiLevelRubberband/menuItems.js +52 -0
  124. package/esm/MultiLevelRubberband/rowLabel.d.ts +22 -0
  125. package/esm/MultiLevelRubberband/rowLabel.js +27 -0
  126. package/esm/MultiLevelRubberband/types.d.ts +1 -0
  127. package/esm/MultiLevelRubberband/useRangeSelect.d.ts +2 -1
  128. package/esm/MultiLevelRubberband/useRangeSelect.js +31 -47
  129. package/esm/displayKitTests/perRegionTestEnv.d.ts +244 -86
  130. package/esm/displayKitTests/perRegionTestEnv.js +30 -29
  131. package/esm/index.d.ts +15 -85
  132. package/esm/index.js +13 -36
  133. package/esm/searchUtils.d.ts +18 -9
  134. package/esm/searchUtils.js +85 -69
  135. package/esm/shared/RubberbandSpan.js +2 -1
  136. package/esm/shared/coordLabels.d.ts +7 -0
  137. package/esm/shared/coordLabels.js +15 -1
  138. package/esm/shared/useWindowDrag.d.ts +18 -0
  139. package/esm/shared/useWindowDrag.js +45 -0
  140. package/package.json +10 -10
  141. package/esm/LinearGenomeView/FloatingSvgOverlay.d.ts +0 -5
  142. package/esm/LinearGenomeView/FloatingSvgOverlay.js +0 -25
  143. package/esm/LinearGenomeView/initKeys.d.ts +0 -13
  144. package/esm/LinearGenomeView/initKeys.js +0 -104
  145. package/esm/MultiLevelRubberband/VerticalGuide.d.ts +0 -6
  146. package/esm/MultiLevelRubberband/VerticalGuide.js +0 -21
  147. package/esm/lazyPluginExports.d.ts +0 -23
  148. package/esm/lazyPluginExports.js +0 -36
@@ -1,25 +1,25 @@
1
1
  import { useEffect, useRef } from 'react';
2
2
  import { svgNodeId } from '@jbrowse/core/svg/svgId';
3
+ import { contractReportsOn, reportContractViolation, } from '@jbrowse/render-core/contractReports';
3
4
  /**
4
- * Dev-only checks that a live SVG figure is being used the way it can work.
5
- * No-ops in production.
5
+ * Checks that a live SVG figure is being used the way it can work.
6
6
  *
7
7
  * `useViewSvgFigure` publishes a figure whose two halves are drawn at different
8
8
  * moments and only agree by construction, and both ways of breaking that are
9
9
  * invisible in the output: the drawing is still a plausible picture of
10
10
  * somewhere, just not of one moment or not with its own clips. So the run
11
11
  * reports instead, through the `[jbrowse <family> contract]` channel every
12
- * dev-only check in the tree uses — `config/jest/contractGate.js` fails the test
13
- * that collects one, and ARCHITECTURAL_LIMITS.md §"Ordering is the contract" is
14
- * the register. `figure` rather than `svg-figure` because the gate's family
15
- * pattern is `\w+`, which a hyphen is not.
12
+ * check in the tree uses — `config/jest/contractGate.js` fails the test that
13
+ * collects one, and ARCHITECTURAL_LIMITS.md §"Ordering is the contract" is the
14
+ * register. `figure` rather than `svg-figure` because the gate's family pattern
15
+ * is `\w+`, which a hyphen is not.
16
16
  *
17
- * `console.error` and never a throw, as the rest of that family: a figure is
18
- * chrome a host renders beside its own, and taking the host's tree down over a
19
- * drawing is worse than the drawing being wrong.
17
+ * Reported and never thrown, as the rest of that family: a figure is chrome a
18
+ * host renders beside its own, and taking the host's tree down over a drawing
19
+ * is worse than the drawing being wrong.
20
20
  */
21
21
  function report(message) {
22
- console.error(`[jbrowse figure contract] ${message}`);
22
+ reportContractViolation('figure', message);
23
23
  }
24
24
  // Live figures per view, keyed the way their SVG ids are. A count rather than a
25
25
  // flag: the report belongs on the second mount, and the first has to survive the
@@ -43,9 +43,6 @@ const figuresPerView = new Map();
43
43
  */
44
44
  export function useOneFigurePerView(view) {
45
45
  useEffect(() => {
46
- if (process.env.NODE_ENV === 'production') {
47
- return undefined;
48
- }
49
46
  // captured, not re-derived on cleanup: by then the view may have left the
50
47
  // tree, and `svgNodeId` walks it
51
48
  const key = svgNodeId(view);
@@ -99,7 +96,10 @@ export function useFrozenFigureContract(ref, snapshot) {
99
96
  rendered.current = snapshot;
100
97
  useEffect(() => {
101
98
  const el = ref.current;
102
- if (process.env.NODE_ENV === 'production' || !el) {
99
+ // the one check here with a cost of its own — a `MutationObserver` over the
100
+ // whole figure subtree — so unlike its neighbour it asks whether the
101
+ // channel is armed rather than running and reporting into it
102
+ if (!contractReportsOn() || !el) {
103
103
  return undefined;
104
104
  }
105
105
  const observer = new MutationObserver(() => {
@@ -7,6 +7,7 @@ export interface SvgExportTrack {
7
7
  displays: {
8
8
  height: number;
9
9
  svgLegendWidth?: () => number;
10
+ svgSidebarWidth?: () => number;
10
11
  /**
11
12
  * Optional, and the option is the point: SVG export is a substantial extra
12
13
  * implementation for a display type, and a third-party plugin that has not
@@ -59,7 +59,7 @@ export async function renderViewTracks({ view, opts, theme, textHeight, trackLab
59
59
  return {
60
60
  tracks,
61
61
  displayResults,
62
- tracksHeight: totalHeight(tracks, textHeight, trackLabels),
62
+ tracksHeight: totalHeight(tracks, trackLabels, textHeight),
63
63
  legendWidth,
64
64
  skippedTracks,
65
65
  };
@@ -1,17 +1,14 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { memo, useCallback, useEffect, useRef, useSyncExternalStore, } from 'react';
3
+ import { SvgThemeProviders } from '@jbrowse/core/svg/SvgThemeProviders';
3
4
  import { exportMargin } from '@jbrowse/core/svg/constants';
5
+ import { svgNodeId } from '@jbrowse/core/svg/svgId';
4
6
  import { svgTrackName } from '@jbrowse/core/svg/trackNames';
5
- import { StyleThemeProvider } from '@jbrowse/core/ui/PaletteContext';
6
- import { resolveStyleTheme } from '@jbrowse/core/ui/styleTheme';
7
- import { createJBrowseTheme } from '@jbrowse/core/ui/theme';
8
7
  import { getSession } from '@jbrowse/core/util';
9
8
  import { useFetch } from '@jbrowse/core/util/useFetch';
10
- import { isAlive } from '@jbrowse/mobx-state-tree';
11
- import { ThemeProvider } from '@mui/material';
9
+ import { getSnapshot, isAlive } from '@jbrowse/mobx-state-tree';
12
10
  import { reaction } from 'mobx';
13
- import SVGRowHeader from './SVGRowHeader.js';
14
- import SVGView from './SVGView.js';
11
+ import SVGStackedRow from './SVGStackedRow.js';
15
12
  import { useFrozenFigureContract, useOneFigurePerView, } from './figureContract.js';
16
13
  import { renderViewTracks } from './renderViewTracks.js';
17
14
  import { defaultTextHeight, getRowHeaderLayout, trackLabelLeftOffset, } from './util.js';
@@ -87,38 +84,31 @@ async function renderFigure(view, opts) {
87
84
  * render. Where it matters, this line is the only thing holding the two halves
88
85
  * together.
89
86
  *
90
- * Both providers, as `wrapSvgExport` mounts for the file path, and for the same
91
- * reason: the chrome takes its colors from the Material UI theme and a
92
- * `makeStyles` component would take its own from the style theme, so a figure
93
- * mounted in a host that installs neither would otherwise draw itself in
94
- * JBrowse's *default light* theme whatever the session is set to.
87
+ * The export's theme providers, as the file path mounts them: a host that
88
+ * installs neither would otherwise get JBrowse's default light theme whatever
89
+ * the session is set to.
95
90
  *
96
91
  * No background rect, unlike a file — that has nothing behind it, and a figure
97
- * in a page has the page. No `resetSvgClipIds` either, and that is the opposite
98
- * decision from the file path on purpose: the counter is document-global, so
99
- * resetting it here would renumber ids that a figure already on the page is
100
- * pointing at. Letting it run is what makes several live figures unique.
92
+ * in a page has the page. No `withFreshSvgClipIds` either, and that is the
93
+ * opposite decision from the file path on purpose: the counter is
94
+ * document-global, so restarting it here would reuse ids that a figure already
95
+ * on the page is pointing at. Letting it run is what makes several live figures
96
+ * unique.
101
97
  */
102
98
  const FrozenSvgFigure = memo(function FrozenSvgFigure({ view, snapshot, }) {
103
99
  const { width, height, margin, theme } = snapshot;
104
- // dev-only, and it watches for the one thing this component cannot enforce:
105
- // an observer INSIDE the tree, which the memo above is not between
100
+ // it watches for the one thing this component cannot enforce: an observer
101
+ // INSIDE the tree, which the memo above is not between
106
102
  const ref = useRef(null);
107
103
  useFrozenFigureContract(ref, snapshot);
108
- return (_jsx(ThemeProvider, { theme: createJBrowseTheme(theme), children: _jsx(StyleThemeProvider, { theme: resolveStyleTheme({ configTheme: theme }), children: _jsx("svg", { ref: ref, width: width, height: height, viewBox: `0 0 ${width} ${height}`,
109
- // It is a vector, so a narrower container scales it rather than
110
- // clipping it; the height attribute stays, so a box reserved for it
111
- // does not change size when that happens. Deliberately not a prop:
112
- // anything passed per-render would have to reach past the memo above,
113
- // which is the one thing holding the figure's two halves together. A
114
- // host that wants other sizing wraps this in a box of its own.
115
- style: { display: 'block', maxWidth: '100%' }, children: _jsx("g", { transform: `translate(${margin} ${snapshot.bandHeight})`, children: _jsx(SVGView, { view: view, displayResults: snapshot.displayResults, header: _jsx(SVGRowHeader, { view: view, fontSize: snapshot.fontSize, rulerHeight: snapshot.rulerHeight, showScalebar: snapshot.showScalebar }), fontSize: snapshot.fontSize, textHeight: snapshot.textHeight, trackLabels: snapshot.trackLabels, trackLabelOffset: snapshot.trackLabelOffset,
116
- // the row header is exactly the ruler, so the bodies start at its
117
- // height
118
- contentTop: snapshot.rulerHeight, tracksHeight: snapshot.tracksHeight, showGridlines: snapshot.showGridlines,
119
- // the left gutter the per-track clip may bleed into, so content
120
- // drawn left of zero — a wiggle's y-axis — survives
121
- leftBuffer: snapshot.margin, legendWidth: snapshot.legendWidth }) }) }) }) }));
104
+ return (_jsx(SvgThemeProviders, { theme: theme, children: _jsx("svg", { ref: ref, width: width, height: height, viewBox: `0 0 ${width} ${height}`,
105
+ // It is a vector, so a narrower container scales it rather than
106
+ // clipping it; the height attribute stays, so a box reserved for it
107
+ // does not change size when that happens. Deliberately not a prop:
108
+ // anything passed per-render would have to reach past the memo above,
109
+ // which is the one thing holding the figure's two halves together. A
110
+ // host that wants other sizing wraps this in a box of its own.
111
+ style: { display: 'block', maxWidth: '100%' }, children: _jsx(SVGStackedRow, { view: view, rendered: snapshot, top: snapshot.bandHeight, margin: margin, fontSize: snapshot.fontSize, textHeight: snapshot.textHeight, rulerHeight: snapshot.rulerHeight, trackLabels: snapshot.trackLabels, trackLabelOffset: snapshot.trackLabelOffset, showGridlines: snapshot.showGridlines, legendWidth: snapshot.legendWidth, showScalebar: snapshot.showScalebar }) }) }));
122
112
  });
123
113
  /**
124
114
  * What "the figure is out of date" means, as a string.
@@ -141,11 +131,10 @@ const FrozenSvgFigure = memo(function FrozenSvgFigure({ view, snapshot, }) {
141
131
  * highlight bands**, which are drawn in the figure and are the one thing in it a
142
132
  * reader adds without navigating anywhere.
143
133
  *
144
- * A plugin's own overlay (grid-bookmark's bookmarks, through
145
- * `LinearGenomeView-HighlightSVGComponent`) is state this cannot see, so a
146
- * bookmark added under a drawn figure shows up at its next redraw rather than at
147
- * once. Subscribing from inside the figure is the thing that must not happen —
148
- * see the note at the top of the file.
134
+ * A plugin's own overlay, through `LinearGenomeView-HighlightSVGComponent`, is
135
+ * state this cannot see, so a change to it shows up at the figure's next
136
+ * redraw rather than at once. Subscribing from inside the figure is the thing
137
+ * that must not happen — see the note at the top of the file.
149
138
  */
150
139
  function figureKey(view, themeName) {
151
140
  // A closed view is not an error, it is a host doing an ordinary thing in an
@@ -156,10 +145,10 @@ function figureKey(view, themeName) {
156
145
  // throw a TypeError inside the reaction, where MobX swallows it. No key means
157
146
  // no figure, which is what a host that closed the view wanted anyway.
158
147
  //
159
- // `view.width` throws by design before the view has been measured, and
160
- // `ready` is the gate that says it has been — and that there are regions to
161
- // draw, which is the second async step `initialized` does not cover
162
- if (!isAlive(view) || !view.ready) {
148
+ // `view.width` throws by design before the view has been measured. `status`
149
+ // is ready only once it has been AND there are regions to draw — `ready` is
150
+ // also true for a view nothing has told where to look, measured or not
151
+ if (!isAlive(view) || view.status.type !== 'ready') {
163
152
  return '';
164
153
  }
165
154
  const session = getSession(view);
@@ -167,14 +156,12 @@ function figureKey(view, themeName) {
167
156
  view.coarseVisibleLocStrings,
168
157
  view.coarseBpPerPx,
169
158
  view.width,
170
- view.tracks.map(track => [
171
- track.configuration.trackId,
172
- track.minimized,
173
- track.displays[0]?.height,
174
- ]),
159
+ // the snapshot carries pinned, minimized and the display settings a body
160
+ // bakes in; the effective height is a getter, so it is read beside it
161
+ view.tracks.map(track => [getSnapshot(track), track.displays[0]?.height]),
175
162
  session.getActiveThemeOptions?.(themeName),
176
- session.highlightsVisible ? view.highlight : false,
177
- view.labelsVisible,
163
+ session.highlightsVisible ? view.highlights : false,
164
+ session.highlightLabelsVisible,
178
165
  ]);
179
166
  }
180
167
  /**
@@ -212,25 +199,7 @@ function useFigureKey(view, themeName) {
212
199
  export function useViewSvgFigure(view, { fontSize = 12, rulerHeight = 30, trackLabels = 'offset', showGridlines = true, showScalebar = true, rasterizeLayers = false, margin = exportMargin, themeName, } = {}) {
213
200
  const key = useFigureKey(view, themeName);
214
201
  useOneFigurePerView(view);
215
- const { data: snapshot, error, isLoading, } = useFetch(
216
- // Every option is in the key by value, so an inline options object costs
217
- // nothing and a changed option redraws. A null key is `useFetch`'s own "not
218
- // yet", which is what an unready view reports.
219
- key
220
- ? [
221
- 'lgv-svg-figure',
222
- key,
223
- JSON.stringify([
224
- fontSize,
225
- rulerHeight,
226
- trackLabels,
227
- showGridlines,
228
- showScalebar,
229
- rasterizeLayers,
230
- margin,
231
- ]),
232
- ]
233
- : null, () => renderFigure(view, {
202
+ const opts = {
234
203
  fontSize,
235
204
  rulerHeight,
236
205
  trackLabels,
@@ -239,7 +208,20 @@ export function useViewSvgFigure(view, { fontSize = 12, rulerHeight = 30, trackL
239
208
  rasterizeLayers,
240
209
  margin,
241
210
  themeName,
242
- }));
211
+ };
212
+ const { data: snapshot, error, isLoading, } = useFetch(
213
+ // Every option is in the key by value, so an inline options object costs
214
+ // nothing and a changed option redraws. The view's id too: two views can
215
+ // share every other term. A null key is `useFetch`'s own "not yet", which
216
+ // is what an unready view reports.
217
+ key
218
+ ? [
219
+ 'lgv-svg-figure',
220
+ svgNodeId(view),
221
+ key,
222
+ JSON.stringify(opts),
223
+ ]
224
+ : null, () => renderFigure(view, opts));
243
225
  // Written in an effect and read during render, which is the order that makes
244
226
  // it work: a key change clears `data` on the render where the ref still holds
245
227
  // the previous commit's size, so the host's box keeps the height it had rather
@@ -5,6 +5,8 @@ import type { ReactNode } from 'react';
5
5
  interface TrackHeights {
6
6
  displays: {
7
7
  height: number;
8
+ prefersOffset?: boolean;
9
+ svgSidebarWidth?: () => number;
8
10
  }[];
9
11
  }
10
12
  export interface SvgDisplayResult {
@@ -12,6 +14,9 @@ export interface SvgDisplayResult {
12
14
  configuration: AnyConfigurationModel;
13
15
  displays: {
14
16
  height: number;
17
+ regionTooLarge?: boolean;
18
+ drawsWhenTooLarge?: boolean;
19
+ svgSidebarWidth?: () => number;
15
20
  }[];
16
21
  };
17
22
  result: ReactNode;
@@ -58,6 +63,7 @@ export declare function getRowHeaderLayout({ fontSize, showScalebar, }: {
58
63
  assemblyLabelBaselineY: number;
59
64
  bandHeight: number;
60
65
  };
66
+ export type HeaderLayout = ReturnType<typeof getHeaderLayout>;
61
67
  export declare function getHeaderLayout({ fontSize, showCytobands, rulerHeight, }: {
62
68
  fontSize: number;
63
69
  showCytobands: boolean;
@@ -71,25 +77,31 @@ export declare function getHeaderLayout({ fontSize, showCytobands, rulerHeight,
71
77
  };
72
78
  export declare const RULER_MAJOR_TICK = 5;
73
79
  export declare const RULER_MINOR_TICK = 3;
74
- export declare const RULER_TICK_FONT_SIZE = 11;
75
80
  export declare function getRulerLayout(rulerHeight: number): {
76
81
  tickTopY: number;
77
82
  numbersBaselineY: number;
78
83
  };
79
84
  export declare const refNameLabelBoxHeight: number;
80
85
  export declare const refNameLabelBaselineY: number;
81
- export declare function labelOffset(trackLabels: TrackLabelMode, textHeight: number): number;
86
+ export declare function trackLabelMode(track: TrackHeights, trackLabels: TrackLabelMode): TrackLabelMode;
87
+ export declare function leftLabelRaised(track: Pick<TrackHeights, 'displays'>, trackLabels: TrackLabelMode): boolean;
88
+ export declare function labelOffset(track: TrackHeights, trackLabels: TrackLabelMode, textHeight: number): number;
89
+ interface SidebarTrack {
90
+ configuration: AnyConfigurationModel;
91
+ displays: {
92
+ svgSidebarWidth?: () => number;
93
+ }[];
94
+ }
82
95
  export declare const TRACK_LABEL_GAP = 40;
83
96
  export declare function trackLabelLeftOffset({ tracks, trackLabels, fontSize, fontFamily, session, }: {
84
- tracks: {
85
- configuration: AnyConfigurationModel;
86
- }[];
97
+ tracks: SidebarTrack[];
87
98
  trackLabels: TrackLabelMode;
88
99
  fontSize: number;
89
100
  fontFamily?: string;
90
101
  session: TrackCatalog;
91
102
  }): number;
92
- export declare function trackBoxHeight(track: TrackHeights, textOffset: number): number;
93
- export declare function trackBoxOffsets(tracks: TrackHeights[], textOffset: number): number[];
94
- export declare function totalHeight(tracks: TrackHeights[], textHeight: number, trackLabels: TrackLabelMode): number;
103
+ export declare function trackSidebarWidth(track: Pick<SidebarTrack, 'displays'>): number;
104
+ export declare function trackBoxHeight(track: TrackHeights, trackLabels: TrackLabelMode, textHeight: number): number;
105
+ export declare function trackBoxOffsets(tracks: TrackHeights[], trackLabels: TrackLabelMode, textHeight: number): number[];
106
+ export declare function totalHeight(tracks: TrackHeights[], trackLabels: TrackLabelMode, textHeight: number): number;
95
107
  export {};
@@ -80,7 +80,7 @@ export function defaultTextHeight(fontSize) {
80
80
  export function offsetLabelBaselineY(textHeight, fontSize) {
81
81
  return textHeight - LABEL_PAD_BOTTOM - labelDescent(fontSize);
82
82
  }
83
- // Baseline for a label inset into the top of the box it draws over ('overlay'
83
+ // Baseline for a label inset into the top of the box it draws over ('overlapping'
84
84
  // mode), measured down from that top edge so the ascenders stay inside it.
85
85
  export function insetLabelBaselineY(fontSize) {
86
86
  return labelBaselineFromTop(LABEL_PAD_TOP, fontSize);
@@ -99,6 +99,9 @@ export function insetLabelBaselineY(fontSize) {
99
99
  // scale in each row the only thing saying whether two rows are at the same zoom
100
100
  // is their ruler coordinates, which are unreadable at the size a figure is
101
101
  // published at. The standalone LGV export has drawn this bar all along.
102
+ //
103
+ // The name's band is reserved whether or not the row draws one: a breakpoint
104
+ // split view names its first row only, and the rest stay the same height as it.
102
105
  export function getRowHeaderLayout({ fontSize, showScalebar, }) {
103
106
  const label = labelInkHeight(fontSize);
104
107
  // the bp label hangs a cap below the bar's line and its own ink box ends a gap
@@ -111,8 +114,10 @@ export function getRowHeaderLayout({ fontSize, showScalebar, }) {
111
114
  // ink box rather than by baseline, because the gaps are fixed while a font's
112
115
  // descent is not: laying the label out from its baseline instead put its
113
116
  // descenders through that cap at the larger export fonts.
117
+ // without a scalebar the name sits on its baseline at the origin, its
118
+ // descenders into the ruler's top, so only the ink above the baseline counts
114
119
  const assemblyInkTop = scalebarLineY === undefined
115
- ? -label
120
+ ? -labelBaselineFromTop(0, fontSize)
116
121
  : scalebarLineY - SVG_SCALEBAR_CAP - ROW_GAP - label;
117
122
  return {
118
123
  // undefined rather than an unused number: a row without a scalebar has no
@@ -123,17 +128,9 @@ export function getRowHeaderLayout({ fontSize, showScalebar, }) {
123
128
  assemblyLabelBaselineY: showScalebar
124
129
  ? labelBaselineFromTop(assemblyInkTop, fontSize)
125
130
  : 0,
126
- // up from the ruler to the top of the topmost ink box — the assembly
127
- // label's, in both cases
128
131
  bandHeight: -assemblyInkTop,
129
132
  };
130
133
  }
131
- // Compact vertical layout for the exported header: rows (assembly name,
132
- // cytoband overview, "you are here" polygon, total-bp scalebar, ruler) are
133
- // stacked with a small fixed gap rather than reserving loose fixed-height
134
- // bands, so nothing is separated by dead space. `tracksTop` is where the track
135
- // bodies begin (renderToSvg's `offset`). Shared by SVGHeader and renderToSvg so
136
- // the header height and the track origin can't drift.
137
134
  export function getHeaderLayout({ fontSize, showCytobands, rulerHeight, }) {
138
135
  // the assembly name is drawn on its ink box, not on `fontSize`: at y=0 with a
139
136
  // hanging baseline its ascenders rose above the export's top edge and got
@@ -161,7 +158,6 @@ export function getHeaderLayout({ fontSize, showCytobands, rulerHeight, }) {
161
158
  // Ruler tick geometry, shared by the tick marks and their number labels.
162
159
  export const RULER_MAJOR_TICK = 5;
163
160
  export const RULER_MINOR_TICK = 3;
164
- export const RULER_TICK_FONT_SIZE = 11;
165
161
  // clearance between the tick-number baseline and the marks below it
166
162
  const RULER_TICK_LABEL_GAP = 2;
167
163
  // Vertical positions within a ruler of the given budget: tick marks hang just
@@ -180,18 +176,39 @@ export function getRulerLayout(rulerHeight) {
180
176
  // 13, and both ends of the glyphs at any smaller export font.
181
177
  export const refNameLabelBoxHeight = labelInkHeight(REF_NAME_LABEL_FONT_SIZE);
182
178
  export const refNameLabelBaselineY = labelBaselineFromTop(0, REF_NAME_LABEL_FONT_SIZE);
183
- // space the label pushes a track down by; only 'offset' mode does
184
- export function labelOffset(trackLabels, textHeight) {
185
- return trackLabels === 'offset' ? textHeight : 0;
179
+ // The mode one track's label is drawn in. 'overlapping' yields to 'offset' for a
180
+ // display that `prefersOffset`, as TrackContainer does on screen: its left edge
181
+ // carries content — an axis, a sample's genotypes — the label would print over.
182
+ export function trackLabelMode(track, trackLabels) {
183
+ return trackLabels === 'overlapping' && track.displays[0]?.prefersOffset
184
+ ? 'offset'
185
+ : trackLabels;
186
+ }
187
+ // Whether a 'left' name sits in a band above its track's rows rather than
188
+ // beside the first of them: the gutter beside the rows holds the track's own
189
+ // sidebar, so the name heads it instead of standing off to its left.
190
+ export function leftLabelRaised(track, trackLabels) {
191
+ return trackLabels === 'left' && trackSidebarWidth(track) > 0;
192
+ }
193
+ // space the label pushes a track's body down by: an 'offset' label, or a raised
194
+ // 'left' one
195
+ export function labelOffset(track, trackLabels, textHeight) {
196
+ return trackLabelMode(track, trackLabels) === 'offset' ||
197
+ leftLabelRaised(track, trackLabels)
198
+ ? textHeight
199
+ : 0;
186
200
  }
187
201
  // Gap between a 'left' track label's right edge and the track body.
188
202
  // SVGTrackLabel right-aligns its text at `trackLabelOffset - TRACK_LABEL_GAP`,
189
203
  // so it and trackLabelLeftOffset must agree or the widest name overflows the
190
204
  // gutter it was measured for.
191
205
  export const TRACK_LABEL_GAP = 40;
192
- // Horizontal gutter reserved for 'left' track labels (0 in every other mode).
193
- // Takes an already-minimized-filtered track list, so the reserved width matches
194
- // the labels that actually get drawn.
206
+ // Horizontal gutter left of the track bodies: the widest of each track's
207
+ // sidebar and, in 'left' mode, its name plus TRACK_LABEL_GAP. A sidebar and a
208
+ // name share the column because the name rises above the sidebar
209
+ // (leftLabelRaised). Takes an
210
+ // already-minimized-filtered track list, so the reserved width matches the
211
+ // labels that actually get drawn.
195
212
  //
196
213
  // `fontFamily` is the export's own font option, and it has to reach the ruler
197
214
  // here: the labels are drawn in it (wrapSvgExport puts it on the root <svg>,
@@ -208,31 +225,35 @@ export const TRACK_LABEL_GAP = 40;
208
225
  // `getScalebarRefNameLabels`, an on-screen model getter, so making them
209
226
  // family-aware means threading an export option through screen state.
210
227
  export function trackLabelLeftOffset({ tracks, trackLabels, fontSize, fontFamily, session, }) {
211
- // no labels means no gutter, and that includes having no tracks to label: the
212
- // gap alone would push the whole figure right of a margin nothing is drawn in
213
- return trackLabels === 'left' && tracks.length > 0
214
- ? max(tracks.map(t => measureText(svgTrackName(t, session), fontSize, fontFamily)), 0) + TRACK_LABEL_GAP
215
- : 0;
228
+ return max(tracks.map(t => Math.max(trackSidebarWidth(t), trackLabels === 'left'
229
+ ? measureText(svgTrackName(t, session), fontSize, fontFamily) +
230
+ TRACK_LABEL_GAP
231
+ : 0)), 0);
232
+ }
233
+ // Px of tree and row labels a track's display draws left of its body.
234
+ export function trackSidebarWidth(track) {
235
+ return track.displays[0]?.svgSidebarWidth?.() ?? 0;
216
236
  }
217
237
  // vertical box a single track occupies. Shared by totalHeight (sum) and
218
238
  // trackBoxOffsets (prefix-sum) so the two can't drift.
219
- export function trackBoxHeight(track, textOffset) {
220
- return track.displays[0].height + textOffset + trackSpacing;
239
+ export function trackBoxHeight(track, trackLabels, textHeight) {
240
+ return (track.displays[0].height +
241
+ labelOffset(track, trackLabels, textHeight) +
242
+ trackSpacing);
221
243
  }
222
244
  // Top y of each track's box within a stack of them. SVGTracks lays the bodies
223
245
  // out with it and the breakpoint-split export anchors its overlay ribbons with
224
246
  // it, so one implementation rather than two prefix-sums that must agree —
225
247
  // they drifted by trackSpacing per track when they didn't.
226
- export function trackBoxOffsets(tracks, textOffset) {
248
+ export function trackBoxOffsets(tracks, trackLabels, textHeight) {
227
249
  const offsets = [];
228
250
  let total = 0;
229
251
  for (const track of tracks) {
230
252
  offsets.push(total);
231
- total += trackBoxHeight(track, textOffset);
253
+ total += trackBoxHeight(track, trackLabels, textHeight);
232
254
  }
233
255
  return offsets;
234
256
  }
235
- export function totalHeight(tracks, textHeight, trackLabels) {
236
- const textOffset = labelOffset(trackLabels, textHeight);
237
- return sum(tracks.map(t => trackBoxHeight(t, textOffset)));
257
+ export function totalHeight(tracks, trackLabels, textHeight) {
258
+ return sum(tracks.map(t => trackBoxHeight(t, trackLabels, textHeight)));
238
259
  }
@@ -1,20 +1,16 @@
1
1
  import type { LinearGenomeViewStateModel } from './model.ts';
2
- import type { HighlightType } from '@jbrowse/core/util/highlights';
2
+ import type { BpOffset as CoreBpOffset } from '@jbrowse/core/util/Base1DUtils';
3
+ import type { HighlightInput, HighlightType } from '@jbrowse/core/util/highlights';
3
4
  import type { TrackInit } from '@jbrowse/core/util/tracks';
4
5
  import type { SnapshotIn } from '@jbrowse/mobx-state-tree';
5
- export interface BpOffset {
6
- refName?: string;
7
- index: number;
8
- offset: number;
9
- start?: number;
10
- end?: number;
6
+ export interface BpOffset extends CoreBpOffset {
11
7
  coord?: number;
12
8
  reversed?: boolean;
13
9
  assemblyName?: string;
14
10
  oob?: boolean;
15
11
  }
16
- export type { ExportSvgOptions, TrackLabelMode, } from '@jbrowse/display-kit/types';
17
- export type { HighlightType };
12
+ export type { ExportSvgOptions, TrackLabelMode, ViewTrackLabelMode, } from '@jbrowse/display-kit/types';
13
+ export type { HighlightInput, HighlightType };
18
14
  export interface NavLocation {
19
15
  refName: string;
20
16
  start?: number;
@@ -34,11 +30,12 @@ export interface InitState {
34
30
  */
35
31
  loc?: string;
36
32
  grow?: number;
33
+ showHitTrack?: boolean;
37
34
  assembly: string;
38
35
  displayedRegionNames?: string[];
39
36
  tracks?: TrackInit[];
40
37
  tracklist?: boolean;
41
38
  nav?: boolean;
42
- highlight?: (string | HighlightType)[];
39
+ highlight?: (string | HighlightInput)[];
43
40
  }
44
- export type LinearGenomeViewLaunchProps = Partial<Omit<SnapshotIn<LinearGenomeViewStateModel>, keyof InitState | 'id' | 'type' | 'init'>>;
41
+ export type LinearGenomeViewLaunchProps = Partial<Omit<SnapshotIn<LinearGenomeViewStateModel>, keyof InitState | 'id' | 'type' | 'launch'>>;