react-msaview 5.0.7 → 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 (169) hide show
  1. package/bundle/index.js +25 -25
  2. package/bundle/index.js.map +1 -1
  3. package/dist/components/Checkbox2.js +3 -6
  4. package/dist/components/Checkbox2.js.map +1 -1
  5. package/dist/components/MSAViewer.d.ts +14 -0
  6. package/dist/components/MSAViewer.js +34 -0
  7. package/dist/components/MSAViewer.js.map +1 -0
  8. package/dist/components/Track.js +5 -24
  9. package/dist/components/Track.js.map +1 -1
  10. package/dist/components/dialogs/DomainDialog.js +2 -5
  11. package/dist/components/dialogs/DomainDialog.js.map +1 -1
  12. package/dist/components/dialogs/InterProScanDialog.js +7 -7
  13. package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
  14. package/dist/components/dialogs/SettingsDialog.js +3 -19
  15. package/dist/components/dialogs/SettingsDialog.js.map +1 -1
  16. package/dist/components/header/ColorSchemeMenu.d.ts +6 -0
  17. package/dist/components/header/ColorSchemeMenu.js +19 -0
  18. package/dist/components/header/ColorSchemeMenu.js.map +1 -0
  19. package/dist/components/header/{ZoomStar.d.ts → FileMenu.d.ts} +2 -2
  20. package/dist/components/header/FileMenu.js +71 -0
  21. package/dist/components/header/FileMenu.js.map +1 -0
  22. package/dist/components/header/Header.js +8 -6
  23. package/dist/components/header/Header.js.map +1 -1
  24. package/dist/components/header/HeaderMenu.js +3 -145
  25. package/dist/components/header/HeaderMenu.js.map +1 -1
  26. package/dist/components/header/MSASettingsMenu.d.ts +6 -0
  27. package/dist/components/header/MSASettingsMenu.js +36 -0
  28. package/dist/components/header/MSASettingsMenu.js.map +1 -0
  29. package/dist/components/header/SettingsMenu.js +1 -21
  30. package/dist/components/header/SettingsMenu.js.map +1 -1
  31. package/dist/components/header/TreeSettingsMenu.d.ts +6 -0
  32. package/dist/components/header/TreeSettingsMenu.js +74 -0
  33. package/dist/components/header/TreeSettingsMenu.js.map +1 -0
  34. package/dist/components/header/ZoomMenu.js +0 -8
  35. package/dist/components/header/ZoomMenu.js.map +1 -1
  36. package/dist/components/header/getDomainsMenu.d.ts +31 -0
  37. package/dist/components/header/getDomainsMenu.js +75 -0
  38. package/dist/components/header/getDomainsMenu.js.map +1 -0
  39. package/dist/components/import/ImportFormExamples.js +21 -19
  40. package/dist/components/import/ImportFormExamples.js.map +1 -1
  41. package/dist/components/msa/MSACanvas.js +13 -84
  42. package/dist/components/msa/MSACanvas.js.map +1 -1
  43. package/dist/components/msa/MSACanvasBlock.js +1 -3
  44. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  45. package/dist/components/msa/renderMSABlock.js +2 -4
  46. package/dist/components/msa/renderMSABlock.js.map +1 -1
  47. package/dist/components/msa/renderMSAMouseover.js +1 -7
  48. package/dist/components/msa/renderMSAMouseover.js.map +1 -1
  49. package/dist/components/tree/TreeCanvas.js +14 -91
  50. package/dist/components/tree/TreeCanvas.js.map +1 -1
  51. package/dist/components/tree/TreeNodeMenu.js +5 -16
  52. package/dist/components/tree/TreeNodeMenu.js.map +1 -1
  53. package/dist/components/tree/renderTreeCanvas.js +4 -12
  54. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  55. package/dist/constants.d.ts +0 -2
  56. package/dist/constants.js +0 -2
  57. package/dist/constants.js.map +1 -1
  58. package/dist/fetchUtils.d.ts +0 -1
  59. package/dist/fetchUtils.js +0 -4
  60. package/dist/fetchUtils.js.map +1 -1
  61. package/dist/flatToTree.d.ts +0 -5
  62. package/dist/flatToTree.js +13 -30
  63. package/dist/flatToTree.js.map +1 -1
  64. package/dist/hierarchy.d.ts +28 -0
  65. package/dist/hierarchy.js +164 -0
  66. package/dist/hierarchy.js.map +1 -0
  67. package/dist/index.d.ts +2 -0
  68. package/dist/index.js +1 -0
  69. package/dist/index.js.map +1 -1
  70. package/dist/launchInterProScan.d.ts +0 -5
  71. package/dist/launchInterProScan.js +5 -3
  72. package/dist/launchInterProScan.js.map +1 -1
  73. package/dist/model/DataModel.d.ts +9 -0
  74. package/dist/model/DataModel.js +12 -1
  75. package/dist/model/DataModel.js.map +1 -1
  76. package/dist/model/msaModel.d.ts +3 -0
  77. package/dist/model/msaModel.js +0 -1
  78. package/dist/model/msaModel.js.map +1 -1
  79. package/dist/model/treeModel.d.ts +3 -6
  80. package/dist/model/treeModel.js +3 -15
  81. package/dist/model/treeModel.js.map +1 -1
  82. package/dist/model.d.ts +24 -77
  83. package/dist/model.js +117 -239
  84. package/dist/model.js.map +1 -1
  85. package/dist/neighborJoining.js +38 -629
  86. package/dist/neighborJoining.js.map +1 -1
  87. package/dist/parseAsn1.d.ts +0 -12
  88. package/dist/parseAsn1.js +125 -332
  89. package/dist/parseAsn1.js.map +1 -1
  90. package/dist/useWheelScroll.d.ts +8 -0
  91. package/dist/useWheelScroll.js +93 -0
  92. package/dist/useWheelScroll.js.map +1 -0
  93. package/dist/util.d.ts +1 -6
  94. package/dist/util.js +5 -34
  95. package/dist/util.js.map +1 -1
  96. package/dist/vendor/copyToClipboard.d.ts +1 -10
  97. package/dist/vendor/copyToClipboard.js +14 -109
  98. package/dist/vendor/copyToClipboard.js.map +1 -1
  99. package/dist/vendor/fileSaver.d.ts +1 -11
  100. package/dist/vendor/fileSaver.js +7 -76
  101. package/dist/vendor/fileSaver.js.map +1 -1
  102. package/dist/version.d.ts +1 -1
  103. package/dist/version.js +1 -1
  104. package/dist/version.js.map +1 -1
  105. package/package.json +10 -13
  106. package/src/collapseLogic.test.ts +115 -0
  107. package/src/components/Checkbox2.tsx +9 -18
  108. package/src/components/MSAViewer.tsx +67 -0
  109. package/src/components/Track.tsx +10 -26
  110. package/src/components/dialogs/DomainDialog.tsx +4 -5
  111. package/src/components/dialogs/InterProScanDialog.tsx +7 -7
  112. package/src/components/dialogs/SettingsDialog.tsx +0 -37
  113. package/src/components/header/ColorSchemeMenu.tsx +35 -0
  114. package/src/components/header/FileMenu.tsx +84 -0
  115. package/src/components/header/Header.tsx +8 -6
  116. package/src/components/header/HeaderMenu.tsx +4 -155
  117. package/src/components/header/MSASettingsMenu.tsx +48 -0
  118. package/src/components/header/SettingsMenu.tsx +0 -23
  119. package/src/components/header/TreeSettingsMenu.tsx +96 -0
  120. package/src/components/header/ZoomMenu.tsx +0 -8
  121. package/src/components/header/getDomainsMenu.ts +83 -0
  122. package/src/components/import/ImportFormExamples.tsx +37 -34
  123. package/src/components/msa/MSACanvas.tsx +21 -91
  124. package/src/components/msa/MSACanvasBlock.tsx +1 -3
  125. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +1 -1
  126. package/src/components/msa/renderMSABlock.ts +2 -5
  127. package/src/components/msa/renderMSAMouseover.ts +0 -6
  128. package/src/components/tree/TreeCanvas.tsx +35 -100
  129. package/src/components/tree/TreeNodeMenu.tsx +5 -14
  130. package/src/components/tree/renderTreeCanvas.ts +8 -21
  131. package/src/constants.ts +0 -2
  132. package/src/fetchUtils.ts +0 -5
  133. package/src/flatToTree.ts +20 -38
  134. package/src/hierarchy.test.ts +120 -0
  135. package/src/hierarchy.ts +220 -0
  136. package/src/index.ts +2 -0
  137. package/src/launchInterProScan.ts +4 -3
  138. package/src/model/DataModel.ts +12 -1
  139. package/src/model/msaModel.ts +0 -2
  140. package/src/model/treeModel.ts +2 -18
  141. package/src/model.ts +179 -278
  142. package/src/neighborJoining.ts +38 -628
  143. package/src/parseAsn1.test.ts +4 -1
  144. package/src/parseAsn1.ts +135 -405
  145. package/src/useWheelScroll.ts +109 -0
  146. package/src/util.ts +5 -50
  147. package/src/vendor/copyToClipboard.ts +14 -122
  148. package/src/vendor/fileSaver.ts +8 -105
  149. package/src/version.ts +1 -1
  150. package/dist/components/dialogs/AddTrackDialog.d.ts +0 -8
  151. package/dist/components/dialogs/AddTrackDialog.js +0 -30
  152. package/dist/components/dialogs/AddTrackDialog.js.map +0 -1
  153. package/dist/components/dialogs/TabPanel.d.ts +0 -6
  154. package/dist/components/dialogs/TabPanel.js +0 -6
  155. package/dist/components/dialogs/TabPanel.js.map +0 -1
  156. package/dist/components/header/ZoomStar.js +0 -40
  157. package/dist/components/header/ZoomStar.js.map +0 -1
  158. package/dist/layout.d.ts +0 -26
  159. package/dist/layout.js +0 -74
  160. package/dist/layout.js.map +0 -1
  161. package/dist/reparseTree.d.ts +0 -2
  162. package/dist/reparseTree.js +0 -15
  163. package/dist/reparseTree.js.map +0 -1
  164. package/src/components/dialogs/AddTrackDialog.tsx +0 -85
  165. package/src/components/dialogs/TabPanel.tsx +0 -19
  166. package/src/components/header/ZoomStar.tsx +0 -74
  167. package/src/createPaletteMap.test.ts +0 -57
  168. package/src/layout.ts +0 -118
  169. 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
