react-msaview 2.1.1 → 2.1.3

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 (37) hide show
  1. package/bundle/index.js +3 -3
  2. package/dist/components/Header.js +4 -4
  3. package/dist/components/Header.js.map +1 -1
  4. package/dist/components/MSAView.js +2 -4
  5. package/dist/components/MSAView.js.map +1 -1
  6. package/dist/components/Rubberband.js +2 -2
  7. package/dist/components/Rubberband.js.map +1 -1
  8. package/dist/components/TreeCanvasBlock.js +9 -6
  9. package/dist/components/TreeCanvasBlock.js.map +1 -1
  10. package/dist/components/TreeMenu.js +4 -2
  11. package/dist/components/TreeMenu.js.map +1 -1
  12. package/dist/components/dialogs/{DetailsDlg.d.ts → MetadataDlg.d.ts} +2 -2
  13. package/dist/components/dialogs/{DetailsDlg.js → MetadataDlg.js} +5 -4
  14. package/dist/components/dialogs/MetadataDlg.js.map +1 -0
  15. package/dist/components/dialogs/TreeNodeInfoDlg.d.ts +9 -0
  16. package/dist/components/dialogs/TreeNodeInfoDlg.js +16 -0
  17. package/dist/components/dialogs/TreeNodeInfoDlg.js.map +1 -0
  18. package/dist/model.d.ts +336 -13
  19. package/dist/model.js +382 -30
  20. package/dist/model.js.map +1 -1
  21. package/dist/version.d.ts +1 -1
  22. package/dist/version.js +1 -1
  23. package/package.json +6 -2
  24. package/src/components/Header.tsx +5 -5
  25. package/src/components/MSAView.tsx +2 -4
  26. package/src/components/Rubberband.tsx +2 -2
  27. package/src/components/TreeCanvasBlock.tsx +9 -5
  28. package/src/components/TreeMenu.tsx +4 -2
  29. package/src/components/dialogs/{DetailsDlg.tsx → MetadataDlg.tsx} +7 -3
  30. package/src/components/dialogs/TreeNodeInfoDlg.tsx +38 -0
  31. package/src/model.ts +440 -113
  32. package/src/version.ts +1 -1
  33. package/dist/components/dialogs/DetailsDlg.js.map +0 -1
  34. package/dist/components/dialogs/MoreInfoDlg.d.ts +0 -6
  35. package/dist/components/dialogs/MoreInfoDlg.js +0 -11
  36. package/dist/components/dialogs/MoreInfoDlg.js.map +0 -1
  37. package/src/components/dialogs/MoreInfoDlg.tsx +0 -21
package/src/model.ts CHANGED
@@ -49,7 +49,10 @@ export interface TextTrackModel extends BasicTrackModel {
49
49
  }
50
50
 
51
51
  export interface BoxTrackModel extends BasicTrackModel {
52
- features: { start: number; end: number }[]
52
+ features: {
53
+ start: number
54
+ end: number
55
+ }[]
53
56
  }
