@jbrowse/plugin-linear-genome-view 1.7.11 → 2.0.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 (270) hide show
  1. package/dist/BaseLinearDisplay/components/BaseLinearDisplay.js +119 -139
  2. package/dist/BaseLinearDisplay/components/BaseLinearDisplay.js.map +1 -0
  3. package/dist/BaseLinearDisplay/components/Block.js +53 -74
  4. package/dist/BaseLinearDisplay/components/Block.js.map +1 -0
  5. package/dist/BaseLinearDisplay/components/LinearBlocks.d.ts +11 -1
  6. package/dist/BaseLinearDisplay/components/LinearBlocks.js +64 -103
  7. package/dist/BaseLinearDisplay/components/LinearBlocks.js.map +1 -0
  8. package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +145 -175
  9. package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js.map +1 -0
  10. package/dist/BaseLinearDisplay/components/Tooltip.js +109 -116
  11. package/dist/BaseLinearDisplay/components/Tooltip.js.map +1 -0
  12. package/dist/BaseLinearDisplay/index.js +13 -40
  13. package/dist/BaseLinearDisplay/index.js.map +1 -0
  14. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +19 -14
  15. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js +605 -684
  16. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js.map +1 -0
  17. package/dist/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.js +15 -22
  18. package/dist/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.js.map +1 -0
  19. package/dist/BaseLinearDisplay/models/serverSideRenderedBlock.d.ts +7 -8
  20. package/dist/BaseLinearDisplay/models/serverSideRenderedBlock.js +266 -312
  21. package/dist/BaseLinearDisplay/models/serverSideRenderedBlock.js.map +1 -0
  22. package/dist/LinearBareDisplay/configSchema.js +11 -17
  23. package/dist/LinearBareDisplay/configSchema.js.map +1 -0
  24. package/dist/LinearBareDisplay/index.js +7 -20
  25. package/dist/LinearBareDisplay/index.js.map +1 -0
  26. package/dist/LinearBareDisplay/model.d.ts +15 -13
  27. package/dist/LinearBareDisplay/model.js +36 -42
  28. package/dist/LinearBareDisplay/model.js.map +1 -0
  29. package/dist/LinearBasicDisplay/components/SetMaxHeight.d.ts +1 -1
  30. package/dist/LinearBasicDisplay/components/SetMaxHeight.js +76 -85
  31. package/dist/LinearBasicDisplay/components/SetMaxHeight.js.map +1 -0
  32. package/dist/LinearBasicDisplay/configSchema.js +15 -23
  33. package/dist/LinearBasicDisplay/configSchema.js.map +1 -0
  34. package/dist/LinearBasicDisplay/index.js +10 -22
  35. package/dist/LinearBasicDisplay/index.js.map +1 -0
  36. package/dist/LinearBasicDisplay/model.d.ts +20 -14
  37. package/dist/LinearBasicDisplay/model.js +180 -159
  38. package/dist/LinearBasicDisplay/model.js.map +1 -0
  39. package/dist/LinearGenomeView/components/CenterLine.d.ts +2 -8
  40. package/dist/LinearGenomeView/components/CenterLine.js +60 -74
  41. package/dist/LinearGenomeView/components/CenterLine.js.map +1 -0
  42. package/dist/LinearGenomeView/components/ExportSvgDialog.js +141 -141
  43. package/dist/LinearGenomeView/components/ExportSvgDialog.js.map +1 -0
  44. package/dist/LinearGenomeView/components/Header.d.ts +1 -0
  45. package/dist/LinearGenomeView/components/Header.js +70 -125
  46. package/dist/LinearGenomeView/components/Header.js.map +1 -0
  47. package/dist/LinearGenomeView/components/HelpDialog.d.ts +0 -1
  48. package/dist/LinearGenomeView/components/HelpDialog.js +62 -44
  49. package/dist/LinearGenomeView/components/HelpDialog.js.map +1 -0
  50. package/dist/LinearGenomeView/components/ImportForm.d.ts +1 -0
  51. package/dist/LinearGenomeView/components/ImportForm.js +222 -289
  52. package/dist/LinearGenomeView/components/ImportForm.js.map +1 -0
  53. package/dist/LinearGenomeView/components/LinearGenomeView.js +64 -124
  54. package/dist/LinearGenomeView/components/LinearGenomeView.js.map +1 -0
  55. package/dist/LinearGenomeView/components/LinearGenomeViewSvg.js +198 -337
  56. package/dist/LinearGenomeView/components/LinearGenomeViewSvg.js.map +1 -0
  57. package/dist/LinearGenomeView/components/MiniControls.js +64 -78
  58. package/dist/LinearGenomeView/components/MiniControls.js.map +1 -0
  59. package/dist/LinearGenomeView/components/OverviewRubberBand.js +226 -293
  60. package/dist/LinearGenomeView/components/OverviewRubberBand.js.map +1 -0
  61. package/dist/LinearGenomeView/components/OverviewScaleBar.d.ts +3 -3
  62. package/dist/LinearGenomeView/components/OverviewScaleBar.js +275 -370
  63. package/dist/LinearGenomeView/components/OverviewScaleBar.js.map +1 -0
  64. package/dist/LinearGenomeView/components/RefNameAutocomplete.d.ts +1 -1
  65. package/dist/LinearGenomeView/components/RefNameAutocomplete.js +237 -324
  66. package/dist/LinearGenomeView/components/RefNameAutocomplete.js.map +1 -0
  67. package/dist/LinearGenomeView/components/RubberBand.js +229 -296
  68. package/dist/LinearGenomeView/components/RubberBand.js.map +1 -0
  69. package/dist/LinearGenomeView/components/Ruler.js +45 -90
  70. package/dist/LinearGenomeView/components/Ruler.js.map +1 -0
  71. package/dist/LinearGenomeView/components/ScaleBar.d.ts +8 -403
  72. package/dist/LinearGenomeView/components/ScaleBar.js +121 -172
  73. package/dist/LinearGenomeView/components/ScaleBar.js.map +1 -0
  74. package/dist/LinearGenomeView/components/SearchBox.js +155 -166
  75. package/dist/LinearGenomeView/components/SearchBox.js.map +1 -0
  76. package/dist/LinearGenomeView/components/SearchResultsDialog.d.ts +0 -1
  77. package/dist/LinearGenomeView/components/SearchResultsDialog.js +105 -149
  78. package/dist/LinearGenomeView/components/SearchResultsDialog.js.map +1 -0
  79. package/dist/LinearGenomeView/components/SequenceDialog.js +219 -272
  80. package/dist/LinearGenomeView/components/SequenceDialog.js.map +1 -0
  81. package/dist/LinearGenomeView/components/TrackContainer.js +116 -156
  82. package/dist/LinearGenomeView/components/TrackContainer.js.map +1 -0
  83. package/dist/LinearGenomeView/components/TrackLabel.d.ts +6 -42
  84. package/dist/LinearGenomeView/components/TrackLabel.js +115 -134
  85. package/dist/LinearGenomeView/components/TrackLabel.js.map +1 -0
  86. package/dist/LinearGenomeView/components/TracksContainer.d.ts +1 -1
  87. package/dist/LinearGenomeView/components/TracksContainer.js +172 -199
  88. package/dist/LinearGenomeView/components/TracksContainer.js.map +1 -0
  89. package/dist/LinearGenomeView/components/VerticalGuides.d.ts +2 -3
  90. package/dist/LinearGenomeView/components/VerticalGuides.js +66 -104
  91. package/dist/LinearGenomeView/components/VerticalGuides.js.map +1 -0
  92. package/dist/LinearGenomeView/components/ZoomControls.js +72 -87
  93. package/dist/LinearGenomeView/components/ZoomControls.js.map +1 -0
  94. package/dist/LinearGenomeView/components/util.js +94 -71
  95. package/dist/LinearGenomeView/components/util.js.map +1 -0
  96. package/dist/LinearGenomeView/index.d.ts +3 -3
  97. package/dist/LinearGenomeView/index.js +1163 -1414
  98. package/dist/LinearGenomeView/index.js.map +1 -0
  99. package/dist/LinearGenomeView/util.js +76 -83
  100. package/dist/LinearGenomeView/util.js.map +1 -0
  101. package/dist/index.d.ts +98 -48
  102. package/dist/index.js +225 -295
  103. package/dist/index.js.map +1 -0
  104. package/esm/BaseLinearDisplay/components/BaseLinearDisplay.d.ts +9 -0
  105. package/esm/BaseLinearDisplay/components/BaseLinearDisplay.js +68 -0
  106. package/esm/BaseLinearDisplay/components/BaseLinearDisplay.js.map +1 -0
  107. package/esm/BaseLinearDisplay/components/Block.d.ts +15 -0
  108. package/esm/BaseLinearDisplay/components/Block.js +46 -0
  109. package/esm/BaseLinearDisplay/components/Block.js.map +1 -0
  110. package/esm/BaseLinearDisplay/components/LinearBlocks.d.ts +22 -0
  111. package/esm/BaseLinearDisplay/components/LinearBlocks.js +62 -0
  112. package/esm/BaseLinearDisplay/components/LinearBlocks.js.map +1 -0
  113. package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.d.ts +4 -0
  114. package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +113 -0
  115. package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js.map +1 -0
  116. package/esm/BaseLinearDisplay/components/Tooltip.d.ts +8 -0
  117. package/esm/BaseLinearDisplay/components/Tooltip.js +64 -0
  118. package/esm/BaseLinearDisplay/components/Tooltip.js.map +1 -0
  119. package/esm/BaseLinearDisplay/index.d.ts +5 -0
  120. package/esm/BaseLinearDisplay/index.js +4 -0
  121. package/esm/BaseLinearDisplay/index.js.map +1 -0
  122. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +232 -0
  123. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js +541 -0
  124. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js.map +1 -0
  125. package/esm/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.d.ts +1 -0
  126. package/esm/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.js +14 -0
  127. package/esm/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.js.map +1 -0
  128. package/esm/BaseLinearDisplay/models/serverSideRenderedBlock.d.ts +96 -0
  129. package/esm/BaseLinearDisplay/models/serverSideRenderedBlock.js +225 -0
  130. package/esm/BaseLinearDisplay/models/serverSideRenderedBlock.js.map +1 -0
  131. package/esm/LinearBareDisplay/configSchema.d.ts +2 -0
  132. package/esm/LinearBareDisplay/configSchema.js +9 -0
  133. package/esm/LinearBareDisplay/configSchema.js.map +1 -0
  134. package/esm/LinearBareDisplay/index.d.ts +2 -0
  135. package/esm/LinearBareDisplay/index.js +3 -0
  136. package/esm/LinearBareDisplay/index.js.map +1 -0
  137. package/esm/LinearBareDisplay/model.d.ts +194 -0
  138. package/esm/LinearBareDisplay/model.js +28 -0
  139. package/esm/LinearBareDisplay/model.js.map +1 -0
  140. package/esm/LinearBasicDisplay/components/SetMaxHeight.d.ts +10 -0
  141. package/esm/LinearBasicDisplay/components/SetMaxHeight.js +40 -0
  142. package/esm/LinearBasicDisplay/components/SetMaxHeight.js.map +1 -0
  143. package/esm/LinearBasicDisplay/configSchema.d.ts +2 -0
  144. package/esm/LinearBasicDisplay/configSchema.js +14 -0
  145. package/esm/LinearBasicDisplay/configSchema.js.map +1 -0
  146. package/esm/LinearBasicDisplay/index.d.ts +2 -0
  147. package/esm/LinearBasicDisplay/index.js +3 -0
  148. package/esm/LinearBasicDisplay/index.js.map +1 -0
  149. package/esm/LinearBasicDisplay/model.d.ts +218 -0
  150. package/esm/LinearBasicDisplay/model.js +127 -0
  151. package/esm/LinearBasicDisplay/model.js.map +1 -0
  152. package/esm/LinearGenomeView/components/CenterLine.d.ts +8 -0
  153. package/esm/LinearGenomeView/components/CenterLine.js +40 -0
  154. package/esm/LinearGenomeView/components/CenterLine.js.map +1 -0
  155. package/esm/LinearGenomeView/components/ExportSvgDialog.d.ts +6 -0
  156. package/esm/LinearGenomeView/components/ExportSvgDialog.js +52 -0
  157. package/esm/LinearGenomeView/components/ExportSvgDialog.js.map +1 -0
  158. package/esm/LinearGenomeView/components/Header.d.ts +7 -0
  159. package/esm/LinearGenomeView/components/Header.js +81 -0
  160. package/esm/LinearGenomeView/components/Header.js.map +1 -0
  161. package/esm/LinearGenomeView/components/HelpDialog.d.ts +4 -0
  162. package/esm/LinearGenomeView/components/HelpDialog.js +58 -0
  163. package/esm/LinearGenomeView/components/HelpDialog.js.map +1 -0
  164. package/esm/LinearGenomeView/components/ImportForm.d.ts +7 -0
  165. package/esm/LinearGenomeView/components/ImportForm.js +141 -0
  166. package/esm/LinearGenomeView/components/ImportForm.js.map +1 -0
  167. package/esm/LinearGenomeView/components/LinearGenomeView.d.ts +7 -0
  168. package/esm/LinearGenomeView/components/LinearGenomeView.js +67 -0
  169. package/esm/LinearGenomeView/components/LinearGenomeView.js.map +1 -0
  170. package/esm/LinearGenomeView/components/LinearGenomeViewSvg.d.ts +4 -0
  171. package/esm/LinearGenomeView/components/LinearGenomeViewSvg.js +132 -0
  172. package/esm/LinearGenomeView/components/LinearGenomeViewSvg.js.map +1 -0
  173. package/esm/LinearGenomeView/components/MiniControls.d.ts +6 -0
  174. package/esm/LinearGenomeView/components/MiniControls.js +25 -0
  175. package/esm/LinearGenomeView/components/MiniControls.js.map +1 -0
  176. package/esm/LinearGenomeView/components/OverviewRubberBand.d.ts +22 -0
  177. package/esm/LinearGenomeView/components/OverviewRubberBand.js +194 -0
  178. package/esm/LinearGenomeView/components/OverviewRubberBand.js.map +1 -0
  179. package/esm/LinearGenomeView/components/OverviewScaleBar.d.ts +148 -0
  180. package/esm/LinearGenomeView/components/OverviewScaleBar.js +287 -0
  181. package/esm/LinearGenomeView/components/OverviewScaleBar.js.map +1 -0
  182. package/esm/LinearGenomeView/components/RefNameAutocomplete.d.ts +22 -0
  183. package/esm/LinearGenomeView/components/RefNameAutocomplete.js +136 -0
  184. package/esm/LinearGenomeView/components/RefNameAutocomplete.js.map +1 -0
  185. package/esm/LinearGenomeView/components/RubberBand.d.ts +9 -0
  186. package/esm/LinearGenomeView/components/RubberBand.js +198 -0
  187. package/esm/LinearGenomeView/components/RubberBand.js.map +1 -0
  188. package/esm/LinearGenomeView/components/Ruler.d.ts +27 -0
  189. package/esm/LinearGenomeView/components/Ruler.js +50 -0
  190. package/esm/LinearGenomeView/components/Ruler.js.map +1 -0
  191. package/esm/LinearGenomeView/components/ScaleBar.d.ts +10 -0
  192. package/esm/LinearGenomeView/components/ScaleBar.js +112 -0
  193. package/esm/LinearGenomeView/components/ScaleBar.js.map +1 -0
  194. package/esm/LinearGenomeView/components/SearchBox.d.ts +8 -0
  195. package/esm/LinearGenomeView/components/SearchBox.js +94 -0
  196. package/esm/LinearGenomeView/components/SearchBox.js.map +1 -0
  197. package/esm/LinearGenomeView/components/SearchResultsDialog.d.ts +7 -0
  198. package/esm/LinearGenomeView/components/SearchResultsDialog.js +107 -0
  199. package/esm/LinearGenomeView/components/SearchResultsDialog.js.map +1 -0
  200. package/esm/LinearGenomeView/components/SequenceDialog.d.ts +8 -0
  201. package/esm/LinearGenomeView/components/SequenceDialog.js +147 -0
  202. package/esm/LinearGenomeView/components/SequenceDialog.js.map +1 -0
  203. package/esm/LinearGenomeView/components/TrackContainer.d.ts +9 -0
  204. package/esm/LinearGenomeView/components/TrackContainer.js +109 -0
  205. package/esm/LinearGenomeView/components/TrackContainer.js.map +1 -0
  206. package/esm/LinearGenomeView/components/TrackLabel.d.ts +8 -0
  207. package/esm/LinearGenomeView/components/TrackLabel.js +89 -0
  208. package/esm/LinearGenomeView/components/TrackLabel.js.map +1 -0
  209. package/esm/LinearGenomeView/components/TracksContainer.d.ts +10 -0
  210. package/esm/LinearGenomeView/components/TracksContainer.js +142 -0
  211. package/esm/LinearGenomeView/components/TracksContainer.js.map +1 -0
  212. package/esm/LinearGenomeView/components/VerticalGuides.d.ts +8 -0
  213. package/esm/LinearGenomeView/components/VerticalGuides.js +71 -0
  214. package/esm/LinearGenomeView/components/VerticalGuides.js.map +1 -0
  215. package/esm/LinearGenomeView/components/ZoomControls.d.ts +7 -0
  216. package/esm/LinearGenomeView/components/ZoomControls.js +32 -0
  217. package/esm/LinearGenomeView/components/ZoomControls.js.map +1 -0
  218. package/esm/LinearGenomeView/components/util.d.ts +14 -0
  219. package/esm/LinearGenomeView/components/util.js +17 -0
  220. package/esm/LinearGenomeView/components/util.js.map +1 -0
  221. package/esm/LinearGenomeView/index.d.ts +288 -0
  222. package/esm/LinearGenomeView/index.js +1124 -0
  223. package/esm/LinearGenomeView/index.js.map +1 -0
  224. package/esm/LinearGenomeView/util.d.ts +14 -0
  225. package/esm/LinearGenomeView/util.js +62 -0
  226. package/esm/LinearGenomeView/util.js.map +1 -0
  227. package/esm/index.d.ts +615 -0
  228. package/esm/index.js +127 -0
  229. package/esm/index.js.map +1 -0
  230. package/package.json +21 -14
  231. package/src/BaseLinearDisplay/components/BaseLinearDisplay.tsx +4 -3
  232. package/src/BaseLinearDisplay/components/Block.tsx +5 -5
  233. package/src/BaseLinearDisplay/components/LinearBlocks.tsx +4 -4
  234. package/src/BaseLinearDisplay/components/ServerSideRenderedBlockContent.tsx +7 -8
  235. package/src/BaseLinearDisplay/components/Tooltip.tsx +14 -4
  236. package/src/BaseLinearDisplay/models/BaseLinearDisplayModel.tsx +6 -4
  237. package/src/BaseLinearDisplay/models/serverSideRenderedBlock.ts +1 -1
  238. package/src/LinearBasicDisplay/components/SetMaxHeight.tsx +10 -7
  239. package/src/LinearBasicDisplay/model.ts +19 -11
  240. package/src/LinearGenomeView/components/CenterLine.tsx +6 -11
  241. package/src/LinearGenomeView/components/ExportSvgDialog.tsx +5 -5
  242. package/src/LinearGenomeView/components/Header.tsx +10 -15
  243. package/src/LinearGenomeView/components/HelpDialog.tsx +5 -5
  244. package/src/LinearGenomeView/components/ImportForm.tsx +6 -12
  245. package/src/LinearGenomeView/components/LinearGenomeView.test.js +16 -6
  246. package/src/LinearGenomeView/components/LinearGenomeView.tsx +4 -3
  247. package/src/LinearGenomeView/components/MiniControls.tsx +29 -40
  248. package/src/LinearGenomeView/components/OverviewRubberBand.tsx +6 -10
  249. package/src/LinearGenomeView/components/OverviewScaleBar.tsx +61 -59
  250. package/src/LinearGenomeView/components/RefNameAutocomplete.tsx +13 -44
  251. package/src/LinearGenomeView/components/RubberBand.tsx +12 -17
  252. package/src/LinearGenomeView/components/Ruler.tsx +5 -11
  253. package/src/LinearGenomeView/components/ScaleBar.tsx +23 -27
  254. package/src/LinearGenomeView/components/SearchBox.tsx +4 -3
  255. package/src/LinearGenomeView/components/SearchResultsDialog.tsx +7 -6
  256. package/src/LinearGenomeView/components/SequenceDialog.tsx +9 -9
  257. package/src/LinearGenomeView/components/TrackContainer.tsx +12 -12
  258. package/src/LinearGenomeView/components/TrackLabel.tsx +21 -31
  259. package/src/LinearGenomeView/components/TracksContainer.tsx +8 -13
  260. package/src/LinearGenomeView/components/VerticalGuides.tsx +9 -11
  261. package/src/LinearGenomeView/components/ZoomControls.tsx +12 -13
  262. package/src/LinearGenomeView/components/__snapshots__/LinearGenomeView.test.js.snap +500 -547
  263. package/src/LinearGenomeView/index.test.ts +1 -0
  264. package/src/LinearGenomeView/index.tsx +47 -62
  265. package/src/index.ts +1 -1
  266. package/dist/LinearBareDisplay/index.test.js +0 -33
  267. package/dist/LinearGenomeView/components/LinearGenomeView.test.js +0 -234
  268. package/dist/LinearGenomeView/components/ScaleBar.test.js +0 -180
  269. package/dist/LinearGenomeView/index.test.js +0 -1187
  270. package/dist/LinearGenomeView/util.test.js +0 -78
