higlass 1.13.5 → 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 (392) hide show
  1. package/README.md +48 -54
  2. package/app/globals.d.ts +1 -1
  3. package/app/missing-types.d.ts +4 -1
  4. package/app/scripts/AddTrackDialog.jsx +11 -4
  5. package/app/scripts/AddTrackPositionMenu.jsx +28 -7
  6. package/app/scripts/Annotations1dTrack.js +90 -251
  7. package/app/scripts/Annotations2dTrack.js +12 -7
  8. package/app/scripts/Autocomplete.jsx +13 -28
  9. package/app/scripts/AxisPixi.js +6 -10
  10. package/app/scripts/BarTrack.js +3 -3
  11. package/app/scripts/BedLikeTrack.js +556 -449
  12. package/app/scripts/Button.jsx +1 -1
  13. package/app/scripts/CNVIntervalTrack.js +1 -1
  14. package/app/scripts/CenterTrack.jsx +8 -7
  15. package/app/scripts/Chromosome2DAnnotations.js +1 -1
  16. package/app/scripts/Chromosome2DLabels.js +1 -1
  17. package/app/scripts/ChromosomeGrid.js +49 -38
  18. package/app/scripts/ChromosomeInfo.js +1 -1
  19. package/app/scripts/CombinedTrack.js +3 -1
  20. package/app/scripts/ConfigTrackMenu.jsx +1 -1
  21. package/app/scripts/ConfigViewMenu.jsx +2 -2
  22. package/app/scripts/ContextMenuContainer.jsx +4 -2
  23. package/app/scripts/ContextMenuItem.jsx +14 -2
  24. package/app/scripts/CrossRule.js +1 -1
  25. package/app/scripts/CustomTrackDialog.jsx +2 -2
  26. package/app/scripts/Dialog.jsx +2 -2
  27. package/app/scripts/DragListeningDiv.jsx +1 -1
  28. package/app/scripts/DraggableDiv.jsx +2 -3
  29. package/app/scripts/ExportLinkDialog.jsx +1 -1
  30. package/app/scripts/FilledLine.js +349 -0
  31. package/app/scripts/GalleryTracks.jsx +77 -78
  32. package/app/scripts/GenomePositionSearchBox.jsx +184 -482
  33. package/app/scripts/HeatmapOptions.jsx +4 -2
  34. package/app/scripts/HeatmapTiledPixiTrack.js +23 -32
  35. package/app/scripts/HiGlassComponent.jsx +515 -444
  36. package/app/scripts/HiGlassComponentContext.js +5 -0
  37. package/app/scripts/Horizontal1dHeatmapTrack.js +1 -1
  38. package/app/scripts/Horizontal2DDomainsTrack.js +1 -1
  39. package/app/scripts/HorizontalChromosomeLabels.js +28 -22
  40. package/app/scripts/HorizontalGeneAnnotationsTrack.js +1 -1
  41. package/app/scripts/HorizontalHeatmapTrack.js +2 -2
  42. package/app/scripts/HorizontalMultivecTrack.js +6 -7
  43. package/app/scripts/HorizontalRule.js +1 -2
  44. package/app/scripts/HorizontalTiled1DPixiTrack.js +4 -4
  45. package/app/scripts/HorizontalTiledPlot.jsx +9 -9
  46. package/app/scripts/LeftTrackModifier.js +4 -0
  47. package/app/scripts/ListWrapper.jsx +1 -2
  48. package/app/scripts/MapboxTilesTrack.js +1 -2
  49. package/app/scripts/Modal.jsx +2 -2
  50. package/app/scripts/MoveableTrack.jsx +10 -12
  51. package/app/scripts/NestedContextMenu.jsx +2 -1
  52. package/app/scripts/OSMTileIdsTrack.js +1 -1
  53. package/app/scripts/OverlayTrack.js +4 -4
  54. package/app/scripts/PixiTrack.js +27 -13
  55. package/app/scripts/PlotTypeChooser.jsx +3 -4
  56. package/app/scripts/SearchField.js +5 -5
  57. package/app/scripts/SeriesListItems.jsx +3 -4
  58. package/app/scripts/SeriesListMenu.jsx +95 -53
  59. package/app/scripts/SeriesListSubmenuMixin.jsx +2 -1
  60. package/app/scripts/SketchInlinePicker.jsx +2 -2
  61. package/app/scripts/SortableList.jsx +1 -1
  62. package/app/scripts/Tiled1DPixiTrack.js +4 -1
  63. package/app/scripts/TiledPixiTrack.js +244 -102
  64. package/app/scripts/TiledPlot.jsx +565 -118
  65. package/app/scripts/TilesetFinder.jsx +12 -4
  66. package/app/scripts/Track.js +1 -1
  67. package/app/scripts/TrackArea.jsx +4 -0
  68. package/app/scripts/TrackControl.jsx +2 -2
  69. package/app/scripts/TrackRenderer.jsx +32 -33
  70. package/app/scripts/ValueIntervalTrack.js +1 -1
  71. package/app/scripts/VerticalRule.js +2 -2
  72. package/app/scripts/VerticalTiledPlot.jsx +7 -7
  73. package/app/scripts/ViewConfigEditor.jsx +1 -1
  74. package/app/scripts/ViewContextMenu.jsx +53 -5
  75. package/app/scripts/ViewHeader.jsx +9 -9
  76. package/app/scripts/ViewportTracker2D.js +1 -1
  77. package/app/scripts/api.js +92 -12
  78. package/app/scripts/configs/available-track-types.js +1 -1
  79. package/app/scripts/configs/index.js +6 -1
  80. package/app/scripts/configs/positions-by-datatype.js +2 -2
  81. package/app/scripts/configs/primitives.js +2 -0
  82. package/app/scripts/configs/themes.js +0 -1
  83. package/app/scripts/configs/tracks-info-by-type.js +11 -8
  84. package/app/scripts/configs/tracks-info.js +3 -2
  85. package/app/scripts/d3-context-menu.js +3 -4
  86. package/app/scripts/data-fetchers/DataFetcher.js +35 -36
  87. package/app/scripts/data-fetchers/genbank-fetcher.js +13 -22
  88. package/app/scripts/data-fetchers/local-tile-fetcher.js +10 -8
  89. package/app/scripts/hglib.jsx +62 -71
  90. package/app/scripts/hocs/with-modal.jsx +32 -10
  91. package/app/scripts/hocs/with-pub-sub.js +12 -3
  92. package/app/scripts/hocs/with-theme.jsx +21 -14
  93. package/app/scripts/icons.jsx +3 -2
  94. package/app/scripts/mixwith.js +2 -2
  95. package/app/scripts/options-info.js +49 -11
  96. package/app/scripts/plugins/get-data-fetcher.js +2 -3
  97. package/app/scripts/services/chrom-info.js +32 -4
  98. package/app/scripts/services/element-resize-listener.js +2 -2
  99. package/app/scripts/services/index.js +0 -1
  100. package/app/scripts/services/tile-proxy.js +368 -285
  101. package/app/scripts/services/worker.js +31 -28
  102. package/app/scripts/test-helpers/index.js +2 -1
  103. package/app/scripts/test-helpers/test-helpers.jsx +157 -69
  104. package/app/scripts/types.ts +118 -47
  105. package/app/scripts/utils/LruCache.js +3 -2
  106. package/app/scripts/utils/assert.js +19 -0
  107. package/app/scripts/utils/background-task-scheduler.js +2 -0
  108. package/app/scripts/utils/color-domain-to-rgba-array.js +13 -3
  109. package/app/scripts/utils/copy-text-to-clipboard.js +36 -0
  110. package/app/scripts/utils/decompress.js +33 -0
  111. package/app/scripts/utils/default-tracks.js +46 -0
  112. package/app/scripts/utils/dict-items.js +1 -0
  113. package/app/scripts/utils/dict-keys.js +1 -0
  114. package/app/scripts/utils/dict-values.js +1 -0
  115. package/app/scripts/utils/expand-combined-tracks.js +11 -7
  116. package/app/scripts/utils/fill-in-min-widths.js +47 -21
  117. package/app/scripts/utils/flatten.js +0 -1
  118. package/app/scripts/utils/get-aggregation-function.js +1 -1
  119. package/app/scripts/utils/get-default-track-for-datatype.js +37 -10
  120. package/app/scripts/utils/get-default-tracks-for-datatype.ts +46 -0
  121. package/app/scripts/utils/get-higlass-components.js +27 -3
  122. package/app/scripts/utils/get-track-position-by-uid.js +8 -1
  123. package/app/scripts/utils/get-xylofon.js +12 -9
  124. package/app/scripts/utils/has-parent.js +5 -5
  125. package/app/scripts/utils/hex-string-to-int.js +1 -1
  126. package/app/scripts/utils/index.js +1 -0
  127. package/app/scripts/utils/interval-tree.js +222 -177
  128. package/app/scripts/utils/load-chrom-infos.js +4 -1
  129. package/app/scripts/utils/pixi-text-to-svg.js +5 -9
  130. package/app/scripts/utils/positioned-tracks-to-all-tracks.js +55 -0
  131. package/app/scripts/utils/range-query-2d.js +3 -3
  132. package/app/scripts/utils/reduce.js +12 -5
  133. package/app/scripts/utils/segments-to-rows.js +14 -11
  134. package/app/scripts/utils/svg-line.js +7 -8
  135. package/app/scripts/utils/type-guards.js +16 -7
  136. package/app/scripts/utils/visit-positioned-tracks.js +9 -4
  137. package/app/styles/AddTrackPositionMenu.module.scss +37 -0
  138. package/app/styles/HiGlass.module.scss +3 -1
  139. package/app/styles/d3-context-menu.css +0 -1
  140. package/app/styles/prism.css +1 -0
  141. package/dist/app/schema.json +525 -0
  142. package/dist/app/scripts/AddTrackDialog.d.ts +64 -0
  143. package/dist/app/scripts/AddTrackPositionMenu.d.ts +5 -0
  144. package/dist/app/scripts/Annotations1dTrack.d.ts +15 -0
  145. package/dist/app/scripts/Annotations2dTrack.d.ts +95 -0
  146. package/dist/app/scripts/ArrowheadDomainsTrack.d.ts +36 -0
  147. package/dist/app/scripts/Autocomplete.d.ts +102 -0
  148. package/dist/app/scripts/AxisPixi.d.ts +25 -0
  149. package/dist/app/scripts/BarTrack.d.ts +28 -0
  150. package/dist/app/scripts/BedLikeTrack.d.ts +84 -0
  151. package/dist/app/scripts/Button.d.ts +3 -0
  152. package/dist/app/scripts/CNVIntervalTrack.d.ts +12 -0
  153. package/dist/app/scripts/CenterTiledPlot.d.ts +3 -0
  154. package/dist/app/scripts/CenterTrack.d.ts +92 -0
  155. package/dist/app/scripts/Chromosome2DAnnotations.d.ts +10 -0
  156. package/dist/app/scripts/Chromosome2DLabels.d.ts +13 -0
  157. package/dist/app/scripts/ChromosomeGrid.d.ts +24 -0
  158. package/dist/app/scripts/ChromosomeInfo.d.ts +14 -0
  159. package/dist/app/scripts/CloseTrackMenu.d.ts +10 -0
  160. package/dist/app/scripts/CombinedTrack.d.ts +32 -0
  161. package/dist/app/scripts/ConfigTrackMenu.d.ts +10 -0
  162. package/dist/app/scripts/ConfigViewMenu.d.ts +34 -0
  163. package/dist/app/scripts/ConfigureSeriesMenu.d.ts +3 -0
  164. package/dist/app/scripts/ContextMenuContainer.d.ts +36 -0
  165. package/dist/app/scripts/ContextMenuItem.d.ts +34 -0
  166. package/dist/app/scripts/Cross.d.ts +3 -0
  167. package/dist/app/scripts/CrossRule.d.ts +24 -0
  168. package/dist/app/scripts/CustomTrackDialog.d.ts +17 -0
  169. package/dist/app/scripts/Dialog.d.ts +5 -0
  170. package/dist/app/scripts/DivergentBarTrack.d.ts +4 -0
  171. package/dist/app/scripts/DragListeningDiv.d.ts +32 -0
  172. package/dist/app/scripts/DraggableDiv.d.ts +63 -0
  173. package/dist/app/scripts/ExportLinkDialog.d.ts +21 -0
  174. package/dist/app/scripts/FilledLine.d.ts +5 -0
  175. package/dist/app/scripts/FixedTrack.d.ts +5 -0
  176. package/dist/app/scripts/GalleryTracks.d.ts +20 -0
  177. package/dist/app/scripts/GenomePositionSearchBox.d.ts +95 -0
  178. package/dist/app/scripts/HeatmapOptions.d.ts +30 -0
  179. package/dist/app/scripts/HeatmapTiledPixiTrack.d.ts +184 -0
  180. package/dist/app/scripts/HiGlassComponent.d.ts +762 -0
  181. package/dist/app/scripts/HiGlassComponentContext.d.ts +3 -0
  182. package/dist/app/scripts/HiGlassTrackComponent.d.ts +37 -0
  183. package/dist/app/scripts/Horizontal1dHeatmapTrack.d.ts +9 -0
  184. package/dist/app/scripts/Horizontal2DDomainsTrack.d.ts +21 -0
  185. package/dist/app/scripts/HorizontalChromosomeLabels.d.ts +47 -0
  186. package/dist/app/scripts/HorizontalGeneAnnotationsTrack.d.ts +25 -0
  187. package/dist/app/scripts/HorizontalHeatmapTrack.d.ts +12 -0
  188. package/dist/app/scripts/HorizontalItem.d.ts +3 -0
  189. package/dist/app/scripts/HorizontalLine1DPixiTrack.d.ts +23 -0
  190. package/dist/app/scripts/HorizontalMultivecTrack.d.ts +50 -0
  191. package/dist/app/scripts/HorizontalPoint1DPixiTrack.d.ts +5 -0
  192. package/dist/app/scripts/HorizontalRule.d.ts +22 -0
  193. package/dist/app/scripts/HorizontalTiled1DPixiTrack.d.ts +26 -0
  194. package/dist/app/scripts/HorizontalTiledPlot.d.ts +49 -0
  195. package/dist/app/scripts/HorizontalTrack.d.ts +6 -0
  196. package/dist/app/scripts/Id2DTiledPixiTrack.d.ts +10 -0
  197. package/dist/app/scripts/IdHorizontal1DTiledPixiTrack.d.ts +6 -0
  198. package/dist/app/scripts/IdVertical1DTiledPixiTrack.d.ts +7 -0
  199. package/dist/app/scripts/LeftAxisTrack.d.ts +9 -0
  200. package/dist/app/scripts/LeftTrackModifier.d.ts +29 -0
  201. package/dist/app/scripts/ListWrapper.d.ts +64 -0
  202. package/dist/app/scripts/MapboxTilesTrack.d.ts +9 -0
  203. package/dist/app/scripts/Modal.d.ts +5 -0
  204. package/dist/app/scripts/MoveableTrack.d.ts +18 -0
  205. package/dist/app/scripts/NestedContextMenu.d.ts +7 -0
  206. package/dist/app/scripts/OSMTileIdsTrack.d.ts +5 -0
  207. package/dist/app/scripts/OSMTilesTrack.d.ts +129 -0
  208. package/dist/app/scripts/OverlayTrack.d.ts +13 -0
  209. package/dist/app/scripts/PixiTrack.d.ts +174 -0
  210. package/dist/app/scripts/PlotTypeChooser.d.ts +25 -0
  211. package/dist/app/scripts/PopupMenu.d.ts +28 -0
  212. package/dist/app/scripts/RasterTilesTrack.d.ts +9 -0
  213. package/dist/app/scripts/RuleMixin.d.ts +2 -0
  214. package/dist/app/scripts/SVGTrack.d.ts +15 -0
  215. package/dist/app/scripts/SearchField.d.ts +13 -0
  216. package/dist/app/scripts/SeriesListItems.d.ts +2 -0
  217. package/dist/app/scripts/SeriesListMenu.d.ts +51 -0
  218. package/dist/app/scripts/SeriesListSubmenuMixin.d.ts +2 -0
  219. package/dist/app/scripts/SketchInlinePicker.d.ts +25 -0
  220. package/dist/app/scripts/SortableList.d.ts +22 -0
  221. package/dist/app/scripts/SquareMarkersTrack.d.ts +22 -0
  222. package/dist/app/scripts/Tiled1DPixiTrack.d.ts +60 -0
  223. package/dist/app/scripts/TiledPixiTrack.d.ts +369 -0
  224. package/dist/app/scripts/TiledPlot.d.ts +313 -0
  225. package/dist/app/scripts/TilesetFinder.d.ts +65 -0
  226. package/dist/app/scripts/TopAxisTrack.d.ts +9 -0
  227. package/dist/app/scripts/Track.d.ts +196 -0
  228. package/dist/app/scripts/TrackArea.d.ts +26 -0
  229. package/dist/app/scripts/TrackControl.d.ts +5 -0
  230. package/dist/app/scripts/TrackRenderer.d.ts +724 -0
  231. package/dist/app/scripts/UnknownPixiTrack.d.ts +7 -0
  232. package/dist/app/scripts/ValueIntervalTrack.d.ts +6 -0
  233. package/dist/app/scripts/VerticalItem.d.ts +3 -0
  234. package/dist/app/scripts/VerticalRule.d.ts +21 -0
  235. package/dist/app/scripts/VerticalTiled1DPixiTrack.d.ts +6 -0
  236. package/dist/app/scripts/VerticalTiledPlot.d.ts +50 -0
  237. package/dist/app/scripts/VerticalTrack.d.ts +6 -0
  238. package/dist/app/scripts/ViewConfigEditor.d.ts +53 -0
  239. package/dist/app/scripts/ViewContextMenu.d.ts +17 -0
  240. package/dist/app/scripts/ViewHeader.d.ts +75 -0
  241. package/dist/app/scripts/ViewportTracker2D.d.ts +17 -0
  242. package/dist/app/scripts/ViewportTracker2DPixi.d.ts +11 -0
  243. package/dist/app/scripts/ViewportTrackerHorizontal.d.ts +17 -0
  244. package/dist/app/scripts/ViewportTrackerVertical.d.ts +17 -0
  245. package/dist/app/scripts/api.d.ts +640 -0
  246. package/dist/app/scripts/configs/available-track-types.d.ts +2 -0
  247. package/dist/app/scripts/configs/colormaps.d.ts +2 -0
  248. package/dist/app/scripts/configs/datatype-to-track-type.d.ts +4 -0
  249. package/dist/app/scripts/configs/default-tracks-for-datatype.d.ts +38 -0
  250. package/dist/app/scripts/configs/dense-data-extrema-config.d.ts +2 -0
  251. package/dist/app/scripts/configs/globals.d.ts +5 -0
  252. package/dist/app/scripts/configs/index.d.ts +16 -0
  253. package/dist/app/scripts/configs/positions-by-datatype.d.ts +2 -0
  254. package/dist/app/scripts/configs/primitives.d.ts +20 -0
  255. package/dist/app/scripts/configs/themes.d.ts +3 -0
  256. package/dist/app/scripts/configs/tracks-info-by-type.d.ts +4 -0
  257. package/dist/app/scripts/configs/tracks-info.d.ts +24 -0
  258. package/dist/app/scripts/d3-context-menu.d.ts +2 -0
  259. package/dist/app/scripts/data-fetchers/DataFetcher.d.ts +151 -0
  260. package/dist/app/scripts/data-fetchers/genbank-fetcher.d.ts +86 -0
  261. package/dist/app/scripts/data-fetchers/index.d.ts +3 -0
  262. package/dist/app/scripts/data-fetchers/local-tile-fetcher.d.ts +47 -0
  263. package/dist/app/scripts/gosling-exports.d.ts +17 -0
  264. package/dist/app/scripts/hglib.d.ts +24 -0
  265. package/dist/app/scripts/hocs/with-modal.d.ts +19 -0
  266. package/dist/app/scripts/hocs/with-pub-sub.d.ts +22 -0
  267. package/dist/app/scripts/hocs/with-theme.d.ts +13 -0
  268. package/dist/app/scripts/icons.d.ts +161 -0
  269. package/dist/app/scripts/mixwith.d.ts +27 -0
  270. package/dist/app/scripts/options-info.d.ts +1355 -0
  271. package/dist/app/scripts/plugins/available-for-plugins.d.ts +2338 -0
  272. package/dist/app/scripts/plugins/get-data-fetcher.d.ts +2 -0
  273. package/dist/app/scripts/plugins/index.d.ts +2 -0
  274. package/dist/app/scripts/services/chrom-info.d.ts +10 -0
  275. package/dist/app/scripts/services/dom-event.d.ts +7 -0
  276. package/dist/app/scripts/services/element-resize-listener.d.ts +5 -0
  277. package/dist/app/scripts/services/index.d.ts +5 -0
  278. package/dist/app/scripts/services/tile-proxy.d.ts +180 -0
  279. package/dist/app/scripts/services/worker.d.ts +157 -0
  280. package/dist/app/scripts/symbol.d.ts +13 -0
  281. package/dist/app/scripts/test-helpers/index.d.ts +1 -0
  282. package/dist/app/scripts/test-helpers/test-helpers.d.ts +33 -0
  283. package/dist/app/scripts/track-utils.d.ts +73 -0
  284. package/dist/app/scripts/types.d.ts +199 -0
  285. package/dist/app/scripts/utils/DenseDataExtrema1D.d.ts +88 -0
  286. package/dist/app/scripts/utils/DenseDataExtrema2D.d.ts +97 -0
  287. package/dist/app/scripts/utils/LruCache.d.ts +44 -0
  288. package/dist/app/scripts/utils/abs-to-chr.d.ts +14 -0
  289. package/dist/app/scripts/utils/accessor-transposition.d.ts +14 -0
  290. package/dist/app/scripts/utils/add-arrays.d.ts +18 -0
  291. package/dist/app/scripts/utils/add-class.d.ts +8 -0
  292. package/dist/app/scripts/utils/add-event-listener-once.d.ts +11 -0
  293. package/dist/app/scripts/utils/assert.d.ts +17 -0
  294. package/dist/app/scripts/utils/background-task-scheduler.d.ts +47 -0
  295. package/dist/app/scripts/utils/base64-to-canvas.d.ts +9 -0
  296. package/dist/app/scripts/utils/chr-to-abs.d.ts +10 -0
  297. package/dist/app/scripts/utils/chrom-info-bisector.d.ts +4 -0
  298. package/dist/app/scripts/utils/clone-event.d.ts +12 -0
  299. package/dist/app/scripts/utils/color-domain-to-rgba-array.d.ts +13 -0
  300. package/dist/app/scripts/utils/color-to-hex.d.ts +9 -0
  301. package/dist/app/scripts/utils/color-to-rgba.d.ts +9 -0
  302. package/dist/app/scripts/utils/copy-text-to-clipboard.d.ts +2 -0
  303. package/dist/app/scripts/utils/data-to-genomic-loci.d.ts +11 -0
  304. package/dist/app/scripts/utils/debounce.d.ts +5 -0
  305. package/dist/app/scripts/utils/dec-to-hex-str.d.ts +8 -0
  306. package/dist/app/scripts/utils/decompress.d.ts +27 -0
  307. package/dist/app/scripts/utils/default-tracks.d.ts +3 -0
  308. package/dist/app/scripts/utils/dict-from-tuples.d.ts +11 -0
  309. package/dist/app/scripts/utils/dict-items.d.ts +18 -0
  310. package/dist/app/scripts/utils/dict-keys.d.ts +10 -0
  311. package/dist/app/scripts/utils/dict-values.d.ts +8 -0
  312. package/dist/app/scripts/utils/download.d.ts +7 -0
  313. package/dist/app/scripts/utils/expand-combined-tracks.d.ts +11 -0
  314. package/dist/app/scripts/utils/fake-pub-sub.d.ts +11 -0
  315. package/dist/app/scripts/utils/fill-in-min-widths.d.ts +44 -0
  316. package/dist/app/scripts/utils/flatten.d.ts +9 -0
  317. package/dist/app/scripts/utils/for-each.d.ts +9 -0
  318. package/dist/app/scripts/utils/forward-event.d.ts +7 -0
  319. package/dist/app/scripts/utils/genome-loci-to-pixels.d.ts +9 -0
  320. package/dist/app/scripts/utils/genomic-range-to-chromosome-chunks.d.ts +21 -0
  321. package/dist/app/scripts/utils/get-aggregation-function.d.ts +10 -0
  322. package/dist/app/scripts/utils/get-default-track-for-datatype.d.ts +21 -0
  323. package/dist/app/scripts/utils/get-default-tracks-for-datatype.d.ts +3 -0
  324. package/dist/app/scripts/utils/get-element-dim.d.ts +7 -0
  325. package/dist/app/scripts/utils/get-higlass-components.d.ts +7 -0
  326. package/dist/app/scripts/utils/get-track-by-uid.d.ts +7 -0
  327. package/dist/app/scripts/utils/get-track-conf-from-hgc.d.ts +10 -0
  328. package/dist/app/scripts/utils/get-track-obj-by-id.d.ts +2 -0
  329. package/dist/app/scripts/utils/get-track-position-by-uid.d.ts +13 -0
  330. package/dist/app/scripts/utils/get-xylofon.d.ts +2 -0
  331. package/dist/app/scripts/utils/gradient.d.ts +14 -0
  332. package/dist/app/scripts/utils/has-class.d.ts +8 -0
  333. package/dist/app/scripts/utils/has-parent.d.ts +9 -0
  334. package/dist/app/scripts/utils/hex-string-to-int.d.ts +14 -0
  335. package/dist/app/scripts/utils/index.d.ts +89 -0
  336. package/dist/app/scripts/utils/interval-tree.d.ts +109 -0
  337. package/dist/app/scripts/utils/into-the-void.d.ts +6 -0
  338. package/dist/app/scripts/utils/is-track-or-child-track.d.ts +7 -0
  339. package/dist/app/scripts/utils/is-track-range-selectable.d.ts +2 -0
  340. package/dist/app/scripts/utils/is-within.d.ts +12 -0
  341. package/dist/app/scripts/utils/lat-to-y.d.ts +9 -0
  342. package/dist/app/scripts/utils/lng-to-x.d.ts +8 -0
  343. package/dist/app/scripts/utils/load-chrom-infos.d.ts +8 -0
  344. package/dist/app/scripts/utils/map.d.ts +13 -0
  345. package/dist/app/scripts/utils/max-non-zero.d.ts +6 -0
  346. package/dist/app/scripts/utils/max.d.ts +10 -0
  347. package/dist/app/scripts/utils/min-non-zero.d.ts +6 -0
  348. package/dist/app/scripts/utils/min.d.ts +10 -0
  349. package/dist/app/scripts/utils/mod.d.ts +9 -0
  350. package/dist/app/scripts/utils/ndarray-assign.d.ts +2 -0
  351. package/dist/app/scripts/utils/ndarray-flatten.d.ts +2 -0
  352. package/dist/app/scripts/utils/ndarray-to-list.d.ts +2 -0
  353. package/dist/app/scripts/utils/numericify-version.d.ts +6 -0
  354. package/dist/app/scripts/utils/obj-vals.d.ts +8 -0
  355. package/dist/app/scripts/utils/or.d.ts +8 -0
  356. package/dist/app/scripts/utils/parse-chromsizes-rows.d.ts +34 -0
  357. package/dist/app/scripts/utils/pixi-text-to-svg.d.ts +2 -0
  358. package/dist/app/scripts/utils/positioned-tracks-to-all-tracks.d.ts +26 -0
  359. package/dist/app/scripts/utils/q.d.ts +18 -0
  360. package/dist/app/scripts/utils/rad-to-deg.d.ts +7 -0
  361. package/dist/app/scripts/utils/range-query-2d.d.ts +17 -0
  362. package/dist/app/scripts/utils/reduce.d.ts +14 -0
  363. package/dist/app/scripts/utils/rel-to-abs-chrom-pos.d.ts +10 -0
  364. package/dist/app/scripts/utils/remove-class.d.ts +7 -0
  365. package/dist/app/scripts/utils/reset-d3-brush-style.d.ts +10 -0
  366. package/dist/app/scripts/utils/rgb-to-hex.d.ts +8 -0
  367. package/dist/app/scripts/utils/scales-center-and-k.d.ts +12 -0
  368. package/dist/app/scripts/utils/scales-to-genome-loci.d.ts +3 -0
  369. package/dist/app/scripts/utils/segments-to-rows.d.ts +15 -0
  370. package/dist/app/scripts/utils/selected-items-to-cum-weights.d.ts +12 -0
  371. package/dist/app/scripts/utils/selected-items-to-size.d.ts +13 -0
  372. package/dist/app/scripts/utils/show-mouse-position.d.ts +54 -0
  373. package/dist/app/scripts/utils/some.d.ts +10 -0
  374. package/dist/app/scripts/utils/sum.d.ts +8 -0
  375. package/dist/app/scripts/utils/svg-line.d.ts +2 -0
  376. package/dist/app/scripts/utils/throttle-and-debounce.d.ts +33 -0
  377. package/dist/app/scripts/utils/tile-to-canvas.d.ts +9 -0
  378. package/dist/app/scripts/utils/timeout.d.ts +3 -0
  379. package/dist/app/scripts/utils/to-void.d.ts +3 -0
  380. package/dist/app/scripts/utils/total-track-pixel-height.d.ts +27 -0
  381. package/dist/app/scripts/utils/trim-trailing-slash.d.ts +7 -0
  382. package/dist/app/scripts/utils/type-guards.d.ts +36 -0
  383. package/dist/app/scripts/utils/value-to-color.d.ts +12 -0
  384. package/dist/app/scripts/utils/visit-positioned-tracks.d.ts +18 -0
  385. package/dist/app/scripts/utils/visit-tracks.d.ts +9 -0
  386. package/dist/esm.html +1 -3
  387. package/dist/hglib.js +86315 -100592
  388. package/dist/hglib.min.js +123 -131
  389. package/dist/higlass.mjs +88861 -103138
  390. package/dist/index.html +1 -3
  391. package/dist/package.json +134 -0
  392. package/package.json +25 -27
