@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.
Files changed (109) hide show
  1. package/dist/BaseLinearDisplay/components/LinearBlocks.d.ts +2 -2
  2. package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +2 -22
  3. package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js.map +1 -1
  4. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +146 -1
  5. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js +600 -464
  6. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js.map +1 -1
  7. package/dist/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.js +19 -1
  8. package/dist/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.js.map +1 -1
  9. package/dist/BasicTrack/configSchema.d.ts +3 -0
  10. package/dist/BasicTrack/configSchema.js +18 -0
  11. package/dist/BasicTrack/configSchema.js.map +1 -0
  12. package/dist/BasicTrack/index.d.ts +3 -0
  13. package/dist/BasicTrack/index.js +18 -0
  14. package/dist/BasicTrack/index.js.map +1 -0
  15. package/dist/FeatureTrack/configSchema.d.ts +3 -0
  16. package/dist/FeatureTrack/configSchema.js +21 -0
  17. package/dist/FeatureTrack/configSchema.js.map +1 -0
  18. package/dist/FeatureTrack/index.d.ts +3 -0
  19. package/dist/FeatureTrack/index.js +18 -0
  20. package/dist/FeatureTrack/index.js.map +1 -0
  21. package/dist/LinearBareDisplay/configSchema.d.ts +5 -1
  22. package/dist/LinearBareDisplay/configSchema.js +12 -1
  23. package/dist/LinearBareDisplay/configSchema.js.map +1 -1
  24. package/dist/LinearBareDisplay/model.d.ts +16 -0
  25. package/dist/LinearBareDisplay/model.js +16 -0
  26. package/dist/LinearBareDisplay/model.js.map +1 -1
  27. package/dist/LinearBasicDisplay/configSchema.d.ts +5 -1
  28. package/dist/LinearBasicDisplay/configSchema.js +16 -1
  29. package/dist/LinearBasicDisplay/configSchema.js.map +1 -1
  30. package/dist/LinearBasicDisplay/model.d.ts +77 -6
  31. package/dist/LinearBasicDisplay/model.js +167 -111
  32. package/dist/LinearBasicDisplay/model.js.map +1 -1
  33. package/dist/LinearGenomeView/components/ImportForm.js +34 -28
  34. package/dist/LinearGenomeView/components/ImportForm.js.map +1 -1
  35. package/dist/LinearGenomeView/components/LinearGenomeView.js +1 -21
  36. package/dist/LinearGenomeView/components/LinearGenomeView.js.map +1 -1
  37. package/dist/LinearGenomeView/components/LinearGenomeViewSvg.js +7 -5
  38. package/dist/LinearGenomeView/components/LinearGenomeViewSvg.js.map +1 -1
  39. package/dist/LinearGenomeView/components/OverviewScaleBar.d.ts +26 -34
  40. package/dist/LinearGenomeView/index.d.ts +189 -15
  41. package/dist/LinearGenomeView/index.js +266 -27
  42. package/dist/LinearGenomeView/index.js.map +1 -1
  43. package/dist/index.d.ts +12 -84
  44. package/dist/index.js +4 -25
  45. package/dist/index.js.map +1 -1
  46. package/esm/BaseLinearDisplay/components/LinearBlocks.d.ts +2 -2
  47. package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +2 -22
  48. package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js.map +1 -1
  49. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +146 -1
  50. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js +600 -464
  51. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js.map +1 -1
  52. package/esm/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.js +19 -1
  53. package/esm/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.js.map +1 -1
  54. package/esm/BasicTrack/configSchema.d.ts +3 -0
  55. package/esm/BasicTrack/configSchema.js +16 -0
  56. package/esm/BasicTrack/configSchema.js.map +1 -0
  57. package/esm/BasicTrack/index.d.ts +3 -0
  58. package/esm/BasicTrack/index.js +13 -0
  59. package/esm/BasicTrack/index.js.map +1 -0
  60. package/esm/FeatureTrack/configSchema.d.ts +3 -0
  61. package/esm/FeatureTrack/configSchema.js +19 -0
  62. package/esm/FeatureTrack/configSchema.js.map +1 -0
  63. package/esm/FeatureTrack/index.d.ts +3 -0
  64. package/esm/FeatureTrack/index.js +13 -0
  65. package/esm/FeatureTrack/index.js.map +1 -0
  66. package/esm/LinearBareDisplay/configSchema.d.ts +5 -1
  67. package/esm/LinearBareDisplay/configSchema.js +14 -2
  68. package/esm/LinearBareDisplay/configSchema.js.map +1 -1
  69. package/esm/LinearBareDisplay/model.d.ts +16 -0
  70. package/esm/LinearBareDisplay/model.js +16 -0
  71. package/esm/LinearBareDisplay/model.js.map +1 -1
  72. package/esm/LinearBasicDisplay/configSchema.d.ts +5 -1
  73. package/esm/LinearBasicDisplay/configSchema.js +18 -2
  74. package/esm/LinearBasicDisplay/configSchema.js.map +1 -1
  75. package/esm/LinearBasicDisplay/model.d.ts +77 -6
  76. package/esm/LinearBasicDisplay/model.js +167 -111
  77. package/esm/LinearBasicDisplay/model.js.map +1 -1
  78. package/esm/LinearGenomeView/components/ImportForm.js +36 -30
  79. package/esm/LinearGenomeView/components/ImportForm.js.map +1 -1
  80. package/esm/LinearGenomeView/components/LinearGenomeView.js +2 -22
  81. package/esm/LinearGenomeView/components/LinearGenomeView.js.map +1 -1
  82. package/esm/LinearGenomeView/components/LinearGenomeViewSvg.js +7 -5
  83. package/esm/LinearGenomeView/components/LinearGenomeViewSvg.js.map +1 -1
  84. package/esm/LinearGenomeView/components/OverviewScaleBar.d.ts +26 -34
  85. package/esm/LinearGenomeView/index.d.ts +189 -15
  86. package/esm/LinearGenomeView/index.js +266 -27
  87. package/esm/LinearGenomeView/index.js.map +1 -1
  88. package/esm/index.d.ts +12 -84
  89. package/esm/index.js +4 -25
  90. package/esm/index.js.map +1 -1
  91. package/package.json +2 -2
  92. package/src/BaseLinearDisplay/components/ServerSideRenderedBlockContent.tsx +2 -24
  93. package/src/BaseLinearDisplay/models/BaseLinearDisplayModel.tsx +695 -555
  94. package/src/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.ts +20 -1
  95. package/src/BasicTrack/configSchema.ts +23 -0
  96. package/src/BasicTrack/index.ts +22 -0
  97. package/src/FeatureTrack/configSchema.ts +27 -0
  98. package/src/FeatureTrack/index.ts +21 -0
  99. package/src/LinearBareDisplay/configSchema.ts +15 -2
  100. package/src/LinearBareDisplay/model.ts +16 -0
  101. package/src/LinearBasicDisplay/configSchema.ts +19 -2
  102. package/src/LinearBasicDisplay/model.ts +63 -11
  103. package/src/LinearGenomeView/components/ImportForm.tsx +79 -63
  104. package/src/LinearGenomeView/components/LinearGenomeView.tsx +2 -26
  105. package/src/LinearGenomeView/components/LinearGenomeViewSvg.tsx +21 -18
  106. package/src/LinearGenomeView/components/__snapshots__/LinearGenomeView.test.tsx.snap +204 -204
  107. package/src/LinearGenomeView/index.test.ts +33 -26
  108. package/src/LinearGenomeView/index.tsx +317 -60
  109. 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