@@ -0,0 +1,1124 @@
1
+ import { getConf } from '@jbrowse/core/configuration';
2
+ import { BaseViewModel } from '@jbrowse/core/pluggableElementTypes/models';
3
+ import { ElementId, Region as MUIRegion } from '@jbrowse/core/util/types/mst';
4
+ import { ReturnToImportFormDialog } from '@jbrowse/core/ui';
5
+ import { assembleLocString, clamp, findLastIndex, getContainingView, getSession, isViewContainer, isSessionModelWithWidgets, measureText, parseLocString, springAnimate, viewBpToPx, } from '@jbrowse/core/util';
6
+ import calculateDynamicBlocks from '@jbrowse/core/util/calculateDynamicBlocks';
7
+ import calculateStaticBlocks from '@jbrowse/core/util/calculateStaticBlocks';
8
+ import { getParentRenderProps } from '@jbrowse/core/util/tracks';
9
+ import { transaction, autorun } from 'mobx';
10
+ import { addDisposer, cast, getSnapshot, getRoot, resolveIdentifier, types, } from 'mobx-state-tree';
11
+ import Base1DView from '@jbrowse/core/util/Base1DViewModel';
12
+ import clone from 'clone';
13
+ import { saveAs } from 'file-saver';
14
+ // icons
15
+ import { TrackSelector as TrackSelectorIcon } from '@jbrowse/core/ui/Icons';
16
+ import SyncAltIcon from '@mui/icons-material/SyncAlt';
17
+ import VisibilityIcon from '@mui/icons-material/Visibility';
18
+ import LabelIcon from '@mui/icons-material/Label';
19
+ import FolderOpenIcon from '@mui/icons-material/FolderOpen';
20
+ import PhotoCameraIcon from '@mui/icons-material/PhotoCamera';
21
+ import ZoomInIcon from '@mui/icons-material/ZoomIn';
22
+ import MenuOpenIcon from '@mui/icons-material/MenuOpen';
23
+ // locals
24
+ import { renderToSvg } from './components/LinearGenomeViewSvg';
25
+ import RefNameAutocomplete from './components/RefNameAutocomplete';
26
+ import SearchBox from './components/SearchBox';
27
+ import ExportSvgDlg from './components/ExportSvgDialog';
28
+ function calculateVisibleLocStrings(contentBlocks) {
29
+ if (!contentBlocks.length) {
30
+ return '';
31
+ }
32
+ const isSingleAssemblyName = contentBlocks.every(block => block.assemblyName === contentBlocks[0].assemblyName);
33
+ const locs = contentBlocks.map(block => assembleLocString({
34
+ ...block,
35
+ start: Math.round(block.start),
36
+ end: Math.round(block.end),
37
+ assemblyName: isSingleAssemblyName ? undefined : block.assemblyName,
38
+ }));
39
+ return locs.join(' ');
40
+ }
41
+ export const HEADER_BAR_HEIGHT = 48;
42
+ export const HEADER_OVERVIEW_HEIGHT = 20;
43
+ export const SCALE_BAR_HEIGHT = 17;
44
+ export const RESIZE_HANDLE_HEIGHT = 3;
45
+ export const INTER_REGION_PADDING_WIDTH = 2;
46
+ export const SPACING = 7;
47
+ export const WIDGET_HEIGHT = 32;
48
+ function localStorageGetItem(item) {
49
+ return typeof localStorage !== 'undefined'
50
+ ? localStorage.getItem(item)
51
+ : undefined;
52
+ }
53
+ export function stateModelFactory(pluginManager) {
54
+ return types
55
+ .compose(BaseViewModel, types.model('LinearGenomeView', {
56
+ id: ElementId,
57
+ type: types.literal('LinearGenomeView'),
58
+ offsetPx: 0,
59
+ bpPerPx: 1,
60
+ displayedRegions: types.array(MUIRegion),
61
+ // we use an array for the tracks because the tracks are displayed in a
62
+ // specific order that we need to keep.
63
+ tracks: types.array(pluginManager.pluggableMstType('track', 'stateModel')),
64
+ hideHeader: false,
65
+ hideHeaderOverview: false,
66
+ hideNoTracksActive: false,
67
+ trackSelectorType: types.optional(types.enumeration(['hierarchical']), 'hierarchical'),
68
+ trackLabels: types.optional(types.string, () => localStorageGetItem('lgv-trackLabels') || 'overlapping'),
69
+ showCenterLine: types.optional(types.boolean, () => {
70
+ const setting = localStorageGetItem('lgv-showCenterLine');
71
+ return setting !== undefined && setting !== null ? !!+setting : false;
72
+ }),
73
+ showCytobandsSetting: types.optional(types.boolean, () => {
74
+ const setting = localStorageGetItem('lgv-showCytobands');
75
+ return setting !== undefined && setting !== null ? !!+setting : true;
76
+ }),
77
+ }))
78
+ .volatile(() => ({
79
+ volatileWidth: undefined,
80
+ minimumBlockWidth: 3,
81
+ draggingTrackId: undefined,
82
+ volatileError: undefined,
83
+ // array of callbacks to run after the next set of the displayedRegions,
84
+ // which is basically like an onLoad
85
+ afterDisplayedRegionsSetCallbacks: [],
86
+ scaleFactor: 1,
87
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
88
+ trackRefs: {},
89
+ coarseDynamicBlocks: [],
90
+ coarseTotalBp: 0,
91
+ leftOffset: undefined,
92
+ rightOffset: undefined,
93
+ searchResults: undefined,
94
+ searchQuery: undefined,
95
+ seqDialogDisplayed: false,
96
+ }))
97
+ .views(self => ({
98
+ get width() {
99
+ if (self.volatileWidth === undefined) {
100
+ throw new Error('width undefined, make sure to check for model.initialized');
101
+ }
102
+ return self.volatileWidth;
103
+ },
104
+ get interRegionPaddingWidth() {
105
+ return INTER_REGION_PADDING_WIDTH;
106
+ },
107
+ get assemblyNames() {
108
+ return [
109
+ ...new Set(self.displayedRegions.map(region => region.assemblyName)),
110
+ ];
111
+ },
112
+ }))
113
+ .views(self => ({
114
+ get assemblyErrors() {
115
+ const { assemblyManager } = getSession(self);
116
+ const { assemblyNames } = self;
117
+ return assemblyNames
118
+ .map(a => { var _a; return (_a = assemblyManager.get(a)) === null || _a === void 0 ? void 0 : _a.error; })
119
+ .filter(f => !!f)
120
+ .join(', ');
121
+ },
122
+ get assembliesInitialized() {
123
+ const { assemblyManager } = getSession(self);
124
+ const { assemblyNames } = self;
125
+ return assemblyNames.every(a => { var _a; return (_a = assemblyManager.get(a)) === null || _a === void 0 ? void 0 : _a.initialized; });
126
+ },
127
+ get initialized() {
128
+ return self.volatileWidth !== undefined && this.assembliesInitialized;
129
+ },
130
+ get hasDisplayedRegions() {
131
+ return self.displayedRegions.length > 0;
132
+ },
133
+ get isSearchDialogDisplayed() {
134
+ return self.searchResults !== undefined;
135
+ },
136
+ get scaleBarHeight() {
137
+ return SCALE_BAR_HEIGHT + RESIZE_HANDLE_HEIGHT;
138
+ },
139
+ get headerHeight() {
140
+ if (self.hideHeader) {
141
+ return 0;
142
+ }
143
+ if (self.hideHeaderOverview) {
144
+ return HEADER_BAR_HEIGHT;
145
+ }
146
+ return HEADER_BAR_HEIGHT + HEADER_OVERVIEW_HEIGHT;
147
+ },
148
+ get trackHeights() {
149
+ return self.tracks
150
+ .map(t => t.displays[0].height)
151
+ .reduce((a, b) => a + b, 0);
152
+ },
153
+ get trackHeightsWithResizeHandles() {
154
+ return this.trackHeights + self.tracks.length * RESIZE_HANDLE_HEIGHT;
155
+ },
156
+ get height() {
157
+ return (this.trackHeightsWithResizeHandles +
158
+ this.headerHeight +
159
+ this.scaleBarHeight);
160
+ },
161
+ get totalBp() {
162
+ return self.displayedRegions.reduce((a, b) => a + b.end - b.start, 0);
163
+ },
164
+ get maxBpPerPx() {
165
+ return this.totalBp / (self.width * 0.9);
166
+ },
167
+ get minBpPerPx() {
168
+ return 1 / 50;
169
+ },
170
+ get error() {
171
+ return self.volatileError || this.assemblyErrors;
172
+ },
173
+ get maxOffset() {
174
+ // objectively determined to keep the linear genome on the main screen
175
+ const leftPadding = 10;
176
+ return this.displayedRegionsTotalPx - leftPadding;
177
+ },
178
+ get minOffset() {
179
+ // objectively determined to keep the linear genome on the main screen
180
+ const rightPadding = 30;
181
+ return -self.width + rightPadding;
182
+ },
183
+ get displayedRegionsTotalPx() {
184
+ return this.totalBp / self.bpPerPx;
185
+ },
186
+ renderProps() {
187
+ return {
188
+ ...getParentRenderProps(self),
189
+ bpPerPx: self.bpPerPx,
190
+ highResolutionScaling: getConf(getSession(self), 'highResolutionScaling'),
191
+ };
192
+ },
193
+ searchScope(assemblyName) {
194
+ return {
195
+ assemblyName,
196
+ includeAggregateIndexes: true,
197
+ tracks: self.tracks,
198
+ };
199
+ },
200
+ bpToPx({ refName, coord, regionNumber, }) {
201
+ return viewBpToPx({ refName, coord, regionNumber, self });
202
+ },
203
+ /**
204
+ *
205
+ * @param px - px in the view area, return value is the displayed regions
206
+ * @returns BpOffset of the displayed region that it lands in
207
+ */
208
+ pxToBp(px) {
209
+ let bpSoFar = 0;
210
+ const bp = (self.offsetPx + px) * self.bpPerPx;
211
+ const n = self.displayedRegions.length;
212
+ if (bp < 0) {
213
+ const region = self.displayedRegions[0];
214
+ const offset = bp;
215
+ const snap = getSnapshot(region);
216
+ return {
217
+ // xref https://github.com/mobxjs/mobx-state-tree/issues/1524 for Omit
218
+ ...snap,
219
+ oob: true,
220
+ coord: region.reversed
221
+ ? Math.floor(region.end - offset) + 1
222
+ : Math.floor(region.start + offset) + 1,
223
+ offset,
224
+ index: 0,
225
+ };
226
+ }
227
+ const interRegionPaddingBp = self.interRegionPaddingWidth * self.bpPerPx;
228
+ const minimumBlockBp = self.minimumBlockWidth * self.bpPerPx;
229
+ for (let index = 0; index < self.displayedRegions.length; index += 1) {
230
+ const region = self.displayedRegions[index];
231
+ const len = region.end - region.start;
232
+ const offset = bp - bpSoFar;
233
+ if (len + bpSoFar > bp && bpSoFar <= bp) {
234
+ const snap = getSnapshot(region);
235
+ return {
236
+ // xref https://github.com/mobxjs/mobx-state-tree/issues/1524 for Omit
237
+ ...snap,
238
+ oob: false,
239
+ offset,
240
+ coord: region.reversed
241
+ ? Math.floor(region.end - offset) + 1
242
+ : Math.floor(region.start + offset) + 1,
243
+ index,
244
+ };
245
+ }
246
+ // add the interRegionPaddingWidth if the boundary is in the screen
247
+ // e.g. offset>0 && offset<width
248
+ if (region.end - region.start > minimumBlockBp &&
249
+ offset / self.bpPerPx > 0 &&
250
+ offset / self.bpPerPx < self.width) {
251
+ bpSoFar += len + interRegionPaddingBp;
252
+ }
253
+ else {
254
+ bpSoFar += len;
255
+ }
256
+ }
257
+ if (bp >= bpSoFar) {
258
+ const region = self.displayedRegions[n - 1];
259
+ const len = region.end - region.start;
260
+ const offset = bp - bpSoFar + len;
261
+ const snap = getSnapshot(region);
262
+ return {
263
+ // xref https://github.com/mobxjs/mobx-state-tree/issues/1524 for Omit
264
+ ...snap,
265
+ oob: true,
266
+ offset,
267
+ coord: region.reversed
268
+ ? Math.floor(region.end - offset) + 1
269
+ : Math.floor(region.start + offset) + 1,
270
+ index: n - 1,
271
+ };
272
+ }
273
+ return {
274
+ coord: 0,
275
+ index: 0,
276
+ refName: '',
277
+ oob: true,
278
+ assemblyName: '',
279
+ offset: 0,
280
+ start: 0,
281
+ end: 0,
282
+ reversed: false,
283
+ };
284
+ },
285
+ getTrack(id) {
286
+ return self.tracks.find(t => t.configuration.trackId === id);
287
+ },
288
+ rankSearchResults(results) {
289
+ // order of rank
290
+ const openTrackIds = self.tracks.map(track => track.configuration.trackId);
291
+ results.forEach(result => {
292
+ if (openTrackIds.includes(result.trackId)) {
293
+ result.updateScore(result.getScore() + 1);
294
+ }
295
+ });
296
+ return results;
297
+ },
298
+ // modifies view menu action onClick to apply to all tracks of same type
299
+ rewriteOnClicks(trackType, viewMenuActions) {
300
+ viewMenuActions.forEach(action => {
301
+ // go to lowest level menu
302
+ if ('subMenu' in action) {
303
+ this.rewriteOnClicks(trackType, action.subMenu);
304
+ }
305
+ if ('onClick' in action) {
306
+ const holdOnClick = action.onClick;
307
+ action.onClick = (...args) => {
308
+ self.tracks.forEach(track => {
309
+ if (track.type === trackType) {
310
+ holdOnClick.apply(track, [track, ...args]);
311
+ }
312
+ });
313
+ };
314
+ }
315
+ });
316
+ },
317
+ get trackTypeActions() {
318
+ const allActions = new Map();
319
+ self.tracks.forEach(track => {
320
+ const trackInMap = allActions.get(track.type);
321
+ if (!trackInMap) {
322
+ const viewMenuActions = clone(track.viewMenuActions);
323
+ this.rewriteOnClicks(track.type, viewMenuActions);
324
+ allActions.set(track.type, viewMenuActions);
325
+ }
326
+ });
327
+ return allActions;
328
+ },
329
+ get centerLineInfo() {
330
+ return self.displayedRegions.length
331
+ ? this.pxToBp(self.width / 2)
332
+ : undefined;
333
+ },
334
+ }))
335
+ .actions(self => ({
336
+ setShowCytobands(flag) {
337
+ self.showCytobandsSetting = flag;
338
+ localStorage.setItem('lgv-showCytobands', `${+flag}`);
339
+ },
340
+ setWidth(newWidth) {
341
+ self.volatileWidth = newWidth;
342
+ },
343
+ setError(error) {
344
+ self.volatileError = error;
345
+ },
346
+ toggleHeader() {
347
+ self.hideHeader = !self.hideHeader;
348
+ },
349
+ toggleHeaderOverview() {
350
+ self.hideHeaderOverview = !self.hideHeaderOverview;
351
+ },
352
+ toggleNoTracksActive() {
353
+ self.hideNoTracksActive = !self.hideNoTracksActive;
354
+ },
355
+ scrollTo(offsetPx) {
356
+ const newOffsetPx = clamp(offsetPx, self.minOffset, self.maxOffset);
357
+ self.offsetPx = newOffsetPx;
358
+ return newOffsetPx;
359
+ },
360
+ zoomTo(bpPerPx) {
361
+ const newBpPerPx = clamp(bpPerPx, self.minBpPerPx, self.maxBpPerPx);
362
+ if (newBpPerPx === self.bpPerPx) {
363
+ return newBpPerPx;
364
+ }
365
+ const oldBpPerPx = self.bpPerPx;
366
+ self.bpPerPx = newBpPerPx;
367
+ if (Math.abs(oldBpPerPx - newBpPerPx) < 0.000001) {
368
+ console.warn('zoomTo bpPerPx rounding error');
369
+ return oldBpPerPx;
370
+ }
371
+ // tweak the offset so that the center of the view remains at the same coordinate
372
+ const viewWidth = self.width;
373
+ this.scrollTo(Math.round(((self.offsetPx + viewWidth / 2) * oldBpPerPx) / newBpPerPx -
374
+ viewWidth / 2));
375
+ return newBpPerPx;
376
+ },
377
+ setOffsets(left, right) {
378
+ // sets offsets used in the get sequence dialog
379
+ self.leftOffset = left;
380
+ self.rightOffset = right;
381
+ },
382
+ setSearchResults(results, query) {
383
+ self.searchResults = results;
384
+ self.searchQuery = query;
385
+ },
386
+ setSequenceDialogOpen(open) {
387
+ self.seqDialogDisplayed = open;
388
+ },
389
+ setNewView(bpPerPx, offsetPx) {
390
+ this.zoomTo(bpPerPx);
391
+ this.scrollTo(offsetPx);
392
+ },
393
+ horizontallyFlip() {
394
+ self.displayedRegions = cast(self.displayedRegions
395
+ .slice()
396
+ .reverse()
397
+ .map(region => ({ ...region, reversed: !region.reversed })));
398
+ this.scrollTo(self.totalBp / self.bpPerPx - self.offsetPx - self.width);
399
+ },
400
+ showTrack(trackId, initialSnapshot = {}, displayInitialSnapshot = {}) {
401
+ const schema = pluginManager.pluggableConfigSchemaType('track');
402
+ const conf = resolveIdentifier(schema, getRoot(self), trackId);
403
+ if (!conf) {
404
+ throw new Error(`Could not resolve identifier "${trackId}"`);
405
+ }
406
+ const trackType = pluginManager.getTrackType(conf === null || conf === void 0 ? void 0 : conf.type);
407
+ if (!trackType) {
408
+ throw new Error(`Unknown track type ${conf.type}`);
409
+ }
410
+ const viewType = pluginManager.getViewType(self.type);
411
+ const supportedDisplays = viewType.displayTypes.map(d => d.name);
412
+ const displayConf = conf.displays.find((d) => supportedDisplays.includes(d.type));
413
+ if (!displayConf) {
414
+ throw new Error(`Could not find a compatible display for view type ${self.type}`);
415
+ }
416
+ const t = self.tracks.filter(t => t.configuration === conf);
417
+ if (t.length === 0) {
418
+ const track = trackType.stateModel.create({
419
+ ...initialSnapshot,
420
+ type: conf.type,
421
+ configuration: conf,
422
+ displays: [
423
+ {
424
+ type: displayConf.type,
425
+ configuration: displayConf,
426
+ ...displayInitialSnapshot,
427
+ },
428
+ ],
429
+ });
430
+ self.tracks.push(track);
431
+ return track;
432
+ }
433
+ return t[0];
434
+ },
435
+ hideTrack(trackId) {
436
+ const schema = pluginManager.pluggableConfigSchemaType('track');
437
+ const conf = resolveIdentifier(schema, getRoot(self), trackId);
438
+ const t = self.tracks.filter(t => t.configuration === conf);
439
+ transaction(() => t.forEach(t => self.tracks.remove(t)));
440
+ return t.length;
441
+ },
442
+ }))
443
+ .actions(self => ({
444
+ moveTrack(movingId, targetId) {
445
+ const oldIndex = self.tracks.findIndex(track => track.id === movingId);
446
+ if (oldIndex === -1) {
447
+ throw new Error(`Track ID ${movingId} not found`);
448
+ }
449
+ const newIndex = self.tracks.findIndex(track => track.id === targetId);
450
+ if (newIndex === -1) {
451
+ throw new Error(`Track ID ${targetId} not found`);
452
+ }
453
+ const track = getSnapshot(self.tracks[oldIndex]);
454
+ self.tracks.splice(oldIndex, 1);
455
+ self.tracks.splice(newIndex, 0, track);
456
+ },
457
+ closeView() {
458
+ const parent = getContainingView(self);
459
+ if (parent) {
460
+ // I am embedded in a some other view
461
+ if (isViewContainer(parent)) {
462
+ parent.removeView(self);
463
+ }
464
+ }
465
+ else {
466
+ // I am part of a session
467
+ getSession(self).removeView(self);
468
+ }
469
+ },
470
+ toggleTrack(trackId) {
471
+ // if we have any tracks with that configuration, turn them off
472
+ const hiddenCount = self.hideTrack(trackId);
473
+ // if none had that configuration, turn one on
474
+ if (!hiddenCount) {
475
+ self.showTrack(trackId);
476
+ }
477
+ },
478
+ setTrackLabels(setting) {
479
+ self.trackLabels = setting;
480
+ localStorage.setItem('lgv-trackLabels', setting);
481
+ },
482
+ toggleCenterLine() {
483
+ self.showCenterLine = !self.showCenterLine;
484
+ localStorage.setItem('lgv-showCenterLine', `${+self.showCenterLine}`);
485
+ },
486
+ setDisplayedRegions(regions) {
487
+ self.displayedRegions = cast(regions);
488
+ self.zoomTo(self.bpPerPx);
489
+ },
490
+ activateTrackSelector() {
491
+ if (self.trackSelectorType === 'hierarchical') {
492
+ const session = getSession(self);
493
+ if (isSessionModelWithWidgets(session)) {
494
+ const selector = session.addWidget('HierarchicalTrackSelectorWidget', 'hierarchicalTrackSelector', { view: self });
495
+ session.showWidget(selector);
496
+ return selector;
497
+ }
498
+ }
499
+ throw new Error(`invalid track selector type ${self.trackSelectorType}`);
500
+ },
501
+ navToLocString(locString, optAssemblyName) {
502
+ const { assemblyNames } = self;
503
+ const { assemblyManager } = getSession(self);
504
+ const { isValidRefName } = assemblyManager;
505
+ const assemblyName = optAssemblyName || assemblyNames[0];
506
+ let parsedLocStrings;
507
+ const inputs = locString
508
+ .split(/(\s+)/)
509
+ .map(f => f.trim())
510
+ .filter(f => !!f);
511
+ // first try interpreting as a whitespace-separated sequence of
512
+ // multiple locstrings
513
+ try {
514
+ parsedLocStrings = inputs.map(l => parseLocString(l, ref => isValidRefName(ref, assemblyName)));
515
+ }
516
+ catch (e) {
517
+ // if this fails, try interpreting as a whitespace-separated refname,
518
+ // start, end if start and end are integer inputs
519
+ const [refName, start, end] = inputs;
520
+ if (`${e}`.match(/Unknown reference sequence/) &&
521
+ Number.isInteger(+start) &&
522
+ Number.isInteger(+end)) {
523
+ parsedLocStrings = [
524
+ parseLocString(refName + ':' + start + '..' + end, ref => isValidRefName(ref, assemblyName)),
525
+ ];
526
+ }
527
+ else {
528
+ throw e;
529
+ }
530
+ }
531
+ const locations = parsedLocStrings === null || parsedLocStrings === void 0 ? void 0 : parsedLocStrings.map(region => {
532
+ const asmName = region.assemblyName || assemblyName;
533
+ const asm = assemblyManager.get(asmName);
534
+ const { refName } = region;
535
+ if (!asm) {
536
+ throw new Error(`assembly ${asmName} not found`);
537
+ }
538
+ const { regions } = asm;
539
+ if (!regions) {
540
+ throw new Error(`regions not loaded yet for ${asmName}`);
541
+ }
542
+ const canonicalRefName = asm.getCanonicalRefName(region.refName);
543
+ if (!canonicalRefName) {
544
+ throw new Error(`Could not find refName ${refName} in ${asm.name}`);
545
+ }
546
+ const parentRegion = regions.find(region => region.refName === canonicalRefName);
547
+ if (!parentRegion) {
548
+ throw new Error(`Could not find refName ${refName} in ${asmName}`);
549
+ }
550
+ return {
551
+ ...region,
552
+ assemblyName: asmName,
553
+ parentRegion,
554
+ };
555
+ });
556
+ if (locations.length === 1) {
557
+ const loc = locations[0];
558
+ this.setDisplayedRegions([
559
+ { reversed: loc.reversed, ...loc.parentRegion },
560
+ ]);
561
+ const { start, end, parentRegion } = loc;
562
+ this.navTo({
563
+ ...loc,
564
+ start: clamp(start !== null && start !== void 0 ? start : 0, 0, parentRegion.end),
565
+ end: clamp(end !== null && end !== void 0 ? end : parentRegion.end, 0, parentRegion.end),
566
+ });
567
+ }
568
+ else {
569
+ this.setDisplayedRegions(
570
+ // @ts-ignore
571
+ locations.map(r => (r.start === undefined ? r.parentRegion : r)));
572
+ this.showAllRegions();
573
+ }
574
+ },
575
+ /**
576
+ * Navigate to a location based on its refName and optionally start, end,
577
+ * and assemblyName. Can handle if there are multiple displayedRegions
578
+ * from same refName. Only navigates to a location if it is entirely
579
+ * within a displayedRegion. Navigates to the first matching location
580
+ * encountered.
581
+ *
582
+ * Throws an error if navigation was unsuccessful
583
+ *
584
+ * @param location - a proposed location to navigate to
585
+ */
586
+ navTo(query) {
587
+ this.navToMultiple([query]);
588
+ },
589
+ navToMultiple(locations) {
590
+ const firstLocation = locations[0];
591
+ let { refName } = firstLocation;
592
+ const { start, end, assemblyName = self.assemblyNames[0], } = firstLocation;
593
+ if (start !== undefined && end !== undefined && start > end) {
594
+ throw new Error(`start "${start + 1}" is greater than end "${end}"`);
595
+ }
596
+ const session = getSession(self);
597
+ const { assemblyManager } = session;
598
+ const assembly = assemblyManager.get(assemblyName);
599
+ if (assembly) {
600
+ const canonicalRefName = assembly.getCanonicalRefName(refName);
601
+ if (canonicalRefName) {
602
+ refName = canonicalRefName;
603
+ }
604
+ }
605
+ let s = start;
606
+ let e = end;
607
+ let refNameMatched = false;
608
+ const predicate = (r) => {
609
+ if (refName === r.refName) {
610
+ refNameMatched = true;
611
+ if (s === undefined) {
612
+ s = r.start;
613
+ }
614
+ if (e === undefined) {
615
+ e = r.end;
616
+ }
617
+ if (s >= r.start && s <= r.end && e <= r.end && e >= r.start) {
618
+ return true;
619
+ }
620
+ s = start;
621
+ e = end;
622
+ }
623
+ return false;
624
+ };
625
+ const lastIndex = findLastIndex(self.displayedRegions, predicate);
626
+ let index;
627
+ while (index !== lastIndex) {
628
+ try {
629
+ const previousIndex = index;
630
+ index = self.displayedRegions
631
+ .slice(previousIndex === undefined ? 0 : previousIndex + 1)
632
+ .findIndex(predicate);
633
+ if (previousIndex !== undefined) {
634
+ index += previousIndex + 1;
635
+ }
636
+ if (!refNameMatched) {
637
+ throw new Error(`could not find a region with refName "${refName}"`);
638
+ }
639
+ if (s === undefined) {
640
+ throw new Error(`could not find a region with refName "${refName}" that contained an end position ${e}`);
641
+ }
642
+ if (e === undefined) {
643
+ throw new Error(`could not find a region with refName "${refName}" that contained a start position ${s + 1}`);
644
+ }
645
+ if (index === -1) {
646
+ throw new Error(`could not find a region that completely contained "${assembleLocString(firstLocation)}"`);
647
+ }
648
+ if (locations.length === 1) {
649
+ const f = self.displayedRegions[index];
650
+ this.moveTo({ index, offset: f.reversed ? f.end - e : s - f.start }, { index, offset: f.reversed ? f.end - s : e - f.start });
651
+ return;
652
+ }
653
+ let locationIndex = 0;
654
+ let locationStart = 0;
655
+ let locationEnd = 0;
656
+ for (locationIndex; locationIndex < locations.length; locationIndex++) {
657
+ const location = locations[locationIndex];
658
+ const region = self.displayedRegions[index + locationIndex];
659
+ locationStart = location.start || region.start;
660
+ locationEnd = location.end || region.end;
661
+ if (location.refName !== region.refName) {
662
+ throw new Error(`Entered location ${assembleLocString(location)} does not match with displayed regions`);
663
+ }
664
+ }
665
+ locationIndex -= 1;
666
+ const startDisplayedRegion = self.displayedRegions[index];
667
+ const endDisplayedRegion = self.displayedRegions[index + locationIndex];
668
+ this.moveTo({
669
+ index,
670
+ offset: startDisplayedRegion.reversed
671
+ ? startDisplayedRegion.end - e
672
+ : s - startDisplayedRegion.start,
673
+ }, {
674
+ index: index + locationIndex,
675
+ offset: endDisplayedRegion.reversed
676
+ ? endDisplayedRegion.end - locationStart
677
+ : locationEnd - endDisplayedRegion.start,
678
+ });
679
+ return;
680
+ }
681
+ catch (error) {
682
+ if (index === lastIndex) {
683
+ throw error;
684
+ }
685
+ }
686
+ }
687
+ },
688
+ /**
689
+ * Navigate to a location based on user clicking and dragging on the
690
+ * overview scale bar to select a region to zoom into.
691
+ * Can handle if there are multiple displayedRegions from same refName.
692
+ * Only navigates to a location if it is entirely within a displayedRegion.
693
+ *
694
+ * @param leftPx- `object as {start, end, index, offset}`, offset = start of user drag
695
+ * @param rightPx- `object as {start, end, index, offset}`, offset = end of user drag
696
+ */
697
+ zoomToDisplayedRegions(leftPx, rightPx) {
698
+ if (leftPx === undefined || rightPx === undefined) {
699
+ return;
700
+ }
701
+ const singleRefSeq = leftPx.refName === rightPx.refName && leftPx.index === rightPx.index;
702
+ // zooming into one displayed Region
703
+ if ((singleRefSeq && rightPx.offset < leftPx.offset) ||
704
+ leftPx.index > rightPx.index) {
705
+ ;
706
+ [leftPx, rightPx] = [rightPx, leftPx];
707
+ }
708
+ const startOffset = {
709
+ start: leftPx.start,
710
+ end: leftPx.end,
711
+ index: leftPx.index,
712
+ offset: leftPx.offset,
713
+ };
714
+ const endOffset = {
715
+ start: rightPx.start,
716
+ end: rightPx.end,
717
+ index: rightPx.index,
718
+ offset: rightPx.offset,
719
+ };
720
+ if (startOffset && endOffset) {
721
+ this.moveTo(startOffset, endOffset);
722
+ }
723
+ else {
724
+ const session = getSession(self);
725
+ session.notify('No regions found to navigate to', 'warning');
726
+ }
727
+ },
728
+ /**
729
+ * Helper method for the fetchSequence.
730
+ * Retrieves the corresponding regions that were selected by the rubberband
731
+ *
732
+ * @param leftOffset - `object as {start, end, index, offset}`, offset = start of user drag
733
+ * @param rightOffset - `object as {start, end, index, offset}`, offset = end of user drag
734
+ * @returns array of Region[]
735
+ */
736
+ getSelectedRegions(leftOffset, rightOffset) {
737
+ const snap = getSnapshot(self);
738
+ const simView = Base1DView.create({
739
+ // xref https://github.com/mobxjs/mobx-state-tree/issues/1524 for Omit
740
+ ...snap,
741
+ interRegionPaddingWidth: self.interRegionPaddingWidth,
742
+ });
743
+ simView.setVolatileWidth(self.width);
744
+ simView.zoomToDisplayedRegions(leftOffset, rightOffset);
745
+ return simView.dynamicBlocks.contentBlocks.map(region => ({
746
+ ...region,
747
+ start: Math.floor(region.start),
748
+ end: Math.ceil(region.end),
749
+ }));
750
+ },
751
+ // schedule something to be run after the next time displayedRegions is set
752
+ afterDisplayedRegionsSet(cb) {
753
+ self.afterDisplayedRegionsSetCallbacks.push(cb);
754
+ },
755
+ /**
756
+ * offset is the base-pair-offset in the displayed region, index is the index of the
757
+ * displayed region in the linear genome view
758
+ *
759
+ * @param start - object as `{start, end, offset, index}`
760
+ * @param end - object as `{start, end, offset, index}`
761
+ */
762
+ moveTo(start, end) {
763
+ // find locations in the modellist
764
+ let bpSoFar = 0;
765
+ if (start.index === end.index) {
766
+ bpSoFar += end.offset - start.offset;
767
+ }
768
+ else {
769
+ const s = self.displayedRegions[start.index];
770
+ bpSoFar += s.end - s.start - start.offset;
771
+ if (end.index - start.index >= 2) {
772
+ for (let i = start.index + 1; i < end.index; i += 1) {
773
+ bpSoFar +=
774
+ self.displayedRegions[i].end - self.displayedRegions[i].start;
775
+ }
776
+ }
777
+ bpSoFar += end.offset;
778
+ }
779
+ const targetBpPerPx = bpSoFar /
780
+ (self.width -
781
+ self.interRegionPaddingWidth * (end.index - start.index));
782
+ const newBpPerPx = self.zoomTo(targetBpPerPx);
783
+ // If our target bpPerPx was smaller than the allowed minBpPerPx, adjust
784
+ // the scroll so the requested range is in the middle of the screen
785
+ let extraBp = 0;
786
+ if (targetBpPerPx < newBpPerPx) {
787
+ extraBp = ((newBpPerPx - targetBpPerPx) * self.width) / 2;
788
+ }
789
+ let bpToStart = -extraBp;
790
+ for (let i = 0; i < self.displayedRegions.length; i += 1) {
791
+ const region = self.displayedRegions[i];
792
+ if (start.index === i) {
793
+ bpToStart += start.offset;
794
+ break;
795
+ }
796
+ else {
797
+ bpToStart += region.end - region.start;
798
+ }
799
+ }
800
+ self.scrollTo(Math.round(bpToStart / self.bpPerPx) +
801
+ self.interRegionPaddingWidth * start.index);
802
+ },
803
+ horizontalScroll(distance) {
804
+ const oldOffsetPx = self.offsetPx;
805
+ // newOffsetPx is the actual offset after the scroll is clamped
806
+ const newOffsetPx = self.scrollTo(self.offsetPx + distance);
807
+ return newOffsetPx - oldOffsetPx;
808
+ },
809
+ /**
810
+ * scrolls the view to center on the given bp. if that is not in any
811
+ * of the displayed regions, does nothing
812
+ * @param bp - basepair at which you want to center the view
813
+ * @param refName - refName of the displayedRegion you are centering at
814
+ * @param regionIndex - index of the displayedRegion
815
+ */
816
+ centerAt(bp, refName, regionIndex) {
817
+ const centerPx = self.bpToPx({
818
+ refName,
819
+ coord: bp,
820
+ regionNumber: regionIndex,
821
+ });
822
+ if (centerPx) {
823
+ self.scrollTo(Math.round(centerPx.offsetPx - self.width / 2));
824
+ }
825
+ },
826
+ center() {
827
+ const centerBp = self.totalBp / 2;
828
+ self.scrollTo(Math.round(centerBp / self.bpPerPx - self.width / 2));
829
+ },
830
+ showAllRegions() {
831
+ self.zoomTo(self.maxBpPerPx);
832
+ this.center();
833
+ },
834
+ showAllRegionsInAssembly(assemblyName) {
835
+ const session = getSession(self);
836
+ const { assemblyManager } = session;
837
+ if (!assemblyName) {
838
+ const assemblyNames = [
839
+ ...new Set(self.displayedRegions.map(region => region.assemblyName)),
840
+ ];
841
+ if (assemblyNames.length > 1) {
842
+ session.notify(`Can't perform this with multiple assemblies currently`);
843
+ return;
844
+ }
845
+ ;
846
+ [assemblyName] = assemblyNames;
847
+ }
848
+ const assembly = assemblyManager.get(assemblyName);
849
+ if (assembly) {
850
+ const { regions } = assembly;
851
+ if (regions) {
852
+ this.setDisplayedRegions(regions);
853
+ self.zoomTo(self.maxBpPerPx);
854
+ this.center();
855
+ }
856
+ }
857
+ },
858
+ setDraggingTrackId(idx) {
859
+ self.draggingTrackId = idx;
860
+ },
861
+ setScaleFactor(factor) {
862
+ self.scaleFactor = factor;
863
+ },
864
+ }))
865
+ .actions(self => {
866
+ let cancelLastAnimation = () => { };
867
+ function slide(viewWidths) {
868
+ const [animate, cancelAnimation] = springAnimate(self.offsetPx, self.offsetPx + self.width * viewWidths, self.scrollTo);
869
+ cancelLastAnimation();
870
+ cancelLastAnimation = cancelAnimation;
871
+ animate();
872
+ }
873
+ return { slide };
874
+ })
875
+ .actions(self => {
876
+ let cancelLastAnimation = () => { };
877
+ function zoom(targetBpPerPx) {
878
+ self.zoomTo(self.bpPerPx);
879
+ if (
880
+ // already zoomed all the way in
881
+ (targetBpPerPx < self.bpPerPx && self.bpPerPx === self.minBpPerPx) ||
882
+ // already zoomed all the way out
883
+ (targetBpPerPx > self.bpPerPx && self.bpPerPx === self.maxBpPerPx)) {
884
+ return;
885
+ }
886
+ const factor = self.bpPerPx / targetBpPerPx;
887
+ const [animate, cancelAnimation] = springAnimate(1, factor, self.setScaleFactor, () => {
888
+ self.zoomTo(targetBpPerPx);
889
+ self.setScaleFactor(1);
890
+ });
891
+ cancelLastAnimation();
892
+ cancelLastAnimation = cancelAnimation;
893
+ animate();
894
+ }
895
+ return { zoom };
896
+ })
897
+ .views(self => ({
898
+ get canShowCytobands() {
899
+ return self.displayedRegions.length === 1 && this.anyCytobandsExist;
900
+ },
901
+ get showCytobands() {
902
+ return this.canShowCytobands && self.showCytobandsSetting;
903
+ },
904
+ get anyCytobandsExist() {
905
+ const { assemblyManager } = getSession(self);
906
+ const { assemblyNames } = self;
907
+ return assemblyNames.some(asm => { var _a, _b; return (_b = (_a = assemblyManager.get(asm)) === null || _a === void 0 ? void 0 : _a.cytobands) === null || _b === void 0 ? void 0 : _b.length; });
908
+ },
909
+ get cytobandOffset() {
910
+ return this.showCytobands
911
+ ? measureText(self.displayedRegions[0].refName, 12) + 15
912
+ : 0;
913
+ },
914
+ }))
915
+ .views(self => ({
916
+ menuItems() {
917
+ const { canShowCytobands, showCytobands } = self;
918
+ const menuItems = [
919
+ {
920
+ label: 'Return to import form',
921
+ onClick: () => {
922
+ getSession(self).queueDialog(handleClose => [
923
+ ReturnToImportFormDialog,
924
+ { model: self, handleClose },
925
+ ]);
926
+ },
927
+ icon: FolderOpenIcon,
928
+ },
929
+ {
930
+ label: 'Export SVG',
931
+ icon: PhotoCameraIcon,
932
+ onClick: () => {
933
+ getSession(self).queueDialog(handleClose => [
934
+ ExportSvgDlg,
935
+ { model: self, handleClose },
936
+ ]);
937
+ },
938
+ },
939
+ {
940
+ label: 'Open track selector',
941
+ onClick: self.activateTrackSelector,
942
+ icon: TrackSelectorIcon,
943
+ },
944
+ {
945
+ label: 'Horizontally flip',
946
+ icon: SyncAltIcon,
947
+ onClick: self.horizontallyFlip,
948
+ },
949
+ { type: 'divider' },
950
+ {
951
+ label: 'Show all regions in assembly',
952
+ icon: VisibilityIcon,
953
+ onClick: self.showAllRegionsInAssembly,
954
+ },
955
+ {
956
+ label: 'Show center line',
957
+ icon: VisibilityIcon,
958
+ type: 'checkbox',
959
+ checked: self.showCenterLine,
960
+ onClick: self.toggleCenterLine,
961
+ },
962
+ {
963
+ label: 'Show header',
964
+ icon: VisibilityIcon,
965
+ type: 'checkbox',
966
+ checked: !self.hideHeader,
967
+ onClick: self.toggleHeader,
968
+ },
969
+ {
970
+ label: 'Show header overview',
971
+ icon: VisibilityIcon,
972
+ type: 'checkbox',
973
+ checked: !self.hideHeaderOverview,
974
+ onClick: self.toggleHeaderOverview,
975
+ disabled: self.hideHeader,
976
+ },
977
+ {
978
+ label: 'Show no tracks active button',
979
+ icon: VisibilityIcon,
980
+ type: 'checkbox',
981
+ checked: !self.hideNoTracksActive,
982
+ onClick: self.toggleNoTracksActive,
983
+ },
984
+ {
985
+ label: 'Track labels',
986
+ icon: LabelIcon,
987
+ subMenu: [
988
+ {
989
+ label: 'Overlapping',
990
+ icon: VisibilityIcon,
991
+ type: 'radio',
992
+ checked: self.trackLabels === 'overlapping',
993
+ onClick: () => self.setTrackLabels('overlapping'),
994
+ },
995
+ {
996
+ label: 'Offset',
997
+ icon: VisibilityIcon,
998
+ type: 'radio',
999
+ checked: self.trackLabels === 'offset',
1000
+ onClick: () => self.setTrackLabels('offset'),
1001
+ },
1002
+ {
1003
+ label: 'Hidden',
1004
+ icon: VisibilityIcon,
1005
+ type: 'radio',
1006
+ checked: self.trackLabels === 'hidden',
1007
+ onClick: () => self.setTrackLabels('hidden'),
1008
+ },
1009
+ ],
1010
+ },
1011
+ ...(canShowCytobands
1012
+ ? [
1013
+ {
1014
+ label: showCytobands ? 'Hide ideogram' : 'Show ideograms',
1015
+ onClick: () => {
1016
+ self.setShowCytobands(!showCytobands);
1017
+ },
1018
+ },
1019
+ ]
1020
+ : []),
1021
+ ];
1022
+ // add track's view level menu options
1023
+ for (const [key, value] of self.trackTypeActions.entries()) {
1024
+ if (value.length) {
1025
+ menuItems.push({ type: 'divider' }, { type: 'subHeader', label: key });
1026
+ value.forEach(action => {
1027
+ menuItems.push(action);
1028
+ });
1029
+ }
1030
+ }
1031
+ return menuItems;
1032
+ },
1033
+ }))
1034
+ .views(self => {
1035
+ let currentlyCalculatedStaticBlocks;
1036
+ let stringifiedCurrentlyCalculatedStaticBlocks = '';
1037
+ return {
1038
+ get staticBlocks() {
1039
+ const ret = calculateStaticBlocks(self);
1040
+ const sret = JSON.stringify(ret);
1041
+ if (stringifiedCurrentlyCalculatedStaticBlocks !== sret) {
1042
+ currentlyCalculatedStaticBlocks = ret;
1043
+ stringifiedCurrentlyCalculatedStaticBlocks = sret;
1044
+ }
1045
+ return currentlyCalculatedStaticBlocks;
1046
+ },
1047
+ get dynamicBlocks() {
1048
+ return calculateDynamicBlocks(self);
1049
+ },
1050
+ get roundedDynamicBlocks() {
1051
+ return this.dynamicBlocks.contentBlocks.map(block => {
1052
+ return {
1053
+ ...block,
1054
+ start: Math.floor(block.start),
1055
+ end: Math.ceil(block.end),
1056
+ };
1057
+ });
1058
+ },
1059
+ get visibleLocStrings() {
1060
+ return calculateVisibleLocStrings(this.dynamicBlocks.contentBlocks);
1061
+ },
1062
+ get coarseVisibleLocStrings() {
1063
+ return calculateVisibleLocStrings(self.coarseDynamicBlocks);
1064
+ },
1065
+ };
1066
+ })
1067
+ .actions(self => ({
1068
+ // this "clears the view" and makes the view return to the import form
1069
+ clearView() {
1070
+ self.setDisplayedRegions([]);
1071
+ self.tracks.clear();
1072
+ // it is necessary to run these after setting displayed regions empty
1073
+ // or else model.offsetPx gets set to Infinity and breaks
1074
+ // mobx-state-tree snapshot
1075
+ self.scrollTo(0);
1076
+ self.zoomTo(10);
1077
+ },
1078
+ setCoarseDynamicBlocks(blocks) {
1079
+ self.coarseDynamicBlocks = blocks.contentBlocks;
1080
+ self.coarseTotalBp = blocks.totalBp;
1081
+ },
1082
+ afterAttach() {
1083
+ addDisposer(self, autorun(() => {
1084
+ if (self.initialized) {
1085
+ this.setCoarseDynamicBlocks(self.dynamicBlocks);
1086
+ }
1087
+ }, { delay: 150 }));
1088
+ },
1089
+ }))
1090
+ .actions(self => ({
1091
+ async exportSvg(opts = {}) {
1092
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1093
+ const html = await renderToSvg(self, opts);
1094
+ const blob = new Blob([html], { type: 'image/svg+xml' });
1095
+ saveAs(blob, opts.filename || 'image.svg');
1096
+ },
1097
+ }))
1098
+ .views(self => ({
1099
+ rubberBandMenuItems() {
1100
+ return [
1101
+ {
1102
+ label: 'Zoom to region',
1103
+ icon: ZoomInIcon,
1104
+ onClick: () => {
1105
+ const { leftOffset, rightOffset } = self;
1106
+ if (leftOffset && rightOffset) {
1107
+ self.moveTo(leftOffset, rightOffset);
1108
+ }
1109
+ },
1110
+ },
1111
+ {
1112
+ label: 'Get sequence',
1113
+ icon: MenuOpenIcon,
1114
+ onClick: () => {
1115
+ self.setSequenceDialogOpen(true);
1116
+ },
1117
+ },
1118
+ ];
1119
+ },
1120
+ }));
1121
+ }
1122
+ export { renderToSvg, RefNameAutocomplete, SearchBox };
1123
+ export { default as ReactComponent } from './components/LinearGenomeView';
1124
+ //# sourceMappingURL=index.js.map