react-msaview 3.0.0 → 3.0.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 (84) hide show
  1. package/bundle/index.js +31 -31
  2. package/dist/components/Header.js +4 -9
  3. package/dist/components/Header.js.map +1 -1
  4. package/dist/components/HeaderInfoArea.d.ts +6 -0
  5. package/dist/components/HeaderInfoArea.js +12 -0
  6. package/dist/components/HeaderInfoArea.js.map +1 -0
  7. package/dist/components/MSAPanel/Loading.d.ts +2 -0
  8. package/dist/components/MSAPanel/Loading.js +12 -0
  9. package/dist/components/MSAPanel/Loading.js.map +1 -0
  10. package/dist/components/MSAPanel/MSACanvas.js +1 -10
  11. package/dist/components/MSAPanel/MSACanvas.js.map +1 -1
  12. package/dist/components/MSAPanel/MSAMouseoverCanvas.js +1 -22
  13. package/dist/components/MSAPanel/MSAMouseoverCanvas.js.map +1 -1
  14. package/dist/components/MSAPanel/renderMSABlock.js +0 -1
  15. package/dist/components/MSAPanel/renderMSABlock.js.map +1 -1
  16. package/dist/components/MSAPanel/renderMSAMouseover.d.ts +5 -0
  17. package/dist/components/MSAPanel/renderMSAMouseover.js +24 -0
  18. package/dist/components/MSAPanel/renderMSAMouseover.js.map +1 -0
  19. package/dist/components/Minimap.js +13 -13
  20. package/dist/components/Minimap.js.map +1 -1
  21. package/dist/components/TreePanel/TreeCanvasBlock.js +8 -4
  22. package/dist/components/TreePanel/TreeCanvasBlock.js.map +1 -1
  23. package/dist/components/TreePanel/{TreeMenu.d.ts → TreeNodeMenu.d.ts} +1 -0
  24. package/dist/components/TreePanel/{TreeMenu.js → TreeNodeMenu.js} +8 -4
  25. package/dist/components/TreePanel/TreeNodeMenu.js.map +1 -0
  26. package/dist/components/TreePanel/dialogs/{TreeNodeInfoDlg.js → TreeNodeInfoDialog.js} +2 -2
  27. package/dist/components/TreePanel/dialogs/TreeNodeInfoDialog.js.map +1 -0
  28. package/dist/components/TreePanel/renderTreeCanvas.d.ts +8 -3
  29. package/dist/components/TreePanel/renderTreeCanvas.js +8 -7
  30. package/dist/components/TreePanel/renderTreeCanvas.js.map +1 -1
  31. package/dist/components/dialogs/SettingsDialog.js +31 -22
  32. package/dist/components/dialogs/SettingsDialog.js.map +1 -1
  33. package/dist/model.d.ts +120 -62
  34. package/dist/model.js +176 -124
  35. package/dist/model.js.map +1 -1
  36. package/dist/parsers/ClustalMSA.d.ts +18 -5
  37. package/dist/parsers/ClustalMSA.js +1 -1
  38. package/dist/parsers/ClustalMSA.js.map +1 -1
  39. package/dist/parsers/FastaMSA.d.ts +1 -1
  40. package/dist/parsers/FastaMSA.js +2 -2
  41. package/dist/parsers/FastaMSA.js.map +1 -1
  42. package/dist/parsers/StockholmMSA.d.ts +1 -1
  43. package/dist/parsers/StockholmMSA.js +2 -2
  44. package/dist/parsers/StockholmMSA.js.map +1 -1
  45. package/dist/util.d.ts +1 -0
  46. package/dist/util.js +15 -7
  47. package/dist/util.js.map +1 -1
  48. package/dist/version.d.ts +1 -1
  49. package/dist/version.js +1 -1
  50. package/package.json +9 -7
  51. package/src/components/Header.tsx +5 -11
  52. package/src/components/HeaderInfoArea.tsx +21 -0
  53. package/src/components/MSAPanel/Loading.tsx +16 -0
  54. package/src/components/MSAPanel/MSACanvas.tsx +1 -16
  55. package/src/components/MSAPanel/MSAMouseoverCanvas.tsx +2 -50
  56. package/src/components/MSAPanel/renderMSABlock.ts +0 -2
  57. package/src/components/MSAPanel/renderMSAMouseover.ts +51 -0
  58. package/src/components/Minimap.tsx +15 -15
  59. package/src/components/TreePanel/TreeCanvasBlock.tsx +8 -3
  60. package/src/components/TreePanel/{TreeMenu.tsx → TreeNodeMenu.tsx} +12 -3
  61. package/src/components/TreePanel/dialogs/{TreeNodeInfoDlg.tsx → TreeNodeInfoDialog.tsx} +1 -1
  62. package/src/components/TreePanel/renderTreeCanvas.ts +13 -4
  63. package/src/components/dialogs/SettingsDialog.tsx +61 -44
  64. package/src/model.ts +279 -154
  65. package/src/parsers/ClustalMSA.ts +1 -1
  66. package/src/parsers/FastaMSA.ts +1 -1
  67. package/src/parsers/StockholmMSA.ts +1 -1
  68. package/src/util.ts +19 -6
  69. package/src/version.ts +1 -1
  70. package/dist/components/OverviewRubberband.d.ts +0 -8
  71. package/dist/components/OverviewRubberband.js +0 -185
  72. package/dist/components/OverviewRubberband.js.map +0 -1
  73. package/dist/components/Rubberband.d.ts +0 -8
  74. package/dist/components/Rubberband.js +0 -185
  75. package/dist/components/Rubberband.js.map +0 -1
  76. package/dist/components/TreePanel/TreeMenu.js.map +0 -1
  77. package/dist/components/TreePanel/dialogs/TreeNodeInfoDlg.js.map +0 -1
  78. package/dist/components/dialogs/AnnotationDialog.d.ts +0 -11
  79. package/dist/components/dialogs/AnnotationDialog.js +0 -65
  80. package/dist/components/dialogs/AnnotationDialog.js.map +0 -1
  81. package/src/components/OverviewRubberband.tsx +0 -283
  82. package/src/components/Rubberband.tsx +0 -283
  83. package/src/components/dialogs/AnnotationDialog.tsx +0 -144
  84. /package/dist/components/TreePanel/dialogs/{TreeNodeInfoDlg.d.ts → TreeNodeInfoDialog.d.ts} +0 -0
