@genome-spy/core 0.64.0 → 0.66.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/dist/bundle/{index-CCJIjehY.js → AbortablePromiseCache-CcuMrnn7.js} +22 -91
  2. package/dist/bundle/browser-BRemItdO.js +138 -0
  3. package/dist/bundle/index-BatuyGAI.js +271 -0
  4. package/dist/bundle/index-ByuE8dvu.js +332 -0
  5. package/dist/bundle/index-Cq3QFUxX.js +1781 -0
  6. package/dist/bundle/{index-C08YCM2T.js → index-D-w7Mmt9.js} +246 -126
  7. package/dist/bundle/index-D28m8tSW.js +1607 -0
  8. package/dist/bundle/index-D74H8TTz.js +508 -0
  9. package/dist/bundle/index-DbJ0oeYM.js +631 -0
  10. package/dist/bundle/index.es.js +15034 -13842
  11. package/dist/bundle/index.js +223 -237
  12. package/dist/bundle/inflate-GtwLkvSP.js +1048 -0
  13. package/dist/bundle/unzip-NywezaRR.js +1492 -0
  14. package/dist/schema.json +22 -4
  15. package/dist/src/config/scaleDefaults.d.ts +8 -0
  16. package/dist/src/config/scaleDefaults.d.ts.map +1 -0
  17. package/dist/src/config/scaleDefaults.js +45 -0
  18. package/dist/src/data/collector.d.ts +7 -2
  19. package/dist/src/data/collector.d.ts.map +1 -1
  20. package/dist/src/data/collector.js +13 -2
  21. package/dist/src/data/dataFlow.d.ts +20 -42
  22. package/dist/src/data/dataFlow.d.ts.map +1 -1
  23. package/dist/src/data/dataFlow.js +57 -80
  24. package/dist/src/data/flowHandle.d.ts +15 -0
  25. package/dist/src/data/flowHandle.d.ts.map +1 -0
  26. package/dist/src/data/flowHandle.js +13 -0
  27. package/dist/src/data/flowInit.d.ts +85 -0
  28. package/dist/src/data/flowInit.d.ts.map +1 -0
  29. package/dist/src/data/flowInit.js +238 -0
  30. package/dist/src/data/flowInit.test.d.ts +2 -0
  31. package/dist/src/data/flowInit.test.d.ts.map +1 -0
  32. package/dist/src/data/flowOptimizer.d.ts +6 -4
  33. package/dist/src/data/flowOptimizer.d.ts.map +1 -1
  34. package/dist/src/data/flowOptimizer.js +29 -14
  35. package/dist/src/data/sources/lazy/axisTickSource.js +1 -1
  36. package/dist/src/data/sources/lazy/bamSource.js +1 -1
  37. package/dist/src/data/sources/lazy/bigBedSource.js +1 -1
  38. package/dist/src/data/sources/lazy/bigWigSource.js +1 -1
  39. package/dist/src/data/sources/lazy/gff3Source.d.ts +2 -6
  40. package/dist/src/data/sources/lazy/gff3Source.d.ts.map +1 -1
  41. package/dist/src/data/sources/lazy/gff3Source.js +4 -8
  42. package/dist/src/data/sources/lazy/indexedFastaSource.d.ts.map +1 -1
  43. package/dist/src/data/sources/lazy/indexedFastaSource.js +17 -17
  44. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts +1 -1
  45. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts.map +1 -1
  46. package/dist/src/data/sources/lazy/singleAxisLazySource.js +10 -3
  47. package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts.map +1 -1
  48. package/dist/src/data/sources/lazy/singleAxisWindowedSource.js +5 -1
  49. package/dist/src/data/sources/lazy/tabixSource.js +1 -1
  50. package/dist/src/data/transforms/filterScoredLabels.d.ts +1 -1
  51. package/dist/src/data/transforms/filterScoredLabels.d.ts.map +1 -1
  52. package/dist/src/data/transforms/filterScoredLabels.js +1 -1
  53. package/dist/src/data/transforms/linearizeGenomicCoordinate.d.ts.map +1 -1
  54. package/dist/src/data/transforms/linearizeGenomicCoordinate.js +2 -1
  55. package/dist/src/encoder/encoder.d.ts +1 -1
  56. package/dist/src/encoder/encoder.d.ts.map +1 -1
  57. package/dist/src/encoder/encoder.js +1 -1
  58. package/dist/src/genome/scaleLocus.d.ts +39 -0
  59. package/dist/src/genome/scaleLocus.d.ts.map +1 -1
  60. package/dist/src/genome/scaleLocus.js +76 -0
  61. package/dist/src/genomeSpy/canvasExport.d.ts +19 -0
  62. package/dist/src/genomeSpy/canvasExport.d.ts.map +1 -0
  63. package/dist/src/genomeSpy/canvasExport.js +66 -0
  64. package/dist/src/genomeSpy/containerUi.d.ts +17 -0
  65. package/dist/src/genomeSpy/containerUi.d.ts.map +1 -0
  66. package/dist/src/genomeSpy/containerUi.js +78 -0
  67. package/dist/src/genomeSpy/eventListenerRegistry.d.ts +19 -0
  68. package/dist/src/genomeSpy/eventListenerRegistry.d.ts.map +1 -0
  69. package/dist/src/genomeSpy/eventListenerRegistry.js +38 -0
  70. package/dist/src/genomeSpy/inputBindingManager.d.ts +14 -0
  71. package/dist/src/genomeSpy/inputBindingManager.d.ts.map +1 -0
  72. package/dist/src/genomeSpy/inputBindingManager.js +63 -0
  73. package/dist/src/genomeSpy/interactionController.d.ts +40 -0
  74. package/dist/src/genomeSpy/interactionController.d.ts.map +1 -0
  75. package/dist/src/genomeSpy/interactionController.js +371 -0
  76. package/dist/src/genomeSpy/keyboardListenerManager.d.ts +10 -0
  77. package/dist/src/genomeSpy/keyboardListenerManager.d.ts.map +1 -0
  78. package/dist/src/genomeSpy/keyboardListenerManager.js +31 -0
  79. package/dist/src/genomeSpy/loadingIndicatorManager.d.ts +15 -0
  80. package/dist/src/genomeSpy/loadingIndicatorManager.d.ts.map +1 -0
  81. package/dist/src/genomeSpy/loadingIndicatorManager.js +92 -0
  82. package/dist/src/genomeSpy/renderCoordinator.d.ts +22 -0
  83. package/dist/src/genomeSpy/renderCoordinator.d.ts.map +1 -0
  84. package/dist/src/genomeSpy/renderCoordinator.js +118 -0
  85. package/dist/src/genomeSpy/viewContextFactory.d.ts +18 -0
  86. package/dist/src/genomeSpy/viewContextFactory.d.ts.map +1 -0
  87. package/dist/src/genomeSpy/viewContextFactory.js +79 -0
  88. package/dist/src/genomeSpy/viewDataInit.d.ts +12 -0
  89. package/dist/src/genomeSpy/viewDataInit.d.ts.map +1 -0
  90. package/dist/src/genomeSpy/viewDataInit.js +41 -0
  91. package/dist/src/genomeSpy/viewHierarchyConfig.d.ts +14 -0
  92. package/dist/src/genomeSpy/viewHierarchyConfig.d.ts.map +1 -0
  93. package/dist/src/genomeSpy/viewHierarchyConfig.js +24 -0
  94. package/dist/src/genomeSpy/viewHighlight.d.ts +5 -0
  95. package/dist/src/genomeSpy/viewHighlight.d.ts.map +1 -0
  96. package/dist/src/genomeSpy/viewHighlight.js +30 -0
  97. package/dist/src/genomeSpy.d.ts +17 -72
  98. package/dist/src/genomeSpy.d.ts.map +1 -1
  99. package/dist/src/genomeSpy.js +180 -789
  100. package/dist/src/gl/glslScaleGenerator.d.ts +1 -1
  101. package/dist/src/gl/webGLHelper.d.ts +2 -2
  102. package/dist/src/gl/webGLHelper.d.ts.map +1 -1
  103. package/dist/src/gl/webGLHelper.js +4 -4
  104. package/dist/src/index.d.ts.map +1 -1
  105. package/dist/src/index.js +2 -12
  106. package/dist/src/marks/mark.d.ts +1 -0
  107. package/dist/src/marks/mark.d.ts.map +1 -1
  108. package/dist/src/marks/mark.js +26 -3
  109. package/dist/src/{view → scales}/axisResolution.d.ts +12 -14
  110. package/dist/src/scales/axisResolution.d.ts.map +1 -0
  111. package/dist/src/{view → scales}/axisResolution.js +38 -12
  112. package/dist/src/scales/axisResolution.test.d.ts.map +1 -0
  113. package/dist/src/scales/scaleDomainAggregator.d.ts +57 -0
  114. package/dist/src/scales/scaleDomainAggregator.d.ts.map +1 -0
  115. package/dist/src/scales/scaleDomainAggregator.js +162 -0
  116. package/dist/src/scales/scaleDomainAggregator.test.d.ts +2 -0
  117. package/dist/src/scales/scaleDomainAggregator.test.d.ts.map +1 -0
  118. package/dist/src/scales/scaleInstanceManager.d.ts +40 -0
  119. package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -0
  120. package/dist/src/scales/scaleInstanceManager.js +313 -0
  121. package/dist/src/scales/scaleInstanceManager.test.d.ts +2 -0
  122. package/dist/src/scales/scaleInstanceManager.test.d.ts.map +1 -0
  123. package/dist/src/scales/scaleInteractionController.d.ts +73 -0
  124. package/dist/src/scales/scaleInteractionController.d.ts.map +1 -0
  125. package/dist/src/scales/scaleInteractionController.js +336 -0
  126. package/dist/src/scales/scaleInteractionController.test.d.ts +2 -0
  127. package/dist/src/scales/scaleInteractionController.test.d.ts.map +1 -0
  128. package/dist/src/scales/scalePropsResolver.d.ts +23 -0
  129. package/dist/src/scales/scalePropsResolver.d.ts.map +1 -0
  130. package/dist/src/scales/scalePropsResolver.js +74 -0
  131. package/dist/src/{view → scales}/scaleResolution.d.ts +53 -31
  132. package/dist/src/scales/scaleResolution.d.ts.map +1 -0
  133. package/dist/src/scales/scaleResolution.js +658 -0
  134. package/dist/src/scales/scaleResolution.test.d.ts.map +1 -0
  135. package/dist/src/scales/scaleResolutionConstants.d.ts +6 -0
  136. package/dist/src/scales/scaleResolutionConstants.d.ts.map +1 -0
  137. package/dist/src/scales/scaleResolutionConstants.js +5 -0
  138. package/dist/src/scales/scaleRules.d.ts +16 -0
  139. package/dist/src/scales/scaleRules.d.ts.map +1 -0
  140. package/dist/src/scales/scaleRules.js +103 -0
  141. package/dist/src/scales/scaleRules.test.d.ts +2 -0
  142. package/dist/src/scales/scaleRules.test.d.ts.map +1 -0
  143. package/dist/src/spec/channel.d.ts +13 -18
  144. package/dist/src/spec/sampleView.d.ts +3 -2
  145. package/dist/src/spec/scale.d.ts +6 -0
  146. package/dist/src/types/embedApi.d.ts +5 -0
  147. package/dist/src/types/scaleResolutionApi.d.ts +1 -1
  148. package/dist/src/types/viewContext.d.ts +1 -1
  149. package/dist/src/view/concatView.d.ts +18 -0
  150. package/dist/src/view/concatView.d.ts.map +1 -1
  151. package/dist/src/view/concatView.js +73 -0
  152. package/dist/src/view/concatView.test.d.ts +2 -0
  153. package/dist/src/view/concatView.test.d.ts.map +1 -0
  154. package/dist/src/view/containerMutationHelper.d.ts +74 -0
  155. package/dist/src/view/containerMutationHelper.d.ts.map +1 -0
  156. package/dist/src/view/containerMutationHelper.js +114 -0
  157. package/dist/src/view/containerView.d.ts +0 -7
  158. package/dist/src/view/containerView.d.ts.map +1 -1
  159. package/dist/src/view/containerView.js +0 -10
  160. package/dist/src/view/facetView.d.ts.map +1 -1
  161. package/dist/src/view/facetView.js +0 -14
  162. package/dist/src/view/flowBuilder.d.ts +2 -2
  163. package/dist/src/view/flowBuilder.d.ts.map +1 -1
  164. package/dist/src/view/flowBuilder.js +21 -4
  165. package/dist/src/view/gridView/gridChild.d.ts +11 -0
  166. package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
  167. package/dist/src/view/gridView/gridChild.js +32 -6
  168. package/dist/src/view/gridView/gridView.d.ts +39 -1
  169. package/dist/src/view/gridView/gridView.d.ts.map +1 -1
  170. package/dist/src/view/gridView/gridView.js +113 -42
  171. package/dist/src/view/gridView/gridView.test.d.ts +2 -0
  172. package/dist/src/view/gridView/gridView.test.d.ts.map +1 -0
  173. package/dist/src/view/gridView/scrollbar.d.ts +39 -8
  174. package/dist/src/view/gridView/scrollbar.d.ts.map +1 -1
  175. package/dist/src/view/gridView/scrollbar.js +184 -69
  176. package/dist/src/view/gridView/selectionRect.d.ts +8 -4
  177. package/dist/src/view/gridView/selectionRect.d.ts.map +1 -1
  178. package/dist/src/view/gridView/selectionRect.js +28 -3
  179. package/dist/src/view/gridView/selectionRect.test.d.ts +2 -0
  180. package/dist/src/view/gridView/selectionRect.test.d.ts.map +1 -0
  181. package/dist/src/view/layerView.d.ts +14 -0
  182. package/dist/src/view/layerView.d.ts.map +1 -1
  183. package/dist/src/view/layerView.js +66 -0
  184. package/dist/src/view/layerView.test.d.ts +2 -0
  185. package/dist/src/view/layerView.test.d.ts.map +1 -0
  186. package/dist/src/view/paramMediator.d.ts +2 -1
  187. package/dist/src/view/paramMediator.d.ts.map +1 -1
  188. package/dist/src/view/paramMediator.js +13 -1
  189. package/dist/src/view/testUtils.d.ts.map +1 -1
  190. package/dist/src/view/testUtils.js +18 -5
  191. package/dist/src/view/unitView.d.ts.map +1 -1
  192. package/dist/src/view/unitView.js +52 -12
  193. package/dist/src/view/view.d.ts +23 -7
  194. package/dist/src/view/view.d.ts.map +1 -1
  195. package/dist/src/view/view.js +61 -5
  196. package/dist/src/view/viewDispose.test.d.ts +2 -0
  197. package/dist/src/view/viewDispose.test.d.ts.map +1 -0
  198. package/dist/src/view/viewUtils.d.ts +4 -4
  199. package/dist/src/view/viewUtils.d.ts.map +1 -1
  200. package/dist/src/view/viewUtils.js +19 -15
  201. package/dist/src/view/viewUtils.test.d.ts +2 -0
  202. package/dist/src/view/viewUtils.test.d.ts.map +1 -0
  203. package/package.json +10 -10
  204. package/dist/bundle/__vite-browser-external-C--ziKoh.js +0 -8
  205. package/dist/bundle/_commonjsHelpers-DjF3Plf2.js +0 -26
  206. package/dist/bundle/index-5ajWdKly.js +0 -1319
  207. package/dist/bundle/index-B03-Om4z.js +0 -274
  208. package/dist/bundle/index-BftNdA0O.js +0 -27
  209. package/dist/bundle/index-Bg7C4Xat.js +0 -2750
  210. package/dist/bundle/index-C3QR8Lv6.js +0 -2131
  211. package/dist/bundle/index-DTcHjAHp.js +0 -505
  212. package/dist/bundle/index-DnIkxb0L.js +0 -1025
  213. package/dist/bundle/index-Ww3TAo6_.js +0 -71
  214. package/dist/bundle/index-g8iXgW0W.js +0 -651
  215. package/dist/bundle/long-B-FASCSo.js +0 -2387
  216. package/dist/bundle/remoteFile-BuaqFGWk.js +0 -94
  217. package/dist/src/data/collector.test.js +0 -138
  218. package/dist/src/data/dataFlow.test.js +0 -5
  219. package/dist/src/data/flow.test.js +0 -81
  220. package/dist/src/data/flowNode.test.js +0 -50
  221. package/dist/src/data/flowOptimizer.test.js +0 -204
  222. package/dist/src/data/formats/fasta.test.js +0 -27
  223. package/dist/src/data/sources/inlineSource.test.js +0 -63
  224. package/dist/src/data/sources/sequenceSource.test.js +0 -81
  225. package/dist/src/data/transforms/aggregate.test.js +0 -134
  226. package/dist/src/data/transforms/clone.test.js +0 -11
  227. package/dist/src/data/transforms/coverage.test.js +0 -238
  228. package/dist/src/data/transforms/filter.test.js +0 -20
  229. package/dist/src/data/transforms/flatten.test.js +0 -96
  230. package/dist/src/data/transforms/flattenDelimited.test.js +0 -90
  231. package/dist/src/data/transforms/flattenSequence.test.js +0 -34
  232. package/dist/src/data/transforms/formula.test.js +0 -25
  233. package/dist/src/data/transforms/identifier.test.js +0 -92
  234. package/dist/src/data/transforms/pileup.test.js +0 -70
  235. package/dist/src/data/transforms/project.test.js +0 -32
  236. package/dist/src/data/transforms/regexExtract.test.js +0 -70
  237. package/dist/src/data/transforms/regexFold.test.js +0 -201
  238. package/dist/src/data/transforms/sample.test.js +0 -38
  239. package/dist/src/data/transforms/stack.test.js +0 -91
  240. package/dist/src/encoder/accessor.test.js +0 -162
  241. package/dist/src/encoder/encoder.test.js +0 -105
  242. package/dist/src/genome/genome.test.js +0 -268
  243. package/dist/src/genome/genomes.test.js +0 -8
  244. package/dist/src/genome/scaleIndex.test.js +0 -78
  245. package/dist/src/genome/scaleLocus.test.js +0 -4
  246. package/dist/src/scale/scale.test.js +0 -326
  247. package/dist/src/scale/ticks.test.js +0 -46
  248. package/dist/src/selection/selection.test.js +0 -14
  249. package/dist/src/utils/addBaseUrl.test.js +0 -30
  250. package/dist/src/utils/binnedIndex.test.js +0 -201
  251. package/dist/src/utils/cloner.test.js +0 -35
  252. package/dist/src/utils/coalesce.test.js +0 -16
  253. package/dist/src/utils/concatIterables.test.js +0 -8
  254. package/dist/src/utils/domainArray.test.js +0 -130
  255. package/dist/src/utils/indexer.test.js +0 -49
  256. package/dist/src/utils/interactionEvent.test.js +0 -35
  257. package/dist/src/utils/iterateNestedMaps.test.js +0 -33
  258. package/dist/src/utils/kWayMerge.test.js +0 -30
  259. package/dist/src/utils/mergeObjects.test.js +0 -42
  260. package/dist/src/utils/numberExtractor.test.js +0 -6
  261. package/dist/src/utils/propertyCacher.test.js +0 -89
  262. package/dist/src/utils/propertyCoalescer.test.js +0 -25
  263. package/dist/src/utils/radixSort.test.js +0 -51
  264. package/dist/src/utils/reservationMap.test.js +0 -20
  265. package/dist/src/utils/ringBuffer.test.js +0 -39
  266. package/dist/src/utils/topK.test.js +0 -54
  267. package/dist/src/utils/trees.test.js +0 -135
  268. package/dist/src/utils/url.test.js +0 -28
  269. package/dist/src/utils/variableTools.test.js +0 -13
  270. package/dist/src/view/axisResolution.d.ts.map +0 -1
  271. package/dist/src/view/axisResolution.test.d.ts.map +0 -1
  272. package/dist/src/view/axisResolution.test.js +0 -206
  273. package/dist/src/view/flowBuilder.test.js +0 -125
  274. package/dist/src/view/layout/flexLayout.test.js +0 -323
  275. package/dist/src/view/layout/grid.test.js +0 -71
  276. package/dist/src/view/layout/rectangle.test.js +0 -192
  277. package/dist/src/view/paramMediator.test.js +0 -260
  278. package/dist/src/view/scaleResolution.d.ts.map +0 -1
  279. package/dist/src/view/scaleResolution.js +0 -1049
  280. package/dist/src/view/scaleResolution.test.d.ts.map +0 -1
  281. package/dist/src/view/scaleResolution.test.js +0 -645
  282. package/dist/src/view/view.test.js +0 -245
  283. package/dist/src/view/viewFactory.test.js +0 -25
  284. /package/dist/src/{view → scales}/axisResolution.test.d.ts +0 -0
  285. /package/dist/src/{view → scales}/scaleResolution.test.d.ts +0 -0
