react-msaview 5.0.6 → 5.0.13

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 (190) hide show
  1. package/bundle/index.js +106 -106
  2. package/bundle/index.js.LICENSE.txt +1 -1
  3. package/bundle/index.js.map +1 -1
  4. package/dist/components/Checkbox2.js +3 -6
  5. package/dist/components/Checkbox2.js.map +1 -1
  6. package/dist/components/MSAViewer.d.ts +14 -0
  7. package/dist/components/MSAViewer.js +34 -0
  8. package/dist/components/MSAViewer.js.map +1 -0
  9. package/dist/components/Track.d.ts +0 -4
  10. package/dist/components/Track.js +6 -26
  11. package/dist/components/Track.js.map +1 -1
  12. package/dist/components/dialogs/DomainDialog.js +2 -5
  13. package/dist/components/dialogs/DomainDialog.js.map +1 -1
  14. package/dist/components/dialogs/InterProScanDialog.js +7 -7
  15. package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
  16. package/dist/components/dialogs/SettingsDialog.js +3 -19
  17. package/dist/components/dialogs/SettingsDialog.js.map +1 -1
  18. package/dist/components/header/ColorSchemeMenu.d.ts +6 -0
  19. package/dist/components/header/ColorSchemeMenu.js +19 -0
  20. package/dist/components/header/ColorSchemeMenu.js.map +1 -0
  21. package/dist/components/header/{ZoomStar.d.ts → FileMenu.d.ts} +2 -2
  22. package/dist/components/header/FileMenu.js +71 -0
  23. package/dist/components/header/FileMenu.js.map +1 -0
  24. package/dist/components/header/Header.js +8 -6
  25. package/dist/components/header/Header.js.map +1 -1
  26. package/dist/components/header/HeaderMenu.js +3 -145
  27. package/dist/components/header/HeaderMenu.js.map +1 -1
  28. package/dist/components/header/MSASettingsMenu.d.ts +6 -0
  29. package/dist/components/header/MSASettingsMenu.js +36 -0
  30. package/dist/components/header/MSASettingsMenu.js.map +1 -0
  31. package/dist/components/header/SettingsMenu.js +1 -21
  32. package/dist/components/header/SettingsMenu.js.map +1 -1
  33. package/dist/components/header/TreeSettingsMenu.d.ts +6 -0
  34. package/dist/components/header/TreeSettingsMenu.js +74 -0
  35. package/dist/components/header/TreeSettingsMenu.js.map +1 -0
  36. package/dist/components/header/ZoomMenu.js +0 -8
  37. package/dist/components/header/ZoomMenu.js.map +1 -1
  38. package/dist/components/header/getDomainsMenu.d.ts +31 -0
  39. package/dist/components/header/getDomainsMenu.js +75 -0
  40. package/dist/components/header/getDomainsMenu.js.map +1 -0
  41. package/dist/components/import/ImportFormExamples.js +21 -19
  42. package/dist/components/import/ImportFormExamples.js.map +1 -1
  43. package/dist/components/msa/MSACanvas.js +13 -89
  44. package/dist/components/msa/MSACanvas.js.map +1 -1
  45. package/dist/components/msa/MSACanvasBlock.js +1 -3
  46. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  47. package/dist/components/msa/renderMSABlock.js +2 -4
  48. package/dist/components/msa/renderMSABlock.js.map +1 -1
  49. package/dist/components/msa/renderMSAMouseover.js +1 -7
  50. package/dist/components/msa/renderMSAMouseover.js.map +1 -1
  51. package/dist/components/tree/TreeCanvas.js +18 -101
  52. package/dist/components/tree/TreeCanvas.js.map +1 -1
  53. package/dist/components/tree/TreeCanvasBlock.js +33 -1
  54. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  55. package/dist/components/tree/TreeNodeMenu.js +5 -16
  56. package/dist/components/tree/TreeNodeMenu.js.map +1 -1
  57. package/dist/components/tree/renderTreeCanvas.js +4 -12
  58. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  59. package/dist/constants.d.ts +0 -2
  60. package/dist/constants.js +0 -2
  61. package/dist/constants.js.map +1 -1
  62. package/dist/fetchUtils.d.ts +0 -1
  63. package/dist/fetchUtils.js +0 -4
  64. package/dist/fetchUtils.js.map +1 -1
  65. package/dist/flatToTree.d.ts +0 -5
  66. package/dist/flatToTree.js +13 -30
  67. package/dist/flatToTree.js.map +1 -1
  68. package/dist/hierarchy.d.ts +28 -0
  69. package/dist/hierarchy.js +164 -0
  70. package/dist/hierarchy.js.map +1 -0
  71. package/dist/index.d.ts +2 -0
  72. package/dist/index.js +1 -0
  73. package/dist/index.js.map +1 -1
  74. package/dist/launchInterProScan.d.ts +0 -5
  75. package/dist/launchInterProScan.js +5 -3
  76. package/dist/launchInterProScan.js.map +1 -1
  77. package/dist/model/DataModel.d.ts +9 -0
  78. package/dist/model/DataModel.js +12 -1
  79. package/dist/model/DataModel.js.map +1 -1
  80. package/dist/model/msaModel.d.ts +3 -0
  81. package/dist/model/msaModel.js +0 -1
  82. package/dist/model/msaModel.js.map +1 -1
  83. package/dist/model/treeModel.d.ts +3 -6
  84. package/dist/model/treeModel.js +3 -15
  85. package/dist/model/treeModel.js.map +1 -1
  86. package/dist/model.d.ts +34 -77
  87. package/dist/model.js +140 -239
  88. package/dist/model.js.map +1 -1
  89. package/dist/neighborJoining.js +40 -633
  90. package/dist/neighborJoining.js.map +1 -1
  91. package/dist/parseAsn1.d.ts +0 -12
  92. package/dist/parseAsn1.js +125 -332
  93. package/dist/parseAsn1.js.map +1 -1
  94. package/dist/useWheelScroll.d.ts +8 -0
  95. package/dist/useWheelScroll.js +93 -0
  96. package/dist/useWheelScroll.js.map +1 -0
  97. package/dist/util.d.ts +1 -6
  98. package/dist/util.js +5 -34
  99. package/dist/util.js.map +1 -1
  100. package/dist/vendor/copyToClipboard.d.ts +1 -10
  101. package/dist/vendor/copyToClipboard.js +14 -109
  102. package/dist/vendor/copyToClipboard.js.map +1 -1
  103. package/dist/vendor/fileSaver.d.ts +1 -11
  104. package/dist/vendor/fileSaver.js +7 -76
  105. package/dist/vendor/fileSaver.js.map +1 -1
  106. package/dist/version.d.ts +1 -1
  107. package/dist/version.js +1 -1
  108. package/dist/version.js.map +1 -1
  109. package/package.json +14 -14
  110. package/src/collapseLogic.test.ts +115 -0
  111. package/src/components/Checkbox2.tsx +9 -18
  112. package/src/components/MSAViewer.tsx +67 -0
  113. package/src/components/Track.tsx +11 -30
  114. package/src/components/dialogs/DomainDialog.tsx +4 -5
  115. package/src/components/dialogs/InterProScanDialog.tsx +7 -7
  116. package/src/components/dialogs/SettingsDialog.tsx +0 -37
  117. package/src/components/header/ColorSchemeMenu.tsx +35 -0
  118. package/src/components/header/FileMenu.tsx +84 -0
  119. package/src/components/header/Header.tsx +8 -6
  120. package/src/components/header/HeaderMenu.tsx +4 -155
  121. package/src/components/header/MSASettingsMenu.tsx +48 -0
  122. package/src/components/header/SettingsMenu.tsx +0 -23
  123. package/src/components/header/TreeSettingsMenu.tsx +96 -0
  124. package/src/components/header/ZoomMenu.tsx +0 -8
  125. package/src/components/header/getDomainsMenu.ts +83 -0
  126. package/src/components/import/ImportFormExamples.tsx +37 -34
  127. package/src/components/msa/MSACanvas.tsx +21 -97
  128. package/src/components/msa/MSACanvasBlock.tsx +1 -3
  129. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +1 -1
  130. package/src/components/msa/renderMSABlock.ts +2 -5
  131. package/src/components/msa/renderMSAMouseover.ts +0 -6
  132. package/src/components/tree/TreeCanvas.tsx +48 -111
  133. package/src/components/tree/TreeCanvasBlock.tsx +44 -0
  134. package/src/components/tree/TreeNodeMenu.tsx +5 -14
  135. package/src/components/tree/renderTreeCanvas.ts +8 -21
  136. package/src/constants.ts +0 -2
  137. package/src/fetchUtils.ts +0 -5
  138. package/src/flatToTree.ts +20 -38
  139. package/src/hierarchy.test.ts +120 -0
  140. package/src/hierarchy.ts +220 -0
  141. package/src/index.ts +2 -0
  142. package/src/launchInterProScan.ts +4 -3
  143. package/src/model/DataModel.ts +12 -1
  144. package/src/model/msaModel.ts +0 -2
  145. package/src/model/treeModel.ts +2 -18
  146. package/src/model.ts +203 -278
  147. package/src/neighborJoining.test.ts +15 -7
  148. package/src/neighborJoining.ts +40 -632
  149. package/src/parseAsn1.test.ts +5 -2
  150. package/src/parseAsn1.ts +135 -405
  151. package/src/useWheelScroll.ts +109 -0
  152. package/src/util.ts +5 -50
  153. package/src/vendor/copyToClipboard.ts +14 -122
  154. package/src/vendor/fileSaver.ts +8 -105
  155. package/src/version.ts +1 -1
  156. package/dist/components/dialogs/AddTrackDialog.d.ts +0 -8
  157. package/dist/components/dialogs/AddTrackDialog.js +0 -30
  158. package/dist/components/dialogs/AddTrackDialog.js.map +0 -1
  159. package/dist/components/dialogs/TabPanel.d.ts +0 -6
  160. package/dist/components/dialogs/TabPanel.js +0 -6
  161. package/dist/components/dialogs/TabPanel.js.map +0 -1
  162. package/dist/components/header/ZoomStar.js +0 -40
  163. package/dist/components/header/ZoomStar.js.map +0 -1
  164. package/dist/createPaletteMap.test.d.ts +0 -1
  165. package/dist/createPaletteMap.test.js +0 -49
  166. package/dist/createPaletteMap.test.js.map +0 -1
  167. package/dist/layout.d.ts +0 -26
  168. package/dist/layout.js +0 -74
  169. package/dist/layout.js.map +0 -1
  170. package/dist/neighborJoining.test.d.ts +0 -1
  171. package/dist/neighborJoining.test.js +0 -110
  172. package/dist/neighborJoining.test.js.map +0 -1
  173. package/dist/parseAsn1.test.d.ts +0 -1
  174. package/dist/parseAsn1.test.js +0 -8
  175. package/dist/parseAsn1.test.js.map +0 -1
  176. package/dist/reparseTree.d.ts +0 -2
  177. package/dist/reparseTree.js +0 -15
  178. package/dist/reparseTree.js.map +0 -1
  179. package/dist/rowCoordinateCalculations.test.d.ts +0 -1
  180. package/dist/rowCoordinateCalculations.test.js +0 -224
  181. package/dist/rowCoordinateCalculations.test.js.map +0 -1
  182. package/dist/seqPosToGlobalCol.test.d.ts +0 -1
  183. package/dist/seqPosToGlobalCol.test.js +0 -60
  184. package/dist/seqPosToGlobalCol.test.js.map +0 -1
  185. package/src/components/dialogs/AddTrackDialog.tsx +0 -85
  186. package/src/components/dialogs/TabPanel.tsx +0 -19
  187. package/src/components/header/ZoomStar.tsx +0 -74
  188. package/src/createPaletteMap.test.ts +0 -57
  189. package/src/layout.ts +0 -118
  190. package/src/reparseTree.ts +0 -18