package/src/model.ts CHANGED
@@ -3,27 +3,23 @@ import { autorun } from 'mobx'
3
3
  import { Instance, cast, types, addDisposer, SnapshotIn } from 'mobx-state-tree'
4
4
  import { hierarchy, cluster, HierarchyNode } from 'd3-hierarchy'
5
5
  import { ascending } from 'd3-array'
6
+ import Stockholm from 'stockholm-js'
7
+ // jbrowse
6
8
  import { FileLocation, ElementId } from '@jbrowse/core/util/types/mst'
7
9
  import { FileLocation as FileLocationType } from '@jbrowse/core/util/types'
8
10
  import { openLocation } from '@jbrowse/core/util/io'
9
- import { measureText, sum } from '@jbrowse/core/util'
11
+ import { measureText, notEmpty, sum } from '@jbrowse/core/util'
10
12
  import BaseViewModel from '@jbrowse/core/pluggableElementTypes/models/BaseViewModel'
11
- import Stockholm from 'stockholm-js'
12
-
13
- export interface RowDetails {
14
- [key: string]: unknown
15
- name: string
16
- range?: { start: number; end: number }
17
- }
18
13
 
19
14
  // locals
20
15
  import {
16
+ clamp,
21
17
  collapse,
18
+ filterHiddenLeafNodes,
22
19
  generateNodeIds,
23
20
  maxLength,
24
21
  setBrLength,
25
22
  skipBlanks,
26
- clamp,
27
23
  NodeWithIds,
28
24
  NodeWithIdsAndLength,
29
25
  } from './util'
@@ -38,12 +34,23 @@ import { UniprotTrack } from './UniprotTrack'
38
34
  import { StructureModel } from './StructureModel'
39
35
  import { DialogQueueSessionMixin } from './DialogQueue'
40
36
 
37
+ export interface RowDetails {
38
+ [key: string]: unknown
39
+ name: string
40
+ range?: { start: number; end: number }
41
+ }
42
+
41
43
  interface BasicTrackModel {
42
44
  id: string
43
45
  name: string
44
46
  associatedRowName?: string
45
47
  height: number
46
48
  }
49
+ interface Structure {
50
+ pdb: string
51
+ startPos: number
52
+ endPos: number
53
+ }
47
54
 
