@jbrowse/plugin-linear-genome-view 5.0.0-beta.7 → 5.0.0-beta.9
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.
- package/esm/FeatureTrack/configSchema.d.ts +140 -48
- package/esm/LinearGenomeView/afterAttach.d.ts +5 -5
- package/esm/LinearGenomeView/afterAttach.js +18 -24
- package/esm/LinearGenomeView/closeUps.d.ts +83 -0
- package/esm/LinearGenomeView/closeUps.js +211 -0
- package/esm/LinearGenomeView/components/AddCloseUpDialog.d.ts +19 -0
- package/esm/LinearGenomeView/components/AddCloseUpDialog.js +29 -0
- package/esm/LinearGenomeView/components/CloseUps.d.ts +5 -0
- package/esm/LinearGenomeView/components/CloseUps.js +75 -0
- package/esm/LinearGenomeView/components/ExportSvgDialog.d.ts +2 -1
- package/esm/LinearGenomeView/components/ExportSvgDialog.js +6 -2
- package/esm/LinearGenomeView/components/GetSequenceDialog.js +2 -2
- package/esm/LinearGenomeView/components/Header.js +6 -1
- package/esm/LinearGenomeView/components/HeaderZoomControls.js +4 -34
- package/esm/LinearGenomeView/components/Highlight.js +2 -4
- package/esm/LinearGenomeView/components/HighlightBand.js +2 -2
- package/esm/LinearGenomeView/components/HighlightChip.d.ts +1 -2
- package/esm/LinearGenomeView/components/HighlightChip.js +5 -7
- package/esm/LinearGenomeView/components/ImportForm.js +2 -2
- package/esm/LinearGenomeView/components/LinearGenomeView.js +3 -3
- package/esm/LinearGenomeView/components/LinearGenomeViewContainer.js +3 -2
- package/esm/LinearGenomeView/components/OverviewHighlight.js +1 -1
- package/esm/LinearGenomeView/components/OverviewHighlightBand.js +2 -3
- package/esm/LinearGenomeView/components/OverviewScalebarPolygon.d.ts +7 -1
- package/esm/LinearGenomeView/components/OverviewScalebarPolygon.js +22 -7
- package/esm/LinearGenomeView/components/SVGHighlightBand.js +2 -3
- package/esm/LinearGenomeView/components/ScalebarRefNameLabels.js +6 -21
- package/esm/LinearGenomeView/components/SearchBox.js +2 -2
- package/esm/LinearGenomeView/components/TrackContainer.js +12 -5
- package/esm/LinearGenomeView/components/TrackLabel.js +1 -1
- package/esm/LinearGenomeView/components/TrackRenderingContainer.d.ts +3 -1
- package/esm/LinearGenomeView/components/TrackRenderingContainer.js +3 -7
- package/esm/LinearGenomeView/components/TracksContainer.js +7 -15
- package/esm/LinearGenomeView/components/fetchSequence.d.ts +1 -2
- package/esm/LinearGenomeView/components/useSideScroll.js +1 -1
- package/esm/LinearGenomeView/consts.d.ts +2 -0
- package/esm/LinearGenomeView/consts.js +5 -0
- package/esm/LinearGenomeView/headerFit.d.ts +2 -1
- package/esm/LinearGenomeView/headerFit.js +7 -2
- package/esm/LinearGenomeView/index.d.ts +1 -1
- package/esm/LinearGenomeView/keyboardHandler.d.ts +2 -0
- package/esm/LinearGenomeView/keyboardHandler.js +11 -0
- package/esm/LinearGenomeView/launchKeys.d.ts +1 -1
- package/esm/LinearGenomeView/launchKeys.js +15 -5
- package/esm/LinearGenomeView/lazyDialogs.d.ts +7 -0
- package/esm/LinearGenomeView/lazyDialogs.js +2 -0
- package/esm/LinearGenomeView/menuItems.d.ts +28 -16
- package/esm/LinearGenomeView/menuItems.js +213 -85
- package/esm/LinearGenomeView/model.d.ts +367 -199
- package/esm/LinearGenomeView/model.js +302 -143
- package/esm/LinearGenomeView/sharedScaleContainer.d.ts +1 -1
- package/esm/LinearGenomeView/showRegionsWithUndo.js +7 -1
- package/esm/LinearGenomeView/svgcomponents/SVGCloseUpFrame.d.ts +5 -0
- package/esm/LinearGenomeView/svgcomponents/SVGCloseUpFrame.js +8 -0
- package/esm/LinearGenomeView/svgcomponents/SVGHighlights.js +3 -4
- package/esm/LinearGenomeView/svgcomponents/SVGHighlightsOverlay.js +3 -3
- package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +95 -24
- package/esm/LinearGenomeView/svgcomponents/SVGRuler.js +4 -5
- package/esm/LinearGenomeView/svgcomponents/SVGTrackLabel.js +13 -4
- package/esm/LinearGenomeView/svgcomponents/SVGTracks.js +4 -4
- package/esm/LinearGenomeView/svgcomponents/renderViewTracks.js +1 -1
- package/esm/LinearGenomeView/svgcomponents/useViewSvgFigure.js +52 -63
- package/esm/LinearGenomeView/svgcomponents/util.d.ts +8 -6
- package/esm/LinearGenomeView/svgcomponents/util.js +37 -16
- package/esm/LinearGenomeView/types.d.ts +5 -4
- package/esm/LinearGenomeView/util.d.ts +20 -37
- package/esm/LinearGenomeView/util.js +58 -141
- package/esm/displayKitTests/perRegionTestEnv.d.ts +64 -45
- package/esm/displayKitTests/perRegionTestEnv.js +6 -6
- package/esm/index.d.ts +9 -67
- package/esm/index.js +6 -22
- package/esm/searchUtils.d.ts +2 -3
- package/esm/searchUtils.js +2 -2
- package/package.json +7 -7
- package/esm/lazyPluginExports.d.ts +0 -23
- package/esm/lazyPluginExports.js +0 -36
|
@@ -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 ('
|
|
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,7 +99,14 @@ 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
|
-
|
|
102
|
+
//
|
|
103
|
+
// `reserveAssemblyName` keeps the name's band whether or not the row draws one,
|
|
104
|
+
// which is what a stack of one genome at several loci wants: a breakpoint split
|
|
105
|
+
// view names its first row only, and the rest stay the same height as it. The
|
|
106
|
+
// close-ups export is the case that says false — every row there is the
|
|
107
|
+
// host's own assembly, so no row names it and the band is space no glyph is
|
|
108
|
+
// drawn in.
|
|
109
|
+
export function getRowHeaderLayout({ fontSize, showScalebar, reserveAssemblyName = true, }) {
|
|
103
110
|
const label = labelInkHeight(fontSize);
|
|
104
111
|
// the bp label hangs a cap below the bar's line and its own ink box ends a gap
|
|
105
112
|
// above the ruler at y=0
|
|
@@ -114,6 +121,14 @@ export function getRowHeaderLayout({ fontSize, showScalebar, }) {
|
|
|
114
121
|
const assemblyInkTop = scalebarLineY === undefined
|
|
115
122
|
? -label
|
|
116
123
|
: scalebarLineY - SVG_SCALEBAR_CAP - ROW_GAP - label;
|
|
124
|
+
// up from the ruler to the topmost ink the row draws above it: the assembly
|
|
125
|
+
// label's box where there is one to reserve, else the scalebar's upper cap,
|
|
126
|
+
// else nothing
|
|
127
|
+
const bandHeight = reserveAssemblyName
|
|
128
|
+
? -assemblyInkTop
|
|
129
|
+
: scalebarLineY === undefined
|
|
130
|
+
? 0
|
|
131
|
+
: SVG_SCALEBAR_CAP - scalebarLineY;
|
|
117
132
|
return {
|
|
118
133
|
// undefined rather than an unused number: a row without a scalebar has no
|
|
119
134
|
// line to place, and the caller draws nothing
|
|
@@ -123,9 +138,7 @@ export function getRowHeaderLayout({ fontSize, showScalebar, }) {
|
|
|
123
138
|
assemblyLabelBaselineY: showScalebar
|
|
124
139
|
? labelBaselineFromTop(assemblyInkTop, fontSize)
|
|
125
140
|
: 0,
|
|
126
|
-
|
|
127
|
-
// label's, in both cases
|
|
128
|
-
bandHeight: -assemblyInkTop,
|
|
141
|
+
bandHeight,
|
|
129
142
|
};
|
|
130
143
|
}
|
|
131
144
|
// Compact vertical layout for the exported header: rows (assembly name,
|
|
@@ -161,7 +174,6 @@ export function getHeaderLayout({ fontSize, showCytobands, rulerHeight, }) {
|
|
|
161
174
|
// Ruler tick geometry, shared by the tick marks and their number labels.
|
|
162
175
|
export const RULER_MAJOR_TICK = 5;
|
|
163
176
|
export const RULER_MINOR_TICK = 3;
|
|
164
|
-
export const RULER_TICK_FONT_SIZE = 11;
|
|
165
177
|
// clearance between the tick-number baseline and the marks below it
|
|
166
178
|
const RULER_TICK_LABEL_GAP = 2;
|
|
167
179
|
// Vertical positions within a ruler of the given budget: tick marks hang just
|
|
@@ -180,9 +192,17 @@ export function getRulerLayout(rulerHeight) {
|
|
|
180
192
|
// 13, and both ends of the glyphs at any smaller export font.
|
|
181
193
|
export const refNameLabelBoxHeight = labelInkHeight(REF_NAME_LABEL_FONT_SIZE);
|
|
182
194
|
export const refNameLabelBaselineY = labelBaselineFromTop(0, REF_NAME_LABEL_FONT_SIZE);
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
|
|
195
|
+
// The mode one track's label is drawn in. 'overlapping' yields to 'offset' for a
|
|
196
|
+
// display that `prefersOffset`, as TrackContainer does on screen: its left edge
|
|
197
|
+
// carries content — an axis, a sample's genotypes — the label would print over.
|
|
198
|
+
export function trackLabelMode(track, trackLabels) {
|
|
199
|
+
return trackLabels === 'overlapping' && track.displays[0]?.prefersOffset
|
|
200
|
+
? 'offset'
|
|
201
|
+
: trackLabels;
|
|
202
|
+
}
|
|
203
|
+
// space the label pushes a track's body down by; only 'offset' mode does
|
|
204
|
+
export function labelOffset(track, trackLabels, textHeight) {
|
|
205
|
+
return trackLabelMode(track, trackLabels) === 'offset' ? textHeight : 0;
|
|
186
206
|
}
|
|
187
207
|
// Gap between a 'left' track label's right edge and the track body.
|
|
188
208
|
// SVGTrackLabel right-aligns its text at `trackLabelOffset - TRACK_LABEL_GAP`,
|
|
@@ -216,23 +236,24 @@ export function trackLabelLeftOffset({ tracks, trackLabels, fontSize, fontFamily
|
|
|
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,
|
|
220
|
-
return track.displays[0].height +
|
|
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,
|
|
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,
|
|
253
|
+
total += trackBoxHeight(track, trackLabels, textHeight);
|
|
232
254
|
}
|
|
233
255
|
return offsets;
|
|
234
256
|
}
|
|
235
|
-
export function totalHeight(tracks,
|
|
236
|
-
|
|
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,5 +1,5 @@
|
|
|
1
1
|
import type { LinearGenomeViewStateModel } from './model.ts';
|
|
2
|
-
import type { HighlightType } from '@jbrowse/core/util/highlights';
|
|
2
|
+
import type { HighlightInput, HighlightType } from '@jbrowse/core/util/highlights';
|
|
3
3
|
import type { TrackInit } from '@jbrowse/core/util/tracks';
|
|
4
4
|
import type { SnapshotIn } from '@jbrowse/mobx-state-tree';
|
|
5
5
|
export interface BpOffset {
|
|
@@ -13,8 +13,8 @@ export interface BpOffset {
|
|
|
13
13
|
assemblyName?: string;
|
|
14
14
|
oob?: boolean;
|
|
15
15
|
}
|
|
16
|
-
export type { ExportSvgOptions, TrackLabelMode, } from '@jbrowse/display-kit/types';
|
|
17
|
-
export type { HighlightType };
|
|
16
|
+
export type { ExportSvgOptions, TrackLabelMode, ViewTrackLabelMode, } from '@jbrowse/display-kit/types';
|
|
17
|
+
export type { HighlightInput, HighlightType };
|
|
18
18
|
export interface NavLocation {
|
|
19
19
|
refName: string;
|
|
20
20
|
start?: number;
|
|
@@ -34,11 +34,12 @@ export interface InitState {
|
|
|
34
34
|
*/
|
|
35
35
|
loc?: string;
|
|
36
36
|
grow?: number;
|
|
37
|
+
showHitTrack?: boolean;
|
|
37
38
|
assembly: string;
|
|
38
39
|
displayedRegionNames?: string[];
|
|
39
40
|
tracks?: TrackInit[];
|
|
40
41
|
tracklist?: boolean;
|
|
41
42
|
nav?: boolean;
|
|
42
|
-
highlight?: (string |
|
|
43
|
+
highlight?: (string | HighlightInput)[];
|
|
43
44
|
}
|
|
44
45
|
export type LinearGenomeViewLaunchProps = Partial<Omit<SnapshotIn<LinearGenomeViewStateModel>, keyof InitState | 'id' | 'type' | 'launch'>>;
|
|
@@ -112,48 +112,31 @@ export interface ScalebarRefNameLabel {
|
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
114
|
* Builds the refName labels drawn along the scalebar as plain data (no JSX): one
|
|
115
|
-
* label per run of same-refName regions
|
|
116
|
-
*
|
|
117
|
-
* refName under the viewport's left border.
|
|
115
|
+
* label per run of same-refName regions, the leftmost of them "sticky" — pinned
|
|
116
|
+
* so the run under the left edge stays named once its own left edge scrolls off.
|
|
118
117
|
*
|
|
119
|
-
* `caption` is the row-level chip at the viewport's left edge
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* label
|
|
123
|
-
*
|
|
118
|
+
* `caption` is the row-level chip at the viewport's left edge: which assembly
|
|
119
|
+
* the row is (`prefix`, synteny only) and whether the whole row is flipped. The
|
|
120
|
+
* chip takes `captionSpanPx` of the strip and the labels get the rest, so the
|
|
121
|
+
* sticky label pins to the chip's right edge ("hg38" then "chr1") and rides the
|
|
122
|
+
* run under THAT edge. A run the chip covers entirely goes unnamed, and the
|
|
123
|
+
* chip is not shortened to uncover it: a width fitted to the run under it
|
|
124
|
+
* changes with every run that pans past, and a fixed cap short enough to
|
|
125
|
+
* uncover a 55px chromosome cuts ordinary names. The assembly's `displayName`
|
|
126
|
+
* is where a long name gets shortened.
|
|
124
127
|
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
* sticky label sits far enough right not to (a view scrolled left of its first
|
|
128
|
-
* region, so the row's data starts mid-viewport) or when there is no sticky
|
|
129
|
-
* label at all.
|
|
128
|
+
* `orientation` decides the ` [rev]` marker, following `assembleLocStrings`: say
|
|
129
|
+
* it where it distinguishes, once where it does not.
|
|
130
130
|
*
|
|
131
|
-
* `
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
* distinguishes, once where it does not.
|
|
136
|
-
*
|
|
137
|
-
* - `reversed`: the whole row is flipped, which is a fact about the row, so the
|
|
138
|
-
* caption carries it and NO name does. The caption cannot fold in this state
|
|
139
|
-
* — the sticky label is pushed clear of it instead — because a marker sitting
|
|
140
|
-
* on a chromosome name is how the mixed case spells a fact about that one
|
|
141
|
-
* region. `chr1 [rev] | chr2` would otherwise mean both "the row is flipped,
|
|
142
|
-
* said once" and "chr1 is flipped and chr2 is not", in identical pixels.
|
|
143
|
-
* - `mixed`: only some regions are flipped — reachable through the label menu's
|
|
144
|
-
* per-region "Reverse region" — so every reversed run is marked and the
|
|
145
|
-
* forward ones are left alone. Here the marker on a name is the only thing
|
|
146
|
-
* telling two labels apart.
|
|
131
|
+
* - `reversed`: the caption carries the marker and no name does. A marker on a
|
|
132
|
+
* chromosome name is how the mixed case flags that one region.
|
|
133
|
+
* - `mixed`: every reversed run is marked. A marked label that fails the fit
|
|
134
|
+
* test falls back to the bare name rather than dropping.
|
|
147
135
|
* - `forward`: no marker.
|
|
148
136
|
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
* Under `mixed` the marker never costs a name: a suffixed label that fails the
|
|
153
|
-
* fit test falls back to the bare name rather than being dropped. Widening the
|
|
154
|
-
* text moves the region width a name needs from ~30px to ~56px at 11px bold,
|
|
155
|
-
* and a feature meant to surface information should not be deleting chromosome
|
|
156
|
-
* names in the narrow half of that gap.
|
|
137
|
+
* `maxWidth` is the whole label box, `paddingLeft` included — both consumers
|
|
138
|
+
* clip from the box's left edge. A name is drawn whole or not at all: clipped
|
|
139
|
+
* mid-glyph, "chr16" reads as "chr1".
|
|
157
140
|
*/
|
|
158
141
|
export declare function getScalebarRefNameLabels({ blocks, offsetPx, prefix, orientation, }: {
|
|
159
142
|
blocks: BaseBlock[];
|
|
@@ -177,13 +177,12 @@ function refNameRuns(blocks) {
|
|
|
177
177
|
return runs;
|
|
178
178
|
}
|
|
179
179
|
/**
|
|
180
|
-
* Index of the run carrying the "sticky" refName label pinned to the
|
|
181
|
-
*
|
|
182
|
-
*
|
|
180
|
+
* Index of the run carrying the "sticky" refName label pinned to the strip's
|
|
181
|
+
* left edge: the rightmost run starting left of `leftEdgePx` (block frame), or
|
|
182
|
+
* the first run when none does.
|
|
183
183
|
*/
|
|
184
|
-
function stickyRunIndex(runs,
|
|
185
|
-
|
|
186
|
-
return scrolledOff === -1 ? 0 : scrolledOff;
|
|
184
|
+
function stickyRunIndex(runs, leftEdgePx) {
|
|
185
|
+
return Math.max(0, runs.findLastIndex(run => run.offsetPx < leftEdgePx));
|
|
187
186
|
}
|
|
188
187
|
export function regionsOrientation(regions) {
|
|
189
188
|
const first = !!regions[0]?.reversed;
|
|
@@ -202,161 +201,79 @@ export function regionsOrientation(regions) {
|
|
|
202
201
|
* upstream is whole-or-nothing, and it would go on believing a glyph fits.
|
|
203
202
|
*/
|
|
204
203
|
const REV_MARKER = ' [rev]';
|
|
205
|
-
/** Clearance between the
|
|
206
|
-
const
|
|
207
|
-
/** Left inset of a non-sticky refName label from its region's left edge. */
|
|
208
|
-
const REF_NAME_LABEL_PADDING_PX = 7;
|
|
204
|
+
/** Clearance between the caption chip and the sticky label after it. */
|
|
205
|
+
const CAPTION_GAP = 4;
|
|
209
206
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
* maxWidth is the width of the whole label box, paddingLeft included, so the
|
|
214
|
-
* text has `maxWidth - paddingLeft` to draw in — that is what both consumers
|
|
215
|
-
* clip to (a border-box max-width on the span, an SVG clip rect from the box's
|
|
216
|
-
* own left edge). The caller decides whether the name it wants to draw fits in
|
|
217
|
-
* that (see refNameLabelWidth); a name is drawn whole or not at all.
|
|
207
|
+
* Left inset of a non-sticky refName label from its region's left edge, clear
|
|
208
|
+
* of the 3px region divider drawn there.
|
|
218
209
|
*/
|
|
219
|
-
|
|
220
|
-
// left-edge x a sticky label may not start before, so a caption chip drawn
|
|
221
|
-
// there keeps the strip to itself. Zero wherever the caption folds into the
|
|
222
|
-
// label instead (the assembly prefix does)
|
|
223
|
-
minTransform = 0, }) {
|
|
224
|
-
const transform = sticky
|
|
225
|
-
? Math.max(minTransform, -offsetPx)
|
|
226
|
-
: run.offsetPx - offsetPx - 1;
|
|
227
|
-
// block-frame x where the label actually starts. Derived from `transform` (=
|
|
228
|
-
// transform + offsetPx) so the width-to-run-end clip stays in lockstep with
|
|
229
|
-
// where the label is drawn: a sticky label pins to the run's left edge, not
|
|
230
|
-
// the viewport's, whenever the view is left-overscrolled (offsetPx < 0) —
|
|
231
|
-
// reading offsetPx directly there over-counted the available width by
|
|
232
|
-
// |offsetPx|, letting the name bleed past its run's right edge.
|
|
233
|
-
const labelStartPx = transform + offsetPx;
|
|
234
|
-
// A non-sticky label's transform anchors at the same x as the region
|
|
235
|
-
// divider drawn just to its left (SVGRegionSeparators, a 3px bar spanning
|
|
236
|
-
// local [0,3] from this same run.offsetPx edge), so paddingLeft must clear
|
|
237
|
-
// that bar plus a few px of breathing room, else the text visually touches
|
|
238
|
-
// the divider. Sticky labels sit at the viewport's own left edge, no divider.
|
|
239
|
-
const paddingLeft = sticky ? 0 : REF_NAME_LABEL_PADDING_PX;
|
|
240
|
-
return { transform, maxWidth: run.endPx - labelStartPx - 1, paddingLeft };
|
|
241
|
-
}
|
|
210
|
+
const REF_NAME_LABEL_PADDING_PX = 7;
|
|
242
211
|
/**
|
|
243
212
|
* Builds the refName labels drawn along the scalebar as plain data (no JSX): one
|
|
244
|
-
* label per run of same-refName regions
|
|
245
|
-
*
|
|
246
|
-
* refName under the viewport's left border.
|
|
247
|
-
*
|
|
248
|
-
* `caption` is the row-level chip at the viewport's left edge, and it says two
|
|
249
|
-
* things: which assembly this row is (`prefix`, an assembly name, synteny only)
|
|
250
|
-
* and whether the row is flipped. Without it a row of stacked genomes would
|
|
251
|
-
* label some rows with the assembly name and some without, depending only on
|
|
252
|
-
* how wide their leftmost chromosome happens to be.
|
|
213
|
+
* label per run of same-refName regions, the leftmost of them "sticky" — pinned
|
|
214
|
+
* so the run under the left edge stays named once its own left edge scrolls off.
|
|
253
215
|
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
216
|
+
* `caption` is the row-level chip at the viewport's left edge: which assembly
|
|
217
|
+
* the row is (`prefix`, synteny only) and whether the whole row is flipped. The
|
|
218
|
+
* chip takes `captionSpanPx` of the strip and the labels get the rest, so the
|
|
219
|
+
* sticky label pins to the chip's right edge ("hg38" then "chr1") and rides the
|
|
220
|
+
* run under THAT edge. A run the chip covers entirely goes unnamed, and the
|
|
221
|
+
* chip is not shortened to uncover it: a width fitted to the run under it
|
|
222
|
+
* changes with every run that pans past, and a fixed cap short enough to
|
|
223
|
+
* uncover a 55px chromosome cuts ordinary names. The assembly's `displayName`
|
|
224
|
+
* is where a long name gets shortened.
|
|
259
225
|
*
|
|
260
|
-
* `orientation` decides the ` [rev]` marker,
|
|
261
|
-
*
|
|
262
|
-
* blocks cover the window, and a row is flipped or not whatever part of it you
|
|
263
|
-
* are looking at. The rule is `assembleLocStrings`': say it where it
|
|
264
|
-
* distinguishes, once where it does not.
|
|
226
|
+
* `orientation` decides the ` [rev]` marker, following `assembleLocStrings`: say
|
|
227
|
+
* it where it distinguishes, once where it does not.
|
|
265
228
|
*
|
|
266
|
-
* - `reversed`: the
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
* region. `chr1 [rev] | chr2` would otherwise mean both "the row is flipped,
|
|
271
|
-
* said once" and "chr1 is flipped and chr2 is not", in identical pixels.
|
|
272
|
-
* - `mixed`: only some regions are flipped — reachable through the label menu's
|
|
273
|
-
* per-region "Reverse region" — so every reversed run is marked and the
|
|
274
|
-
* forward ones are left alone. Here the marker on a name is the only thing
|
|
275
|
-
* telling two labels apart.
|
|
229
|
+
* - `reversed`: the caption carries the marker and no name does. A marker on a
|
|
230
|
+
* chromosome name is how the mixed case flags that one region.
|
|
231
|
+
* - `mixed`: every reversed run is marked. A marked label that fails the fit
|
|
232
|
+
* test falls back to the bare name rather than dropping.
|
|
276
233
|
* - `forward`: no marker.
|
|
277
234
|
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
* Under `mixed` the marker never costs a name: a suffixed label that fails the
|
|
282
|
-
* fit test falls back to the bare name rather than being dropped. Widening the
|
|
283
|
-
* text moves the region width a name needs from ~30px to ~56px at 11px bold,
|
|
284
|
-
* and a feature meant to surface information should not be deleting chromosome
|
|
285
|
-
* names in the narrow half of that gap.
|
|
235
|
+
* `maxWidth` is the whole label box, `paddingLeft` included — both consumers
|
|
236
|
+
* clip from the box's left edge. A name is drawn whole or not at all: clipped
|
|
237
|
+
* mid-glyph, "chr16" reads as "chr1".
|
|
286
238
|
*/
|
|
287
239
|
export function getScalebarRefNameLabels({ blocks, offsetPx, prefix, orientation = 'forward', }) {
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
: prefix;
|
|
293
|
-
// the caption chip occupies the viewport's left edge, so a sticky label
|
|
294
|
-
// starting inside that span has to yield: absorb the assembly name, or, where
|
|
295
|
-
// the caption carries the row's orientation and so cannot fold, start after
|
|
296
|
-
// it. The gap is part of the span: a sticky label carries no padding of its
|
|
297
|
-
// own, so one starting at exactly the chip's right edge abuts it and the two
|
|
298
|
-
// read as a single word ("volvoxctgA") for the few px of scroll around the
|
|
299
|
-
// threshold
|
|
300
|
-
const captionSpanPx = captionText
|
|
301
|
-
? refNameLabelWidth(captionText) + PREFIX_GAP
|
|
302
|
-
: 0;
|
|
240
|
+
const caption = [prefix, orientation === 'reversed' ? REV_MARKER.trim() : undefined]
|
|
241
|
+
.filter(Boolean)
|
|
242
|
+
.join(' ') || undefined;
|
|
243
|
+
const captionSpanPx = caption ? refNameLabelWidth(caption) + CAPTION_GAP : 0;
|
|
303
244
|
const runs = refNameRuns(blocks);
|
|
304
|
-
const stickyIdx = stickyRunIndex(runs, offsetPx);
|
|
245
|
+
const stickyIdx = stickyRunIndex(runs, offsetPx + captionSpanPx);
|
|
305
246
|
const labels = [];
|
|
306
|
-
let stickyHasPrefix = false;
|
|
307
247
|
for (const [i, run] of runs.entries()) {
|
|
308
248
|
const sticky = i === stickyIdx;
|
|
309
|
-
//
|
|
310
|
-
|
|
311
|
-
// every earlier run also *ends* left of the viewport. Its label would just
|
|
312
|
-
// repeat the name the sticky label already shows, drawn off-canvas —
|
|
313
|
-
// invisible on screen (overflow:clip) but bleeding into the margin of an
|
|
314
|
-
// SVG export, which has no such clip.
|
|
315
|
-
const runStart = run.offsetPx >= offsetPx && run.isLeftEndOfDisplayedRegion;
|
|
316
|
-
if (!(sticky || runStart)) {
|
|
249
|
+
// runs left of the sticky one end under the caption or off screen
|
|
250
|
+
if (i < stickyIdx || !(sticky || run.isLeftEndOfDisplayedRegion)) {
|
|
317
251
|
continue;
|
|
318
252
|
}
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const withPrefix = !rowIsReversed && sticky && hasPrefix && layout.transform < captionSpanPx;
|
|
326
|
-
const name = withPrefix ? `${prefix}:${run.refName}` : run.refName;
|
|
327
|
-
// draw the name whole or not at all: a name clipped mid-glyph reads as a
|
|
328
|
-
// different chromosome ("LG2" cut to "LG"), and measuring it means a short
|
|
329
|
-
// name gets its label in a region a fixed minimum width would have skipped.
|
|
330
|
-
// The marker is the one part that degrades instead of dropping the label
|
|
253
|
+
const runStartPx = run.offsetPx - offsetPx;
|
|
254
|
+
const transform = sticky
|
|
255
|
+
? Math.max(captionSpanPx, runStartPx)
|
|
256
|
+
: runStartPx - 1;
|
|
257
|
+
const paddingLeft = sticky ? 0 : REF_NAME_LABEL_PADDING_PX;
|
|
258
|
+
const maxWidth = run.endPx - offsetPx - transform - 1;
|
|
331
259
|
const text = (orientation === 'mixed' && run.reversed
|
|
332
|
-
? [`${
|
|
333
|
-
: [
|
|
334
|
-
if (text
|
|
335
|
-
|
|
260
|
+
? [`${run.refName}${REV_MARKER}`, run.refName]
|
|
261
|
+
: [run.refName]).find(candidate => maxWidth >= paddingLeft + refNameLabelWidth(candidate));
|
|
262
|
+
if (text !== undefined) {
|
|
263
|
+
labels.push({
|
|
264
|
+
key: run.key,
|
|
265
|
+
refName: run.refName,
|
|
266
|
+
displayedRegionIndex: run.firstRegionIndex,
|
|
267
|
+
lastDisplayedRegionIndex: run.lastRegionIndex,
|
|
268
|
+
sticky,
|
|
269
|
+
transform,
|
|
270
|
+
maxWidth,
|
|
271
|
+
paddingLeft,
|
|
272
|
+
text,
|
|
273
|
+
});
|
|
336
274
|
}
|
|
337
|
-
stickyHasPrefix ||= withPrefix;
|
|
338
|
-
labels.push({
|
|
339
|
-
key: run.key,
|
|
340
|
-
refName: run.refName,
|
|
341
|
-
displayedRegionIndex: run.firstRegionIndex,
|
|
342
|
-
lastDisplayedRegionIndex: run.lastRegionIndex,
|
|
343
|
-
sticky,
|
|
344
|
-
transform: layout.transform,
|
|
345
|
-
maxWidth: layout.maxWidth,
|
|
346
|
-
paddingLeft: layout.paddingLeft,
|
|
347
|
-
text,
|
|
348
|
-
});
|
|
349
275
|
}
|
|
350
|
-
return {
|
|
351
|
-
labels,
|
|
352
|
-
// the chip to draw at the left edge, or undefined for none. A caption
|
|
353
|
-
// carrying the orientation always draws; one carrying only the assembly
|
|
354
|
-
// name draws when no sticky label folded it in
|
|
355
|
-
caption: rowIsReversed || (hasPrefix && !stickyHasPrefix)
|
|
356
|
-
? captionText
|
|
357
|
-
: undefined,
|
|
358
|
-
captionSpanPx,
|
|
359
|
-
};
|
|
276
|
+
return { labels, caption, captionSpanPx };
|
|
360
277
|
}
|
|
361
278
|
/**
|
|
362
279
|
* Screen-x span a refName label paints over: its padding and its glyphs, which
|