54
57
  export interface ITextTrack {
55
58
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -64,65 +67,175 @@ export interface IBoxTrack {
64
67
  }
65
68
 
66
69
  type BasicTrack = IBoxTrack | ITextTrack
67
-
68
- const MSAModel = types
69
- .model('MsaView', {
70
- id: ElementId,
71
- type: types.literal('MsaView'),
72
- height: types.optional(types.number, 550),
73
- treeAreaWidth: types.optional(types.number, 400),
74
- treeWidth: types.optional(types.number, 300),
75
- rowHeight: 20,
76
- scrollY: 0,
77
- scrollX: 0,
78
- resizeHandleWidth: 5,
79
- blockSize: 1000,
80
- mouseRow: types.maybe(types.number),
81
- mouseCol: types.maybe(types.number),
82
- selectedStructures: types.array(StructureModel),
83
- labelsAlignRight: false,
84
- colWidth: 16,
85
- showBranchLen: true,
86
- bgColor: true,
87
- drawTree: true,
88
- drawNodeBubbles: true,
89
- highResScaleFactor: 2,
90
- colorSchemeName: 'maeditor',
91
- treeFilehandle: types.maybe(FileLocation),
92
- msaFilehandle: types.maybe(FileLocation),
93
- currentAlignment: 0,
94
- collapsed: types.array(types.string),
95
- showOnly: types.maybe(types.string),
96
- boxTracks: types.array(UniprotTrack),
97
- turnedOffTracks: types.map(types.boolean),
98
- annotatedRegions: types.array(
99
- types.model({
100
- start: types.number,
101
- end: types.number,
102
- attributes: types.frozen<Record<string, string[]>>(),
103
- }),
104
- ),
105
- data: types.optional(
106
- types
107
- .model({
108
- tree: types.maybe(types.string),
109
- msa: types.maybe(types.string),
110
- })
111
- .actions(self => ({
112
- setTree(tree?: string) {
113
- self.tree = tree
114
- },
115
- setMSA(msa?: string) {
116
- self.msa = msa
117
- },
118
- })),
119
- { tree: '', msa: '' },
120
- ),
121
- })
70
+ type StructureSnap = SnapshotIn<typeof StructureModel>
71
+
72
+ /**
73
+ * #stateModel MsaView
74
+ */
75
+ function x() {} // eslint-disable-line @typescript-eslint/no-unused-vars
76
+
77
+ const model = types
78
+ .compose(
79
+ BaseViewModel,
80
+ types.model('MsaView', {
81
+ /**
82
+ * #property
83
+ */
84
+ id: ElementId,
85
+ /**
86
+ * #property
87
+ */
88
+ type: types.literal('MsaView'),
89
+ /**
90
+ * #property
91
+ */
92
+ height: types.optional(types.number, 550),
93
+ /**
94
+ * #property
95
+ */
96
+ treeAreaWidth: types.optional(types.number, 400),
97
+ /**
98
+ * #property
99
+ */
100
+ treeWidth: types.optional(types.number, 300),
101
+ /**
102
+ * #property
103
+ */
104
+ rowHeight: 20,
105
+ /**
106
+ * #property
107
+ */
108
+ scrollY: 0,
109
+ /**
110
+ * #property
111
+ */
112
+ scrollX: 0,
113
+ /**
114
+ * #property
115
+ */
116
+ resizeHandleWidth: 5,
117
+ /**
118
+ * #property
119
+ */
120
+ blockSize: 1000,
121
+ /**
122
+ * #property
123
+ */
124
+ mouseRow: types.maybe(types.number),
125
+ /**
126
+ * #property
127
+ */
128
+ mouseCol: types.maybe(types.number),
129
+ /**
130
+ * #property
131
+ */
132
+ selectedStructures: types.array(StructureModel),
133
+ /**
134
+ * #property
135
+ */
136
+ labelsAlignRight: false,
137
+ /**
138
+ * #property
139
+ */
140
+ colWidth: 16,
141
+ /**
142
+ * #property
143
+ */
144
+ showBranchLen: true,
145
+ /**
146
+ * #property
147
+ */
148
+ bgColor: true,
149
+ /**
150
+ * #property
151
+ */
152
+ drawTree: true,
153
+ /**
154
+ * #property
155
+ */
156
+ drawNodeBubbles: true,
157
+ /**
158
+ * #property
159
+ */
160
+ highResScaleFactor: 2,
161
+ /**
162
+ * #property
163
+ */
164
+ colorSchemeName: 'maeditor',
165
+ /**
166
+ * #property
167
+ */
168
+ treeFilehandle: types.maybe(FileLocation),
169
+ /**
170
+ * #property
171
+ */
172
+ msaFilehandle: types.maybe(FileLocation),
173
+ /**
174
+ * #property
175
+ */
176
+ treeMetadataFilehandle: types.maybe(FileLocation),
177
+ /**
178
+ * #property
179
+ */
180
+ currentAlignment: 0,
181
+ /**
182
+ * #property
183
+ */
184
+ collapsed: types.array(types.string),
185
+ /**
186
+ * #property
187
+ */
188
+ showOnly: types.maybe(types.string),
189
+ /**
190
+ * #property
191
+ */
192
+ boxTracks: types.array(UniprotTrack),
193
+ /**
194
+ * #property
195
+ */
196
+ turnedOffTracks: types.map(types.boolean),
197
+ /**
198
+ * #property
199
+ */
200
+ annotatedRegions: types.array(
201
+ types.model({
202
+ start: types.number,
203
+ end: types.number,
204
+ attributes: types.frozen<Record<string, string[]>>(),
205
+ }),
206
+ ),
207
+ /**
208
+ * #property
209
+ */
210
+ data: types.optional(
211
+ types
212
+ .model({
213
+ tree: types.maybe(types.string),
214
+ msa: types.maybe(types.string),
215
+ treeMetadata: types.maybe(types.string),
216
+ })
217
+ .actions(self => ({
218
+ setTree(tree?: string) {
219
+ self.tree = tree
220
+ },
221
+ setMSA(msa?: string) {
222
+ self.msa = msa
223
+ },
224
+ setTreeMetadata(treeMetadata?: string) {
225
+ self.treeMetadata = treeMetadata
226
+ },
227
+ })),
228
+ { tree: '', msa: '' },
229
+ ),
230
+ }),
231
+ )
122
232
  .volatile(() => ({
123
233
  rulerHeight: 20,
124
234
  error: undefined as unknown,
125
- margin: { left: 20, top: 20 },
235
+ margin: {
236
+ left: 20,
237
+ top: 20,
238
+ },
126
239
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
127
240
  DialogComponent: undefined as undefined | React.FC<any>,
128
241
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -132,23 +245,38 @@ const MSAModel = types
132
245
  annotPos: undefined as { left: number; right: number } | undefined,
133
246
  }))
134
247
  .actions(self => ({
248
+ /**
249
+ * #action
250
+ */
135
251
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
- setDialogComponent(dlg: React.FC<any> | undefined, props: any) {
252
+ setDialogComponent(dlg?: React.FC<any>, props?: any) {
137
253
  self.DialogComponent = dlg
138
254
  self.DialogProps = props
139
255
  },
256
+ /**
257
+ * #action
258
+ */
140
259
  setHeight(height: number) {
141
260
  self.height = height
142
261
  },
143
- addStructureToSelection(elt: SnapshotIn<typeof StructureModel>) {
262
+ /**
263
+ * #action
264
+ */
265
+ addStructureToSelection(elt: StructureSnap) {
144
266
  self.selectedStructures.push(elt)
145
267
  },
146
- removeStructureFromSelection(elt: SnapshotIn<typeof StructureModel>) {
268
+ /**
269
+ * #action
270
+ */
271
+ removeStructureFromSelection(elt: StructureSnap) {
147
272
  const r = self.selectedStructures.find(node => node.id === elt.id)
148
273
  if (r) {
149
274
  self.selectedStructures.remove(r)
150
275
  }
151
276
  },
277
+ /**
278
+ * #action
279
+ */
152
280
  toggleStructureSelection(elt: {
153
281
  id: string
154
282
  structure: { startPos: number; endPos: number; pdb: string }
@@ -160,47 +288,88 @@ const MSAModel = types
160
288
  self.selectedStructures.push(elt)
161
289
  }
162
290
  },
291
+ /**
292
+ * #action
293
+ */
163
294
  clearSelectedStructures() {
164
- // @ts-expect-error
165
- self.selectedStructures = []
295
+ self.selectedStructures = cast([])
166
296
  },
297
+ /**
298
+ * #action
299
+ */
167
300
  setError(error?: unknown) {
168
301
  self.error = error
169
302
  },
303
+ /**
304
+ * #action
305
+ */
170
306
  setMousePos(col?: number, row?: number) {
171
307
  self.mouseCol = col
172
308
  self.mouseRow = row
173
309
  },
310
+ /**
311
+ * #action
312
+ */
174
313
  setRowHeight(n: number) {
175
314
  self.rowHeight = n
176
315
  },
316
+ /**
317
+ * #action
318
+ */
177
319
  setColWidth(n: number) {
178
320
  self.colWidth = n
179
321
  },
322
+ /**
323
+ * #action
324
+ */
180
325
  setColorSchemeName(name: string) {
181
326
  self.colorSchemeName = name
182
327
  },
328
+ /**
329
+ * #action
330
+ */
183
331
  setScrollY(n: number) {
184
332
  self.scrollY = n
185
333
  },
334
+ /**
335
+ * #action
336
+ */
186
337
  setScrollX(n: number) {
187
338
  self.scrollX = n
188
339
  },
340
+ /**
341
+ * #action
342
+ */
189
343
  setTreeAreaWidth(n: number) {
190
344
  self.treeAreaWidth = n
191
345
  },
346
+ /**
347
+ * #action
348
+ */
192
349
  setTreeWidth(n: number) {
193
350
  self.treeWidth = n
194
351
  },
352
+ /**
353
+ * #action
354
+ */
195
355
  setCurrentAlignment(n: number) {
196
356
  self.currentAlignment = n
197
357
  },
358
+ /**
359
+ * #action
360
+ */
198
361
  toggleLabelsAlignRight() {
199
362
  self.labelsAlignRight = !self.labelsAlignRight
200
363
  },
364
+ /**
365
+ * #action
366
+ */
201
367
  toggleDrawTree() {
202
368
  self.drawTree = !self.drawTree
203
369
  },
370
+ /**
371
+ * #action
372
+ */
204
373
  toggleCollapsed(node: string) {
205
374
  if (self.collapsed.includes(node)) {
206
375
  self.collapsed.remove(node)
@@ -208,24 +377,45 @@ const MSAModel = types
208
377
  self.collapsed.push(node)
209
378
  }
210
379
  },
380
+ /**
381
+ * #action
382
+ */
211
383
  setShowOnly(node?: string) {
212
384
  self.showOnly = node
213
385
  },
386
+ /**
387
+ * #action
388
+ */
214
389
  toggleBranchLen() {
215
390
  self.showBranchLen = !self.showBranchLen
216
391
  },
392
+ /**
393
+ * #action
394
+ */
217
395
  toggleBgColor() {
218
396
  self.bgColor = !self.bgColor
219
397
  },
398
+ /**
399
+ * #action
400
+ */
220
401
  toggleNodeBubbles() {
221
402
  self.drawNodeBubbles = !self.drawNodeBubbles
222
403
  },
404
+ /**
405
+ * #action
406
+ */
223
407
  setData(data: { msa?: string; tree?: string }) {
224
408
  self.data = cast(data)
225
409
  },
410
+ /**
411
+ * #action
412
+ */
226
413
  async setMSAFilehandle(msaFilehandle?: FileLocationType) {
227
414
  self.msaFilehandle = msaFilehandle
228
415
  },
416
+ /**
417
+ * #action
418
+ */
229
419
  async setTreeFilehandle(treeFilehandle?: FileLocationType) {
230
420
  if (treeFilehandle && 'blobId' in treeFilehandle) {
231
421
  const r = await openLocation(treeFilehandle).readFile('utf8')
@@ -234,12 +424,24 @@ const MSAModel = types
234
424
  self.treeFilehandle = treeFilehandle
235
425
  }
236
426
  },
427
+ /**
428
+ * #action
429
+ */
237
430
  setMSA(result: string) {
238
431
  self.data.setMSA(result)
239
432
  },
433
+ /**
434
+ * #action
435
+ */
240
436
  setTree(result: string) {
241
437
  self.data.setTree(result)
242
438
  },
439
+ /**
440
+ * #action
441
+ */
442
+ setTreeMetadata(result: string) {
443
+ self.data.setTreeMetadata(result)
444
+ },
243
445
 
244
446
  afterCreate() {
245
447
  addDisposer(
@@ -256,6 +458,22 @@ const MSAModel = types
256
458
  }
257
459
  }),
258
460
  )
461
+ addDisposer(
462
+ self,
463
+ autorun(async () => {
464
+ const { treeMetadataFilehandle } = self
465
+ if (treeMetadataFilehandle) {
466
+ try {
467
+ this.setTreeMetadata(
468
+ await openLocation(treeMetadataFilehandle).readFile('utf8'),
469
+ )
470
+ } catch (e) {
471
+ console.error(e)
472
+ this.setError(e)
473
+ }
474
+ }
475
+ }),
476
+ )
259
477
  addDisposer(
260
478
  self,
261
479
  autorun(async () => {
@@ -278,6 +496,9 @@ const MSAModel = types
278
496
  let oldValX = 0
279
497
  let oldValY = 0
280
498
  return {
499
+ /**
500
+ * #getter
501
+ */
281
502
  get initialized() {
282
503
  return (
283
504
  (self.data.msa ||
@@ -287,7 +508,9 @@ const MSAModel = types
287
508
  !self.error
288
509
  )
289
510
  },
290
-
511
+ /**
512
+ * #getter
513
+ */
291
514
  get blocksX() {
292
515
  const { scrollX, blockSize: size, colWidth } = self
293
516
  const ret = -(size * Math.floor(scrollX / size)) - size
@@ -307,7 +530,9 @@ const MSAModel = types
307
530
  }
308
531
  return oldBlocksX
309
532
  },
310
-
533
+ /**
534
+ * #getter
535
+ */
311
536
  get blocksY() {
312
537
  const { scrollY, blockSize: size, rowHeight } = self
313
538
  const ret = -(size * Math.floor(scrollY / size)) - 2 * size
@@ -330,21 +555,33 @@ const MSAModel = types
330
555
  }
331
556
  })
332
557
  .views(self => ({
558
+ /**
559
+ * #getter
560
+ */
333
561
  get blocks2d() {
334
562
  return self.blocksY.flatMap(by => self.blocksX.map(bx => [bx, by]))
335
563
  },
564
+ /**
565
+ * #getter
566
+ */
336
567
  get done() {
337
568
  return self.initialized && (self.data.msa || self.data.tree)
338
569
  },
339
-
570
+ /**
571
+ * #getter
572
+ */
340
573
  get colorScheme() {
341
574
  return colorSchemes[self.colorSchemeName]
342
575
  },
343
-
576
+ /**
577
+ * #getter
578
+ */
344
579
  get header() {
345
580
  return this.MSA?.getHeader() || {}
346
581
  },
347
-
582
+ /**
583
+ * #method
584
+ */
348
585
  getRowData(name: string) {
349
586
  const matches = name.match(/\S+\/(\d+)-(\d+)/)
350
587
  return {
@@ -352,23 +589,39 @@ const MSAModel = types
352
589
  ...(matches && { range: { start: +matches[1], end: +matches[2] } }),
353
590
  }
354
591
  },
355
-
592
+ /**
593
+ * #getter
594
+ */
356
595
  get currentAlignmentName() {
357
596
  return this.alignmentNames[self.currentAlignment]
358
597
  },
359
-
598
+ /**
599
+ * #getter
600
+ */
360
601
  get alignmentNames() {
361
602
  return this.MSA?.alignmentNames || []
362
603
  },
363
-
604
+ /**
605
+ * #getter
606
+ */
364
607
  get noTree() {
365
608
  return !!this.tree.noTree
366
609
  },
367
-
610
+ /**
611
+ * #getter
612
+ */
368
613
  get menuItems() {
369
614
  return []
370
615
  },
371
-
616
+ /**
617
+ * #getter
618
+ */
619
+ get treeMetadata() {
620
+ return self.data.treeMetadata ? JSON.parse(self.data.treeMetadata) : {}
621
+ },
622
+ /**
623
+ * #getter
624
+ */
372
625
  get MSA() {
373
626
  const text = self.data.msa
374
627
  if (text) {
@@ -382,11 +635,15 @@ const MSAModel = types
382
635
  }
383
636
  return null
384
637
  },
385
-
638
+ /**
639
+ * #getter
640
+ */
386
641
  get numColumns() {
387
642
  return ((this.MSA?.getWidth() || 0) - this.blanks.length) * self.colWidth
388
643
  },
389
-
644
+ /**
645
+ * #getter
646
+ */
390
647
  get tree(): NodeWithIds {
391
648
  return self.data.tree
392
649
  ? generateNodeIds(parseNewick(self.data.tree))
@@ -397,21 +654,30 @@ const MSAModel = types
397
654
  name: 'empty',
398
655
  }
399
656
  },
400
-
657
+ /**
658
+ * #getter
659
+ */
401
660
  get rowNames(): string[] {
402
661
  return this.hierarchy.leaves().map(node => node.data.name)
403
662
  },
404
-
405
- get mouseOverRowName() {
663
+ /**
664
+ * #getter
665
+ */ get mouseOverRowName() {
406
666
  return self.mouseRow !== undefined
407
667
  ? this.rowNames[self.mouseRow]
408
668
  : undefined
409
669
  },
410
670
 
671
+ /**
672
+ * #method
673
+ */
411
674
  getMouseOverResidue(rowName: string) {
412
675
  return this.columns[rowName]
413
676
  },
414
677
 
678
+ /**
679
+ * #getter
680
+ */
415
681
  get root() {
416
682
  let hier = hierarchy(this.tree, d => d.branchset)
417
683
  .sum(d => (d.branchset ? 0 : 1))
@@ -431,7 +697,9 @@ const MSAModel = types
431
697
  }
432
698
  return hier
433
699
  },
434
-
700
+ /**
701
+ * #getter
702
+ */
435
703
  get structures(): Record<
436
704
  string,
437
705
  {
@@ -442,7 +710,9 @@ const MSAModel = types
442
710
  > {
443
711
  return this.MSA?.getStructures() || {}
444
712
  },
445
-
713
+ /**
714
+ * #getter
715
+ */
446
716
  get inverseStructures() {
447
717
  return Object.fromEntries(
448
718
  Object.entries(this.structures).flatMap(([key, val]) =>
@@ -450,12 +720,15 @@ const MSAModel = types
450
720
  ),
451
721
  )
452
722
  },
453
-
723
+ /**
724
+ * #getter
725
+ */
454
726
  get msaAreaWidth() {
455
- // @ts-expect-error
456
727
  return self.width - self.treeAreaWidth
457
728
  },
458
-
729
+ /**
730
+ * #getter
731
+ */
459
732
  get blanks() {
460
733
  const blanks = []
461
734
  const strs = this.hierarchy
@@ -476,24 +749,32 @@ const MSAModel = types
476
749
  }
477
750
  return blanks
478
751
  },
479
-
752
+ /**
753
+ * #getter
754
+ */
480
755
  get rows() {
481
756
  return this.hierarchy
482
757
  .leaves()
483
758
  .map(({ data }) => [data.name, this.MSA?.getRow(data.name)] as const)
484
759
  .filter((f): f is [string, string[]] => !!f[1])
485
760
  },
486
-
761
+ /**
762
+ * #getter
763
+ */
487
764
  get columns() {
488
765
  return Object.fromEntries(
489
766
  this.rows.map((row, index) => [row[0], this.columns2d[index]] as const),
490
767
  )
491
768
  },
492
-
769
+ /**
770
+ * #getter
771
+ */
493
772
  get columns2d() {
494
773
  return this.rows.map(r => r[1]).map(str => skipBlanks(this.blanks, str))
495
774
  },
496
-
775
+ /**
776
+ * #getter
777
+ */
497
778
  get colStats() {
498
779
  const r = [] as Record<string, number>[]
499
780
  const columns = this.columns2d
@@ -509,8 +790,10 @@ const MSAModel = types
509
790
  }
510
791
  return r
511
792
  },
512
-
513
- // generates a new tree that is clustered with x,y positions
793
+ /**
794
+ * #getter
795
+ * generates a new tree that is clustered with x,y positions
796
+ */
514
797
  get hierarchy(): HierarchyNode<NodeWithIdsAndLength> {
515
798
  const root = this.root
516
799
  const clust = cluster<NodeWithIds>()
@@ -524,12 +807,17 @@ const MSAModel = types
524
807
  )
525
808
  return root as HierarchyNode<NodeWithIdsAndLength>
526
809
  },
527
-
810
+ /**
811
+ * #getter
812
+ */
528
813
  get totalHeight() {
529
814
  return this.root.leaves().length * self.rowHeight
530
815
  },
531
816
  }))
532
817
  .actions(self => ({
818
+ /**
819
+ * #action
820
+ */
533
821
  addUniprotTrack(node: { name: string; accession: string }) {
534
822
  if (self.boxTracks.some(t => t.name === node.name)) {
535
823
  if (self.turnedOffTracks.has(node.name)) {
@@ -543,11 +831,15 @@ const MSAModel = types
543
831
  })
544
832
  }
545
833
  },
546
-
834
+ /**
835
+ * #action
836
+ */
547
837
  doScrollY(deltaY: number) {
548
838
  self.scrollY = clamp(-self.totalHeight + 10, self.scrollY + deltaY, 0)
549
839
  },
550
-
840
+ /**
841
+ * #action
842
+ */
551
843
  doScrollX(deltaX: number) {
552
844
  self.scrollX = clamp(
553
845
  -self.numColumns + (self.msaAreaWidth - 100),
@@ -555,6 +847,9 @@ const MSAModel = types
555
847
  0,
556
848
  )
557
849
  },
850
+ /**
851
+ * #action
852
+ */
558
853
  setMouseoveredColumn(n: number, chain: string, file: string) {
559
854
  let j = 0
560
855
  let i = 0
@@ -572,7 +867,9 @@ const MSAModel = types
572
867
  self.mouseCol = undefined
573
868
  }
574
869
  },
575
-
870
+ /**
871
+ * #action
872
+ */
576
873
  toggleTrack(id: string) {
577
874
  if (self.turnedOffTracks.has(id)) {
578
875
  self.turnedOffTracks.delete(id)
@@ -582,14 +879,21 @@ const MSAModel = types
582
879
  },
583
880
  }))
584
881
  .views(self => ({
882
+ /**
883
+ * #getter
884
+ */
585
885
  get secondaryStructureConsensus() {
586
886
  return self.MSA?.secondaryStructureConsensus
587
887
  },
588
-
888
+ /**
889
+ * #getter
890
+ */
589
891
  get seqConsensus() {
590
892
  return self.MSA?.seqConsensus
591
893
  },
592
-
894
+ /**
895
+ * #getter
896
+ */
593
897
  get conservation() {
594
898
  if (self.columns2d.length) {
595
899
  for (let i = 0; i < self.columns2d[0].length; i++) {
@@ -601,7 +905,9 @@ const MSAModel = types
601
905
  }
602
906
  return ['a']
603
907
  },
604
-
908
+ /**
909
+ * #getter
910
+ */
605
911
  get tracks(): BasicTrack[] {
606
912
  const blanks = self.blanks
607
913
  const adapterTracks = self.MSA
@@ -663,16 +969,22 @@ const MSAModel = types
663
969
 
664
970
  return [...adapterTracks, ...boxTracks, ...annotationTracks]
665
971
  },
666
-
972
+ /**
973
+ * #getter
974
+ */
667
975
  get turnedOnTracks() {
668
976
  return this.tracks.filter(f => !self.turnedOffTracks.has(f.model.id))
669
977
  },
670
-
671
- // returns coordinate in the current relative coordinate scheme
978
+ /**
979
+ * #method
980
+ * returns coordinate in the current relative coordinate scheme
981
+ */
672
982
  pxToBp(coord: number) {
673
983
  return Math.floor((coord - self.scrollX) / self.colWidth)
674
984
  },
675
-
985
+ /**
986
+ * #method
987
+ */
676
988
  rowSpecificBpToPx(rowName: string, position: number) {
677
989
  const { rowNames, rows, blanks } = self
678
990
  const index = rowNames.indexOf(rowName)
@@ -680,6 +992,7 @@ const MSAModel = types
680
992
  const details = self.getRowData(rowName)
681
993
  const offset = details.range?.start || 0
682
994
  const current = position - offset
995
+ const s = new Set(self.blanks)
683
996
 
684
997
  if (current < 0) {
685
998
  return 0
@@ -696,24 +1009,31 @@ const MSAModel = types
696
1009
 
697
1010
  let count = 0
698
1011
  for (let k = 0; k < row.length; k++) {
699
- if (blanks.includes(k) && k < i + 1) {
1012
+ if (s.has(k) && k < i + 1) {
700
1013
  count++
701
1014
  }
702
1015
  }
703
1016
 
704
1017
  return i - count
705
1018
  },
1019
+ /**
1020
+ * #method
1021
+ */
706
1022
  globalBpToPx(position: number) {
707
1023
  let count = 0
1024
+ const s = new Set(self.blanks)
1025
+
708
1026
  for (let k = 0; k < self.rows[0]?.[1].length; k++) {
709
- if (self.blanks.includes(k) && k < position + 1) {
1027
+ if (s.has(k) && k < position + 1) {
710
1028
  count++
711
1029
  }
712
1030
  }
713
1031
 
714
1032
  return position - count
715
1033
  },
716
-
1034
+ /**
1035
+ * #method
1036
+ */
717
1037
  relativePxToBp(rowName: string, position: number) {
718
1038
  const { rowNames, rows } = self
719
1039
  const index = rowNames.indexOf(rowName)
@@ -732,7 +1052,9 @@ const MSAModel = types
732
1052
  }
733
1053
  return 0
734
1054
  },
735
-
1055
+ /**
1056
+ * #method
1057
+ */
736
1058
  getPos(pos: number) {
737
1059
  let j = 0
738
1060
  for (let i = 0, k = 0; i < pos; i++, j++) {
@@ -745,6 +1067,9 @@ const MSAModel = types
745
1067
  },
746
1068
  }))
747
1069
  .actions(self => ({
1070
+ /**
1071
+ * #action
1072
+ */
748
1073
  addAnnotation(
749
1074
  start: number,
750
1075
  end: number,
@@ -756,21 +1081,23 @@ const MSAModel = types
756
1081
  attributes,
757
1082
  })
758
1083
  },
759
-
760
- setOffsets(left: number, right: number) {
1084
+ /**
1085
+ * #action
1086
+ */
1087
+ setAnnotationClickBoundaries(left: number, right: number) {
761
1088
  self.annotPos = { left, right }
762
1089
  },
763
-
764
- clearAnnotPos() {
1090
+ /**
1091
+ * #action
1092
+ */
1093
+ clearAnnotationClickBoundaries() {
765
1094
  self.annotPos = undefined
766
1095
  },
767
1096
  }))
768
-
769
- const model = types.snapshotProcessor(types.compose(BaseViewModel, MSAModel), {
770
- postProcessor(result) {
1097
+ .postProcessSnapshot(result => {
771
1098
  const snap = result as Omit<typeof result, symbol>
772
1099
  const {
773
- data: { tree, msa },
1100
+ data: { tree, msa, treeMetadata },
774
1101
  ...rest
775
1102
  } = snap
776
1103
 
@@ -781,11 +1108,11 @@ const model = types.snapshotProcessor(types.compose(BaseViewModel, MSAModel), {
781
1108
  // https://andreasimonecosta.dev/posts/the-shortest-way-to-conditionally-insert-properties-into-an-object-literal/
782
1109
  ...(!result.treeFilehandle && { tree }),
783
1110
  ...(!result.msaFilehandle && { msa }),
1111
+ ...(!result.treeMetadataFilehandle && { treeMetadata }),
784
1112
  },
785
1113
  ...rest,
786
1114
  }
787
- },
788
- })
1115
+ })
789
1116
 
790
1117
  export default model
791
1118