48
55
  export interface TextTrackModel extends BasicTrackModel {
49
56
  customColorScheme?: Record<string, string>
@@ -73,6 +80,9 @@ type StructureSnap = SnapshotIn<typeof StructureModel>
73
80
 
74
81
  /**
75
82
  * #stateModel MsaView
83
+ * extends
84
+ * - BaseViewModel
85
+ * - DialogQueueSessionMixin
76
86
  */
77
87
  function x() {} // eslint-disable-line @typescript-eslint/no-unused-vars
78
88
 
@@ -89,136 +99,174 @@ const model = types
89
99
  types.model('MsaView', {
90
100
  /**
91
101
  * #property
102
+ * id of view, randomly generated if not provided
92
103
  */
93
104
  id: ElementId,
105
+
94
106
  /**
95
107
  * #property
108
+ * hardcoded view type
96
109
  */
97
110
  type: types.literal('MsaView'),
111
+
98
112
  /**
99
113
  * #property
114
+ * height of the div containing the view, px
100
115
  */
101
116
  height: types.optional(types.number, 550),
117
+
102
118
  /**
103
119
  * #property
120
+ * width of the area the tree is drawn in, px
104
121
  */
105
122
  treeAreaWidth: types.optional(types.number, 400),
123
+
106
124
  /**
107
125
  * #property
126
+ * width of the tree within the treeArea, px
108
127
  */
109
128
  treeWidth: types.optional(types.number, 300),
129
+
110
130
  /**
111
131
  * #getter
132
+ * synchronization that matches treeWidth to treeAreaWidth
112
133
  */
113
134
  treeWidthMatchesArea: true,
135
+
114
136
  /**
115
137
  * #property
138
+ * height of each row, px
116
139
  */
117
140
  rowHeight: 20,
141
+
118
142
  /**
119
143
  * #property
144
+ * scroll position, Y-offset, px
120
145
  */
121
146
  scrollY: 0,
147
+
122
148
  /**
123
149
  * #property
150
+ * scroll position, X-offset, px
124
151
  */
125
152
  scrollX: 0,
153
+
126
154
  /**
127
155
  * #property
128
- */
129
- resizeHandleWidth: 5,
130
- /**
131
- * #property
132
- */
133
- blockSize: 1000,
134
- /**
135
- * #property
136
- */
137
- mouseRow: types.maybe(types.number),
138
- /**
139
- * #property
140
- */
141
- mouseCol: types.maybe(types.number),
142
- /**
143
- * #property
156
+ * currently "selected" structures, generally PDB 3-D protein structures
144
157
  */
145
158
  selectedStructures: types.array(StructureModel),
159
+
146
160
  /**
147
161
  * #property
162
+ * right-align the labels
148
163
  */
149
164
  labelsAlignRight: false,
165
+
150
166
  /**
151
167
  * #property
168
+ * width of columns, px
152
169
  */
153
170
  colWidth: 16,
171
+
154
172
  /**
155
173
  * #property
174
+ * use "branch length" e.g. evolutionary distance to draw tree branch
175
+ * lengths. if false, the layout is a "cladogram" that does not take into
176
+ * account evolutionary distances
156
177
  */
157
178
  showBranchLen: true,
158
179
  /**
159
180
  * #property
181
+ * draw MSA tiles with a background color
160
182
  */
161
183
  bgColor: true,
184
+
162
185
  /**
163
186
  * #property
187
+ * draw tree, boolean
164
188
  */
165
189
  drawTree: true,
190
+
166
191
  /**
167
192
  * #property
193
+ * draw clickable node bubbles on the tree
168
194
  */
169
195
  drawNodeBubbles: true,
196
+
170
197
  /**
171
198
  * #property
199
+ * high resolution scale factor, helps make canvas look better on hi-dpi
200
+ * screens
172
201
  */
173
202
  highResScaleFactor: 2,
203
+
174
204
  /**
175
205
  * #property
206
+ * default color scheme name
176
207
  */
177
208
  colorSchemeName: 'maeditor',
209
+
178
210
  /**
179
211
  * #property
212
+ * filehandle object for the tree
180
213
  */
181
214
  treeFilehandle: types.maybe(FileLocation),
215
+
182
216
  /**
183
217
  * #property
218
+ * filehandle object for the MSA (which could contain a tree e.g. with
219
+ * stockholm files)
184
220
  */
185
221
  msaFilehandle: types.maybe(FileLocation),
222
+
186
223
  /**
187
224
  * #property
225
+ * filehandle object for tree metadata
188
226
  */
189
227
  treeMetadataFilehandle: types.maybe(FileLocation),
228
+
190
229
  /**
191
230
  * #property
231
+ *
192
232
  */
193
233
  currentAlignment: 0,
234
+
194
235
  /**
195
236
  * #property
237
+ * array of tree nodes that are 'collapsed'
196
238
  */
197
239
  collapsed: types.array(types.string),
240
+
198
241
  /**
199
242
  * #property
243
+ * array of leaf nodes that are 'hidden', similar to collapsed but for leaf nodes
200
244
  */
201
- showOnly: types.maybe(types.string),
245
+ hidden: types.array(types.string),
246
+
202
247
  /**
203
248
  * #property
249
+ * focus on particular subtree
204
250
  */
205
- boxTracks: types.array(UniprotTrack),
251
+ showOnly: types.maybe(types.string),
252
+
206
253
  /**
207
254
  * #property
255
+ * a list of "tracks" to display, as box-like glyphs (e.g. protein
256
+ * domains)
208
257
  */
209
- turnedOffTracks: types.map(types.boolean),
258
+ boxTracks: types.array(UniprotTrack),
259
+
210
260
  /**
211
261
  * #property
262
+ * turned off tracks
212
263
  */
213
- annotatedRegions: types.array(
214
- types.model({
215
- start: types.number,
216
- end: types.number,
217
- attributes: types.frozen<Record<string, string[]>>(),
218
- }),
219
- ),
264
+ turnedOffTracks: types.map(types.boolean),
265
+
220
266
  /**
221
267
  * #property
268
+ * data from the loaded tree/msa/treeMetadata, generally loaded by
269
+ * autorun
222
270
  */
223
271
  data: types.optional(
224
272
  types
@@ -243,20 +291,47 @@ const model = types
243
291
  }),
244
292
  )
245
293
  .volatile(() => ({
294
+ /**
295
+ * #volatile
296
+ * resize handle width between tree and msa area, px
297
+ */
298
+ resizeHandleWidth: 5,
299
+
300
+ /**
301
+ * #volatile
302
+ * size of blocks of content to be drawn, px
303
+ */
304
+ blockSize: 1000,
305
+
306
+ /**
307
+ * #volatile
308
+ * the currently mouse-hovered row
309
+ */
310
+ mouseRow: undefined as number | undefined,
311
+
312
+ /**
313
+ * #volatile
314
+ * the currently mouse-hovered column
315
+ */
316
+ mouseCol: undefined as number | undefined,
317
+
246
318
  /**
247
319
  * #volatile
248
320
  * a dummy variable that is incremented when ref changes so autorun for
249
321
  * drawing canvas commands will run
250
322
  */
251
323
  nref: 0,
324
+
252
325
  /**
253
326
  * #volatile
254
327
  */
255
328
  minimapHeight: 56,
329
+
256
330
  /**
257
331
  * #volatile
258
332
  */
259
333
  error: undefined as unknown,
334
+
260
335
  /**
261
336
  * #volatile
262
337
  */
@@ -264,6 +339,7 @@ const model = types
264
339
  left: 20,
265
340
  top: 20,
266
341
  },
342
+
267
343
  /**
268
344
  * #volatile
269
345
  */
@@ -272,18 +348,23 @@ const model = types
272
348
  .actions(self => ({
273
349
  /**
274
350
  * #action
351
+ * set the height of the view in px
275
352
  */
276
353
  setHeight(height: number) {
277
354
  self.height = height
278
355
  },
356
+
279
357
  /**
280
358
  * #action
359
+ * add to the selected structures
281
360
  */
282
361
  addStructureToSelection(elt: StructureSnap) {
283
362
  self.selectedStructures.push(elt)
284
363
  },
364
+
285
365
  /**
286
366
  * #action
367
+ * remove from the selected structures
287
368
  */
288
369
  removeStructureFromSelection(elt: StructureSnap) {
289
370
  const r = self.selectedStructures.find(node => node.id === elt.id)
@@ -291,8 +372,10 @@ const model = types
291
372
  self.selectedStructures.remove(r)
292
373
  }
293
374
  },
375
+
294
376
  /**
295
377
  * #action
378
+ * toggle a structure from the selected structures list
296
379
  */
297
380
  toggleStructureSelection(elt: {
298
381
  id: string
@@ -305,51 +388,67 @@ const model = types
305
388
  self.selectedStructures.push(elt)
306
389
  }
307
390
  },
391
+
308
392
  /**
309
393
  * #action
394
+ * clear all selected structures
310
395
  */
311
396
  clearSelectedStructures() {
312
397
  self.selectedStructures = cast([])
313
398
  },
399
+
314
400
  /**
315
401
  * #action
402
+ * set error state
316
403
  */
317
404
  setError(error?: unknown) {
318
405
  self.error = error
319
406
  },
407
+
320
408
  /**
321
409
  * #action
410
+ * set mouse position (row, column) in the MSA
322
411
  */
323
412
  setMousePos(col?: number, row?: number) {
324
413
  self.mouseCol = col
325
414
  self.mouseRow = row
326
415
  },
416
+
327
417
  /**
328
418
  * #action
419
+ * set row height (px)
329
420
  */
330
421
  setRowHeight(n: number) {
331
422
  self.rowHeight = n
332
423
  },
424
+
333
425
  /**
334
426
  * #action
427
+ * set col width (px)
335
428
  */
336
429
  setColWidth(n: number) {
337
430
  self.colWidth = n
338
431
  },
432
+
339
433
  /**
340
434
  * #action
435
+ * set color scheme name
341
436
  */
342
437
  setColorSchemeName(name: string) {
343
438
  self.colorSchemeName = name
344
439
  },
440
+
345
441
  /**
346
442
  * #action
443
+ * synchronize the treewidth and treeareawidth
347
444
  */
348
445
  setTreeWidthMatchesArea(arg: boolean) {
349
446
  self.treeWidthMatchesArea = arg
350
447
  },
448
+
351
449
  /**
352
450
  * #action
451
+ * set scroll Y-offset (px)
353
452
  */
354
453
  setScrollY(n: number) {
355
454
  self.scrollY = n
@@ -357,22 +456,27 @@ const model = types
357
456
 
358
457
  /**
359
458
  * #action
459
+ * set tree area width (px)
360
460
  */
361
461
  setTreeAreaWidth(n: number) {
362
462
  self.treeAreaWidth = n
363
463
  },
364
464
  /**
365
465
  * #action
466
+ * set tree width (px)
366
467
  */
367
468
  setTreeWidth(n: number) {
368
469
  self.treeWidth = n
369
470
  },
471
+
370
472
  /**
371
473
  * #action
474
+ *
372
475
  */
373
476
  setCurrentAlignment(n: number) {
374
477
  self.currentAlignment = n
375
478
  },
479
+
376
480
  /**
377
481
  * #action
378
482
  */
@@ -385,6 +489,21 @@ const model = types
385
489
  setDrawTree(arg: boolean) {
386
490
  self.drawTree = arg
387
491
  },
492
+
493
+ /**
494
+ * #action
495
+ */
496
+ hideNode(arg: string) {
497
+ self.hidden.push(arg)
498
+ },
499
+
500
+ /**
501
+ * #action
502
+ */
503
+ clearHidden() {
504
+ self.hidden.clear()
505
+ },
506
+
388
507
  /**
389
508
  * #action
390
509
  */
@@ -395,42 +514,49 @@ const model = types
395
514
  self.collapsed.push(node)
396
515
  }
397
516
  },
517
+
398
518
  /**
399
519
  * #action
400
520
  */
401
521
  setShowOnly(node?: string) {
402
522
  self.showOnly = node
403
523
  },
524
+
404
525
  /**
405
526
  * #action
406
527
  */
407
528
  setShowBranchLen(arg: boolean) {
408
529
  self.showBranchLen = arg
409
530
  },
531
+
410
532
  /**
411
533
  * #action
412
534
  */
413
535
  setBgColor(arg: boolean) {
414
536
  self.bgColor = arg
415
537
  },
538
+
416
539
  /**
417
540
  * #action
418
541
  */
419
542
  setDrawNodeBubbles(arg: boolean) {
420
543
  self.drawNodeBubbles = arg
421
544
  },
545
+
422
546
  /**
423
547
  * #action
424
548
  */
425
549
  setData(data: { msa?: string; tree?: string }) {
426
550
  self.data = cast(data)
427
551
  },
552
+
428
553
  /**
429
554
  * #action
430
555
  */
431
556
  async setMSAFilehandle(msaFilehandle?: FileLocationType) {
432
557
  self.msaFilehandle = msaFilehandle
433
558
  },
559
+
434
560
  /**
435
561
  * #action
436
562
  */
@@ -442,18 +568,21 @@ const model = types
442
568
  self.treeFilehandle = treeFilehandle
443
569
  }
444
570
  },
571
+
445
572
  /**
446
573
  * #action
447
574
  */
448
575
  setMSA(result: string) {
449
576
  self.data.setMSA(result)
450
577
  },
578
+
451
579
  /**
452
580
  * #action
453
581
  */
454
582
  setTree(result: string) {
455
583
  self.data.setTree(result)
456
584
  },
585
+
457
586
  /**
458
587
  * #action
459
588
  */
@@ -533,12 +662,14 @@ const model = types
533
662
  get blocks2d() {
534
663
  return self.blocksY.flatMap(by => self.blocksX.map(bx => [bx, by]))
535
664
  },
665
+
536
666
  /**
537
667
  * #getter
538
668
  */
539
669
  get done() {
540
670
  return self.initialized && (self.data.msa || self.data.tree)
541
671
  },
672
+
542
673
  /**
543
674
  * #getter
544
675
  */
@@ -552,6 +683,7 @@ const model = types
552
683
  get header() {
553
684
  return this.MSA?.getHeader() || {}
554
685
  },
686
+
555
687
  /**
556
688
  * #method
557
689
  */
@@ -559,7 +691,12 @@ const model = types
559
691
  const matches = name.match(/\S+\/(\d+)-(\d+)/)
560
692
  return {
561
693
  data: this.MSA?.getRowData(name) || ({} as Record<string, unknown>),
562
- ...(matches && { range: { start: +matches[1], end: +matches[2] } }),
694
+ ...(matches && {
695
+ range: {
696
+ start: +matches[1],
697
+ end: +matches[2],
698
+ },
699
+ }),
563
700
  }
564
701
  },
565
702
  /**
@@ -578,7 +715,7 @@ const model = types
578
715
  * #getter
579
716
  */
580
717
  get noTree() {
581
- return !!this.tree.noTree
718
+ return !!this._tree.noTree
582
719
  },
583
720
  /**
584
721
  * #getter
@@ -617,7 +754,7 @@ const model = types
617
754
  /**
618
755
  * #getter
619
756
  */
620
- get tree(): NodeWithIds {
757
+ get _tree(): NodeWithIds {
621
758
  return self.data.tree
622
759
  ? generateNodeIds(parseNewick(self.data.tree))
623
760
  : this.MSA?.getTree() || {
@@ -635,7 +772,8 @@ const model = types
635
772
  },
636
773
  /**
637
774
  * #getter
638
- */ get mouseOverRowName() {
775
+ */
776
+ get mouseOverRowName() {
639
777
  return self.mouseRow !== undefined
640
778
  ? this.rowNames[self.mouseRow]
641
779
  : undefined
@@ -652,9 +790,10 @@ const model = types
652
790
  * #getter
653
791
  */
654
792
  get root() {
655
- let hier = hierarchy(this.tree, d => d.branchset)
793
+ let hier = hierarchy(this._tree, d => d.branchset)
656
794
  .sum(d => (d.branchset ? 0 : 1))
657
795
  .sort((a, b) => ascending(a.data.length || 1, b.data.length || 1))
796
+
658
797
  if (self.showOnly) {
659
798
  const res = hier.find(node => node.data.id === self.showOnly)
660
799
  if (res) {
@@ -665,22 +804,21 @@ const model = types
665
804
  if (self.collapsed.length) {
666
805
  self.collapsed
667
806
  .map(collapsedId => hier.find(node => node.data.id === collapsedId))
668
- .filter((f): f is HierarchyNode<NodeWithIds> => !!f)
807
+ .filter(notEmpty)
669
808
  .map(node => collapse(node))
670
809
  }
810
+ if (self.hidden.length) {
811
+ self.hidden
812
+ .map(hiddenId => hier.find(node => node.data.id === hiddenId))
813
+ .filter(notEmpty)
814
+ .map(node => filterHiddenLeafNodes(node.parent, node.id))
815
+ }
671
816
  return hier
672
817
  },
673
818
  /**
674
819
  * #getter
675
820
  */
676
- get structures(): Record<
677
- string,
678
- {
679
- pdb: string
680
- startPos: number
681
- endPos: number
682
- }[]
683
- > {
821
+ get structures(): Record<string, Structure[]> {
684
822
  return this.MSA?.getStructures() || {}
685
823
  },
686
824
  /**
@@ -695,6 +833,7 @@ const model = types
695
833
  },
696
834
  /**
697
835
  * #getter
836
+ * widget width minus the tree area gives the space for the MSA
698
837
  */
699
838
  get msaAreaWidth() {
700
839
  return self.width - self.treeAreaWidth
@@ -706,8 +845,8 @@ const model = types
706
845
  const blanks = []
707
846
  const strs = this.hierarchy
708
847
  .leaves()
709
- .map(({ data }) => this.MSA?.getRow(data.name))
710
- .filter((item): item is string[] => !!item)
848
+ .map(leaf => this.MSA?.getRow(leaf.data.name))
849
+ .filter((item): item is string => !!item)
711
850
 
712
851
  for (let i = 0; i < strs[0]?.length; i++) {
713
852
  let counter = 0
@@ -726,10 +865,11 @@ const model = types
726
865
  * #getter
727
866
  */
728
867
  get rows() {
868
+ const MSA = this.MSA
729
869
  return this.hierarchy
730
870
  .leaves()
731
- .map(({ data }) => [data.name, this.MSA?.getRow(data.name)] as const)
732
- .filter((f): f is [string, string[]] => !!f[1])
871
+ .map(leaf => [leaf.data.name, MSA?.getRow(leaf.data.name)] as const)
872
+ .filter((f): f is [string, string] => !!f[1])
733
873
  },
734
874
  /**
735
875
  * #getter
@@ -774,18 +914,15 @@ const model = types
774
914
  * generates a new tree that is clustered with x,y positions
775
915
  */
776
916
  get hierarchy(): HierarchyNode<NodeWithIdsAndLength> {
777
- const root = this.root
917
+ const r = this.root
778
918
  const clust = cluster<NodeWithIds>()
779
919
  .size([this.totalHeight, self.treeWidth])
780
920
  .separation(() => 1)
781
- clust(root)
782
- setBrLength(
783
- root,
784
- (root.data.length = 0),
785
- self.treeWidth / maxLength(root),
786
- )
787
- return root as HierarchyNode<NodeWithIdsAndLength>
921
+ clust(r)
922
+ setBrLength(r, (r.data.length = 0), self.treeWidth / maxLength(r))
923
+ return r as HierarchyNode<NodeWithIdsAndLength>
788
924
  },
925
+
789
926
  /**
790
927
  * #getter
791
928
  */
@@ -810,12 +947,14 @@ const model = types
810
947
  })
811
948
  }
812
949
  },
950
+
813
951
  /**
814
952
  * #action
815
953
  */
816
954
  doScrollY(deltaY: number) {
817
955
  self.scrollY = clamp(-self.totalHeight + 10, self.scrollY + deltaY, 0)
818
956
  },
957
+
819
958
  /**
820
959
  * #action
821
960
  */
@@ -826,6 +965,7 @@ const model = types
826
965
  0,
827
966
  )
828
967
  },
968
+
829
969
  /**
830
970
  * #action
831
971
  */
@@ -836,6 +976,7 @@ const model = types
836
976
  0,
837
977
  )
838
978
  },
979
+
839
980
  /**
840
981
  * #action
841
982
  */
@@ -876,27 +1017,32 @@ const model = types
876
1017
  const { rowHeight, hierarchy, treeMetadata, fontSize } = self
877
1018
  if (rowHeight > 5) {
878
1019
  for (const node of hierarchy.leaves()) {
879
- const {
880
- data: { name },
881
- } = node
882
- const displayName = treeMetadata[name]?.genome || name
883
- x = Math.max(measureText(displayName, fontSize), x)
1020
+ x = Math.max(
1021
+ measureText(
1022
+ treeMetadata[node.data.name]?.genome || node.data.name,
1023
+ fontSize,
1024
+ ),
1025
+ x,
1026
+ )
884
1027
  }
885
1028
  }
886
1029
  return x
887
1030
  },
1031
+
888
1032
  /**
889
1033
  * #getter
890
1034
  */
891
1035
  get secondaryStructureConsensus() {
892
1036
  return self.MSA?.secondaryStructureConsensus
893
1037
  },
1038
+
894
1039
  /**
895
1040
  * #getter
896
1041
  */
897
1042
  get seqConsensus() {
898
1043
  return self.MSA?.seqConsensus
899
1044
  },
1045
+
900
1046
  /**
901
1047
  * #getter
902
1048
  */
@@ -911,69 +1057,40 @@ const model = types
911
1057
  }