@@ -1500,53 +1423,30 @@ function stateModelFactory() {
1500
1423
  get totalTrackAreaHeight() {
1501
1424
  return sum(self.turnedOnTracks.map(r => r.model.height));
1502
1425
  },
1503
- /**
1504
- * #getter
1505
- */
1506
1426
  get tidyInterProAnnotationTypes() {
1507
- const types = new Map();
1508
- for (const annot of this.tidyInterProAnnotations) {
1509
- types.set(annot.accession, annot);
1510
- }
1511
- return types;
1427
+ return new Map(this.tidyInterProAnnotations.map(annot => [annot.accession, annot]));
1512
1428
  },
1513
- /**
1514
- * #getter
1515
- */
1516
1429
  get tidyInterProAnnotations() {
1517
- const ret = [];
1518
1430
  const { interProAnnotations } = self;
1519
- if (interProAnnotations) {
1520
- for (const [id, val] of Object.entries(interProAnnotations)) {
1521
- for (const { signature, locations } of val.matches) {
1522
- const { entry } = signature;
1523
- if (entry) {
1524
- const { name, accession, description } = entry;
1525
- for (const { start, end } of locations) {
1526
- ret.push({
1527
- id,
1528
- name,
1529
- accession,
1530
- description,
1531
- start,
1532
- end,
1533
- });
1534
- }
1535
- }
1536
- }
1537
- }
1431
+ if (!interProAnnotations) {
1432
+ return [];
1538
1433
  }
1539
- 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));
1540
1446
  },
