@jbrowse/plugin-linear-genome-view 2.1.6 → 2.2.0
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/models/BaseLinearDisplayModel.d.ts +146 -1
- 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/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 +77 -6
- package/dist/LinearBasicDisplay/model.js +167 -111
- package/dist/LinearBasicDisplay/model.js.map +1 -1
- package/dist/LinearGenomeView/components/ImportForm.js +34 -28
- package/dist/LinearGenomeView/components/ImportForm.js.map +1 -1
- package/dist/LinearGenomeView/components/LinearGenomeView.js +1 -21
- package/dist/LinearGenomeView/components/LinearGenomeView.js.map +1 -1
- package/dist/LinearGenomeView/components/LinearGenomeViewSvg.js +7 -5
- package/dist/LinearGenomeView/components/LinearGenomeViewSvg.js.map +1 -1
- package/dist/LinearGenomeView/components/OverviewScaleBar.d.ts +26 -34
- package/dist/LinearGenomeView/index.d.ts +189 -15
- package/dist/LinearGenomeView/index.js +266 -27
- package/dist/LinearGenomeView/index.js.map +1 -1
- package/dist/index.d.ts +12 -84
- 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/models/BaseLinearDisplayModel.d.ts +146 -1
- 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/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 +77 -6
- package/esm/LinearBasicDisplay/model.js +167 -111
- package/esm/LinearBasicDisplay/model.js.map +1 -1
- package/esm/LinearGenomeView/components/ImportForm.js +36 -30
- package/esm/LinearGenomeView/components/ImportForm.js.map +1 -1
- package/esm/LinearGenomeView/components/LinearGenomeView.js +2 -22
- package/esm/LinearGenomeView/components/LinearGenomeView.js.map +1 -1
- package/esm/LinearGenomeView/components/LinearGenomeViewSvg.js +7 -5
- package/esm/LinearGenomeView/components/LinearGenomeViewSvg.js.map +1 -1
- package/esm/LinearGenomeView/components/OverviewScaleBar.d.ts +26 -34
- package/esm/LinearGenomeView/index.d.ts +189 -15
- package/esm/LinearGenomeView/index.js +266 -27
- package/esm/LinearGenomeView/index.js.map +1 -1
- package/esm/index.d.ts +12 -84
- 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 +22 -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 +79 -63
- package/src/LinearGenomeView/components/LinearGenomeView.tsx +2 -26
- package/src/LinearGenomeView/components/LinearGenomeViewSvg.tsx +21 -18
- package/src/LinearGenomeView/components/__snapshots__/LinearGenomeView.test.tsx.snap +204 -204
- package/src/LinearGenomeView/index.test.ts +33 -26
- package/src/LinearGenomeView/index.tsx +317 -60
- package/src/index.ts +6 -46
|
@@ -78,6 +78,8 @@ export interface BpOffset {
|
|
|
78
78
|
export interface ExportSvgOptions {
|
|
79
79
|
rasterizeLayers?: boolean
|
|
80
80
|
filename?: string
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
82
|
+
Wrapper?: React.FC<any>
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
function calculateVisibleLocStrings(contentBlocks: BaseBlock[]) {
|
|
@@ -85,7 +87,7 @@ function calculateVisibleLocStrings(contentBlocks: BaseBlock[]) {
|
|
|
85
87
|
return ''
|
|
86
88
|
}
|
|
87
89
|
const isSingleAssemblyName = contentBlocks.every(
|
|
88
|
-
|
|
90
|
+
b => b.assemblyName === contentBlocks[0].assemblyName,
|
|
89
91
|
)
|
|
90
92
|
const locs = contentBlocks.map(block =>
|
|
91
93
|
assembleLocString({
|
|
@@ -119,41 +121,107 @@ function localStorageGetItem(item: string) {
|
|
|
119
121
|
: undefined
|
|
120
122
|
}
|
|
121
123
|
|
|
124
|
+
/**
|
|
125
|
+
* #stateModel LinearGenomeView
|
|
126
|
+
*/
|
|
122
127
|
export function stateModelFactory(pluginManager: PluginManager) {
|
|
123
128
|
return types
|
|
124
129
|
.compose(
|
|
125
130
|
BaseViewModel,
|
|
126
131
|
types.model('LinearGenomeView', {
|
|
132
|
+
/**
|
|
133
|
+
* #property
|
|
134
|
+
*/
|
|
127
135
|
id: ElementId,
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* #property
|
|
139
|
+
*/
|
|
128
140
|
type: types.literal('LinearGenomeView'),
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* #property
|
|
144
|
+
* corresponds roughly to the horizontal scroll of the LGV
|
|
145
|
+
*/
|
|
129
146
|
offsetPx: 0,
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* #property
|
|
150
|
+
* corresponds roughly to the zoom level, base-pairs per pixel
|
|
151
|
+
*/
|
|
130
152
|
bpPerPx: 1,
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* #property
|
|
156
|
+
* currently displayed regions, can be a single chromosome, arbitrary subsections,
|
|
157
|
+
* or the entire set of chromosomes in the genome, but it not advised to use the
|
|
158
|
+
* entire set of chromosomes if your assembly is very fragmented
|
|
159
|
+
*/
|
|
131
160
|
displayedRegions: types.array(MUIRegion),
|
|
132
161
|
|
|
133
|
-
|
|
134
|
-
|
|
162
|
+
/**
|
|
163
|
+
* #property
|
|
164
|
+
* array of currently displayed tracks state models instances
|
|
165
|
+
*/
|
|
135
166
|
tracks: types.array(
|
|
136
167
|
pluginManager.pluggableMstType('track', 'stateModel'),
|
|
137
168
|
),
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* #property
|
|
172
|
+
* array of currently displayed tracks state model's
|
|
173
|
+
*/
|
|
138
174
|
hideHeader: false,
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* #property
|
|
178
|
+
*/
|
|
139
179
|
hideHeaderOverview: false,
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* #property
|
|
183
|
+
*/
|
|
140
184
|
hideNoTracksActive: false,
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* #property
|
|
188
|
+
*/
|
|
141
189
|
trackSelectorType: types.optional(
|
|
142
190
|
types.enumeration(['hierarchical']),
|
|
143
191
|
'hierarchical',
|
|
144
192
|
),
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* #property
|
|
196
|
+
* how to display the track labels, can be "overlapping", "offset", or "hidden"
|
|
197
|
+
*/
|
|
145
198
|
trackLabels: types.optional(
|
|
146
199
|
types.string,
|
|
147
200
|
() => localStorageGetItem('lgv-trackLabels') || 'overlapping',
|
|
148
201
|
),
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* #property
|
|
205
|
+
* show the "center line"
|
|
206
|
+
*/
|
|
149
207
|
showCenterLine: types.optional(types.boolean, () => {
|
|
150
208
|
const setting = localStorageGetItem('lgv-showCenterLine')
|
|
151
209
|
return setting !== undefined && setting !== null ? !!+setting : false
|
|
152
210
|
}),
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* #property
|
|
214
|
+
* show the "cytobands" in the overview scale bar
|
|
215
|
+
*/
|
|
153
216
|
showCytobandsSetting: types.optional(types.boolean, () => {
|
|
154
217
|
const setting = localStorageGetItem('lgv-showCytobands')
|
|
155
218
|
return setting !== undefined && setting !== null ? !!+setting : true
|
|
156
219
|
}),
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* #property
|
|
223
|
+
* show the "gridlines" in the track area
|
|
224
|
+
*/
|
|
157
225
|
showGridlines: true,
|
|
158
226
|
}),
|
|
159
227
|
)
|
|
@@ -178,6 +246,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
178
246
|
seqDialogDisplayed: false,
|
|
179
247
|
}))
|
|
180
248
|
.views(self => ({
|
|
249
|
+
/**
|
|
250
|
+
* #getter
|
|
251
|
+
*/
|
|
181
252
|
get width(): number {
|
|
182
253
|
if (self.volatileWidth === undefined) {
|
|
183
254
|
throw new Error(
|
|
@@ -186,10 +257,16 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
186
257
|
}
|
|
187
258
|
return self.volatileWidth
|
|
188
259
|
},
|
|
260
|
+
/**
|
|
261
|
+
* #getter
|
|
262
|
+
*/
|
|
189
263
|
get interRegionPaddingWidth() {
|
|
190
264
|
return INTER_REGION_PADDING_WIDTH
|
|
191
265
|
},
|
|
192
266
|
|
|
267
|
+
/**
|
|
268
|
+
* #getter
|
|
269
|
+
*/
|
|
193
270
|
get assemblyNames() {
|
|
194
271
|
return [
|
|
195
272
|
...new Set(self.displayedRegions.map(region => region.assemblyName)),
|
|
@@ -361,37 +438,61 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
361
438
|
},
|
|
362
439
|
}))
|
|
363
440
|
.actions(self => ({
|
|
441
|
+
/**
|
|
442
|
+
* #action
|
|
443
|
+
*/
|
|
364
444
|
setShowCytobands(flag: boolean) {
|
|
365
445
|
self.showCytobandsSetting = flag
|
|
366
446
|
localStorage.setItem('lgv-showCytobands', `${+flag}`)
|
|
367
447
|
},
|
|
448
|
+
/**
|
|
449
|
+
* #action
|
|
450
|
+
*/
|
|
368
451
|
setWidth(newWidth: number) {
|
|
369
452
|
self.volatileWidth = newWidth
|
|
370
453
|
},
|
|
454
|
+
/**
|
|
455
|
+
* #action
|
|
456
|
+
*/
|
|
371
457
|
setError(error: Error | undefined) {
|
|
372
458
|
self.volatileError = error
|
|
373
459
|
},
|
|
374
|
-
|
|
460
|
+
/**
|
|
461
|
+
* #action
|
|
462
|
+
*/
|
|
375
463
|
toggleHeader() {
|
|
376
464
|
self.hideHeader = !self.hideHeader
|
|
377
465
|
},
|
|
378
|
-
|
|
466
|
+
/**
|
|
467
|
+
* #action
|
|
468
|
+
*/
|
|
379
469
|
toggleHeaderOverview() {
|
|
380
470
|
self.hideHeaderOverview = !self.hideHeaderOverview
|
|
381
471
|
},
|
|
472
|
+
/**
|
|
473
|
+
* #action
|
|
474
|
+
*/
|
|
382
475
|
toggleNoTracksActive() {
|
|
383
476
|
self.hideNoTracksActive = !self.hideNoTracksActive
|
|
384
477
|
},
|
|
478
|
+
/**
|
|
479
|
+
* #action
|
|
480
|
+
*/
|
|
385
481
|
toggleShowGridlines() {
|
|
386
482
|
self.showGridlines = !self.showGridlines
|
|
387
483
|
},
|
|
388
|
-
|
|
484
|
+
/**
|
|
485
|
+
* #action
|
|
486
|
+
*/
|
|
389
487
|
scrollTo(offsetPx: number) {
|
|
390
488
|
const newOffsetPx = clamp(offsetPx, self.minOffset, self.maxOffset)
|
|
391
489
|
self.offsetPx = newOffsetPx
|
|
392
490
|
return newOffsetPx
|
|
393
491
|
},
|
|
394
492
|
|
|
493
|
+
/**
|
|
494
|
+
* #action
|
|
495
|
+
*/
|
|
395
496
|
zoomTo(bpPerPx: number) {
|
|
396
497
|
const newBpPerPx = clamp(bpPerPx, self.minBpPerPx, self.maxBpPerPx)
|
|
397
498
|
if (newBpPerPx === self.bpPerPx) {
|
|
@@ -416,26 +517,41 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
416
517
|
return newBpPerPx
|
|
417
518
|
},
|
|
418
519
|
|
|
520
|
+
/**
|
|
521
|
+
* #action
|
|
522
|
+
* sets offsets used in the get sequence dialog
|
|
523
|
+
*/
|
|
419
524
|
setOffsets(left?: BpOffset, right?: BpOffset) {
|
|
420
|
-
// sets offsets used in the get sequence dialog
|
|
421
525
|
self.leftOffset = left
|
|
422
526
|
self.rightOffset = right
|
|
423
527
|
},
|
|
424
528
|
|
|
529
|
+
/**
|
|
530
|
+
* #action
|
|
531
|
+
*/
|
|
425
532
|
setSearchResults(results?: BaseResult[], query?: string) {
|
|
426
533
|
self.searchResults = results
|
|
427
534
|
self.searchQuery = query
|
|
428
535
|
},
|
|
429
536
|
|
|
537
|
+
/**
|
|
538
|
+
* #action
|
|
539
|
+
*/
|
|
430
540
|
setGetSequenceDialogOpen(open: boolean) {
|
|
431
541
|
self.seqDialogDisplayed = open
|
|
432
542
|
},
|
|
433
543
|
|
|
544
|
+
/**
|
|
545
|
+
* #action
|
|
546
|
+
*/
|
|
434
547
|
setNewView(bpPerPx: number, offsetPx: number) {
|
|
435
548
|
this.zoomTo(bpPerPx)
|
|
436
549
|
this.scrollTo(offsetPx)
|
|
437
550
|
},
|
|
438
551
|
|
|
552
|
+
/**
|
|
553
|
+
* #action
|
|
554
|
+
*/
|
|
439
555
|
horizontallyFlip() {
|
|
440
556
|
self.displayedRegions = cast(
|
|
441
557
|
self.displayedRegions
|
|
@@ -446,6 +562,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
446
562
|
this.scrollTo(self.totalBp / self.bpPerPx - self.offsetPx - self.width)
|
|
447
563
|
},
|
|
448
564
|
|
|
565
|
+
/**
|
|
566
|
+
* #action
|
|
567
|
+
*/
|
|
449
568
|
showTrack(
|
|
450
569
|
trackId: string,
|
|
451
570
|
initialSnapshot = {},
|
|
@@ -500,6 +619,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
500
619
|
},
|
|
501
620
|
}))
|
|
502
621
|
.actions(self => ({
|
|
622
|
+
/**
|
|
623
|
+
* #action
|
|
624
|
+
*/
|
|
503
625
|
moveTrack(movingId: string, targetId: string) {
|
|
504
626
|
const oldIndex = self.tracks.findIndex(track => track.id === movingId)
|
|
505
627
|
if (oldIndex === -1) {
|
|
@@ -514,6 +636,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
514
636
|
self.tracks.splice(newIndex, 0, track)
|
|
515
637
|
},
|
|
516
638
|
|
|
639
|
+
/**
|
|
640
|
+
* #action
|
|
641
|
+
*/
|
|
517
642
|
closeView() {
|
|
518
643
|
const parent = getContainingView(self)
|
|
519
644
|
if (parent) {
|
|
@@ -527,6 +652,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
527
652
|
}
|
|
528
653
|
},
|
|
529
654
|
|
|
655
|
+
/**
|
|
656
|
+
* #action
|
|
657
|
+
*/
|
|
530
658
|
toggleTrack(trackId: string) {
|
|
531
659
|
// if we have any tracks with that configuration, turn them off
|
|
532
660
|
const hiddenCount = self.hideTrack(trackId)
|
|
@@ -536,21 +664,33 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
536
664
|
}
|
|
537
665
|
},
|
|
538
666
|
|
|
667
|
+
/**
|
|
668
|
+
* #action
|
|
669
|
+
*/
|
|
539
670
|
setTrackLabels(setting: 'overlapping' | 'offset' | 'hidden') {
|
|
540
671
|
self.trackLabels = setting
|
|
541
672
|
localStorage.setItem('lgv-trackLabels', setting)
|
|
542
673
|
},
|
|
543
674
|
|
|
675
|
+
/**
|
|
676
|
+
* #action
|
|
677
|
+
*/
|
|
544
678
|
toggleCenterLine() {
|
|
545
679
|
self.showCenterLine = !self.showCenterLine
|
|
546
680
|
localStorage.setItem('lgv-showCenterLine', `${+self.showCenterLine}`)
|
|
547
681
|
},
|
|
548
682
|
|
|
683
|
+
/**
|
|
684
|
+
* #action
|
|
685
|
+
*/
|
|
549
686
|
setDisplayedRegions(regions: Region[]) {
|
|
550
687
|
self.displayedRegions = cast(regions)
|
|
551
688
|
self.zoomTo(self.bpPerPx)
|
|
552
689
|
},
|
|
553
690
|
|
|
691
|
+
/**
|
|
692
|
+
* #action
|
|
693
|
+
*/
|
|
554
694
|
activateTrackSelector() {
|
|
555
695
|
if (self.trackSelectorType === 'hierarchical') {
|
|
556
696
|
const session = getSession(self)
|
|
@@ -568,6 +708,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
568
708
|
},
|
|
569
709
|
|
|
570
710
|
/**
|
|
711
|
+
* #method
|
|
571
712
|
* Helper method for the fetchSequence.
|
|
572
713
|
* Retrieves the corresponding regions that were selected by the rubberband
|
|
573
714
|
*
|
|
@@ -592,11 +733,17 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
592
733
|
}))
|
|
593
734
|
},
|
|
594
735
|
|
|
595
|
-
|
|
736
|
+
/**
|
|
737
|
+
* #action
|
|
738
|
+
* schedule something to be run after the next time displayedRegions is set
|
|
739
|
+
*/
|
|
596
740
|
afterDisplayedRegionsSet(cb: Function) {
|
|
597
741
|
self.afterDisplayedRegionsSetCallbacks.push(cb)
|
|
598
742
|
},
|
|
599
743
|
|
|
744
|
+
/**
|
|
745
|
+
* #action
|
|
746
|
+
*/
|
|
600
747
|
horizontalScroll(distance: number) {
|
|
601
748
|
const oldOffsetPx = self.offsetPx
|
|
602
749
|
// newOffsetPx is the actual offset after the scroll is clamped
|
|
@@ -604,34 +751,38 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
604
751
|
return newOffsetPx - oldOffsetPx
|
|
605
752
|
},
|
|
606
753
|
|
|
754
|
+
/**
|
|
755
|
+
* #action
|
|
756
|
+
*/
|
|
607
757
|
center() {
|
|
608
758
|
const centerBp = self.totalBp / 2
|
|
609
759
|
const centerPx = centerBp / self.bpPerPx
|
|
610
760
|
self.scrollTo(Math.round(centerPx - self.width / 2))
|
|
611
761
|
},
|
|
612
762
|
|
|
763
|
+
/**
|
|
764
|
+
* #action
|
|
765
|
+
*/
|
|
613
766
|
showAllRegions() {
|
|
614
767
|
self.zoomTo(self.maxBpPerPx)
|
|
615
768
|
this.center()
|
|
616
769
|
},
|
|
617
770
|
|
|
771
|
+
/**
|
|
772
|
+
* #action
|
|
773
|
+
*/
|
|
618
774
|
showAllRegionsInAssembly(assemblyName?: string) {
|
|
619
775
|
const session = getSession(self)
|
|
620
776
|
const { assemblyManager } = session
|
|
621
777
|
if (!assemblyName) {
|
|
622
|
-
const
|
|
623
|
-
|
|
624
|
-
self.displayedRegions.map(region => region.assemblyName),
|
|
625
|
-
),
|
|
626
|
-
]
|
|
627
|
-
if (assemblyNames.length > 1) {
|
|
778
|
+
const names = new Set(self.displayedRegions.map(r => r.assemblyName))
|
|
779
|
+
if (names.size > 1) {
|
|
628
780
|
session.notify(
|
|
629
|
-
`Can't perform
|
|
781
|
+
`Can't perform operation with multiple assemblies currently`,
|
|
630
782
|
)
|
|
631
783
|
return
|
|
632
784
|
}
|
|
633
|
-
|
|
634
|
-
;[assemblyName] = assemblyNames
|
|
785
|
+
;[assemblyName] = [...names]
|
|
635
786
|
}
|
|
636
787
|
const assembly = assemblyManager.get(assemblyName)
|
|
637
788
|
if (assembly) {
|
|
@@ -644,14 +795,24 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
644
795
|
}
|
|
645
796
|
},
|
|
646
797
|
|
|
798
|
+
/**
|
|
799
|
+
* #action
|
|
800
|
+
*/
|
|
647
801
|
setDraggingTrackId(idx?: string) {
|
|
648
802
|
self.draggingTrackId = idx
|
|
649
803
|
},
|
|
650
804
|
|
|
805
|
+
/**
|
|
806
|
+
* #action
|
|
807
|
+
*/
|
|
651
808
|
setScaleFactor(factor: number) {
|
|
652
809
|
self.scaleFactor = factor
|
|
653
810
|
},
|
|
654
|
-
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* #action
|
|
814
|
+
* this "clears the view" and makes the view return to the import form
|
|
815
|
+
*/
|
|
655
816
|
clearView() {
|
|
656
817
|
this.setDisplayedRegions([])
|
|
657
818
|
self.tracks.clear()
|
|
@@ -661,6 +822,11 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
661
822
|
self.scrollTo(0)
|
|
662
823
|
self.zoomTo(10)
|
|
663
824
|
},
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* #action
|
|
828
|
+
* creates an svg export and save using FileSaver
|
|
829
|
+
*/
|
|
664
830
|
async exportSvg(opts: ExportSvgOptions = {}) {
|
|
665
831
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
666
832
|
const html = await renderToSvg(self as any, opts)
|
|
@@ -671,6 +837,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
671
837
|
.actions(self => {
|
|
672
838
|
let cancelLastAnimation = () => {}
|
|
673
839
|
|
|
840
|
+
/**
|
|
841
|
+
* #action
|
|
842
|
+
* perform animated slide
|
|
843
|
+
*/
|
|
674
844
|
function slide(viewWidths: number) {
|
|
675
845
|
const [animate, cancelAnimation] = springAnimate(
|
|
676
846
|
self.offsetPx,
|
|
@@ -687,6 +857,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
687
857
|
.actions(self => {
|
|
688
858
|
let cancelLastAnimation = () => {}
|
|
689
859
|
|
|
860
|
+
/**
|
|
861
|
+
* #action
|
|
862
|
+
* perform animated zoom
|
|
863
|
+
*/
|
|
690
864
|
function zoom(targetBpPerPx: number) {
|
|
691
865
|
self.zoomTo(self.bpPerPx)
|
|
692
866
|
if (
|
|
@@ -715,20 +889,32 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
715
889
|
return { zoom }
|
|
716
890
|
})
|
|
717
891
|
.views(self => ({
|
|
892
|
+
/**
|
|
893
|
+
* #getter
|
|
894
|
+
*/
|
|
718
895
|
get canShowCytobands() {
|
|
719
896
|
return self.displayedRegions.length === 1 && this.anyCytobandsExist
|
|
720
897
|
},
|
|
898
|
+
/**
|
|
899
|
+
* #getter
|
|
900
|
+
*/
|
|
721
901
|
get showCytobands() {
|
|
722
902
|
return this.canShowCytobands && self.showCytobandsSetting
|
|
723
903
|
},
|
|
904
|
+
/**
|
|
905
|
+
* #getter
|
|
906
|
+
*/
|
|
724
907
|
get anyCytobandsExist() {
|
|
725
908
|
const { assemblyManager } = getSession(self)
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
asm => assemblyManager.get(asm)?.cytobands?.length,
|
|
909
|
+
return self.assemblyNames.some(
|
|
910
|
+
a => assemblyManager.get(a)?.cytobands?.length,
|
|
729
911
|
)
|
|
730
912
|
},
|
|
731
|
-
|
|
913
|
+
/**
|
|
914
|
+
* #getter
|
|
915
|
+
* the cytoband is displayed to the right of the chromosome name,
|
|
916
|
+
* and that offset is calculated manually with this method
|
|
917
|
+
*/
|
|
732
918
|
get cytobandOffset() {
|
|
733
919
|
return this.showCytobands
|
|
734
920
|
? measureText(self.displayedRegions[0].refName, 12) + 15
|
|
@@ -736,6 +922,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
736
922
|
},
|
|
737
923
|
}))
|
|
738
924
|
.views(self => ({
|
|
925
|
+
/**
|
|
926
|
+
* #method
|
|
927
|
+
* return the view menu items
|
|
928
|
+
*/
|
|
739
929
|
menuItems(): MenuItem[] {
|
|
740
930
|
const { canShowCytobands, showCytobands } = self
|
|
741
931
|
const session = getSession(self)
|
|
@@ -883,6 +1073,15 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
883
1073
|
let currentlyCalculatedStaticBlocks: BlockSet | undefined
|
|
884
1074
|
let stringifiedCurrentlyCalculatedStaticBlocks = ''
|
|
885
1075
|
return {
|
|
1076
|
+
/**
|
|
1077
|
+
* #getter
|
|
1078
|
+
* static blocks are an important concept jbrowse uses to avoid
|
|
1079
|
+
* re-rendering when you scroll to the side. when you horizontally
|
|
1080
|
+
* scroll to the right, old blocks to the left may be removed, and
|
|
1081
|
+
* new blocks may be instantiated on the right. tracks may use the
|
|
1082
|
+
* static blocks to render their data for the region represented by
|
|
1083
|
+
* the block
|
|
1084
|
+
*/
|
|
886
1085
|
get staticBlocks() {
|
|
887
1086
|
const ret = calculateStaticBlocks(self)
|
|
888
1087
|
const sret = JSON.stringify(ret)
|
|
@@ -892,33 +1091,58 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
892
1091
|
}
|
|
893
1092
|
return currentlyCalculatedStaticBlocks as BlockSet
|
|
894
1093
|
},
|
|
895
|
-
|
|
1094
|
+
/**
|
|
1095
|
+
* #getter
|
|
1096
|
+
* dynamic blocks represent the exact coordinates of the currently
|
|
1097
|
+
* visible genome regions on the screen. they are similar to static
|
|
1098
|
+
* blocks, but statcic blocks can go offscreen while dynamic blocks
|
|
1099
|
+
* represent exactly what is on screen
|
|
1100
|
+
*/
|
|
896
1101
|
get dynamicBlocks() {
|
|
897
1102
|
return calculateDynamicBlocks(self)
|
|
898
1103
|
},
|
|
899
|
-
|
|
1104
|
+
/**
|
|
1105
|
+
* #getter
|
|
1106
|
+
* rounded dynamic blocks are dynamic blocks without fractions of bp
|
|
1107
|
+
*/
|
|
900
1108
|
get roundedDynamicBlocks() {
|
|
901
|
-
return this.dynamicBlocks.contentBlocks.map(
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1109
|
+
return this.dynamicBlocks.contentBlocks.map(
|
|
1110
|
+
block =>
|
|
1111
|
+
({
|
|
1112
|
+
...block,
|
|
1113
|
+
start: Math.floor(block.start),
|
|
1114
|
+
end: Math.ceil(block.end),
|
|
1115
|
+
} as BaseBlock),
|
|
1116
|
+
)
|
|
908
1117
|
},
|
|
1118
|
+
|
|
1119
|
+
/**
|
|
1120
|
+
* #getter
|
|
1121
|
+
* a single "combo-locstring" representing all the regions visible
|
|
1122
|
+
* on the screen
|
|
1123
|
+
*/
|
|
909
1124
|
get visibleLocStrings() {
|
|
910
1125
|
return calculateVisibleLocStrings(this.dynamicBlocks.contentBlocks)
|
|
911
1126
|
},
|
|
1127
|
+
|
|
1128
|
+
/**
|
|
1129
|
+
* #getter
|
|
1130
|
+
* same as visibleLocStrings, but only updated every 300ms
|
|
1131
|
+
*/
|
|
912
1132
|
get coarseVisibleLocStrings() {
|
|
913
1133
|
return calculateVisibleLocStrings(self.coarseDynamicBlocks)
|
|
914
1134
|
},
|
|
915
1135
|
}
|
|
916
1136
|
})
|
|
917
1137
|
.actions(self => ({
|
|
1138
|
+
/**
|
|
1139
|
+
* #action
|
|
1140
|
+
*/
|
|
918
1141
|
setCoarseDynamicBlocks(blocks: BlockSet) {
|
|
919
1142
|
self.coarseDynamicBlocks = blocks.contentBlocks
|
|
920
1143
|
self.coarseTotalBp = blocks.totalBp
|
|
921
1144
|
},
|
|
1145
|
+
|
|
922
1146
|
afterAttach() {
|
|
923
1147
|
addDisposer(
|
|
924
1148
|
self,
|
|
@@ -935,6 +1159,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
935
1159
|
}))
|
|
936
1160
|
.actions(self => ({
|
|
937
1161
|
/**
|
|
1162
|
+
* #action
|
|
938
1163
|
* offset is the base-pair-offset in the displayed region, index is the index of the
|
|
939
1164
|
* displayed region in the linear genome view
|
|
940
1165
|
*
|
|
@@ -945,7 +1170,14 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
945
1170
|
moveTo(self, start, end)
|
|
946
1171
|
},
|
|
947
1172
|
|
|
948
|
-
|
|
1173
|
+
/**
|
|
1174
|
+
* #action
|
|
1175
|
+
* navigate to the given locstring
|
|
1176
|
+
*
|
|
1177
|
+
* @param locString - e.g. "chr1:1-100"
|
|
1178
|
+
* @param optAssemblyName - (optional) the assembly name to use when navigating to the locstring
|
|
1179
|
+
*/
|
|
1180
|
+
async navToLocString(locString: string, optAssemblyName?: string) {
|
|
949
1181
|
const { assemblyNames } = self
|
|
950
1182
|
const { assemblyManager } = getSession(self)
|
|
951
1183
|
const { isValidRefName } = assemblyManager
|
|
@@ -956,6 +1188,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
956
1188
|
.map(f => f.trim())
|
|
957
1189
|
.filter(f => !!f)
|
|
958
1190
|
|
|
1191
|
+
if (assemblyName) {
|
|
1192
|
+
await assemblyManager.waitForAssembly(assemblyName)
|
|
1193
|
+
}
|
|
1194
|
+
|
|
959
1195
|
// first try interpreting as a whitespace-separated sequence of
|
|
960
1196
|
// multiple locstrings
|
|
961
1197
|
try {
|
|
@@ -981,34 +1217,38 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
981
1217
|
}
|
|
982
1218
|
}
|
|
983
1219
|
|
|
984
|
-
const locations =
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1220
|
+
const locations = await Promise.all(
|
|
1221
|
+
parsedLocStrings?.map(async region => {
|
|
1222
|
+
const asmName = region.assemblyName || assemblyName
|
|
1223
|
+
const asm = await assemblyManager.waitForAssembly(asmName)
|
|
1224
|
+
const { refName } = region
|
|
1225
|
+
if (!asm) {
|
|
1226
|
+
throw new Error(`assembly ${asmName} not found`)
|
|
1227
|
+
}
|
|
1228
|
+
const { regions } = asm
|
|
1229
|
+
if (!regions) {
|
|
1230
|
+
throw new Error(`regions not loaded yet for ${asmName}`)
|
|
1231
|
+
}
|
|
1232
|
+
const canonicalRefName = asm.getCanonicalRefName(region.refName)
|
|
1233
|
+
if (!canonicalRefName) {
|
|
1234
|
+
throw new Error(
|
|
1235
|
+
`Could not find refName ${refName} in ${asm.name}`,
|
|
1236
|
+
)
|
|
1237
|
+
}
|
|
1238
|
+
const parentRegion = regions.find(
|
|
1239
|
+
r => r.refName === canonicalRefName,
|
|
1240
|
+
)
|
|
1241
|
+
if (!parentRegion) {
|
|
1242
|
+
throw new Error(`Could not find refName ${refName} in ${asmName}`)
|
|
1243
|
+
}
|
|
1005
1244
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1245
|
+
return {
|
|
1246
|
+
...region,
|
|
1247
|
+
assemblyName: asmName,
|
|
1248
|
+
parentRegion,
|
|
1249
|
+
}
|
|
1250
|
+
}),
|
|
1251
|
+
)
|
|
1012
1252
|
|
|
1013
1253
|
if (locations.length === 1) {
|
|
1014
1254
|
const loc = locations[0]
|
|
@@ -1032,6 +1272,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
1032
1272
|
},
|
|
1033
1273
|
|
|
1034
1274
|
/**
|
|
1275
|
+
* #action
|
|
1035
1276
|
* Navigate to a location based on its refName and optionally start, end,
|
|
1036
1277
|
* and assemblyName. Can handle if there are multiple displayedRegions
|
|
1037
1278
|
* from same refName. Only navigates to a location if it is entirely
|
|
@@ -1040,12 +1281,15 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
1040
1281
|
*
|
|
1041
1282
|
* Throws an error if navigation was unsuccessful
|
|
1042
1283
|
*
|
|
1043
|
-
* @param
|
|
1284
|
+
* @param query - a proposed location to navigate to
|
|
1044
1285
|
*/
|
|
1045
1286
|
navTo(query: NavLocation) {
|
|
1046
1287
|
this.navToMultiple([query])
|
|
1047
1288
|
},
|
|
1048
1289
|
|
|
1290
|
+
/**
|
|
1291
|
+
* #action
|
|
1292
|
+
*/
|
|
1049
1293
|
navToMultiple(locations: NavLocation[]) {
|
|
1050
1294
|
const firstLocation = locations[0]
|
|
1051
1295
|
let { refName } = firstLocation
|
|
@@ -1179,6 +1423,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
1179
1423
|
},
|
|
1180
1424
|
}))
|
|
1181
1425
|
.views(self => ({
|
|
1426
|
+
/**
|
|
1427
|
+
* #method
|
|
1428
|
+
*/
|
|
1182
1429
|
rubberBandMenuItems(): MenuItem[] {
|
|
1183
1430
|
return [
|
|
1184
1431
|
{
|
|
@@ -1197,6 +1444,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
1197
1444
|
]
|
|
1198
1445
|
},
|
|
1199
1446
|
|
|
1447
|
+
/**
|
|
1448
|
+
* #method
|
|
1449
|
+
*/
|
|
1200
1450
|
bpToPx({
|
|
1201
1451
|
refName,
|
|
1202
1452
|
coord,
|
|
@@ -1210,6 +1460,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
1210
1460
|
},
|
|
1211
1461
|
|
|
1212
1462
|
/**
|
|
1463
|
+
* #method
|
|
1213
1464
|
* scrolls the view to center on the given bp. if that is not in any
|
|
1214
1465
|
* of the displayed regions, does nothing
|
|
1215
1466
|
* @param coord - basepair at which you want to center the view
|
|
@@ -1227,10 +1478,16 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
1227
1478
|
}
|
|
1228
1479
|
},
|
|
1229
1480
|
|
|
1481
|
+
/**
|
|
1482
|
+
* #method
|
|
1483
|
+
*/
|
|
1230
1484
|
pxToBp(px: number) {
|
|
1231
1485
|
return pxToBp(self, px)
|
|
1232
1486
|
},
|
|
1233
1487
|
|
|
1488
|
+
/**
|
|
1489
|
+
* #getter
|
|
1490
|
+
*/
|
|
1234
1491
|
get centerLineInfo() {
|
|
1235
1492
|
return self.displayedRegions.length
|
|
1236
1493
|
? this.pxToBp(self.width / 2)
|