912
1058
  return ['a']
913
1059
  },
1060
+
914
1061
  /**
915
1062
  * #getter
916
1063
  */
917
- get tracks(): BasicTrack[] {
918
- const blanks = self.blanks
919
- const adapterTracks = self.MSA
920
- ? self.MSA.tracks.map(track => {
921
- const { data } = track
922
- return {
923
- model: {
924
- ...track,
925
- data: data ? skipBlanks(blanks, data) : undefined,
926
- height: self.rowHeight,
927
- } as TextTrackModel,
928
- ReactComponent: TextTrack,
929
- }
930
- })
931
- : ([] as BasicTrack[])
1064
+ get adapterTrackModels(): BasicTrack[] {
1065
+ return (
1066
+ self.MSA?.tracks.map(t => ({
1067
+ model: {
1068
+ ...t,
1069
+ data: t.data ? skipBlanks(self.blanks, t.data) : undefined,
1070
+ height: self.rowHeight,
1071
+ } as TextTrackModel,
1072
+ ReactComponent: TextTrack,
1073
+ })) || []
1074
+ )
1075
+ },
932
1076
 
933
- const boxTracks = self.boxTracks
934
- // filter out tracks that are associated with hidden rows
935
- .filter(track => !!self.rows.some(row => row[0] === track.name))
1077
+ /**
1078
+ * #getter
1079
+ */
1080
+ get boxTrackModels(): BasicTrack[] {
1081
+ return self.boxTracks
1082
+ .filter(track => self.rows.some(row => row[0] === track.name))
936
1083
  .map(track => ({
937
1084
  model: track as BoxTrackModel,
938
1085
  ReactComponent: BoxTrack,
939
1086
  }))
1087
+ },
940
1088
 
941
- const annotationTracks =
942
- self.annotatedRegions.length > 0
943
- ? [
944
- {
945
- model: {
946
- features: self.annotatedRegions,
947
- height: 100,
948
- id: 'annotations',
949
- name: 'User-created annotations',
950
- data: self.annotatedRegions
951
- .map(region => {
952
- const attrs = region.attributes
953
- ? Object.entries(region.attributes)
954
- .map(([k, v]) => `${k}=${v.join(',')}`)
955
- .join(';')
956
- : '.'
957
- return [
958
- 'MSA_refcoord',
959
- '.',
960
- '.',
961
- region.start,
962
- region.end,
963
- '.',
964
- '.',
965
- '.',
966
- attrs,
967
- ].join('\t')
968
- })
969
- .join('\n'),
970
- } as BoxTrackModel,
971
- ReactComponent: BoxTrack,
972
- },
973
- ]
974
- : ([] as BasicTrack[])
975
-
976
- return [...adapterTracks, ...boxTracks, ...annotationTracks]
1089
+ /**
1090
+ * #getter
1091
+ */
1092
+ get tracks(): BasicTrack[] {
1093
+ return [...this.adapterTrackModels, ...this.boxTrackModels]
977
1094
  },
978
1095
  /**
979
1096
  * #getter
@@ -981,6 +1098,7 @@ const model = types
981
1098
  get turnedOnTracks() {
982
1099
  return this.tracks.filter(f => !self.turnedOffTracks.has(f.model.id))
983
1100
  },
1101
+
984
1102
  /**
985
1103
  * #method
986
1104
  * returns coordinate in the current relative coordinate scheme
@@ -988,6 +1106,7 @@ const model = types
988
1106
  pxToBp(coord: number) {
989
1107
  return Math.floor((coord - self.scrollX) / self.colWidth)
990
1108
  },
1109
+
991
1110
  /**
992
1111
  * #method
993
1112
  */
@@ -1022,6 +1141,7 @@ const model = types
1022
1141
 
1023
1142
  return i - count
1024
1143
  },