package/dist/model.js CHANGED
@@ -1,19 +1,18 @@
1
- import { clamp, fetchAndMaybeUnzipText, groupBy, localStorageGetBoolean, localStorageSetBoolean, notEmpty, sum, } from '@jbrowse/core/util';
1
+ import { clamp, fetchAndMaybeUnzipText, groupBy, notEmpty, sum, } from '@jbrowse/core/util';
2
2
  import { openLocation } from '@jbrowse/core/util/io';
3
3
  import { ElementId, FileLocation } from '@jbrowse/core/util/types/mst';
4
4
  import { addDisposer, cast, types } from '@jbrowse/mobx-state-tree';
5
5
  import { colord } from 'colord';
6
- import { ascending } from 'd3-array';
7
- import { cluster, hierarchy } from 'd3-hierarchy';
8
6
  import { autorun, transaction } from 'mobx';
9
- import { A3mMSA, ClustalMSA, EmfMSA, FastaMSA, StockholmMSA, generateNodeIds, gffToInterProResults, parseEmfTree, parseGFF, parseNewick, stockholmSniff, } from 'msa-parsers';
7
+ import { generateNodeIds, gffToInterProResults, parseEmfTree, parseGFF, parseMSA, parseNewick, } from 'msa-parsers';
10
8
  import { blocksX, blocksY } from "./calculateBlocks.js";