1541
- /**
1542
- * #getter
1543
- */
1544
1447
  get tidyFilteredInterProAnnotations() {
1545
1448
  return this.tidyInterProAnnotations.filter(r => self.featureFilters.get(r.accession));
1546
1449
  },
1547
- /**
1548
- * #getter
1549
- */
1550
1450
  get tidyFilteredGatheredInterProAnnotations() {
1551
1451
  return groupBy(this.tidyFilteredInterProAnnotations, r => r.id);
1552
1452
  },
@@ -1566,15 +1466,9 @@ function stateModelFactory() {
1566
1466
  get verticalScrollbarWidth() {
1567
1467
  return self.showVerticalScrollbar ? 20 : 0;
1568
1468
  },
1569
- /**
1570
- * #getter
1571
- */
1572
1469
  get fillPalette() {
1573
1470
  return createPaletteMap([...self.tidyInterProAnnotationTypes.keys()]);
1574
1471
  },
1575
- /**
1576
- * #getter
1577
- */
1578
1472
  get strokePalette() {
1579
1473
  return Object.fromEntries(Object.entries(this.fillPalette).map(([key, val]) => [
1580
1474
  key,
@@ -1619,6 +1513,9 @@ function stateModelFactory() {
1619
1513
  self.setCurrentAlignment(0);
1620
1514
  self.setTreeFilehandle(undefined);
1621
1515
  self.setMSAFilehandle(undefined);
1516
+ self.setGFFFilehandle(undefined);
1517
+ self.setInterProAnnotations({});
1518
+ self.setShowDomains(false);
1622
1519
  },
1623
1520
  /**
1624
1521
  * #action
@@ -1636,18 +1533,12 @@ function stateModelFactory() {
1636
1533
  incrementRef() {
1637
1534
  self.nref++;
1638
1535
  },
1639
- /**
1640
- * #action
1641
- */
1642
1536
  initFilter(arg) {
1643
1537
  const ret = self.featureFilters.get(arg);
1644
1538
  if (ret === undefined) {
1645
1539
  self.featureFilters.set(arg, true);
1646
1540
  }
1647
1541
  },
1648
- /**
1649
- * #action
1650
- */
1651
1542
  setFilter(arg, flag) {
1652
1543
  self.featureFilters.set(arg, flag);
1653
1544
  },
@@ -1680,10 +1571,6 @@ function stateModelFactory() {
1680
1571
  this.initFilter(key);
1681
1572
  }
1682
1573
  }));
1683
- // autorun saves local settings
1684
- addDisposer(self, autorun(() => {
1685
- localStorageSetBoolean(showZoomStarKey, self.showZoomStar);
1686
- }));
1687
1574
  // autorun opens treeFilehandle
1688
1575
  addDisposer(self, autorun(async () => {
1689
1576
  const { treeFilehandle } = self;
@@ -1718,16 +1605,26 @@ function stateModelFactory() {
1718
1605
  }
1719
1606
  }
1720
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
+ }));
1721
1621
  // autorun opens gffFilehandle for InterProScan domains
1722
1622
  addDisposer(self, autorun(async () => {
1723
1623
  const { gffFilehandle } = self;
1724
1624
  if (gffFilehandle) {
1725
1625
  try {
1726
1626
  const gffText = await fetchAndMaybeUnzipText(openLocation(gffFilehandle));
1727
- const gffRecords = parseGFF(gffText);
1728
- const interProResults = gffToInterProResults(gffRecords);
1729
- self.setInterProAnnotations(interProResults);
1730
- self.setShowDomains(true);
1627
+ self.applyGFFText(gffText);
1731
1628
  if (gffFilehandle.locationType === 'BlobLocation') {
1732
1629
  self.setGFFFilehandle(undefined);
1733
1630
  }
@@ -1777,10 +1674,8 @@ function stateModelFactory() {
1777
1674
  self.columns;
1778
1675
  }));
1779
1676
  // autorun synchronizes treeWidth with treeAreaWidth
1780
- addDisposer(self, autorun(async () => {
1781
- if (self.treeWidthMatchesArea) {
1782
- self.setTreeWidth(Math.max(50, self.treeAreaWidth - self.labelsWidth - 10 - self.marginLeft));
1783
- }
1677
+ addDisposer(self, autorun(() => {
1678
+ self.setTreeWidth(Math.max(50, self.treeAreaWidth - self.labelsWidth - 10 - self.marginLeft));
1784
1679
  }));
1785
1680
  },
1786
1681
  }))
@@ -1788,15 +1683,38 @@ function stateModelFactory() {
1788
1683
  const snap = result;
1789
1684
  const { data: { tree, msa, treeMetadata },
1790
1685
  // Main model properties
1791
- 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,
1792
1687
  // MSA model properties
1793
1688
  bgColor, colorSchemeName,
1794
1689
  // Tree model properties
1795
- drawLabels, labelsAlignRight, treeAreaWidth, treeWidth, treeWidthMatchesArea, showBranchLen, drawTree, drawNodeBubbles,
1690
+ drawLabels, labelsAlignRight, treeAreaWidth, treeWidth, showBranchLen, drawTree, drawNodeBubbles,
1796
1691
  // Always include
1797
1692
  ...rest } = snap;
1798
- // remove the MSA/tree data from the tree if the filehandle available in
1799
- // 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]]));
1800
1718
  return {
1801
1719
  ...rest,
1802
1720
  data: {
@@ -1804,29 +1722,9 @@ function stateModelFactory() {
1804
1722
  ...(result.msaFilehandle ? {} : { msa }),
1805
1723
  ...(result.treeMetadataFilehandle ? {} : { treeMetadata }),
1806
1724
  },
1807
- // Main model - only include non-default values
1808
- ...(showDomains !== defaultShowDomains ? { showDomains } : {}),
1809
- ...(hideGaps !== defaultHideGaps ? { hideGaps } : {}),
1810
- ...(allowedGappyness !== defaultAllowedGappyness
1811
- ? { allowedGappyness }
1812
- : {}),
1813
- ...(contrastLettering !== defaultContrastLettering
1814
- ? { contrastLettering }
1815
- : {}),
1816
- ...(subFeatureRows !== defaultSubFeatureRows ? { subFeatureRows } : {}),
1817
- ...(drawMsaLetters !== defaultDrawMsaLetters ? { drawMsaLetters } : {}),
1818
- ...(height !== defaultHeight ? { height } : {}),
1819
- ...(rowHeight !== defaultRowHeight ? { rowHeight } : {}),
1820
- ...(scrollY !== defaultScrollY ? { scrollY } : {}),
1821
- ...(scrollX !== defaultScrollX ? { scrollX } : {}),
1822
- ...(colWidth !== defaultColWidth ? { colWidth } : {}),
1823
- ...(currentAlignment !== defaultCurrentAlignment
1824
- ? { currentAlignment }
1825
- : {}),
1725
+ ...nonDefaults,
1826
1726
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1827
1727
  ...(collapsed?.length ? { collapsed } : {}),
1828
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1829
- ...(collapsedLeaves?.length ? { collapsedLeaves } : {}),
1830
1728
  ...(showOnly !== undefined ? { showOnly } : {}),
1831
1729
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1832
1730
  ...(turnedOffTracks && Object.keys(turnedOffTracks).length > 0
@@ -1837,26 +1735,6 @@ function stateModelFactory() {
1837
1735
  ? { featureFilters }
1838
1736
  : {}),
1839
1737
  ...(relativeTo !== undefined ? { relativeTo } : {}),
1840
- // MSA model - only include non-default values
1841
- ...(bgColor !== defaultBgColor ? { bgColor } : {}),
1842
- ...(colorSchemeName !== defaultColorSchemeName
1843
- ? { colorSchemeName }
1844
- : {}),
1845
- // Tree model - only include non-default values
1846
- ...(drawLabels !== defaultDrawLabels ? { drawLabels } : {}),
1847
- ...(labelsAlignRight !== defaultLabelsAlignRight
1848
- ? { labelsAlignRight }
1849
- : {}),
1850
- ...(treeAreaWidth !== defaultTreeAreaWidth ? { treeAreaWidth } : {}),
1851
- ...(treeWidth !== defaultTreeWidth ? { treeWidth } : {}),
1852
- ...(treeWidthMatchesArea !== defaultTreeWidthMatchesArea
1853
- ? { treeWidthMatchesArea }
1854
- : {}),
1855
- ...(showBranchLen !== defaultShowBranchLen ? { showBranchLen } : {}),
1856
- ...(drawTree !== defaultDrawTree ? { drawTree } : {}),
1857
- ...(drawNodeBubbles !== defaultDrawNodeBubbles
1858
- ? { drawNodeBubbles }
1859
- : {}),
1860
1738
  };
1861
1739
  });
1862
1740
  }