1144
+
1025
1145
  /**
1026
1146
  * #method
1027
1147
  */
@@ -1037,6 +1157,7 @@ const model = types
1037
1157
 
1038
1158
  return position - count
1039
1159
  },
1160
+
1040
1161
  /**
1041
1162
  * #method
1042
1163
  */
@@ -1058,6 +1179,29 @@ const model = types
1058
1179
  }
1059
1180
  return 0
1060
1181
  },
1182
+
1183
+ /**
1184
+ * #method
1185
+ */
1186
+ relativePxToBp2(rowName: string, position: number) {
1187
+ const { rowNames, rows } = self
1188
+ const index = rowNames.indexOf(rowName)
1189
+ if (index !== -1) {
1190
+ const row = rows[index][1]
1191
+
1192
+ let k = 0
1193
+ let i = 0
1194
+ for (; k < position; i++) {
1195
+ if (row[i] !== '-') {
1196
+ k++
1197
+ } else if (k >= position) {
1198
+ break
1199
+ }
1200
+ }
1201
+ return i
1202
+ }
1203
+ return 0
1204
+ },
1061
1205
  /**
1062
1206
  * #method
1063
1207
  */
@@ -1072,50 +1216,26 @@ const model = types
1072
1216
  return j
1073
1217
  },
1074
1218
  }))
