@jbrowse/plugin-linear-genome-view 2.1.7 → 2.2.1
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/dist/BaseLinearDisplay/components/LinearBlocks.d.ts +2 -2
- package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +2 -22
- package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js.map +1 -1
- package/dist/BaseLinearDisplay/components/Tooltip.d.ts +1 -1
- package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +149 -4
- package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js +600 -464
- package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js.map +1 -1
- package/dist/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.js +19 -1
- package/dist/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.js.map +1 -1
- package/dist/BaseLinearDisplay/models/serverSideRenderedBlock.d.ts +2 -2
- package/dist/BasicTrack/configSchema.d.ts +3 -0
- package/dist/BasicTrack/configSchema.js +18 -0
- package/dist/BasicTrack/configSchema.js.map +1 -0
- package/dist/BasicTrack/index.d.ts +3 -0
- package/dist/BasicTrack/index.js +18 -0
- package/dist/BasicTrack/index.js.map +1 -0
- package/dist/FeatureTrack/configSchema.d.ts +3 -0
- package/dist/FeatureTrack/configSchema.js +21 -0
- package/dist/FeatureTrack/configSchema.js.map +1 -0
- package/dist/FeatureTrack/index.d.ts +3 -0
- package/dist/FeatureTrack/index.js +18 -0
- package/dist/FeatureTrack/index.js.map +1 -0
- package/dist/LinearBareDisplay/configSchema.d.ts +5 -1
- package/dist/LinearBareDisplay/configSchema.js +12 -1
- package/dist/LinearBareDisplay/configSchema.js.map +1 -1
- package/dist/LinearBareDisplay/model.d.ts +16 -0
- package/dist/LinearBareDisplay/model.js +16 -0
- package/dist/LinearBareDisplay/model.js.map +1 -1
- package/dist/LinearBasicDisplay/configSchema.d.ts +5 -1
- package/dist/LinearBasicDisplay/configSchema.js +16 -1
- package/dist/LinearBasicDisplay/configSchema.js.map +1 -1
- package/dist/LinearBasicDisplay/model.d.ts +79 -8
- package/dist/LinearBasicDisplay/model.js +167 -111
- package/dist/LinearBasicDisplay/model.js.map +1 -1
- package/dist/LinearGenomeView/components/CenterLine.d.ts +1 -1
- package/dist/LinearGenomeView/components/GetSequenceDialog.d.ts +1 -1
- package/dist/LinearGenomeView/components/Gridlines.d.ts +1 -1
- package/dist/LinearGenomeView/components/Header.d.ts +1 -1
- package/dist/LinearGenomeView/components/ImportForm.d.ts +1 -1
- package/dist/LinearGenomeView/components/ImportForm.js +38 -31
- package/dist/LinearGenomeView/components/ImportForm.js.map +1 -1
- package/dist/LinearGenomeView/components/LinearGenomeView.d.ts +1 -1
- package/dist/LinearGenomeView/components/LinearGenomeView.js +2 -24
- package/dist/LinearGenomeView/components/LinearGenomeView.js.map +1 -1
- package/dist/LinearGenomeView/components/LinearGenomeViewSvg.d.ts +1 -1
- package/dist/LinearGenomeView/components/LinearGenomeViewSvg.js +7 -5
- package/dist/LinearGenomeView/components/LinearGenomeViewSvg.js.map +1 -1
- package/dist/LinearGenomeView/components/OverviewRubberBand.d.ts +1 -1
- package/dist/LinearGenomeView/components/OverviewScaleBar.d.ts +27 -35
- package/dist/LinearGenomeView/components/RubberBand.d.ts +1 -1
- package/dist/LinearGenomeView/components/ScaleBar.d.ts +1 -1
- package/dist/LinearGenomeView/components/TrackContainer.d.ts +1 -1
- package/dist/LinearGenomeView/components/TrackContainer.js +12 -9
- package/dist/LinearGenomeView/components/TrackContainer.js.map +1 -1
- package/dist/LinearGenomeView/components/TrackLabel.js +9 -1
- package/dist/LinearGenomeView/components/TrackLabel.js.map +1 -1
- package/dist/LinearGenomeView/components/TracksContainer.d.ts +1 -1
- package/dist/LinearGenomeView/index.d.ts +273 -18
- package/dist/LinearGenomeView/index.js +409 -90
- package/dist/LinearGenomeView/index.js.map +1 -1
- package/dist/index.d.ts +30 -90
- package/dist/index.js +4 -25
- package/dist/index.js.map +1 -1
- package/esm/BaseLinearDisplay/components/LinearBlocks.d.ts +2 -2
- package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +2 -22
- package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js.map +1 -1
- package/esm/BaseLinearDisplay/components/Tooltip.d.ts +1 -1
- package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +149 -4
- package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js +600 -464
- package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js.map +1 -1
- package/esm/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.js +19 -1
- package/esm/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.js.map +1 -1
- package/esm/BaseLinearDisplay/models/serverSideRenderedBlock.d.ts +2 -2
- package/esm/BasicTrack/configSchema.d.ts +3 -0
- package/esm/BasicTrack/configSchema.js +16 -0
- package/esm/BasicTrack/configSchema.js.map +1 -0
- package/esm/BasicTrack/index.d.ts +3 -0
- package/esm/BasicTrack/index.js +13 -0
- package/esm/BasicTrack/index.js.map +1 -0
- package/esm/FeatureTrack/configSchema.d.ts +3 -0
- package/esm/FeatureTrack/configSchema.js +19 -0
- package/esm/FeatureTrack/configSchema.js.map +1 -0
- package/esm/FeatureTrack/index.d.ts +3 -0
- package/esm/FeatureTrack/index.js +13 -0
- package/esm/FeatureTrack/index.js.map +1 -0
- package/esm/LinearBareDisplay/configSchema.d.ts +5 -1
- package/esm/LinearBareDisplay/configSchema.js +14 -2
- package/esm/LinearBareDisplay/configSchema.js.map +1 -1
- package/esm/LinearBareDisplay/model.d.ts +16 -0
- package/esm/LinearBareDisplay/model.js +16 -0
- package/esm/LinearBareDisplay/model.js.map +1 -1
- package/esm/LinearBasicDisplay/configSchema.d.ts +5 -1
- package/esm/LinearBasicDisplay/configSchema.js +18 -2
- package/esm/LinearBasicDisplay/configSchema.js.map +1 -1
- package/esm/LinearBasicDisplay/model.d.ts +79 -8
- package/esm/LinearBasicDisplay/model.js +167 -111
- package/esm/LinearBasicDisplay/model.js.map +1 -1
- package/esm/LinearGenomeView/components/CenterLine.d.ts +1 -1
- package/esm/LinearGenomeView/components/GetSequenceDialog.d.ts +1 -1
- package/esm/LinearGenomeView/components/Gridlines.d.ts +1 -1
- package/esm/LinearGenomeView/components/Header.d.ts +1 -1
- package/esm/LinearGenomeView/components/ImportForm.d.ts +1 -1
- package/esm/LinearGenomeView/components/ImportForm.js +40 -33
- package/esm/LinearGenomeView/components/ImportForm.js.map +1 -1
- package/esm/LinearGenomeView/components/LinearGenomeView.d.ts +1 -1
- package/esm/LinearGenomeView/components/LinearGenomeView.js +3 -25
- package/esm/LinearGenomeView/components/LinearGenomeView.js.map +1 -1
- package/esm/LinearGenomeView/components/LinearGenomeViewSvg.d.ts +1 -1
- package/esm/LinearGenomeView/components/LinearGenomeViewSvg.js +7 -5
- package/esm/LinearGenomeView/components/LinearGenomeViewSvg.js.map +1 -1
- package/esm/LinearGenomeView/components/OverviewRubberBand.d.ts +1 -1
- package/esm/LinearGenomeView/components/OverviewScaleBar.d.ts +27 -35
- package/esm/LinearGenomeView/components/RubberBand.d.ts +1 -1
- package/esm/LinearGenomeView/components/ScaleBar.d.ts +1 -1
- package/esm/LinearGenomeView/components/TrackContainer.d.ts +1 -1
- package/esm/LinearGenomeView/components/TrackContainer.js +13 -10
- package/esm/LinearGenomeView/components/TrackContainer.js.map +1 -1
- package/esm/LinearGenomeView/components/TrackLabel.js +9 -1
- package/esm/LinearGenomeView/components/TrackLabel.js.map +1 -1
- package/esm/LinearGenomeView/components/TracksContainer.d.ts +1 -1
- package/esm/LinearGenomeView/index.d.ts +273 -18
- package/esm/LinearGenomeView/index.js +409 -90
- package/esm/LinearGenomeView/index.js.map +1 -1
- package/esm/index.d.ts +30 -90
- package/esm/index.js +4 -25
- package/esm/index.js.map +1 -1
- package/package.json +2 -2
- package/src/BaseLinearDisplay/components/ServerSideRenderedBlockContent.tsx +2 -24
- package/src/BaseLinearDisplay/models/BaseLinearDisplayModel.tsx +695 -555
- package/src/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.ts +20 -1
- package/src/BasicTrack/configSchema.ts +23 -0
- package/src/BasicTrack/index.ts +18 -0
- package/src/FeatureTrack/configSchema.ts +27 -0
- package/src/FeatureTrack/index.ts +21 -0
- package/src/LinearBareDisplay/configSchema.ts +15 -2
- package/src/LinearBareDisplay/model.ts +16 -0
- package/src/LinearBasicDisplay/configSchema.ts +19 -2
- package/src/LinearBasicDisplay/model.ts +63 -11
- package/src/LinearGenomeView/components/ImportForm.tsx +85 -67
- package/src/LinearGenomeView/components/LinearGenomeView.tsx +3 -33
- package/src/LinearGenomeView/components/LinearGenomeViewSvg.tsx +21 -18
- package/src/LinearGenomeView/components/TrackContainer.tsx +38 -27
- package/src/LinearGenomeView/components/TrackLabel.tsx +10 -1
- package/src/LinearGenomeView/components/__snapshots__/LinearGenomeView.test.tsx.snap +204 -204
- package/src/LinearGenomeView/index.test.ts +33 -26
- package/src/LinearGenomeView/index.tsx +471 -133
- package/src/index.ts +6 -46
|
@@ -59,6 +59,7 @@ import Header from './components/Header'
|
|
|
59
59
|
import ZoomControls from './components/ZoomControls'
|
|
60
60
|
import LinearGenomeView from './components/LinearGenomeView'
|
|
61
61
|
|
|
62
|
+
// lazies
|
|
62
63
|
const SequenceSearchDialog = lazy(
|
|
63
64
|
() => import('./components/SequenceSearchDialog'),
|
|
64
65
|
)
|
|
@@ -78,6 +79,8 @@ export interface BpOffset {
|
|
|
78
79
|
export interface ExportSvgOptions {
|
|
79
80
|
rasterizeLayers?: boolean
|
|
80
81
|
filename?: string
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
83
|
+
Wrapper?: React.FC<any>
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
function calculateVisibleLocStrings(contentBlocks: BaseBlock[]) {
|
|
@@ -85,7 +88,7 @@ function calculateVisibleLocStrings(contentBlocks: BaseBlock[]) {
|
|
|
85
88
|
return ''
|
|
86
89
|
}
|
|
87
90
|
const isSingleAssemblyName = contentBlocks.every(
|
|
88
|
-
|
|
91
|
+
b => b.assemblyName === contentBlocks[0].assemblyName,
|
|
89
92
|
)
|
|
90
93
|
const locs = contentBlocks.map(block =>
|
|
91
94
|
assembleLocString({
|
|
@@ -119,41 +122,110 @@ function localStorageGetItem(item: string) {
|
|
|
119
122
|
: undefined
|
|
120
123
|
}
|
|
121
124
|
|
|
125
|
+
/**
|
|
126
|
+
* #stateModel LinearGenomeView
|
|
127
|
+
*/
|
|
122
128
|
export function stateModelFactory(pluginManager: PluginManager) {
|
|
123
129
|
return types
|
|
124
130
|
.compose(
|
|
125
131
|
BaseViewModel,
|
|
126
132
|
types.model('LinearGenomeView', {
|
|
133
|
+
/**
|
|
134
|
+
* #property
|
|
135
|
+
*/
|
|
127
136
|
id: ElementId,
|
|
128
|
-
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* #property
|
|
140
|
+
* this is a string instead of the const literal 'LinearGenomeView' to reduce some
|
|
141
|
+
* typescripting strictness, but you should pass the string 'LinearGenomeView' to
|
|
142
|
+
* the model explicitly
|
|
143
|
+
*/
|
|
144
|
+
type: types.literal('LinearGenomeView') as unknown as string,
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* #property
|
|
148
|
+
* corresponds roughly to the horizontal scroll of the LGV
|
|
149
|
+
*/
|
|
129
150
|
offsetPx: 0,
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* #property
|
|
154
|
+
* corresponds roughly to the zoom level, base-pairs per pixel
|
|
155
|
+
*/
|
|
130
156
|
bpPerPx: 1,
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* #property
|
|
160
|
+
* currently displayed regions, can be a single chromosome, arbitrary subsections,
|
|
161
|
+
* or the entire set of chromosomes in the genome, but it not advised to use the
|
|
162
|
+
* entire set of chromosomes if your assembly is very fragmented
|
|
163
|
+
*/
|
|
131
164
|
displayedRegions: types.array(MUIRegion),
|
|
132
165
|
|
|
133
|
-
|
|
134
|
-
|
|
166
|
+
/**
|
|
167
|
+
* #property
|
|
168
|
+
* array of currently displayed tracks state models instances
|
|
169
|
+
*/
|
|
135
170
|
tracks: types.array(
|
|
136
171
|
pluginManager.pluggableMstType('track', 'stateModel'),
|
|
137
172
|
),
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* #property
|
|
176
|
+
* array of currently displayed tracks state model's
|
|
177
|
+
*/
|
|
138
178
|
hideHeader: false,
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* #property
|
|
182
|
+
*/
|
|
139
183
|
hideHeaderOverview: false,
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* #property
|
|
187
|
+
*/
|
|
140
188
|
hideNoTracksActive: false,
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* #property
|
|
192
|
+
*/
|
|
141
193
|
trackSelectorType: types.optional(
|
|
142
194
|
types.enumeration(['hierarchical']),
|
|
143
195
|
'hierarchical',
|
|
144
196
|
),
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* #property
|
|
200
|
+
* how to display the track labels, can be "overlapping", "offset", or "hidden"
|
|
201
|
+
*/
|
|
145
202
|
trackLabels: types.optional(
|
|
146
203
|
types.string,
|
|
147
204
|
() => localStorageGetItem('lgv-trackLabels') || 'overlapping',
|
|
148
205
|
),
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* #property
|
|
209
|
+
* show the "center line"
|
|
210
|
+
*/
|
|
149
211
|
showCenterLine: types.optional(types.boolean, () => {
|
|
150
212
|
const setting = localStorageGetItem('lgv-showCenterLine')
|
|
151
213
|
return setting !== undefined && setting !== null ? !!+setting : false
|
|
152
214
|
}),
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* #property
|
|
218
|
+
* show the "cytobands" in the overview scale bar
|
|
219
|
+
*/
|
|
153
220
|
showCytobandsSetting: types.optional(types.boolean, () => {
|
|
154
221
|
const setting = localStorageGetItem('lgv-showCytobands')
|
|
155
222
|
return setting !== undefined && setting !== null ? !!+setting : true
|
|
156
223
|
}),
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* #property
|
|
227
|
+
* show the "gridlines" in the track area
|
|
228
|
+
*/
|
|
157
229
|
showGridlines: true,
|
|
158
230
|
}),
|
|
159
231
|
)
|
|
@@ -178,6 +250,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
178
250
|
seqDialogDisplayed: false,
|
|
179
251
|
}))
|
|
180
252
|
.views(self => ({
|
|
253
|
+
/**
|
|
254
|
+
* #getter
|
|
255
|
+
*/
|
|
181
256
|
get width(): number {
|
|
182
257
|
if (self.volatileWidth === undefined) {
|
|
183
258
|
throw new Error(
|
|
@@ -186,10 +261,16 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
186
261
|
}
|
|
187
262
|
return self.volatileWidth
|
|
188
263
|
},
|
|
264
|
+
/**
|
|
265
|
+
* #getter
|
|
266
|
+
*/
|
|
189
267
|
get interRegionPaddingWidth() {
|
|
190
268
|
return INTER_REGION_PADDING_WIDTH
|
|
191
269
|
},
|
|
192
270
|
|
|
271
|
+
/**
|
|
272
|
+
* #getter
|
|
273
|
+
*/
|
|
193
274
|
get assemblyNames() {
|
|
194
275
|
return [
|
|
195
276
|
...new Set(self.displayedRegions.map(region => region.assemblyName)),
|
|
@@ -197,16 +278,25 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
197
278
|
},
|
|
198
279
|
}))
|
|
199
280
|
.views(self => ({
|
|
281
|
+
/**
|
|
282
|
+
* #method
|
|
283
|
+
*/
|
|
200
284
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
201
285
|
MiniControlsComponent(): React.FC<any> {
|
|
202
286
|
return MiniControls
|
|
203
287
|
},
|
|
204
288
|
|
|
289
|
+
/**
|
|
290
|
+
* #method
|
|
291
|
+
*/
|
|
205
292
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
206
293
|
HeaderComponent(): React.FC<any> {
|
|
207
294
|
return Header
|
|
208
295
|
},
|
|
209
296
|
|
|
297
|
+
/**
|
|
298
|
+
* #getter
|
|
299
|
+
*/
|
|
210
300
|
get assemblyErrors() {
|
|
211
301
|
const { assemblyManager } = getSession(self)
|
|
212
302
|
const { assemblyNames } = self
|
|
@@ -216,23 +306,46 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
216
306
|
.join(', ')
|
|
217
307
|
},
|
|
218
308
|
|
|
309
|
+
/**
|
|
310
|
+
* #getter
|
|
311
|
+
*/
|
|
219
312
|
get assembliesInitialized() {
|
|
220
313
|
const { assemblyManager } = getSession(self)
|
|
221
314
|
const { assemblyNames } = self
|
|
222
315
|
return assemblyNames.every(a => assemblyManager.get(a)?.initialized)
|
|
223
316
|
},
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* #getter
|
|
320
|
+
*/
|
|
224
321
|
get initialized() {
|
|
225
322
|
return self.volatileWidth !== undefined && this.assembliesInitialized
|
|
226
323
|
},
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* #getter
|
|
327
|
+
*/
|
|
227
328
|
get hasDisplayedRegions() {
|
|
228
329
|
return self.displayedRegions.length > 0
|
|
229
330
|
},
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* #getter
|
|
334
|
+
*/
|
|
230
335
|
get isSearchDialogDisplayed() {
|
|
231
336
|
return self.searchResults !== undefined
|
|
232
337
|
},
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* #getter
|
|
341
|
+
*/
|
|
233
342
|
get scaleBarHeight() {
|
|
234
343
|
return SCALE_BAR_HEIGHT + RESIZE_HANDLE_HEIGHT
|
|
235
344
|
},
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* #getter
|
|
348
|
+
*/
|
|
236
349
|
get headerHeight() {
|
|
237
350
|
if (self.hideHeader) {
|
|
238
351
|
return 0
|
|
@@ -242,15 +355,26 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
242
355
|
}
|
|
243
356
|
return HEADER_BAR_HEIGHT + HEADER_OVERVIEW_HEIGHT
|
|
244
357
|
},
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* #getter
|
|
361
|
+
*/
|
|
245
362
|
get trackHeights() {
|
|
246
363
|
return self.tracks
|
|
247
364
|
.map(t => t.displays[0].height)
|
|
248
365
|
.reduce((a, b) => a + b, 0)
|
|
249
366
|
},
|
|
250
367
|
|
|
368
|
+
/**
|
|
369
|
+
* #getter
|
|
370
|
+
*/
|
|
251
371
|
get trackHeightsWithResizeHandles() {
|
|
252
372
|
return this.trackHeights + self.tracks.length * RESIZE_HANDLE_HEIGHT
|
|
253
373
|
},
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* #getter
|
|
377
|
+
*/
|
|
254
378
|
get height() {
|
|
255
379
|
return (
|
|
256
380
|
this.trackHeightsWithResizeHandles +
|
|
@@ -258,38 +382,63 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
258
382
|
this.scaleBarHeight
|
|
259
383
|
)
|
|
260
384
|
},
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* #getter
|
|
388
|
+
*/
|
|
261
389
|
get totalBp() {
|
|
262
390
|
return self.displayedRegions.reduce((a, b) => a + b.end - b.start, 0)
|
|
263
391
|
},
|
|
264
392
|
|
|
393
|
+
/**
|
|
394
|
+
* #getter
|
|
395
|
+
*/
|
|
265
396
|
get maxBpPerPx() {
|
|
266
397
|
return this.totalBp / (self.width * 0.9)
|
|
267
398
|
},
|
|
268
399
|
|
|
400
|
+
/**
|
|
401
|
+
* #getter
|
|
402
|
+
*/
|
|
269
403
|
get minBpPerPx() {
|
|
270
404
|
return 1 / 50
|
|
271
405
|
},
|
|
272
406
|
|
|
407
|
+
/**
|
|
408
|
+
* #getter
|
|
409
|
+
*/
|
|
273
410
|
get error() {
|
|
274
411
|
return self.volatileError || this.assemblyErrors
|
|
275
412
|
},
|
|
276
413
|
|
|
414
|
+
/**
|
|
415
|
+
* #getter
|
|
416
|
+
*/
|
|
277
417
|
get maxOffset() {
|
|
278
418
|
// objectively determined to keep the linear genome on the main screen
|
|
279
419
|
const leftPadding = 10
|
|
280
420
|
return this.displayedRegionsTotalPx - leftPadding
|
|
281
421
|
},
|
|
282
422
|
|
|
423
|
+
/**
|
|
424
|
+
* #getter
|
|
425
|
+
*/
|
|
283
426
|
get minOffset() {
|
|
284
427
|
// objectively determined to keep the linear genome on the main screen
|
|
285
428
|
const rightPadding = 30
|
|
286
429
|
return -self.width + rightPadding
|
|
287
430
|
},
|
|
288
431
|
|
|
432
|
+
/**
|
|
433
|
+
* #getter
|
|
434
|
+
*/
|
|
289
435
|
get displayedRegionsTotalPx() {
|
|
290
436
|
return this.totalBp / self.bpPerPx
|
|
291
437
|
},
|
|
292
438
|
|
|
439
|
+
/**
|
|
440
|
+
* #method
|
|
441
|
+
*/
|
|
293
442
|
renderProps() {
|
|
294
443
|
return {
|
|
295
444
|
...getParentRenderProps(self),
|
|
@@ -301,6 +450,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
301
450
|
}
|
|
302
451
|
},
|
|
303
452
|
|
|
453
|
+
/**
|
|
454
|
+
* #method
|
|
455
|
+
*/
|
|
304
456
|
searchScope(assemblyName: string) {
|
|
305
457
|
return {
|
|
306
458
|
assemblyName,
|
|
@@ -309,10 +461,16 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
309
461
|
}
|
|
310
462
|
},
|
|
311
463
|
|
|
464
|
+
/**
|
|
465
|
+
* #method
|
|
466
|
+
*/
|
|
312
467
|
getTrack(id: string) {
|
|
313
468
|
return self.tracks.find(t => t.configuration.trackId === id)
|
|
314
469
|
},
|
|
315
470
|
|
|
471
|
+
/**
|
|
472
|
+
* #method
|
|
473
|
+
*/
|
|
316
474
|
rankSearchResults(results: BaseResult[]) {
|
|
317
475
|
// order of rank
|
|
318
476
|
const openTrackIds = self.tracks.map(
|
|
@@ -326,7 +484,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
326
484
|
return results
|
|
327
485
|
},
|
|
328
486
|
|
|
329
|
-
|
|
487
|
+
/**
|
|
488
|
+
* #method
|
|
489
|
+
* modifies view menu action onClick to apply to all tracks of same type
|
|
490
|
+
*/
|
|
330
491
|
rewriteOnClicks(trackType: string, viewMenuActions: MenuItem[]) {
|
|
331
492
|
viewMenuActions.forEach(action => {
|
|
332
493
|
// go to lowest level menu
|
|
@@ -345,7 +506,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
345
506
|
}
|
|
346
507
|
})
|
|
347
508
|
},
|
|
348
|
-
|
|
509
|
+
/**
|
|
510
|
+
* #getter
|
|
511
|
+
*/
|
|
349
512
|
get trackTypeActions() {
|
|
350
513
|
const allActions: Map<string, MenuItem[]> = new Map()
|
|
351
514
|
self.tracks.forEach(track => {
|
|
@@ -361,37 +524,61 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
361
524
|
},
|
|
362
525
|
}))
|
|
363
526
|
.actions(self => ({
|
|
527
|
+
/**
|
|
528
|
+
* #action
|
|
529
|
+
*/
|
|
364
530
|
setShowCytobands(flag: boolean) {
|
|
365
531
|
self.showCytobandsSetting = flag
|
|
366
532
|
localStorage.setItem('lgv-showCytobands', `${+flag}`)
|
|
367
533
|
},
|
|
534
|
+
/**
|
|
535
|
+
* #action
|
|
536
|
+
*/
|
|
368
537
|
setWidth(newWidth: number) {
|
|
369
538
|
self.volatileWidth = newWidth
|
|
370
539
|
},
|
|
540
|
+
/**
|
|
541
|
+
* #action
|
|
542
|
+
*/
|
|
371
543
|
setError(error: Error | undefined) {
|
|
372
544
|
self.volatileError = error
|
|
373
545
|
},
|
|
374
|
-
|
|
546
|
+
/**
|
|
547
|
+
* #action
|
|
548
|
+
*/
|
|
375
549
|
toggleHeader() {
|
|
376
550
|
self.hideHeader = !self.hideHeader
|
|
377
551
|
},
|
|
378
|
-
|
|
552
|
+
/**
|
|
553
|
+
* #action
|
|
554
|
+
*/
|
|
379
555
|
toggleHeaderOverview() {
|
|
380
556
|
self.hideHeaderOverview = !self.hideHeaderOverview
|
|
381
557
|
},
|
|
558
|
+
/**
|
|
559
|
+
* #action
|
|
560
|
+
*/
|
|
382
561
|
toggleNoTracksActive() {
|
|
383
562
|
self.hideNoTracksActive = !self.hideNoTracksActive
|
|
384
563
|
},
|
|
564
|
+
/**
|
|
565
|
+
* #action
|
|
566
|
+
*/
|
|
385
567
|
toggleShowGridlines() {
|
|
386
568
|
self.showGridlines = !self.showGridlines
|
|
387
569
|
},
|
|
388
|
-
|
|
570
|
+
/**
|
|
571
|
+
* #action
|
|
572
|
+
*/
|
|
389
573
|
scrollTo(offsetPx: number) {
|
|
390
574
|
const newOffsetPx = clamp(offsetPx, self.minOffset, self.maxOffset)
|
|
391
575
|
self.offsetPx = newOffsetPx
|
|
392
576
|
return newOffsetPx
|
|
393
577
|
},
|
|
394
578
|
|
|
579
|
+
/**
|
|
580
|
+
* #action
|
|
581
|
+
*/
|
|
395
582
|
zoomTo(bpPerPx: number) {
|
|
396
583
|
const newBpPerPx = clamp(bpPerPx, self.minBpPerPx, self.maxBpPerPx)
|
|
397
584
|
if (newBpPerPx === self.bpPerPx) {
|
|
@@ -416,26 +603,41 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
416
603
|
return newBpPerPx
|
|
417
604
|
},
|
|
418
605
|
|
|
606
|
+
/**
|
|
607
|
+
* #action
|
|
608
|
+
* sets offsets used in the get sequence dialog
|
|
609
|
+
*/
|
|
419
610
|
setOffsets(left?: BpOffset, right?: BpOffset) {
|
|
420
|
-
// sets offsets used in the get sequence dialog
|
|
421
611
|
self.leftOffset = left
|
|
422
612
|
self.rightOffset = right
|
|
423
613
|
},
|
|
424
614
|
|
|
615
|
+
/**
|
|
616
|
+
* #action
|
|
617
|
+
*/
|
|
425
618
|
setSearchResults(results?: BaseResult[], query?: string) {
|
|
426
619
|
self.searchResults = results
|
|
427
620
|
self.searchQuery = query
|
|
428
621
|
},
|
|
429
622
|
|
|
623
|
+
/**
|
|
624
|
+
* #action
|
|
625
|
+
*/
|
|
430
626
|
setGetSequenceDialogOpen(open: boolean) {
|
|
431
627
|
self.seqDialogDisplayed = open
|
|
432
628
|
},
|
|
433
629
|
|
|
630
|
+
/**
|
|
631
|
+
* #action
|
|
632
|
+
*/
|
|
434
633
|
setNewView(bpPerPx: number, offsetPx: number) {
|
|
435
634
|
this.zoomTo(bpPerPx)
|
|
436
635
|
this.scrollTo(offsetPx)
|
|
437
636
|
},
|
|
438
637
|
|
|
638
|
+
/**
|
|
639
|
+
* #action
|
|
640
|
+
*/
|
|
439
641
|
horizontallyFlip() {
|
|
440
642
|
self.displayedRegions = cast(
|
|
441
643
|
self.displayedRegions
|
|
@@ -446,6 +648,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
446
648
|
this.scrollTo(self.totalBp / self.bpPerPx - self.offsetPx - self.width)
|
|
447
649
|
},
|
|
448
650
|
|
|
651
|
+
/**
|
|
652
|
+
* #action
|
|
653
|
+
*/
|
|
449
654
|
showTrack(
|
|
450
655
|
trackId: string,
|
|
451
656
|
initialSnapshot = {},
|
|
@@ -490,7 +695,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
490
695
|
}
|
|
491
696
|
return t[0]
|
|
492
697
|
},
|
|
493
|
-
|
|
698
|
+
/**
|
|
699
|
+
* #action
|
|
700
|
+
*/
|
|
494
701
|
hideTrack(trackId: string) {
|
|
495
702
|
const schema = pluginManager.pluggableConfigSchemaType('track')
|
|
496
703
|
const conf = resolveIdentifier(schema, getRoot(self), trackId)
|
|
@@ -500,6 +707,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
500
707
|
},
|
|
501
708
|
}))
|
|
502
709
|
.actions(self => ({
|
|
710
|
+
/**
|
|
711
|
+
* #action
|
|
712
|
+
*/
|
|
503
713
|
moveTrack(movingId: string, targetId: string) {
|
|
504
714
|
const oldIndex = self.tracks.findIndex(track => track.id === movingId)
|
|
505
715
|
if (oldIndex === -1) {
|
|
@@ -514,6 +724,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
514
724
|
self.tracks.splice(newIndex, 0, track)
|
|
515
725
|
},
|
|
516
726
|
|
|
727
|
+
/**
|
|
728
|
+
* #action
|
|
729
|
+
*/
|
|
517
730
|
closeView() {
|
|
518
731
|
const parent = getContainingView(self)
|
|
519
732
|
if (parent) {
|
|
@@ -527,6 +740,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
527
740
|
}
|
|
528
741
|
},
|
|
529
742
|
|
|
743
|
+
/**
|
|
744
|
+
* #action
|
|
745
|
+
*/
|
|
530
746
|
toggleTrack(trackId: string) {
|
|
531
747
|
// if we have any tracks with that configuration, turn them off
|
|
532
748
|
const hiddenCount = self.hideTrack(trackId)
|
|
@@ -536,21 +752,33 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
536
752
|
}
|
|
537
753
|
},
|
|
538
754
|
|
|
755
|
+
/**
|
|
756
|
+
* #action
|
|
757
|
+
*/
|
|
539
758
|
setTrackLabels(setting: 'overlapping' | 'offset' | 'hidden') {
|
|
540
759
|
self.trackLabels = setting
|
|
541
760
|
localStorage.setItem('lgv-trackLabels', setting)
|
|
542
761
|
},
|
|
543
762
|
|
|
763
|
+
/**
|
|
764
|
+
* #action
|
|
765
|
+
*/
|
|
544
766
|
toggleCenterLine() {
|
|
545
767
|
self.showCenterLine = !self.showCenterLine
|
|
546
768
|
localStorage.setItem('lgv-showCenterLine', `${+self.showCenterLine}`)
|
|
547
769
|
},
|
|
548
770
|
|
|
771
|
+
/**
|
|
772
|
+
* #action
|
|
773
|
+
*/
|
|
549
774
|
setDisplayedRegions(regions: Region[]) {
|
|
550
775
|
self.displayedRegions = cast(regions)
|
|
551
776
|
self.zoomTo(self.bpPerPx)
|
|
552
777
|
},
|
|
553
778
|
|
|
779
|
+
/**
|
|
780
|
+
* #action
|
|
781
|
+
*/
|
|
554
782
|
activateTrackSelector() {
|
|
555
783
|
if (self.trackSelectorType === 'hierarchical') {
|
|
556
784
|
const session = getSession(self)
|
|
@@ -568,6 +796,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
568
796
|
},
|
|
569
797
|
|
|
570
798
|
/**
|
|
799
|
+
* #method
|
|
571
800
|
* Helper method for the fetchSequence.
|
|
572
801
|
* Retrieves the corresponding regions that were selected by the rubberband
|
|
573
802
|
*
|
|
@@ -592,11 +821,17 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
592
821
|
}))
|
|
593
822
|
},
|
|
594
823
|
|
|
595
|
-
|
|
824
|
+
/**
|
|
825
|
+
* #action
|
|
826
|
+
* schedule something to be run after the next time displayedRegions is set
|
|
827
|
+
*/
|
|
596
828
|
afterDisplayedRegionsSet(cb: Function) {
|
|
597
829
|
self.afterDisplayedRegionsSetCallbacks.push(cb)
|
|
598
830
|
},
|
|
599
831
|
|
|
832
|
+
/**
|
|
833
|
+
* #action
|
|
834
|
+
*/
|
|
600
835
|
horizontalScroll(distance: number) {
|
|
601
836
|
const oldOffsetPx = self.offsetPx
|
|
602
837
|
// newOffsetPx is the actual offset after the scroll is clamped
|
|
@@ -604,34 +839,38 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
604
839
|
return newOffsetPx - oldOffsetPx
|
|
605
840
|
},
|
|
606
841
|
|
|
842
|
+
/**
|
|
843
|
+
* #action
|
|
844
|
+
*/
|
|
607
845
|
center() {
|
|
608
846
|
const centerBp = self.totalBp / 2
|
|
609
847
|
const centerPx = centerBp / self.bpPerPx
|
|
610
848
|
self.scrollTo(Math.round(centerPx - self.width / 2))
|
|
611
849
|
},
|
|
612
850
|
|
|
851
|
+
/**
|
|
852
|
+
* #action
|
|
853
|
+
*/
|
|
613
854
|
showAllRegions() {
|
|
614
855
|
self.zoomTo(self.maxBpPerPx)
|
|
615
856
|
this.center()
|
|
616
857
|
},
|
|
617
858
|
|
|
859
|
+
/**
|
|
860
|
+
* #action
|
|
861
|
+
*/
|
|
618
862
|
showAllRegionsInAssembly(assemblyName?: string) {
|
|
619
863
|
const session = getSession(self)
|
|
620
864
|
const { assemblyManager } = session
|
|
621
865
|
if (!assemblyName) {
|
|
622
|
-
const
|
|
623
|
-
|
|
624
|
-
self.displayedRegions.map(region => region.assemblyName),
|
|
625
|
-
),
|
|
626
|
-
]
|
|
627
|
-
if (assemblyNames.length > 1) {
|
|
866
|
+
const names = new Set(self.displayedRegions.map(r => r.assemblyName))
|
|
867
|
+
if (names.size > 1) {
|
|
628
868
|
session.notify(
|
|
629
|
-
`Can't perform
|
|
869
|
+
`Can't perform operation with multiple assemblies currently`,
|
|
630
870
|
)
|
|
631
871
|
return
|
|
632
872
|
}
|
|
633
|
-
|
|
634
|
-
;[assemblyName] = assemblyNames
|
|
873
|
+
;[assemblyName] = [...names]
|
|
635
874
|
}
|
|
636
875
|
const assembly = assemblyManager.get(assemblyName)
|
|
637
876
|
if (assembly) {
|
|
@@ -644,16 +883,26 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
644
883
|
}
|
|
645
884
|
},
|
|
646
885
|
|
|
886
|
+
/**
|
|
887
|
+
* #action
|
|
888
|
+
*/
|
|
647
889
|
setDraggingTrackId(idx?: string) {
|
|
648
890
|
self.draggingTrackId = idx
|
|
649
891
|
},
|
|
650
892
|
|
|
893
|
+
/**
|
|
894
|
+
* #action
|
|
895
|
+
*/
|
|
651
896
|
setScaleFactor(factor: number) {
|
|
652
897
|
self.scaleFactor = factor
|
|
653
898
|
},
|
|
654
|
-
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* #action
|
|
902
|
+
* this "clears the view" and makes the view return to the import form
|
|
903
|
+
*/
|
|
655
904
|
clearView() {
|
|
656
|
-
|
|
905
|
+
self.displayedRegions.clear()
|
|
657
906
|
self.tracks.clear()
|
|
658
907
|
// it is necessary to run these after setting displayed regions empty
|
|
659
908
|
// or else model.offsetPx gets set to Infinity and breaks
|
|
@@ -661,6 +910,11 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
661
910
|
self.scrollTo(0)
|
|
662
911
|
self.zoomTo(10)
|
|
663
912
|
},
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* #action
|
|
916
|
+
* creates an svg export and save using FileSaver
|
|
917
|
+
*/
|
|
664
918
|
async exportSvg(opts: ExportSvgOptions = {}) {
|
|
665
919
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
666
920
|
const html = await renderToSvg(self as any, opts)
|
|
@@ -671,6 +925,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
671
925
|
.actions(self => {
|
|
672
926
|
let cancelLastAnimation = () => {}
|
|
673
927
|
|
|
928
|
+
/**
|
|
929
|
+
* #action
|
|
930
|
+
* perform animated slide
|
|
931
|
+
*/
|
|
674
932
|
function slide(viewWidths: number) {
|
|
675
933
|
const [animate, cancelAnimation] = springAnimate(
|
|
676
934
|
self.offsetPx,
|
|
@@ -687,6 +945,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
687
945
|
.actions(self => {
|
|
688
946
|
let cancelLastAnimation = () => {}
|
|
689
947
|
|
|
948
|
+
/**
|
|
949
|
+
* #action
|
|
950
|
+
* perform animated zoom
|
|
951
|
+
*/
|
|
690
952
|
function zoom(targetBpPerPx: number) {
|
|
691
953
|
self.zoomTo(self.bpPerPx)
|
|
692
954
|
if (
|
|
@@ -715,20 +977,32 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
715
977
|
return { zoom }
|
|
716
978
|
})
|
|
717
979
|
.views(self => ({
|
|
980
|
+
/**
|
|
981
|
+
* #getter
|
|
982
|
+
*/
|
|
718
983
|
get canShowCytobands() {
|
|
719
984
|
return self.displayedRegions.length === 1 && this.anyCytobandsExist
|
|
720
985
|
},
|
|
986
|
+
/**
|
|
987
|
+
* #getter
|
|
988
|
+
*/
|
|
721
989
|
get showCytobands() {
|
|
722
990
|
return this.canShowCytobands && self.showCytobandsSetting
|
|
723
991
|
},
|
|
992
|
+
/**
|
|
993
|
+
* #getter
|
|
994
|
+
*/
|
|
724
995
|
get anyCytobandsExist() {
|
|
725
996
|
const { assemblyManager } = getSession(self)
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
asm => assemblyManager.get(asm)?.cytobands?.length,
|
|
997
|
+
return self.assemblyNames.some(
|
|
998
|
+
a => assemblyManager.get(a)?.cytobands?.length,
|
|
729
999
|
)
|
|
730
1000
|
},
|
|
731
|
-
|
|
1001
|
+
/**
|
|
1002
|
+
* #getter
|
|
1003
|
+
* the cytoband is displayed to the right of the chromosome name,
|
|
1004
|
+
* and that offset is calculated manually with this method
|
|
1005
|
+
*/
|
|
732
1006
|
get cytobandOffset() {
|
|
733
1007
|
return this.showCytobands
|
|
734
1008
|
? measureText(self.displayedRegions[0].refName, 12) + 15
|
|
@@ -736,6 +1010,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
736
1010
|
},
|
|
737
1011
|
}))
|
|
738
1012
|
.views(self => ({
|
|
1013
|
+
/**
|
|
1014
|
+
* #method
|
|
1015
|
+
* return the view menu items
|
|
1016
|
+
*/
|
|
739
1017
|
menuItems(): MenuItem[] {
|
|
740
1018
|
const { canShowCytobands, showCytobands } = self
|
|
741
1019
|
const session = getSession(self)
|
|
@@ -783,59 +1061,57 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
783
1061
|
icon: SyncAltIcon,
|
|
784
1062
|
onClick: self.horizontallyFlip,
|
|
785
1063
|
},
|
|
786
|
-
{ type: 'divider' },
|
|
787
|
-
{
|
|
788
|
-
label: 'Show all regions in assembly',
|
|
789
|
-
icon: VisibilityIcon,
|
|
790
|
-
onClick: self.showAllRegionsInAssembly,
|
|
791
|
-
},
|
|
792
|
-
{
|
|
793
|
-
label: 'Show center line',
|
|
794
|
-
icon: VisibilityIcon,
|
|
795
|
-
type: 'checkbox',
|
|
796
|
-
checked: self.showCenterLine,
|
|
797
|
-
onClick: self.toggleCenterLine,
|
|
798
|
-
},
|
|
799
1064
|
{
|
|
800
|
-
label: 'Show
|
|
1065
|
+
label: 'Show...',
|
|
801
1066
|
icon: VisibilityIcon,
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
1067
|
+
subMenu: [
|
|
1068
|
+
{
|
|
1069
|
+
label: 'Show all regions in assembly',
|
|
1070
|
+
onClick: self.showAllRegionsInAssembly,
|
|
1071
|
+
},
|
|
1072
|
+
{
|
|
1073
|
+
label: 'Show center line',
|
|
1074
|
+
type: 'checkbox',
|
|
1075
|
+
checked: self.showCenterLine,
|
|
1076
|
+
onClick: self.toggleCenterLine,
|
|
1077
|
+
},
|
|
1078
|
+
{
|
|
1079
|
+
label: 'Show header',
|
|
1080
|
+
type: 'checkbox',
|
|
1081
|
+
checked: !self.hideHeader,
|
|
1082
|
+
onClick: self.toggleHeader,
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
label: 'Show header overview',
|
|
1086
|
+
type: 'checkbox',
|
|
1087
|
+
checked: !self.hideHeaderOverview,
|
|
1088
|
+
onClick: self.toggleHeaderOverview,
|
|
1089
|
+
disabled: self.hideHeader,
|
|
1090
|
+
},
|
|
1091
|
+
{
|
|
1092
|
+
label: 'Show no tracks active button',
|
|
1093
|
+
type: 'checkbox',
|
|
1094
|
+
checked: !self.hideNoTracksActive,
|
|
1095
|
+
onClick: self.toggleNoTracksActive,
|
|
1096
|
+
},
|
|
1097
|
+
{
|
|
1098
|
+
label: 'Show guidelines',
|
|
1099
|
+
type: 'checkbox',
|
|
1100
|
+
checked: self.showGridlines,
|
|
1101
|
+
onClick: self.toggleShowGridlines,
|
|
1102
|
+
},
|
|
1103
|
+
...(canShowCytobands
|
|
1104
|
+
? [
|
|
1105
|
+
{
|
|
1106
|
+
label: 'Show ideogram',
|
|
1107
|
+
type: 'checkbox' as const,
|
|
1108
|
+
checked: self.showCytobands,
|
|
1109
|
+
onClick: () => self.setShowCytobands(!showCytobands),
|
|
1110
|
+
},
|
|
1111
|
+
]
|
|
1112
|
+
: []),
|
|
1113
|
+
],
|
|
827
1114
|
},
|
|
828
|
-
...(canShowCytobands
|
|
829
|
-
? [
|
|
830
|
-
{
|
|
831
|
-
label: 'Show ideogram',
|
|
832
|
-
icon: VisibilityIcon,
|
|
833
|
-
type: 'checkbox' as const,
|
|
834
|
-
checked: self.showCytobands,
|
|
835
|
-
onClick: () => self.setShowCytobands(!showCytobands),
|
|
836
|
-
},
|
|
837
|
-
]
|
|
838
|
-
: []),
|
|
839
1115
|
{
|
|
840
1116
|
label: 'Track labels',
|
|
841
1117
|
icon: LabelIcon,
|
|
@@ -883,6 +1159,15 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
883
1159
|
let currentlyCalculatedStaticBlocks: BlockSet | undefined
|
|
884
1160
|
let stringifiedCurrentlyCalculatedStaticBlocks = ''
|
|
885
1161
|
return {
|
|
1162
|
+
/**
|
|
1163
|
+
* #getter
|
|
1164
|
+
* static blocks are an important concept jbrowse uses to avoid
|
|
1165
|
+
* re-rendering when you scroll to the side. when you horizontally
|
|
1166
|
+
* scroll to the right, old blocks to the left may be removed, and
|
|
1167
|
+
* new blocks may be instantiated on the right. tracks may use the
|
|
1168
|
+
* static blocks to render their data for the region represented by
|
|
1169
|
+
* the block
|
|
1170
|
+
*/
|
|
886
1171
|
get staticBlocks() {
|
|
887
1172
|
const ret = calculateStaticBlocks(self)
|
|
888
1173
|
const sret = JSON.stringify(ret)
|
|
@@ -892,33 +1177,58 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
892
1177
|
}
|
|
893
1178
|
return currentlyCalculatedStaticBlocks as BlockSet
|
|
894
1179
|
},
|
|
895
|
-
|
|
1180
|
+
/**
|
|
1181
|
+
* #getter
|
|
1182
|
+
* dynamic blocks represent the exact coordinates of the currently
|
|
1183
|
+
* visible genome regions on the screen. they are similar to static
|
|
1184
|
+
* blocks, but statcic blocks can go offscreen while dynamic blocks
|
|
1185
|
+
* represent exactly what is on screen
|
|
1186
|
+
*/
|
|
896
1187
|
get dynamicBlocks() {
|
|
897
1188
|
return calculateDynamicBlocks(self)
|
|
898
1189
|
},
|
|
899
|
-
|
|
1190
|
+
/**
|
|
1191
|
+
* #getter
|
|
1192
|
+
* rounded dynamic blocks are dynamic blocks without fractions of bp
|
|
1193
|
+
*/
|
|
900
1194
|
get roundedDynamicBlocks() {
|
|
901
|
-
return this.dynamicBlocks.contentBlocks.map(
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1195
|
+
return this.dynamicBlocks.contentBlocks.map(
|
|
1196
|
+
block =>
|
|
1197
|
+
({
|
|
1198
|
+
...block,
|
|
1199
|
+
start: Math.floor(block.start),
|
|
1200
|
+
end: Math.ceil(block.end),
|
|
1201
|
+
} as BaseBlock),
|
|
1202
|
+
)
|
|
908
1203
|
},
|
|
1204
|
+
|
|
1205
|
+
/**
|
|
1206
|
+
* #getter
|
|
1207
|
+
* a single "combo-locstring" representing all the regions visible
|
|
1208
|
+
* on the screen
|
|
1209
|
+
*/
|
|
909
1210
|
get visibleLocStrings() {
|
|
910
1211
|
return calculateVisibleLocStrings(this.dynamicBlocks.contentBlocks)
|
|
911
1212
|
},
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* #getter
|
|
1216
|
+
* same as visibleLocStrings, but only updated every 300ms
|
|
1217
|
+
*/
|
|
912
1218
|
get coarseVisibleLocStrings() {
|
|
913
1219
|
return calculateVisibleLocStrings(self.coarseDynamicBlocks)
|
|
914
1220
|
},
|
|
915
1221
|
}
|
|
916
1222
|
})
|
|
917
1223
|
.actions(self => ({
|
|
1224
|
+
/**
|
|
1225
|
+
* #action
|
|
1226
|
+
*/
|
|
918
1227
|
setCoarseDynamicBlocks(blocks: BlockSet) {
|
|
919
1228
|
self.coarseDynamicBlocks = blocks.contentBlocks
|
|
920
1229
|
self.coarseTotalBp = blocks.totalBp
|
|
921
1230
|
},
|
|
1231
|
+
|
|
922
1232
|
afterAttach() {
|
|
923
1233
|
addDisposer(
|
|
924
1234
|
self,
|
|
@@ -935,6 +1245,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
935
1245
|
}))
|
|
936
1246
|
.actions(self => ({
|
|
937
1247
|
/**
|
|
1248
|
+
* #action
|
|
938
1249
|
* offset is the base-pair-offset in the displayed region, index is the index of the
|
|
939
1250
|
* displayed region in the linear genome view
|
|
940
1251
|
*
|
|
@@ -945,7 +1256,14 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
945
1256
|
moveTo(self, start, end)
|
|
946
1257
|
},
|
|
947
1258
|
|
|
948
|
-
|
|
1259
|
+
/**
|
|
1260
|
+
* #action
|
|
1261
|
+
* navigate to the given locstring
|
|
1262
|
+
*
|
|
1263
|
+
* @param locString - e.g. "chr1:1-100"
|
|
1264
|
+
* @param optAssemblyName - (optional) the assembly name to use when navigating to the locstring
|
|
1265
|
+
*/
|
|
1266
|
+
async navToLocString(locString: string, optAssemblyName?: string) {
|
|
949
1267
|
const { assemblyNames } = self
|
|
950
1268
|
const { assemblyManager } = getSession(self)
|
|
951
1269
|
const { isValidRefName } = assemblyManager
|
|
@@ -956,6 +1274,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
956
1274
|
.map(f => f.trim())
|
|
957
1275
|
.filter(f => !!f)
|
|
958
1276
|
|
|
1277
|
+
if (assemblyName) {
|
|
1278
|
+
await assemblyManager.waitForAssembly(assemblyName)
|
|
1279
|
+
}
|
|
1280
|
+
|
|
959
1281
|
// first try interpreting as a whitespace-separated sequence of
|
|
960
1282
|
// multiple locstrings
|
|
961
1283
|
try {
|
|
@@ -981,34 +1303,38 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
981
1303
|
}
|
|
982
1304
|
}
|
|
983
1305
|
|
|
984
|
-
const locations =
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1306
|
+
const locations = await Promise.all(
|
|
1307
|
+
parsedLocStrings?.map(async region => {
|
|
1308
|
+
const asmName = region.assemblyName || assemblyName
|
|
1309
|
+
const asm = await assemblyManager.waitForAssembly(asmName)
|
|
1310
|
+
const { refName } = region
|
|
1311
|
+
if (!asm) {
|
|
1312
|
+
throw new Error(`assembly ${asmName} not found`)
|
|
1313
|
+
}
|
|
1314
|
+
const { regions } = asm
|
|
1315
|
+
if (!regions) {
|
|
1316
|
+
throw new Error(`regions not loaded yet for ${asmName}`)
|
|
1317
|
+
}
|
|
1318
|
+
const canonicalRefName = asm.getCanonicalRefName(region.refName)
|
|
1319
|
+
if (!canonicalRefName) {
|
|
1320
|
+
throw new Error(
|
|
1321
|
+
`Could not find refName ${refName} in ${asm.name}`,
|
|
1322
|
+
)
|
|
1323
|
+
}
|
|
1324
|
+
const parentRegion = regions.find(
|
|
1325
|
+
r => r.refName === canonicalRefName,
|
|
1326
|
+
)
|
|
1327
|
+
if (!parentRegion) {
|
|
1328
|
+
throw new Error(`Could not find refName ${refName} in ${asmName}`)
|
|
1329
|
+
}
|
|
1005
1330
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1331
|
+
return {
|
|
1332
|
+
...region,
|
|
1333
|
+
assemblyName: asmName,
|
|
1334
|
+
parentRegion,
|
|
1335
|
+
}
|
|
1336
|
+
}),
|
|
1337
|
+
)
|
|
1012
1338
|
|
|
1013
1339
|
if (locations.length === 1) {
|
|
1014
1340
|
const loc = locations[0]
|
|
@@ -1032,6 +1358,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
1032
1358
|
},
|
|
1033
1359
|
|
|
1034
1360
|
/**
|
|
1361
|
+
* #action
|
|
1035
1362
|
* Navigate to a location based on its refName and optionally start, end,
|
|
1036
1363
|
* and assemblyName. Can handle if there are multiple displayedRegions
|
|
1037
1364
|
* from same refName. Only navigates to a location if it is entirely
|
|
@@ -1040,12 +1367,15 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
1040
1367
|
*
|
|
1041
1368
|
* Throws an error if navigation was unsuccessful
|
|
1042
1369
|
*
|
|
1043
|
-
* @param
|
|
1370
|
+
* @param query - a proposed location to navigate to
|
|
1044
1371
|
*/
|
|
1045
1372
|
navTo(query: NavLocation) {
|
|
1046
1373
|
this.navToMultiple([query])
|
|
1047
1374
|
},
|
|
1048
1375
|
|
|
1376
|
+
/**
|
|
1377
|
+
* #action
|
|
1378
|
+
*/
|
|
1049
1379
|
navToMultiple(locations: NavLocation[]) {
|
|
1050
1380
|
const firstLocation = locations[0]
|
|
1051
1381
|
let { refName } = firstLocation
|
|
@@ -1131,18 +1461,14 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
1131
1461
|
)
|
|
1132
1462
|
return
|
|
1133
1463
|
}
|
|
1134
|
-
let
|
|
1135
|
-
let
|
|
1136
|
-
let
|
|
1137
|
-
for (
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
const location = locations[locationIndex]
|
|
1143
|
-
const region = self.displayedRegions[index + locationIndex]
|
|
1144
|
-
locationStart = location.start || region.start
|
|
1145
|
-
locationEnd = location.end || region.end
|
|
1464
|
+
let idx = 0
|
|
1465
|
+
let start = 0
|
|
1466
|
+
let end = 0
|
|
1467
|
+
for (idx; idx < locations.length; idx++) {
|
|
1468
|
+
const location = locations[idx]
|
|
1469
|
+
const region = self.displayedRegions[index + idx]
|
|
1470
|
+
start = location.start || region.start
|
|
1471
|
+
end = location.end || region.end
|
|
1146
1472
|
if (location.refName !== region.refName) {
|
|
1147
1473
|
throw new Error(
|
|
1148
1474
|
`Entered location ${assembleLocString(
|
|
@@ -1151,10 +1477,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
1151
1477
|
)
|
|
1152
1478
|
}
|
|
1153
1479
|
}
|
|
1154
|
-
|
|
1480
|
+
idx -= 1
|
|
1155
1481
|
const startDisplayedRegion = self.displayedRegions[index]
|
|
1156
|
-
const endDisplayedRegion =
|
|
1157
|
-
self.displayedRegions[index + locationIndex]
|
|
1482
|
+
const endDisplayedRegion = self.displayedRegions[index + idx]
|
|
1158
1483
|
this.moveTo(
|
|
1159
1484
|
{
|
|
1160
1485
|
index,
|
|
@@ -1163,10 +1488,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
1163
1488
|
: s - startDisplayedRegion.start,
|
|
1164
1489
|
},
|
|
1165
1490
|
{
|
|
1166
|
-
index: index +
|
|
1491
|
+
index: index + idx,
|
|
1167
1492
|
offset: endDisplayedRegion.reversed
|
|
1168
|
-
? endDisplayedRegion.end -
|
|
1169
|
-
:
|
|
1493
|
+
? endDisplayedRegion.end - start
|
|
1494
|
+
: end - endDisplayedRegion.start,
|
|
1170
1495
|
},
|
|
1171
1496
|
)
|
|
1172
1497
|
return
|
|
@@ -1179,6 +1504,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
1179
1504
|
},
|
|
1180
1505
|
}))
|
|
1181
1506
|
.views(self => ({
|
|
1507
|
+
/**
|
|
1508
|
+
* #method
|
|
1509
|
+
*/
|
|
1182
1510
|
rubberBandMenuItems(): MenuItem[] {
|
|
1183
1511
|
return [
|
|
1184
1512
|
{
|
|
@@ -1197,6 +1525,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
1197
1525
|
]
|
|
1198
1526
|
},
|
|
1199
1527
|
|
|
1528
|
+
/**
|
|
1529
|
+
* #method
|
|
1530
|
+
*/
|
|
1200
1531
|
bpToPx({
|
|
1201
1532
|
refName,
|
|
1202
1533
|
coord,
|
|
@@ -1210,6 +1541,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
1210
1541
|
},
|
|
1211
1542
|
|
|
1212
1543
|
/**
|
|
1544
|
+
* #method
|
|
1213
1545
|
* scrolls the view to center on the given bp. if that is not in any
|
|
1214
1546
|
* of the displayed regions, does nothing
|
|
1215
1547
|
* @param coord - basepair at which you want to center the view
|
|
@@ -1227,10 +1559,16 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
1227
1559
|
}
|
|
1228
1560
|
},
|
|
1229
1561
|
|
|
1562
|
+
/**
|
|
1563
|
+
* #method
|
|
1564
|
+
*/
|
|
1230
1565
|
pxToBp(px: number) {
|
|
1231
1566
|
return pxToBp(self, px)
|
|
1232
1567
|
},
|
|
1233
1568
|
|
|
1569
|
+
/**
|
|
1570
|
+
* #getter
|
|
1571
|
+
*/
|
|
1234
1572
|
get centerLineInfo() {
|
|
1235
1573
|
return self.displayedRegions.length
|
|
1236
1574
|
? this.pxToBp(self.width / 2)
|