@jbrowse/plugin-linear-genome-view 5.0.0-beta.13 → 5.0.0-beta.16
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 +2 -1
- package/esm/FeatureTrack/configSchema.js +2 -1
- package/esm/LinearGenomeView/afterAttach.js +1 -23
- package/esm/LinearGenomeView/components/ExportSvgDialog.d.ts +6 -2
- package/esm/LinearGenomeView/components/ExportSvgDialog.js +31 -7
- package/esm/LinearGenomeView/components/HeaderPanControls.d.ts +3 -3
- package/esm/LinearGenomeView/components/HeaderPanControls.js +4 -2
- package/esm/LinearGenomeView/components/HighlightBand.d.ts +3 -2
- package/esm/LinearGenomeView/components/HighlightBand.js +4 -2
- package/esm/LinearGenomeView/components/HighlightChip.d.ts +3 -2
- package/esm/LinearGenomeView/components/HighlightChip.js +4 -2
- package/esm/LinearGenomeView/components/LinearGenomeView.js +5 -12
- package/esm/LinearGenomeView/components/LinearGenomeViewContainer.js +4 -5
- package/esm/LinearGenomeView/components/OverviewHighlightBand.d.ts +3 -2
- package/esm/LinearGenomeView/components/OverviewHighlightBand.js +4 -2
- package/esm/LinearGenomeView/components/RegionWidthEditorDialog.d.ts +3 -2
- package/esm/LinearGenomeView/components/RegionWidthEditorDialog.js +4 -4
- package/esm/LinearGenomeView/components/ScalebarRefNameLabels.js +4 -4
- package/esm/LinearGenomeView/components/SearchPanelForm.d.ts +3 -2
- package/esm/LinearGenomeView/components/SearchPanelForm.js +4 -2
- package/esm/LinearGenomeView/components/SearchResultsDialog.d.ts +4 -9
- package/esm/LinearGenomeView/components/SearchResultsDialog.js +7 -4
- package/esm/LinearGenomeView/components/SearchResultsTable.d.ts +3 -8
- package/esm/LinearGenomeView/components/SearchResultsTable.js +4 -32
- package/esm/LinearGenomeView/components/StrandCheckboxes.d.ts +3 -2
- package/esm/LinearGenomeView/components/StrandCheckboxes.js +4 -2
- package/esm/LinearGenomeView/components/TrackContainer.js +1 -1
- package/esm/LinearGenomeView/components/TrackLabelMenu.js +8 -12
- package/esm/LinearGenomeView/components/useRangeSelect.js +3 -2
- package/esm/LinearGenomeView/components/useSearchBoxPrefs.d.ts +3 -1
- package/esm/LinearGenomeView/components/useSearchBoxPrefs.js +4 -2
- package/esm/LinearGenomeView/components/useSideScroll.js +11 -12
- package/esm/LinearGenomeView/lazyDialogs.d.ts +17 -2
- package/esm/LinearGenomeView/menuItems.js +32 -24
- package/esm/LinearGenomeView/model.d.ts +197 -151
- package/esm/LinearGenomeView/model.js +203 -201
- package/esm/LinearGenomeView/rExport/exportLinearView.d.ts +13 -0
- package/esm/LinearGenomeView/rExport/exportLinearView.js +98 -0
- package/esm/LinearGenomeView/rExport/rScript.d.ts +75 -0
- package/esm/LinearGenomeView/rExport/rScript.js +465 -0
- package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +3 -6
- package/esm/LinearGenomeView/svgcomponents/SVGStackedRow.d.ts +1 -2
- package/esm/LinearGenomeView/svgcomponents/SVGStackedRow.js +2 -2
- package/esm/LinearGenomeView/svgcomponents/SVGTracks.d.ts +1 -2
- package/esm/LinearGenomeView/svgcomponents/SVGTracks.js +4 -4
- package/esm/LinearGenomeView/svgcomponents/SVGView.d.ts +1 -2
- package/esm/LinearGenomeView/svgcomponents/SVGView.js +3 -3
- package/esm/LinearGenomeView/svgcomponents/renderViewTracks.d.ts +22 -20
- package/esm/LinearGenomeView/svgcomponents/renderViewTracks.js +42 -29
- package/esm/LinearGenomeView/svgcomponents/useViewSvgFigure.js +6 -11
- package/esm/LinearGenomeView/svgcomponents/util.d.ts +2 -1
- package/esm/LinearGenomeView/util.d.ts +42 -0
- package/esm/LinearGenomeView/util.js +81 -0
- package/esm/MultiLevelRubberband/MultiLevelRubberband.js +2 -2
- package/esm/displayKitTests/perRegionTestEnv.d.ts +45 -12
- package/esm/index.d.ts +4 -2
- package/esm/index.js +3 -1
- package/esm/searchUtils.js +1 -0
- package/esm/shared/RubberbandSpan.d.ts +3 -2
- package/esm/shared/RubberbandSpan.js +4 -2
- package/esm/shared/coordLabels.d.ts +6 -6
- package/esm/shared/coordLabels.js +7 -6
- package/package.json +9 -9
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { canonicalLocString } from '@jbrowse/core/TextSearch/places';
|
|
2
2
|
import { getConf } from '@jbrowse/core/configuration';
|
|
3
3
|
import { BaseViewModel } from '@jbrowse/core/pluggableElementTypes/models';
|
|
4
4
|
import { exportViewSvg } from '@jbrowse/core/svg/exportViewSvg';
|
|
5
|
-
import { assembleLocString, clamp, getBpDisplayStr,
|
|
5
|
+
import { assembleLocString, clamp, getBpDisplayStr, getNotificationSink, getSession, isSessionModelWithWidgets, localStorageGetBoolean, localStorageGetItem, localStorageSetBoolean, localStorageSetItem, springAnimate, sum, } from '@jbrowse/core/util';
|
|
6
6
|
import { bpToLinearBp, bpToPx, computeMoveToLayout, createOverviewLayout, getLayoutHighlightCoords, getOverviewRegionPxSpan, moveTo, pxToBp, } from '@jbrowse/core/util/Base1DUtils';
|
|
7
7
|
import { scaleBandHeights } from '@jbrowse/core/util/bandHeight';
|
|
8
8
|
import { wholeBaseRegions } from '@jbrowse/core/util/blockTypes';
|
|
9
9
|
import calculateDynamicBlocks from '@jbrowse/core/util/calculateDynamicBlocks';
|
|
10
10
|
import calculateStaticBlocks from '@jbrowse/core/util/calculateStaticBlocks';
|
|
11
|
-
import { tickLabelsWorthDrawing } from '@jbrowse/core/util/tickLabels';
|
|
12
11
|
import { hideTrackGeneric, launchToggleTrackGeneric, launchTrackGeneric, showTrackGeneric, toggleTrackGeneric, } from '@jbrowse/core/util/tracks';
|
|
13
12
|
import { ElementId } from '@jbrowse/core/util/types/mst';
|
|
14
13
|
import { highlightsOnAssemblies } from '@jbrowse/core/util/viewHighlights';
|
|
@@ -18,7 +17,7 @@ import { MIN_DISPLAY_HEIGHT } from '@jbrowse/display-kit/const';
|
|
|
18
17
|
import { contentRightEdgePx } from '@jbrowse/display-kit/regionHost';
|
|
19
18
|
import { cast, getParent, hasParent, isAlive, types, } from '@jbrowse/mobx-state-tree';
|
|
20
19
|
import { observable, when } from 'mobx';
|
|
21
|
-
import { handleSelectedRegion, navToOption } from '../searchUtils.js';
|
|
20
|
+
import { handleSelectedRegion, isOpenInView, navToOption, } from '../searchUtils.js';
|
|
22
21
|
import { doAfterAttach } from './afterAttach.js';
|
|
23
22
|
import { getCytobands, shouldSwapTracks } from './components/util.js';
|
|
24
23
|
import { HEADER_BAR_HEIGHT, HEADER_OVERVIEW_HEIGHT, MIN_BP_PER_PX, MINIMIZED_TRACK_HEIGHT, MINIMUM_BLOCK_WIDTH, RESIZE_ALL_HANDLE_HEIGHT, RESIZE_HANDLE_HEIGHT, SCALE_BAR_HEIGHT, SHOW_ALL_REGIONS_FILL, TRACK_OUTLINE_BORDER, TRACK_TOP_GAP, } from './consts.js';
|
|
@@ -27,13 +26,7 @@ import { setupKeyboardHandler } from './keyboardHandler.js';
|
|
|
27
26
|
import { lgvLaunchKeys } from './launchKeys.js';
|
|
28
27
|
import { buildHighlightsSubMenuItems, buildMenuItems, buildRubberBandMenuItems, buildRubberbandClickMenuItems, } from './menuItems.js';
|
|
29
28
|
import { sharedScaleContainerOf } from './sharedScaleContainer.js';
|
|
30
|
-
import { calculateVisibleLocStrings, cytobandLabelGutterWidth, expandRegion, fitAllRegionsWindow, generateLocations,
|
|
31
|
-
// lazies
|
|
32
|
-
const SearchResultsDialog = lazy(() => import('./components/SearchResultsDialog.js'));
|
|
33
|
-
// The one array `paddingSpans` returns when a view has nothing to mask, so that
|
|
34
|
-
// computed's value repeats by identity. Frozen because every consumer maps over
|
|
35
|
-
// it and a caller that sorted in place would do so for the whole session.
|
|
36
|
-
const NO_PADDING_SPANS = Object.freeze([]);
|
|
29
|
+
import { calculateVisibleLocStrings, cytobandLabelGutterWidth, expandRegion, fitAllRegionsWindow, generateLocations, getGridlineTicks, getPaddingSpans, getScalebarLabels, getScalebarRefNameLabels, regionsOrientation, setDisplayedRegionsKeepingCenter, } from './util.js';
|
|
37
30
|
/**
|
|
38
31
|
* Calculate the offsetPx needed to center content within a viewport.
|
|
39
32
|
* Returns a negative offset when content is smaller than viewport (padding on left).
|
|
@@ -60,13 +53,18 @@ function canonicalHighlight(self, region) {
|
|
|
60
53
|
// bpPerPx deltas smaller than this are treated as no zoom change, avoiding
|
|
61
54
|
// pointless offset re-anchoring on micro-steps
|
|
62
55
|
const BP_PER_PX_EPSILON = 0.000001;
|
|
63
|
-
// px of
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
|
|
56
|
+
// px of blank space a view may scroll past either end of its regions, enough to
|
|
57
|
+
// read as an edge and too little to lose the content
|
|
58
|
+
const OVERSCROLL_PX = 100;
|
|
59
|
+
// The offsets a view can scroll to: the content edge to edge plus a little
|
|
60
|
+
// overscroll. Content narrower than the view collapses the range to the
|
|
61
|
+
// centered offset, which is what `showAllRegions` and `fitAllRegions` ask for.
|
|
62
|
+
function offsetBounds(contentPx, viewportPx) {
|
|
63
|
+
const centered = getCenteredOffsetPx(contentPx, viewportPx);
|
|
64
|
+
return contentPx > viewportPx
|
|
65
|
+
? { min: -OVERSCROLL_PX, max: contentPx - viewportPx + OVERSCROLL_PX }
|
|
66
|
+
: { min: centered, max: centered };
|
|
67
|
+
}
|
|
70
68
|
// whether two BlockSets cover the same blocks, by key and in order
|
|
71
69
|
function sameBlockKeys(a, b) {
|
|
72
70
|
return (a.blocks.length === b.blocks.length &&
|
|
@@ -143,6 +141,8 @@ function isTrackSelectorVisible(self) {
|
|
|
143
141
|
/**
|
|
144
142
|
* #stateModel LinearGenomeView
|
|
145
143
|
* #category view
|
|
144
|
+
* Stacks tracks under a horizontal axis of one or more genomic regions laid
|
|
145
|
+
* end to end, which the user scrolls, zooms and searches.
|
|
146
146
|
*
|
|
147
147
|
* #example
|
|
148
148
|
* A `LinearGenomeView` is what you hand-author under `defaultSession.views`, and
|
|
@@ -192,17 +192,16 @@ export function stateModelFactory(pluginManager) {
|
|
|
192
192
|
/**
|
|
193
193
|
* #property
|
|
194
194
|
* Left edge of the viewport, in linearized bp — the concatenated
|
|
195
|
-
* `displayedRegions` space that `offsetPx` indexes, which
|
|
195
|
+
* `displayedRegions` space that `offsetPx` indexes, which has no
|
|
196
196
|
* inter-region padding, so the two differ only by `bpPerPx`. May be
|
|
197
|
-
* negative
|
|
197
|
+
* negative when the view is scrolled past the left end.
|
|
198
198
|
*
|
|
199
|
-
* The
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
* at 500px
|
|
204
|
-
*
|
|
205
|
-
* view disagreed, and only the one that stores intent was right.
|
|
199
|
+
* The view stores the genomic window it frames rather than the pixels
|
|
200
|
+
* that framed it, because pixels mean nothing without the width they
|
|
201
|
+
* were measured at and a snapshot does not include one. A session
|
|
202
|
+
* authored in a 1000px window therefore shows the same region when
|
|
203
|
+
* opened at 500px, as the same location given as a `&loc=` does: both
|
|
204
|
+
* ways to share a view store intent.
|
|
206
205
|
*/
|
|
207
206
|
windowStartBp: types.stripDefault(types.number, 0),
|
|
208
207
|
/**
|
|
@@ -215,14 +214,15 @@ export function stateModelFactory(pluginManager) {
|
|
|
215
214
|
windowWidthBp: types.stripDefault(types.number, 0),
|
|
216
215
|
/**
|
|
217
216
|
* #property
|
|
218
|
-
*
|
|
219
|
-
* was stored
|
|
217
|
+
* Used for migration only, and permanent: share links written before the
|
|
218
|
+
* window was stored contain `bpPerPx`/`offsetPx`, and there are many of
|
|
219
|
+
* them.
|
|
220
220
|
*
|
|
221
|
-
* A snapshot written before the window was stored
|
|
221
|
+
* A snapshot written before the window was stored has `offsetPx` and
|
|
222
222
|
* `bpPerPx` but not the width they were measured at, so the window they
|
|
223
223
|
* framed cannot be recovered. `windowStartBp` can (it is
|
|
224
224
|
* `offsetPx * bpPerPx`, no width needed); the width in bp cannot. This
|
|
225
|
-
*
|
|
225
|
+
* property holds the old `bpPerPx` until the first measure, which adopts it at
|
|
226
226
|
* whatever width arrives — exactly what the old code did — and clears
|
|
227
227
|
* this. So an old link keeps its old behavior rather than being
|
|
228
228
|
* reinterpreted, and everything authored since restores its window.
|
|
@@ -231,9 +231,8 @@ export function stateModelFactory(pluginManager) {
|
|
|
231
231
|
/**
|
|
232
232
|
* #property
|
|
233
233
|
* currently displayed regions, can be a single chromosome, arbitrary
|
|
234
|
-
* subsections, or the entire
|
|
235
|
-
*
|
|
236
|
-
* fragmented
|
|
234
|
+
* subsections, or the entire set of chromosomes in the genome. The entire
|
|
235
|
+
* set is not advised if your assembly is very fragmented
|
|
237
236
|
*/
|
|
238
237
|
displayedRegions: types.stripDefault(types.frozen(), []),
|
|
239
238
|
/**
|
|
@@ -267,7 +266,10 @@ export function stateModelFactory(pluginManager) {
|
|
|
267
266
|
trackSelectorType: types.stripDefault(types.enumeration(['hierarchical']), 'hierarchical'),
|
|
268
267
|
/**
|
|
269
268
|
* #property
|
|
270
|
-
* show the "center line"
|
|
269
|
+
* show the "center line". A personal preference: never in a session
|
|
270
|
+
* snapshot. It, showCytobands, trackLabels, colorByCDS, showAminoAcids
|
|
271
|
+
* and showTrackOutlines start from this browser's last choice, which
|
|
272
|
+
* their setters record.
|
|
271
273
|
*/
|
|
272
274
|
showCenterLine: types.optional(types.boolean, () => localStorageGetBoolean('lgv-showCenterLine', false)),
|
|
273
275
|
/**
|
|
@@ -280,11 +282,11 @@ export function stateModelFactory(pluginManager) {
|
|
|
280
282
|
/**
|
|
281
283
|
* #property
|
|
282
284
|
* how to display the track labels, can be "overlapping", "offset", or
|
|
283
|
-
* "hidden", or empty string "" (which
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
* https://jbrowse.org/jb2/docs/config/lineargenomeviewconfigschema/
|
|
287
|
-
* how
|
|
285
|
+
* "hidden", or empty string "" (which uses the LinearGenomeViewPlugin
|
|
286
|
+
* config default). The resolved value is the `effectiveTrackLabels`
|
|
287
|
+
* getter. See the LinearGenomeViewPlugin docs at
|
|
288
|
+
* https://jbrowse.org/jb2/docs/config/lineargenomeviewconfigschema/ for
|
|
289
|
+
* how the config is used
|
|
288
290
|
*/
|
|
289
291
|
trackLabels: types.optional(types.string, () => localStorageGetItem('lgv-trackLabels') ?? ''),
|
|
290
292
|
/**
|
|
@@ -355,6 +357,12 @@ export function stateModelFactory(pluginManager) {
|
|
|
355
357
|
* #volatile
|
|
356
358
|
*/
|
|
357
359
|
draggingTrackId: undefined,
|
|
360
|
+
/**
|
|
361
|
+
* #volatile
|
|
362
|
+
* Set by a search whose hits name more than one place, until the user
|
|
363
|
+
* picks one or closes it.
|
|
364
|
+
*/
|
|
365
|
+
searchPicker: undefined,
|
|
358
366
|
/**
|
|
359
367
|
* #volatile
|
|
360
368
|
*/
|
|
@@ -650,8 +658,8 @@ export function stateModelFactory(pluginManager) {
|
|
|
650
658
|
},
|
|
651
659
|
/**
|
|
652
660
|
* #getter
|
|
653
|
-
*
|
|
654
|
-
* assembly
|
|
661
|
+
* The loading screen's text while `showLoading`, taken from the
|
|
662
|
+
* assembly being waited on; undefined otherwise.
|
|
655
663
|
*/
|
|
656
664
|
get loading() {
|
|
657
665
|
return viewLoading(this.showLoading, () => this.loadingAssembly);
|
|
@@ -705,12 +713,12 @@ export function stateModelFactory(pluginManager) {
|
|
|
705
713
|
* `width`/`staticBlocks` throw by design before the view has been
|
|
706
714
|
* measured and navigated.
|
|
707
715
|
*
|
|
708
|
-
*
|
|
716
|
+
* Use this getter rather than `initialized`.
|
|
709
717
|
* That one answers "have the assembly's regions loaded", which is only
|
|
710
718
|
* the first of two async steps: navigating then populates
|
|
711
719
|
* `displayedRegions`, and in the window between the two `initialized` is
|
|
712
720
|
* already true while there is still nothing on screen. `showLoading`
|
|
713
|
-
* folds in `awaitingInitNavigation`, the getter
|
|
721
|
+
* folds in `awaitingInitNavigation`, the getter for exactly
|
|
714
722
|
* that gap.
|
|
715
723
|
*
|
|
716
724
|
* `error` is the third outcome and is why this is not a bare
|
|
@@ -787,7 +795,7 @@ export function stateModelFactory(pluginManager) {
|
|
|
787
795
|
},
|
|
788
796
|
/**
|
|
789
797
|
* #method
|
|
790
|
-
* rendered height of a single track,
|
|
798
|
+
* rendered height of a single track, using a fixed height when
|
|
791
799
|
* minimized. Shared by trackHeights and getTrackYOffset so the two can't
|
|
792
800
|
* disagree. Reads `activeDisplay` — the display TrackContainer actually
|
|
793
801
|
* mounts — rather than re-picking `displays[0]`, so the view's height math
|
|
@@ -917,8 +925,8 @@ export function stateModelFactory(pluginManager) {
|
|
|
917
925
|
* or 0 for a standalone view, a mode that is off, and a stack that cannot
|
|
918
926
|
* answer yet.
|
|
919
927
|
*
|
|
920
|
-
*
|
|
921
|
-
* function of every row's fit, all of which
|
|
928
|
+
* A getter rather than a volatile the container writes: the number is a
|
|
929
|
+
* function of every row's fit, all of which change on a resize, and a
|
|
922
930
|
* stored copy would be stale by the next layout.
|
|
923
931
|
*/
|
|
924
932
|
get sharedFitBpPerPx() {
|
|
@@ -967,15 +975,13 @@ export function stateModelFactory(pluginManager) {
|
|
|
967
975
|
* #getter
|
|
968
976
|
*/
|
|
969
977
|
get maxOffset() {
|
|
970
|
-
|
|
971
|
-
return this.displayedRegionsTotalPx - MAX_OFFSET_PADDING_PX;
|
|
978
|
+
return offsetBounds(this.displayedRegionsTotalPx, self.width).max;
|
|
972
979
|
},
|
|
973
980
|
/**
|
|
974
981
|
* #getter
|
|
975
982
|
*/
|
|
976
983
|
get minOffset() {
|
|
977
|
-
|
|
978
|
-
return -self.width + MIN_OFFSET_PADDING_PX;
|
|
984
|
+
return offsetBounds(this.displayedRegionsTotalPx, self.width).min;
|
|
979
985
|
},
|
|
980
986
|
/**
|
|
981
987
|
* #getter
|
|
@@ -1020,6 +1026,15 @@ export function stateModelFactory(pluginManager) {
|
|
|
1020
1026
|
getActiveDisplayId(trackId) {
|
|
1021
1027
|
return this.getTrack(trackId)?.activeDisplay.configuration.displayId;
|
|
1022
1028
|
},
|
|
1029
|
+
}))
|
|
1030
|
+
.actions(self => ({
|
|
1031
|
+
/**
|
|
1032
|
+
* #action
|
|
1033
|
+
* Dismiss the search picker without choosing a place.
|
|
1034
|
+
*/
|
|
1035
|
+
closeSearchPicker() {
|
|
1036
|
+
self.searchPicker = undefined;
|
|
1037
|
+
},
|
|
1023
1038
|
}))
|
|
1024
1039
|
.actions(self => ({
|
|
1025
1040
|
/**
|
|
@@ -1027,6 +1042,7 @@ export function stateModelFactory(pluginManager) {
|
|
|
1027
1042
|
*/
|
|
1028
1043
|
setShowTrackOutlines(arg) {
|
|
1029
1044
|
self.showTrackOutlines = arg;
|
|
1045
|
+
localStorageSetBoolean('lgv-showTrackOutlines', arg);
|
|
1030
1046
|
},
|
|
1031
1047
|
/**
|
|
1032
1048
|
* #action
|
|
@@ -1039,18 +1055,21 @@ export function stateModelFactory(pluginManager) {
|
|
|
1039
1055
|
*/
|
|
1040
1056
|
setColorByCDS(flag) {
|
|
1041
1057
|
self.colorByCDS = flag;
|
|
1058
|
+
localStorageSetBoolean('lgv-colorByCDS', flag);
|
|
1042
1059
|
},
|
|
1043
1060
|
/**
|
|
1044
1061
|
* #action
|
|
1045
1062
|
*/
|
|
1046
1063
|
setShowAminoAcids(flag) {
|
|
1047
1064
|
self.showAminoAcids = flag;
|
|
1065
|
+
localStorageSetBoolean('lgv-showAminoAcids', flag);
|
|
1048
1066
|
},
|
|
1049
1067
|
/**
|
|
1050
1068
|
* #action
|
|
1051
1069
|
*/
|
|
1052
1070
|
setShowCytobands(flag) {
|
|
1053
1071
|
self.showCytobands = flag;
|
|
1072
|
+
localStorageSetBoolean('lgv-showCytobands', flag);
|
|
1054
1073
|
},
|
|
1055
1074
|
/**
|
|
1056
1075
|
* #action
|
|
@@ -1236,17 +1255,46 @@ export function stateModelFactory(pluginManager) {
|
|
|
1236
1255
|
model: self,
|
|
1237
1256
|
assemblyName,
|
|
1238
1257
|
})) {
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1258
|
+
const session = getSession(self);
|
|
1259
|
+
const assembly = session.assemblyManager.get(assemblyName);
|
|
1260
|
+
const ordered = [...searchResults].sort((a, b) => Number(isOpenInView(b, self)) -
|
|
1261
|
+
Number(isOpenInView(a, self)));
|
|
1262
|
+
const picker = {
|
|
1263
|
+
query: searchQuery,
|
|
1264
|
+
assemblyName,
|
|
1265
|
+
rows: ordered.map(result => {
|
|
1266
|
+
const location = result.getLocation() ?? '';
|
|
1267
|
+
const trackId = result.getTrackId();
|
|
1268
|
+
return {
|
|
1269
|
+
id: result.getId(),
|
|
1270
|
+
label: result.getLabel(),
|
|
1271
|
+
location: assembly && location
|
|
1272
|
+
? canonicalLocString(location, assembly)
|
|
1273
|
+
: location,
|
|
1274
|
+
trackName: trackId
|
|
1275
|
+
? (session.getTrackById(trackId)?.name ?? '')
|
|
1276
|
+
: '',
|
|
1277
|
+
};
|
|
1278
|
+
}),
|
|
1279
|
+
pick: async (id) => {
|
|
1280
|
+
const picked = ordered.find(result => result.getId() === id);
|
|
1281
|
+
try {
|
|
1282
|
+
if (picked) {
|
|
1283
|
+
await onPick(picked);
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
catch (e) {
|
|
1287
|
+
console.error(e);
|
|
1288
|
+
if (isAlive(self)) {
|
|
1289
|
+
getNotificationSink(self).notifyError(`${e}`, e);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
if (isAlive(self) && self.searchPicker === picker) {
|
|
1293
|
+
self.closeSearchPicker();
|
|
1294
|
+
}
|
|
1248
1295
|
},
|
|
1249
|
-
|
|
1296
|
+
};
|
|
1297
|
+
self.searchPicker = picker;
|
|
1250
1298
|
},
|
|
1251
1299
|
/**
|
|
1252
1300
|
* #action
|
|
@@ -1372,12 +1420,14 @@ export function stateModelFactory(pluginManager) {
|
|
|
1372
1420
|
*/
|
|
1373
1421
|
setTrackLabels(setting) {
|
|
1374
1422
|
self.trackLabels = setting;
|
|
1423
|
+
localStorageSetItem('lgv-trackLabels', setting);
|
|
1375
1424
|
},
|
|
1376
1425
|
/**
|
|
1377
1426
|
* #action
|
|
1378
1427
|
*/
|
|
1379
1428
|
setShowCenterLine(b) {
|
|
1380
1429
|
self.showCenterLine = b;
|
|
1430
|
+
localStorageSetBoolean('lgv-showCenterLine', b);
|
|
1381
1431
|
},
|
|
1382
1432
|
/**
|
|
1383
1433
|
* #action
|
|
@@ -1403,7 +1453,7 @@ export function stateModelFactory(pluginManager) {
|
|
|
1403
1453
|
/**
|
|
1404
1454
|
* #method
|
|
1405
1455
|
* Helper method for the fetchSequence.
|
|
1406
|
-
*
|
|
1456
|
+
* Returns the regions the user selected with the
|
|
1407
1457
|
* rubberband
|
|
1408
1458
|
*
|
|
1409
1459
|
* @param leftOffset - `object as {start, end, index, offset}`, offset = start
|
|
@@ -1426,7 +1476,8 @@ export function stateModelFactory(pluginManager) {
|
|
|
1426
1476
|
const { bpPerPx, offsetPx: rawOffsetPx } = computeMoveToLayout(layout, leftOffset, rightOffset);
|
|
1427
1477
|
// mirror Base1DView.scrollTo clamping: raw offsetPx can be far outside
|
|
1428
1478
|
// the valid range when both offsets are oob on the same side
|
|
1429
|
-
const
|
|
1479
|
+
const { min, max } = offsetBounds(self.totalBp / bpPerPx, self.width);
|
|
1480
|
+
const offsetPx = clamp(rawOffsetPx, min, max);
|
|
1430
1481
|
return wholeBaseRegions(calculateDynamicBlocks({
|
|
1431
1482
|
...layout,
|
|
1432
1483
|
bpPerPx,
|
|
@@ -1477,16 +1528,6 @@ export function stateModelFactory(pluginManager) {
|
|
|
1477
1528
|
setLaunch(launch) {
|
|
1478
1529
|
self.launch = launch;
|
|
1479
1530
|
},
|
|
1480
|
-
/**
|
|
1481
|
-
* #method
|
|
1482
|
-
* renders the view to SVG markup, which it returns; saves it through
|
|
1483
|
-
* FileSaver unless `save: false`
|
|
1484
|
-
*/
|
|
1485
|
-
// Promise<string> is stated: renderToSvg is typed against this very
|
|
1486
|
-
// model, so an inferred return makes the type reference itself (TS2456)
|
|
1487
|
-
async exportSvg(opts = {}) {
|
|
1488
|
-
return exportViewSvg(self, opts, () => import('./svgcomponents/SVGLinearGenomeView.js'));
|
|
1489
|
-
},
|
|
1490
1531
|
}))
|
|
1491
1532
|
.actions(self => {
|
|
1492
1533
|
let cancelLastSlide = () => { };
|
|
@@ -1542,8 +1583,8 @@ export function stateModelFactory(pluginManager) {
|
|
|
1542
1583
|
}
|
|
1543
1584
|
/**
|
|
1544
1585
|
* #action
|
|
1545
|
-
* cancel an in-flight animated zoom.
|
|
1546
|
-
*
|
|
1586
|
+
* cancel an in-flight animated zoom. Any other zoom already stops the
|
|
1587
|
+
* animation, so this is for stopping it *without* changing the
|
|
1547
1588
|
* zoom — the slider grabbing the thumb, before it has a value to commit.
|
|
1548
1589
|
*/
|
|
1549
1590
|
function cancelZoomAnimation() {
|
|
@@ -1571,8 +1612,8 @@ export function stateModelFactory(pluginManager) {
|
|
|
1571
1612
|
* #getter
|
|
1572
1613
|
* an ideogram only reads correctly against an entire chromosome: on a
|
|
1573
1614
|
* sub-region it is a meaningless slice of bands, and the centromere shows
|
|
1574
|
-
* up as a lone half-triangle. The chromosome needs bands
|
|
1575
|
-
*
|
|
1615
|
+
* up as a lone half-triangle. The chromosome also needs bands: a cytoband
|
|
1616
|
+
* file often lists only the main chromosomes, not chrM or the
|
|
1576
1617
|
* unplaced scaffolds
|
|
1577
1618
|
*/
|
|
1578
1619
|
get canShowCytobands() {
|
|
@@ -1584,7 +1625,7 @@ export function stateModelFactory(pluginManager) {
|
|
|
1584
1625
|
/**
|
|
1585
1626
|
* #getter
|
|
1586
1627
|
* the `showCytobands` setting gated by whether cytobands can be shown at
|
|
1587
|
-
* all (whole chromosome with bands
|
|
1628
|
+
* all (whole chromosome with bands in the cytoband file) and by the header
|
|
1588
1629
|
* overview they draw in being on, which the SVG export follows too
|
|
1589
1630
|
*/
|
|
1590
1631
|
get effectiveShowCytobands() {
|
|
@@ -1708,9 +1749,8 @@ export function stateModelFactory(pluginManager) {
|
|
|
1708
1749
|
/**
|
|
1709
1750
|
* #getter
|
|
1710
1751
|
* dynamic blocks represent the exact coordinates of the currently
|
|
1711
|
-
* visible genome regions on the screen.
|
|
1712
|
-
*
|
|
1713
|
-
* represent exactly what is on screen
|
|
1752
|
+
* visible genome regions on the screen. Static blocks can go
|
|
1753
|
+
* offscreen, while dynamic blocks represent exactly what is on screen
|
|
1714
1754
|
*/
|
|
1715
1755
|
get dynamicBlocks() {
|
|
1716
1756
|
return calculateDynamicBlocks(self);
|
|
@@ -1752,12 +1792,10 @@ export function stateModelFactory(pluginManager) {
|
|
|
1752
1792
|
* The frame exists because those three are laid out across every
|
|
1753
1793
|
* displayed region rather than across the viewport — it overhangs on
|
|
1754
1794
|
* both sides — so their `x` values are stable under a scroll and only
|
|
1755
|
-
* this number moves. `scalebarRefNameLabels` is the exception
|
|
1756
|
-
* so: its `transform` is already a screen x, because a sticky label's
|
|
1795
|
+
* this number moves. `scalebarRefNameLabels` is the exception: its `transform` is already a screen x, because a sticky label's
|
|
1757
1796
|
* position is a function of the scroll rather than of block geometry.
|
|
1758
1797
|
*
|
|
1759
|
-
* **The subtraction must happen here, in float64
|
|
1760
|
-
* load-bearing part.** `offsetPx` is a whole-genome pixel coordinate —
|
|
1798
|
+
* **The subtraction must happen here, in float64.** `offsetPx` is a whole-genome pixel coordinate —
|
|
1761
1799
|
* hg38 chr1 at base resolution is already past 1e10 — and a length that
|
|
1762
1800
|
* size does not survive the trip through CSS: the transform matrix is
|
|
1763
1801
|
* float32 by the time it reaches the compositor, where consecutive
|
|
@@ -1769,13 +1807,12 @@ export function stateModelFactory(pluginManager) {
|
|
|
1769
1807
|
* assemblies at high zoom, which is not where anyone tests. Both
|
|
1770
1808
|
* operands here are large and their difference is bounded by the
|
|
1771
1809
|
* overhang (a block or two), so what reaches CSS is small and exact.
|
|
1772
|
-
*
|
|
1810
|
+
* This applies the GPU side's rule one layer up; see
|
|
1773
1811
|
* `agent-docs/reference/BP_PRECISION.md`.
|
|
1774
1812
|
*
|
|
1775
1813
|
* Published because it is the one piece of coordinate arithmetic a host
|
|
1776
1814
|
* drawing its own chrome would otherwise have to know, and because
|
|
1777
|
-
*
|
|
1778
|
-
* came to disagree about rounding. Round it where the content is text
|
|
1815
|
+
* copies written out at each call site disagree about rounding. Round it where the content is text
|
|
1779
1816
|
* (a fractional offset blurs a label); leave it alone for paths and
|
|
1780
1817
|
* boxes.
|
|
1781
1818
|
*/
|
|
@@ -1790,21 +1827,12 @@ export function stateModelFactory(pluginManager) {
|
|
|
1790
1827
|
* than recomputing the makeTicks loop per component.
|
|
1791
1828
|
*/
|
|
1792
1829
|
get gridlineTicks() {
|
|
1793
|
-
const { bpPerPx } = self;
|
|
1794
1830
|
const { blocks, offsetPx: firstBlockOffset } = this.staticBlocks;
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
const runLeft = run.offsetPx - firstBlockOffset;
|
|
1801
|
-
for (const { type, x } of makeBlockTicks(run, bpPerPx)) {
|
|
1802
|
-
if (x >= 0 && x <= run.widthPx) {
|
|
1803
|
-
ticks.push({ x: runLeft + x, major: type === 'major' });
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1806
|
-
}
|
|
1807
|
-
return ticks;
|
|
1831
|
+
return getGridlineTicks({
|
|
1832
|
+
blocks,
|
|
1833
|
+
firstBlockOffset,
|
|
1834
|
+
bpPerPx: self.bpPerPx,
|
|
1835
|
+
});
|
|
1808
1836
|
},
|
|
1809
1837
|
/**
|
|
1810
1838
|
* #getter
|
|
@@ -1822,33 +1850,13 @@ export function stateModelFactory(pluginManager) {
|
|
|
1822
1850
|
* reads as the same number repeated across the row.
|
|
1823
1851
|
*/
|
|
1824
1852
|
get scalebarLabels() {
|
|
1825
|
-
const { bpPerPx } = self;
|
|
1826
1853
|
const { blocks, offsetPx: firstBlockOffset } = this.staticBlocks;
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
for (const { base, x } of makeBlockTicks(run, bpPerPx, false)) {
|
|
1834
|
-
const label = getTickDisplayStr(base + 1, bpPerPx);
|
|
1835
|
-
const w = tickLabelWidth(label);
|
|
1836
|
-
// the bold refName label pinned at the run's left edge takes
|
|
1837
|
-
// precedence, as it does on the overview scalebar
|
|
1838
|
-
if (x - w / 2 >= refNameLabelPx[i] &&
|
|
1839
|
-
labelFitsInBlock(x - w / 2, w, run.widthPx)) {
|
|
1840
|
-
runLabels.push({
|
|
1841
|
-
x: runLeft + x,
|
|
1842
|
-
label,
|
|
1843
|
-
key: `${run.offsetPx}-${base}`,
|
|
1844
|
-
});
|
|
1845
|
-
}
|
|
1846
|
-
}
|
|
1847
|
-
if (tickLabelsWorthDrawing(runLabels.length)) {
|
|
1848
|
-
labels.push(...runLabels);
|
|
1849
|
-
}
|
|
1850
|
-
}
|
|
1851
|
-
return labels;
|
|
1854
|
+
return getScalebarLabels({
|
|
1855
|
+
blocks,
|
|
1856
|
+
firstBlockOffset,
|
|
1857
|
+
bpPerPx: self.bpPerPx,
|
|
1858
|
+
orientation: self.displayedRegionsOrientation,
|
|
1859
|
+
});
|
|
1852
1860
|
},
|
|
1853
1861
|
/**
|
|
1854
1862
|
* #getter
|
|
@@ -1857,11 +1865,11 @@ export function stateModelFactory(pluginManager) {
|
|
|
1857
1865
|
* sticky, transform, maxWidth, paddingLeft, text}` each. One per run of
|
|
1858
1866
|
* same-refName regions, plus a "sticky" one pinned to the viewport's
|
|
1859
1867
|
* left edge naming the refName under it, so panning into a chromosome
|
|
1860
|
-
* does not scroll
|
|
1868
|
+
* does not scroll the chromosome's name off the screen.
|
|
1861
1869
|
*
|
|
1862
|
-
*
|
|
1863
|
-
* otherwise
|
|
1864
|
-
*
|
|
1870
|
+
* This getter applies three rules that a host drawing region names would
|
|
1871
|
+
* otherwise have to rediscover, and two of them are invisible until the
|
|
1872
|
+
* data is awkward:
|
|
1865
1873
|
*
|
|
1866
1874
|
* - the sticky label rides the rightmost run that has scrolled off the
|
|
1867
1875
|
* left, not the region's first block — that one is gone from
|
|
@@ -1873,7 +1881,7 @@ export function stateModelFactory(pluginManager) {
|
|
|
1873
1881
|
* is fitted to the whole run, and `displayedRegionIndex` /
|
|
1874
1882
|
* `lastDisplayedRegionIndex` bracket what it stands for.
|
|
1875
1883
|
* - a name is drawn whole or not at all, measured against the space its
|
|
1876
|
-
* run leaves. Clipped to
|
|
1884
|
+
* run leaves. Clipped to that space, `chr16` reads as `chr1` —
|
|
1877
1885
|
* a different chromosome rather than a shortened name, which is why
|
|
1878
1886
|
* this is a fit test rather than an ellipsis.
|
|
1879
1887
|
*
|
|
@@ -1893,7 +1901,7 @@ export function stateModelFactory(pluginManager) {
|
|
|
1893
1901
|
* pins to its right edge rather than the viewport's, and the coordinate
|
|
1894
1902
|
* numbers stay out from under it. The SVG export deliberately
|
|
1895
1903
|
* calls `getScalebarRefNameLabels` itself with no prefix rather than
|
|
1896
|
-
* reading this, since it draws
|
|
1904
|
+
* reading this, since it draws the assembly name itself above the ruler.
|
|
1897
1905
|
*/
|
|
1898
1906
|
get scalebarRefNameLabels() {
|
|
1899
1907
|
return getScalebarRefNameLabels({
|
|
@@ -1908,7 +1916,7 @@ export function stateModelFactory(pluginManager) {
|
|
|
1908
1916
|
* Every span along the row that is not track data, as plain geometry in
|
|
1909
1917
|
* the staticBlocks frame — the same frame as gridlineTicks and
|
|
1910
1918
|
* scalebarLabels, so one `translateX(staticBlocksTranslateX)` places all
|
|
1911
|
-
* three. A host
|
|
1919
|
+
* three. A host that draws the row's chrome itself needs all three kinds:
|
|
1912
1920
|
*
|
|
1913
1921
|
* - `seam`: the 3px bar at a region's right edge. Displayed regions are
|
|
1914
1922
|
* laid out **contiguously** (calculateStaticBlocks emits boundary
|
|
@@ -1923,11 +1931,11 @@ export function stateModelFactory(pluginManager) {
|
|
|
1923
1931
|
* but the 24 chromosomes are under a pixel wide), so a host that
|
|
1924
1932
|
* skips them leaves that part of the row empty.
|
|
1925
1933
|
* - `boundary`: past the start of the first region or the end of the
|
|
1926
|
-
* last. Greying it
|
|
1934
|
+
* last. Greying it makes the last region's seam read as the edge of a
|
|
1927
1935
|
* filled area, where it would otherwise look like a line in the
|
|
1928
1936
|
* middle of the track.
|
|
1929
1937
|
*
|
|
1930
|
-
* Elided blocks get no seam even though they
|
|
1938
|
+
* Elided blocks get no seam even though they have the flag: at the
|
|
1931
1939
|
* zoom where regions elide, one bar per region would fill the row solid
|
|
1932
1940
|
* grey.
|
|
1933
1941
|
*
|
|
@@ -1946,38 +1954,7 @@ export function stateModelFactory(pluginManager) {
|
|
|
1946
1954
|
// cannot drift without failing here.
|
|
1947
1955
|
get paddingSpans() {
|
|
1948
1956
|
const { blocks, offsetPx: firstBlockOffset } = this.staticBlocks;
|
|
1949
|
-
|
|
1950
|
-
for (const block of blocks) {
|
|
1951
|
-
const x = block.offsetPx - firstBlockOffset;
|
|
1952
|
-
if (block.type === 'ContentBlock') {
|
|
1953
|
-
if (block.isRightEndOfDisplayedRegion) {
|
|
1954
|
-
spans.push({
|
|
1955
|
-
key: `${block.key}-sep`,
|
|
1956
|
-
x: x + block.widthPx - 1,
|
|
1957
|
-
width: 3,
|
|
1958
|
-
kind: 'seam',
|
|
1959
|
-
});
|
|
1960
|
-
}
|
|
1961
|
-
}
|
|
1962
|
-
else {
|
|
1963
|
-
spans.push({
|
|
1964
|
-
key: block.key,
|
|
1965
|
-
x,
|
|
1966
|
-
width: block.widthPx,
|
|
1967
|
-
kind: block.type === 'ElidedBlock'
|
|
1968
|
-
? 'elided'
|
|
1969
|
-
: 'boundary',
|
|
1970
|
-
});
|
|
1971
|
-
}
|
|
1972
|
-
}
|
|
1973
|
-
// One shared array when there is nothing to draw, so the computed's
|
|
1974
|
-
// value repeats and MobX's `===` stops the chain here. This is the
|
|
1975
|
-
// COMMON case, not an edge one: a view sitting inside one contig has
|
|
1976
|
-
// no region seam, no elision and no boundary, and that is where a
|
|
1977
|
-
// reader spends nearly all of a session. A fresh `[]` each frame
|
|
1978
|
-
// instead re-rendered a PaddingBlocks per track plus one for the
|
|
1979
|
-
// container, every frame of every gesture, to draw nothing.
|
|
1980
|
-
return spans.length > 0 ? spans : NO_PADDING_SPANS;
|
|
1957
|
+
return getPaddingSpans(blocks, firstBlockOffset);
|
|
1981
1958
|
},
|
|
1982
1959
|
/**
|
|
1983
1960
|
* #getter
|
|
@@ -2050,7 +2027,7 @@ export function stateModelFactory(pluginManager) {
|
|
|
2050
2027
|
/**
|
|
2051
2028
|
* #getter
|
|
2052
2029
|
* Right edge (px, viewport-relative) of the on-screen content, clamped
|
|
2053
|
-
* to the track — where a right-pinned overlay such as a wiggle's
|
|
2030
|
+
* to the track — where a right-pinned overlay such as a wiggle's color
|
|
2054
2031
|
* or score legend belongs, rather than out in the empty gutter the
|
|
2055
2032
|
* regions can leave at whole-genome zoom. `@jbrowse/display-kit`'s
|
|
2056
2033
|
* `contentRightEdgePx` states the rule; an SVG export applies the same
|
|
@@ -2060,7 +2037,7 @@ export function stateModelFactory(pluginManager) {
|
|
|
2060
2037
|
* array of fresh objects on every pan and zoom frame, so a component
|
|
2061
2038
|
* that derived this number itself re-rendered on every frame of every
|
|
2062
2039
|
* gesture — to produce, whenever content fills the track, the unchanged
|
|
2063
|
-
* `trackWidthPx`.
|
|
2040
|
+
* `trackWidthPx`. A component that reads the scalar lets MobX's `===` stop the chain at
|
|
2064
2041
|
* this computed instead. Measured over 20 zoom frames: the wiggle
|
|
2065
2042
|
* family's three body instances fell from 66 renders to 7.
|
|
2066
2043
|
*/
|
|
@@ -2199,17 +2176,16 @@ export function stateModelFactory(pluginManager) {
|
|
|
2199
2176
|
* as a navigation that didn't happen, and wiggle's autoscale domain is
|
|
2200
2177
|
* computed over the window the user just left — measured at `[0,200]`
|
|
2201
2178
|
* against a correct `[0,300]` when a 40bp coarse window survived a jump to
|
|
2202
|
-
* 4040bp on screen.
|
|
2203
|
-
* made that reachable: at 600ms the data never landed inside the hole.
|
|
2179
|
+
* 4040bp on screen.
|
|
2204
2180
|
*
|
|
2205
|
-
* **The continuous paths
|
|
2181
|
+
* **The continuous paths do not call this.** The
|
|
2206
2182
|
* spring zoom writes through `zoomTo` per frame and a drag through
|
|
2207
2183
|
* `scrollTo`, so this adds no per-frame work; the settle is idempotent
|
|
2208
2184
|
* (`setCoarseDynamicBlocks` compares block keys), so the debounced
|
|
2209
|
-
* autorun's
|
|
2185
|
+
* autorun's follow-up run costs nothing.
|
|
2210
2186
|
*
|
|
2211
|
-
* Every placer calls it, and `placersSettleCoarseBlocks.test.ts`
|
|
2212
|
-
*
|
|
2187
|
+
* Every placer calls it, and `placersSettleCoarseBlocks.test.ts` enforces
|
|
2188
|
+
* that for the next one: it scans this file for the writes and
|
|
2213
2189
|
* fails on a placer that reaches neither the settle nor its named list of
|
|
2214
2190
|
* continuous paths. Two passes by hand missed four.
|
|
2215
2191
|
*/
|
|
@@ -2219,6 +2195,42 @@ export function stateModelFactory(pluginManager) {
|
|
|
2219
2195
|
}
|
|
2220
2196
|
},
|
|
2221
2197
|
}))
|
|
2198
|
+
.actions(self => {
|
|
2199
|
+
function layOutAt(width) {
|
|
2200
|
+
self.setWidth(width);
|
|
2201
|
+
self.settleCoarseBlocks();
|
|
2202
|
+
}
|
|
2203
|
+
return {
|
|
2204
|
+
/**
|
|
2205
|
+
* #method
|
|
2206
|
+
* renders the view to SVG markup, which it returns; saves it through
|
|
2207
|
+
* FileSaver unless `save: false`. A `viewWidth` lays the view out at
|
|
2208
|
+
* that many pixels for the export, as a window that wide would, and
|
|
2209
|
+
* puts the on-screen width back after.
|
|
2210
|
+
*/
|
|
2211
|
+
// Promise<string> is stated: renderToSvg is typed against this very
|
|
2212
|
+
// model, so an inferred return makes the type reference itself (TS2456)
|
|
2213
|
+
async exportSvg(opts = {}) {
|
|
2214
|
+
const { width } = self;
|
|
2215
|
+
const { viewWidth = width } = opts;
|
|
2216
|
+
// The on-screen tracks redraw at `viewWidth` until the export ends. A
|
|
2217
|
+
// hidden copy of the view would keep them still, but it needs a
|
|
2218
|
+
// session slot that autosave and undo skip, and new ids for the view,
|
|
2219
|
+
// its tracks and displays, since widgets find them by id.
|
|
2220
|
+
if (viewWidth !== width) {
|
|
2221
|
+
layOutAt(viewWidth);
|
|
2222
|
+
}
|
|
2223
|
+
try {
|
|
2224
|
+
return await exportViewSvg(self, opts, () => import('./svgcomponents/SVGLinearGenomeView.js'));
|
|
2225
|
+
}
|
|
2226
|
+
finally {
|
|
2227
|
+
if (viewWidth !== width) {
|
|
2228
|
+
layOutAt(width);
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
},
|
|
2232
|
+
};
|
|
2233
|
+
})
|
|
2222
2234
|
.actions(self => ({
|
|
2223
2235
|
/**
|
|
2224
2236
|
* #action
|
|
@@ -2234,8 +2246,8 @@ export function stateModelFactory(pluginManager) {
|
|
|
2234
2246
|
},
|
|
2235
2247
|
/**
|
|
2236
2248
|
* #action
|
|
2237
|
-
* Place the viewport by
|
|
2238
|
-
*
|
|
2249
|
+
* Place the viewport by `bpPerPx` and `offsetPx`, the pixel pair the view
|
|
2250
|
+
* stored before it stored the window. Prefer `setWindow`: pixels mean nothing without the width they were
|
|
2239
2251
|
* measured at, so a round trip through here is only exact while the width
|
|
2240
2252
|
* holds still. Kept for callers that genuinely have pixels — a wheel
|
|
2241
2253
|
* gesture, a rubberband.
|
|
@@ -2248,7 +2260,7 @@ export function stateModelFactory(pluginManager) {
|
|
|
2248
2260
|
/**
|
|
2249
2261
|
* #action
|
|
2250
2262
|
* Place the viewport by the pair it is actually STORED as: the window's
|
|
2251
|
-
* width and left edge, both in bp.
|
|
2263
|
+
* width and left edge, both in bp. The bp-space counterpart of `setNewView`, and the
|
|
2252
2264
|
* right one for anything that captures a viewport and puts it back later
|
|
2253
2265
|
* (an Undo, a saved location) — those two moments can be a window resize
|
|
2254
2266
|
* apart, and this pair survives one where a pixel pair does not.
|
|
@@ -2278,8 +2290,8 @@ export function stateModelFactory(pluginManager) {
|
|
|
2278
2290
|
},
|
|
2279
2291
|
/**
|
|
2280
2292
|
* #action
|
|
2281
|
-
*
|
|
2282
|
-
* by `displayedRegionIndex`, so a set that outlives the region list has a
|
|
2293
|
+
* Settles the coarse blocks, since this is the worst jump to leave
|
|
2294
|
+
* unsettled: they are keyed by `displayedRegionIndex`, so a set that outlives the region list has a
|
|
2283
2295
|
* consumer reading one contig's data against another's blocks.
|
|
2284
2296
|
*/
|
|
2285
2297
|
setDisplayedRegions(regions) {
|
|
@@ -2498,8 +2510,7 @@ export function stateModelFactory(pluginManager) {
|
|
|
2498
2510
|
// unrelated files lose their parameter types to it.
|
|
2499
2511
|
async navToLocString(input, optAssemblyName, grow, opts) {
|
|
2500
2512
|
const { assemblyNames } = self;
|
|
2501
|
-
const
|
|
2502
|
-
const { assemblyManager } = session;
|
|
2513
|
+
const { assemblyManager } = getSession(self);
|
|
2503
2514
|
const assemblyName = optAssemblyName || assemblyNames[0];
|
|
2504
2515
|
if (assemblyName) {
|
|
2505
2516
|
await assemblyManager.waitForAssembly(assemblyName);
|
|
@@ -2739,7 +2750,7 @@ export function stateModelFactory(pluginManager) {
|
|
|
2739
2750
|
/**
|
|
2740
2751
|
* #method
|
|
2741
2752
|
* returns menu items for a highlight context menu. plugins can extend
|
|
2742
|
-
* this via Core-extendPluggableElement to add
|
|
2753
|
+
* this via Core-extendPluggableElement to add items
|
|
2743
2754
|
*/
|
|
2744
2755
|
highlightMenuItems(_highlight) {
|
|
2745
2756
|
return [];
|
|
@@ -2911,22 +2922,13 @@ export function stateModelFactory(pluginManager) {
|
|
|
2911
2922
|
};
|
|
2912
2923
|
})
|
|
2913
2924
|
.postProcessSnapshot(snap => {
|
|
2914
|
-
// showCenterLine is
|
|
2915
|
-
//
|
|
2916
|
-
//
|
|
2917
|
-
//
|
|
2918
|
-
//
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
const { showCenterLine, showCytobands, trackLabels, colorByCDS, showAminoAcids, showTrackOutlines, ...rest } = snap;
|
|
2922
|
-
return {
|
|
2923
|
-
...rest,
|
|
2924
|
-
...(!showCytobands ? { showCytobands } : {}),
|
|
2925
|
-
...(trackLabels ? { trackLabels } : {}),
|
|
2926
|
-
...(colorByCDS ? { colorByCDS } : {}),
|
|
2927
|
-
...(!showAminoAcids ? { showAminoAcids } : {}),
|
|
2928
|
-
...(!showTrackOutlines ? { showTrackOutlines } : {}),
|
|
2929
|
-
};
|
|
2925
|
+
// showCenterLine is a personal preference and stays out of the session.
|
|
2926
|
+
// The other localStorage-seeded settings are always written: the
|
|
2927
|
+
// receiving browser's localStorage fills an absent one, so stripping a
|
|
2928
|
+
// default would show a share link's reader their own setting instead
|
|
2929
|
+
// of the author's.
|
|
2930
|
+
const { showCenterLine, ...rest } = snap;
|
|
2931
|
+
return rest;
|
|
2930
2932
|
});
|
|
2931
2933
|
}
|
|
2932
2934
|
// #endregion
|