1075
- .actions(self => ({
1076
- /**
1077
- * #action
1078
- */
1079
- addAnnotation(
1080
- start: number,
1081
- end: number,
1082
- attributes: Record<string, string[]>,
1083
- ) {
1084
- self.annotatedRegions.push({
1085
- start: self.getPos(start),
1086
- end: self.getPos(end),
1087
- attributes,
1088
- })
1089
- },
1090
- /**
1091
- * #action
1092
- */
1093
- setAnnotationClickBoundaries(left: number, right: number) {
1094
- self.annotPos = { left, right }
1095
- },
1096
- /**
1097
- * #action
1098
- */
1099
- clearAnnotationClickBoundaries() {
1100
- self.annotPos = undefined
1101
- },
1102
- /**
1103
- * #action
1104
- */
1105
- incrementRef() {
1106
- self.nref++
1107
- },
1108
- }))
1219
+
1109
1220
  .views(self => ({
1110
1221
  /**
1111
1222
  * #getter
1223
+ * total height of track area (px)
1112
1224
  */
1113
1225
  get totalTrackAreaHeight() {
1114
1226
  return sum(self.turnedOnTracks.map(r => r.model.height))
1115
1227
  },
1116
1228
  }))
1117
1229
  .actions(self => ({
1230
+ /**
1231
+ * #action
1232
+ * internal, used for drawing to canvas
1233
+ */
1234
+ incrementRef() {
1235
+ self.nref++
1236
+ },
1118
1237
  afterCreate() {
1238
+ // autorun opens treeFilehandle
1119
1239
  addDisposer(
1120
1240
  self,
1121
1241
  autorun(async () => {
@@ -1130,6 +1250,7 @@ const model = types
1130
1250
  }
1131
1251
  }),
1132
1252
  )
1253
+ // autorun opens treeMetadataFilehandle
1133
1254
  addDisposer(
1134
1255
  self,
1135
1256
  autorun(async () => {
@@ -1146,6 +1267,8 @@ const model = types
1146
1267
  }
1147
1268
  }),
1148
1269
  )
1270
+
1271
+ // autorun opens msaFilehandle
1149
1272
  addDisposer(
1150
1273
  self,
1151
1274
  autorun(async () => {
@@ -1160,6 +1283,8 @@ const model = types
1160
1283
  }
1161
1284
  }),
1162
1285
  )
1286
+
1287
+ // autorun synchronizes treeWidth with treeAreaWidth
1163
1288
  addDisposer(
1164
1289
  self,
1165
1290
  autorun(async () => {