@@ -1,41 +1,42 @@
1
1
  // @ts-nocheck
2
- import React from 'react';
3
- import PropTypes from 'prop-types';
4
- import { select, pointer } from 'd3-selection';
2
+ import clsx from 'clsx';
3
+ import { ElementQueries, ResizeSensor } from 'css-element-queries';
5
4
  import { scaleLinear } from 'd3-scale';
6
- import slugid from 'slugid';
5
+ import { pointer, select } from 'd3-selection';
7
6
  import * as PIXI from 'pixi.js';
8
- import ReactDOM from 'react-dom';
7
+ import PropTypes from 'prop-types';
8
+ import createPubSub, { globalPubSub } from 'pub-sub-es';
9
+ import React from 'react';
9
10
  import ReactGridLayout from 'react-grid-layout';
10
- import { ResizeSensor, ElementQueries } from 'css-element-queries';
11
- import vkbeautify from 'vkbeautify';
11
+ import slugid from 'slugid';
12
12
  import parse from 'url-parse';
13
- import createPubSub, { globalPubSub } from 'pub-sub-es';
14
- import clsx from 'clsx';
13
+ import vkbeautify from 'vkbeautify';
15
14
 
16
- import TiledPlot from './TiledPlot';
17
- import GenomePositionSearchBox from './GenomePositionSearchBox';
18
- import ExportLinkDialog from './ExportLinkDialog';
19
- import ViewHeader from './ViewHeader';
20
15
  import ChromosomeInfo from './ChromosomeInfo';
