@jbrowse/plugin-linear-genome-view 5.0.0-beta.8 → 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
|
@@ -40,85 +40,177 @@ import type PluginManager from '@jbrowse/core/PluginManager';
|
|
|
40
40
|
* }
|
|
41
41
|
* ```
|
|
42
42
|
*/
|
|
43
|
-
declare const configSchema: (pluginManager: PluginManager) => import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
43
|
+
declare const configSchema: (pluginManager: PluginManager) => import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
44
|
+
adapter: import("@jbrowse/mobx-state-tree").IAnyType;
|
|
45
|
+
assemblyNames: {
|
|
46
|
+
type: "stringArray";
|
|
47
|
+
} & {
|
|
48
|
+
defaultValue: unknown;
|
|
49
|
+
};
|
|
50
|
+
category: {
|
|
51
|
+
type: "stringArray";
|
|
52
|
+
} & {
|
|
53
|
+
defaultValue: unknown;
|
|
54
|
+
};
|
|
55
|
+
description: {
|
|
56
|
+
type: "string";
|
|
57
|
+
} & {
|
|
58
|
+
defaultValue: string;
|
|
59
|
+
};
|
|
60
|
+
displays: import("@jbrowse/mobx-state-tree").IArrayType<import("@jbrowse/mobx-state-tree").IAnyType>;
|
|
61
|
+
formatAbout: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
62
|
+
readonly config: {
|
|
63
|
+
type: "frozen";
|
|
64
|
+
} & {
|
|
65
|
+
defaultValue: unknown;
|
|
66
|
+
};
|
|
67
|
+
readonly hideUris: {
|
|
68
|
+
type: "boolean";
|
|
69
|
+
} & {
|
|
70
|
+
defaultValue: boolean;
|
|
71
|
+
};
|
|
72
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, undefined, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>>>;
|
|
73
|
+
formatDetails: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
74
|
+
readonly feature: {
|
|
75
|
+
type: "frozen";
|
|
76
|
+
} & {
|
|
77
|
+
defaultValue: unknown;
|
|
78
|
+
};
|
|
79
|
+
readonly subfeatures: {
|
|
80
|
+
type: "frozen";
|
|
81
|
+
} & {
|
|
82
|
+
defaultValue: unknown;
|
|
83
|
+
};
|
|
84
|
+
readonly depth: {
|
|
85
|
+
type: "maybeNumber";
|
|
86
|
+
};
|
|
87
|
+
readonly maxDepth: {
|
|
88
|
+
type: "maybeNumber";
|
|
89
|
+
};
|
|
90
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, undefined, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>>>;
|
|
91
|
+
metadata: {
|
|
92
|
+
type: "frozen";
|
|
93
|
+
} & {
|
|
94
|
+
defaultValue: unknown;
|
|
95
|
+
};
|
|
96
|
+
name: {
|
|
97
|
+
type: "string";
|
|
98
|
+
} & {
|
|
99
|
+
defaultValue: string;
|
|
100
|
+
};
|
|
101
|
+
textSearching: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
102
|
+
readonly indexingAttributes: {
|
|
103
|
+
type: "stringArray";
|
|
104
|
+
} & {
|
|
105
|
+
defaultValue: unknown;
|
|
106
|
+
};
|
|
107
|
+
readonly indexingFeatureTypesToExclude: {
|
|
108
|
+
type: "stringArray";
|
|
109
|
+
} & {
|
|
110
|
+
defaultValue: unknown;
|
|
111
|
+
};
|
|
112
|
+
readonly indexingFeatureTypesToInclude: {
|
|
113
|
+
type: "stringArray";
|
|
114
|
+
} & {
|
|
115
|
+
defaultValue: unknown;
|
|
116
|
+
};
|
|
117
|
+
readonly textSearchAdapter: import("@jbrowse/mobx-state-tree").ITypeUnion<any, any, any>;
|
|
118
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, undefined, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>>>;
|
|
119
|
+
trackId: {
|
|
120
|
+
type: "string";
|
|
121
|
+
} & {
|
|
122
|
+
defaultValue: string;
|
|
123
|
+
};
|
|
124
|
+
type: {
|
|
125
|
+
type: "string";
|
|
126
|
+
} & {
|
|
127
|
+
defaultValue: string;
|
|
128
|
+
};
|
|
129
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
44
130
|
readonly name: {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
131
|
+
type: "string";
|
|
132
|
+
} & {
|
|
133
|
+
defaultValue: string;
|
|
48
134
|
};
|
|
49
135
|
readonly assemblyNames: {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
136
|
+
type: "stringArray";
|
|
137
|
+
} & {
|
|
138
|
+
defaultValue: unknown;
|
|
53
139
|
};
|
|
54
140
|
readonly description: {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
141
|
+
type: "string";
|
|
142
|
+
} & {
|
|
143
|
+
defaultValue: string;
|
|
58
144
|
};
|
|
59
145
|
readonly category: {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
146
|
+
type: "stringArray";
|
|
147
|
+
} & {
|
|
148
|
+
defaultValue: unknown;
|
|
63
149
|
};
|
|
64
150
|
readonly metadata: {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
151
|
+
type: "frozen";
|
|
152
|
+
} & {
|
|
153
|
+
defaultValue: unknown;
|
|
68
154
|
};
|
|
69
155
|
readonly adapter: import("@jbrowse/mobx-state-tree").IAnyType;
|
|
70
156
|
readonly textSearching: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
71
157
|
readonly indexingAttributes: {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
158
|
+
type: "stringArray";
|
|
159
|
+
} & {
|
|
160
|
+
defaultValue: unknown;
|
|
75
161
|
};
|
|
76
162
|
readonly indexingFeatureTypesToExclude: {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
163
|
+
type: "stringArray";
|
|
164
|
+
} & {
|
|
165
|
+
defaultValue: unknown;
|
|
80
166
|
};
|
|
81
167
|
readonly indexingFeatureTypesToInclude: {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
168
|
+
type: "stringArray";
|
|
169
|
+
} & {
|
|
170
|
+
defaultValue: unknown;
|
|
85
171
|
};
|
|
86
|
-
readonly textSearchAdapter: import("@jbrowse/mobx-state-tree").
|
|
87
|
-
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined
|
|
172
|
+
readonly textSearchAdapter: import("@jbrowse/mobx-state-tree").ITypeUnion<any, any, any>;
|
|
173
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, undefined, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>>>;
|
|
88
174
|
readonly displays: import("@jbrowse/mobx-state-tree").IArrayType<import("@jbrowse/mobx-state-tree").IAnyType>;
|
|
89
175
|
readonly formatDetails: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
90
176
|
readonly feature: {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
readonly contextVariable: ["feature"];
|
|
177
|
+
type: "frozen";
|
|
178
|
+
} & {
|
|
179
|
+
defaultValue: unknown;
|
|
95
180
|
};
|
|
96
181
|
readonly subfeatures: {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
readonly contextVariable: ["feature"];
|
|
182
|
+
type: "frozen";
|
|
183
|
+
} & {
|
|
184
|
+
defaultValue: unknown;
|
|
101
185
|
};
|
|
102
186
|
readonly depth: {
|
|
103
|
-
|
|
104
|
-
readonly description: "levels of subfeature the formatDetails.subfeatures callback runs on, default 2";
|
|
187
|
+
type: "maybeNumber";
|
|
105
188
|
};
|
|
106
189
|
readonly maxDepth: {
|
|
107
|
-
|
|
108
|
-
readonly description: "hide subfeatures nested deeper than this, default no limit";
|
|
190
|
+
type: "maybeNumber";
|
|
109
191
|
};
|
|
110
|
-
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined
|
|
192
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, undefined, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>>>;
|
|
111
193
|
readonly formatAbout: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
112
194
|
readonly config: {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
readonly contextVariable: ["config"];
|
|
195
|
+
type: "frozen";
|
|
196
|
+
} & {
|
|
197
|
+
defaultValue: unknown;
|
|
117
198
|
};
|
|
118
199
|
readonly hideUris: {
|
|
119
|
-
|
|
120
|
-
|
|
200
|
+
type: "boolean";
|
|
201
|
+
} & {
|
|
202
|
+
defaultValue: boolean;
|
|
121
203
|
};
|
|
122
|
-
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined
|
|
123
|
-
|
|
204
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, undefined, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>>>;
|
|
205
|
+
type: {
|
|
206
|
+
type: "string";
|
|
207
|
+
} & {
|
|
208
|
+
defaultValue: string;
|
|
209
|
+
};
|
|
210
|
+
trackId: {
|
|
211
|
+
type: "string";
|
|
212
|
+
} & {
|
|
213
|
+
defaultValue: string;
|
|
214
|
+
};
|
|
215
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, "trackId", true, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>>>, "trackId", undefined, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>>>;
|
|
124
216
|
export default configSchema;
|
|
@@ -1,17 +1,17 @@
|
|
|
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
|
-
* or wire-format object and reporting a bad one without taking out
|
|
7
|
-
* siblings.
|
|
5
|
+
* Add an init blob's `highlight` entries to the session, coercing each
|
|
6
|
+
* locstring or wire-format object and reporting a bad one without taking out
|
|
7
|
+
* its siblings. An entry without an assembly takes the launch's.
|
|
8
8
|
*
|
|
9
9
|
* Exported because a LinearSyntenyView row is a LinearGenomeView that does NOT
|
|
10
10
|
* go through this file's init autorun — the synteny view builds each row's
|
|
11
11
|
* snapshot and navigates it itself — so the row's own `highlight` had nowhere
|
|
12
12
|
* to be applied and was dropped in silence.
|
|
13
13
|
*/
|
|
14
|
-
export declare function applyInitHighlights(
|
|
14
|
+
export declare function applyInitHighlights(session: AssemblyHost & NotificationSink & Pick<AbstractSessionModel, 'addHighlight'>, init: Pick<InitState, 'highlight' | 'assembly'>): void;
|
|
15
15
|
/**
|
|
16
16
|
* Autorun that handles the init state - navigating to initial location,
|
|
17
17
|
* showing tracks, etc.
|
|
@@ -5,6 +5,7 @@ import { normalizeTrackInit } from '@jbrowse/core/util/tracks';
|
|
|
5
5
|
import { addDisposer } from '@jbrowse/mobx-state-tree';
|
|
6
6
|
import { autorun, when } from 'mobx';
|
|
7
7
|
import { SearchResultsNotFoundError } from '../searchUtils.js';
|
|
8
|
+
import { installCloseUps } from './closeUps.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
|
|
10
11
|
// looping over it directly walked its characters — one track opened per letter.
|
|
@@ -97,7 +98,7 @@ async function navigateInit(self, session, init) {
|
|
|
97
98
|
// a spec that listed its tracks gets those tracks; one that only named
|
|
98
99
|
// a gene gets the track the gene was found in, as the search box would
|
|
99
100
|
await self.navToLocString(init.loc, init.assembly, init.grow, {
|
|
100
|
-
showHitTrack: asArray(init.tracks).length === 0,
|
|
101
|
+
showHitTrack: init.showHitTrack ?? asArray(init.tracks).length === 0,
|
|
101
102
|
});
|
|
102
103
|
}
|
|
103
104
|
else if (init.displayedRegionNames?.length) {
|
|
@@ -138,36 +139,24 @@ async function showInitTracks(self, init) {
|
|
|
138
139
|
await self.launchTrack(trackId, trackSnapshot, displaySnapshot);
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
|
-
// backfill assemblyName on any session-authored highlights that omitted it so
|
|
142
|
-
// downstream code (bookmark widget grid, addBookmark, etc) doesn't have to keep
|
|
143
|
-
// falling back
|
|
144
|
-
function backfillHighlightAssemblies(self) {
|
|
145
|
-
const fallback = self.assemblyNames[0];
|
|
146
|
-
if (self.highlight.length && fallback) {
|
|
147
|
-
const normalized = self.highlight.map(h => h.assemblyName ? h : { ...h, assemblyName: fallback });
|
|
148
|
-
if (normalized.some((h, i) => h !== self.highlight[i])) {
|
|
149
|
-
self.setHighlight(normalized);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
142
|
/**
|
|
154
|
-
*
|
|
155
|
-
* or wire-format object and reporting a bad one without taking out
|
|
156
|
-
* siblings.
|
|
143
|
+
* Add an init blob's `highlight` entries to the session, coercing each
|
|
144
|
+
* locstring or wire-format object and reporting a bad one without taking out
|
|
145
|
+
* its siblings. An entry without an assembly takes the launch's.
|
|
157
146
|
*
|
|
158
147
|
* Exported because a LinearSyntenyView row is a LinearGenomeView that does NOT
|
|
159
148
|
* go through this file's init autorun — the synteny view builds each row's
|
|
160
149
|
* snapshot and navigates it itself — so the row's own `highlight` had nowhere
|
|
161
150
|
* to be applied and was dropped in silence.
|
|
162
151
|
*/
|
|
163
|
-
export function applyInitHighlights(
|
|
152
|
+
export function applyInitHighlights(session, init) {
|
|
164
153
|
for (const h of asArray(init.highlight)) {
|
|
165
154
|
// parseLocString throws on an unknown refName or a malformed locstring, and
|
|
166
155
|
// one bad entry must take out neither its siblings nor the rest of init
|
|
167
156
|
try {
|
|
168
157
|
const highlight = coerceHighlight(h, init.assembly, refName => session.assemblyManager.isValidRefName(refName, init.assembly));
|
|
169
158
|
if (highlight) {
|
|
170
|
-
|
|
159
|
+
session.addHighlight(highlight);
|
|
171
160
|
}
|
|
172
161
|
}
|
|
173
162
|
catch (e) {
|
|
@@ -176,8 +165,14 @@ export function applyInitHighlights(self, session, init) {
|
|
|
176
165
|
}
|
|
177
166
|
}
|
|
178
167
|
}
|
|
179
|
-
async function applyInit(self,
|
|
168
|
+
async function applyInit(self, pending, { superseded }) {
|
|
180
169
|
const session = getSession(self);
|
|
170
|
+
// A re-launch of a view already showing an assembly need not restate it:
|
|
171
|
+
// jb.setSession writes `loc` and track entries beside the built state, and
|
|
172
|
+
// the readiness gate resolved the same default before letting this run.
|
|
173
|
+
const init = pending.assembly
|
|
174
|
+
? pending
|
|
175
|
+
: { ...pending, assembly: self.launchAssemblyName ?? pending.assembly };
|
|
181
176
|
if (init.tracklist) {
|
|
182
177
|
await openTracklist(self, session, superseded);
|
|
183
178
|
}
|
|
@@ -186,9 +181,8 @@ async function applyInit(self, init, { superseded }) {
|
|
|
186
181
|
// reading or mutating a detached node throws) and also covers the case
|
|
187
182
|
// isAlive misses — a newer setLaunch landed mid-apply, so the rest of *this*
|
|
188
183
|
// 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.
|
|
184
|
+
// the one that replaced it. The drain loop applies the newer init next either
|
|
185
|
+
// way.
|
|
192
186
|
//
|
|
193
187
|
// Checked after each await rather than once at the top, and as early returns
|
|
194
188
|
// rather than nesting, so a step added later takes a guard beside it instead
|
|
@@ -207,8 +201,7 @@ async function applyInit(self, init, { superseded }) {
|
|
|
207
201
|
if (init.nav !== undefined) {
|
|
208
202
|
self.setHideHeader(!init.nav);
|
|
209
203
|
}
|
|
210
|
-
|
|
211
|
-
applyInitHighlights(self, session, init);
|
|
204
|
+
applyInitHighlights(session, init);
|
|
212
205
|
}
|
|
213
206
|
/**
|
|
214
207
|
* Autorun that handles the init state - navigating to initial location,
|
|
@@ -272,4 +265,5 @@ export function doAfterAttach(self) {
|
|
|
272
265
|
setupInitAutorun(self);
|
|
273
266
|
setupCoarseDynamicBlocksAutorun(self);
|
|
274
267
|
setupLocalStorageAutorun(self);
|
|
268
|
+
installCloseUps(self);
|
|
275
269
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { LinearGenomeViewModel } from './model.ts';
|
|
2
|
+
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
3
|
+
import type { Region } from '@jbrowse/core/util/types';
|
|
4
|
+
import type { IAnyModelType, IStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
5
|
+
import type { Theme } from '@mui/material';
|
|
6
|
+
/**
|
|
7
|
+
* A close-up is a LinearGenomeView nested under another one, drawn below the
|
|
8
|
+
* host's tracks and showing a narrower window of the same locus — the header
|
|
9
|
+
* overview's relationship to the view, carried on downward. Its only state of
|
|
10
|
+
* its own is `windowWidthBp` and `tracks`: the host writes its regions, width
|
|
11
|
+
* and left edge, so the close-up always shares the host's centre.
|
|
12
|
+
*
|
|
13
|
+
* Declared by hand rather than as the view model's own instance type: the
|
|
14
|
+
* close-up array is a `types.late` back onto the registered LinearGenomeView,
|
|
15
|
+
* and naming the model's instance type inside its own factory is a circular
|
|
16
|
+
* reference tsc refuses (TS7022). Components outside the factory cast a
|
|
17
|
+
* close-up to `LinearGenomeViewModel`.
|
|
18
|
+
*/
|
|
19
|
+
export interface CloseUp extends IStateTreeNode {
|
|
20
|
+
id: string;
|
|
21
|
+
bpPerPx: number;
|
|
22
|
+
minBpPerPx: number;
|
|
23
|
+
windowWidthBp: number;
|
|
24
|
+
windowStartBp: number;
|
|
25
|
+
displayedRegions: Region[];
|
|
26
|
+
pendingLaunch?: unknown;
|
|
27
|
+
setWidth(width: number): void;
|
|
28
|
+
setDisplayedRegions(regions: Region[]): void;
|
|
29
|
+
setWindowFrame(windowWidthBp: number, windowStartBp: number): void;
|
|
30
|
+
showTrack(trackId: string): unknown;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The stack in page order, each close-up paired with the row above it — the
|
|
34
|
+
* previous close-up, or the host for the first. The array is widest first and
|
|
35
|
+
* every close-up is narrower than its host, so page order and array order are
|
|
36
|
+
* the same thing and each pair reads as one trapezoid: the span this row shows,
|
|
37
|
+
* marked on the row above it.
|
|
38
|
+
*
|
|
39
|
+
* One derivation for the screen and the SVG export, which otherwise agree on
|
|
40
|
+
* the order by having been written twice.
|
|
41
|
+
*/
|
|
42
|
+
export declare function closeUpStackRows<T>(host: T, closeUps: T[]): {
|
|
43
|
+
closeUp: T;
|
|
44
|
+
context: T;
|
|
45
|
+
}[];
|
|
46
|
+
export declare const CLOSE_UP_FRAME_WIDTH = 1;
|
|
47
|
+
/**
|
|
48
|
+
* The outline a close-up and its connector share. Tertiary is the header
|
|
49
|
+
* overview's colour, but the dark themes set it near black, so those take its
|
|
50
|
+
* light shade.
|
|
51
|
+
*/
|
|
52
|
+
export declare function closeUpColor(theme: {
|
|
53
|
+
palette: Pick<Theme['palette'], 'mode' | 'tertiary'>;
|
|
54
|
+
}): string;
|
|
55
|
+
/**
|
|
56
|
+
* The registered view, read at first instantiation rather than the factory's
|
|
57
|
+
* own return value, so a close-up is the LinearGenomeView every
|
|
58
|
+
* `extendViewType` caller has already extended. The name goes through a
|
|
59
|
+
* `string` so the typed `getViewType` overload, whose result names this model's
|
|
60
|
+
* own instance type, is not what tsc resolves here.
|
|
61
|
+
*/
|
|
62
|
+
export declare function closeUpType(pluginManager: PluginManager): IAnyModelType;
|
|
63
|
+
/**
|
|
64
|
+
* What a close-up reaches on the view holding it. Not `LinearGenomeViewModel`:
|
|
65
|
+
* the model's own factory calls this, and a return type naming the model there
|
|
66
|
+
* is the same circular reference as above, which tsc resolves by quietly
|
|
67
|
+
* widening the whole model.
|
|
68
|
+
*/
|
|
69
|
+
export interface CloseUpHost extends IStateTreeNode {
|
|
70
|
+
bpPerPx: number;
|
|
71
|
+
windowWidthBp: number;
|
|
72
|
+
closeUpViews: CloseUp[];
|
|
73
|
+
removeCloseUp(closeUp: CloseUp): void;
|
|
74
|
+
horizontalScroll(distance: number): number;
|
|
75
|
+
slide(viewWidths: number): void;
|
|
76
|
+
scrollToBp(startBp: number): number;
|
|
77
|
+
}
|
|
78
|
+
export declare function closeUpHost(node: IStateTreeNode): CloseUpHost | undefined;
|
|
79
|
+
export declare const CLOSE_UP_PANS: Set<string>;
|
|
80
|
+
export declare const CLOSE_UP_NAVIGATIONS: Set<string>;
|
|
81
|
+
export declare const CLOSE_UP_REPLAYS: Set<string>;
|
|
82
|
+
export declare const CLOSE_UP_OWN: Set<string>;
|
|
83
|
+
export declare function installCloseUps(self: LinearGenomeViewModel): void;
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { addDisposer, addMiddleware, getParent, hasParent, types, } from '@jbrowse/mobx-state-tree';
|
|
2
|
+
import { autorun, untracked } from 'mobx';
|
|
3
|
+
/**
|
|
4
|
+
* The stack in page order, each close-up paired with the row above it — the
|
|
5
|
+
* previous close-up, or the host for the first. The array is widest first and
|
|
6
|
+
* every close-up is narrower than its host, so page order and array order are
|
|
7
|
+
* the same thing and each pair reads as one trapezoid: the span this row shows,
|
|
8
|
+
* marked on the row above it.
|
|
9
|
+
*
|
|
10
|
+
* One derivation for the screen and the SVG export, which otherwise agree on
|
|
11
|
+
* the order by having been written twice.
|
|
12
|
+
*/
|
|
13
|
+
export function closeUpStackRows(host, closeUps) {
|
|
14
|
+
return closeUps.map((closeUp, i) => ({
|
|
15
|
+
closeUp,
|
|
16
|
+
context: closeUps[i - 1] ?? host,
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
export const CLOSE_UP_FRAME_WIDTH = 1;
|
|
20
|
+
/**
|
|
21
|
+
* The outline a close-up and its connector share. Tertiary is the header
|
|
22
|
+
* overview's colour, but the dark themes set it near black, so those take its
|
|
23
|
+
* light shade.
|
|
24
|
+
*/
|
|
25
|
+
export function closeUpColor(theme) {
|
|
26
|
+
return theme.palette.mode === 'dark'
|
|
27
|
+
? theme.palette.tertiary.light
|
|
28
|
+
: theme.palette.tertiary.main;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The registered view, read at first instantiation rather than the factory's
|
|
32
|
+
* own return value, so a close-up is the LinearGenomeView every
|
|
33
|
+
* `extendViewType` caller has already extended. The name goes through a
|
|
34
|
+
* `string` so the typed `getViewType` overload, whose result names this model's
|
|
35
|
+
* own instance type, is not what tsc resolves here.
|
|
36
|
+
*/
|
|
37
|
+
export function closeUpType(pluginManager) {
|
|
38
|
+
const name = 'LinearGenomeView';
|
|
39
|
+
return types.late(() => pluginManager.getViewType(name).stateModel);
|
|
40
|
+
}
|
|
41
|
+
export function closeUpHost(node) {
|
|
42
|
+
const host = hasParent(node, 2)
|
|
43
|
+
? getParent(node, 2)
|
|
44
|
+
: undefined;
|
|
45
|
+
return host && 'closeUps' in host
|
|
46
|
+
? host
|
|
47
|
+
: undefined;
|
|
48
|
+
}
|
|
49
|
+
// The three sets below classify every navigation-shaped action of the view, and
|
|
50
|
+
// `closeUps.test.ts` holds them against the view's real action list: a
|
|
51
|
+
// navigation in none of them lands on the close-up and quietly walks it off the
|
|
52
|
+
// host's centre.
|
|
53
|
+
// A gesture on a close-up that means "move": replayed on the host in the host's
|
|
54
|
+
// own units, and swallowed on the close-up, whose left edge is derived.
|
|
55
|
+
export const CLOSE_UP_PANS = new Set(['horizontalScroll', 'scrollTo', 'slide']);
|
|
56
|
+
// A navigation on a close-up that names a place. The close-up and its host lay
|
|
57
|
+
// out the same regions, so the arguments mean the same thing on either, and the
|
|
58
|
+
// host is where they belong. `centerAt` is not an action but a method over
|
|
59
|
+
// `scrollTo`, so the pan above already lands it.
|
|
60
|
+
export const CLOSE_UP_NAVIGATIONS = new Set([
|
|
61
|
+
'moveTo',
|
|
62
|
+
'flyToCenter',
|
|
63
|
+
'navTo',
|
|
64
|
+
'navToLocation',
|
|
65
|
+
'navToMultiple',
|
|
66
|
+
'navToLocString',
|
|
67
|
+
'navToLocations',
|
|
68
|
+
'navigateNewestHighlight',
|
|
69
|
+
'showRegions',
|
|
70
|
+
'showAllRegionsInAssembly',
|
|
71
|
+
]);
|
|
72
|
+
// Everything a close-up hands to its host untouched. The navigations above,
|
|
73
|
+
// plus the picture: what a reader sees is the stack, and `renderToSvg` draws
|
|
74
|
+
// every close-up above the host that owns them, so a close-up's own Export SVG
|
|
75
|
+
// is the host's. A close-up alone is a row out of a figure nobody asked for.
|
|
76
|
+
export const CLOSE_UP_REPLAYS = new Set([...CLOSE_UP_NAVIGATIONS, 'exportSvg']);
|
|
77
|
+
// What stays the close-up's own: its zoom, which is its one piece of state, and
|
|
78
|
+
// the primitives the sync writes through. `zoomTo` from a wheel anchors under
|
|
79
|
+
// the cursor, and the sync puts the centre back on the same frame.
|
|
80
|
+
export const CLOSE_UP_OWN = new Set([
|
|
81
|
+
'zoomTo',
|
|
82
|
+
'zoom',
|
|
83
|
+
'flyTo',
|
|
84
|
+
'flyToFit',
|
|
85
|
+
'showAllRegions',
|
|
86
|
+
'fitAllRegions',
|
|
87
|
+
'clampZoomToCeiling',
|
|
88
|
+
'setWindow',
|
|
89
|
+
'setWindowFrame',
|
|
90
|
+
'setNewView',
|
|
91
|
+
'scrollToBp',
|
|
92
|
+
'setDisplayedRegions',
|
|
93
|
+
'horizontallyFlip',
|
|
94
|
+
]);
|
|
95
|
+
/**
|
|
96
|
+
* Keep every close-up a derived view of its host: same regions, same width,
|
|
97
|
+
* same centre, and between base level and the row above it, so the stack reads
|
|
98
|
+
* the host's span at the top zooming in the whole way down. A close-up zoomed
|
|
99
|
+
* out past its ceiling is pulled back in; a host zoomed in past a close-up
|
|
100
|
+
* pulls the close-up in with it.
|
|
101
|
+
*/
|
|
102
|
+
function syncCloseUps(self) {
|
|
103
|
+
// The stack first and alone, so a view with no close-ups — which is every
|
|
104
|
+
// view in the session but the one someone built a stack on — depends on this
|
|
105
|
+
// array and nothing else, and its own pans and zooms wake nothing here.
|
|
106
|
+
const closeUps = self.closeUpViews;
|
|
107
|
+
if (!closeUps.length) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const { volatileWidth, displayedRegions, windowStartBp, windowWidthBp } = self;
|
|
111
|
+
if (volatileWidth === undefined || !displayedRegions.length) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const centerBp = windowStartBp + windowWidthBp / 2;
|
|
115
|
+
let ceiling = windowWidthBp;
|
|
116
|
+
for (const closeUp of closeUps) {
|
|
117
|
+
closeUp.setWidth(volatileWidth);
|
|
118
|
+
if (closeUp.displayedRegions !== displayedRegions) {
|
|
119
|
+
closeUp.setDisplayedRegions(displayedRegions);
|
|
120
|
+
}
|
|
121
|
+
// BOTH of the close-up's own window numbers are read, not just its width,
|
|
122
|
+
// and that is what makes this the thing holding a close-up under its host
|
|
123
|
+
// rather than the gesture sets below. An action that moves a close-up
|
|
124
|
+
// without resizing it — one the sets do not cover, or one added later that
|
|
125
|
+
// nobody classified — otherwise never wakes this autorun, and the close-up
|
|
126
|
+
// sits off the centre for good with nothing to put it back. Read, it snaps
|
|
127
|
+
// back, so a miss costs that gesture rather than the stack.
|
|
128
|
+
const { windowWidthBp: closeUpWidth, windowStartBp: closeUpStart } = closeUp;
|
|
129
|
+
// The floor is base level, which `zoomTo` holds every gesture to but not
|
|
130
|
+
// the window a snapshot arrives carrying — and a stack ten times in each
|
|
131
|
+
// step reaches it in three or four rungs.
|
|
132
|
+
const floor = closeUp.minBpPerPx * volatileWidth;
|
|
133
|
+
const width = Math.min(Math.max(closeUpWidth, floor), ceiling);
|
|
134
|
+
const start = centerBp - width / 2;
|
|
135
|
+
if (width !== closeUpWidth || start !== closeUpStart) {
|
|
136
|
+
closeUp.setWindowFrame(width, start);
|
|
137
|
+
}
|
|
138
|
+
ceiling = closeUp.windowWidthBp;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Replay a gesture made on a close-up onto its host, or answer false to let it
|
|
143
|
+
* through. Every read here is of the live tree, which is why the caller runs it
|
|
144
|
+
* untracked.
|
|
145
|
+
*/
|
|
146
|
+
function redirectCloseUpGesture(self, host, call, abort) {
|
|
147
|
+
// a close-up's own launch navigates it, and that is not a gesture: the sync
|
|
148
|
+
// above puts it back under the host once the regions land
|
|
149
|
+
if (self.pendingLaunch) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
if (CLOSE_UP_PANS.has(call.name)) {
|
|
153
|
+
const arg = call.args[0];
|
|
154
|
+
const ratio = self.bpPerPx / host.bpPerPx;
|
|
155
|
+
if (call.name === 'horizontalScroll') {
|
|
156
|
+
abort(host.horizontalScroll(arg * ratio) / ratio);
|
|
157
|
+
}
|
|
158
|
+
else if (call.name === 'slide') {
|
|
159
|
+
// a fraction of the close-up's window is that many of its bases, which is
|
|
160
|
+
// `ratio` widths of the host
|
|
161
|
+
host.slide(arg * ratio);
|
|
162
|
+
abort(undefined);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
const centerBp = arg * self.bpPerPx + self.windowWidthBp / 2;
|
|
166
|
+
host.scrollToBp(centerBp - host.windowWidthBp / 2);
|
|
167
|
+
abort(arg);
|
|
168
|
+
}
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
if (CLOSE_UP_REPLAYS.has(call.name)) {
|
|
172
|
+
const target = host;
|
|
173
|
+
abort(target[call.name](...call.args));
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* The gesture redirect, installed on the CLOSE-UP rather than on the view
|
|
180
|
+
* holding it. MST collects a call's middleware by walking from the action's own
|
|
181
|
+
* node up to the root, so one installed on the host runs for every action of
|
|
182
|
+
* every track and display beneath it — a tax on views that will never hold a
|
|
183
|
+
* close-up, which is nearly all of them — where one installed here sees this
|
|
184
|
+
* close-up's subtree and nothing else. It also makes the identity check an
|
|
185
|
+
* identity check rather than a search through the host's array.
|
|
186
|
+
*/
|
|
187
|
+
function installCloseUpGestures(self, host) {
|
|
188
|
+
addDisposer(self, addMiddleware(self, (call, next, abort) => {
|
|
189
|
+
const mine = call.type === 'action' &&
|
|
190
|
+
call.id === call.rootId &&
|
|
191
|
+
call.context === self;
|
|
192
|
+
// A middleware handler runs in whatever context dispatched the action,
|
|
193
|
+
// and one of those is `syncCloseUps` — an autorun that dispatches
|
|
194
|
+
// close-up actions. A `pendingLaunch` or `bpPerPx` read registered there
|
|
195
|
+
// re-runs the whole sync whenever anything in the stack moves.
|
|
196
|
+
// eslint-disable-next-line no-restricted-syntax -- effect input: the two scales a gesture is replayed at, read where an autorun may be the caller
|
|
197
|
+
const handled = untracked(() => mine && redirectCloseUpGesture(self, host, call, abort));
|
|
198
|
+
if (!handled) {
|
|
199
|
+
next(call);
|
|
200
|
+
}
|
|
201
|
+
}));
|
|
202
|
+
}
|
|
203
|
+
export function installCloseUps(self) {
|
|
204
|
+
addDisposer(self, autorun(function closeUpsAutorun() {
|
|
205
|
+
syncCloseUps(self);
|
|
206
|
+
}, { name: 'LGVCloseUps' }));
|
|
207
|
+
const host = closeUpHost(self);
|
|
208
|
+
if (host) {
|
|
209
|
+
installCloseUpGestures(self, host);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { LinearGenomeViewModel } from '../model.ts';
|
|
2
|
+
import type { BpOffset } from '../types.ts';
|
|
3
|
+
/**
|
|
4
|
+
* The one thing a close-up still needs deciding once a drag has named its
|
|
5
|
+
* span: whether it opens showing what this view shows, or empty for a track
|
|
6
|
+
* chosen afterwards.
|
|
7
|
+
*
|
|
8
|
+
* The span is the selection's, named here the way the same menu's "Copy range"
|
|
9
|
+
* names it, so the dialog says what it is about to open rather than describing
|
|
10
|
+
* the feature. The model's action takes the same two offsets, for a stack an
|
|
11
|
+
* agent or a spec builds outright.
|
|
12
|
+
*/
|
|
13
|
+
declare const AddCloseUpDialog: ({ model, leftOffset, rightOffset, handleClose, }: {
|
|
14
|
+
model: LinearGenomeViewModel;
|
|
15
|
+
leftOffset?: BpOffset;
|
|
16
|
+
rightOffset?: BpOffset;
|
|
17
|
+
handleClose: () => void;
|
|
18
|
+
}) => import("react").JSX.Element;
|
|
19
|
+
export default AddCloseUpDialog;
|