@jbrowse/plugin-linear-genome-view 2.4.1 → 2.5.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 (201) hide show
  1. package/dist/BaseLinearDisplay/components/LinearBlocks.d.ts +1 -4
  2. package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +8 -43
  3. package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js.map +1 -1
  4. package/dist/BaseLinearDisplay/components/TooLargeMessage.d.ts +3 -4
  5. package/dist/BaseLinearDisplay/components/TooLargeMessage.js +9 -9
  6. package/dist/BaseLinearDisplay/components/TooLargeMessage.js.map +1 -1
  7. package/dist/BaseLinearDisplay/components/Tooltip.d.ts +0 -1
  8. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +81 -35
  9. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js +105 -143
  10. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js.map +1 -1
  11. package/dist/BaseLinearDisplay/models/configSchema.d.ts +35 -1
  12. package/dist/BaseLinearDisplay/models/configSchema.js +9 -0
  13. package/dist/BaseLinearDisplay/models/configSchema.js.map +1 -1
  14. package/dist/BaseLinearDisplay/models/serverSideRenderedBlock.js +10 -6
  15. package/dist/BaseLinearDisplay/models/serverSideRenderedBlock.js.map +1 -1
  16. package/dist/BaseLinearDisplay/models/util.d.ts +8 -0
  17. package/dist/BaseLinearDisplay/models/util.js +33 -1
  18. package/dist/BaseLinearDisplay/models/util.js.map +1 -1
  19. package/dist/BasicTrack/configSchema.d.ts +73 -1
  20. package/dist/FeatureTrack/configSchema.d.ts +75 -1
  21. package/dist/LinearBareDisplay/configSchema.d.ts +28 -1
  22. package/dist/LinearBareDisplay/index.js +1 -0
  23. package/dist/LinearBareDisplay/index.js.map +1 -1
  24. package/dist/LinearBareDisplay/model.d.ts +66 -23
  25. package/dist/LinearBareDisplay/model.js +2 -2
  26. package/dist/LinearBareDisplay/model.js.map +1 -1
  27. package/dist/LinearBasicDisplay/components/SetMaxHeight.d.ts +1 -2
  28. package/dist/LinearBasicDisplay/components/SetMaxHeight.js +2 -5
  29. package/dist/LinearBasicDisplay/components/SetMaxHeight.js.map +1 -1
  30. package/dist/LinearBasicDisplay/configSchema.d.ts +28 -1
  31. package/dist/LinearBasicDisplay/configSchema.js +0 -9
  32. package/dist/LinearBasicDisplay/configSchema.js.map +1 -1
  33. package/dist/LinearBasicDisplay/index.js +1 -1
  34. package/dist/LinearBasicDisplay/model.d.ts +76 -36
  35. package/dist/LinearBasicDisplay/model.js.map +1 -1
  36. package/dist/LinearGenomeView/components/CenterLine.d.ts +0 -1
  37. package/dist/LinearGenomeView/components/Cytobands.d.ts +22 -23
  38. package/dist/LinearGenomeView/components/ExportSvgDialog.d.ts +0 -1
  39. package/dist/LinearGenomeView/components/GetSequenceDialog.d.ts +0 -1
  40. package/dist/LinearGenomeView/components/GetSequenceDialog.js +14 -16
  41. package/dist/LinearGenomeView/components/GetSequenceDialog.js.map +1 -1
  42. package/dist/LinearGenomeView/components/Gridlines.d.ts +0 -1
  43. package/dist/LinearGenomeView/components/Header.d.ts +0 -1
  44. package/dist/LinearGenomeView/components/HelpDialog.d.ts +0 -1
  45. package/dist/LinearGenomeView/components/ImportForm.d.ts +0 -1
  46. package/dist/LinearGenomeView/components/ImportForm.js +4 -7
  47. package/dist/LinearGenomeView/components/ImportForm.js.map +1 -1
  48. package/dist/LinearGenomeView/components/LinearGenomeView.d.ts +0 -1
  49. package/dist/LinearGenomeView/components/LinearGenomeView.js +0 -4
  50. package/dist/LinearGenomeView/components/LinearGenomeView.js.map +1 -1
  51. package/dist/LinearGenomeView/components/MiniControls.d.ts +0 -1
  52. package/dist/LinearGenomeView/components/OverviewScalebar.js +2 -1
  53. package/dist/LinearGenomeView/components/OverviewScalebar.js.map +1 -1
  54. package/dist/LinearGenomeView/components/RefNameAutocomplete.js +53 -52
  55. package/dist/LinearGenomeView/components/RefNameAutocomplete.js.map +1 -1
  56. package/dist/LinearGenomeView/components/RubberbandSpan.d.ts +0 -1
  57. package/dist/LinearGenomeView/components/SearchBox.d.ts +0 -1
  58. package/dist/LinearGenomeView/components/SearchResultsDialog.d.ts +5 -3
  59. package/dist/LinearGenomeView/components/SearchResultsDialog.js +6 -82
  60. package/dist/LinearGenomeView/components/SearchResultsDialog.js.map +1 -1
  61. package/dist/LinearGenomeView/components/SearchResultsTable.d.ts +8 -0
  62. package/dist/LinearGenomeView/components/SearchResultsTable.js +83 -0
  63. package/dist/LinearGenomeView/components/SearchResultsTable.js.map +1 -0
  64. package/dist/LinearGenomeView/components/SequenceSearchDialog.d.ts +0 -1
  65. package/dist/LinearGenomeView/components/TrackContainer.d.ts +0 -1
  66. package/dist/LinearGenomeView/components/TrackContainer.js +8 -2
  67. package/dist/LinearGenomeView/components/TrackContainer.js.map +1 -1
  68. package/dist/LinearGenomeView/components/TracksContainer.js +5 -7
  69. package/dist/LinearGenomeView/components/TracksContainer.js.map +1 -1
  70. package/dist/LinearGenomeView/components/VerticalGuide.d.ts +0 -1
  71. package/dist/LinearGenomeView/components/ZoomControls.d.ts +0 -1
  72. package/dist/LinearGenomeView/components/util.js +1 -1
  73. package/dist/LinearGenomeView/components/util.js.map +1 -1
  74. package/dist/LinearGenomeView/model.d.ts +49 -36
  75. package/dist/LinearGenomeView/model.js +121 -198
  76. package/dist/LinearGenomeView/model.js.map +1 -1
  77. package/dist/LinearGenomeView/svgcomponents/SVGBackground.d.ts +0 -1
  78. package/dist/LinearGenomeView/svgcomponents/SVGHeader.d.ts +0 -1
  79. package/dist/LinearGenomeView/svgcomponents/SVGRegionSeparators.d.ts +0 -1
  80. package/dist/LinearGenomeView/svgcomponents/SVGRuler.d.ts +0 -1
  81. package/dist/LinearGenomeView/svgcomponents/SVGScalebar.d.ts +0 -1
  82. package/dist/LinearGenomeView/svgcomponents/SVGTrackLabel.d.ts +0 -1
  83. package/dist/LinearGenomeView/svgcomponents/SVGTracks.d.ts +0 -1
  84. package/dist/LinearGenomeView/util.d.ts +29 -0
  85. package/dist/LinearGenomeView/util.js +79 -1
  86. package/dist/LinearGenomeView/util.js.map +1 -1
  87. package/dist/index.d.ts +201 -106
  88. package/esm/BaseLinearDisplay/components/LinearBlocks.d.ts +1 -4
  89. package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +8 -20
  90. package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js.map +1 -1
  91. package/esm/BaseLinearDisplay/components/TooLargeMessage.d.ts +3 -4
  92. package/esm/BaseLinearDisplay/components/TooLargeMessage.js +9 -9
  93. package/esm/BaseLinearDisplay/components/TooLargeMessage.js.map +1 -1
  94. package/esm/BaseLinearDisplay/components/Tooltip.d.ts +0 -1
  95. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +81 -35
  96. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js +105 -143
  97. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js.map +1 -1
  98. package/esm/BaseLinearDisplay/models/configSchema.d.ts +35 -1
  99. package/esm/BaseLinearDisplay/models/configSchema.js +9 -0
  100. package/esm/BaseLinearDisplay/models/configSchema.js.map +1 -1
  101. package/esm/BaseLinearDisplay/models/serverSideRenderedBlock.js +10 -6
  102. package/esm/BaseLinearDisplay/models/serverSideRenderedBlock.js.map +1 -1
  103. package/esm/BaseLinearDisplay/models/util.d.ts +8 -0
  104. package/esm/BaseLinearDisplay/models/util.js +30 -0
  105. package/esm/BaseLinearDisplay/models/util.js.map +1 -1
  106. package/esm/BasicTrack/configSchema.d.ts +73 -1
  107. package/esm/FeatureTrack/configSchema.d.ts +75 -1
  108. package/esm/LinearBareDisplay/configSchema.d.ts +28 -1
  109. package/esm/LinearBareDisplay/index.js +1 -0
  110. package/esm/LinearBareDisplay/index.js.map +1 -1
  111. package/esm/LinearBareDisplay/model.d.ts +66 -23
  112. package/esm/LinearBareDisplay/model.js +1 -1
  113. package/esm/LinearBareDisplay/model.js.map +1 -1
  114. package/esm/LinearBasicDisplay/components/SetMaxHeight.d.ts +1 -2
  115. package/esm/LinearBasicDisplay/components/SetMaxHeight.js +2 -5
  116. package/esm/LinearBasicDisplay/components/SetMaxHeight.js.map +1 -1
  117. package/esm/LinearBasicDisplay/configSchema.d.ts +28 -1
  118. package/esm/LinearBasicDisplay/configSchema.js +0 -9
  119. package/esm/LinearBasicDisplay/configSchema.js.map +1 -1
  120. package/esm/LinearBasicDisplay/index.js +1 -1
  121. package/esm/LinearBasicDisplay/model.d.ts +76 -36
  122. package/esm/LinearBasicDisplay/model.js.map +1 -1
  123. package/esm/LinearGenomeView/components/CenterLine.d.ts +0 -1
  124. package/esm/LinearGenomeView/components/Cytobands.d.ts +22 -23
  125. package/esm/LinearGenomeView/components/ExportSvgDialog.d.ts +0 -1
  126. package/esm/LinearGenomeView/components/GetSequenceDialog.d.ts +0 -1
  127. package/esm/LinearGenomeView/components/GetSequenceDialog.js +15 -17
  128. package/esm/LinearGenomeView/components/GetSequenceDialog.js.map +1 -1
  129. package/esm/LinearGenomeView/components/Gridlines.d.ts +0 -1
  130. package/esm/LinearGenomeView/components/Header.d.ts +0 -1
  131. package/esm/LinearGenomeView/components/HelpDialog.d.ts +0 -1
  132. package/esm/LinearGenomeView/components/ImportForm.d.ts +0 -1
  133. package/esm/LinearGenomeView/components/ImportForm.js +5 -8
  134. package/esm/LinearGenomeView/components/ImportForm.js.map +1 -1
  135. package/esm/LinearGenomeView/components/LinearGenomeView.d.ts +0 -1
  136. package/esm/LinearGenomeView/components/LinearGenomeView.js +0 -4
  137. package/esm/LinearGenomeView/components/LinearGenomeView.js.map +1 -1
  138. package/esm/LinearGenomeView/components/MiniControls.d.ts +0 -1
  139. package/esm/LinearGenomeView/components/OverviewScalebar.js +2 -1
  140. package/esm/LinearGenomeView/components/OverviewScalebar.js.map +1 -1
  141. package/esm/LinearGenomeView/components/RefNameAutocomplete.js +53 -52
  142. package/esm/LinearGenomeView/components/RefNameAutocomplete.js.map +1 -1
  143. package/esm/LinearGenomeView/components/RubberbandSpan.d.ts +0 -1
  144. package/esm/LinearGenomeView/components/SearchBox.d.ts +0 -1
  145. package/esm/LinearGenomeView/components/SearchResultsDialog.d.ts +5 -3
  146. package/esm/LinearGenomeView/components/SearchResultsDialog.js +7 -83
  147. package/esm/LinearGenomeView/components/SearchResultsDialog.js.map +1 -1
  148. package/esm/LinearGenomeView/components/SearchResultsTable.d.ts +8 -0
  149. package/esm/LinearGenomeView/components/SearchResultsTable.js +77 -0
  150. package/esm/LinearGenomeView/components/SearchResultsTable.js.map +1 -0
  151. package/esm/LinearGenomeView/components/SequenceSearchDialog.d.ts +0 -1
  152. package/esm/LinearGenomeView/components/TrackContainer.d.ts +0 -1
  153. package/esm/LinearGenomeView/components/TrackContainer.js +8 -2
  154. package/esm/LinearGenomeView/components/TrackContainer.js.map +1 -1
  155. package/esm/LinearGenomeView/components/TracksContainer.js +5 -7
  156. package/esm/LinearGenomeView/components/TracksContainer.js.map +1 -1
  157. package/esm/LinearGenomeView/components/VerticalGuide.d.ts +0 -1
  158. package/esm/LinearGenomeView/components/ZoomControls.d.ts +0 -1
  159. package/esm/LinearGenomeView/components/util.js +1 -1
  160. package/esm/LinearGenomeView/components/util.js.map +1 -1
  161. package/esm/LinearGenomeView/model.d.ts +49 -36
  162. package/esm/LinearGenomeView/model.js +122 -199
  163. package/esm/LinearGenomeView/model.js.map +1 -1
  164. package/esm/LinearGenomeView/svgcomponents/SVGBackground.d.ts +0 -1
  165. package/esm/LinearGenomeView/svgcomponents/SVGHeader.d.ts +0 -1
  166. package/esm/LinearGenomeView/svgcomponents/SVGRegionSeparators.d.ts +0 -1
  167. package/esm/LinearGenomeView/svgcomponents/SVGRuler.d.ts +0 -1
  168. package/esm/LinearGenomeView/svgcomponents/SVGScalebar.d.ts +0 -1
  169. package/esm/LinearGenomeView/svgcomponents/SVGTrackLabel.d.ts +0 -1
  170. package/esm/LinearGenomeView/svgcomponents/SVGTracks.d.ts +0 -1
  171. package/esm/LinearGenomeView/util.d.ts +29 -0
  172. package/esm/LinearGenomeView/util.js +76 -0
  173. package/esm/LinearGenomeView/util.js.map +1 -1
  174. package/esm/index.d.ts +201 -106
  175. package/package.json +3 -3
  176. package/src/BaseLinearDisplay/components/ServerSideRenderedBlockContent.tsx +8 -28
  177. package/src/BaseLinearDisplay/components/TooLargeMessage.tsx +13 -11
  178. package/src/BaseLinearDisplay/models/BaseLinearDisplayModel.tsx +148 -197
  179. package/src/BaseLinearDisplay/models/configSchema.ts +10 -0
  180. package/src/BaseLinearDisplay/models/serverSideRenderedBlock.ts +19 -13
  181. package/src/BaseLinearDisplay/models/util.ts +43 -0
  182. package/src/LinearBareDisplay/index.ts +1 -0
  183. package/src/LinearBareDisplay/model.ts +1 -1
  184. package/src/LinearBasicDisplay/components/SetMaxHeight.tsx +3 -7
  185. package/src/LinearBasicDisplay/configSchema.ts +0 -10
  186. package/src/LinearBasicDisplay/index.ts +1 -1
  187. package/src/LinearBasicDisplay/model.ts +1 -1
  188. package/src/LinearGenomeView/components/GetSequenceDialog.tsx +15 -25
  189. package/src/LinearGenomeView/components/ImportForm.tsx +4 -14
  190. package/src/LinearGenomeView/components/LinearGenomeView.tsx +0 -14
  191. package/src/LinearGenomeView/components/OverviewScalebar.tsx +2 -1
  192. package/src/LinearGenomeView/components/RefNameAutocomplete.tsx +122 -82
  193. package/src/LinearGenomeView/components/SearchResultsDialog.tsx +17 -112
  194. package/src/LinearGenomeView/components/SearchResultsTable.tsx +121 -0
  195. package/src/LinearGenomeView/components/TrackContainer.tsx +12 -3
  196. package/src/LinearGenomeView/components/TracksContainer.tsx +9 -6
  197. package/src/LinearGenomeView/components/__snapshots__/LinearGenomeView.test.tsx.snap +11 -11
  198. package/src/LinearGenomeView/components/util.ts +2 -1
  199. package/src/LinearGenomeView/index.test.ts +10 -12
  200. package/src/LinearGenomeView/model.ts +163 -236
  201. package/src/LinearGenomeView/util.ts +98 -0
