react-msaview 3.2.0 → 3.2.2

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 (175) hide show
  1. package/bundle/index.js +13 -13
  2. package/dist/colorSchemes.js +2 -2
  3. package/dist/colorSchemes.js.map +1 -1
  4. package/dist/components/Loading.js +3 -1
  5. package/dist/components/Loading.js.map +1 -1
  6. package/dist/components/ResizeHandles.js +6 -2
  7. package/dist/components/ResizeHandles.js.map +1 -1
  8. package/dist/components/SequenceTextArea.js +9 -3
  9. package/dist/components/SequenceTextArea.js.map +1 -1
  10. package/dist/components/TextTrack.js +1 -1
  11. package/dist/components/TextTrack.js.map +1 -1
  12. package/dist/components/Track.js +6 -2
  13. package/dist/components/Track.js.map +1 -1
  14. package/dist/components/VerticalScrollbar.js +5 -1
  15. package/dist/components/VerticalScrollbar.js.map +1 -1
  16. package/dist/components/dialogs/AboutDialog.js +3 -1
  17. package/dist/components/dialogs/AboutDialog.js.map +1 -1
  18. package/dist/components/dialogs/AddTrackDialog.d.ts +2 -2
  19. package/dist/components/dialogs/AddTrackDialog.js +8 -3
  20. package/dist/components/dialogs/AddTrackDialog.js.map +1 -1
  21. package/dist/components/dialogs/DomainDialog.js +6 -2
  22. package/dist/components/dialogs/DomainDialog.js.map +1 -1
  23. package/dist/components/dialogs/ExportSVGDialog.js +29 -17
  24. package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
  25. package/dist/components/dialogs/FeatureDialog.js +8 -4
  26. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  27. package/dist/components/dialogs/InterProScanDialog.js +23 -9
  28. package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
  29. package/dist/components/dialogs/MetadataDialog.js +3 -1
  30. package/dist/components/dialogs/MetadataDialog.js.map +1 -1
  31. package/dist/components/dialogs/SettingsDialog.js +61 -23
  32. package/dist/components/dialogs/SettingsDialog.js.map +1 -1
  33. package/dist/components/dialogs/TracklistDialog.d.ts +2 -2
  34. package/dist/components/dialogs/TracklistDialog.js +8 -3
  35. package/dist/components/dialogs/TracklistDialog.js.map +1 -1
  36. package/dist/components/dialogs/UserProvidedDomainsDialog.js +10 -4
  37. package/dist/components/dialogs/UserProvidedDomainsDialog.js.map +1 -1
  38. package/dist/components/header/Header.js +3 -1
  39. package/dist/components/header/Header.js.map +1 -1
  40. package/dist/components/header/HeaderInfoArea.js +5 -2
  41. package/dist/components/header/HeaderInfoArea.js.map +1 -1
  42. package/dist/components/header/HeaderMenu.js +12 -4
  43. package/dist/components/header/HeaderMenu.js.map +1 -1
  44. package/dist/components/header/HeaderMenuExtra.js +34 -16
  45. package/dist/components/header/HeaderMenuExtra.js.map +1 -1
  46. package/dist/components/header/ZoomControls.js +18 -6
  47. package/dist/components/header/ZoomControls.js.map +1 -1
  48. package/dist/components/import/ImportForm.d.ts +2 -2
  49. package/dist/components/import/ImportForm.js +15 -2
  50. package/dist/components/import/ImportForm.js.map +1 -1
  51. package/dist/components/import/ImportFormExamples.js +44 -31
  52. package/dist/components/import/ImportFormExamples.js.map +1 -1
  53. package/dist/components/minimap/Minimap.js +12 -4
  54. package/dist/components/minimap/Minimap.js.map +1 -1
  55. package/dist/components/msa/MSACanvasBlock.js +5 -3
  56. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  57. package/dist/components/msa/MSAMouseoverCanvas.d.ts +1 -1
  58. package/dist/components/msa/MSAMouseoverCanvas.js +1 -1
  59. package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
  60. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +2 -2
  61. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  62. package/dist/components/msa/renderMSABlock.js +28 -24
  63. package/dist/components/msa/renderMSABlock.js.map +1 -1
  64. package/dist/components/msa/renderMSAMouseover.js +3 -3
  65. package/dist/components/msa/renderMSAMouseover.js.map +1 -1
  66. package/dist/components/tree/TreeBranchMenu.js +6 -3
  67. package/dist/components/tree/TreeBranchMenu.js.map +1 -1
  68. package/dist/components/tree/TreeCanvasBlock.js +11 -4
  69. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  70. package/dist/components/tree/TreeNodeMenu.js +1 -1
  71. package/dist/components/tree/TreeNodeMenu.js.map +1 -1
  72. package/dist/components/tree/dialogs/TreeNodeInfoDialog.d.ts +2 -2
  73. package/dist/components/tree/dialogs/TreeNodeInfoDialog.js +7 -5
  74. package/dist/components/tree/dialogs/TreeNodeInfoDialog.js.map +1 -1
  75. package/dist/components/tree/renderTreeCanvas.js +5 -5
  76. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  77. package/dist/components/util.js +0 -3
  78. package/dist/components/util.js.map +1 -1
  79. package/dist/ggplotPalettes.js.map +1 -1
  80. package/dist/launchInterProScan.js +4 -4
  81. package/dist/launchInterProScan.js.map +1 -1
  82. package/dist/model/DialogQueue.d.ts +1 -1
  83. package/dist/model/DialogQueue.js +0 -1
  84. package/dist/model/DialogQueue.js.map +1 -1
  85. package/dist/model.d.ts +59 -25
  86. package/dist/model.js +123 -62
  87. package/dist/model.js.map +1 -1
  88. package/dist/parseGFF.js +8 -6
  89. package/dist/parseGFF.js.map +1 -1
  90. package/dist/parseNewick.d.ts +4 -4
  91. package/dist/parseNewick.js +7 -8
  92. package/dist/parseNewick.js.map +1 -1
  93. package/dist/parsers/ClustalMSA.d.ts +5 -5
  94. package/dist/parsers/ClustalMSA.js +2 -2
  95. package/dist/parsers/ClustalMSA.js.map +1 -1
  96. package/dist/parsers/EmfMSA.d.ts +27 -0
  97. package/dist/parsers/EmfMSA.js +53 -0
  98. package/dist/parsers/EmfMSA.js.map +1 -0
  99. package/dist/parsers/EmfTree.d.ts +5 -0
  100. package/dist/parsers/EmfTree.js +8 -0
  101. package/dist/parsers/EmfTree.js.map +1 -0
  102. package/dist/parsers/FastaMSA.js +5 -5
  103. package/dist/parsers/FastaMSA.js.map +1 -1
  104. package/dist/parsers/StockholmMSA.d.ts +6 -6
  105. package/dist/parsers/StockholmMSA.js +6 -6
  106. package/dist/parsers/StockholmMSA.js.map +1 -1
  107. package/dist/renderToSvg.js +3 -6
  108. package/dist/renderToSvg.js.map +1 -1
  109. package/dist/reparseTree.js +2 -2
  110. package/dist/reparseTree.js.map +1 -1
  111. package/dist/rowCoordinateCalculations.d.ts +2 -0
  112. package/dist/rowCoordinateCalculations.js +26 -0
  113. package/dist/rowCoordinateCalculations.js.map +1 -0
  114. package/dist/rowCoordinateCalculations.test.d.ts +1 -0
  115. package/dist/rowCoordinateCalculations.test.js +18 -0
  116. package/dist/rowCoordinateCalculations.test.js.map +1 -0
  117. package/dist/util.d.ts +3 -3
  118. package/dist/util.js +1 -1
  119. package/dist/util.js.map +1 -1
  120. package/dist/version.d.ts +1 -1
  121. package/dist/version.js +1 -1
  122. package/package.json +8 -3
  123. package/src/colorSchemes.ts +4 -4
  124. package/src/components/Loading.tsx +7 -1
  125. package/src/components/ResizeHandles.tsx +6 -2
  126. package/src/components/SequenceTextArea.tsx +10 -4
  127. package/src/components/TextTrack.tsx +2 -2
  128. package/src/components/Track.tsx +8 -4
  129. package/src/components/VerticalScrollbar.tsx +6 -2
  130. package/src/components/dialogs/AboutDialog.tsx +7 -1
  131. package/src/components/dialogs/AddTrackDialog.tsx +13 -3
  132. package/src/components/dialogs/DomainDialog.tsx +9 -2
  133. package/src/components/dialogs/ExportSVGDialog.tsx +36 -17
  134. package/src/components/dialogs/FeatureDialog.tsx +7 -5
  135. package/src/components/dialogs/InterProScanDialog.tsx +20 -10
  136. package/src/components/dialogs/MetadataDialog.tsx +8 -1
  137. package/src/components/dialogs/SettingsDialog.tsx +76 -32
  138. package/src/components/dialogs/TracklistDialog.tsx +17 -3
  139. package/src/components/dialogs/UserProvidedDomainsDialog.tsx +11 -5
  140. package/src/components/header/Header.tsx +3 -1
  141. package/src/components/header/HeaderInfoArea.tsx +3 -2
  142. package/src/components/header/HeaderMenu.tsx +12 -7
  143. package/src/components/header/HeaderMenuExtra.tsx +28 -16
  144. package/src/components/header/ZoomControls.tsx +22 -6
  145. package/src/components/import/ImportForm.tsx +14 -2
  146. package/src/components/import/ImportFormExamples.tsx +59 -48
  147. package/src/components/minimap/Minimap.tsx +21 -9
  148. package/src/components/msa/MSACanvasBlock.tsx +5 -3
  149. package/src/components/msa/MSAMouseoverCanvas.tsx +5 -1
  150. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +4 -4
  151. package/src/components/msa/renderMSABlock.ts +42 -38
  152. package/src/components/msa/renderMSAMouseover.ts +3 -3
  153. package/src/components/tree/TreeBranchMenu.tsx +5 -3
  154. package/src/components/tree/TreeCanvasBlock.tsx +11 -4
  155. package/src/components/tree/TreeNodeMenu.tsx +1 -1
  156. package/src/components/tree/dialogs/TreeNodeInfoDialog.tsx +17 -4
  157. package/src/components/tree/renderTreeCanvas.ts +2 -2
  158. package/src/components/util.ts +1 -4
  159. package/src/ggplotPalettes.ts +1 -1
  160. package/src/launchInterProScan.ts +5 -5
  161. package/src/model/DialogQueue.ts +0 -1
  162. package/src/model.ts +136 -74
  163. package/src/parseGFF.ts +13 -11
  164. package/src/parseNewick.ts +10 -10
  165. package/src/parsers/ClustalMSA.ts +4 -4
  166. package/src/parsers/EmfMSA.ts +66 -0
  167. package/src/parsers/EmfTree.ts +9 -0
  168. package/src/parsers/FastaMSA.ts +6 -6
  169. package/src/parsers/StockholmMSA.ts +12 -12
  170. package/src/renderToSvg.tsx +1 -2
  171. package/src/reparseTree.ts +3 -3
  172. package/src/rowCoordinateCalculations.test.ts +19 -0
  173. package/src/rowCoordinateCalculations.ts +26 -0
  174. package/src/util.ts +5 -5
  175. package/src/version.ts +1 -1