11
9
  import colorSchemes from "./colorSchemes.js";
12
10
  import ConservationTrack from "./components/ConservationTrack.js";
13
11
  import TextTrack from "./components/TextTrack.js";
14
- import { defaultAllowedGappyness, defaultBgColor, defaultColWidth, defaultColorSchemeName, defaultContrastLettering, defaultCurrentAlignment, defaultDrawLabels, defaultDrawMsaLetters, defaultDrawNodeBubbles, defaultDrawTree, defaultHeight, defaultHideGaps, defaultLabelsAlignRight, defaultRowHeight, defaultScrollX, defaultScrollY, defaultShowBranchLen, defaultShowDomains, defaultSubFeatureRows, defaultTreeAreaWidth, defaultTreeWidth, defaultTreeWidthMatchesArea, } from "./constants.js";
12
+ import { defaultAllowedGappyness, defaultBgColor, defaultColWidth, defaultColorSchemeName, defaultCurrentAlignment, defaultDrawLabels, defaultDrawMsaLetters, defaultDrawNodeBubbles, defaultDrawTree, defaultHeight, defaultHideGaps, defaultLabelsAlignRight, defaultRowHeight, defaultScrollX, defaultScrollY, defaultShowBranchLen, defaultShowDomains, defaultSubFeatureRows, defaultTreeAreaWidth, defaultTreeWidth, } from "./constants.js";
15
13
  import { createPaletteMap } from "./createPaletteMap.js";
16
14
  import { flatToTree } from "./flatToTree.js";
15
+ import { clusterLayout, collapse, find, hierarchy, leaves, links, maxLength, setBrLength, sort, sum as hierarchySum, } from "./hierarchy.js";
17
16
  import { measureTextCanvas } from "./measureTextCanvas.js";
18
17
  import { DataModelF } from "./model/DataModel.js";
19
18
  import { DialogQueueSessionMixin } from "./model/DialogQueue.js";
@@ -21,12 +20,10 @@ import { MSAModelF } from "./model/msaModel.js";
21
20
  import { TreeModelF } from "./model/treeModel.js";
22
21
  import { calculateNeighborJoiningTree } from "./neighborJoining.js";
23
22
  import { parseAsn1 } from "./parseAsn1.js";
24
- import { reparseTree } from "./reparseTree.js";
25
23
  import { globalColToVisibleCol, visibleColToGlobalCol, visibleColToSeqPosForRow, } from "./rowCoordinateCalculations.js";
26
24
  import { seqPosToGlobalCol } from "./seqPosToGlobalCol.js";
27
- import { collapse, len, maxLength, setBrLength, skipBlanks } from "./util.js";
25
+ import { len, skipBlanks } from "./util.js";
28
26
  import { saveAs } from "./vendor/fileSaver.js";
29
- const showZoomStarKey = 'msa-showZoomStar';
30
27
  /**
31
28
  * #stateModel MsaView
32
29
  * extends
@@ -54,10 +51,6 @@ function stateModelFactory() {
54
51
  * #property
55
52
  */
56
53
  allowedGappyness: defaultAllowedGappyness,
57
- /**
58
- * #property
59
- */
60
- contrastLettering: defaultContrastLettering,
61
54
  /**
62
55
  * #property
63
56
  */