@@ -7,7 +7,7 @@ import { MenuItem, ReturnToImportFormDialog } from '@jbrowse/core/ui'
7
7
  import {
8
8
  assembleLocString,
9
9
  clamp,
10
- findLastIndex,
10
+ findLast,
11
11
  getContainingView,
12
12
  getSession,
13
13
  isViewContainer,
@@ -15,9 +15,9 @@ import {
15
15
  isSessionWithAddTracks,
16
16
  localStorageGetItem,
17
17
  measureText,
18
- parseLocString,
19
18
  springAnimate,
20
19
  sum,
20
+ ParsedLocString,
21
21
  } from '@jbrowse/core/util'
22
22
  import BaseResult from '@jbrowse/core/TextSearch/BaseResults'
23
23
  import { BlockSet, BaseBlock } from '@jbrowse/core/util/blockTypes'
@@ -57,12 +57,19 @@ import { renderToSvg } from './svgcomponents/SVGLinearGenomeView'
57
57
  import ExportSvgDlg from './components/ExportSvgDialog'
58
58
  import MiniControls from './components/MiniControls'
59
59
  import Header from './components/Header'
60
+ import { generateLocations, parseLocStrings } from './util'
60
61
 
61
62
  // lazies
62
63
  const SequenceSearchDialog = lazy(
63
64
  () => import('./components/SequenceSearchDialog'),
64
65
  )
65
66
 
67
+ const GetSequenceDialog = lazy(() => import('./components/GetSequenceDialog'))
68
+
69
+ const SearchResultsDialog = lazy(
70
+ () => import('./components/SearchResultsDialog'),
71
+ )
72
+
66
73
  export interface BpOffset {
67
74
  refName?: string
68
75
  index: number
@@ -198,7 +205,8 @@ export function stateModelFactory(pluginManager: PluginManager) {
198
205
 
199
206
  /**
200
207
  * #property
201
- * how to display the track labels, can be "overlapping", "offset", or "hidden"
208
+ * how to display the track labels, can be "overlapping", "offset", or
209
+ * "hidden"
202
210
  */
203
211
  trackLabels: types.optional(
204
212
  types.string,
@@ -248,9 +256,6 @@ export function stateModelFactory(pluginManager: PluginManager) {
248
256
  coarseTotalBp: 0,
249
257
  leftOffset: undefined as undefined | BpOffset,
250
258
  rightOffset: undefined as undefined | BpOffset,
251
- searchResults: undefined as undefined | BaseResult[],
252
- searchQuery: undefined as undefined | string,
253
- seqDialogDisplayed: false,
254
259
  }))
255
260
  .views(self => ({
256
261
  /**
@@ -332,13 +337,6 @@ export function stateModelFactory(pluginManager: PluginManager) {
332
337
  return self.displayedRegions.length > 0
333
338
  },
334
339
 
335
- /**
336
- * #getter
337
- */
338
- get isSearchDialogDisplayed() {
339
- return self.searchResults !== undefined
340
- },
341
-
342
340
  /**
343
341
  * #getter
344
342
  */
@@ -605,7 +603,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
605
603
 
606
604
  /**
607
605
  * #action
608
- * sets offsets used in the get sequence dialog
606
+ * sets offsets of rubberband, used in the get sequence dialog can call
607
+ * view.getSelectedRegions(view.leftOffset,view.rightOffset) to compute
608
+ * the selected regions from the offsets
609
609
  */
610
610
  setOffsets(left?: BpOffset, right?: BpOffset) {
611
611
  self.leftOffset = left
@@ -615,16 +615,23 @@ export function stateModelFactory(pluginManager: PluginManager) {
615
615
  /**
616
616
  * #action
617
617
  */
618
- setSearchResults(results?: BaseResult[], query?: string) {
619
- self.searchResults = results
620
- self.searchQuery = query
621
- },
618
+ setSearchResults(
619
+ searchResults: BaseResult[],
620
+ searchQuery: string,
621
+ assemblyName?: string,
622
+ ) {
623
+ getSession(self).queueDialog(handleClose => [
624
+ SearchResultsDialog,
622
625
 
623
- /**
624
- * #action
625
- */
626
- setGetSequenceDialogOpen(open: boolean) {
627
- self.seqDialogDisplayed = open
626
+ {
627
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
628
+ model: self as any,
629
+ searchResults,
630
+ searchQuery,
631
+ handleClose,
632
+ assemblyName,
633
+ },
634
+ ])
628
635
  },
629
636
 
630
637
  /**
@@ -797,10 +804,13 @@ export function stateModelFactory(pluginManager: PluginManager) {
797
804
  /**
798
805
  * #method
799
806
  * Helper method for the fetchSequence.
800
- * Retrieves the corresponding regions that were selected by the rubberband
807
+ * Retrieves the corresponding regions that were selected by the
808
+ * rubberband
801
809
  *
802
- * @param leftOffset - `object as {start, end, index, offset}`, offset = start of user drag
803
- * @param rightOffset - `object as {start, end, index, offset}`, offset = end of user drag
810
+ * @param leftOffset - `object as {start, end, index, offset}`, offset = start
811
+ * of user drag
812
+ * @param rightOffset - `object as {start, end, index, offset}`,
813
+ * offset = end of user drag
804
814
  * @returns array of Region[]
805
815
  */
806
816
  getSelectedRegions(leftOffset?: BpOffset, rightOffset?: BpOffset) {
@@ -822,7 +832,8 @@ export function stateModelFactory(pluginManager: PluginManager) {
822
832
 
823
833
  /**
824
834
  * #action
825
- * schedule something to be run after the next time displayedRegions is set
835
+ * schedule something to be run after the next time displayedRegions is
836
+ * set
826
837
  */
827
838
  afterDisplayedRegionsSet(cb: Function) {
828
839
  self.afterDisplayedRegionsSetCallbacks.push(cb)
@@ -999,8 +1010,8 @@ export function stateModelFactory(pluginManager: PluginManager) {
999
1010
  },
1000
1011
  /**
1001
1012
  * #getter
1002
- * the cytoband is displayed to the right of the chromosome name,
1003
- * and that offset is calculated manually with this method
1013
+ * the cytoband is displayed to the right of the chromosome name, and
1014
+ * that offset is calculated manually with this method
1004
1015
  */
1005
1016
  get cytobandOffset() {
1006
1017
  return this.showCytobands
@@ -1161,11 +1172,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
1161
1172
  /**
1162
1173
  * #getter
1163
1174
  * static blocks are an important concept jbrowse uses to avoid
1164
- * re-rendering when you scroll to the side. when you horizontally
1165
- * scroll to the right, old blocks to the left may be removed, and
1166
- * new blocks may be instantiated on the right. tracks may use the
1167
- * static blocks to render their data for the region represented by
1168
- * the block
1175
+ * re-rendering when you scroll to the side. when you horizontally scroll to the
1176
+ * right, old blocks to the left may be removed, and new blocks may be
1177
+ * instantiated on the right. tracks may use the static blocks to render their
1178
+ * data for the region represented by the block
1169
1179
  */
1170
1180
  get staticBlocks() {
1171
1181
  const ret = calculateStaticBlocks(self)
@@ -1179,9 +1189,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
1179
1189
  /**
1180
1190
  * #getter
1181
1191
  * dynamic blocks represent the exact coordinates of the currently
1182
- * visible genome regions on the screen. they are similar to static
1183
- * blocks, but statcic blocks can go offscreen while dynamic blocks
1184
- * represent exactly what is on screen
1192
+ * visible genome regions on the screen. they are similar to static blocks, but
1193
+ * static blocks can go offscreen while dynamic blocks represent exactly what
1194
+ * is on screen
1185
1195
  */
1186
1196
  get dynamicBlocks() {
1187
1197
  return calculateDynamicBlocks(self)
@@ -1258,8 +1268,8 @@ export function stateModelFactory(pluginManager: PluginManager) {
1258
1268
  .actions(self => ({
1259
1269
  /**
1260
1270
  * #action
1261
- * offset is the base-pair-offset in the displayed region, index is the index of the
1262
- * displayed region in the linear genome view
1271
+ * offset is the base-pair-offset in the displayed region, index is the
1272
+ * index of the displayed region in the linear genome view
1263
1273
  *
1264
1274
  * @param start - object as `{start, end, offset, index}`
1265
1275
  * @param end - object as `{start, end, offset, index}`
@@ -1270,91 +1280,52 @@ export function stateModelFactory(pluginManager: PluginManager) {
1270
1280
 
1271
1281
  /**
1272
1282
  * #action
1273
- * navigate to the given locstring
1283
+ * Navigate to the given locstring, will change displayed regions if
1284
+ * needed, and wait for assemblies to be initialized
1274
1285
  *
1275
- * @param locString - e.g. "chr1:1-100"
1276
- * @param optAssemblyName - (optional) the assembly name to use when navigating to the locstring
1286
+ * @param input - e.g. "chr1:1-100", "chr1:1-100 chr2:1-100", "chr 1 100"
1287
+ * @param optAssemblyName - (optional) the assembly name to use when
1288
+ * navigating to the locstring
1277
1289
  */
1278
- async navToLocString(locString: string, optAssemblyName?: string) {
1290
+ async navToLocString(input: string, optAssemblyName?: string) {
1279
1291
  const { assemblyNames } = self
1280
1292
  const { assemblyManager } = getSession(self)
1281
1293
  const { isValidRefName } = assemblyManager
1282
- await when(() => self.volatileWidth !== undefined)
1283
1294
  const assemblyName = optAssemblyName || assemblyNames[0]
1284
- let parsedLocStrings
1285
- const inputs = locString
1286
- .split(/(\s+)/)
1287
- .map(f => f.trim())
1288
- .filter(f => !!f)
1289
-
1290
1295
  if (assemblyName) {
1296
+ // wait before isValidRefName can be called
1291
1297
  await assemblyManager.waitForAssembly(assemblyName)
1292
1298
  }
1293
1299
 
1294
- // first try interpreting as a whitespace-separated sequence of
1295
- // multiple locstrings
1296
- try {
1297
- parsedLocStrings = inputs.map(loc =>
1298
- parseLocString(loc, ref => isValidRefName(ref, assemblyName)),
1299
- )
1300
- } catch (e) {
1301
- // if this fails, try interpreting as a whitespace-separated refname,
1302
- // start, end if start and end are integer inputs
1303
- const [refName, start, end] = inputs
1304
- if (
1305
- `${e}`.match(/Unknown reference sequence/) &&
1306
- Number.isInteger(+start) &&
1307
- Number.isInteger(+end)
1308
- ) {
1309
- parsedLocStrings = [
1310
- parseLocString(refName + ':' + start + '..' + end, ref =>
1311
- isValidRefName(ref, assemblyName),
1312
- ),
1313
- ]
1314
- } else {
1315
- throw e
1316
- }
1317
- }
1300
+ return this.navToLocations(
1301
+ parseLocStrings(input, assemblyName, isValidRefName),
1302
+ assemblyName,
1303
+ )
1304
+ },
1318
1305
 
1319
- const locations = await Promise.all(
1320
- parsedLocStrings?.map(async region => {
1321
- const asmName = region.assemblyName || assemblyName
1322
- const asm = await assemblyManager.waitForAssembly(asmName)
1323
- const { refName } = region
1324
- if (!asm) {
1325
- throw new Error(`assembly ${asmName} not found`)
1326
- }
1327
- const { regions } = asm
1328
- if (!regions) {
1329
- throw new Error(`regions not loaded yet for ${asmName}`)
1330
- }
1331
- const canonicalRefName = asm.getCanonicalRefName(region.refName)
1332
- if (!canonicalRefName) {
1333
- throw new Error(
1334
- `Could not find refName ${refName} in ${asm.name}`,
1335
- )
1336
- }
1337
- const parentRegion = regions.find(
1338
- r => r.refName === canonicalRefName,
1339
- )
1340
- if (!parentRegion) {
1341
- throw new Error(`Could not find refName ${refName} in ${asmName}`)
1342
- }
1306
+ /**
1307
+ * #action
1308
+ * Similar to `navToLocString`, but accepts parsed location objects
1309
+ * instead of strings. Will try to perform `setDisplayedRegions` if
1310
+ * changing regions
1311
+ */
1312
+ async navToLocations(
1313
+ parsedLocStrings: ParsedLocString[],
1314
+ assemblyName?: string,
1315
+ ) {
1316
+ const { assemblyManager } = getSession(self)
1317
+ await when(() => self.volatileWidth !== undefined)
1343
1318
 
1344
- return {
1345
- ...region,
1346
- assemblyName: asmName,
1347
- parentRegion,
1348
- }
1349
- }),
1319
+ const locations = await generateLocations(
1320
+ parsedLocStrings,
1321
+ assemblyManager,
1322
+ assemblyName,
1350
1323
  )
1351
1324
 
1352
1325
  if (locations.length === 1) {
1353
1326
  const loc = locations[0]
1354
- self.setDisplayedRegions([
1355
- { reversed: loc.reversed, ...loc.parentRegion },
1356
- ])
1357
- const { start, end, parentRegion } = loc
1327
+ const { reversed, parentRegion, start, end } = loc
1328
+ self.setDisplayedRegions([{ reversed, ...parentRegion }])
1358
1329
 
1359
1330
  this.navTo({
1360
1331
  ...loc,
@@ -1373,9 +1344,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
1373
1344
  /**
1374
1345
  * #action
1375
1346
  * Navigate to a location based on its refName and optionally start, end,
1376
- * and assemblyName. Can handle if there are multiple displayedRegions
1377
- * from same refName. Only navigates to a location if it is entirely
1378
- * within a displayedRegion. Navigates to the first matching location
1347
+ * and assemblyName. Will not try to change displayed regions, use
1348
+ * `navToLocations` instead. Only navigates to a location if it is
1349
+ * entirely within a displayedRegion. Navigates to the first matching location
1379
1350
  * encountered.
1380
1351
  *
1381
1352
  * Throws an error if navigation was unsuccessful
@@ -1388,132 +1359,86 @@ export function stateModelFactory(pluginManager: PluginManager) {
1388
1359
 
1389
1360
  /**
1390
1361
  * #action
1362
+ * Navigate to a location based on its refName and optionally start, end,
1363
+ * and assemblyName. Will not try to change displayed regions, use
1364
+ * navToLocations instead. Only navigates to a location if it is entirely
1365
+ * within a displayedRegion. Navigates to the first matching location
1366
+ * encountered.
1367
+ *
1368
+ * Throws an error if navigation was unsuccessful
1369
+ *
1370
+ * @param locations - proposed location to navigate to
1391
1371
  */
1392
1372
  navToMultiple(locations: NavLocation[]) {
1393
- const firstLocation = locations[0]
1394
- let { refName } = firstLocation
1395
- const {
1396
- start,
1397
- end,
1398
- assemblyName = self.assemblyNames[0],
1399
- } = firstLocation
1400
-
1401
- if (start !== undefined && end !== undefined && start > end) {
1402
- throw new Error(`start "${start + 1}" is greater than end "${end}"`)
1373
+ if (
1374
+ locations.some(
1375
+ l =>
1376
+ l.start !== undefined && l.end !== undefined && l.start > l.end,
1377
+ )
1378
+ ) {
1379
+ throw new Error('found start greater than end')
1403
1380
  }
1404
- const session = getSession(self)
1405
- const { assemblyManager } = session
1406
- const assembly = assemblyManager.get(assemblyName)
1407
- if (assembly) {
1408
- const canonicalRefName = assembly.getCanonicalRefName(refName)
1409
- if (canonicalRefName) {
1410
- refName = canonicalRefName
1411
- }
1381
+ const f1 = locations[0]
1382
+ const f2 = locations[locations.length - 1]
1383
+ const a = self.assemblyNames[0]
1384
+ const { assemblyManager } = getSession(self)
1385
+ const assembly1 = assemblyManager.get(f1.assemblyName || a)
1386
+ const assembly2 = assemblyManager.get(f2.assemblyName || a)
1387
+ const ref1 = assembly1?.getCanonicalRefName(f1.refName) || f1.refName
1388
+ const ref2 = assembly2?.getCanonicalRefName(f2.refName) || f2.refName
1389
+ const r1 = self.displayedRegions.find(r => r.refName === ref1)
1390
+ const r2 = findLast(self.displayedRegions, r => r.refName === ref2)
1391
+ if (!r1) {
1392
+ throw new Error(`could not find a region with refName "${ref1}"`)
1412
1393
  }
1413
- let s = start
1414
- let e = end
1415
- let refNameMatched = false
1416
- const predicate = (r: Region) => {
1417
- if (refName === r.refName) {
1418
- refNameMatched = true
1419
- if (s === undefined) {
1420
- s = r.start
1421
- }
1422
- if (e === undefined) {
1423
- e = r.end
1424
- }
1425
- if (s >= r.start && s <= r.end && e <= r.end && e >= r.start) {
1426
- return true
1427
- }
1428
- s = start
1429
- e = end
1430
- }
1431
- return false
1394
+ if (!r2) {
1395
+ throw new Error(`could not find a region with refName "${ref2}"`)
1432
1396
  }
1433
1397
 
1434
- const lastIndex = findLastIndex(self.displayedRegions, predicate)
1435
- let index
1436
- while (index !== lastIndex) {
1437
- try {
1438
- const previousIndex: number | undefined = index
1439
- index = self.displayedRegions
1440
- .slice(previousIndex === undefined ? 0 : previousIndex + 1)
1441
- .findIndex(predicate)
1442
- if (previousIndex !== undefined) {
1443
- index += previousIndex + 1
1444
- }
1445
- if (!refNameMatched) {
1446
- throw new Error(
1447
- `could not find a region with refName "${refName}"`,
1448
- )
1449
- }
1450
- if (s === undefined) {
1451
- throw new Error(
1452
- `could not find a region with refName "${refName}" that contained an end position ${e}`,
1453
- )
1454
- }
1455
- if (e === undefined) {
1456
- throw new Error(
1457
- `could not find a region with refName "${refName}" that contained a start position ${
1458
- s + 1
1459
- }`,
1460
- )
1461
- }
1462
- if (index === -1) {
1463
- throw new Error(
1464
- `could not find a region that completely contained "${assembleLocString(
1465
- firstLocation,
1466
- )}"`,
1467
- )
1468
- }
1469
- if (locations.length === 1) {
1470
- const f = self.displayedRegions[index]
1471
- this.moveTo(
1472
- { index, offset: f.reversed ? f.end - e : s - f.start },
1473
- { index, offset: f.reversed ? f.end - s : e - f.start },
1474
- )
1475
- return
1476
- }
1477
- let idx = 0
1478
- let start = 0
1479
- let end = 0
1480
- for (idx; idx < locations.length; idx++) {
1481
- const location = locations[idx]
1482
- const region = self.displayedRegions[index + idx]
1483
- start = location.start || region.start
1484
- end = location.end || region.end
1485
- if (location.refName !== region.refName) {
1486
- throw new Error(
1487
- `Entered location ${assembleLocString(
1488
- location,
1489
- )} does not match with displayed regions`,
1490
- )
1491
- }
1492
- }
1493
- idx -= 1
1494
- const startDisplayedRegion = self.displayedRegions[index]
1495
- const endDisplayedRegion = self.displayedRegions[index + idx]
1496
- this.moveTo(
1497
- {
1498
- index,
1499
- offset: startDisplayedRegion.reversed
1500
- ? startDisplayedRegion.end - e
1501
- : s - startDisplayedRegion.start,
1502
- },
1503
- {
1504
- index: index + idx,
1505
- offset: endDisplayedRegion.reversed
1506
- ? endDisplayedRegion.end - start
1507
- : end - endDisplayedRegion.start,
1508
- },
1509
- )
1510
- return
1511
- } catch (error) {
1512
- if (index === lastIndex) {
1513
- throw error
1514
- }
1515
- }
1398
+ const s1 = f1.start === undefined ? r1.start : f1.start
1399
+ const e1 = f1.end === undefined ? r1.end : f1.end
1400
+ const s2 = f2.start === undefined ? r2.start : f2.start
1401
+ const e2 = f2.end === undefined ? r2.end : f2.end
1402
+
1403
+ const index = self.displayedRegions.findIndex(
1404
+ r =>
1405
+ ref1 === r.refName &&
1406
+ s1 >= r.start &&
1407
+ s1 <= r.end &&
1408
+ e1 <= r.end &&
1409
+ e1 >= r.start,
1410
+ )
1411
+
1412
+ const index2 = self.displayedRegions.findIndex(
1413
+ r =>
1414
+ ref2 === r.refName &&
1415
+ s2 >= r.start &&
1416
+ s2 <= r.end &&
1417
+ e2 <= r.end &&
1418
+ e2 >= r.start,
1419
+ )
1420
+
1421
+ if (index === -1 || index2 === -1) {
1422
+ throw new Error(
1423
+ `could not find a region that contained "${locations.map(l =>
1424
+ assembleLocString(l),
1425
+ )}"`,
1426
+ )
1516
1427
  }
1428
+
1429
+ const sd = self.displayedRegions[index]
1430
+ const ed = self.displayedRegions[index2]
1431
+
1432
+ this.moveTo(
1433
+ {
1434
+ index,
1435
+ offset: sd.reversed ? sd.end - e1 : s1 - sd.start,
1436
+ },
1437
+ {
1438
+ index: index2,
1439
+ offset: ed.reversed ? ed.end - s2 : e2 - ed.start,
1440
+ },
1441
+ )
1517
1442
  },
1518
1443
  }))
1519
1444
  .views(self => ({
@@ -1525,15 +1450,17 @@ export function stateModelFactory(pluginManager: PluginManager) {
1525
1450
  {
1526
1451
  label: 'Zoom to region',
1527
1452
  icon: ZoomInIcon,
1528
- onClick: () => {
1529
- const { leftOffset, rightOffset } = self
1530
- self.moveTo(leftOffset, rightOffset)
1531
- },
1453
+ onClick: () => self.moveTo(self.leftOffset, self.rightOffset),
1532
1454
  },
1533
1455
  {
1534
1456
  label: 'Get sequence',
1535
1457
  icon: MenuOpenIcon,
1536
- onClick: () => self.setGetSequenceDialogOpen(true),
1458
+ onClick: () =>
1459
+ getSession(self).queueDialog(handleClose => [
1460
+ GetSequenceDialog,
1461
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1462
+ { model: self as any, handleClose },
1463
+ ]),
1537
1464
  },
1538
1465
  ]
1539
1466
  },
@@ -1567,7 +1494,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
1567
1494
  coord,
1568
1495
  regionNumber,
1569
1496
  })
1570
- if (centerPx) {
1497
+ if (centerPx !== undefined) {
1571
1498
  self.scrollTo(Math.round(centerPx.offsetPx - self.width / 2))
1572
1499
  }
1573
1500
  },
@@ -1,3 +1,9 @@
1
+ import {
2
+ AssemblyManager,
3
+ ParsedLocString,
4
+ parseLocString,
5
+ } from '@jbrowse/core/util'
6
+
1
7
  /**
2
8
  * Given a scale ( bp/px ) and minimum distances (px) between major and minor
3
9
  * gridlines, return an object like `{ majorPitch: bp, minorPitch: bp }` giving
@@ -82,3 +88,95 @@ export function makeTicks(
82
88
  }
83
89
  return ticks
84
90
  }
91
+
92
+ /**
93
+ * Generate location objects for a set of parsed locstrings, which includes
94
+ * translating the refNames to assembly-canonical refNames and adding the
95
+ * 'parentRegion'
96
+ *
97
+ * Used by navToLocations and navToLocString
98
+ */
99
+ export async function generateLocations(
100
+ regions: ParsedLocString[] = [],
101
+ assemblyManager: AssemblyManager,
102
+ assemblyName?: string,
103
+ ) {
104
+ return Promise.all(
105
+ regions.map(async region => {
106
+ const asmName = region.assemblyName || assemblyName
107
+ if (!asmName) {
108
+ throw new Error('no assembly provided')
109
+ }
110
+ const asm = await assemblyManager.waitForAssembly(asmName)
111
+ const { refName } = region
112
+ if (!asm) {
113
+ throw new Error(`assembly ${asmName} not found`)
114
+ }
115
+ const { regions } = asm
116
+ if (!regions) {
117
+ throw new Error(`regions not loaded yet for ${asmName}`)
118
+ }
119
+ const canonicalRefName = asm.getCanonicalRefName(region.refName)
120
+ if (!canonicalRefName) {
121
+ throw new Error(`Could not find refName ${refName} in ${asm.name}`)
122
+ }
123
+ const parentRegion = regions.find(r => r.refName === canonicalRefName)
124
+ if (!parentRegion) {
125
+ throw new Error(`Could not find refName ${refName} in ${asmName}`)
126
+ }
127
+
128
+ return {
129
+ ...(region as Omit<typeof region, symbol>),
130
+ assemblyName: asmName,
131
+ parentRegion,
132
+ }
133
+ }),
134
+ )
135
+ }
136
+
137
+ /**
138
+ * Parses locString or space separated set of locStrings into location objects
139
+ * Example inputs:
140
+ * "chr1"
141
+ * "chr1:1-100"
142
+ * "chr1:1..100"
143
+ * "chr1 chr2"
144
+ * "chr1:1-100 chr2:1-100"
145
+ * "chr1 100 200" equivalent to "chr1:1-100"
146
+ *
147
+ * Used by navToLocString
148
+ */
149
+ export function parseLocStrings(
150
+ input: string,
151
+ assemblyName: string,
152
+ isValidRefName: (str: string, assemblyName: string) => boolean,
153
+ ) {
154
+ const inputs = input
155
+ .split(/(\s+)/)
156
+ .map(f => f.trim())
157
+ .filter(f => !!f)
158
+ // first try interpreting as a whitespace-separated sequence of
159
+ // multiple locstrings
160
+ try {
161
+ return inputs.map(loc =>
162
+ parseLocString(loc, ref => isValidRefName(ref, assemblyName)),
163
+ )
164
+ } catch (e) {
165
+ // if this fails, try interpreting as a whitespace-separated refname,
166
+ // start, end if start and end are integer inputs
167
+ const [refName, start, end] = inputs
168
+ if (
169
+ `${e}`.match(/Unknown reference sequence/) &&
170
+ Number.isInteger(+start) &&
171
+ Number.isInteger(+end)
172
+ ) {
173
+ return [
174
+ parseLocString(refName + ':' + start + '..' + end, ref =>
175
+ isValidRefName(ref, assemblyName),
176
+ ),
177
+ ]
178
+ } else {
179
+ throw e
180
+ }
181
+ }
182
+ }