@@ -0,0 +1,371 @@
1
+ import UnitView from "../view/unitView.js";
2
+ import { VISIT_STOP } from "../view/view.js";
3
+ import { readPickingPixel } from "../gl/webGLHelper.js";
4
+ import InteractionEvent from "../utils/interactionEvent.js";
5
+ import Inertia, { makeEventTemplate } from "../utils/inertia.js";
6
+ import Point from "../view/layout/point.js";
7
+ import { isStillZooming } from "../view/zoom.js";
8
+
9
+ export default class InteractionController {
10
+ /** @type {import("../view/view.js").default} */
11
+ #viewRoot;
12
+ /** @type {import("../gl/webGLHelper.js").default} */
13
+ #glHelper;
14
+ /** @type {import("../utils/ui/tooltip.js").default} */
15
+ #tooltip;
16
+ /** @type {import("../utils/animator.js").default} */
17
+ #animator;
18
+ /** @type {(type: string, event: any) => void} */
19
+ #emitEvent;
20
+ /** @type {Record<string, import("../tooltip/tooltipHandler.js").TooltipHandler>} */
21
+ #tooltipHandlers;
22
+ /** @type {() => void} */
23
+ #renderPickingFramebuffer;
24
+ /** @type {() => number} */
25
+ #getDevicePixelRatio;
26
+ /**
27
+ * @type {{ mark: import("../marks/mark.js").default, datum: import("../data/flowNode.js").Datum, uniqueId: number }}
28
+ */
29
+ #currentHover;
30
+ /** @type {Inertia} */
31
+ #wheelInertia;
32
+ /** @type {Point} */
33
+ #mouseDownCoords;
34
+ /** @type {boolean} */
35
+ #tooltipUpdateRequested;
36
+ /**
37
+ * @param {object} options
38
+ * @param {import("../view/view.js").default} options.viewRoot
39
+ * @param {import("../gl/webGLHelper.js").default} options.glHelper
40
+ * @param {import("../utils/ui/tooltip.js").default} options.tooltip
41
+ * @param {import("../utils/animator.js").default} options.animator
42
+ * @param {(type: string, event: any) => void} options.emitEvent
43
+ * @param {Record<string, import("../tooltip/tooltipHandler.js").TooltipHandler>} options.tooltipHandlers
44
+ * @param {() => void} options.renderPickingFramebuffer
45
+ * @param {() => number} options.getDevicePixelRatio
46
+ */
47
+ constructor({
48
+ viewRoot,
49
+ glHelper,
50
+ tooltip,
51
+ animator,
52
+ emitEvent,
53
+ tooltipHandlers,
54
+ renderPickingFramebuffer,
55
+ getDevicePixelRatio,
56
+ }) {
57
+ this.#viewRoot = viewRoot;
58
+ this.#glHelper = glHelper;
59
+ this.#tooltip = tooltip;
60
+ this.#animator = animator;
61
+ this.#emitEvent = emitEvent;
62
+ this.#tooltipHandlers = tooltipHandlers;
63
+ this.#renderPickingFramebuffer = renderPickingFramebuffer;
64
+ this.#getDevicePixelRatio = getDevicePixelRatio;
65
+
66
+ /**
67
+ * Currently hovered mark and datum
68
+ * @type {{ mark: import("../marks/mark.js").default, datum: import("../data/flowNode.js").Datum, uniqueId: number }}
69
+ */
70
+ this.#currentHover = undefined;
71
+
72
+ this.#wheelInertia = new Inertia(this.#animator);
73
+
74
+ /** @type {Point} */
75
+ this.#mouseDownCoords = undefined;
76
+
77
+ this.#tooltipUpdateRequested = false;
78
+ }
79
+
80
+ getCurrentHover() {
81
+ return this.#currentHover;
82
+ }
83
+
84
+ registerMouseEvents() {
85
+ const canvas = this.#glHelper.canvas;
86
+
87
+ let lastWheelEvent = performance.now();
88
+ let longPressTriggered = false;
89
+
90
+ /** @param {Event} event */
91
+ const listener = (event) => {
92
+ const now = performance.now();
93
+ const wheeling = now - lastWheelEvent < 200;
94
+
95
+ if (event instanceof MouseEvent) {
96
+ const rect = canvas.getBoundingClientRect();
97
+ const point = new Point(
98
+ event.clientX - rect.left - canvas.clientLeft,
99
+ event.clientY - rect.top - canvas.clientTop
100
+ );
101
+
102
+ if (event.type == "mousemove" && !wheeling) {
103
+ this.#tooltip.handleMouseMove(event);
104
+ this.#tooltipUpdateRequested = false;
105
+
106
+ // Disable picking during dragging. Also postpone picking until
107
+ // the user has stopped zooming as reading pixels from the
108
+ // picking buffer is slow and ruins smooth animations.
109
+ if (event.buttons == 0 && !isStillZooming()) {
110
+ this.#renderPickingFramebuffer();
111
+ this.#handlePicking(point.x, point.y);
112
+ }
113
+ }
114
+
115
+ /**
116
+ * @param {MouseEvent} dispatchedEvent
117
+ */
118
+ const dispatchEvent = (dispatchedEvent) => {
119
+ this.#viewRoot.propagateInteractionEvent(
120
+ new InteractionEvent(point, dispatchedEvent)
121
+ );
122
+
123
+ if (!this.#tooltipUpdateRequested) {
124
+ this.#tooltip.clear();
125
+ }
126
+ };
127
+
128
+ if (event.type != "wheel") {
129
+ this.#wheelInertia.cancel();
130
+ }
131
+
132
+ if (
133
+ (event.type == "mousedown" || event.type == "mouseup") &&
134
+ !isStillZooming()
135
+ ) {
136
+ // Actually, only needed when clicking on a mark
137
+ this.#renderPickingFramebuffer();
138
+ } else if (event.type == "wheel") {
139
+ lastWheelEvent = now;
140
+ this.#tooltipUpdateRequested = false;
141
+
142
+ const wheelEvent = /** @type {WheelEvent} */ (event);
143
+
144
+ if (
145
+ Math.abs(wheelEvent.deltaX) >
146
+ Math.abs(wheelEvent.deltaY)
147
+ ) {
148
+ // If the viewport is panned (horizontally) using the wheel (touchpad),
149
+ // the picking buffer becomes stale and needs redrawing. However, we
150
+ // optimize by just clearing the currently hovered item so that snapping
151
+ // doesn't work incorrectly when zooming in/out.
152
+
153
+ // TODO: More robust solution (handle at higher level such as ScaleResolution's zoom method)
154
+ this.#currentHover = null;
155
+
156
+ this.#wheelInertia.cancel();
157
+ } else {
158
+ // Vertical wheeling zooms.
159
+ // We use inertia to generate fake wheel events for smoother zooming
160
+
161
+ const template = makeEventTemplate(wheelEvent);
162
+
163
+ this.#wheelInertia.setMomentum(
164
+ wheelEvent.deltaY * (wheelEvent.deltaMode ? 80 : 1),
165
+ (delta) => {
166
+ const e = new WheelEvent("wheel", {
167
+ ...template,
168
+ deltaMode: 0,
169
+ deltaX: 0,
170
+ deltaY: delta,
171
+ });
172
+ dispatchEvent(e);
173
+ }
174
+ );
175
+
176
+ wheelEvent.preventDefault();
177
+ return;
178
+ }
179
+ }
180
+
181
+ // TODO: Should be handled at the view level, not globally
182
+ if (event.type == "click") {
183
+ if (longPressTriggered) {
184
+ return;
185
+ }
186
+
187
+ const e = this.#currentHover
188
+ ? {
189
+ type: event.type,
190
+ viewPath: this.#currentHover.mark.unitView
191
+ .getLayoutAncestors()
192
+ .map(
193
+ /** @param {import("../view/view.js").default} view */
194
+ (view) => view.name
195
+ )
196
+ .reverse(),
197
+ datum: this.#currentHover.datum,
198
+ }
199
+ : {
200
+ type: event.type,
201
+ viewPath: null,
202
+ datum: null,
203
+ };
204
+
205
+ this.#emitEvent("click", e);
206
+ }
207
+
208
+ if (
209
+ event.type != "click" ||
210
+ // Suppress click events if the mouse has been dragged
211
+ this.#mouseDownCoords?.subtract(Point.fromMouseEvent(event))
212
+ .length < 3
213
+ ) {
214
+ dispatchEvent(event);
215
+ }
216
+ }
217
+ };
218
+
219
+ [
220
+ "mousedown",
221
+ "mouseup",
222
+ "wheel",
223
+ "click",
224
+ "mousemove",
225
+ "gesturechange",
226
+ "contextmenu",
227
+ "dblclick",
228
+ ].forEach((type) => canvas.addEventListener(type, listener));
229
+
230
+ canvas.addEventListener("mousedown", (/** @type {MouseEvent} */ e) => {
231
+ this.#mouseDownCoords = Point.fromMouseEvent(e);
232
+ if (this.#tooltip.sticky) {
233
+ this.#tooltip.sticky = false;
234
+ this.#tooltip.clear();
235
+ // A hack to prevent selection if the tooltip is sticky.
236
+ // Let the tooltip be destickified first.
237
+ longPressTriggered = true;
238
+ } else {
239
+ longPressTriggered = false;
240
+ }
241
+
242
+ const disableTooltip = () => {
243
+ document.addEventListener(
244
+ "mouseup",
245
+ () => this.#tooltip.popEnabledState(),
246
+ { once: true }
247
+ );
248
+ this.#tooltip.pushEnabledState(false);
249
+ };
250
+
251
+ // Opening context menu or using modifier keys disables the tooltip
252
+ if (e.button == 2 || e.shiftKey || e.ctrlKey || e.metaKey) {
253
+ disableTooltip();
254
+ } else if (this.#tooltip.visible) {
255
+ // Make tooltip sticky if the user long-presses
256
+ const timeout = setTimeout(() => {
257
+ longPressTriggered = true;
258
+ this.#tooltip.sticky = true;
259
+ }, 400);
260
+
261
+ const clear = () => clearTimeout(timeout);
262
+ document.addEventListener("mouseup", clear, { once: true });
263
+ document.addEventListener("mousemove", clear, { once: true });
264
+ }
265
+ });
266
+
267
+ // Prevent text selections etc while dragging
268
+ canvas.addEventListener("dragstart", (event) =>
269
+ event.stopPropagation()
270
+ );
271
+
272
+ canvas.addEventListener("mouseout", () => {
273
+ this.#tooltip.clear();
274
+ this.#currentHover = null;
275
+ });
276
+ }
277
+
278
+ /**
279
+ * @param {number} x
280
+ * @param {number} y
281
+ */
282
+ #handlePicking(x, y) {
283
+ const dpr = this.#getDevicePixelRatio();
284
+ const pp = readPickingPixel(
285
+ this.#glHelper.gl,
286
+ this.#glHelper._pickingBufferInfo,
287
+ x * dpr,
288
+ y * dpr
289
+ );
290
+
291
+ const uniqueId = pp[0] | (pp[1] << 8) | (pp[2] << 16) | (pp[3] << 24);
292
+
293
+ if (uniqueId == 0) {
294
+ this.#currentHover = null;
295
+ return;
296
+ }
297
+
298
+ if (uniqueId !== this.#currentHover?.uniqueId) {
299
+ this.#currentHover = null;
300
+ }
301
+
302
+ if (!this.#currentHover) {
303
+ this.#viewRoot.visit((view) => {
304
+ if (view instanceof UnitView) {
305
+ if (
306
+ view.mark.isPickingParticipant() &&
307
+ [...view.facetCoords.values()].some((coords) =>
308
+ coords.containsPoint(x, y)
309
+ )
310
+ ) {
311
+ const datum = view
312
+ .getCollector()
313
+ .findDatumByUniqueId(uniqueId);
314
+ if (datum) {
315
+ this.#currentHover = {
316
+ mark: view.mark,
317
+ datum,
318
+ uniqueId,
319
+ };
320
+ }
321
+ }
322
+ if (this.#currentHover) {
323
+ return VISIT_STOP;
324
+ }
325
+ }
326
+ });
327
+ }
328
+
329
+ if (this.#currentHover) {
330
+ const mark = this.#currentHover.mark;
331
+ this.updateTooltip(this.#currentHover.datum, async (datum) => {
332
+ if (!mark.isPickingParticipant()) {
333
+ return;
334
+ }
335
+
336
+ const tooltipProps = mark.properties.tooltip;
337
+
338
+ if (tooltipProps !== null) {
339
+ const handlerName = tooltipProps?.handler ?? "default";
340
+ const handler = this.#tooltipHandlers[handlerName];
341
+ if (!handler) {
342
+ throw new Error(
343
+ "No such tooltip handler: " + handlerName
344
+ );
345
+ }
346
+
347
+ return handler(datum, mark, tooltipProps?.params);
348
+ }
349
+ });
350
+ }
351
+ }
352
+
353
+ /**
354
+ * This method should be called in a mouseMove handler. If not called, the
355
+ * tooltip will be hidden.
356
+ *
357
+ * @param {T} datum
358
+ * @param {function(T):Promise<string | HTMLElement | import("lit").TemplateResult>} [converter]
359
+ * @template T
360
+ */
361
+ updateTooltip(datum, converter) {
362
+ if (!this.#tooltipUpdateRequested || !datum) {
363
+ this.#tooltip.updateWithDatum(datum, converter);
364
+ this.#tooltipUpdateRequested = true;
365
+ } else {
366
+ throw new Error(
367
+ "Tooltip has already been updated! Duplicate event handler?"
368
+ );
369
+ }
370
+ }
371
+ }
@@ -0,0 +1,10 @@
1
+ export default class KeyboardListenerManager {
2
+ /**
3
+ * @param {"keydown" | "keyup"} type
4
+ * @param {(event: KeyboardEvent) => void} listener
5
+ */
6
+ add(type: "keydown" | "keyup", listener: (event: KeyboardEvent) => void): void;
7
+ removeAll(): void;
8
+ #private;
9
+ }
10
+ //# sourceMappingURL=keyboardListenerManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboardListenerManager.d.ts","sourceRoot":"","sources":["../../../src/genomeSpy/keyboardListenerManager.js"],"names":[],"mappings":"AAAA;IAQI;;;OAGG;IACH,UAHW,SAAS,GAAG,OAAO,YACnB,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,QAUxC;IAED,kBAOC;;CACJ"}
@@ -0,0 +1,31 @@
1
+ export default class KeyboardListenerManager {
2
+ /** @type {Map<string, (function(KeyboardEvent):void)[]>} */
3
+ #listeners;
4
+
5
+ constructor() {
6
+ this.#listeners = new Map();
7
+ }
8
+
9
+ /**
10
+ * @param {"keydown" | "keyup"} type
11
+ * @param {(event: KeyboardEvent) => void} listener
12
+ */
13
+ add(type, listener) {
14
+ document.addEventListener(type, listener);
15
+ let listeners = this.#listeners.get(type);
16
+ if (!listeners) {
17
+ listeners = [];
18
+ this.#listeners.set(type, listeners);
19
+ }
20
+ listeners.push(listener);
21
+ }
22
+
23
+ removeAll() {
24
+ for (const [type, listeners] of this.#listeners) {
25
+ for (const listener of listeners) {
26
+ document.removeEventListener(type, listener);
27
+ }
28
+ }
29
+ this.#listeners.clear();
30
+ }
31
+ }
@@ -0,0 +1,15 @@
1
+ export default class LoadingIndicatorManager {
2
+ /**
3
+ * @param {HTMLElement} loadingIndicatorsElement
4
+ */
5
+ constructor(loadingIndicatorsElement: HTMLElement);
6
+ /**
7
+ * @param {import("../view/view.js").default} view
8
+ * @param {import("../types/viewContext.js").DataLoadingStatus} status
9
+ * @param {string} [detail]
10
+ */
11
+ setDataLoadingStatus(view: import("../view/view.js").default, status: import("../types/viewContext.js").DataLoadingStatus, detail?: string): void;
12
+ updateLayout(): void;
13
+ #private;
14
+ }
15
+ //# sourceMappingURL=loadingIndicatorManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadingIndicatorManager.d.ts","sourceRoot":"","sources":["../../../src/genomeSpy/loadingIndicatorManager.js"],"names":[],"mappings":"AAIA;IAQI;;OAEG;IACH,sCAFW,WAAW,EASrB;IAED;;;;OAIG;IACH,2BAJW,OAAO,iBAAiB,EAAE,OAAO,UACjC,OAAO,yBAAyB,EAAE,iBAAiB,WACnD,MAAM,QAKhB;IAED,qBAwDC;;CACJ"}
@@ -0,0 +1,92 @@
1
+ import { html, nothing, render } from "lit";
2
+ import { styleMap } from "lit/directives/style-map.js";
3
+ import SPINNER from "../img/90-ring-with-bg.svg";
4
+
5
+ export default class LoadingIndicatorManager {
6
+ /** @type {HTMLElement} */
7
+ #loadingIndicatorsElement;
8
+ /**
9
+ * @type {Map<import("../view/view.js").default, { status: import("../types/viewContext.js").DataLoadingStatus, detail?: string }>}
10
+ */
11
+ #loadingViews;
12
+
13
+ /**
14
+ * @param {HTMLElement} loadingIndicatorsElement
15
+ */
16
+ constructor(loadingIndicatorsElement) {
17
+ this.#loadingIndicatorsElement = loadingIndicatorsElement;
18
+
19
+ /**
20
+ * @type {Map<import("../view/view.js").default, { status: import("../types/viewContext.js").DataLoadingStatus, detail?: string }>}
21
+ */
22
+ this.#loadingViews = new Map();
23
+ }
24
+
25
+ /**
26
+ * @param {import("../view/view.js").default} view
27
+ * @param {import("../types/viewContext.js").DataLoadingStatus} status
28
+ * @param {string} [detail]
29
+ */
30
+ setDataLoadingStatus(view, status, detail) {
31
+ this.#loadingViews.set(view, { status, detail });
32
+ this.updateLayout();
33
+ }
34
+
35
+ updateLayout() {
36
+ /** @type {import("lit").TemplateResult[]} */
37
+ const indicators = [];
38
+
39
+ const isSomethingVisible = () =>
40
+ [...this.#loadingViews.values()].some(
41
+ (v) => v.status == "loading" || v.status == "error"
42
+ );
43
+
44
+ for (const [view, status] of this.#loadingViews) {
45
+ const c = view.coords;
46
+ if (c) {
47
+ const style = {
48
+ left: `${c.x}px`,
49
+ top: `${c.y}px`,
50
+ width: `${c.width}px`,
51
+ height: `${c.height}px`,
52
+ };
53
+ indicators.push(
54
+ html`<div style=${styleMap(style)}>
55
+ <div class=${status.status}>
56
+ ${status.status == "error"
57
+ ? html`<span
58
+ >Loading
59
+ failed${status.detail
60
+ ? html`: ${status.detail}`
61
+ : nothing}</span
62
+ >`
63
+ : html`
64
+ <img src="${SPINNER}" alt="" />
65
+ <span>Loading...</span>
66
+ `}
67
+ </div>
68
+ </div>`
69
+ );
70
+ }
71
+ }
72
+
73
+ // Do some hacks to stop css animations of the loading indicators.
74
+ // Otherwise they fire animation frames even when their opacity is zero.
75
+ // TODO: Instead of this, replace the animated spinners with static images.
76
+ // Or even better, once more widely supported, use `allow-discrete`
77
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/transition-behavior
78
+ // to enable transition of the display property.
79
+ if (isSomethingVisible()) {
80
+ this.#loadingIndicatorsElement.style.display = "block";
81
+ } else {
82
+ // TODO: Clear previous timeout
83
+ setTimeout(() => {
84
+ if (!isSomethingVisible()) {
85
+ this.#loadingIndicatorsElement.style.display = "none";
86
+ }
87
+ }, 3000);
88
+ }
89
+
90
+ render(indicators, this.#loadingIndicatorsElement);
91
+ }
92
+ }
@@ -0,0 +1,22 @@
1
+ export default class RenderCoordinator {
2
+ /**
3
+ * @param {object} options
4
+ * @param {import("../view/view.js").default} options.viewRoot
5
+ * @param {import("../gl/webGLHelper.js").default} options.glHelper
6
+ * @param {() => string} options.getBackground
7
+ * @param {(type: import("../genomeSpy.js").BroadcastEventType, payload?: any) => void} options.broadcast
8
+ * @param {() => void} options.onLayoutComputed
9
+ */
10
+ constructor({ viewRoot, glHelper, getBackground, broadcast, onLayoutComputed, }: {
11
+ viewRoot: import("../view/view.js").default;
12
+ glHelper: import("../gl/webGLHelper.js").default;
13
+ getBackground: () => string;
14
+ broadcast: (type: import("../genomeSpy.js").BroadcastEventType, payload?: any) => void;
15
+ onLayoutComputed: () => void;
16
+ });
17
+ computeLayout(): void;
18
+ renderAll(): void;
19
+ renderPickingFramebuffer(): void;
20
+ #private;
21
+ }
22
+ //# sourceMappingURL=renderCoordinator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderCoordinator.d.ts","sourceRoot":"","sources":["../../../src/genomeSpy/renderCoordinator.js"],"names":[],"mappings":"AAIA;IAiBI;;;;;;;OAOG;IACH,iFANG;QAAmD,QAAQ,EAAnD,OAAO,iBAAiB,EAAE,OAAO;QACe,QAAQ,EAAxD,OAAO,sBAAsB,EAAE,OAAO;QAChB,aAAa,EAAnC,MAAM,MAAM;QACyE,SAAS,EAA9F,CAAC,IAAI,EAAE,OAAO,iBAAiB,EAAE,kBAAkB,EAAE,OAAO,CAAC,EAAE,GAAG,KAAK,IAAI;QACvD,gBAAgB,EAApC,MAAM,IAAI;KACpB,EAqBA;IAED,sBAkDC;IAED,kBAIC;IAED,iCAOC;;CACJ"}
@@ -0,0 +1,118 @@
1
+ import BufferedViewRenderingContext from "../view/renderingContext/bufferedViewRenderingContext.js";
2
+ import CompositeViewRenderingContext from "../view/renderingContext/compositeViewRenderingContext.js";
3
+ import Rectangle from "../view/layout/rectangle.js";
4
+
5
+ export default class RenderCoordinator {
6
+ /** @type {import("../view/view.js").default} */
7
+ #viewRoot;
8
+ /** @type {import("../gl/webGLHelper.js").default} */
9
+ #glHelper;
10
+ /** @type {() => string} */
11
+ #getBackground;
12
+ /** @type {(type: import("../genomeSpy.js").BroadcastEventType, payload?: any) => void} */
13
+ #broadcast;
14
+ /** @type {() => void} */
15
+ #onLayoutComputed;
16
+ /** @type {BufferedViewRenderingContext} */
17
+ #renderingContext;
18
+ /** @type {BufferedViewRenderingContext} */
19
+ #pickingContext;
20
+ /** @type {boolean} */
21
+ #dirtyPickingBuffer;
22
+ /**
23
+ * @param {object} options
24
+ * @param {import("../view/view.js").default} options.viewRoot
25
+ * @param {import("../gl/webGLHelper.js").default} options.glHelper
26
+ * @param {() => string} options.getBackground
27
+ * @param {(type: import("../genomeSpy.js").BroadcastEventType, payload?: any) => void} options.broadcast
28
+ * @param {() => void} options.onLayoutComputed
29
+ */
30
+ constructor({
31
+ viewRoot,
32
+ glHelper,
33
+ getBackground,
34
+ broadcast,
35
+ onLayoutComputed,
36
+ }) {
37
+ this.#viewRoot = viewRoot;
38
+ this.#glHelper = glHelper;
39
+ this.#getBackground = getBackground;
40
+ this.#broadcast = broadcast;
41
+ this.#onLayoutComputed = onLayoutComputed;
42
+
43
+ /** @type {BufferedViewRenderingContext} */
44
+ this.#renderingContext = undefined;
45
+ /** @type {BufferedViewRenderingContext} */
46
+ this.#pickingContext = undefined;
47
+
48
+ /** Does picking buffer need to be rendered again */
49
+ this.#dirtyPickingBuffer = false;
50
+ }
51
+
52
+ computeLayout() {
53
+ const root = this.#viewRoot;
54
+ if (!root) {
55
+ return;
56
+ }
57
+
58
+ this.#broadcast("layout");
59
+
60
+ const canvasSize = this.#glHelper.getLogicalCanvasSize();
61
+
62
+ if (isNaN(canvasSize.width) || isNaN(canvasSize.height)) {
63
+ // TODO: Figure out what causes this
64
+ console.log(
65
+ `NaN in canvas size: ${canvasSize.width}x${canvasSize.height}. Skipping computeLayout().`
66
+ );
67
+ return;
68
+ }
69
+
70
+ const commonOptions = {
71
+ webGLHelper: this.#glHelper,
72
+ canvasSize,
73
+ devicePixelRatio: window.devicePixelRatio ?? 1,
74
+ };
75
+
76
+ this.#renderingContext = new BufferedViewRenderingContext(
77
+ { picking: false },
78
+ {
79
+ ...commonOptions,
80
+ clearColor: this.#getBackground(),
81
+ }
82
+ );
83
+ this.#pickingContext = new BufferedViewRenderingContext(
84
+ { picking: true },
85
+ {
86
+ ...commonOptions,
87
+ framebufferInfo: this.#glHelper._pickingBufferInfo,
88
+ }
89
+ );
90
+
91
+ root.render(
92
+ new CompositeViewRenderingContext(
93
+ this.#renderingContext,
94
+ this.#pickingContext
95
+ ),
96
+ // Canvas should now be sized based on the root view or the container
97
+ Rectangle.create(0, 0, canvasSize.width, canvasSize.height)
98
+ );
99
+
100
+ this.#onLayoutComputed();
101
+ this.#broadcast("layoutComputed");
102
+ }
103
+
104
+ renderAll() {
105
+ this.#renderingContext?.render();
106
+
107
+ this.#dirtyPickingBuffer = true;
108
+ }
109
+
110
+ renderPickingFramebuffer() {
111
+ if (!this.#dirtyPickingBuffer) {
112
+ return;
113
+ }
114
+
115
+ this.#pickingContext.render();
116
+ this.#dirtyPickingBuffer = false;
117
+ }
118
+ }