@@ -11,7 +11,7 @@ import {
11
11
  import type { MsaViewModel } from '../../../model'
12
12
  import SequenceTextArea from '../../SequenceTextArea'
13
13
 
14
- export default observer(function ({
14
+ const TreeNodeInfoDialog = observer(function ({
15
15
  info,
16
16
  model,
17
17
  nodeName,
@@ -24,15 +24,26 @@ export default observer(function ({
24
24
  }) {
25
25
  const { treeMetadata, rows } = model
26
26
  const metadata = treeMetadata[nodeName]
27
- const [name, sequence] = rows.find(f => f[0] === nodeName)!
27
+ const res = rows.find(f => f[0] === nodeName)
28
28
  return (
29
- <Dialog onClose={() => onClose()} open title="Tree node info" maxWidth="xl">
29
+ <Dialog
30
+ onClose={() => {
31
+ onClose()
32
+ }}
33
+ open
34
+ title="Tree node info"
35
+ maxWidth="xl"
36
+ >
30
37
  <DialogContent>
31
38
  <BaseCard title="Attributes">
32
39
  <Attributes attributes={{ nodeName, ...info }} />
33
40
  </BaseCard>
34
41
  <BaseCard title="Sequence">
35
- <SequenceTextArea str={[[name, sequence]]} />
42
+ {res ? (
43
+ <SequenceTextArea str={[res]} />
44
+ ) : (
45
+ <div>Sequence not found</div>
46
+ )}
36
47
  </BaseCard>
37
48
  {metadata ? (
38
49
  <BaseCard title="Extra metadata">
@@ -43,3 +54,5 @@ export default observer(function ({
43
54
  </Dialog>
44
55
  )
45
56
  })
57
+
58
+ export default TreeNodeInfoDialog
@@ -147,8 +147,6 @@ export function renderTreeLabels({
147
147
  marginLeft,
148
148
  leaves,
149
149
  noTree,
150
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
151
- rowHeight: _rowHeight, // this is needed for redrawing after zoom change
152
150
  } = model
153
151
  const by = blockSizeYOverride || blockSize
154
152
  if (labelsAlignRight) {
@@ -250,6 +248,8 @@ export function renderTreeCanvas({
250
248
  showTreeText,
251
249
  marginLeft,
252
250
  nref,
251
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
252
+ rowHeight: _rowHeight, // this is needed for redrawing after zoom change
253
253
  } = model
254
254
  const by = blockSizeYOverride || blockSize
255
255
 
@@ -11,7 +11,7 @@ export function chooseGridPitch(
11
11
  scale = Math.abs(scale)
12
12
  const minMajorPitchBp = minMajorPitchPx * scale
13
13
  const majorMagnitude = Number.parseInt(
14
- Number(minMajorPitchBp).toExponential().split(/e/i)[1],
14
+ Number(minMajorPitchBp).toExponential().split(/e/i)[1]!,
15
15
  10,
16
16
  )
17
17
 
@@ -50,9 +50,6 @@ export function makeTicks(
50
50
 
51
51
  let minBase = start
52
52
  let maxBase = end
53
- if (minBase === null || maxBase === null) {
54
- return []
55
- }
56
53
 
57
54
  if (bpPerPx < 0) {
58
55
  ;[minBase, maxBase] = [maxBase, minBase]
@@ -19,7 +19,7 @@ const palettes = [
19
19
  ]
20
20
 
21
21
  export function getPalette(l: number) {
22
- return palettes[Math.min(l, palettes.length - 1)]
22
+ return palettes[Math.min(l, palettes.length - 1)]!
23
23
  }
24
24
 
25
25
  export default palettes
@@ -38,7 +38,7 @@ async function runInterProScan({
38
38
  method: 'POST',
39
39
  body: new URLSearchParams({
40
40
  email: 'colin.diesh@gmail.com',
41
- sequence: `${seq}`,
41
+ sequence: seq,
42
42
  programs: programs.join(','),
43
43
  }),
44
44
  })
@@ -47,7 +47,7 @@ async function runInterProScan({
47
47
  jobId,
48
48
  onProgress,
49
49
  })
50
- return loadInterProScanResultsWithStatus({ jobId, model })
50
+ await loadInterProScanResultsWithStatus({ jobId, model })
51
51
  }
52
52
 
53
53
  export function loadInterProScanResults(jobId: string) {
@@ -65,6 +65,7 @@ async function wait({
65
65
  }) {
66
66
  const url = `${base}/iprscan5/status/${jobId}`
67
67
  try {
68
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
68
69
  while (true) {
69
70
  for (let i = 0; i < 10; i++) {
70
71
  await timeout(1000)
@@ -101,14 +102,13 @@ export async function launchInterProScan({
101
102
  try {
102
103
  onProgress({ msg: `Launching ${algorithm} MSA` })
103
104
  if (algorithm === 'interproscan') {
104
- const result = await runInterProScan({
105
+ await runInterProScan({
105
106
  seq,
106
107
  onJobId,
107
108
  onProgress,
108
109
  programs,
109
110
  model,
110
111
  })
111
- return result
112
112
  }
113
113
  throw new Error('unknown algorithm')
114
114
  } finally {
@@ -130,7 +130,7 @@ export async function loadInterProScanResultsWithStatus({
130
130
  })
131
131
  const ret = await loadInterProScanResults(jobId)
132
132
  model.setInterProAnnotations(
133
- Object.fromEntries(ret.results.map(r => [r.xref[0].id, r])),
133
+ Object.fromEntries(ret.results.map(r => [r.xref[0]!.id, r])),
134
134
  )
135
135
  model.setShowDomains(true)
136
136
  getSession(model).notify(`Loaded interproscan ${jobId} results`, 'success')
@@ -8,7 +8,6 @@ export function DialogQueueSessionMixin() {
8
8
  return types
9
9
  .model('DialogQueueSessionMixin', {})
10
10
  .volatile(() => ({
11
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
11
  queueOfDialogs: [] as [DialogComponentType, any][],
13
12
  }))
14
13
  .views(self => ({
package/src/model.ts CHANGED
@@ -42,6 +42,7 @@ import TextTrack from './components/TextTrack'
42
42
 
43
43
  // parsers
44
44
  import ClustalMSA from './parsers/ClustalMSA'
45
+ import EmfMSA from './parsers/EmfMSA'
45
46
  import StockholmMSA from './parsers/StockholmMSA'
46
47
  import FastaMSA from './parsers/FastaMSA'
47
48
  import parseNewick from './parseNewick'
@@ -53,6 +54,11 @@ import { DialogQueueSessionMixin } from './model/DialogQueue'
53
54
  import { TreeF } from './model/treeModel'
54
55
  import { MSAModelF } from './model/msaModel'
55
56
  import type { InterProScanResults } from './launchInterProScan'
57
+ import {
58
+ mouseOverCoordToGlobalCoord,
59
+ globalCoordToRowSpecificCoord,
60
+ } from './rowCoordinateCalculations'
61
+ import EmfTree from './parsers/EmfTree'
56
62
 
57
63
  export interface Accession {
58
64
  accession: string
@@ -72,7 +78,6 @@ export interface TextTrackModel extends BasicTrackModel {
72
78
  }
73
79
 
74
80
  export interface ITextTrack {
75
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
76
81
  ReactComponent: React.FC<any>
77
82
  model: TextTrackModel
78
83
  }
@@ -147,7 +152,7 @@ function stateModelFactory() {
147
152
  * #property
148
153
  * height of each row, px
149
154
  */
150
- rowHeight: 20,
155
+ rowHeight: 18,
151
156
 
152
157
  /**
153
158
  * #property
@@ -165,7 +170,7 @@ function stateModelFactory() {
165
170
  * #property
166
171
  * width of columns, px
167
172
  */
168
- colWidth: 16,
173
+ colWidth: 14,
169
174
 
170
175
  /**
171
176
  * #property
@@ -221,7 +226,11 @@ function stateModelFactory() {
221
226
  * data from the loaded tree/msa/treeMetadata, generally loaded by
222
227
  * autorun
223
228
  */
224
- data: types.optional(DataModelF(), { tree: '', msa: '' }),
229
+ data: types.optional(DataModelF(), {
230
+ tree: '',
231
+ msa: '',
232
+ treeMetadata: '',
233
+ }),
225
234
 
226
235
  /**
227
236
  * #property
@@ -473,7 +482,7 @@ function stateModelFactory() {
473
482
  /**
474
483
  * #action
475
484
  */
476
- setData(data: { msa?: string; tree?: string }) {
485
+ setData(data: { msa?: string; tree?: string; treeMetadata?: string }) {
477
486
  self.data = cast(data)
478
487
  },
479
488
 
@@ -514,6 +523,12 @@ function stateModelFactory() {
514
523
  }))
515
524
 
516
525
  .views(self => ({
526
+ /**
527
+ * #getter
528
+ */
529
+ get realAllowedGappyness() {
530
+ return self.hideGaps ? self.allowedGappyness : 100
531
+ },
517
532
  /**
518
533
  * #getter
519
534
  */
@@ -548,7 +563,7 @@ function stateModelFactory() {
548
563
  * #getter
549
564
  */
550
565
  get colorScheme() {
551
- return colorSchemes[self.colorSchemeName]
566
+ return colorSchemes[self.colorSchemeName]!
552
567
  },
553
568
 
554
569
  /**
@@ -588,7 +603,7 @@ function stateModelFactory() {
588
603
  /**
589
604
  * #getter
590
605
  */
591
- get menuItems() {
606
+ menuItems() {
592
607
  return []
593
608
  },
594
609
  /**
@@ -605,11 +620,13 @@ function stateModelFactory() {
605
620
  if (text) {
606
621
  if (Stockholm.sniff(text)) {
607
622
  return new StockholmMSA(text, self.currentAlignment)
608
- }
609
- if (text.startsWith('>')) {
623
+ } else if (text.startsWith('>')) {
610
624
  return new FastaMSA(text)
625
+ } else if (text.startsWith('SEQ')) {
626
+ return new EmfMSA(text)
627
+ } else {
628
+ return new ClustalMSA(text)
611
629
  }
612
- return new ClustalMSA(text)
613
630
  }
614
631
  return null
615
632
  },
@@ -624,14 +641,25 @@ function stateModelFactory() {
624
641
  * #getter
625
642
  */
626
643
  get tree(): NodeWithIds {
627
- const ret = self.data.tree
628
- ? generateNodeIds(parseNewick(self.data.tree))
629
- : this.MSA?.getTree() || {
630
- noTree: true,
631
- branchset: [],
632
- id: 'empty',
633
- name: 'empty',
634
- }
644
+ const text = self.data.tree
645
+ let ret: NodeWithIds
646
+ if (text) {
647
+ let t: string
648
+ if (text.startsWith('SEQ')) {
649
+ const r = new EmfTree(text)
650
+ t = r.data.tree
651
+ } else {
652
+ t = text
653
+ }
654
+ ret = generateNodeIds(parseNewick(t))
655
+ } else {
656
+ ret = this.MSA?.getTree() || {
657
+ noTree: true,
658
+ children: [],
659
+ id: 'empty',
660
+ name: 'empty',
661
+ }
662
+ }
635
663
  return reparseTree(ret)
636
664
  },
637
665
  /**
@@ -652,8 +680,10 @@ function stateModelFactory() {
652
680
  * #getter
653
681
  */
654
682
  get root() {
655
- let hier = hierarchy(this.tree, d => d.branchset)
656
- .sum(d => (d.branchset ? 0 : 1))
683
+ let hier = hierarchy(this.tree, d => d.children)
684
+ // todo: investigate whether needed, typescript says children always true
685
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
686
+ .sum(d => (d.children ? 0 : 1))
657
687
  .sort((a, b) => ascending(a.data.length || 1, b.data.length || 1))
658
688
 
659
689
  if (self.showOnly) {
@@ -666,7 +696,9 @@ function stateModelFactory() {
666
696
  ;[...self.collapsed, ...self.collapsedLeaves]
667
697
  .map(collapsedId => hier.find(node => node.data.id === collapsedId))
668
698
  .filter(notEmpty)
669
- .map(node => collapse(node))
699
+ .map(node => {
700
+ collapse(node)
701
+ })
670
702
 
671
703
  return hier
672
704
  },
@@ -689,25 +721,34 @@ function stateModelFactory() {
689
721
  * #getter
690
722
  */
691
723
  get blanks() {
692
- const { allowedGappyness } = self
724
+ const { hideGaps, realAllowedGappyness } = self
693
725
  const blanks = []
694
- const strs = this.leaves
695
- .map(leaf => this.MSA?.getRow(leaf.data.name))
696
- .filter((item): item is string => !!item)
697
-
698
- for (let i = 0; i < strs[0]?.length; i++) {
699
- let counter = 0
700
- for (const str of strs) {
701
- if (str[i] === '-') {
702
- counter++
726
+ if (hideGaps) {
727
+ const strs = this.leaves
728
+ .map(leaf => this.MSA?.getRow(leaf.data.name))
729
+ .filter((item): item is string => !!item)
730
+ if (strs.length) {
731
+ for (let i = 0; i < strs[0]!.length; i++) {
732
+ let counter = 0
733
+ for (const str of strs) {
734
+ if (str[i] === '-') {
735
+ counter++
736
+ }
737
+ }
738
+ if (counter / strs.length >= realAllowedGappyness / 100) {
739
+ blanks.push(i)
740
+ }
703
741
  }
704
742
  }
705
- if (counter / strs.length >= allowedGappyness / 100) {
706
- blanks.push(i)
707
- }
708
743
  }
709
744
  return blanks
710
745
  },
746
+ /**
747
+ * #getter
748
+ */
749
+ get blanksSet() {
750
+ return new Set(this.blanks)
751
+ },
711
752
  /**
712
753
  * #getter
713
754
  */
@@ -717,13 +758,20 @@ function stateModelFactory() {
717
758
  .map(leaf => [leaf.data.name, MSA?.getRow(leaf.data.name)] as const)
718
759
  .filter((f): f is [string, string] => !!f[1])
719
760
  },
761
+
762
+ /**
763
+ * #getter
764
+ */
765
+ get rowMap() {
766
+ return new Map(this.rows)
767
+ },
720
768
  /**
721
769
  * #getter
722
770
  */
723
771
  get columns() {
724
772
  return Object.fromEntries(
725
773
  this.rows.map(
726
- (row, index) => [row[0], this.columns2d[index]] as const,
774
+ (row, index) => [row[0], this.columns2d[index]!] as const,
727
775
  ),
728
776
  )
729
777
  },
@@ -737,7 +785,7 @@ function stateModelFactory() {
737
785
  * #getter
738
786
  */
739
787
  get fontSize() {
740
- return Math.min(Math.max(6, self.rowHeight - 8), 18)
788
+ return Math.min(Math.max(6, self.rowHeight - 3), 18)
741
789
  },
742
790
  /**
743
791
  * #getter
@@ -748,10 +796,11 @@ function stateModelFactory() {
748
796
  for (const column of columns) {
749
797
  for (let j = 0; j < column.length; j++) {
750
798
  const l = r[j] || {}
751
- if (!l[column[j]]) {
752
- l[column[j]] = 0
799
+ const cj = column[j]!
800
+ if (!l[cj]) {
801
+ l[cj] = 0
753
802
  }
754
- l[column[j]]++
803
+ l[cj]++
755
804
  r[j] = l
756
805
  }
757
806
  }
@@ -843,7 +892,7 @@ function stateModelFactory() {
843
892
  const ret = []
844
893
  for (const by of self.blocksY) {
845
894
  for (const bx of self.blocksX) {
846
- ret.push([bx, by])
895
+ ret.push([bx, by] as const)
847
896
  }
848
897
  }
849
898
  return ret
@@ -1013,7 +1062,7 @@ function stateModelFactory() {
1013
1062
  */
1014
1063
  get conservation() {
1015
1064
  if (self.columns2d.length) {
1016
- for (let i = 0; i < self.columns2d[0].length; i++) {
1065
+ for (let i = 0; i < self.columns2d[0]!.length; i++) {
1017
1066
  const col = []
1018
1067
  for (const column of self.columns2d) {
1019
1068
  col.push(column[i])
@@ -1060,28 +1109,39 @@ function stateModelFactory() {
1060
1109
  return self.msaAreaWidth < self.totalWidth
1061
1110
  },
1062
1111
 
1112
+ /**
1113
+ * #getter
1114
+ */
1115
+ get rowNamesSet() {
1116
+ return new Map(self.rowNames.map((r, idx) => [r, idx]))
1117
+ },
1118
+
1063
1119
  /**
1064
1120
  * #method
1065
- * return a row-specific sequence coordinate, skipping gaps, given a global
1066
- * coordinate
1121
+ * return a row-specific letter, or undefined if gap
1067
1122
  */
1068
- globalCoordToRowSpecificSeqCoord(rowName: string, position: number) {
1069
- const { rowNames, rows } = self
1070
- const index = rowNames.indexOf(rowName)
1071
- if (index !== -1 && rows[index]) {
1072
- const row = rows[index][1]
1123
+ mouseOverCoordToRowLetter(rowName: string, position: number) {
1124
+ const { rowMap, blanks } = self
1125
+ return rowMap.get(rowName)?.[
1126
+ mouseOverCoordToGlobalCoord(blanks, position)
1127
+ ]
1128
+ },
1073
1129
 
1074
- let k = 0
1075
- for (let i = 0; i < position; i++) {
1076
- if (row[i] !== '-') {
1077
- k++
1078
- } else if (k >= position) {
1079
- break
1080
- }
1081
- }
1082
- return k
1130
+ /**
1131
+ * #method
1132
+ * return a row-specific sequence coordinate, skipping gaps, given a
1133
+ * global coordinate
1134
+ */
1135
+ mouseOverCoordToGapRemovedRowCoord(rowName: string, position: number) {
1136
+ const { rowMap, blanks } = self
1137
+ const seq = rowMap.get(rowName)
1138
+ if (seq !== undefined) {
1139
+ const pos2 = mouseOverCoordToGlobalCoord(blanks, position)
1140
+ const pos1 = globalCoordToRowSpecificCoord(seq, pos2)
1141
+ return seq[pos1] === '-' || !seq[pos1] ? undefined : pos1
1142
+ } else {
1143
+ return undefined
1083
1144
  }
1084
- return 0
1085
1145
  },
1086
1146
 
1087
1147
  /**
@@ -1132,19 +1192,17 @@ function stateModelFactory() {
1132
1192
  /**
1133
1193
  * #getter
1134
1194
  */
1135
- get tidyTypes() {
1195
+ get tidyInterProAnnotationTypes() {
1136
1196
  const types = new Map<string, Accession>()
1137
- if (this.tidyAnnotations) {
1138
- for (const { name, accession, description } of this.tidyAnnotations) {
1139
- types.set(accession, { name, accession, description })
1140
- }
1197
+ for (const annot of this.tidyInterProAnnotations) {
1198
+ types.set(annot.accession, annot)
1141
1199
  }
1142
1200
  return types
1143
1201
  },
1144
1202
  /**
1145
1203
  * #getter
1146
1204
  */
1147
- get tidyAnnotations() {
1205
+ get tidyInterProAnnotations() {
1148
1206
  const ret = []
1149
1207
  const { interProAnnotations } = self
1150
1208
  if (interProAnnotations) {
@@ -1172,16 +1230,16 @@ function stateModelFactory() {
1172
1230
  /**
1173
1231
  * #getter
1174
1232
  */
1175
- get tidyFilteredAnnotations() {
1176
- return this.tidyAnnotations.filter(r =>
1233
+ get tidyFilteredInterProAnnotations() {
1234
+ return this.tidyInterProAnnotations.filter(r =>
1177
1235
  self.featureFilters.get(r.accession),
1178
1236
  )
1179
1237
  },
1180
1238
  /**
1181
1239
  * #getter
1182
1240
  */
1183
- get tidyFilteredGatheredAnnotations() {
1184
- return groupBy(this.tidyFilteredAnnotations, r => r.id)
1241
+ get tidyFilteredGatheredInterProAnnotations() {
1242
+ return groupBy(this.tidyFilteredInterProAnnotations, r => r.id)
1185
1243
  },
1186
1244
  }))
1187
1245
  .views(self => ({
@@ -1203,12 +1261,12 @@ function stateModelFactory() {
1203
1261
  * #getter
1204
1262
  */
1205
1263
  get fillPalette() {
1206
- const arr = [...self.tidyTypes.keys()]
1264
+ const arr = [...self.tidyInterProAnnotationTypes.keys()]
1207
1265
  let i = 0
1208
1266
  const map = {} as Record<string, string>
1209
1267
  for (const key of arr) {
1210
1268
  const k = Math.min(arr.length - 1, palettes.length - 1)
1211
- map[key] = palettes[k][i]
1269
+ map[key] = palettes[k]![i]!
1212
1270
  i++
1213
1271
  }
1214
1272
  return map
@@ -1285,7 +1343,7 @@ function stateModelFactory() {
1285
1343
  addDisposer(
1286
1344
  self,
1287
1345
  autorun(() => {
1288
- for (const key of self.tidyTypes.keys()) {
1346
+ for (const key of self.tidyInterProAnnotationTypes.keys()) {
1289
1347
  this.initFilter(key)
1290
1348
  }
1291
1349
  }),
@@ -1361,17 +1419,21 @@ function stateModelFactory() {
1361
1419
  }),
1362
1420
  )
1363
1421
 
1422
+ // force colStats not to go stale
1423
+ // xref solution https://github.com/mobxjs/mobx/issues/266#issuecomment-222007278
1424
+ // xref problem https://github.com/GMOD/react-msaview/issues/75
1364
1425
  addDisposer(
1365
1426
  self,
1366
1427
  autorun(() => {
1367
- // force colStats not to go stale,
1368
- // xref solution https://github.com/mobxjs/mobx/issues/266#issuecomment-222007278
1369
- // xref problem https://github.com/GMOD/react-msaview/issues/75
1428
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
1370
1429
  self.colStats
1430
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
1371
1431
  self.colStatsSums
1432
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
1372
1433
  self.columns
1373
1434
  }),
1374
1435
  )
1436
+
1375
1437
  // autorun synchronizes treeWidth with treeAreaWidth
1376
1438
  addDisposer(
1377
1439
  self,
package/src/parseGFF.ts CHANGED
@@ -8,23 +8,25 @@ export function parseGFF(str?: string) {
8
8
  f.split('\t')
9
9
 
10
10
  return {
11
- seq_id,
12
- source,
13
- type,
14
- start: +start,
15
- end: +end,
16
- score: +score,
17
- strand,
18
- phase,
11
+ seq_id: seq_id!,
12
+ source: source!,
13
+ type: type!,
14
+ start: +start!,
15
+ end: +end!,
16
+ score: +score!,
17
+ strand: strand!,
18
+ phase: phase!,
19
19
  ...Object.fromEntries(
20
- col9
20
+ col9!
21
21
  .split(';')
22
22
  .map(f => f.trim())
23
23
  .filter(f => !!f)
24
24
  .map(f => f.split('='))
25
25
  .map(([key, val]) => [
26
- key.trim(),
27
- decodeURIComponent(val).trim().split(',').join(' '),
26
+ key!.trim(),
27
+ val
28
+ ? decodeURIComponent(val).trim().split(',').join(' ')
29
+ : undefined,
28
30
  ]),
29
31
  ),
30
32
  }
@@ -34,13 +34,13 @@
34
34
  * Converted to JSON:
35
35
  * {
36
36
  * name: "F",
37
- * branchset: [
37
+ * children: [
38
38
  * {name: "A", length: 0.1},
39
39
  * {name: "B", length: 0.2},
40
40
  * {
41
41
  * name: "E",
42
42
  * length: 0.5,
43
- * branchset: [
43
+ * children: [
44
44
  * {name: "C", length: 0.3},
45
45
  * {name: "D", length: 0.4}
46
46
  * ]
@@ -50,29 +50,29 @@
50
50
  *
51
51
  * Converted to JSON, but with no names or lengths:
52
52
  * {
53
- * branchset: [
53
+ * children: [
54
54
  * {}, {}, {
55
- * branchset: [{}, {}]
55
+ * children: [{}, {}]
56
56
  * }
57
57
  * ]
58
58
  * }
59
59
  */
60
60
  export default function parse(s: string) {
61
61
  const ancestors = []
62
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
+
63
63
  let tree = {} as Record<string, any>
64
64
  const tokens = s.split(/\s*(;|\(|\)|,|:)\s*/)
65
65
  for (let i = 0; i < tokens.length; i++) {
66
- const token = tokens[i]
66
+ const token = tokens[i]!
67
67
  const subtree = {}
68
68
  switch (token) {
69
- case '(': // new branchset
70
- tree.branchset = [subtree]
69
+ case '(': // new children
70
+ tree.children = [subtree]
71
71
  ancestors.push(tree)
72
72
  tree = subtree
73
73
  break
74
74
  case ',': // another branch
75
- ancestors.at(-1)?.branchset.push(subtree)
75
+ ancestors.at(-1)?.children.push(subtree)
76
76
  tree = subtree
77
77
  break
78
78
  case ')': // optional name next
@@ -81,7 +81,7 @@ export default function parse(s: string) {
81
81
  case ':': // optional length next
82
82
  break
83
83
  default: {
84
- const x = tokens[i - 1]
84
+ const x = tokens[i - 1]!
85
85
  if (x === ')' || x === '(' || x === ',') {
86
86
  tree.name = token
87
87
  } else if (x === ':') {