@jbrowse/plugin-linear-genome-view 5.0.0-beta.2 → 5.0.0-beta.20
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/LinearGenomeView/afterAttach.d.ts +22 -5
- package/esm/LinearGenomeView/afterAttach.js +129 -106
- package/esm/LinearGenomeView/axisConfigSchema.d.ts +18 -0
- package/esm/LinearGenomeView/axisConfigSchema.js +22 -0
- package/esm/LinearGenomeView/components/CenterLine.js +10 -5
- package/esm/LinearGenomeView/components/ConnectedHoverHighlight.js +2 -3
- package/esm/LinearGenomeView/components/CrisprGuidePanel.js +3 -5
- package/esm/LinearGenomeView/components/ExportSvgDialog.d.ts +9 -4
- package/esm/LinearGenomeView/components/ExportSvgDialog.js +45 -8
- package/esm/LinearGenomeView/components/GetSequenceDialog.js +2 -2
- package/esm/LinearGenomeView/components/Header.js +11 -1
- package/esm/LinearGenomeView/components/HeaderClearHighlightButton.js +2 -2
- package/esm/LinearGenomeView/components/HeaderPanControls.d.ts +3 -3
- package/esm/LinearGenomeView/components/HeaderPanControls.js +6 -3
- package/esm/LinearGenomeView/components/HeaderSearchBoxes.js +4 -1
- package/esm/LinearGenomeView/components/HeaderZoomControls.js +6 -35
- package/esm/LinearGenomeView/components/Highlight.js +27 -11
- package/esm/LinearGenomeView/components/HighlightBand.d.ts +11 -2
- package/esm/LinearGenomeView/components/HighlightBand.js +32 -13
- package/esm/LinearGenomeView/components/HighlightChip.d.ts +3 -3
- package/esm/LinearGenomeView/components/HighlightChip.js +13 -8
- package/esm/LinearGenomeView/components/HighlightLabelDialog.d.ts +6 -0
- package/esm/LinearGenomeView/components/HighlightLabelDialog.js +18 -0
- package/esm/LinearGenomeView/components/HoverPositionHighlight.js +3 -3
- package/esm/LinearGenomeView/components/ImportForm.js +7 -9
- package/esm/LinearGenomeView/components/LinearGenomeView.js +5 -12
- package/esm/LinearGenomeView/components/LinearGenomeViewContainer.js +13 -14
- package/esm/LinearGenomeView/components/MiniControls.js +11 -5
- package/esm/LinearGenomeView/components/OverviewHighlight.js +3 -3
- package/esm/LinearGenomeView/components/OverviewHighlightBand.d.ts +3 -2
- package/esm/LinearGenomeView/components/OverviewHighlightBand.js +6 -5
- package/esm/LinearGenomeView/components/OverviewRubberband.js +25 -48
- package/esm/LinearGenomeView/components/OverviewScalebar.js +4 -5
- package/esm/LinearGenomeView/components/OverviewScalebarPolygon.js +5 -3
- package/esm/LinearGenomeView/components/PaddingBlocks.js +3 -8
- package/esm/LinearGenomeView/components/RangeSelectOverlay.js +5 -6
- package/esm/LinearGenomeView/components/RegionWidthEditorDialog.d.ts +3 -2
- package/esm/LinearGenomeView/components/RegionWidthEditorDialog.js +4 -4
- package/esm/LinearGenomeView/components/ResizeAllTracksHandle.d.ts +5 -0
- package/esm/LinearGenomeView/components/ResizeAllTracksHandle.js +74 -0
- package/esm/LinearGenomeView/components/Rubberband.d.ts +1 -2
- package/esm/LinearGenomeView/components/Rubberband.js +3 -2
- package/esm/LinearGenomeView/components/SVGHighlightBand.d.ts +10 -1
- package/esm/LinearGenomeView/components/SVGHighlightBand.js +16 -5
- package/esm/LinearGenomeView/components/Scalebar.d.ts +2 -6
- package/esm/LinearGenomeView/components/Scalebar.js +8 -5
- package/esm/LinearGenomeView/components/ScalebarRefNameLabels.js +51 -36
- package/esm/LinearGenomeView/components/SearchBox.js +10 -16
- 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 -8
- package/esm/LinearGenomeView/components/SearchResultsDialog.js +7 -4
- package/esm/LinearGenomeView/components/SearchResultsTable.d.ts +4 -7
- package/esm/LinearGenomeView/components/SearchResultsTable.js +25 -53
- package/esm/LinearGenomeView/components/SequenceFeatureHoverHighlight.js +2 -3
- package/esm/LinearGenomeView/components/SequenceSearchDialog.d.ts +2 -4
- package/esm/LinearGenomeView/components/StrandCheckboxes.d.ts +3 -2
- package/esm/LinearGenomeView/components/StrandCheckboxes.js +4 -2
- package/esm/LinearGenomeView/components/TrackContainer.d.ts +2 -3
- package/esm/LinearGenomeView/components/TrackContainer.js +63 -10
- package/esm/LinearGenomeView/components/TrackLabel.js +14 -4
- package/esm/LinearGenomeView/components/TrackLabelButtons.js +2 -2
- package/esm/LinearGenomeView/components/TrackLabelDragHandle.js +4 -5
- package/esm/LinearGenomeView/components/TrackLabelMenu.js +11 -14
- package/esm/LinearGenomeView/components/TrackLabelRefNameWarning.d.ts +3 -4
- package/esm/LinearGenomeView/components/TrackLabelRefNameWarning.js +23 -10
- package/esm/LinearGenomeView/components/TrackRenderingContainer.d.ts +6 -4
- package/esm/LinearGenomeView/components/TrackRenderingContainer.js +12 -24
- package/esm/LinearGenomeView/components/TracksContainer.js +11 -23
- package/esm/LinearGenomeView/components/VerticalGuide.d.ts +1 -2
- package/esm/LinearGenomeView/components/VerticalGuide.js +3 -16
- package/esm/LinearGenomeView/components/fetchSequence.d.ts +1 -2
- package/esm/LinearGenomeView/components/fetchSequence.js +15 -7
- package/esm/LinearGenomeView/components/recentLocationsMenu.js +10 -0
- package/esm/LinearGenomeView/components/useRangeSelect.d.ts +0 -3
- package/esm/LinearGenomeView/components/useRangeSelect.js +26 -61
- package/esm/LinearGenomeView/components/useSearchBoxPrefs.d.ts +3 -1
- package/esm/LinearGenomeView/components/useSearchBoxPrefs.js +4 -2
- package/esm/LinearGenomeView/components/useSideScroll.d.ts +17 -2
- package/esm/LinearGenomeView/components/useSideScroll.js +156 -102
- package/esm/LinearGenomeView/components/util.d.ts +4 -0
- package/esm/LinearGenomeView/components/util.js +4 -2
- package/esm/LinearGenomeView/consts.d.ts +2 -0
- package/esm/LinearGenomeView/consts.js +3 -1
- package/esm/LinearGenomeView/containingLgv.d.ts +21 -0
- package/esm/LinearGenomeView/containingLgv.js +23 -0
- package/esm/LinearGenomeView/headerFit.d.ts +4 -2
- package/esm/LinearGenomeView/headerFit.js +20 -7
- package/esm/LinearGenomeView/index.d.ts +2 -7
- package/esm/LinearGenomeView/index.js +5 -7
- package/esm/LinearGenomeView/keyboardHandler.d.ts +8 -7
- package/esm/LinearGenomeView/keyboardHandler.js +61 -23
- package/esm/LinearGenomeView/launchKeys.d.ts +1 -1
- package/esm/LinearGenomeView/launchKeys.js +11 -6
- package/esm/LinearGenomeView/lazyDialogs.d.ts +16 -3
- package/esm/LinearGenomeView/lazyDialogs.js +1 -0
- package/esm/LinearGenomeView/linkedViewSync.d.ts +3 -11
- package/esm/LinearGenomeView/linkedViewSync.js +27 -49
- package/esm/LinearGenomeView/menuItems.d.ts +16 -18
- package/esm/LinearGenomeView/menuItems.js +264 -164
- package/esm/LinearGenomeView/model.d.ts +1192 -560
- package/esm/LinearGenomeView/model.js +805 -590
- package/esm/LinearGenomeView/rExport/exportLinearView.d.ts +17 -0
- package/esm/LinearGenomeView/rExport/exportLinearView.js +117 -0
- package/esm/LinearGenomeView/rExport/rScript.d.ts +110 -0
- package/esm/LinearGenomeView/rExport/rScript.js +524 -0
- package/esm/LinearGenomeView/sharedScaleContainer.d.ts +1 -1
- package/esm/LinearGenomeView/sharedScaleContainer.js +2 -12
- package/esm/LinearGenomeView/showFiles.d.ts +2 -0
- package/esm/LinearGenomeView/showFiles.js +19 -0
- package/esm/LinearGenomeView/showRegionsWithUndo.d.ts +1 -1
- package/esm/LinearGenomeView/showRegionsWithUndo.js +13 -5
- package/esm/LinearGenomeView/stickyChrome.d.ts +5 -9
- package/esm/LinearGenomeView/stickyChrome.js +5 -9
- package/esm/LinearGenomeView/svgcomponents/SVGGridlines.js +4 -4
- package/esm/LinearGenomeView/svgcomponents/SVGHeader.d.ts +4 -1
- package/esm/LinearGenomeView/svgcomponents/SVGHeader.js +8 -21
- package/esm/LinearGenomeView/svgcomponents/SVGHighlights.d.ts +3 -1
- package/esm/LinearGenomeView/svgcomponents/SVGHighlights.js +9 -9
- package/esm/LinearGenomeView/svgcomponents/SVGHighlightsOverlay.d.ts +3 -1
- package/esm/LinearGenomeView/svgcomponents/SVGHighlightsOverlay.js +5 -5
- package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +13 -18
- package/esm/LinearGenomeView/svgcomponents/SVGRegionSeparators.js +2 -5
- package/esm/LinearGenomeView/svgcomponents/SVGRowHeader.js +5 -6
- package/esm/LinearGenomeView/svgcomponents/SVGRuler.js +9 -10
- package/esm/LinearGenomeView/svgcomponents/SVGScalebar.js +3 -3
- package/esm/LinearGenomeView/svgcomponents/SVGStackedRow.d.ts +26 -0
- package/esm/LinearGenomeView/svgcomponents/SVGStackedRow.js +12 -0
- package/esm/LinearGenomeView/svgcomponents/SVGTrackLabel.js +21 -13
- package/esm/LinearGenomeView/svgcomponents/SVGTracks.d.ts +4 -5
- package/esm/LinearGenomeView/svgcomponents/SVGTracks.js +7 -6
- package/esm/LinearGenomeView/svgcomponents/SVGView.d.ts +2 -3
- package/esm/LinearGenomeView/svgcomponents/SVGView.js +17 -3
- package/esm/LinearGenomeView/svgcomponents/renderViewTracks.d.ts +24 -20
- package/esm/LinearGenomeView/svgcomponents/renderViewTracks.js +42 -29
- package/esm/LinearGenomeView/svgcomponents/useViewSvgFigure.js +74 -77
- package/esm/LinearGenomeView/svgcomponents/util.d.ts +28 -12
- package/esm/LinearGenomeView/svgcomponents/util.js +41 -29
- package/esm/LinearGenomeView/types.d.ts +13 -10
- package/esm/LinearGenomeView/types.js +0 -1
- package/esm/LinearGenomeView/util.d.ts +103 -46
- package/esm/LinearGenomeView/util.js +222 -170
- package/esm/MultiLevelRubberband/MultiLevelRubberband.js +9 -6
- package/esm/MultiLevelRubberband/index.d.ts +2 -0
- package/esm/MultiLevelRubberband/index.js +2 -0
- package/esm/MultiLevelRubberband/menuItems.d.ts +34 -0
- package/esm/MultiLevelRubberband/menuItems.js +52 -0
- package/esm/MultiLevelRubberband/rowLabel.d.ts +22 -0
- package/esm/MultiLevelRubberband/rowLabel.js +27 -0
- package/esm/MultiLevelRubberband/types.d.ts +1 -0
- package/esm/MultiLevelRubberband/useRangeSelect.d.ts +2 -1
- package/esm/MultiLevelRubberband/useRangeSelect.js +45 -59
- package/esm/TrackModifiers/index.d.ts +83 -0
- package/esm/TrackModifiers/index.js +231 -0
- package/esm/displayKitTests/perRegionTestEnv.d.ts +244 -68
- package/esm/displayKitTests/perRegionTestEnv.js +30 -26
- package/esm/index.d.ts +21 -99
- package/esm/index.js +17 -38
- package/esm/searchUtils.d.ts +26 -10
- package/esm/searchUtils.js +137 -137
- package/esm/shared/RubberbandSpan.d.ts +3 -2
- package/esm/shared/RubberbandSpan.js +6 -3
- package/esm/shared/coordLabels.d.ts +11 -4
- package/esm/shared/coordLabels.js +20 -5
- package/esm/shared/useWindowDrag.d.ts +18 -0
- package/esm/shared/useWindowDrag.js +47 -0
- package/package.json +12 -11
- package/esm/FeatureTrack/configSchema.d.ts +0 -124
- package/esm/FeatureTrack/configSchema.js +0 -54
- package/esm/FeatureTrack/index.d.ts +0 -2
- package/esm/FeatureTrack/index.js +0 -13
- package/esm/LinearGenomeView/initKeys.d.ts +0 -12
- package/esm/LinearGenomeView/initKeys.js +0 -68
- package/esm/MultiLevelRubberband/VerticalGuide.d.ts +0 -6
- package/esm/MultiLevelRubberband/VerticalGuide.js +0 -21
- package/esm/lazyPluginExports.d.ts +0 -23
- package/esm/lazyPluginExports.js +0 -36
|
@@ -1,17 +1,34 @@
|
|
|
1
1
|
import type { LinearGenomeViewModel } from './model.ts';
|
|
2
2
|
import type { InitState } from './types.ts';
|
|
3
|
-
import type { AssemblyHost, NotificationSink } from '@jbrowse/core/util';
|
|
3
|
+
import type { AbstractSessionModel, AssemblyHost, NotificationSink } from '@jbrowse/core/util';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Navigate a view to an init blob's `loc`, reporting a bad one as a warning
|
|
6
|
+
* (a gene that matched nothing) or an error rather than throwing. Exported
|
|
7
|
+
* because a LinearSyntenyView row is a LinearGenomeView that does not go
|
|
8
|
+
* through this file's init autorun, so it navigates each row itself.
|
|
9
|
+
*/
|
|
10
|
+
export declare function navToInitLoc(view: Pick<LinearGenomeViewModel, 'navToLocString'>, session: NotificationSink, init: Pick<InitState, 'assembly' | 'grow' | 'showHitTrack' | 'tracks'> & {
|
|
11
|
+
loc: string;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Open an init blob's `tracks`, one at a time, a bare string taken as a list
|
|
15
|
+
* of one. launchTrack funnels through showTrackGeneric, which surfaces any
|
|
16
|
+
* failure (unresolved id, bad config) as its own snackbar, and loads the
|
|
17
|
+
* picked display's state model first when it is registered lazily. Exported
|
|
18
|
+
* for the LinearSyntenyView rows, as `navToInitLoc` is.
|
|
19
|
+
*/
|
|
20
|
+
export declare function showInitTracks(self: LinearGenomeViewModel, init: Pick<InitState, 'tracks'>): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Add an init blob's `highlight` entries to the session, coercing each
|
|
23
|
+
* locstring or wire-format object and reporting a bad one without taking out
|
|
24
|
+
* its siblings. An entry without an assembly takes the launch's.
|
|
8
25
|
*
|
|
9
26
|
* Exported because a LinearSyntenyView row is a LinearGenomeView that does NOT
|
|
10
27
|
* go through this file's init autorun — the synteny view builds each row's
|
|
11
28
|
* snapshot and navigates it itself — so the row's own `highlight` had nowhere
|
|
12
29
|
* to be applied and was dropped in silence.
|
|
13
30
|
*/
|
|
14
|
-
export declare function applyInitHighlights(
|
|
31
|
+
export declare function applyInitHighlights(session: AssemblyHost & NotificationSink & Pick<AbstractSessionModel, 'addHighlight'>, init: Pick<InitState, 'highlight' | 'assembly'>): void;
|
|
15
32
|
/**
|
|
16
33
|
* Autorun that handles the init state - navigating to initial location,
|
|
17
34
|
* showing tracks, etc.
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { getSession, isSessionModelWithWidgets,
|
|
1
|
+
import { getSession, isSessionModelWithWidgets, resolveNamedRegions, } from '@jbrowse/core/util';
|
|
2
2
|
import { coerceHighlight } from '@jbrowse/core/util/highlights';
|
|
3
3
|
import { installInitAutorun } from '@jbrowse/core/util/installInitAutorun';
|
|
4
|
-
import { normalizeTrackInit } from '@jbrowse/core/util/tracks';
|
|
4
|
+
import { normalizeTrackInit, warmTrackDisplayGeneric, } from '@jbrowse/core/util/tracks';
|
|
5
|
+
import { requestedDisplayType } from '@jbrowse/launch-args/displayAlias';
|
|
5
6
|
import { addDisposer } from '@jbrowse/mobx-state-tree';
|
|
6
|
-
import { autorun, when } from 'mobx';
|
|
7
|
+
import { autorun, untracked, when } from 'mobx';
|
|
7
8
|
import { SearchResultsNotFoundError } from '../searchUtils.js';
|
|
8
9
|
// `init` is a frozen blob that often comes from hand-authored JSON, where a
|
|
9
10
|
// lone entry gets written bare (`tracks: 'genes'`). A string is iterable, so
|
|
@@ -21,24 +22,17 @@ async function openTracklist(self, session, superseded) {
|
|
|
21
22
|
// activateTrackSelector throws without widget support, which would abort the
|
|
22
23
|
// rest of init (navigation included) over an optional extra
|
|
23
24
|
if (isSessionModelWithWidgets(session)) {
|
|
24
|
-
// `
|
|
25
|
-
//
|
|
26
|
-
// product-core's, so
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
// at all. A *minimized* drawer still holds a visibleWidget while taking no
|
|
32
|
-
// width, and showWidget un-minimizes it (product-core's DrawerWidgets), so
|
|
33
|
-
// `!!visibleWidget` alone reads "already open", skips the wait below, and
|
|
34
|
-
// hands navigation the pre-drawer width — the exact thing this is here to
|
|
35
|
-
// prevent.
|
|
36
|
-
const drawerWasOpen = !!session.visibleWidget && !session.minimized && !poppedOut;
|
|
25
|
+
// `drawerVisible` is the line App.tsx draws to render the drawer column at
|
|
26
|
+
// all: a minimized, popped-out or modal-only widget takes no width. It
|
|
27
|
+
// lives on SessionWithDrawerWidgets, whose type guard is product-core's, so
|
|
28
|
+
// it is duck-typed rather than taken as an upward dependency; a session
|
|
29
|
+
// without it has no drawer column.
|
|
30
|
+
const drawerVisible = () => 'drawerVisible' in session && session.drawerVisible === true;
|
|
31
|
+
const drawerWasVisible = drawerVisible();
|
|
37
32
|
const widthBefore = self.volatileWidth;
|
|
38
33
|
self.activateTrackSelector();
|
|
39
|
-
// a width change is only coming if the drawer
|
|
40
|
-
|
|
41
|
-
if (!drawerWasOpen && !poppedOut) {
|
|
34
|
+
// a width change is only coming if the drawer just started taking space
|
|
35
|
+
if (!drawerWasVisible && drawerVisible()) {
|
|
42
36
|
// Bounded so init can't wedge here if the drawer doesn't shrink the view
|
|
43
37
|
// (e.g. embedded or modal-drawer layouts, where no width change is
|
|
44
38
|
// coming), and superseded so a re-launch landing mid-wait isn't held
|
|
@@ -88,86 +82,102 @@ function showNamedRegions(self, session, assemblyName, names) {
|
|
|
88
82
|
}
|
|
89
83
|
}
|
|
90
84
|
}
|
|
91
|
-
|
|
85
|
+
function reportInitNavError(session, e) {
|
|
86
|
+
console.error(e);
|
|
87
|
+
if (e instanceof SearchResultsNotFoundError) {
|
|
88
|
+
// a gene name that matched nothing is a soft miss, not an app error
|
|
89
|
+
session.notify(e.message, 'warning');
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
session.notifyError(`${e}`, e);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Navigate a view to an init blob's `loc`, reporting a bad one as a warning
|
|
97
|
+
* (a gene that matched nothing) or an error rather than throwing. Exported
|
|
98
|
+
* because a LinearSyntenyView row is a LinearGenomeView that does not go
|
|
99
|
+
* through this file's init autorun, so it navigates each row itself.
|
|
100
|
+
*/
|
|
101
|
+
export async function navToInitLoc(view, session, init) {
|
|
92
102
|
try {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
await self.navToLocString(init.loc, init.assembly, init.grow, {
|
|
100
|
-
showHitTrack: asArray(init.tracks).length === 0,
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
else if (init.displayedRegionNames?.length) {
|
|
104
|
-
// an explicit region list is a navigation request just like `loc`, so it
|
|
105
|
-
// applies even when regions already exist (URL params layered onto a
|
|
106
|
-
// defaultSession that already navigated)
|
|
107
|
-
//
|
|
108
|
-
// `?.length`, not the bare key: an empty array is truthy, so a spec
|
|
109
|
-
// writing `displayedRegionNames: []` took this path with nothing to name
|
|
110
|
-
// and got told its list of no names had matched no regions. That is why
|
|
111
|
-
// LinearSyntenyView's doSubmit omits the key rather than passing [].
|
|
112
|
-
showNamedRegions(self, session, init.assembly, init.displayedRegionNames);
|
|
113
|
-
}
|
|
114
|
-
else if (!self.hasDisplayedRegions) {
|
|
115
|
-
// a highlight-only init (nothing to navigate to) must not clobber a
|
|
116
|
-
// defaultSession's existing navigation, so only auto-navigate when
|
|
117
|
-
// nothing is shown yet
|
|
118
|
-
self.showAllRegionsInAssembly(init.assembly);
|
|
119
|
-
}
|
|
103
|
+
// navToLocString waits for the assembly itself. A spec that listed its
|
|
104
|
+
// tracks gets those tracks; one that only named a gene gets the track the
|
|
105
|
+
// gene was found in, as the search box would
|
|
106
|
+
await view.navToLocString(init.loc, init.assembly, init.grow, {
|
|
107
|
+
showHitTrack: init.showHitTrack ?? asArray(init.tracks).length === 0,
|
|
108
|
+
});
|
|
120
109
|
}
|
|
121
110
|
catch (e) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
111
|
+
reportInitNavError(session, e);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function navigateInit(self, session, init) {
|
|
115
|
+
if (init.loc) {
|
|
116
|
+
await navToInitLoc(self, session, { ...init, loc: init.loc });
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
try {
|
|
120
|
+
navigateWithoutLoc(self, session, init);
|
|
126
121
|
}
|
|
127
|
-
|
|
128
|
-
session
|
|
122
|
+
catch (e) {
|
|
123
|
+
reportInitNavError(session, e);
|
|
129
124
|
}
|
|
130
125
|
}
|
|
131
126
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
127
|
+
function navigateWithoutLoc(self, session, init) {
|
|
128
|
+
if (init.displayedRegionNames?.length) {
|
|
129
|
+
// an explicit region list is a navigation request just like `loc`, so it
|
|
130
|
+
// applies even when regions already exist (URL params layered onto a
|
|
131
|
+
// defaultSession that already navigated)
|
|
132
|
+
//
|
|
133
|
+
// `?.length`, not the bare key: an empty array is truthy, so a spec
|
|
134
|
+
// writing `displayedRegionNames: []` took this path with nothing to name
|
|
135
|
+
// and got told its list of no names had matched no regions. That is why
|
|
136
|
+
// LinearSyntenyView's doSubmit omits the key rather than passing [].
|
|
137
|
+
showNamedRegions(self, session, init.assembly, init.displayedRegionNames);
|
|
138
|
+
}
|
|
139
|
+
else if (!self.hasDisplayedRegions) {
|
|
140
|
+
// a highlight-only init (nothing to navigate to) must not clobber a
|
|
141
|
+
// defaultSession's existing navigation, so only auto-navigate when
|
|
142
|
+
// nothing is shown yet
|
|
143
|
+
self.showAllRegionsInAssembly(init.assembly);
|
|
139
144
|
}
|
|
140
145
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
/**
|
|
147
|
+
* Open an init blob's `tracks`, one at a time, a bare string taken as a list
|
|
148
|
+
* of one. launchTrack funnels through showTrackGeneric, which surfaces any
|
|
149
|
+
* failure (unresolved id, bad config) as its own snackbar, and loads the
|
|
150
|
+
* picked display's state model first when it is registered lazily. Exported
|
|
151
|
+
* for the LinearSyntenyView rows, as `navToInitLoc` is.
|
|
152
|
+
*/
|
|
153
|
+
export async function showInitTracks(self, init) {
|
|
154
|
+
const tracks = asArray(init.tracks);
|
|
155
|
+
if (tracks.length) {
|
|
156
|
+
// lazy, so the modifier and slot tables stay out of every page's first load
|
|
157
|
+
const { launchTrackInit } = await import('../TrackModifiers/index.js');
|
|
158
|
+
for (const t of tracks) {
|
|
159
|
+
await launchTrackInit(self, t);
|
|
150
160
|
}
|
|
151
161
|
}
|
|
152
162
|
}
|
|
153
163
|
/**
|
|
154
|
-
*
|
|
155
|
-
* or wire-format object and reporting a bad one without taking out
|
|
156
|
-
* siblings.
|
|
164
|
+
* Add an init blob's `highlight` entries to the session, coercing each
|
|
165
|
+
* locstring or wire-format object and reporting a bad one without taking out
|
|
166
|
+
* its siblings. An entry without an assembly takes the launch's.
|
|
157
167
|
*
|
|
158
168
|
* Exported because a LinearSyntenyView row is a LinearGenomeView that does NOT
|
|
159
169
|
* go through this file's init autorun — the synteny view builds each row's
|
|
160
170
|
* snapshot and navigates it itself — so the row's own `highlight` had nowhere
|
|
161
171
|
* to be applied and was dropped in silence.
|
|
162
172
|
*/
|
|
163
|
-
export function applyInitHighlights(
|
|
173
|
+
export function applyInitHighlights(session, init) {
|
|
164
174
|
for (const h of asArray(init.highlight)) {
|
|
165
175
|
// parseLocString throws on an unknown refName or a malformed locstring, and
|
|
166
176
|
// one bad entry must take out neither its siblings nor the rest of init
|
|
167
177
|
try {
|
|
168
|
-
const highlight = coerceHighlight(h, init.assembly, refName => session.assemblyManager.isValidRefName(refName, init.assembly));
|
|
178
|
+
const highlight = coerceHighlight(h, init.assembly, (refName, asm) => session.assemblyManager.isValidRefName(refName, asm ?? init.assembly));
|
|
169
179
|
if (highlight) {
|
|
170
|
-
|
|
180
|
+
session.addHighlight(highlight);
|
|
171
181
|
}
|
|
172
182
|
}
|
|
173
183
|
catch (e) {
|
|
@@ -176,8 +186,14 @@ export function applyInitHighlights(self, session, init) {
|
|
|
176
186
|
}
|
|
177
187
|
}
|
|
178
188
|
}
|
|
179
|
-
async function applyInit(self,
|
|
189
|
+
async function applyInit(self, pending, { superseded }) {
|
|
180
190
|
const session = getSession(self);
|
|
191
|
+
// A re-launch of a view already showing an assembly need not restate it:
|
|
192
|
+
// jb.setSession writes `loc` and track entries beside the built state, and
|
|
193
|
+
// the readiness gate resolved the same default before letting this run.
|
|
194
|
+
const init = pending.assembly
|
|
195
|
+
? pending
|
|
196
|
+
: { ...pending, assembly: self.launchAssemblyName ?? pending.assembly };
|
|
181
197
|
if (init.tracklist) {
|
|
182
198
|
await openTracklist(self, session, superseded);
|
|
183
199
|
}
|
|
@@ -186,9 +202,8 @@ async function applyInit(self, init, { superseded }) {
|
|
|
186
202
|
// reading or mutating a detached node throws) and also covers the case
|
|
187
203
|
// isAlive misses — a newer setLaunch landed mid-apply, so the rest of *this*
|
|
188
204
|
// blob is stale. Finishing it anyway appends its tracks and highlights under
|
|
189
|
-
// the one that replaced it
|
|
190
|
-
//
|
|
191
|
-
// the newer init next either way.
|
|
205
|
+
// the one that replaced it. The drain loop applies the newer init next either
|
|
206
|
+
// way.
|
|
192
207
|
//
|
|
193
208
|
// Checked after each await rather than once at the top, and as early returns
|
|
194
209
|
// rather than nesting, so a step added later takes a guard beside it instead
|
|
@@ -207,8 +222,38 @@ async function applyInit(self, init, { superseded }) {
|
|
|
207
222
|
if (init.nav !== undefined) {
|
|
208
223
|
self.setHideHeader(!init.nav);
|
|
209
224
|
}
|
|
210
|
-
|
|
211
|
-
|
|
225
|
+
applyInitHighlights(session, init);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* The init autorun below waits for the view's width, which exists only once its
|
|
229
|
+
* component has rendered, and then loads the assembly and launches the tracks
|
|
230
|
+
* one at a time. The assembly and the display code each pending track needs can
|
|
231
|
+
* start loading now, all at once.
|
|
232
|
+
*/
|
|
233
|
+
function setupInitWarmup(self) {
|
|
234
|
+
addDisposer(self, autorun(function initWarmupAutorun() {
|
|
235
|
+
const pending = self.pendingLaunch;
|
|
236
|
+
const tracks = asArray(pending?.tracks);
|
|
237
|
+
// eslint-disable-next-line no-restricted-syntax -- EFFECT INPUT: the assembly and track configs only feed the loads started here; the pending launch is the key
|
|
238
|
+
untracked(() => {
|
|
239
|
+
const assembly = pending?.assembly ?? self.launchAssemblyName;
|
|
240
|
+
if (pending && assembly) {
|
|
241
|
+
try {
|
|
242
|
+
getSession(self)
|
|
243
|
+
.assemblyManager.waitForAssembly(assembly)
|
|
244
|
+
.catch(() => { });
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
// navigation reports an assembly that cannot load
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
for (const t of tracks) {
|
|
251
|
+
const { trackId, displaySnapshot, modifiers } = normalizeTrackInit(t);
|
|
252
|
+
const type = requestedDisplayType(modifiers);
|
|
253
|
+
warmTrackDisplayGeneric(self, trackId, type ? { ...displaySnapshot, type } : displaySnapshot, assembly);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}, { name: 'LGVInitWarmup' }));
|
|
212
257
|
}
|
|
213
258
|
/**
|
|
214
259
|
* Autorun that handles the init state - navigating to initial location,
|
|
@@ -219,9 +264,8 @@ export function setupInitAutorun(self) {
|
|
|
219
264
|
name: 'LGVInit',
|
|
220
265
|
// `init` is what makes hasSomethingToShow / awaitingInitNavigation report
|
|
221
266
|
// "loading"
|
|
222
|
-
// until navigation populates displayedRegions, so
|
|
223
|
-
//
|
|
224
|
-
// import form mid-load.
|
|
267
|
+
// until navigation populates displayedRegions, so clearing `init` up front
|
|
268
|
+
// would flash the import form mid-load.
|
|
225
269
|
ready: () => self.initialized,
|
|
226
270
|
// No pre-materialization phase to protect: the view is its own single row,
|
|
227
271
|
// and `error` already derives a failed init assembly declaratively (via
|
|
@@ -244,32 +288,11 @@ function setupCoarseDynamicBlocksAutorun(self) {
|
|
|
244
288
|
name: 'LGVCoarseDynamicBlocks',
|
|
245
289
|
}));
|
|
246
290
|
}
|
|
247
|
-
/**
|
|
248
|
-
* Autorun that syncs view settings to localStorage
|
|
249
|
-
*/
|
|
250
|
-
function setupLocalStorageAutorun(self) {
|
|
251
|
-
addDisposer(self, autorun(function localStorageAutorun() {
|
|
252
|
-
const s = (s) => JSON.stringify(s);
|
|
253
|
-
const { showCytobands, showCenterLine, colorByCDS, showAminoAcids, showTrackOutlines, trackLabels, } = self;
|
|
254
|
-
localStorageSetItem('lgv-showCytobands', s(showCytobands));
|
|
255
|
-
localStorageSetItem('lgv-showCenterLine', s(showCenterLine));
|
|
256
|
-
localStorageSetItem('lgv-colorByCDS', s(colorByCDS));
|
|
257
|
-
localStorageSetItem('lgv-showAminoAcids', s(showAminoAcids));
|
|
258
|
-
localStorageSetItem('lgv-showTrackOutlines', s(showTrackOutlines));
|
|
259
|
-
// skip writing the empty default — otherwise reads later round-trip to
|
|
260
|
-
// '' instead of null, hiding the config-fallback path
|
|
261
|
-
if (trackLabels) {
|
|
262
|
-
localStorageSetItem('lgv-trackLabels', trackLabels);
|
|
263
|
-
}
|
|
264
|
-
}, {
|
|
265
|
-
name: 'LGVLocalStorage',
|
|
266
|
-
}));
|
|
267
|
-
}
|
|
268
291
|
/**
|
|
269
292
|
* Sets up all afterAttach autoruns for the LinearGenomeView
|
|
270
293
|
*/
|
|
271
294
|
export function doAfterAttach(self) {
|
|
295
|
+
setupInitWarmup(self);
|
|
272
296
|
setupInitAutorun(self);
|
|
273
297
|
setupCoarseDynamicBlocksAutorun(self);
|
|
274
|
-
setupLocalStorageAutorun(self);
|
|
275
298
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #config LinearGenomeViewAxis
|
|
3
|
+
* #category view
|
|
4
|
+
* The linear genome view's `axis`: the ruler along its top, whose ticks
|
|
5
|
+
* `grid` carries down through the tracks.
|
|
6
|
+
*
|
|
7
|
+
* #example
|
|
8
|
+
* ```js
|
|
9
|
+
* { type: 'LinearGenomeView', axis: { grid: false } }
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export declare const linearGenomeViewAxisConfigSchema: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
13
|
+
readonly grid: {
|
|
14
|
+
type: "boolean";
|
|
15
|
+
} & {
|
|
16
|
+
defaultValue: boolean;
|
|
17
|
+
};
|
|
18
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, undefined, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>, any>>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ConfigurationSchema } from '@jbrowse/core/configuration';
|
|
2
|
+
/**
|
|
3
|
+
* #config LinearGenomeViewAxis
|
|
4
|
+
* #category view
|
|
5
|
+
* The linear genome view's `axis`: the ruler along its top, whose ticks
|
|
6
|
+
* `grid` carries down through the tracks.
|
|
7
|
+
*
|
|
8
|
+
* #example
|
|
9
|
+
* ```js
|
|
10
|
+
* { type: 'LinearGenomeView', axis: { grid: false } }
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export const linearGenomeViewAxisConfigSchema = ConfigurationSchema('LinearGenomeViewAxis', {
|
|
14
|
+
/**
|
|
15
|
+
* #slot
|
|
16
|
+
*/
|
|
17
|
+
grid: {
|
|
18
|
+
type: 'boolean',
|
|
19
|
+
defaultValue: true,
|
|
20
|
+
description: "carry the ruler's ticks down through the tracks",
|
|
21
|
+
},
|
|
22
|
+
}, { closed: true });
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { clamp } from '@jbrowse/core/util';
|
|
3
|
+
import { basePaintedAt, bpIntoPaintedBase, } from '@jbrowse/core/util/Base1DUtils';
|
|
2
4
|
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
3
5
|
import CloseIcon from '@mui/icons-material/Close';
|
|
4
6
|
import { IconButton, Tooltip } from '@mui/material';
|
|
@@ -18,6 +20,7 @@ const useStyles = makeStyles()(theme => ({
|
|
|
18
20
|
centerLineText: {
|
|
19
21
|
position: 'absolute',
|
|
20
22
|
left: 0,
|
|
23
|
+
bottom: 0,
|
|
21
24
|
display: 'flex',
|
|
22
25
|
alignItems: 'center',
|
|
23
26
|
gap: 2,
|
|
@@ -31,16 +34,18 @@ const useStyles = makeStyles()(theme => ({
|
|
|
31
34
|
},
|
|
32
35
|
}));
|
|
33
36
|
const CenterLine = observer(function CenterLine({ model }) {
|
|
34
|
-
const { bpPerPx, centerLineInfo,
|
|
37
|
+
const { bpPerPx, centerLineInfo, tracks, width } = model;
|
|
35
38
|
const { classes } = useStyles();
|
|
36
|
-
const
|
|
39
|
+
const left = centerLineInfo && bpPerPx < 1
|
|
40
|
+
? width / 2 -
|
|
41
|
+
bpIntoPaintedBase(centerLineInfo, centerLineInfo.offset) / bpPerPx
|
|
42
|
+
: width / 2;
|
|
37
43
|
return tracks.length ? (_jsx("div", { "data-testid": "centerline_container", className: classes.centerLineContainer, role: "presentation", style: {
|
|
38
|
-
transform: `translateX(${
|
|
44
|
+
transform: `translateX(${left}px)`,
|
|
39
45
|
width: Math.max(1 / bpPerPx, 1),
|
|
40
46
|
}, children: centerLineInfo && (_jsxs("div", { "data-testid": "centerline_text", className: classes.centerLineText, role: "presentation", style: {
|
|
41
47
|
transform: `translateX(${Math.max(1 / bpPerPx, 1) + 5}px)`,
|
|
42
|
-
|
|
43
|
-
}, children: [centerLineInfo.refName, ":", ' ', Math.max(Math.round(centerLineInfo.offset) + 1, 0).toLocaleString('en-US'), _jsx(Tooltip, { title: "Hide center line", children: _jsx(IconButton, { className: classes.dismiss, onClick: () => {
|
|
48
|
+
}, children: [centerLineInfo.refName, ":", ' ', (clamp(basePaintedAt(centerLineInfo, centerLineInfo.offset), centerLineInfo.start, centerLineInfo.end - 1) + 1).toLocaleString('en-US'), _jsx(Tooltip, { title: "Hide center line", children: _jsx(IconButton, { className: classes.dismiss, onClick: () => {
|
|
44
49
|
model.setShowCenterLine(false);
|
|
45
50
|
}, children: _jsx(CloseIcon, { fontSize: "inherit" }) }) })] })) })) : null;
|
|
46
51
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { getSession } from '@jbrowse/core/util';
|
|
2
|
+
import { getSession, isSessionModelWithWidgets } from '@jbrowse/core/util';
|
|
3
3
|
import { observer } from 'mobx-react';
|
|
4
4
|
import HoverPositionHighlight from './HoverPositionHighlight.js';
|
|
5
5
|
// Draws a crosshair for every session widget "connected" to this view that is
|
|
@@ -9,8 +9,7 @@ import HoverPositionHighlight from './HoverPositionHighlight.js';
|
|
|
9
9
|
// hovered interval or undefined; the widget iteration and drawing are shared.
|
|
10
10
|
const ConnectedHoverHighlight = observer(function ConnectedHoverHighlight({ model, getPosition, }) {
|
|
11
11
|
const session = getSession(model);
|
|
12
|
-
|
|
13
|
-
return !widgets ? null : (_jsx(_Fragment, { children: [...widgets].flatMap(([id, widget]) => {
|
|
12
|
+
return !isSessionModelWithWidgets(session) ? null : (_jsx(_Fragment, { children: [...session.widgets].flatMap(([id, widget]) => {
|
|
14
13
|
const position = getPosition(widget, model.id);
|
|
15
14
|
return position
|
|
16
15
|
? [
|
|
@@ -64,15 +64,14 @@ const CrisprGuidePanel = observer(function CrisprGuidePanel({ model, handleClose
|
|
|
64
64
|
const [pamLocation, setPamLocation] = useState(DEFAULT_PRESET.pamLocation);
|
|
65
65
|
const [guideLengthStr, setGuideLengthStr] = useState(String(DEFAULT_PRESET.guideLength));
|
|
66
66
|
const [cutOffsetStr, setCutOffsetStr] = useState(String(DEFAULT_PRESET.cutOffset));
|
|
67
|
-
// presets carry this; Custom is treated as a blunt cutter, so the panel does
|
|
68
|
-
// not grow a second offset field for a case config can express directly
|
|
69
|
-
const [cutOffsetBottom, setCutOffsetBottom] = useState(DEFAULT_PRESET.cutOffsetBottom);
|
|
70
67
|
const [searchForward, setSearchForward] = useState(true);
|
|
71
68
|
const [searchReverse, setSearchReverse] = useState(true);
|
|
72
69
|
const [filterQuality, setFilterQuality] = useState(false);
|
|
73
70
|
// `Number('')` is 0, so an emptied field would otherwise read as a valid zero
|
|
74
71
|
const guideLength = toBpCount(guideLengthStr);
|
|
75
72
|
const cutOffset = toBpCount(cutOffsetStr);
|
|
73
|
+
// Custom is a blunt cutter: config can express a staggered one directly
|
|
74
|
+
const cutOffsetBottom = ENZYME_PRESETS[enzyme]?.cutOffsetBottom ?? cutOffset;
|
|
76
75
|
// both are bp counts used for fixed-length string slicing, so a fractional
|
|
77
76
|
// value would silently truncate and skew the placement
|
|
78
77
|
const guideLengthValid = Number.isInteger(guideLength) && guideLength > 0;
|
|
@@ -101,7 +100,6 @@ const CrisprGuidePanel = observer(function CrisprGuidePanel({ model, handleClose
|
|
|
101
100
|
setPamLocation(preset.pamLocation);
|
|
102
101
|
setGuideLengthStr(String(preset.guideLength));
|
|
103
102
|
setCutOffsetStr(String(preset.cutOffset));
|
|
104
|
-
setCutOffsetBottom(preset.cutOffsetBottom);
|
|
105
103
|
}
|
|
106
104
|
}
|
|
107
105
|
function handleSubmit() {
|
|
@@ -116,7 +114,7 @@ const CrisprGuidePanel = observer(function CrisprGuidePanel({ model, handleClose
|
|
|
116
114
|
guideLength,
|
|
117
115
|
pamLocation,
|
|
118
116
|
cutOffset,
|
|
119
|
-
cutOffsetBottom
|
|
117
|
+
cutOffsetBottom,
|
|
120
118
|
searchForward,
|
|
121
119
|
searchReverse,
|
|
122
120
|
excludePolyT: filterQuality,
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import type { TrackLabelMode } from '../types.ts';
|
|
1
|
+
import type { TrackLabelMode, ViewTrackLabelMode } from '../types.ts';
|
|
2
2
|
import type { BaseExportSvgOptions } from '@jbrowse/core/ui';
|
|
3
|
-
|
|
3
|
+
declare const ExportSvgDialog: ({ model, handleClose, chooseWidth, }: {
|
|
4
4
|
model: {
|
|
5
|
+
width?: number;
|
|
6
|
+
effectiveTrackLabels?: ViewTrackLabelMode;
|
|
5
7
|
exportSvg(opts: BaseExportSvgOptions & {
|
|
6
8
|
trackLabels: TrackLabelMode;
|
|
7
9
|
showGridlines: boolean;
|
|
8
|
-
|
|
10
|
+
viewWidth?: number;
|
|
11
|
+
}): Promise<unknown>;
|
|
9
12
|
};
|
|
10
13
|
handleClose: () => void;
|
|
11
|
-
|
|
14
|
+
chooseWidth?: boolean;
|
|
15
|
+
}) => import("react").JSX.Element;
|
|
16
|
+
export default ExportSvgDialog;
|
|
@@ -1,14 +1,51 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
2
3
|
import { BaseExportSvgDialog, LabeledCheckbox, useExportSvgPreference, } from '@jbrowse/core/ui';
|
|
3
4
|
import { MenuItem, TextField } from '@mui/material';
|
|
5
|
+
import { observer } from 'mobx-react';
|
|
6
|
+
const MIN_WIDTH = 100;
|
|
7
|
+
const MAX_WIDTH = 10000;
|
|
8
|
+
const TRACK_LABEL_MODES = [
|
|
9
|
+
{ mode: 'offset', label: 'Offset' },
|
|
10
|
+
{ mode: 'overlapping', label: 'Overlapping' },
|
|
11
|
+
{ mode: 'left', label: 'Left' },
|
|
12
|
+
{ mode: 'hidden', label: 'Hidden' },
|
|
13
|
+
];
|
|
4
14
|
// Shared track-label + gridlines export dialog. Used by LGV, linear-synteny and
|
|
5
|
-
// breakpoint-split views (their lazyDialogs re-export this).
|
|
6
|
-
|
|
7
|
-
|
|
15
|
+
// breakpoint-split views (their lazyDialogs re-export this). Only a caller
|
|
16
|
+
// passing `chooseWidth` offers the width field, whose model lays itself out at
|
|
17
|
+
// `viewWidth`.
|
|
18
|
+
const ExportSvgDialog = observer(function ExportSvgDialog({ model, handleClose, chooseWidth, }) {
|
|
19
|
+
// The view's own setting is already an export mode, so a user who has not
|
|
20
|
+
// picked one here gets the figure their screen shows. The key is `-mode`
|
|
21
|
+
// because the modes were once spelled `overlay`/`none`, and a stored one of
|
|
22
|
+
// those would select nothing in the dropdown.
|
|
23
|
+
const [trackLabels, setTrackLabels] = useExportSvgPreference('tracklabels-mode', model.effectiveTrackLabels ?? 'offset');
|
|
8
24
|
const [showGridlines, setShowGridlines] = useExportSvgPreference('gridlines', false);
|
|
9
|
-
|
|
25
|
+
// not a stored preference: a remembered width would drift from the screen's
|
|
26
|
+
const [widthText, setWidthText] = useState(() => String(Math.round(model.width ?? 0)));
|
|
27
|
+
const typedWidth = Number(widthText);
|
|
28
|
+
const widthValid = Number.isInteger(typedWidth) &&
|
|
29
|
+
typedWidth >= MIN_WIDTH &&
|
|
30
|
+
typedWidth <= MAX_WIDTH;
|
|
31
|
+
return (_jsxs(BaseExportSvgDialog, { model: model, handleClose: handleClose, exportSvg: opts => model.exportSvg({
|
|
32
|
+
...opts,
|
|
33
|
+
trackLabels,
|
|
34
|
+
showGridlines,
|
|
35
|
+
viewWidth: chooseWidth && widthValid ? typedWidth : undefined,
|
|
36
|
+
}), checkboxes: _jsx(LabeledCheckbox, { checked: showGridlines, onChange: val => {
|
|
10
37
|
setShowGridlines(val);
|
|
11
|
-
}, label: "Show gridlines" }), children:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
38
|
+
}, label: "Show gridlines" }), children: [_jsx(TextField, { select: true, label: "Track label positioning", variant: "outlined", style: { minWidth: 200 }, value: trackLabels, onChange: event => {
|
|
39
|
+
const picked = TRACK_LABEL_MODES.find(m => m.mode === event.target.value);
|
|
40
|
+
if (picked) {
|
|
41
|
+
setTrackLabels(picked.mode);
|
|
42
|
+
}
|
|
43
|
+
}, children: TRACK_LABEL_MODES.map(({ mode, label }) => (_jsx(MenuItem, { value: mode, children: label }, mode))) }), chooseWidth ? (_jsx(TextField, { label: "Width (px)", variant: "outlined", type: "number", value: widthText, error: !widthValid, helperText: widthValid
|
|
44
|
+
? 'Lays the tracks out as a window this wide would; text keeps its size'
|
|
45
|
+
: `A whole number from ${MIN_WIDTH} to ${MAX_WIDTH}`, slotProps: {
|
|
46
|
+
htmlInput: { min: MIN_WIDTH, max: MAX_WIDTH, step: 1 },
|
|
47
|
+
}, onChange: event => {
|
|
48
|
+
setWidthText(event.target.value);
|
|
49
|
+
} })) : null] }));
|
|
50
|
+
});
|
|
51
|
+
export default ExportSvgDialog;
|
|
@@ -32,12 +32,12 @@ const GetSequenceDialog = observer(function GetSequenceDialog({ model, regions,
|
|
|
32
32
|
// a selection this dialog will happily take is a whole chromosome of
|
|
33
33
|
// sequence, so both handles are forwarded: closing the dialog stops the
|
|
34
34
|
// read rather than leaving the worker on it, and the wait is named
|
|
35
|
-
async (_name, _locs,
|
|
35
|
+
async (_name, _locs, signal, statusCallback) => {
|
|
36
36
|
if (regions.length === 0) {
|
|
37
37
|
throw new Error('Selected region is out of bounds');
|
|
38
38
|
}
|
|
39
39
|
const chunks = await fetchSequence(model, regions, {
|
|
40
|
-
|
|
40
|
+
signal,
|
|
41
41
|
statusCallback,
|
|
42
42
|
});
|
|
43
43
|
// validate here (in the async path) so a length mismatch surfaces via the
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
|
|
2
3
|
import ScrollZoomToggle from '@jbrowse/core/ui/ScrollZoomToggle';
|
|
4
|
+
import { getSession } from '@jbrowse/core/util';
|
|
3
5
|
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
4
6
|
import useMeasure from '@jbrowse/core/util/useMeasure';
|
|
7
|
+
import MenuIcon from '@mui/icons-material/Menu';
|
|
8
|
+
import { useMediaQuery } from '@mui/material';
|
|
5
9
|
import { observer } from 'mobx-react';
|
|
6
10
|
import { HEADER_BAR_HEIGHT } from '../consts.js';
|
|
7
11
|
import { headerFit } from '../headerFit.js';
|
|
@@ -35,12 +39,18 @@ const Controls = observer(function Controls({ model, }) {
|
|
|
35
39
|
// row
|
|
36
40
|
const [ref, { width }] = useMeasure('width');
|
|
37
41
|
const highlighted = highlightedDisplays(model);
|
|
42
|
+
const viewMenu = getSession(model).viewTitleBars === false;
|
|
43
|
+
const touch = useMediaQuery('(pointer: coarse)', {
|
|
44
|
+
noSsr: true,
|
|
45
|
+
});
|
|
38
46
|
const fit = headerFit({
|
|
39
47
|
width,
|
|
40
48
|
searchBoxPx: searchBoxWidth(model.coarseVisibleLocStrings),
|
|
41
49
|
clearHighlight: highlighted.length > 0,
|
|
50
|
+
viewMenu,
|
|
51
|
+
touch,
|
|
42
52
|
});
|
|
43
|
-
return (_jsxs("div", { className: classes.headerBar, ref: ref, children: [_jsx(HeaderTrackSelectorButton, { model: model, indent: fit.trackSelectorIndent }), _jsx(ScrollZoomToggle, { model: model, iconOnly: !fit.scrollZoomLabel }), _jsx("div", { className: classes.spacer }), _jsx(HeaderPanControls, { model: model, compact: !fit.panButtonSpacing }), _jsx(SearchBox, { model: model }), _jsx(HeaderClearHighlightButton, { highlighted: highlighted }), fit.regionWidth ? _jsx(HeaderRegionWidth, { model: model }) : null, _jsx(HeaderZoomControls, { model: model, showSlider: fit.zoomSlider }), _jsx("div", { className: classes.spacer })] }));
|
|
53
|
+
return (_jsxs("div", { className: classes.headerBar, ref: ref, children: [viewMenu ? (_jsx(CascadingMenuButton, { tooltip: "View menu", menuItems: () => model.menuItems(), "data-testid": "view_menu_icon", children: _jsx(MenuIcon, {}) })) : null, _jsx(HeaderTrackSelectorButton, { model: model, indent: fit.trackSelectorIndent }), touch ? null : (_jsx(ScrollZoomToggle, { model: model, iconOnly: !fit.scrollZoomLabel })), _jsx("div", { className: classes.spacer }), touch ? null : (_jsx(HeaderPanControls, { model: model, compact: !fit.panButtonSpacing })), _jsx(SearchBox, { model: model }), _jsx(HeaderClearHighlightButton, { highlighted: highlighted }), fit.regionWidth ? _jsx(HeaderRegionWidth, { model: model }) : null, _jsx(HeaderZoomControls, { model: model, showSlider: fit.zoomSlider }), _jsx("div", { className: classes.spacer })] }));
|
|
44
54
|
});
|
|
45
55
|
const LinearGenomeViewHeader = observer(function LinearGenomeViewHeader({ model, }) {
|
|
46
56
|
const { hideHeader, hideHeaderOverview } = model;
|