16
+ import ExportLinkDialog from './ExportLinkDialog';
17
+ import GenomePositionSearchBox from './GenomePositionSearchBox';
18
+ import TiledPlot from './TiledPlot';
21
19
  import ViewConfigEditor from './ViewConfigEditor';
20
+ import ViewHeader from './ViewHeader';
22
21
 
23
- import createSymbolIcon from './symbol';
24
- import { all as icons } from './icons';
25
22
  import createApi from './api';
23
+ import { all as icons } from './icons';
24
+ import createSymbolIcon from './symbol';
26
25
 
26
+ import { Provider as ModalProvider } from './hocs/with-modal';
27
27
  // Higher-order components
28
28
  import { Provider as PubSubProvider } from './hocs/with-pub-sub';
29
- import { Provider as ModalProvider } from './hocs/with-modal';
30
29
  import { Provider as ThemeProvider } from './hocs/with-theme';
31
30
 
31
+ import HiGlassComponentContext from './HiGlassComponentContext';
32
+
32
33
  // Services
33
34
  import {
34
35
  chromInfo,
35
36
  createDomEvent,
37
+ requestsInFlight,
36
38
  setTileProxyAuthHeader,
37
39
  tileProxy,
38
- requestsInFlight,
39
40
  } from './services';
40
41
 
41
42
  // Utils
@@ -61,38 +62,40 @@ import {
61
62
  toVoid,
62
63
  visitPositionedTracks,
63
64
  } from './utils';
65
+ import positionedTracksToAllTracks from './utils/positioned-tracks-to-all-tracks';
64
66
 
65
67
  // Configs
66
68
  import {
67
- DEFAULT_SERVER,
68
69
  DEFAULT_CONTAINER_PADDING_X,
69
70
  DEFAULT_CONTAINER_PADDING_Y,
71
+ DEFAULT_SERVER,
70
72
  DEFAULT_VIEW_MARGIN,
71
73
  DEFAULT_VIEW_PADDING,
72
74
  GLOBALS,
73
- MOUSE_TOOL_MOVE,
74
- MOUSE_TOOL_SELECT,
75
75
  LOCATION_LISTENER_PREFIX,
76
76
  LONG_DRAG_TIMEOUT,
77
+ MOUSE_TOOL_MOVE,
78
+ MOUSE_TOOL_SELECT,
79
+ MOUSE_TOOL_TRACK_SELECT,
77
80
  SHORT_DRAG_TIMEOUT,
81
+ SIZE_MODE_BOUNDED,
82
+ SIZE_MODE_BOUNDED_OVERFLOW,
83
+ SIZE_MODE_DEFAULT,
84
+ SIZE_MODE_OVERFLOW,
85
+ SIZE_MODE_SCROLL,
78
86
  THEME_DARK,
79
87
  THEME_LIGHT,
80
88
  TRACKS_INFO_BY_TYPE,
81
89
  } from './configs';
82
90
 
83
91
  // Styles
84
- import styles from '../styles/HiGlass.module.scss'; // eslint-disable-line no-unused-vars
85
- import stylesMTHeader from '../styles/ViewHeader.module.scss'; // eslint-disable-line no-unused-vars
86
-
87
- import '../styles/HiGlass.scss'; // eslint-disable-line no-unused-vars
92
+ import styles from '../styles/HiGlass.module.scss';
93
+ import stylesMTHeader from '../styles/ViewHeader.module.scss';
94
+ import '../styles/HiGlass.scss';
88
95
 
89
96
  const NUM_GRID_COLUMNS = 12;
90
97
  const DEFAULT_NEW_VIEW_HEIGHT = 12;
91
98
  const VIEW_HEADER_HEIGHT = 20;
92
- const SIZE_MODE_DEFAULT = 'default';
93
- const SIZE_MODE_BOUNDED = 'bounded';
94
- const SIZE_MODE_OVERFLOW = 'overflow';
95
- const SIZE_MODE_SCROLL = 'scroll';
96
99
 