- block => block.assemblyName === contentBlocks[0].assemblyName,
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
- // we use an array for the tracks because the tracks are displayed in a
134
- // specific order that we need to keep.
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
- // schedule something to be run after the next time displayedRegions is set
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 assemblyNames = [
623
- ...new Set(
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 this with multiple assemblies currently`,
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
- // this "clears the view" and makes the view return to the import form
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
- const { assemblyNames } = self
727
- return assemblyNames.some(
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(block => {
902
- return {
903
- ...block,
904
- start: Math.floor(block.start),
905
- end: Math.ceil(block.end),
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
- navToLocString(locString: string, optAssemblyName?: string) {
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 = parsedLocStrings?.map(region => {
985
- const asmName = region.assemblyName || assemblyName
986
- const asm = assemblyManager.get(asmName)
987
- const { refName } = region
988
- if (!asm) {
989
- throw new Error(`assembly ${asmName} not found`)
990
- }
991
- const { regions } = asm
992
- if (!regions) {
993
- throw new Error(`regions not loaded yet for ${asmName}`)
994
- }
995
- const canonicalRefName = asm.getCanonicalRefName(region.refName)
996
- if (!canonicalRefName) {
997
- throw new Error(`Could not find refName ${refName} in ${asm.name}`)
998
- }
999
- const parentRegion = regions.find(
1000
- region => region.refName === canonicalRefName,
1001
- )
1002
- if (!parentRegion) {
1003
- throw new Error(`Could not find refName ${refName} in ${asmName}`)
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
- return {
1007
- ...region,
1008
- assemblyName: asmName,
1009
- parentRegion,
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 location - a proposed location to navigate to
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)