@@ -119,7 +112,6 @@ function stateModelFactory() {
119
112
  gffFilehandle: types.maybe(FileLocation),
120
113
  /**
121
114
  * #property
122
- *
123
115
  */
124
116
  currentAlignment: defaultCurrentAlignment,
125
117
  /**
@@ -128,12 +120,6 @@ function stateModelFactory() {
128
120
  * hidden)
129
121
  */
130
122
  collapsed: types.array(types.string),
131
- /**
132
- * #property
133
- * array of tree leaf nodes that are 'collapsed' (just that leaf node
134
- * is hidden)
135
- */
136
- collapsedLeaves: types.array(types.string),
137
123
  /**
138
124
  * #property
139
125
  * focus on particular subtree
@@ -178,11 +164,6 @@ function stateModelFactory() {
178
164
  * screens
179
165
  */
180
166
  highResScaleFactor: 2,
181
- /**
182
- * #volatile
183
- * obtained from localStorage
184
- */
185
- showZoomStar: localStorageGetBoolean(showZoomStarKey, false),
186
167
  /**
187
168
  * #volatile
188
169
  */
@@ -257,10 +238,6 @@ function stateModelFactory() {
257
238
  * #volatile
258
239
  */
259
240
  error: undefined,
260
- /**
261
- * #volatile
262
- */
263
- annotPos: undefined,
264
241
  /**
265
242
  * #volatile
266
243
  */
@@ -285,24 +262,12 @@ function stateModelFactory() {
285
262
  setAllowedGappyness(arg) {
286
263
  self.allowedGappyness = arg;
287
264
  },
288
- /**
289
- * #action
290
- */
291
- setContrastLettering(arg) {
292
- self.contrastLettering = arg;
293
- },
294
265
  /**
295
266
  * #action
296
267
  */
297
268
  setLoadingMSA(arg) {
298
269
  self.loadingMSA = arg;
299
270
  },
300
- /**
301
- * #action
302
- */
303
- setShowZoomStar(arg) {
304
- self.showZoomStar = arg;
305
- },
306
271
  /**
307
272
  * #action
308
273
  */
@@ -346,14 +311,12 @@ function stateModelFactory() {
346
311
  self.hoveredTreeNode = undefined;
347
312
  return;
348
313
  }
349
- // Find the node in the hierarchy
350
- const node = self.hierarchy.find(n => n.data.id === nodeId);
314
+ const node = find(self.hierarchy, n => n.data.id === nodeId);
351
315
  if (!node) {
352
316
  self.hoveredTreeNode = undefined;
353
317
  return;
354
318
  }
355
- // Get all descendant leaf names
356
- const descendantNames = node.leaves().map(leaf => leaf.data.name);
319
+ const descendantNames = leaves(node).map(leaf => leaf.data.name);
357
320
  self.hoveredTreeNode = { nodeId, descendantNames };
358
321
  },
359
322
  /**
@@ -422,17 +385,6 @@ function stateModelFactory() {
422
385
  self.collapsed.push(node);
423
386
  }
424
387
  },
425
- /**
426
- * #action
427
- */
428
- toggleCollapsedLeaf(node) {
429
- if (self.collapsedLeaves.includes(node)) {
430
- self.collapsedLeaves.remove(node);
431
- }
432
- else {
433
- self.collapsedLeaves.push(node);
434
- }
435
- },
436
388
  /**
437
389
  * #action
438
390
  */
@@ -489,9 +441,7 @@ function stateModelFactory() {
489
441
  */
490
442
  get hideGapsEffective() {
491
443
  return (self.hideGaps &&
492
- (self.collapsed.length > 0 ||
493
- self.collapsedLeaves.length > 0 ||
494
- self.allowedGappyness < 100));
444
+ (self.collapsed.length > 0 || self.allowedGappyness < 100));
495
445
  },
496
446
  /**
497
447
  * #getter
@@ -505,9 +455,6 @@ function stateModelFactory() {
505
455
  get actuallyShowDomains() {
506
456
  return self.showDomains && !!self.interProAnnotations;
507
457
  },
508
- /**
509
- * #getter
510
- */
511
458
  get viewInitialized() {
512
459
  return self.volatileWidth !== undefined;
513
460
  },
@@ -539,7 +486,7 @@ function stateModelFactory() {
539
486
  * #getter
540
487
  */
541
488
  get header() {
542
- return this.MSA?.getHeader() || {};
489
+ return (this.MSA?.getHeader() || {});
543
490
  },
544
491
  /**
545
492
  * #getter
@@ -553,15 +500,9 @@ function stateModelFactory() {
553
500
  get noTree() {
554
501
  return !!this.tree.noTree;
555
502
  },
556
- /**
557
- * #getter
558
- */
559
503
  get noDomains() {
560
504
  return !self.interProAnnotations;
561
505
  },
562
- /**
563
- * #getter
564
- */
565
506
  menuItems() {
566
507
  return [];
567
508
  },
@@ -576,24 +517,9 @@ function stateModelFactory() {
576
517
  */
577
518
  get MSA() {
578
519
  const text = self.data.msa;
579
- if (text) {
580
- if (stockholmSniff(text)) {
581
- return new StockholmMSA(text, self.currentAlignment);
582
- }
583
- else if (A3mMSA.sniff(text)) {
584
- return new A3mMSA(text);
585
- }
586
- else if (text.startsWith('>')) {
587
- return new FastaMSA(text);
588
- }
589
- else if (text.startsWith('SEQ')) {
590
- return new EmfMSA(text);
591
- }
592
- else {
593
- return new ClustalMSA(text);
594
- }
595
- }
596
- return null;
520
+ // uses parseMSA so the named MSAParserType return type is portable
521
+ // to downstream consumers (avoids TS2883 with default exports)
522
+ return text ? parseMSA(text, self.currentAlignment) : null;
597
523
  },
598
524
  /**
599
525
  * #getter
@@ -606,7 +532,7 @@ function stateModelFactory() {
606
532
  */
607
533
  get tree() {
608
534
  const text = self.data.tree;
609
- return reparseTree(text
535
+ return text
610
536
  ? generateNodeIds(text.startsWith('BioTreeContainer')
611
537
  ? flatToTree(parseAsn1(text))
612
538
  : parseNewick(text.startsWith('SEQ') ? parseEmfTree(text).tree : text))
@@ -615,7 +541,7 @@ function stateModelFactory() {
615
541
  children: [],
616
542
  id: 'empty',
617
543
  name: 'empty',
618
- });
544
+ };
619
545
  },
620
546
  /**
621
547
  * #getter
@@ -661,25 +587,27 @@ function stateModelFactory() {
661
587
  * #getter
662
588
  */
663
589
  get root() {
664
- let hier = hierarchy(this.tree, d => d.children)
665
- // todo: investigate whether needed, typescript says children always true
666
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
667
- .sum(d => (d.children ? 0 : 1))
668
- // eslint-disable-next-line unicorn/no-array-sort
669
- .sort((a, b) => ascending(a.data.length || 1, b.data.length || 1));
590
+ let hier = hierarchy(this.tree, d => d.children);
591
+ hierarchySum(hier, d => (d.children.length > 0 ? 0 : 1));
592
+ sort(hier, (a, b) => (a.data.length ?? 1) - (b.data.length ?? 1));
670
593
  if (self.showOnly) {
671
- const res = hier.find(n => n.data.id === self.showOnly);
594
+ const res = find(hier, n => n.data.id === self.showOnly);
672
595
  if (res) {
673
596
  hier = res;
674
597
  }
675
598
  }
676
- ;
677
- [...self.collapsed, ...self.collapsedLeaves]
678
- .map(collapsedId => hier.find(node => node.data.id === collapsedId))
679
- .filter(notEmpty)
680
- .forEach(node => {
681
- collapse(node);
682
- });
599
+ for (const collapsedId of self.collapsed) {
600
+ const node = find(hier, n => n.data.id === collapsedId);
601
+ if (!node) {
602
+ continue;
603
+ }
604
+ if (node.children) {
605
+ collapse(node);
606
+ }
607
+ else if (node.parent?.children) {
608
+ node.parent.children = node.parent.children.filter(c => c.data.id !== collapsedId);
609
+ }
610
+ }
683
611
  return hier;
684
612
  },
685
613
  /**
@@ -1072,30 +1000,28 @@ function stateModelFactory() {
1072
1000
  */
1073
1001
  get hierarchy() {
1074
1002
  const r = this.root;
1075
- const clust = cluster()
1076
- .size([this.totalHeight, self.treeWidth])
1077
- .separation(() => 1);
1078
- clust(r);
1079
- setBrLength(r, (r.data.length = 0), self.treeWidth / maxLength(r));
1003
+ clusterLayout(r, this.totalHeight, self.treeWidth);
1004
+ r.data.length = 0;
1005
+ setBrLength(r, 0, self.treeWidth / maxLength(r));
1080
1006
  return r;
1081
1007
  },
1082
1008
  /**
1083
1009
  * #getter
1084
1010
  */
1085
1011
  get totalHeight() {
1086
- return this.root.leaves().length * self.rowHeight;
1012
+ return leaves(this.root).length * self.rowHeight;
1087
1013
  },
1088
1014
  /**
1089
1015
  * #getter
1090
1016
  */
1091
1017
  get leaves() {
1092
- return this.hierarchy.leaves();
1018
+ return leaves(this.hierarchy);
1093
1019
  },
1094
1020
  /**
1095
1021
  * #getter
1096
1022
  */
1097
1023
  get allBranchesLength0() {
1098
- return this.hierarchy.links().every(s => !s.source.data.length);
1024
+ return links(this.hierarchy).every(s => !s.source.data.length);
1099
1025
  },
1100
1026
  /**
1101
1027
  * #getter
@@ -1148,13 +1074,7 @@ function stateModelFactory() {
1148
1074
  * #getter
1149
1075
  */
1150
1076
  get blocks2d() {
1151
- const ret = [];
1152
- for (const by of self.blocksY) {
1153
- for (const bx of self.blocksX) {
1154
- ret.push([bx, by]);
1155
- }
1156
- }
1157
- return ret;
1077
+ return self.blocksY.flatMap(by => self.blocksX.map(bx => [bx, by]));
1158
1078
  },
1159
1079
  /**
1160
1080
  * #getter
@@ -1257,14 +1177,17 @@ function stateModelFactory() {
1257
1177
  /**
1258
1178
  * #action
1259
1179
  */
1180
+ doScrollY(deltaY) {
1181
+ self.scrollY = clamp(self.scrollY + deltaY, -self.totalHeight + 10, 0);
1182
+ },
1260
1183
  setInterProAnnotations(data) {
1261
1184
  self.interProAnnotations = data;
1262
1185
  },
1263
- /**
1264
- * #action
1265
- */
1266
- doScrollY(deltaY) {
1267
- self.scrollY = clamp(self.scrollY + deltaY, -self.totalHeight + 10, 0);
1186
+ applyGFFText(gffText) {
1187
+ const gffRecords = parseGFF(gffText);
1188
+ const interProResults = gffToInterProResults(gffRecords);
1189
+ self.interProAnnotations = interProResults;
1190
+ self.setShowDomains(true);
1268
1191
  },
1269
1192
  /**
1270
1193
  * #action
@@ -1301,14 +1224,11 @@ function stateModelFactory() {
1301
1224
  * #getter
1302
1225
  */
1303
1226
  get labelsWidth() {
1304
- let x = 0;
1305
1227
  const { rowHeight, leaves, treeMetadata, fontSize } = self;
1306
- if (rowHeight > 5) {
1307
- for (const node of leaves) {
1308
- x = Math.max(measureTextCanvas(treeMetadata[node.data.name]?.genome || node.data.name, fontSize), x);
1309
- }
1228
+ if (rowHeight <= 5) {
1229
+ return 0;
1310
1230
  }
1311
- return x;
1231
+ return leaves.reduce((max, node) => Math.max(max, measureTextCanvas(treeMetadata[node.data.name]?.genome || node.data.name, fontSize)), 0);
1312
1232
  },
1313
1233
  /**
1314
1234
  * #getter
@@ -1327,16 +1247,19 @@ function stateModelFactory() {
1327
1247
  */
1328
1248
  get adapterTrackModels() {
1329
1249
  const { rowHeight, MSA, hideGapsEffective, blanks } = self;
1330
- return (MSA?.tracks
1331
- .filter(t => t.data)
1250
+ const tracks = (MSA?.tracks ?? []);
1251
+ return tracks
1252
+ .filter(t => !!t.data)
1332
1253
  .map(t => ({
1333
1254
  model: {
1334
1255
  ...t,
1335
- data: hideGapsEffective ? skipBlanks(blanks, t.data) : t.data,
1256
+ data: hideGapsEffective && t.data
1257
+ ? skipBlanks(blanks, t.data)
1258
+ : t.data,
1336
1259
  height: rowHeight,
1337
1260
  },
1338
1261
  ReactComponent: TextTrack,
1339
- })) || []);
1262
+ }));
1340
1263
  },
1341
1264
  /**
1342
1265
  * #getter
@@ -1461,6 +1384,29 @@ function stateModelFactory() {
1461
1384
  },
1462
1385
  }))
1463
1386
  .views(self => ({
1387
+ /**
1388
+ * #getter
1389
+ * Returns information about the currently hovered cell
1390
+ */
1391
+ get hoveredCell() {
1392
+ const { mouseCol, mouseRow } = self;
1393
+ if (mouseCol === undefined || mouseRow === undefined) {
1394
+ return undefined;
1395
+ }
1396
+ const rowName = self.rowNames[mouseRow];
1397
+ if (!rowName) {
1398
+ return undefined;
1399
+ }
1400
+ const seq = self.columns[rowName];
1401
+ const base = seq?.[mouseCol];
1402
+ const seqPos = self.visibleColToSeqPosOneBased(rowName, mouseCol);
1403
+ return {
1404
+ rowName,
1405
+ col: mouseCol,
1406
+ base,
1407
+ seqPos,
1408
+ };
1409
+ },
1464
1410
  /**
1465
1411
  * #getter
1466
1412
  * widget width minus the tree area gives the space for the MSA
@@ -1477,53 +1423,30 @@ function stateModelFactory() {
1477
1423
  get totalTrackAreaHeight() {
1478
1424
  return sum(self.turnedOnTracks.map(r => r.model.height));
1479
1425
  },
1480
- /**
1481
- * #getter
1482
- */
1483
1426
  get tidyInterProAnnotationTypes() {
1484
- const types = new Map();
1485
- for (const annot of this.tidyInterProAnnotations) {
1486
- types.set(annot.accession, annot);
1487
- }
1488
- return types;
1427
+ return new Map(this.tidyInterProAnnotations.map(annot => [annot.accession, annot]));
1489
1428
  },
1490
- /**
1491
- * #getter
1492
- */
1493
1429
  get tidyInterProAnnotations() {
1494
- const ret = [];
1495
1430
  const { interProAnnotations } = self;
1496
- if (interProAnnotations) {
1497
- for (const [id, val] of Object.entries(interProAnnotations)) {
1498
- for (const { signature, locations } of val.matches) {
1499
- const { entry } = signature;
1500
- if (entry) {
1501
- const { name, accession, description } = entry;
1502
- for (const { start, end } of locations) {
1503
- ret.push({
1504
- id,
1505
- name,
1506
- accession,
1507
- description,
1508
- start,
1509
- end,
1510
- });
1511
- }
1512
- }
1513
- }
1514
- }
1431
+ if (!interProAnnotations) {
1432
+ return [];
1515
1433
  }
1516
- return ret.toSorted((a, b) => len(b) - len(a));
1434
+ return Object.entries(interProAnnotations)
1435
+ .flatMap(([id, val]) => val.matches.flatMap(({ signature, locations }) => signature.entry
1436
+ ? locations.map(({ start, end }) => ({
1437
+ id,
1438
+ name: signature.entry.name,
1439
+ accession: signature.entry.accession,
1440
+ description: signature.entry.description,
1441
+ start,
1442
+ end,
1443
+ }))
1444
+ : []))
1445
+ .toSorted((a, b) => len(b) - len(a));
1517
1446
  },
1518
- /**
1519
- * #getter
1520
- */
1521
1447
  get tidyFilteredInterProAnnotations() {
1522
1448
  return this.tidyInterProAnnotations.filter(r => self.featureFilters.get(r.accession));
1523
1449
  },
1524
- /**
1525
- * #getter
1526
- */
1527
1450
  get tidyFilteredGatheredInterProAnnotations() {
1528
1451
  return groupBy(this.tidyFilteredInterProAnnotations, r => r.id);
1529
1452
  },
@@ -1543,15 +1466,9 @@ function stateModelFactory() {
1543
1466
  get verticalScrollbarWidth() {
1544
1467
  return self.showVerticalScrollbar ? 20 : 0;
1545
1468
  },
1546
- /**
1547
- * #getter
1548
- */
1549
1469
  get fillPalette() {
1550
1470
  return createPaletteMap([...self.tidyInterProAnnotationTypes.keys()]);
1551
1471
  },
1552
- /**
1553
- * #getter
1554
- */
1555
1472
  get strokePalette() {
1556
1473
  return Object.fromEntries(Object.entries(this.fillPalette).map(([key, val]) => [
1557
1474
  key,
@@ -1596,6 +1513,9 @@ function stateModelFactory() {
1596
1513
  self.setCurrentAlignment(0);
1597
1514
  self.setTreeFilehandle(undefined);
1598
1515
  self.setMSAFilehandle(undefined);
1516
+ self.setGFFFilehandle(undefined);
1517
+ self.setInterProAnnotations({});
1518
+ self.setShowDomains(false);
1599
1519
  },
1600
1520
  /**
1601
1521
  * #action
@@ -1613,18 +1533,12 @@ function stateModelFactory() {
1613
1533
  incrementRef() {
1614
1534
  self.nref++;
1615
1535
  },
1616
- /**
1617
- * #action
1618
- */
1619
1536
  initFilter(arg) {
1620
1537
  const ret = self.featureFilters.get(arg);
1621
1538
  if (ret === undefined) {
1622
1539
  self.featureFilters.set(arg, true);
1623
1540
  }
1624
1541
  },
1625
- /**
1626
- * #action
1627
- */
1628
1542
  setFilter(arg, flag) {
1629
1543
  self.featureFilters.set(arg, flag);
1630
1544
  },
@@ -1657,10 +1571,6 @@ function stateModelFactory() {
1657
1571
  this.initFilter(key);
1658
1572
  }
1659
1573
  }));
1660
- // autorun saves local settings
1661
- addDisposer(self, autorun(() => {
1662
- localStorageSetBoolean(showZoomStarKey, self.showZoomStar);
1663
- }));
1664
1574
  // autorun opens treeFilehandle
1665
1575
  addDisposer(self, autorun(async () => {
1666
1576
  const { treeFilehandle } = self;
@@ -1695,16 +1605,26 @@ function stateModelFactory() {
1695
1605
  }
1696
1606
  }
1697
1607
  }));
1608
+ // autorun parses inline gff text from data.gff
1609
+ addDisposer(self, autorun(() => {
1610
+ const gffText = self.data.gff;
1611
+ if (gffText) {
1612
+ try {
1613
+ self.applyGFFText(gffText);
1614
+ }
1615
+ catch (e) {
1616
+ console.error(e);
1617
+ self.setError(e);
1618
+ }
1619
+ }
1620
+ }));
1698
1621
  // autorun opens gffFilehandle for InterProScan domains
1699
1622
  addDisposer(self, autorun(async () => {
1700
1623
  const { gffFilehandle } = self;
1701
1624
  if (gffFilehandle) {
1702
1625
  try {
1703
1626
  const gffText = await fetchAndMaybeUnzipText(openLocation(gffFilehandle));
1704
- const gffRecords = parseGFF(gffText);
1705
- const interProResults = gffToInterProResults(gffRecords);
1706
- self.setInterProAnnotations(interProResults);
1707
- self.setShowDomains(true);
1627
+ self.applyGFFText(gffText);
1708
1628
  if (gffFilehandle.locationType === 'BlobLocation') {
1709
1629
  self.setGFFFilehandle(undefined);
1710
1630
  }
@@ -1754,10 +1674,8 @@ function stateModelFactory() {
1754
1674
  self.columns;
1755
1675
  }));
1756
1676
  // autorun synchronizes treeWidth with treeAreaWidth
1757
- addDisposer(self, autorun(async () => {
1758
- if (self.treeWidthMatchesArea) {
1759
- self.setTreeWidth(Math.max(50, self.treeAreaWidth - self.labelsWidth - 10 - self.marginLeft));
1760
- }
1677
+ addDisposer(self, autorun(() => {
1678
+ self.setTreeWidth(Math.max(50, self.treeAreaWidth - self.labelsWidth - 10 - self.marginLeft));
1761
1679
  }));
1762
1680
  },
1763
1681
  }))
@@ -1765,15 +1683,38 @@ function stateModelFactory() {
1765
1683
  const snap = result;
1766
1684
  const { data: { tree, msa, treeMetadata },
1767
1685
  // Main model properties
1768
- showDomains, hideGaps, allowedGappyness, contrastLettering, subFeatureRows, drawMsaLetters, height, rowHeight, scrollY, scrollX, colWidth, currentAlignment, collapsed, collapsedLeaves, showOnly, turnedOffTracks, featureFilters, relativeTo,
1686
+ showDomains, hideGaps, allowedGappyness, subFeatureRows, drawMsaLetters, height, rowHeight, scrollY, scrollX, colWidth, currentAlignment, collapsed, showOnly, turnedOffTracks, featureFilters, relativeTo,
1769
1687
  // MSA model properties
1770
1688
  bgColor, colorSchemeName,
1771
1689
  // Tree model properties
1772
- drawLabels, labelsAlignRight, treeAreaWidth, treeWidth, treeWidthMatchesArea, showBranchLen, drawTree, drawNodeBubbles,
1690
+ drawLabels, labelsAlignRight, treeAreaWidth, treeWidth, showBranchLen, drawTree, drawNodeBubbles,
1773
1691
  // Always include
1774
1692
  ...rest } = snap;
1775
- // remove the MSA/tree data from the tree if the filehandle available in
1776
- // which case it can be reloaded on refresh
1693
+ const defaults = {
1694
+ showDomains: defaultShowDomains,
1695
+ hideGaps: defaultHideGaps,
1696
+ allowedGappyness: defaultAllowedGappyness,
1697
+ subFeatureRows: defaultSubFeatureRows,
1698
+ drawMsaLetters: defaultDrawMsaLetters,
1699
+ height: defaultHeight,
1700
+ rowHeight: defaultRowHeight,
1701
+ scrollY: defaultScrollY,
1702
+ scrollX: defaultScrollX,
1703
+ colWidth: defaultColWidth,
1704
+ currentAlignment: defaultCurrentAlignment,
1705
+ bgColor: defaultBgColor,
1706
+ colorSchemeName: defaultColorSchemeName,
1707
+ drawLabels: defaultDrawLabels,
1708
+ labelsAlignRight: defaultLabelsAlignRight,
1709
+ treeAreaWidth: defaultTreeAreaWidth,
1710
+ treeWidth: defaultTreeWidth,
1711
+ showBranchLen: defaultShowBranchLen,
1712
+ drawTree: defaultDrawTree,
1713
+ drawNodeBubbles: defaultDrawNodeBubbles,
1714
+ };
1715
+ const nonDefaults = Object.fromEntries(Object.entries(defaults)
1716
+ .filter(([key, def]) => snap[key] !== def)
1717
+ .map(([key]) => [key, snap[key]]));
1777
1718
  return {
1778
1719
  ...rest,
1779
1720
  data: {
@@ -1781,29 +1722,9 @@ function stateModelFactory() {
1781
1722
  ...(result.msaFilehandle ? {} : { msa }),
1782
1723
  ...(result.treeMetadataFilehandle ? {} : { treeMetadata }),
1783
1724
  },
1784
- // Main model - only include non-default values
1785
- ...(showDomains !== defaultShowDomains ? { showDomains } : {}),
1786
- ...(hideGaps !== defaultHideGaps ? { hideGaps } : {}),
1787
- ...(allowedGappyness !== defaultAllowedGappyness
1788
- ? { allowedGappyness }
1789
- : {}),
1790
- ...(contrastLettering !== defaultContrastLettering
1791
- ? { contrastLettering }
1792
- : {}),
1793
- ...(subFeatureRows !== defaultSubFeatureRows ? { subFeatureRows } : {}),
1794
- ...(drawMsaLetters !== defaultDrawMsaLetters ? { drawMsaLetters } : {}),
1795
- ...(height !== defaultHeight ? { height } : {}),
1796
- ...(rowHeight !== defaultRowHeight ? { rowHeight } : {}),
1797
- ...(scrollY !== defaultScrollY ? { scrollY } : {}),
1798
- ...(scrollX !== defaultScrollX ? { scrollX } : {}),
1799
- ...(colWidth !== defaultColWidth ? { colWidth } : {}),
1800
- ...(currentAlignment !== defaultCurrentAlignment
1801
- ? { currentAlignment }
1802
- : {}),
1725
+ ...nonDefaults,
1803
1726
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1804
1727
  ...(collapsed?.length ? { collapsed } : {}),
1805
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1806
- ...(collapsedLeaves?.length ? { collapsedLeaves } : {}),
1807
1728
  ...(showOnly !== undefined ? { showOnly } : {}),
1808
1729
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1809
1730
  ...(turnedOffTracks && Object.keys(turnedOffTracks).length > 0
@@ -1814,26 +1735,6 @@ function stateModelFactory() {
1814
1735
  ? { featureFilters }
1815
1736
  : {}),
1816
1737
  ...(relativeTo !== undefined ? { relativeTo } : {}),
1817
- // MSA model - only include non-default values
1818
- ...(bgColor !== defaultBgColor ? { bgColor } : {}),
1819
- ...(colorSchemeName !== defaultColorSchemeName
1820
- ? { colorSchemeName }
1821
- : {}),
1822
- // Tree model - only include non-default values
1823
- ...(drawLabels !== defaultDrawLabels ? { drawLabels } : {}),
1824
- ...(labelsAlignRight !== defaultLabelsAlignRight
1825
- ? { labelsAlignRight }
1826
- : {}),
1827
- ...(treeAreaWidth !== defaultTreeAreaWidth ? { treeAreaWidth } : {}),
1828
- ...(treeWidth !== defaultTreeWidth ? { treeWidth } : {}),
1829
- ...(treeWidthMatchesArea !== defaultTreeWidthMatchesArea
1830
- ? { treeWidthMatchesArea }
1831
- : {}),
1832
- ...(showBranchLen !== defaultShowBranchLen ? { showBranchLen } : {}),
1833
- ...(drawTree !== defaultDrawTree ? { drawTree } : {}),
1834
- ...(drawNodeBubbles !== defaultDrawNodeBubbles
1835
- ? { drawNodeBubbles }
1836
- : {}),
1837
1738
  };
1838
1739
  });
1839
1740
  }