97
100
  class HiGlassComponent extends React.Component {
98
101
  constructor(props) {
@@ -106,6 +109,8 @@ class HiGlassComponent extends React.Component {
106
109
  );
107
110
  }
108
111
 
112
+ this.topDivRef = React.createRef();
113
+
109
114
  this.pubSub = createPubSub();
110
115
  this.domEvent = createDomEvent(this.pubSub);
111
116
 
@@ -116,11 +121,14 @@ class HiGlassComponent extends React.Component {
116
121
  this.resizeSensor = null;
117
122
 
118
123
  this.uid = slugid.nice();
124
+ /** @type {Record<string, import('./TiledPlot').TiledPlot> */
119
125
  this.tiledPlots = {};
120
126
  this.genomePositionSearchBoxes = {};
121
127
 
122
128
  // keep track of the xScales of each Track Renderer
129
+ /** @type {Record<string, import('./TrackRenderer').Scale>} */
123
130
  this.xScales = {};
131
+ /** @type {Record<string, import('./TrackRenderer').Scale>} */
124
132
  this.yScales = {};
125
133
  this.projectionXDomains = {};
126
134
  this.projectionYDomains = {};
@@ -144,7 +152,6 @@ class HiGlassComponent extends React.Component {
144
152
  // between views
145
153
  this.zoomLocks = {};
146
154
  this.locationLocks = {};
147
-
148
155
  // axis-specific location lock
149
156
  this.locationLocksAxisWise = { x: {}, y: {} };
150
157
 
@@ -160,7 +167,7 @@ class HiGlassComponent extends React.Component {
160
167
 
161
168
  // allow a different PIXI to be passed in case the
162
169
  // caller wants to use a different version
163
- GLOBALS.PIXI = (props.options && props.options.PIXI) || PIXI;
170
+ GLOBALS.PIXI = props.options?.PIXI || PIXI;
164
171
 
165
172
  this.viewMarginTop =
166
173
  +props.options.viewMarginTop >= 0
@@ -210,7 +217,9 @@ class HiGlassComponent extends React.Component {
210
217
  this.viewconfLoaded = false;
211
218
 
212
219
  const { viewConfig } = this.props;
213
- const views = this.loadIfRemoteViewConfig(this.props.viewConfig);
220
+ const views = this.processViewConfig(
221
+ JSON.parse(JSON.stringify(this.props.viewConfig)),
222
+ );
214
223
 
215
224
  if (props.options.authToken) {
216
225
  setTileProxyAuthHeader(props.options.authToken);
@@ -237,6 +246,9 @@ class HiGlassComponent extends React.Component {
237
246
  case MOUSE_TOOL_SELECT:
238
247
  mouseTool = MOUSE_TOOL_SELECT;
239
248
  break;
249
+ case MOUSE_TOOL_TRACK_SELECT:
250
+ mouseTool = MOUSE_TOOL_TRACK_SELECT;
251
+ break;
240
252
  default:
241
253
  break;
242
254
  }
@@ -291,10 +303,12 @@ class HiGlassComponent extends React.Component {
291
303
  typedEditable: undefined,
292
304
  mouseOverOverlayUid: null,
293
305
  mouseTool,
306
+ overTrackChooser: null,
294
307
  isDarkTheme: false,
295
- rangeSelection1dSize: [0, Infinity],
308
+ rangeSelection1dSize: [0, Number.POSITIVE_INFINITY],
296
309
  rangeSelectionToInt: false,
297
310
  modal: null,
311
+ sizeMode: this.props.options?.sizeMode,
298
312
  };
299
313
 
300
314
  // monitor whether this element is attached to the DOM so that
@@ -350,6 +364,7 @@ class HiGlassComponent extends React.Component {
350
364
  this.closeModalBound = this.closeModal.bind(this);
351
365
  this.handleEditViewConfigBound = this.handleEditViewConfig.bind(this);
352
366
  this.onScrollHandlerBound = this.onScrollHandler.bind(this);
367
+ this.viewUidToNameBound = this.viewUidToName.bind(this);
353
368
 
354
369
  // for typed shortcuts (e.g. e-d-i-t) to toggle editable
355
370
  this.typedText = '';
@@ -370,7 +385,6 @@ class HiGlassComponent extends React.Component {
370
385
  );
371
386
  }
372
387
 
373
- // eslint-disable-next-line camelcase
374
388
  UNSAFE_componentWillMount() {
375
389
  this.domEvent.register('keydown', document);
376
390
  this.domEvent.register('keyup', document);
@@ -422,12 +436,11 @@ class HiGlassComponent extends React.Component {
422
436
  }
423
437
 
424
438
  get sizeMode() {
425
- // eslint-disable-next-line no-nested-ternary
426
- return typeof this.props.options.sizeMode === 'undefined'
439
+ return typeof this.state.sizeMode === 'undefined'
427
440
  ? this.props.options.bounded
428
441
  ? 'bounded'
429
442
  : SIZE_MODE_DEFAULT
430
- : this.props.options.sizeMode;
443
+ : this.state.sizeMode;
431
444
  }
432
445
 
433
446
  setBroadcastMousePositionGlobally(isBroadcastMousePositionGlobally = false) {
@@ -476,9 +489,9 @@ class HiGlassComponent extends React.Component {
476
489
  waitForDOMAttachment(callback) {
477
490
  if (!this.mounted) return;
478
491
 
479
- const thisElement = ReactDOM.findDOMNode(this);
492
+ const thisElement = this.topDivRef.current;
480
493
 
481
- if (document.body.contains(thisElement)) {
494
+ if (thisElement && document.body.contains(thisElement)) {
482
495
  callback();
483
496
  } else {
484
497
  requestAnimationFrame(() => this.waitForDOMAttachment(callback));
@@ -491,7 +504,8 @@ class HiGlassComponent extends React.Component {
491
504
  // in focus, everything is drawn at the top and overlaps. When it gains
492
505
  // focus we need to redraw everything in its proper place
493
506
  this.mounted = true;
494
- this.element = ReactDOM.findDOMNode(this);
507
+ this.element = this.topDivRef.current;
508
+
495
509
  window.addEventListener('focus', this.boundRefreshView);
496
510
 
497
511
  Object.values(this.state.views).forEach((view) => {
@@ -514,30 +528,27 @@ class HiGlassComponent extends React.Component {
514
528
  autoResize: true,
515
529
  };
516
530
 
517
- const versionNumber = parseInt(PIXI.VERSION[0], 10);
518
-
519
- if (versionNumber === 4) {
520
- console.warn(
521
- 'Deprecation warning: please update Pixi.js to version 5 or above!',
522
- );
523
- if (this.props.options.renderer === 'canvas') {
524
- this.pixiRenderer = new GLOBALS.PIXI.CanvasRenderer(rendererOptions);
525
- } else {
526
- this.pixiRenderer = new GLOBALS.PIXI.WebGLRenderer(rendererOptions);
527
- }
528
- } else {
529
- if (versionNumber < 4) {
531
+ switch (PIXI.VERSION[0]) {
532
+ case '4':
530
533
  console.warn(
531
- 'Deprecation warning: please update Pixi.js to version 5 or above! ' +
532
- 'This version of Pixi.js is unsupported. Good luck 🤞',
534
+ 'Deprecation warning: please update Pixi.js to version 5!',
533
535
  );
534
- }
535
-
536
- if (this.props.options.renderer === 'canvas') {
537
- this.pixiRenderer = new GLOBALS.PIXI.CanvasRenderer(rendererOptions);
538
- } else {
539
- this.pixiRenderer = new GLOBALS.PIXI.Renderer(rendererOptions);
540
- }
536
+ if (this.props.options.renderer === 'canvas') {
537
+ this.pixiRenderer = new GLOBALS.PIXI.CanvasRenderer(rendererOptions);
538
+ } else {
539
+ this.pixiRenderer = new GLOBALS.PIXI.WebGLRenderer(rendererOptions);
540
+ }
541
+ break;
542
+ // case '5':
543
+ // case '6':
544
+ // case '7': // Gosling uses PIXI.js v7
545
+ default:
546
+ if (this.props.options.renderer === 'canvas') {
547
+ this.pixiRenderer = new GLOBALS.PIXI.CanvasRenderer(rendererOptions);
548
+ } else {
549
+ this.pixiRenderer = new GLOBALS.PIXI.Renderer(rendererOptions);
550
+ }
551
+ break;
541
552
  }
542
553
 
543
554
  // PIXI.RESOLUTION=2;
@@ -545,7 +556,6 @@ class HiGlassComponent extends React.Component {
545
556
 
546
557
  // keep track of the width and height of this element, because it
547
558
  // needs to be reflected in the size of our drawing surface
548
- // eslint-disable-next-line react/no-did-mount-set-state
549
559
  this.setState({
550
560
  svgElement: this.svgElement,
551
561
  canvasElement: this.canvasElement,
@@ -585,61 +595,19 @@ class HiGlassComponent extends React.Component {
585
595
  return this.tiledPlots[viewUid].trackRenderer;
586
596
  }
587
597
 
588
- /**
589
- * Check if the passed in viewConfig is remote (i.e. is a string).
590
- * If it is, fetch it before proceeding
591
- */
592
- loadIfRemoteViewConfig(viewConfig) {
593
- let views = {};
594
- if (typeof viewConfig === 'string') {
595
- // Load external viewConfig
596
- tileProxy.json(
597
- viewConfig,
598
- (error, remoteViewConfig) => {
599
- viewConfig = remoteViewConfig;
600
- this.setState({
601
- views: this.processViewConfig(
602
- JSON.parse(JSON.stringify(remoteViewConfig)),
603
- ),
604
- viewConfig: remoteViewConfig,
605
- });
606
- this.unsetOnLocationChange.forEach(
607
- ({ viewId, callback, callbackId }) => {
608
- this.onLocationChange(viewId, callback, callbackId);
609
- },
610
- );
611
- },
612
- this.pubSub,
613
- );
614
- } else {
615
- views = this.processViewConfig(JSON.parse(JSON.stringify(viewConfig)));
616
- if (this.mounted) {
617
- this.setState({
618
- viewConfig,
619
- });
620
- }
621
- }
622
-
623
- return views;
624
- }
625
-
626
- // eslint-disable-next-line camelcase
627
598
  UNSAFE_componentWillReceiveProps(newProps) {
628
- const viewsByUid = this.loadIfRemoteViewConfig(newProps.viewConfig);
599
+ if (this.mounted) {
600
+ this.setState({ viewConfig: newProps.viewConfig });
601
+ }
602
+ const viewsByUid = this.processViewConfig(
603
+ JSON.parse(JSON.stringify(newProps.viewConfig)),
604
+ );
629
605
 
630
606
  if (newProps.options.authToken !== this.prevAuthToken) {
631
607
  // we go a new auth token so we should reload everything
632
608
  setTileProxyAuthHeader(newProps.options.authToken);
633
609
 
634
- for (const viewId of this.iterateOverViews()) {
635
- const trackRenderer = this.getTrackRenderer(viewId);
636
- const trackDefinitions = JSON.parse(trackRenderer.prevTrackDefinitions);
637
-
638
- // this will remove all the tracks and then recreate them
639
- // re-requesting all tiles with the new auth key
640
- trackRenderer.syncTrackObjects([]);
641
- trackRenderer.syncTrackObjects(trackDefinitions);
642
- }
610
+ this.reload();
643
611
 
644
612
  this.prevAuthToken = newProps.options.authToken;
645
613
  }
@@ -655,7 +623,6 @@ class HiGlassComponent extends React.Component {
655
623
  });
656
624
  }
657
625
 
658
- // eslint-disable-next-line camelcase
659
626
  UNSAFE_componentWillUpdate() {
660
627
  // let width = this.element.clientWidth;
661
628
  // let height = this.element.clientHeight;
@@ -663,6 +630,20 @@ class HiGlassComponent extends React.Component {
663
630
  this.pixiRenderer.render(this.pixiRoot);
664
631
  }
665
632
 
633
+ reload() {
634
+ for (const viewId of this.iterateOverViews()) {
635
+ const trackRenderer = this.getTrackRenderer(viewId);
636
+ if (!trackRenderer) continue;
637
+
638
+ const trackDefinitions = JSON.parse(trackRenderer.prevTrackDefinitions);
639
+
640
+ // this will remove all the tracks and then recreate them
641
+ // re-requesting all tiles with the new auth key
642
+ trackRenderer.syncTrackObjects([]);
643
+ trackRenderer.syncTrackObjects(trackDefinitions);
644
+ }
645
+ }
646
+
666
647
  componentDidUpdate() {
667
648
  this.setTheme(this.props.options.theme, this.props.options.isDarkTheme);
668
649
 
@@ -765,13 +746,15 @@ class HiGlassComponent extends React.Component {
765
746
  }
766
747
 
767
748
  fitPixiToParentContainer() {
768
- if (!this.element || !this.element.parentNode) {
769
- console.warn('No parentNode:', this.element);
749
+ const element = this.topDivRef.current;
750
+
751
+ if (!element || !element.parentNode) {
752
+ // console.warn('No parentNode:', element);
770
753
  return;
771
754
  }
772
755
 
773
- const width = this.element.parentNode.clientWidth;
774
- const height = this.element.parentNode.clientHeight;
756
+ const width = element.parentNode.clientWidth;
757
+ const height = element.parentNode.clientHeight;
775
758
 
776
759
  this.pixiMask.beginFill(0xffffff).drawRect(0, 0, width, height).endFill();
777
760
 
@@ -804,10 +787,7 @@ class HiGlassComponent extends React.Component {
804
787
  const trackOptions = track.options ? track.options : {};
805
788
 
806
789
  if (this.props.options.defaultTrackOptions) {
807
- if (
808
- this.props.options.defaultTrackOptions.trackSpecific &&
809
- this.props.options.defaultTrackOptions.trackSpecific[track.type]
810
- ) {
790
+ if (this.props.options.defaultTrackOptions.trackSpecific?.[track.type]) {
811
791
  // track specific options take precedence over all options
812
792
 
813
793
  const options =
@@ -834,11 +814,9 @@ class HiGlassComponent extends React.Component {
834
814
  }
835
815
 
836
816
  if (trackInfo.defaultOptions) {
837
- const defaultThemeOptions =
838
- trackInfo.defaultOptionsByTheme &&
839
- trackInfo.defaultOptionsByTheme[this.theme]
840
- ? trackInfo.defaultOptionsByTheme[this.theme]
841
- : {};
817
+ const defaultThemeOptions = trackInfo.defaultOptionsByTheme?.[this.theme]
818
+ ? trackInfo.defaultOptionsByTheme[this.theme]
819
+ : {};
842
820
 
843
821
  const defaultOptions = {
844
822
  ...trackInfo.defaultOptions,
@@ -1106,7 +1084,7 @@ class HiGlassComponent extends React.Component {
1106
1084
  iterateOverViews() {
1107
1085
  const viewIds = [];
1108
1086
 
1109
- for (const viewId in Object.keys(this.state.views)) {
1087
+ for (const viewId of Object.keys(this.state.views)) {
1110
1088
  viewIds.push(viewId);
1111
1089
  }
1112
1090
 
@@ -1136,6 +1114,7 @@ class HiGlassComponent extends React.Component {
1136
1114
  * Iterate over all the tracks in this component.
1137
1115
  */
1138
1116
  iterateOverTracks() {
1117
+ /** @type {Array<{ viewId: string, trackId: string, track: import('./types').UnknownTrackConfig }>}*/
1139
1118
  const allTracks = [];
1140
1119
  for (const viewId in this.state.views) {
1141
1120
  const { tracks } = this.state.views[viewId];
@@ -1164,6 +1143,10 @@ class HiGlassComponent extends React.Component {
1164
1143
  this.setState({ mouseTool });
1165
1144
  }
1166
1145
 
1146
+ setSizeMode(sizeMode) {
1147
+ this.setState({ sizeMode });
1148
+ }
1149
+
1167
1150
  /**
1168
1151
  * Checks if a track's value scale is locked with another track
1169
1152
  */
@@ -1209,6 +1192,9 @@ class HiGlassComponent extends React.Component {
1209
1192
  )
1210
1193
  // filter out stale locks with non-existant tracks
1211
1194
  .filter((track) => track)
1195
+ // Filter out tracks that don't have values scales (e.g. chromosome tracks).
1196
+ // The .originalTrack check covers LeftTrackModifier style tracks.
1197
+ .filter((track) => track.valueScale || track.originalTrack?.valueScale)
1212
1198
  // if the track is a LeftTrackModifier we want the originalTrack
1213
1199
  .map((track) =>
1214
1200
  track.originalTrack === undefined ? track : track.originalTrack,
@@ -1234,14 +1220,16 @@ class HiGlassComponent extends React.Component {
1234
1220
 
1235
1221
  if (
1236
1222
  minValues.length === 0 ||
1237
- minValues.filter((x) => x === null || x === Infinity).length > 0
1223
+ minValues.filter((x) => x === null || x === Number.POSITIVE_INFINITY)
1224
+ .length > 0
1238
1225
  ) {
1239
1226
  return null; // Data hasn't loaded completely
1240
1227
  }
1241
1228
 
1242
1229
  if (
1243
1230
  maxValues.length === 0 ||
1244
- maxValues.filter((x) => x === null || x === -Infinity).length > 0
1231
+ maxValues.filter((x) => x === null || x === Number.NEGATIVE_INFINITY)
1232
+ .length > 0
1245
1233
  ) {
1246
1234
  return null; // Data hasn't loaded completely
1247
1235
  }
@@ -1395,7 +1383,7 @@ class HiGlassComponent extends React.Component {
1395
1383
  * Event handler is called with parameters (xScale, yScale)
1396
1384
  */
1397
1385
  addDraggingChangedListener(viewUid, listenerUid, eventHandler) {
1398
- if (!this.draggingChangedListeners.hasOwnProperty(viewUid)) {
1386
+ if (!(viewUid in this.draggingChangedListeners)) {
1399
1387
  this.draggingChangedListeners[viewUid] = {};
1400
1388
  }
1401
1389
 
@@ -1412,10 +1400,10 @@ class HiGlassComponent extends React.Component {
1412
1400
  * @param listenerUid: The uid of the listener itself.
1413
1401
  */
1414
1402
  removeDraggingChangedListener(viewUid, listenerUid) {
1415
- if (this.draggingChangedListeners.hasOwnProperty(viewUid)) {
1403
+ if (viewUid in this.draggingChangedListeners) {
1416
1404
  const listeners = this.draggingChangedListeners[viewUid];
1417
1405
 
1418
- if (listeners.hasOwnProperty(listenerUid)) {
1406
+ if (listenerUid in listeners) {
1419
1407
  // make sure the listener doesn't think we're still
1420
1408
  // dragging
1421
1409
  listeners[listenerUid](false);
@@ -1561,7 +1549,10 @@ class HiGlassComponent extends React.Component {
1561
1549
  this.canvasElement.width,
1562
1550
  this.canvasElement.height,
1563
1551
  );
1564
- img.src = `data:image/svg+xml;base64,${btoa(svgString)}`;
1552
+
1553
+ img.src = `data:image/svg+xml;base64,${btoa(
1554
+ unescape(encodeURIComponent(svgString)),
1555
+ )}`;
1565
1556
  img.onload = () => {
1566
1557
  const targetCanvas = document.createElement('canvas');
1567
1558
  // TODO: I have no idea why dimensions are doubled!
@@ -1605,7 +1596,6 @@ class HiGlassComponent extends React.Component {
1605
1596
  const lockGroup = this.zoomLocks[uid];
1606
1597
  const lockGroupItems = dictItems(lockGroup);
1607
1598
 
1608
- // eslint-disable-next-line no-unused-vars
1609
1599
  const [centerX, centerY, k] = scalesCenterAndK(
1610
1600
  this.xScales[uid],
1611
1601
  this.yScales[uid],
@@ -1624,7 +1614,6 @@ class HiGlassComponent extends React.Component {
1624
1614
  continue;
1625
1615
  }
1626
1616
 
1627
- // eslint-disable-next-line no-unused-vars
1628
1617
  const [keyCenterX, keyCenterY, keyK] = scalesCenterAndK(
1629
1618
  this.xScales[key],
1630
1619
  this.yScales[key],
@@ -1656,7 +1645,7 @@ class HiGlassComponent extends React.Component {
1656
1645
 
1657
1646
  // notify the listeners of all locked views that the scales of
1658
1647
  // this view have changed
1659
- if (this.scalesChangedListeners.hasOwnProperty(key)) {
1648
+ if (key in this.scalesChangedListeners) {
1660
1649
  dictValues(this.scalesChangedListeners[key]).forEach((x) => {
1661
1650
  x(newXScale, newYScale);
1662
1651
  });
@@ -1669,7 +1658,6 @@ class HiGlassComponent extends React.Component {
1669
1658
  const lockGroup = this.locationLocks[uid];
1670
1659
  const lockGroupItems = dictItems(lockGroup);
1671
1660
 
1672
- // eslint-disable-next-line no-unused-vars
1673
1661
  const [centerX, centerY, k] = scalesCenterAndK(
1674
1662
  this.xScales[uid],
1675
1663
  this.yScales[uid],
@@ -1683,7 +1671,6 @@ class HiGlassComponent extends React.Component {
1683
1671
  continue;
1684
1672
  }
1685
1673
 
1686
- // eslint-disable-next-line no-unused-vars
1687
1674
  const [keyCenterX, keyCenterY, keyK] = scalesCenterAndK(
1688
1675
  this.xScales[key],
1689
1676
  this.yScales[key],
@@ -1718,7 +1705,7 @@ class HiGlassComponent extends React.Component {
1718
1705
 
1719
1706
  // notify the listeners of all locked views that the scales of
1720
1707
  // this view have changed
1721
- if (this.scalesChangedListeners.hasOwnProperty(key)) {
1708
+ if (key in this.scalesChangedListeners) {
1722
1709
  dictValues(this.scalesChangedListeners[key]).forEach((x) => {
1723
1710
  x(newXScale, newYScale);
1724
1711
  });
@@ -1734,7 +1721,6 @@ class HiGlassComponent extends React.Component {
1734
1721
  // this means the x axis of this view (uid) is locked to the y axis of another view
1735
1722
  const lockCrossAxis = this.locationLocksAxisWise.x[uid].axis !== 'x';
1736
1723
 
1737
- // eslint-disable-next-line no-unused-vars
1738
1724
  const [centerX, centerY, k] = scalesCenterAndK(
1739
1725
  this.xScales[uid],
1740
1726
  this.yScales[uid],
@@ -1748,7 +1734,6 @@ class HiGlassComponent extends React.Component {
1748
1734
  continue;
1749
1735
  }
1750
1736
 
1751
- // eslint-disable-next-line no-unused-vars
1752
1737
  const [keyCenterX, keyCenterY, keyK] = scalesCenterAndK(
1753
1738
  this.xScales[key],
1754
1739
  this.yScales[key],
@@ -1781,7 +1766,7 @@ class HiGlassComponent extends React.Component {
1781
1766
 
1782
1767
  // notify the listeners of all locked views that the scales of
1783
1768
  // this view have changed
1784
- if (this.scalesChangedListeners.hasOwnProperty(key)) {
1769
+ if (key in this.scalesChangedListeners) {
1785
1770
  dictValues(this.scalesChangedListeners[key]).forEach((x) => {
1786
1771
  x(newXScale, newYScale);
1787
1772
  });
@@ -1797,7 +1782,6 @@ class HiGlassComponent extends React.Component {
1797
1782
  // this means the y axis of this view (uid) is locked to the x axis of another view
1798
1783
  const lockCrossAxis = this.locationLocksAxisWise.y[uid].axis !== 'y';
1799
1784
 
1800
- // eslint-disable-next-line no-unused-vars
1801
1785
  const [centerX, centerY, k] = scalesCenterAndK(
1802
1786
  this.xScales[uid],
1803
1787
  this.yScales[uid],
@@ -1811,7 +1795,6 @@ class HiGlassComponent extends React.Component {
1811
1795
  continue;
1812
1796
  }
1813
1797
 
1814
- // eslint-disable-next-line no-unused-vars
1815
1798
  const [keyCenterX, keyCenterY, keyK] = scalesCenterAndK(
1816
1799
  this.xScales[key],
1817
1800
  this.yScales[key],
@@ -1844,7 +1827,7 @@ class HiGlassComponent extends React.Component {
1844
1827
 
1845
1828
  // notify the listeners of all locked views that the scales of
1846
1829
  // this view have changed
1847
- if (this.scalesChangedListeners.hasOwnProperty(key)) {
1830
+ if (key in this.scalesChangedListeners) {
1848
1831
  dictValues(this.scalesChangedListeners[key]).forEach((x) => {
1849
1832
  x(newXScale, newYScale);
1850
1833
  });
@@ -2105,7 +2088,7 @@ class HiGlassComponent extends React.Component {
2105
2088
  };
2106
2089
 
2107
2090
  this.addCallbacks(toView, newTrack);
2108
- this.handleTrackAdded(toView, newTrack, position, hostTrack);
2091
+ this.handleTrackAdded(toView, newTrack, position, null, hostTrack);
2109
2092
  }
2110
2093
  this.setState({
2111
2094
  chooseTrackHandler: null,
@@ -2125,12 +2108,11 @@ class HiGlassComponent extends React.Component {
2125
2108
  const targetXScale = this.xScales[uid1];
2126
2109
  const targetYScale = this.yScales[uid1];
2127
2110
 
2128
- // eslint-disable-next-line no-unused-vars
2129
2111
  const [targetCenterX, targetCenterY, targetK] = scalesCenterAndK(
2130
2112
  targetXScale,
2131
2113
  targetYScale,
2132
2114
  );
2133
- // eslint-disable-next-line no-unused-vars
2115
+
2134
2116
  const [sourceCenterX, sourceCenterY, sourceK] = scalesCenterAndK(
2135
2117
  sourceXScale,
2136
2118
  sourceYScale,
@@ -2155,12 +2137,11 @@ class HiGlassComponent extends React.Component {
2155
2137
  const targetXScale = this.xScales[uid1];
2156
2138
  const targetYScale = this.yScales[uid1];
2157
2139
 
2158
- // eslint-disable-next-line no-unused-vars
2159
2140
  const [targetCenterX, targetCenterY, targetK] = scalesCenterAndK(
2160
2141
  targetXScale,
2161
2142
  targetYScale,
2162
2143
  );
2163
- // eslint-disable-next-line no-unused-vars
2144
+
2164
2145
  const [sourceCenterX, sourceCenterY, sourceK] = scalesCenterAndK(
2165
2146
  sourceXScale,
2166
2147
  sourceYScale,
@@ -2180,9 +2161,10 @@ class HiGlassComponent extends React.Component {
2180
2161
  *
2181
2162
  * We just need to close the menu here.
2182
2163
  */
2183
- handleTrackPositionChosen(viewUid, position) {
2164
+ handleTrackPositionChosen(viewUid, position, extent) {
2184
2165
  this.setState({
2185
2166
  addTrackPosition: position,
2167
+ addTrackExtent: extent,
2186
2168
  addTrackPositionView: viewUid,
2187
2169
  });
2188
2170
  }
@@ -2194,7 +2176,8 @@ class HiGlassComponent extends React.Component {
2194
2176
  updateRowHeight() {
2195
2177
  if (
2196
2178
  !this.props.options ||
2197
- this.sizeMode !== SIZE_MODE_BOUNDED ||
2179
+ (this.sizeMode !== SIZE_MODE_BOUNDED &&
2180
+ this.sizeMode !== SIZE_MODE_BOUNDED_OVERFLOW) ||
2198
2181
  this.props.options.pixelPreciseMarginPadding
2199
2182
  ) {
2200
2183
  // not bounded so we don't need to update the row height
@@ -2204,9 +2187,34 @@ class HiGlassComponent extends React.Component {
2204
2187
  // const width = this.element.parentNode.clientWidth;
2205
2188
  const height = this.element.parentNode.clientHeight;
2206
2189
 
2207
- let maxHeight = 0;
2190
+ let maxHeight = 1;
2191
+ let perTrackRowHeight = 0;
2192
+
2208
2193
  for (const view of dictValues(this.state.views)) {
2209
2194
  maxHeight = Math.max(maxHeight, view.layout.y + view.layout.h);
2195
+
2196
+ const topTrackHeights = view.tracks.top
2197
+ .filter((x) => x.height)
2198
+ .map((x) => x.height)
2199
+ .reduce((a, b) => a + b, 0);
2200
+ const bottomTrackHeights = view.tracks.bottom
2201
+ .filter((x) => x.height)
2202
+ .map((x) => x.height)
2203
+ .reduce((a, b) => a + b, 0);
2204
+
2205
+ // centerTrackHeight is excluded because center tracks
2206
+ // are resized to fit into the remaining part of the view
2207
+ // const centerTrackHeight = view.tracks.center
2208
+ // .filter(x => x.height)
2209
+ // .map(x => x.height)
2210
+ // .reduce((a, b) => a + b, 0);
2211
+
2212
+ perTrackRowHeight = Math.ceil(
2213
+ Math.max(
2214
+ perTrackRowHeight,
2215
+ (topTrackHeights + bottomTrackHeights) / view.layout.h,
2216
+ ),
2217
+ );
2210
2218
  }
2211
2219
 
2212
2220
  this.handleDragStart();
@@ -2217,43 +2225,23 @@ class HiGlassComponent extends React.Component {
2217
2225
  const marginHeight = MARGIN_HEIGHT * maxHeight - 1;
2218
2226
  const availableHeight = height - marginHeight;
2219
2227
 
2220
- // const currentRowHeight = this.state.rowHeight;
2221
- const prospectiveRowHeight = availableHeight / maxHeight; // maxHeight is the number of
2222
- // rows necessary to display this view
2228
+ // if we're bounded, then maxHeight * rowHeight = availableHeight
2229
+ // but the catch is that if we have tracks that would be larger
2230
+ // than the largest div, we have to adjust the height to make sure
2231
+ // all of them are visible
2232
+ // to do that, we have to go view by view and calculate the heights
2233
+ // of the top and bottom sections and then divide that by the view's height
2234
+ // if there's a center track
2223
2235
 
2224
- const chosenRowHeight = Math.floor(prospectiveRowHeight);
2225
-
2226
- // for (const view of dictValues(this.state.views)) {
2227
- // const {
2228
- // totalWidth,
2229
- // totalHeight,
2230
- // topHeight,
2231
- // bottomHeight,
2232
- // leftWidth,
2233
- // rightWidth,
2234
- // centerWidth,
2235
- // centerHeight,
2236
- // minNecessaryHeight
2237
- // } = this.calculateViewDimensions(view);
2238
-
2239
- // // If the view is bounded, then we always fit everything inside the container
2240
- // //
2241
- // // It used to be that if the viewconfig was too long, we just let it overflow,
2242
- // // but I think it's better that it's always contained.
2243
-
2244
- // /*
2245
- // if (minNecessaryHeight > view.layout.h * (prospectiveRowHeight + MARGIN_HEIGHT)) {
2246
- // // we don't have space for one of the containers, so let them exceed the bounds
2247
- // // of the box
2248
- // chosenRowHeight = currentRowHeight;
2249
- // break;
2250
- // }
2251
- // */
2252
- // }
2236
+ const prospectiveRowHeight = Math.floor(availableHeight / maxHeight); // maxHeight is the number of
2237
+ // rows necessary to display this view
2238
+ const chosenRowHeight = Math.max(prospectiveRowHeight, perTrackRowHeight);
2253
2239
 
2254
- this.setState({
2255
- rowHeight: chosenRowHeight,
2256
- });
2240
+ if (this.topDivRef.current) {
2241
+ this.setState({
2242
+ rowHeight: chosenRowHeight,
2243
+ });
2244
+ }
2257
2245
  }
2258
2246
 
2259
2247
  /**
@@ -2299,11 +2287,11 @@ class HiGlassComponent extends React.Component {
2299
2287
  return TRACKS_INFO_BY_TYPE[trackType];
2300
2288
  }
2301
2289
 
2302
- if (this.pluginTracks && this.pluginTracks[trackType]) {
2290
+ if (this.pluginTracks?.[trackType]) {
2303
2291
  return this.pluginTracks[trackType].config;
2304
2292
  }
2305
2293
 
2306
- if (window.higlassTracksByType && window.higlassTracksByType[trackType]) {
2294
+ if (window.higlassTracksByType?.[trackType]) {
2307
2295
  return window.higlassTracksByType[trackType].config;
2308
2296
  }
2309
2297
 
@@ -2317,7 +2305,7 @@ class HiGlassComponent extends React.Component {
2317
2305
 
2318
2306
  forceRefreshView() {
2319
2307
  // force everything to rerender
2320
- this.setState(this.state); // eslint-disable-line react/no-access-state-in-setstate
2308
+ this.setState(this.state);
2321
2309
  }
2322
2310
 
2323
2311
  refreshView(timeout = SHORT_DRAG_TIMEOUT) {
@@ -2474,8 +2462,8 @@ class HiGlassComponent extends React.Component {
2474
2462
  : defaultCenterWidth;
2475
2463
  }
2476
2464
 
2477
- currHeight += height;
2478
- currWidth += width;
2465
+ centerHeight = height;
2466
+ centerWidth = width;
2479
2467
  }
2480
2468
  } else if (
2481
2469
  ((view.tracks.top && dictValues(view.tracks.top).length > 1) ||
@@ -2535,29 +2523,30 @@ class HiGlassComponent extends React.Component {
2535
2523
  }
2536
2524
 
2537
2525
  calculateZoomLimits(view, initialXDomain) {
2538
- const limits = [0, Infinity];
2526
+ const limits = [0, Number.POSITIVE_INFINITY];
2539
2527
 
2540
- // By default, highest zoom resolution is 1bp
2541
- const viewConfLimit = view.zoomLimits || [1, null];
2542
- const diffX = initialXDomain[1] - initialXDomain[0];
2528
+ if ('zoomLimits' in view) {
2529
+ const diffX = initialXDomain[1] - initialXDomain[0];
2530
+ const viewConfLimit = view.zoomLimits;
2543
2531
 
2544
- if (viewConfLimit.length !== 2) {
2545
- return limits;
2546
- }
2532
+ if (viewConfLimit.length !== 2) {
2533
+ return limits;
2534
+ }
2547
2535
 
2548
- if (viewConfLimit[0] !== null && viewConfLimit[0] > 0) {
2549
- const upperLimit = diffX / viewConfLimit[0];
2550
- limits[1] = Math.max(upperLimit, 1);
2551
- if (upperLimit < 1) {
2552
- console.warn(`Invalid zoom limits. Lower limit set to ${diffX}`);
2536
+ if (viewConfLimit[0] !== null && viewConfLimit[0] > 0) {
2537
+ const upperLimit = diffX / viewConfLimit[0];
2538
+ limits[1] = Math.max(upperLimit, 1);
2539
+ if (upperLimit < 1) {
2540
+ console.warn(`Invalid zoom limits. Lower limit set to ${diffX}`);
2541
+ }
2553
2542
  }
2554
- }
2555
2543
 
2556
- if (viewConfLimit[1] !== null && viewConfLimit[1] > viewConfLimit[0]) {
2557
- const lowerLimit = diffX / viewConfLimit[1];
2558
- limits[0] = Math.min(lowerLimit, 1);
2559
- if (lowerLimit > 1) {
2560
- console.warn(`Invalid zoom limits. Upper limit set to ${diffX}`);
2544
+ if (viewConfLimit[1] !== null && viewConfLimit[1] > viewConfLimit[0]) {
2545
+ const lowerLimit = diffX / viewConfLimit[1];
2546
+ limits[0] = Math.min(lowerLimit, 1);
2547
+ if (lowerLimit > 1) {
2548
+ console.warn(`Invalid zoom limits. Upper limit set to ${diffX}`);
2549
+ }
2561
2550
  }
2562
2551
  }
2563
2552
  return limits;
@@ -2690,6 +2679,7 @@ class HiGlassComponent extends React.Component {
2690
2679
 
2691
2680
  if (hostTrack.type === 'combined') {
2692
2681
  hostTrack.contents.push(newTrack);
2682
+ hostTrack.uid = slugid.nice();
2693
2683
 
2694
2684
  if (newTrack.type === 'heatmap') {
2695
2685
  // For stacked heatmaps we will adjust some options automatically for convenience
@@ -2716,6 +2706,8 @@ class HiGlassComponent extends React.Component {
2716
2706
  this.setState((prevState) => ({
2717
2707
  views: prevState.views,
2718
2708
  }));
2709
+
2710
+ this.triggerViewChangeDb();
2719
2711
  }
2720
2712
 
2721
2713
  handleNoTrackAdded() {
@@ -2723,6 +2715,7 @@ class HiGlassComponent extends React.Component {
2723
2715
  // we've already added the track, remove the add track dialog
2724
2716
  this.setState({
2725
2717
  addTrackPosition: null,
2718
+ addTrackExtent: null,
2726
2719
  });
2727
2720
  }
2728
2721
  }
@@ -2735,11 +2728,11 @@ class HiGlassComponent extends React.Component {
2735
2728
  * @param position: The position the track is being added to
2736
2729
  * @param host: If this track is being added to another track
2737
2730
  */
2738
- handleTracksAdded(viewId, newTracks, position, host) {
2731
+ handleTracksAdded(viewId, newTracks, position, extent, host) {
2739
2732
  this.storeTrackSizes(viewId);
2740
2733
 
2741
2734
  for (const newTrack of newTracks) {
2742
- this.handleTrackAdded(viewId, newTrack, position, host);
2735
+ this.handleTrackAdded(viewId, newTrack, position, extent, host);
2743
2736
  }
2744
2737
  }
2745
2738
 
@@ -2808,7 +2801,7 @@ class HiGlassComponent extends React.Component {
2808
2801
  * @returns {Object}: A trackConfig (\{ uid: "", width: x \})
2809
2802
  * describing this track
2810
2803
  */
2811
- handleTrackAdded(viewId, newTrack, position, host = null) {
2804
+ handleTrackAdded(viewId, newTrack, position, extent = null, host = null) {
2812
2805
  this.addDefaultTrackOptions(newTrack);
2813
2806
 
2814
2807
  // make sure the new track has a uid
@@ -2825,6 +2818,7 @@ class HiGlassComponent extends React.Component {
2825
2818
  // we've already added the track, remove the add track dialog
2826
2819
  this.setState({
2827
2820
  addTrackPosition: null,
2821
+ addTrackExtent: null,
2828
2822
  });
2829
2823
  }
2830
2824
 
@@ -2835,16 +2829,18 @@ class HiGlassComponent extends React.Component {
2835
2829
  }
2836
2830
 
2837
2831
  newTrack.position = position;
2838
-
2832
+ if (extent) {
2833
+ newTrack.options.extent = extent;
2834
+ }
2839
2835
  const trackInfo = this.getTrackInfo(newTrack.type);
2840
2836
 
2841
2837
  newTrack.width =
2842
2838
  trackInfo.defaultWidth ||
2843
- (trackInfo.defaultOptions && trackInfo.defaultOptions.minWidth) ||
2839
+ trackInfo.defaultOptions?.minWidth ||
2844
2840
  this.minVerticalWidth;
2845
2841
  newTrack.height =
2846
2842
  trackInfo.defaultHeight ||
2847
- (trackInfo.defaultOptions && trackInfo.defaultOptions.minHeight) ||
2843
+ trackInfo.defaultOptions?.minHeight ||
2848
2844
  this.minHorizontalHeight;
2849
2845
 
2850
2846
  const { tracks } = this.state.views[viewId];
@@ -3006,7 +3002,10 @@ class HiGlassComponent extends React.Component {
3006
3002
  totalTrackHeight += MARGIN_HEIGHT;
3007
3003
  const rowHeight = this.state.rowHeight + MARGIN_HEIGHT;
3008
3004
 
3009
- if (this.sizeMode !== SIZE_MODE_BOUNDED) {
3005
+ if (
3006
+ this.sizeMode !== SIZE_MODE_BOUNDED &&
3007
+ this.sizeMode !== SIZE_MODE_BOUNDED_OVERFLOW
3008
+ ) {
3010
3009
  view.layout.h = Math.ceil(totalTrackHeight / rowHeight);
3011
3010
  }
3012
3011
  }
@@ -3195,7 +3194,15 @@ class HiGlassComponent extends React.Component {
3195
3194
  *
3196
3195
  * Done in place.
3197
3196
  *
3198
- * @param track: A view with tracks.
3197
+ * @param {string} viewUid
3198
+ * @param {{
3199
+ * type: string;
3200
+ * uid: string;
3201
+ * fromViewUid: string;
3202
+ * registerViewportChanged: (string: trackId, listener: () => void) => void;
3203
+ * removeViewportChanged: (string: trackId) => void;
3204
+ * removeScalesChangedListener: (string: trackId) => void;
3205
+ * }} track A view with tracks.
3199
3206
  */
3200
3207
  addCallbacks(viewUid, track) {
3201
3208
  const trackInfo = this.getTrackInfo(track.type);
@@ -3203,7 +3210,7 @@ class HiGlassComponent extends React.Component {
3203
3210
  track.type === 'viewport-projection-center' ||
3204
3211
  track.type === 'viewport-projection-horizontal' ||
3205
3212
  track.type === 'viewport-projection-vertical' ||
3206
- (trackInfo && trackInfo.projection)
3213
+ trackInfo?.projection
3207
3214
  ) {
3208
3215
  const fromView = track.fromViewUid;
3209
3216
 
@@ -3399,7 +3406,7 @@ class HiGlassComponent extends React.Component {
3399
3406
  const locksByViewUid = {};
3400
3407
 
3401
3408
  for (const viewUid of dictKeys(locks)) {
3402
- let lockUid = locks[viewUid] && locks[viewUid].uid;
3409
+ let lockUid = locks[viewUid]?.uid;
3403
3410
 
3404
3411
  if (!lockUid) {
3405
3412
  // otherwise, assign this locationLock its own uid
@@ -3425,23 +3432,18 @@ class HiGlassComponent extends React.Component {
3425
3432
  return { locksByViewUid, locksDict };
3426
3433
  }
3427
3434
 
3428
- getViewsAsJson() {
3435
+ getViewsAsJson(views) {
3429
3436
  const newJson = JSON.parse(JSON.stringify(this.state.viewConfig));
3430
- newJson.views = Object.values(this.state.views).map((k) => {
3437
+ newJson.views = Object.values(views || this.state.views).map((k) => {
3431
3438
  const newView = JSON.parse(JSON.stringify(k));
3432
3439
 
3433
3440
  visitPositionedTracks(newView.tracks, (track) => {
3434
3441
  if (track.server) {
3435
- const url = parse(track.server, {});
3436
-
3437
- if (!url.hostname.length) {
3438
- // no hostname specified in the track source servers so we'll add
3439
- // the current URL's
3440
- const hostString = window.location.host;
3441
- const { protocol } = window.location;
3442
- const newUrl = `${protocol}//${hostString}${url.pathname}`;
3443
-
3444
- track.server = newUrl;
3442
+ if (!URL.canParse(track.server)) {
3443
+ // Not a valid URL, so we'll extend the current one
3444
+ const url = new URL(window.location);
3445
+ url.pathname = track.server;
3446
+ track.server = url.href;
3445
3447
  }
3446
3448
  }
3447
3449
 
@@ -3455,6 +3457,7 @@ class HiGlassComponent extends React.Component {
3455
3457
  // so the `projectionXDomain` field must be used.
3456
3458
  track.projectionXDomain = this.projectionXDomains[k.uid][track.uid];
3457
3459
  }
3460
+
3458
3461
  if (
3459
3462
  (track.type === 'viewport-projection-center' ||
3460
3463
  track.type === 'viewport-projection-vertical') &&
@@ -3482,8 +3485,10 @@ class HiGlassComponent extends React.Component {
3482
3485
  delete track.datatype;
3483
3486
  delete track.maxWidth;
3484
3487
  delete track.datafile;
3488
+ delete track.filetype;
3485
3489
  delete track.binsPerDimension;
3486
3490
  delete track.resolutions;
3491
+ delete track.row_infos;
3487
3492
  delete track.aggregationModes;
3488
3493
  });
3489
3494
 
@@ -3639,7 +3644,7 @@ class HiGlassComponent extends React.Component {
3639
3644
  * User clicked on the "Add View" button. We'll duplicate the last
3640
3645
  * view.
3641
3646
  */
3642
- handleAddView(view) {
3647
+ handleAddView(view, newViewParams, noUpdate) {
3643
3648
  const views = dictValues(this.state.views);
3644
3649
  const lastView = view;
3645
3650
 
@@ -3679,11 +3684,14 @@ class HiGlassComponent extends React.Component {
3679
3684
 
3680
3685
  const jsonString = JSON.stringify(lastView);
3681
3686
 
3682
- const newView = JSON.parse(jsonString); // ghetto copy
3687
+ let newView = JSON.parse(jsonString); // ghetto copy
3683
3688
 
3684
3689
  newView.initialXDomain = this.xScales[newView.uid].domain();
3685
3690
  newView.initialYDomain = this.yScales[newView.uid].domain();
3686
3691
 
3692
+ if (newViewParams) {
3693
+ newView = { ...newView, ...newViewParams };
3694
+ }
3687
3695
  // place this new view below all the others
3688
3696
 
3689
3697
  [[newView.layout.x, newView.layout.y]] = potentialPositions;
@@ -3692,47 +3700,38 @@ class HiGlassComponent extends React.Component {
3692
3700
  newView.uid = slugid.nice();
3693
3701
  newView.layout.i = newView.uid;
3694
3702
 
3703
+ this.xScales[newView.uid] = scaleLinear().domain(newView.initialXDomain);
3704
+ this.yScales[newView.uid] = scaleLinear().domain(newView.initialYDomain);
3705
+
3695
3706
  visitPositionedTracks(newView.tracks, (track) => {
3696
3707
  this.addCallbacks(newView.uid, track);
3697
3708
  });
3698
3709
 
3699
- this.setState((prevState) => {
3700
- // eslint-disable-next-line no-shadow
3701
- const views = JSON.parse(JSON.stringify(prevState.views));
3702
- views[newView.uid] = newView;
3703
- return { views };
3704
- });
3705
- }
3710
+ const createNewViews = (prevViews) => {
3711
+ const _views = JSON.parse(JSON.stringify(prevViews));
3712
+ _views[newView.uid] = newView;
3706
3713
 
3707
- handleSelectedAssemblyChanged(
3708
- viewUid,
3709
- newAssembly,
3710
- newAutocompleteId,
3711
- newServer,
3712
- ) {
3713
- /*
3714
- * A new assembly was selected in the GenomePositionSearchBox.
3715
- * Update the corresponding
3716
- * view's entry
3717
- *
3718
- * Arguments
3719
- * ---------
3720
- *
3721
- * viewUid: string
3722
- * The uid of the view this genomepositionsearchbox belongs to
3723
- * newAssembly: string
3724
- * The new assembly it should display coordinates for
3725
- *
3726
- * Returns
3727
- * -------
3728
- *
3729
- * Nothing
3730
- */
3731
- const { views } = this.state;
3714
+ return _views;
3715
+ };
3716
+
3717
+ let newViews = null;
3718
+
3719
+ if (!noUpdate) {
3720
+ this.setState((prevState) => {
3721
+ newViews = createNewViews(prevState.views);
3722
+
3723
+ return {
3724
+ views: newViews,
3725
+ };
3726
+ });
3727
+ } else {
3728
+ newViews = createNewViews(this.state.views);
3729
+ }
3732
3730
 
3733
- views[viewUid].genomePositionSearchBox.chromInfoId = newAssembly;
3734
- views[viewUid].genomePositionSearchBox.autocompleteId = newAutocompleteId;
3735
- views[viewUid].genomePositionSearchBox.autocompleteServer = newServer;
3731
+ return {
3732
+ newViewUid: newView.uid,
3733
+ newViewconf: this.getViewsAsJson(newViews),
3734
+ };
3736
3735
  }
3737
3736
 
3738
3737
  createGenomePostionSearchBoxEntry(
@@ -3891,7 +3890,7 @@ class HiGlassComponent extends React.Component {
3891
3890
  if (
3892
3891
  otherTrack.type === 'heatmap' &&
3893
3892
  otherTrack.uid !== track.uid &&
3894
- otherTrack.options.extent !== 'lower-left'
3893
+ otherTrack.options.extent === 'full'
3895
3894
  ) {
3896
3895
  // Automatically change the extent of the other track to
3897
3896
  // `lower-left``
@@ -3936,7 +3935,7 @@ class HiGlassComponent extends React.Component {
3936
3935
  if (
3937
3936
  otherTrack.type === 'heatmap' &&
3938
3937
  otherTrack.uid !== track.uid &&
3939
- otherTrack.options.extent !== 'upper-right'
3938
+ otherTrack.options.extent === 'full'
3940
3939
  ) {
3941
3940
  // Automatically change the extent of the other track to
3942
3941
  // `upper-right``
@@ -4025,7 +4024,7 @@ class HiGlassComponent extends React.Component {
4025
4024
  'right',
4026
4025
  'bottom',
4027
4026
  ]) {
4028
- if (v.tracks && v.tracks.hasOwnProperty(trackOrientation)) {
4027
+ if (trackOrientation in (v.tracks ?? {})) {
4029
4028
  // filter out invalid tracks
4030
4029
  v.tracks[trackOrientation] = v.tracks[trackOrientation].filter((t) =>
4031
4030
  this.isTrackValid(t, viewUidsSet),
@@ -4049,6 +4048,7 @@ class HiGlassComponent extends React.Component {
4049
4048
  processViewConfig(viewConfig) {
4050
4049
  let { views } = viewConfig;
4051
4050
  let viewsByUid = {};
4051
+ let maxHeight = 1;
4052
4052
 
4053
4053
  if (!viewConfig.views || viewConfig.views.length === 0) {
4054
4054
  console.warn('No views provided in viewConfig');
@@ -4102,6 +4102,25 @@ class HiGlassComponent extends React.Component {
4102
4102
  }
4103
4103
 
4104
4104
  visitPositionedTracks(v.tracks, (track) => {
4105
+ if (this.state?.views?.[v.uid]) {
4106
+ // This whole song and dance is to ensure that if the higlass
4107
+ // viewer is reinstantiated all of the tileset info meta that
4108
+ // is stored with the track gets passed on.
4109
+ // Necessary to make sure that the "Zoom Limit" option still
4110
+ // functions.
4111
+ const existingTrack = getTrackByUid(
4112
+ this.state.views[v.uid].tracks,
4113
+ track.uid,
4114
+ );
4115
+
4116
+ if (existingTrack) {
4117
+ track.maxZoom = existingTrack.maxZoom;
4118
+ track.resolutions = existingTrack.resolutions;
4119
+ track.binsPerDimension = existingTrack.binsPerDimension;
4120
+ track.maxWidth = existingTrack.maxWidth;
4121
+ }
4122
+ }
4123
+
4105
4124
  if (!track.uid) track.uid = slugid.nice();
4106
4125
 
4107
4126
  this.addCallbacks(v.uid, track);
@@ -4119,6 +4138,18 @@ class HiGlassComponent extends React.Component {
4119
4138
  } else {
4120
4139
  v.layout = this.generateViewLayout(v);
4121
4140
  }
4141
+
4142
+ maxHeight = Math.max(maxHeight, v.layout.y + v.layout.h);
4143
+ });
4144
+
4145
+ // we want to scale up the height of each view so that the maxHeight
4146
+ // is closer to 12
4147
+ const TARGET_MAX_HEIGHT = 12;
4148
+ const multiplier = Math.ceil(TARGET_MAX_HEIGHT / maxHeight);
4149
+
4150
+ views.forEach((v) => {
4151
+ v.layout.h *= multiplier;
4152
+ v.layout.y *= multiplier;
4122
4153
  });
4123
4154
 
4124
4155
  this.deserializeZoomLocks(viewConfig);
@@ -4152,8 +4183,12 @@ class HiGlassComponent extends React.Component {
4152
4183
  this.apiPublish('rangeSelection', range);
4153
4184
  }
4154
4185
 
4155
- offViewChange(listenerId) {
4156
- this.viewChangeListener.splice(listenerId, 1);
4186
+ offViewChange(callback) {
4187
+ const index = this.viewChangeListener.indexOf(callback);
4188
+
4189
+ if (index > -1) {
4190
+ this.viewChangeListener.splice(index, 1);
4191
+ }
4157
4192
  }
4158
4193
 
4159
4194
  onViewChange(callback) {
@@ -4161,9 +4196,8 @@ class HiGlassComponent extends React.Component {
4161
4196
  }
4162
4197
 
4163
4198
  triggerViewChange() {
4164
- this.viewChangeListener.forEach((callback) =>
4165
- callback(this.getViewsAsString()),
4166
- );
4199
+ const viewsString = this.getViewsAsString();
4200
+ this.viewChangeListener.forEach((callback) => callback(viewsString));
4167
4201
  }
4168
4202
 
4169
4203
  getGenomeLocation(viewId) {
@@ -4355,7 +4389,10 @@ class HiGlassComponent extends React.Component {
4355
4389
  (listenerId) => listenerId.indexOf(LOCATION_LISTENER_PREFIX) === 0,
4356
4390
  )
4357
4391
  .map((listenerId) =>
4358
- parseInt(listenerId.slice(LOCATION_LISTENER_PREFIX.length + 1), 10),
4392
+ Number.parseInt(
4393
+ listenerId.slice(LOCATION_LISTENER_PREFIX.length + 1),
4394
+ 10,
4395
+ ),
4359
4396
  )
4360
4397
  .reduce((max, value) => Math.max(max, value), 0) + 1;
4361
4398
  }
@@ -4408,11 +4445,11 @@ class HiGlassComponent extends React.Component {
4408
4445
  * @param {object} e Event object.
4409
4446
  */
4410
4447
  mouseMoveHandler(e) {
4411
- if (!this.topDiv || this.state.modal) return;
4448
+ if (!this.topDivRef.current || this.state.modal) return;
4412
4449
 
4413
4450
  const absX = e.clientX;
4414
4451
  const absY = e.clientY;
4415
- const relPos = pointer(e, this.topDiv);
4452
+ const relPos = pointer(e, this.topDivRef.current);
4416
4453
  // We need to add the scrollTop
4417
4454
  relPos[1] += this.scrollTop;
4418
4455
  const hoveredTiledPlot = this.getTiledPlotAtPosition(absX, absY);
@@ -4450,15 +4487,13 @@ class HiGlassComponent extends React.Component {
4450
4487
  const evt = {
4451
4488
  x: relPos[0],
4452
4489
  y: relPos[1],
4453
- relTrackX:
4454
- hoveredTrack && hoveredTrack.flipText ? relTrackPos[1] : relTrackPos[0],
4455
- relTrackY:
4456
- hoveredTrack && hoveredTrack.flipText ? relTrackPos[0] : relTrackPos[1],
4490
+ relTrackX: hoveredTrack?.flipText ? relTrackPos[1] : relTrackPos[0],
4491
+ relTrackY: hoveredTrack?.flipText ? relTrackPos[0] : relTrackPos[1],
4457
4492
  dataX,
4458
4493
  dataY,
4459
4494
  // See below why we need these derived boolean values
4460
- isFrom2dTrack: !!(hoveredTrack && hoveredTrack.is2d),
4461
- isFromVerticalTrack: !!(hoveredTrack && hoveredTrack.flipText),
4495
+ isFrom2dTrack: !!hoveredTrack?.is2d,
4496
+ isFromVerticalTrack: !!hoveredTrack?.flipText,
4462
4497
  track: hoveredTrack,
4463
4498
  origEvt: e,
4464
4499
  sourceUid: this.uid,
@@ -4536,13 +4571,12 @@ class HiGlassComponent extends React.Component {
4536
4571
  showHoverMenu(evt) {
4537
4572
  // each track should have a function that returns an HTML representation
4538
4573
  // of the data at a give position
4539
- const mouseOverHtml =
4540
- evt.track && evt.track.getMouseOverHtml
4541
- ? evt.track.getMouseOverHtml(evt.relTrackX, evt.relTrackY)
4542
- : '';
4574
+ const mouseOverHtml = evt.track?.getMouseOverHtml
4575
+ ? evt.track.getMouseOverHtml(evt.relTrackX, evt.relTrackY)
4576
+ : '';
4543
4577
 
4544
4578
  if (evt.track !== this.prevMouseHoverTrack) {
4545
- if (this.prevMouseHoverTrack && this.prevMouseHoverTrack.stopHover) {
4579
+ if (this.prevMouseHoverTrack?.stopHover) {
4546
4580
  this.prevMouseHoverTrack.stopHover();
4547
4581
  }
4548
4582
  }
@@ -4551,7 +4585,7 @@ class HiGlassComponent extends React.Component {
4551
4585
 
4552
4586
  if (this.zooming) return;
4553
4587
 
4554
- const data = mouseOverHtml && mouseOverHtml.length ? [1] : [];
4588
+ const data = mouseOverHtml?.length ? [1] : [];
4555
4589
 
4556
4590
  // try to select the mouseover div
4557
4591
  let mouseOverDiv = select('body')
@@ -4639,7 +4673,7 @@ class HiGlassComponent extends React.Component {
4639
4673
 
4640
4674
  const hoveredTiledPlot = this.getTiledPlotAtPosition(absX, absY);
4641
4675
 
4642
- const relPos = pointer(nativeEvent, this.topDiv);
4676
+ const relPos = pointer(nativeEvent, this.topDivRef.current);
4643
4677
  relPos[1] += this.scrollTop;
4644
4678
 
4645
4679
  const hoveredTracks = hoveredTiledPlot
@@ -4663,10 +4697,8 @@ class HiGlassComponent extends React.Component {
4663
4697
  ]
4664
4698
  : relPos;
4665
4699
 
4666
- const relTrackX =
4667
- hoveredTrack && hoveredTrack.flipText ? relTrackPos[1] : relTrackPos[0];
4668
- const relTrackY =
4669
- hoveredTrack && hoveredTrack.flipText ? relTrackPos[0] : relTrackPos[1];
4700
+ const relTrackX = hoveredTrack?.flipText ? relTrackPos[1] : relTrackPos[0];
4701
+ const relTrackY = hoveredTrack?.flipText ? relTrackPos[0] : relTrackPos[1];
4670
4702
 
4671
4703
  for (const track of this.iterateOverTracks()) {
4672
4704
  const trackObj = getTrackObjById(
@@ -4765,7 +4797,7 @@ class HiGlassComponent extends React.Component {
4765
4797
  mouseDownHandler(evt) {}
4766
4798
 
4767
4799
  onScrollHandler() {
4768
- if (this.props.options.sizeMode !== SIZE_MODE_SCROLL) return;
4800
+ if (this.sizeMode !== SIZE_MODE_SCROLL) return;
4769
4801
  this.scrollTop = this.scrollContainer.scrollTop;
4770
4802
  this.pixiStage.y = -this.scrollTop;
4771
4803
  this.pubSub.publish('app.scroll', this.scrollTop);
@@ -4817,17 +4849,18 @@ class HiGlassComponent extends React.Component {
4817
4849
  this.props.zoomFixed ||
4818
4850
  this.props.options.zoomFixed ||
4819
4851
  this.state.viewConfig.zoomFixed ||
4820
- this.props.options.sizeMode === SIZE_MODE_SCROLL ||
4821
- (view && view.zoomFixed)
4852
+ this.sizeMode === SIZE_MODE_SCROLL ||
4853
+ view?.zoomFixed
4822
4854
  );
4823
4855
  }
4824
4856
 
4825
4857
  /**
4826
4858
  * Handle trackDimensionsModified events
4827
- * settings.viewId = id of the view
4828
- * settings.trackId = id of the track
4829
- * settings.height = new height of the track or undefined if current height should remain
4830
- * settings.width = new width of the track or undefined if current width should remain
4859
+ * @param {Object} settings
4860
+ * @param {string} settings.viewId = id of the view
4861
+ * @param {string} settings.trackId = id of the track
4862
+ * @param {number} settings.height = new height of the track or undefined if current height should remain
4863
+ * @param {nmber} settings.width = new width of the track or undefined if current width should remain
4831
4864
  */
4832
4865
  trackDimensionsModifiedHandler(settings) {
4833
4866
  const view = this.state.views[settings.viewId];
@@ -4853,15 +4886,12 @@ class HiGlassComponent extends React.Component {
4853
4886
  }
4854
4887
 
4855
4888
  wheelHandler(evt) {
4856
- if (this.state.modal || this.props.options.sizeMode === SIZE_MODE_SCROLL)
4857
- return;
4889
+ if (this.state.modal || this.sizeMode === SIZE_MODE_SCROLL) return;
4858
4890
 
4859
4891
  // The event forwarder wasn't written for React's SyntheticEvent
4860
4892
  const nativeEvent = evt.nativeEvent || evt;
4861
4893
 
4862
- const isTargetCanvas = evt.target === this.canvasElement;
4863
-
4864
- if (!hasParent(nativeEvent.target, this.topDiv)) {
4894
+ if (!hasParent(nativeEvent.target, this.topDivRef.current)) {
4865
4895
  // ignore events that don't come from within the
4866
4896
  // HiGlass container
4867
4897
  return;
@@ -4900,14 +4930,8 @@ class HiGlassComponent extends React.Component {
4900
4930
  const evtToPublish = {
4901
4931
  x: relPos[0],
4902
4932
  y: relPos[1],
4903
- relTrackX:
4904
- hoveredTrack && hoveredTrack.flipText
4905
- ? relTrackPos[1]
4906
- : relTrackPos[0],
4907
- relTrackY:
4908
- hoveredTrack && hoveredTrack.flipText
4909
- ? relTrackPos[0]
4910
- : relTrackPos[1],
4933
+ relTrackX: hoveredTrack?.flipText ? relTrackPos[1] : relTrackPos[0],
4934
+ relTrackY: hoveredTrack?.flipText ? relTrackPos[0] : relTrackPos[1],
4911
4935
  track: hoveredTrack,
4912
4936
  origEvt: nativeEvent,
4913
4937
  sourceUid: this.uid,
@@ -4918,6 +4942,7 @@ class HiGlassComponent extends React.Component {
4918
4942
  this.apiPublish('wheel', evtToPublish);
4919
4943
  }
4920
4944
 
4945
+ const isTargetCanvas = evt.target === this.canvasElement;
4921
4946
  if (nativeEvent.forwarded || isTargetCanvas) {
4922
4947
  evt.stopPropagation();
4923
4948
  evt.preventDefault();
@@ -4941,6 +4966,24 @@ class HiGlassComponent extends React.Component {
4941
4966
  }
4942
4967
  }
4943
4968
 
4969
+ /** Convert a viewUid to a view name so that when we create
4970
+ * viewport projects, we can show which view they refer to.
4971
+ * The name is usually a single letter like 'A'
4972
+ * The view's name will also be visible in the header. */
4973
+ viewUidToName(viewUid) {
4974
+ const views = Object.keys(this.state.views);
4975
+
4976
+ for (let i = 0; i < views.length; i++) {
4977
+ if (views[i] === viewUid) {
4978
+ // Starting from A and then onwards. God help us if there's
4979
+ // more than 52 views
4980
+ return String.fromCharCode(65 + i);
4981
+ }
4982
+ }
4983
+
4984
+ return 'UU';
4985
+ }
4986
+
4944
4987
  render() {
4945
4988
  this.tiledAreasDivs = {};
4946
4989
  this.tiledAreas = <div className={styles['tiled-area']} />;
@@ -4988,24 +5031,32 @@ class HiGlassComponent extends React.Component {
4988
5031
  this.tiledPlots[view.uid] = c;
4989
5032
  }}
4990
5033
  // Custom props
5034
+ addTrackExtent={
5035
+ this.state.addTrackPositionView === view.uid
5036
+ ? this.state.addTrackExtent
5037
+ : null
5038
+ }
4991
5039
  addTrackPosition={
4992
5040
  this.state.addTrackPositionView === view.uid
4993
5041
  ? this.state.addTrackPosition
4994
5042
  : null
4995
5043
  }
4996
5044
  addTrackPositionMenuPosition={addTrackPositionMenuPosition}
5045
+ apiPublish={this.apiPublish}
4997
5046
  canvasElement={this.state.canvasElement}
4998
5047
  chooseTrackHandler={
4999
5048
  this.state.chooseTrackHandler
5000
- ? (trackId) => this.state.chooseTrackHandler(view.uid, trackId)
5049
+ ? (trackId, evt) =>
5050
+ this.state.chooseTrackHandler(view.uid, trackId, evt)
5001
5051
  : null
5002
5052
  }
5003
5053
  customDialog={this.state.customDialog}
5004
5054
  closeCustomDialog={() => this.closeCustomDialog()}
5005
5055
  chromInfoPath={view.chromInfoPath}
5006
5056
  disableTrackMenu={this.isTrackMenuDisabled()}
5007
- draggingHappening={this.state.draggingHappening !== null}
5057
+ draggingHappening={this.state.draggingHappening}
5008
5058
  editable={this.isEditable()}
5059
+ genomePositionSearchBox={this.genomePositionSearchBoxes[view.uid]}
5009
5060
  getLockGroupExtrema={(uid) =>
5010
5061
  this.getLockGroupExtrema(view.uid, uid)
5011
5062
  }
@@ -5036,20 +5087,30 @@ class HiGlassComponent extends React.Component {
5036
5087
  }
5037
5088
  onNoTrackAdded={this.handleNoTrackAdded.bind(this)}
5038
5089
  onRangeSelection={this.rangeSelectionHandler.bind(this)}
5039
- onResizeTrack={this.triggerViewChangeDb}
5090
+ onResizeTrack={() => {
5091
+ this.adjustLayoutToTrackSizes(view);
5092
+ this.triggerViewChangeDb();
5093
+ }}
5040
5094
  onScalesChanged={(x, y) => this.handleScalesChanged(view.uid, x, y)}
5041
5095
  onTrackOptionsChanged={(trackId, options) =>
5042
5096
  this.handleTrackOptionsChanged(view.uid, trackId, options)
5043
5097
  }
5044
5098
  onTrackPositionChosen={this.handleTrackPositionChosen.bind(this)}
5045
- onTracksAdded={(newTracks, position, host) =>
5046
- this.handleTracksAdded(view.uid, newTracks, position, host)
5099
+ onTracksAdded={(newTracks, position, extent, host) =>
5100
+ this.handleTracksAdded(
5101
+ view.uid,
5102
+ newTracks,
5103
+ position,
5104
+ extent,
5105
+ host,
5106
+ )
5047
5107
  }
5048
5108
  onUnlockValueScale={(uid) =>
5049
5109
  this.handleUnlockValueScale(view.uid, uid)
5050
5110
  }
5051
5111
  onValueScaleChanged={(uid) => this.syncValueScales(view.uid, uid)}
5052
5112
  overlays={view.overlays}
5113
+ overTrackChooser={this.state.overTrackChooser}
5053
5114
  paddingBottom={this.viewPaddingBottom}
5054
5115
  paddingLeft={this.viewPaddingLeft}
5055
5116
  paddingRight={this.viewPaddingRight}
@@ -5069,6 +5130,9 @@ class HiGlassComponent extends React.Component {
5069
5130
  setCentersFunction={(c) => {
5070
5131
  this.setCenters[view.uid] = c;
5071
5132
  }}
5133
+ setOverTrackChooser={(val) =>
5134
+ this.setState({ overTrackChooser: val })
5135
+ }
5072
5136
  svgElement={this.state.svgElement}
5073
5137
  tracks={view.tracks}
5074
5138
  trackSourceServers={this.state.viewConfig.trackSourceServers}
@@ -5086,62 +5150,67 @@ class HiGlassComponent extends React.Component {
5086
5150
  />
5087
5151
  );
5088
5152
 
5089
- const getGenomePositionSearchBox = (isFocused, onFocus) => {
5090
- if (!view.genomePositionSearchBox) return null;
5091
-
5092
- return (
5093
- <GenomePositionSearchBox
5094
- // Reserved props
5095
- key={`gpsb${view.uid}`}
5096
- ref={(c) => {
5097
- this.genomePositionSearchBoxes[view.uid] = c;
5098
- }}
5099
- // Custom props
5100
- autocompleteId={view.genomePositionSearchBox.autocompleteId}
5101
- autocompleteServer={
5102
- view.genomePositionSearchBox.autocompleteServer
5103
- }
5104
- chromInfoId={view.genomePositionSearchBox.chromInfoId}
5105
- chromInfoPath={view.genomePositionSearchBox.chromInfoPath}
5106
- chromInfoServer={view.genomePositionSearchBox.chromInfoServer}
5107
- hideAvailableAssemblies={
5108
- view.genomePositionSearchBox.hideAvailableAssemblies
5109
- }
5110
- isFocused={isFocused}
5111
- // the chromInfoId is either specified in the viewconfig or guessed based on
5112
- // the visible tracks (see createGenomePositionSearchBoxEntry)
5113
- onFocus={onFocus}
5114
- onGeneSearch={this.geneSearchHandler.bind(this)}
5115
- onSelectedAssemblyChanged={(x, y, server) =>
5116
- this.handleSelectedAssemblyChanged(view.uid, x, y, server)
5117
- }
5118
- registerViewportChangedListener={(listener) =>
5119
- this.addScalesChangedListener(view.uid, view.uid, listener)
5120
- }
5121
- removeViewportChangedListener={() =>
5122
- this.removeScalesChangedListener(view.uid, view.uid)
5123
- }
5124
- setCenters={(centerX, centerY, k, animateTime) =>
5125
- this.setCenters[view.uid](
5126
- centerX,
5127
- centerY,
5128
- k,
5129
- false,
5130
- animateTime,
5131
- )
5132
- }
5133
- trackSourceServers={this.state.viewConfig.trackSourceServers}
5134
- twoD={
5135
- !!(
5136
- view.tracks.left.length ||
5137
- view.tracks.right.length ||
5138
- view.tracks.center.length
5139
- )
5140
- }
5141
- />
5142
- );
5143
- };
5153
+ const looseTracks = positionedTracksToAllTracks(view.tracks);
5154
+ const annotationTracks = looseTracks.filter(
5155
+ (x) =>
5156
+ x.type === 'horizontal-gene-annotations' ||
5157
+ x.type === 'vertical-gene-annotations' ||
5158
+ x.type === 'gene-annotations',
5159
+ );
5160
+ const chromSizesTracks = looseTracks.filter(
5161
+ (x) =>
5162
+ x.type === 'horizontal-chromosome-labels' ||
5163
+ x.type === 'vertical-chromosome-labels' ||
5164
+ x.type === 'chromosome-labels',
5165
+ );
5144
5166
 
5167
+ const getGenomePositionSearchBox = (isFocused, onFocus) => (
5168
+ <GenomePositionSearchBox
5169
+ // Reserved props
5170
+ key={`gpsb${view.uid}`}
5171
+ ref={(c) => {
5172
+ this.genomePositionSearchBoxes[view.uid] = c;
5173
+ }}
5174
+ // Custom props
5175
+ autocompleteId={
5176
+ annotationTracks.length === 1
5177
+ ? annotationTracks[0].tilesetUid
5178
+ : null
5179
+ }
5180
+ autocompleteServer={
5181
+ annotationTracks.length === 1 ? annotationTracks[0].server : null
5182
+ }
5183
+ chromInfoId={
5184
+ chromSizesTracks.length ? chromSizesTracks[0].tilesetUid : null
5185
+ }
5186
+ chromInfoServer={
5187
+ chromSizesTracks.length ? chromSizesTracks[0].server : null
5188
+ }
5189
+ isFocused={isFocused}
5190
+ // the chromInfoId is either specified in the viewconfig or guessed based on
5191
+ // the visible tracks (see createGenomePositionSearchBoxEntry)
5192
+ onFocus={onFocus}
5193
+ registerViewportChangedListener={(listener) =>
5194
+ this.addScalesChangedListener(view.uid, view.uid, listener)
5195
+ }
5196
+ removeViewportChangedListener={() =>
5197
+ this.removeScalesChangedListener(view.uid, view.uid)
5198
+ }
5199
+ setCenters={(centerX, centerY, k, animateTime) =>
5200
+ this.setCenters[view.uid](centerX, centerY, k, false, animateTime)
5201
+ }
5202
+ trackSourceServers={this.state.viewConfig.trackSourceServers}
5203
+ twoD={true}
5204
+ error={
5205
+ (chromSizesTracks.length === 0 &&
5206
+ 'no chromosome track present') ||
5207
+ (chromSizesTracks.length >= 2 &&
5208
+ 'multiple chromosome tracks present') ||
5209
+ (annotationTracks.length >= 2 &&
5210
+ 'multiple annotation tracks present')
5211
+ }
5212
+ />
5213
+ );
5145
5214
  const multiTrackHeader =
5146
5215
  this.isEditable() &&
5147
5216
  !this.isViewHeaderDisabled() &&
@@ -5151,10 +5220,7 @@ class HiGlassComponent extends React.Component {
5151
5220
  this.viewHeaders[view.uid] = c;
5152
5221
  }}
5153
5222
  getGenomePositionSearchBox={getGenomePositionSearchBox}
5154
- isGenomePositionSearchBoxVisible={
5155
- view.genomePositionSearchBox &&
5156
- view.genomePositionSearchBox.visible
5157
- }
5223
+ isGenomePositionSearchBoxVisible={true}
5158
5224
  mouseTool={this.state.mouseTool}
5159
5225
  onAddView={() => this.handleAddView(view)}
5160
5226
  onClearView={() => this.handleClearView(view.uid)}
@@ -5194,8 +5260,8 @@ class HiGlassComponent extends React.Component {
5194
5260
  onTogglePositionSearchBox={this.handleTogglePositionSearchBox.bind(
5195
5261
  this,
5196
5262
  )}
5197
- onTrackPositionChosen={(position) =>
5198
- this.handleTrackPositionChosen(view.uid, position)
5263
+ onTrackPositionChosen={(position, extent) =>
5264
+ this.handleTrackPositionChosen(view.uid, position, extent)
5199
5265
  }
5200
5266
  onUnlockLocation={(uid) =>
5201
5267
  this.handleUnlock(uid, this.locationLocks)
@@ -5229,16 +5295,17 @@ class HiGlassComponent extends React.Component {
5229
5295
  ) : null;
5230
5296
 
5231
5297
  return (
5232
- <div
5233
- key={view.uid}
5234
- ref={(c) => {
5235
- this.tiledAreasDivs[view.uid] = c;
5236
- }}
5237
- className={styles['tiled-area']}
5238
- >
5239
- {multiTrackHeader}
5240
- {tiledPlot}
5241
- {overlay}
5298
+ <div key={view.uid}>
5299
+ <div
5300
+ ref={(c) => {
5301
+ this.tiledAreasDivs[view.uid] = c;
5302
+ }}
5303
+ className={styles['tiled-area']}
5304
+ >
5305
+ {multiTrackHeader}
5306
+ {tiledPlot}
5307
+ {overlay}
5308
+ </div>
5242
5309
  </div>
5243
5310
  );
5244
5311
  });
@@ -5307,14 +5374,13 @@ class HiGlassComponent extends React.Component {
5307
5374
  return (
5308
5375
  <div
5309
5376
  key={this.uid}
5310
- ref={(c) => {
5311
- this.topDiv = c;
5312
- }}
5313
- className={clsx('higlass', {
5314
- 'higlass-dark-theme': this.theme === THEME_DARK,
5315
- 'higlass-container-overflow':
5316
- this.props.options.sizeMode === SIZE_MODE_OVERFLOW ||
5317
- this.props.options.sizeMode === SIZE_MODE_SCROLL,
5377
+ ref={this.topDivRef}
5378
+ className={clsx('higlass', styles.higlass, {
5379
+ [styles['higlass-dark-theme']]: this.theme === THEME_DARK,
5380
+ [styles['higlass-container-overflow']]:
5381
+ this.sizeMode === SIZE_MODE_OVERFLOW ||
5382
+ this.sizeMode === SIZE_MODE_BOUNDED_OVERFLOW ||
5383
+ this.sizeMode === SIZE_MODE_SCROLL,
5318
5384
  })}
5319
5385
  onMouseLeave={this.onMouseLeaveHandlerBound}
5320
5386
  onMouseMove={this.mouseMoveHandlerBound}
@@ -5322,55 +5388,60 @@ class HiGlassComponent extends React.Component {
5322
5388
  <PubSubProvider value={this.pubSub}>
5323
5389
  <ModalProvider value={this.modal}>
5324
5390
  <ThemeProvider value={this.theme}>
5325
- {this.state.modal}
5326
- <canvas
5327
- key={this.uid}
5328
- ref={(c) => {
5329
- this.canvasElement = c;
5330
- }}
5331
- onClick={this.canvasClickHandlerBound}
5332
- className={styles['higlass-canvas']}
5333
- />
5334
- <div
5335
- ref={(c) => {
5336
- this.scrollContainer = c;
5337
- }}
5338
- className={clsx('higlass-scroll-container', {
5339
- 'higlass-scroll-container-overflow':
5340
- this.props.options.sizeMode === SIZE_MODE_OVERFLOW,
5341
- 'higlass-scroll-container-scroll':
5342
- this.props.options.sizeMode === SIZE_MODE_SCROLL,
5343
- })}
5344
- onScroll={this.onScrollHandlerBound}
5391
+ <HiGlassComponentContext.Provider
5392
+ value={{ viewUidToName: this.viewUidToNameBound }}
5345
5393
  >
5394
+ {this.state.modal}
5395
+ <canvas
5396
+ key={this.uid}
5397
+ ref={(c) => {
5398
+ this.canvasElement = c;
5399
+ }}
5400
+ onClick={this.canvasClickHandlerBound}
5401
+ className={styles['higlass-canvas']}
5402
+ />
5346
5403
  <div
5347
5404
  ref={(c) => {
5348
- this.divDrawingSurface = c;
5405
+ this.scrollContainer = c;
5349
5406
  }}
5350
- className={clsx(
5351
- 'higlass-drawing-surface',
5352
- styles['higlass-drawing-surface'],
5353
- )}
5407
+ className={clsx('higlass-scroll-container', {
5408
+ [styles['higlass-scroll-container-overflow']]:
5409
+ this.sizeMode === SIZE_MODE_OVERFLOW ||
5410
+ this.sizeMode === SIZE_MODE_BOUNDED_OVERFLOW,
5411
+ [styles['higlass-scroll-container-scroll']]:
5412
+ this.sizeMode === SIZE_MODE_SCROLL,
5413
+ })}
5414
+ onScroll={this.onScrollHandlerBound}
5354
5415
  >
5355
- {gridLayout}
5416
+ <div
5417
+ ref={(c) => {
5418
+ this.divDrawingSurface = c;
5419
+ }}
5420
+ className={clsx(
5421
+ 'higlass-drawing-surface',
5422
+ styles['higlass-drawing-surface'],
5423
+ )}
5424
+ >
5425
+ {gridLayout}
5426
+ </div>
5427
+ <svg
5428
+ ref={(c) => {
5429
+ this.svgElement = c;
5430
+ }}
5431
+ style={{
5432
+ // inline the styles so they aren't overriden by other css
5433
+ // on the web page
5434
+ position: 'absolute',
5435
+ width: '100%',
5436
+ height: '100%',
5437
+ left: 0,
5438
+ top: 0,
5439
+ pointerEvents: 'none',
5440
+ }}
5441
+ className={styles['higlass-svg']}
5442
+ />
5356
5443
  </div>
5357
- <svg
5358
- ref={(c) => {
5359
- this.svgElement = c;
5360
- }}
5361
- className={styles['higlass-svg']}
5362
- style={{
5363
- // inline the styles so they aren't overriden by other css
5364
- // on the web page
5365
- position: 'absolute',
5366
- width: '100%',
5367
- height: '100%',
5368
- left: 0,
5369
- top: 0,
5370
- pointerEvents: 'none',
5371
- }}
5372
- />
5373
- </div>
5444
+ </HiGlassComponentContext.Provider>
5374
5445
  </ThemeProvider>
5375
5446
  </ModalProvider>
5376
5447
  </PubSubProvider>