@jbrowse/plugin-linear-genome-view 1.7.10 → 2.0.1

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