react-msaview 4.4.6 → 4.6.0
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.
- package/bundle/index.js +9 -9
- package/bundle/index.js.LICENSE.txt +8 -8
- package/bundle/index.js.map +1 -1
- package/dist/colorSchemes.d.ts +0 -6
- package/dist/colorSchemes.js +1 -119
- package/dist/colorSchemes.js.map +1 -1
- package/dist/components/ConservationTrack.d.ts +8 -0
- package/dist/components/ConservationTrack.js +54 -0
- package/dist/components/ConservationTrack.js.map +1 -0
- package/dist/components/Loading.js +14 -2
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/MSAView.js +36 -0
- package/dist/components/MSAView.js.map +1 -1
- package/dist/components/SequenceTextArea.js +3 -2
- package/dist/components/SequenceTextArea.js.map +1 -1
- package/dist/components/TextTrack.d.ts +3 -3
- package/dist/components/TextTrack.js +4 -1
- package/dist/components/TextTrack.js.map +1 -1
- package/dist/components/Track.js +21 -8
- package/dist/components/Track.js.map +1 -1
- package/dist/components/dialogs/ExportSVGDialog.js +19 -3
- package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
- package/dist/components/header/GappynessSlider.d.ts +6 -0
- package/dist/components/header/GappynessSlider.js +19 -0
- package/dist/components/header/GappynessSlider.js.map +1 -0
- package/dist/components/header/Header.js +3 -1
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/HeaderMenu.js +30 -14
- package/dist/components/header/HeaderMenu.js.map +1 -1
- package/dist/components/minimap/MinimapSVG.js +4 -3
- package/dist/components/minimap/MinimapSVG.js.map +1 -1
- package/dist/components/msa/MSACanvasBlock.js +56 -42
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/renderMSABlock.js +71 -26
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +8 -1
- package/dist/components/msa/renderMSAMouseover.js.map +1 -1
- package/dist/components/tracks/renderTracksSvg.d.ts +29 -0
- package/dist/components/tracks/renderTracksSvg.js +83 -0
- package/dist/components/tracks/renderTracksSvg.js.map +1 -0
- package/dist/components/tree/TreeNodeMenu.js +2 -2
- package/dist/components/tree/TreeNodeMenu.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.d.ts +0 -1
- package/dist/components/tree/renderTreeCanvas.js +23 -24
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/constants.d.ts +22 -0
- package/dist/constants.js +26 -0
- package/dist/constants.js.map +1 -0
- package/dist/layout.js.map +1 -1
- package/dist/model/msaModel.js +3 -2
- package/dist/model/msaModel.js.map +1 -1
- package/dist/model/treeModel.js +9 -8
- package/dist/model/treeModel.js.map +1 -1
- package/dist/model.d.ts +271 -15
- package/dist/model.js +427 -128
- package/dist/model.js.map +1 -1
- package/dist/neighborJoining.d.ts +1 -0
- package/dist/neighborJoining.js +839 -0
- package/dist/neighborJoining.js.map +1 -0
- package/dist/neighborJoining.test.d.ts +1 -0
- package/dist/neighborJoining.test.js +110 -0
- package/dist/neighborJoining.test.js.map +1 -0
- package/dist/parsers/A3mMSA.d.ts +43 -0
- package/dist/parsers/A3mMSA.js +277 -0
- package/dist/parsers/A3mMSA.js.map +1 -0
- package/dist/parsers/A3mMSA.test.d.ts +1 -0
- package/dist/parsers/A3mMSA.test.js +138 -0
- package/dist/parsers/A3mMSA.test.js.map +1 -0
- package/dist/parsers/ClustalMSA.d.ts +4 -4
- package/dist/parsers/ClustalMSA.js +3 -1
- package/dist/parsers/ClustalMSA.js.map +1 -1
- package/dist/parsers/FastaMSA.js +17 -16
- package/dist/parsers/FastaMSA.js.map +1 -1
- package/dist/renderToSvg.d.ts +1 -0
- package/dist/renderToSvg.js +48 -18
- package/dist/renderToSvg.js.map +1 -1
- package/dist/rowCoordinateCalculations.js +2 -0
- package/dist/rowCoordinateCalculations.js.map +1 -1
- package/dist/types.d.ts +2 -3
- package/dist/util.js +17 -9
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -6
- package/src/colorSchemes.ts +1 -179
- package/src/components/ConservationTrack.tsx +104 -0
- package/src/components/Loading.tsx +44 -2
- package/src/components/MSAView.tsx +68 -0
- package/src/components/SequenceTextArea.tsx +3 -2
- package/src/components/TextTrack.tsx +7 -4
- package/src/components/Track.tsx +25 -9
- package/src/components/dialogs/ExportSVGDialog.tsx +25 -1
- package/src/components/header/GappynessSlider.tsx +35 -0
- package/src/components/header/Header.tsx +3 -1
- package/src/components/header/HeaderMenu.tsx +36 -15
- package/src/components/minimap/MinimapSVG.tsx +6 -3
- package/src/components/msa/MSACanvasBlock.tsx +66 -48
- package/src/components/msa/renderMSABlock.ts +103 -40
- package/src/components/msa/renderMSAMouseover.ts +9 -0
- package/src/components/tracks/renderTracksSvg.ts +157 -0
- package/src/components/tree/TreeNodeMenu.tsx +2 -2
- package/src/components/tree/renderTreeCanvas.ts +25 -34
- package/src/constants.ts +27 -0
- package/src/layout.ts +1 -6
- package/src/model/msaModel.ts +4 -2
- package/src/model/treeModel.ts +19 -8
- package/src/model.ts +517 -140
- package/src/neighborJoining.test.ts +129 -0
- package/src/neighborJoining.ts +885 -0
- package/src/parsers/A3mMSA.test.ts +164 -0
- package/src/parsers/A3mMSA.ts +321 -0
- package/src/parsers/ClustalMSA.ts +7 -5
- package/src/parsers/FastaMSA.ts +17 -17
- package/src/renderToSvg.tsx +105 -26
- package/src/rowCoordinateCalculations.ts +2 -0
- package/src/types.ts +2 -4
- package/src/util.ts +21 -8
- package/src/version.ts +1 -1
- package/dist/components/dialogs/TracklistDialog.d.ts +0 -7
- package/dist/components/dialogs/TracklistDialog.js +0 -23
- package/dist/components/dialogs/TracklistDialog.js.map +0 -1
- package/src/components/dialogs/TracklistDialog.tsx +0 -73
package/src/model.ts
CHANGED
|
@@ -20,7 +20,32 @@ import Stockholm from 'stockholm-js'
|
|
|
20
20
|
|
|
21
21
|
import { blocksX, blocksY } from './calculateBlocks'
|
|
22
22
|
import colorSchemes from './colorSchemes'
|
|
23
|
+
import ConservationTrack from './components/ConservationTrack'
|
|
23
24
|
import TextTrack from './components/TextTrack'
|
|
25
|
+
import {
|
|
26
|
+
defaultAllowedGappyness,
|
|
27
|
+
defaultBgColor,
|
|
28
|
+
defaultColWidth,
|
|
29
|
+
defaultColorSchemeName,
|
|
30
|
+
defaultContrastLettering,
|
|
31
|
+
defaultCurrentAlignment,
|
|
32
|
+
defaultDrawLabels,
|
|
33
|
+
defaultDrawMsaLetters,
|
|
34
|
+
defaultDrawNodeBubbles,
|
|
35
|
+
defaultDrawTree,
|
|
36
|
+
defaultHeight,
|
|
37
|
+
defaultHideGaps,
|
|
38
|
+
defaultLabelsAlignRight,
|
|
39
|
+
defaultRowHeight,
|
|
40
|
+
defaultScrollX,
|
|
41
|
+
defaultScrollY,
|
|
42
|
+
defaultShowBranchLen,
|
|
43
|
+
defaultShowDomains,
|
|
44
|
+
defaultSubFeatureRows,
|
|
45
|
+
defaultTreeAreaWidth,
|
|
46
|
+
defaultTreeWidth,
|
|
47
|
+
defaultTreeWidthMatchesArea,
|
|
48
|
+
} from './constants'
|
|
24
49
|
import { flatToTree } from './flatToTree'
|
|
25
50
|
import palettes from './ggplotPalettes'
|
|
26
51
|
import { measureTextCanvas } from './measureTextCanvas'
|
|
@@ -28,8 +53,10 @@ import { DataModelF } from './model/DataModel'
|
|
|
28
53
|
import { DialogQueueSessionMixin } from './model/DialogQueue'
|
|
29
54
|
import { MSAModelF } from './model/msaModel'
|
|
30
55
|
import { TreeModelF } from './model/treeModel'
|
|
56
|
+
import { calculateNeighborJoiningTree } from './neighborJoining'
|
|
31
57
|
import { parseAsn1 } from './parseAsn1'
|
|
32
58
|
import parseNewick from './parseNewick'
|
|
59
|
+
import A3mMSA from './parsers/A3mMSA'
|
|
33
60
|
import ClustalMSA from './parsers/ClustalMSA'
|
|
34
61
|
import EmfMSA from './parsers/EmfMSA'
|
|
35
62
|
import FastaMSA from './parsers/FastaMSA'
|
|
@@ -43,7 +70,6 @@ import { seqCoordToRowSpecificGlobalCoord } from './seqCoordToRowSpecificGlobalC
|
|
|
43
70
|
import {
|
|
44
71
|
collapse,
|
|
45
72
|
generateNodeIds,
|
|
46
|
-
isBlank,
|
|
47
73
|
len,
|
|
48
74
|
maxLength,
|
|
49
75
|
setBrLength,
|
|
@@ -63,8 +89,6 @@ import type { Theme } from '@mui/material'
|
|
|
63
89
|
import type { HierarchyNode } from 'd3-hierarchy'
|
|
64
90
|
import type { Instance } from 'mobx-state-tree'
|
|
65
91
|
|
|
66
|
-
const defaultRowHeight = 16
|
|
67
|
-
const defaultColWidth = 12
|
|
68
92
|
const showZoomStarKey = 'msa-showZoomStar'
|
|
69
93
|
|
|
70
94
|
/**
|
|
@@ -90,24 +114,24 @@ function stateModelFactory() {
|
|
|
90
114
|
/**
|
|
91
115
|
* #property
|
|
92
116
|
*/
|
|
93
|
-
showDomains:
|
|
117
|
+
showDomains: defaultShowDomains,
|
|
94
118
|
/**
|
|
95
119
|
* #property
|
|
96
120
|
*/
|
|
97
|
-
hideGaps:
|
|
121
|
+
hideGaps: defaultHideGaps,
|
|
98
122
|
/**
|
|
99
123
|
* #property
|
|
100
124
|
*/
|
|
101
|
-
allowedGappyness:
|
|
125
|
+
allowedGappyness: defaultAllowedGappyness,
|
|
102
126
|
/**
|
|
103
127
|
* #property
|
|
104
128
|
*/
|
|
105
|
-
contrastLettering:
|
|
129
|
+
contrastLettering: defaultContrastLettering,
|
|
106
130
|
|
|
107
131
|
/**
|
|
108
132
|
* #property
|
|
109
133
|
*/
|
|
110
|
-
subFeatureRows:
|
|
134
|
+
subFeatureRows: defaultSubFeatureRows,
|
|
111
135
|
|
|
112
136
|
/**
|
|
113
137
|
* #property
|
|
@@ -118,13 +142,13 @@ function stateModelFactory() {
|
|
|
118
142
|
/**
|
|
119
143
|
* #property
|
|
120
144
|
*/
|
|
121
|
-
drawMsaLetters:
|
|
145
|
+
drawMsaLetters: defaultDrawMsaLetters,
|
|
122
146
|
|
|
123
147
|
/**
|
|
124
148
|
* #property
|
|
125
149
|
* height of the div containing the view, px
|
|
126
150
|
*/
|
|
127
|
-
height: types.optional(types.number,
|
|
151
|
+
height: types.optional(types.number, defaultHeight),
|
|
128
152
|
|
|
129
153
|
/**
|
|
130
154
|
* #property
|
|
@@ -136,13 +160,13 @@ function stateModelFactory() {
|
|
|
136
160
|
* #property
|
|
137
161
|
* scroll position, Y-offset, px
|
|
138
162
|
*/
|
|
139
|
-
scrollY:
|
|
163
|
+
scrollY: defaultScrollY,
|
|
140
164
|
|
|
141
165
|
/**
|
|
142
166
|
* #property
|
|
143
167
|
* scroll position, X-offset, px
|
|
144
168
|
*/
|
|
145
|
-
scrollX:
|
|
169
|
+
scrollX: defaultScrollX,
|
|
146
170
|
|
|
147
171
|
/**
|
|
148
172
|
* #property
|
|
@@ -173,7 +197,7 @@ function stateModelFactory() {
|
|
|
173
197
|
* #property
|
|
174
198
|
*
|
|
175
199
|
*/
|
|
176
|
-
currentAlignment:
|
|
200
|
+
currentAlignment: defaultCurrentAlignment,
|
|
177
201
|
|
|
178
202
|
/**
|
|
179
203
|
* #property
|
|
@@ -297,6 +321,12 @@ function stateModelFactory() {
|
|
|
297
321
|
| { nodeId: string; descendantNames: string[] }
|
|
298
322
|
| undefined,
|
|
299
323
|
|
|
324
|
+
/**
|
|
325
|
+
* #volatile
|
|
326
|
+
* array of column indices to highlight
|
|
327
|
+
*/
|
|
328
|
+
highlightedColumns: undefined as number[] | undefined,
|
|
329
|
+
|
|
300
330
|
/**
|
|
301
331
|
* #volatile
|
|
302
332
|
* a dummy variable that is incremented when ref changes so autorun for
|
|
@@ -363,7 +393,7 @@ function stateModelFactory() {
|
|
|
363
393
|
self.loadingMSA = arg
|
|
364
394
|
},
|
|
365
395
|
/**
|
|
366
|
-
* #
|
|
396
|
+
* #action
|
|
367
397
|
*/
|
|
368
398
|
setShowZoomStar(arg: boolean) {
|
|
369
399
|
self.showZoomStar = arg
|
|
@@ -416,8 +446,8 @@ function stateModelFactory() {
|
|
|
416
446
|
}
|
|
417
447
|
|
|
418
448
|
// Find the node in the hierarchy
|
|
419
|
-
const node = (self as
|
|
420
|
-
|
|
449
|
+
const node = (self as MsaViewModel).hierarchy.find(
|
|
450
|
+
n => n.data.id === nodeId,
|
|
421
451
|
)
|
|
422
452
|
if (!node) {
|
|
423
453
|
self.hoveredTreeNode = undefined
|
|
@@ -429,6 +459,13 @@ function stateModelFactory() {
|
|
|
429
459
|
|
|
430
460
|
self.hoveredTreeNode = { nodeId, descendantNames }
|
|
431
461
|
},
|
|
462
|
+
/**
|
|
463
|
+
* #action
|
|
464
|
+
* set highlighted columns
|
|
465
|
+
*/
|
|
466
|
+
setHighlightedColumns(columns?: number[]) {
|
|
467
|
+
self.highlightedColumns = columns
|
|
468
|
+
},
|
|
432
469
|
/**
|
|
433
470
|
* #action
|
|
434
471
|
*/
|
|
@@ -497,7 +534,7 @@ function stateModelFactory() {
|
|
|
497
534
|
/**
|
|
498
535
|
* #action
|
|
499
536
|
*/
|
|
500
|
-
|
|
537
|
+
toggleCollapsedLeaf(node: string) {
|
|
501
538
|
if (self.collapsedLeaves.includes(node)) {
|
|
502
539
|
self.collapsedLeaves.remove(node)
|
|
503
540
|
} else {
|
|
@@ -555,11 +592,23 @@ function stateModelFactory() {
|
|
|
555
592
|
}))
|
|
556
593
|
|
|
557
594
|
.views(self => ({
|
|
595
|
+
/**
|
|
596
|
+
* #getter
|
|
597
|
+
* hideGaps takes effect when there are collapsed rows or allowedGappyness < 100
|
|
598
|
+
*/
|
|
599
|
+
get hideGapsEffective() {
|
|
600
|
+
return (
|
|
601
|
+
self.hideGaps &&
|
|
602
|
+
(self.collapsed.length > 0 ||
|
|
603
|
+
self.collapsedLeaves.length > 0 ||
|
|
604
|
+
self.allowedGappyness < 100)
|
|
605
|
+
)
|
|
606
|
+
},
|
|
558
607
|
/**
|
|
559
608
|
* #getter
|
|
560
609
|
*/
|
|
561
610
|
get realAllowedGappyness() {
|
|
562
|
-
return
|
|
611
|
+
return this.hideGapsEffective ? self.allowedGappyness : 100
|
|
563
612
|
},
|
|
564
613
|
/**
|
|
565
614
|
* #getter
|
|
@@ -643,6 +692,8 @@ function stateModelFactory() {
|
|
|
643
692
|
if (text) {
|
|
644
693
|
if (Stockholm.sniff(text)) {
|
|
645
694
|
return new StockholmMSA(text, self.currentAlignment)
|
|
695
|
+
} else if (A3mMSA.sniff(text)) {
|
|
696
|
+
return new A3mMSA(text)
|
|
646
697
|
} else if (text.startsWith('>')) {
|
|
647
698
|
return new FastaMSA(text)
|
|
648
699
|
} else if (text.startsWith('SEQ')) {
|
|
@@ -697,6 +748,33 @@ function stateModelFactory() {
|
|
|
697
748
|
const { mouseRow } = self
|
|
698
749
|
return mouseRow === undefined ? undefined : this.rowNames[mouseRow]
|
|
699
750
|
},
|
|
751
|
+
/**
|
|
752
|
+
* #getter
|
|
753
|
+
* Returns insertion info if mouse is hovering over an insertion indicator
|
|
754
|
+
*/
|
|
755
|
+
get hoveredInsertion() {
|
|
756
|
+
const { mouseCol, mouseRow } = self
|
|
757
|
+
if (mouseCol === undefined || mouseRow === undefined) {
|
|
758
|
+
return undefined
|
|
759
|
+
}
|
|
760
|
+
const rowName = this.rowNames[mouseRow]
|
|
761
|
+
if (!rowName) {
|
|
762
|
+
return undefined
|
|
763
|
+
}
|
|
764
|
+
const insertions = this.insertionPositions.get(rowName)
|
|
765
|
+
if (!insertions) {
|
|
766
|
+
return undefined
|
|
767
|
+
}
|
|
768
|
+
const insertion = insertions.find(ins => ins.pos === mouseCol)
|
|
769
|
+
if (insertion) {
|
|
770
|
+
return {
|
|
771
|
+
rowName,
|
|
772
|
+
col: mouseCol,
|
|
773
|
+
letters: insertion.letters,
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
return undefined
|
|
777
|
+
},
|
|
700
778
|
|
|
701
779
|
/**
|
|
702
780
|
* #getter
|
|
@@ -719,7 +797,7 @@ function stateModelFactory() {
|
|
|
719
797
|
;[...self.collapsed, ...self.collapsedLeaves]
|
|
720
798
|
.map(collapsedId => hier.find(node => node.data.id === collapsedId))
|
|
721
799
|
.filter(notEmpty)
|
|
722
|
-
.
|
|
800
|
+
.forEach(node => {
|
|
723
801
|
collapse(node)
|
|
724
802
|
})
|
|
725
803
|
|
|
@@ -744,28 +822,35 @@ function stateModelFactory() {
|
|
|
744
822
|
* #getter
|
|
745
823
|
*/
|
|
746
824
|
get blanks() {
|
|
747
|
-
const {
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
825
|
+
const { hideGapsEffective, realAllowedGappyness } = self
|
|
826
|
+
if (!hideGapsEffective) {
|
|
827
|
+
return []
|
|
828
|
+
}
|
|
829
|
+
const strs = this.leaves
|
|
830
|
+
.map(leaf => this.MSA?.getRow(leaf.data.name))
|
|
831
|
+
.filter(notEmpty)
|
|
832
|
+
if (strs.length === 0) {
|
|
833
|
+
return []
|
|
834
|
+
}
|
|
835
|
+
const numCols = strs[0]!.length
|
|
836
|
+
const numRows = strs.length
|
|
837
|
+
const threshold = Math.ceil((realAllowedGappyness / 100) * numRows)
|
|
838
|
+
const blankCounts = new Uint16Array(numCols)
|
|
839
|
+
for (let j = 0; j < numRows; j++) {
|
|
840
|
+
const str = strs[j]!
|
|
841
|
+
for (let i = 0; i < numCols; i++) {
|
|
842
|
+
// bit trick: (code - 45) >>> 0 <= 1 checks for '-' (45) or '.' (46)
|
|
843
|
+
if ((str.charCodeAt(i) - 45) >>> 0 <= 1) {
|
|
844
|
+
blankCounts[i]!++
|
|
766
845
|
}
|
|
767
846
|
}
|
|
768
847
|
}
|
|
848
|
+
const blanks = []
|
|
849
|
+
for (let i = 0; i < numCols; i++) {
|
|
850
|
+
if (blankCounts[i]! >= threshold) {
|
|
851
|
+
blanks.push(i)
|
|
852
|
+
}
|
|
853
|
+
}
|
|
769
854
|
return blanks
|
|
770
855
|
},
|
|
771
856
|
/**
|
|
@@ -774,6 +859,60 @@ function stateModelFactory() {
|
|
|
774
859
|
get blanksSet() {
|
|
775
860
|
return new Set(this.blanks)
|
|
776
861
|
},
|
|
862
|
+
/**
|
|
863
|
+
* #getter
|
|
864
|
+
* Returns a map of row name to array of insertions with display position and letters
|
|
865
|
+
*/
|
|
866
|
+
get insertionPositions() {
|
|
867
|
+
const { hideGapsEffective } = self
|
|
868
|
+
const { blanks, rows } = this
|
|
869
|
+
const blanksLen = blanks.length
|
|
870
|
+
if (blanksLen === 0 || !hideGapsEffective) {
|
|
871
|
+
return new Map<string, { pos: number; letters: string }[]>()
|
|
872
|
+
}
|
|
873
|
+
const result = new Map<string, { pos: number; letters: string }[]>()
|
|
874
|
+
for (const [name, seq] of rows) {
|
|
875
|
+
const insertions: { pos: number; letters: string }[] = []
|
|
876
|
+
let displayPos = 0
|
|
877
|
+
let blankIdx = 0
|
|
878
|
+
let currentInsertPos = -1
|
|
879
|
+
let letterChars: string[] = []
|
|
880
|
+
const seqLen = seq.length
|
|
881
|
+
for (let i = 0; i < seqLen; i++) {
|
|
882
|
+
if (blankIdx < blanksLen && blanks[blankIdx] === i) {
|
|
883
|
+
// bit trick: (code - 45) >>> 0 <= 1 checks for '-' (45) or '.' (46)
|
|
884
|
+
const code = seq.charCodeAt(i)
|
|
885
|
+
if (!((code - 45) >>> 0 <= 1)) {
|
|
886
|
+
if (currentInsertPos === displayPos) {
|
|
887
|
+
letterChars.push(seq[i]!)
|
|
888
|
+
} else {
|
|
889
|
+
if (letterChars.length > 0) {
|
|
890
|
+
insertions.push({
|
|
891
|
+
pos: currentInsertPos,
|
|
892
|
+
letters: letterChars.join(''),
|
|
893
|
+
})
|
|
894
|
+
}
|
|
895
|
+
currentInsertPos = displayPos
|
|
896
|
+
letterChars = [seq[i]!]
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
blankIdx++
|
|
900
|
+
} else {
|
|
901
|
+
displayPos++
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
if (letterChars.length > 0) {
|
|
905
|
+
insertions.push({
|
|
906
|
+
pos: currentInsertPos,
|
|
907
|
+
letters: letterChars.join(''),
|
|
908
|
+
})
|
|
909
|
+
}
|
|
910
|
+
if (insertions.length > 0) {
|
|
911
|
+
result.set(name, insertions)
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
return result
|
|
915
|
+
},
|
|
777
916
|
/**
|
|
778
917
|
* #getter
|
|
779
918
|
*/
|
|
@@ -810,10 +949,10 @@ function stateModelFactory() {
|
|
|
810
949
|
* #getter
|
|
811
950
|
*/
|
|
812
951
|
get columns2d() {
|
|
813
|
-
const {
|
|
952
|
+
const { hideGapsEffective } = self
|
|
814
953
|
return this.rows
|
|
815
954
|
.map(r => r[1])
|
|
816
|
-
.map(str => (
|
|
955
|
+
.map(str => (hideGapsEffective ? skipBlanks(this.blanks, str) : str))
|
|
817
956
|
},
|
|
818
957
|
/**
|
|
819
958
|
* #getter
|
|
@@ -847,6 +986,216 @@ function stateModelFactory() {
|
|
|
847
986
|
get colStatsSums() {
|
|
848
987
|
return this.colStats.map(val => sum(Object.values(val)))
|
|
849
988
|
},
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* #getter
|
|
992
|
+
* Pre-computed consensus letter and percent identity color per column.
|
|
993
|
+
* Used by percent_identity_dynamic color scheme.
|
|
994
|
+
*/
|
|
995
|
+
get colConsensus() {
|
|
996
|
+
const { colStats, colStatsSums } = this
|
|
997
|
+
return colStats.map((stats, i) => {
|
|
998
|
+
const total = colStatsSums[i]!
|
|
999
|
+
let maxCount = 0
|
|
1000
|
+
let letter = ''
|
|
1001
|
+
for (const key in stats) {
|
|
1002
|
+
const val = stats[key]!
|
|
1003
|
+
if (val > maxCount && key !== '-' && key !== '.') {
|
|
1004
|
+
maxCount = val
|
|
1005
|
+
letter = key
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
const proportion = maxCount / total
|
|
1009
|
+
return {
|
|
1010
|
+
letter,
|
|
1011
|
+
color:
|
|
1012
|
+
proportion > 0.4
|
|
1013
|
+
? `hsl(240, 30%, ${100 * Math.max(1 - proportion / 3, 0.3)}%)`
|
|
1014
|
+
: undefined,
|
|
1015
|
+
}
|
|
1016
|
+
})
|
|
1017
|
+
},
|
|
1018
|
+
|
|
1019
|
+
/**
|
|
1020
|
+
* #getter
|
|
1021
|
+
* Pre-computed ClustalX colors per column.
|
|
1022
|
+
* Returns a map of letter -> color for each column.
|
|
1023
|
+
* ref http://www.jalview.org/help/html/colourSchemes/clustal.html
|
|
1024
|
+
*/
|
|
1025
|
+
get colClustalX() {
|
|
1026
|
+
const { colStats, colStatsSums } = this
|
|
1027
|
+
return colStats.map((stats, i) => {
|
|
1028
|
+
const total = colStatsSums[i]!
|
|
1029
|
+
const colors: Record<string, string> = {}
|
|
1030
|
+
|
|
1031
|
+
const W = stats.W ?? 0
|
|
1032
|
+
const L = stats.L ?? 0
|
|
1033
|
+
const V = stats.V ?? 0
|
|
1034
|
+
const I = stats.I ?? 0
|
|
1035
|
+
const M = stats.M ?? 0
|
|
1036
|
+
const A = stats.A ?? 0
|
|
1037
|
+
const F = stats.F ?? 0
|
|
1038
|
+
const C = stats.C ?? 0
|
|
1039
|
+
const H = stats.H ?? 0
|
|
1040
|
+
const P = stats.P ?? 0
|
|
1041
|
+
const R = stats.R ?? 0
|
|
1042
|
+
const K = stats.K ?? 0
|
|
1043
|
+
const Q = stats.Q ?? 0
|
|
1044
|
+
const E = stats.E ?? 0
|
|
1045
|
+
const D = stats.D ?? 0
|
|
1046
|
+
const T = stats.T ?? 0
|
|
1047
|
+
const S = stats.S ?? 0
|
|
1048
|
+
const G = stats.G ?? 0
|
|
1049
|
+
const Y = stats.Y ?? 0
|
|
1050
|
+
const N = stats.N ?? 0
|
|
1051
|
+
|
|
1052
|
+
const WLVIMAFCHPY = W + L + V + I + M + A + F + C + H + P + Y
|
|
1053
|
+
const KR = K + R
|
|
1054
|
+
const QE = Q + E
|
|
1055
|
+
const ED = E + D
|
|
1056
|
+
const TS = T + S
|
|
1057
|
+
|
|
1058
|
+
if (WLVIMAFCHPY / total > 0.6) {
|
|
1059
|
+
colors.W = 'rgb(128,179,230)'
|
|
1060
|
+
colors.L = 'rgb(128,179,230)'
|
|
1061
|
+
colors.V = 'rgb(128,179,230)'
|
|
1062
|
+
colors.A = 'rgb(128,179,230)'
|
|
1063
|
+
colors.I = 'rgb(128,179,230)'
|
|
1064
|
+
colors.M = 'rgb(128,179,230)'
|
|
1065
|
+
colors.F = 'rgb(128,179,230)'
|
|
1066
|
+
colors.C = 'rgb(128,179,230)'
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
if (
|
|
1070
|
+
KR / total > 0.6 ||
|
|
1071
|
+
K / total > 0.8 ||
|
|
1072
|
+
R / total > 0.8 ||
|
|
1073
|
+
Q / total > 0.8
|
|
1074
|
+
) {
|
|
1075
|
+
colors.K = '#d88'
|
|
1076
|
+
colors.R = '#d88'
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
if (
|
|
1080
|
+
KR / total > 0.6 ||
|
|
1081
|
+
QE / total > 0.5 ||
|
|
1082
|
+
E / total > 0.8 ||
|
|
1083
|
+
Q / total > 0.8 ||
|
|
1084
|
+
D / total > 0.8
|
|
1085
|
+
) {
|
|
1086
|
+
colors.E = 'rgb(192, 72, 192)'
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
if (
|
|
1090
|
+
KR / total > 0.6 ||
|
|
1091
|
+
ED / total > 0.5 ||
|
|
1092
|
+
K / total > 0.8 ||
|
|
1093
|
+
R / total > 0.8 ||
|
|
1094
|
+
Q / total > 0.8
|
|
1095
|
+
) {
|
|
1096
|
+
colors.D = 'rgb(204, 77, 204)'
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
if (N / total > 0.5 || Y / total > 0.85) {
|
|
1100
|
+
colors.N = '#8f8'
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
if (
|
|
1104
|
+
KR / total > 0.6 ||
|
|
1105
|
+
QE / total > 0.6 ||
|
|
1106
|
+
Q / total > 0.85 ||
|
|
1107
|
+
E / total > 0.85 ||
|
|
1108
|
+
K / total > 0.85 ||
|
|
1109
|
+
R / total > 0.85
|
|
1110
|
+
) {
|
|
1111
|
+
colors.Q = '#8f8'
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
if (
|
|
1115
|
+
WLVIMAFCHPY / total > 0.6 ||
|
|
1116
|
+
TS / total > 0.5 ||
|
|
1117
|
+
S / total > 0.85 ||
|
|
1118
|
+
T / total > 0.85
|
|
1119
|
+
) {
|
|
1120
|
+
colors.S = 'rgb(26,204,26)'
|
|
1121
|
+
colors.T = 'rgb(26,204,26)'
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
if (C / total > 0.85) {
|
|
1125
|
+
colors.C = 'rgb(240, 128, 128)'
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
if (G / total > 0) {
|
|
1129
|
+
colors.G = 'rgb(240, 144, 72)'
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
if (P / total > 0) {
|
|
1133
|
+
colors.P = 'rgb(204, 204, 0)'
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
if (
|
|
1137
|
+
WLVIMAFCHPY / total > 0.6 ||
|
|
1138
|
+
W / total > 0.85 ||
|
|
1139
|
+
Y / total > 0.85 ||
|
|
1140
|
+
A / total > 0.85 ||
|
|
1141
|
+
C / total > 0.85 ||
|
|
1142
|
+
P / total > 0.85 ||
|
|
1143
|
+
Q / total > 0.85 ||
|
|
1144
|
+
F / total > 0.85 ||
|
|
1145
|
+
H / total > 0.85 ||
|
|
1146
|
+
I / total > 0.85 ||
|
|
1147
|
+
L / total > 0.85 ||
|
|
1148
|
+
M / total > 0.85 ||
|
|
1149
|
+
V / total > 0.85
|
|
1150
|
+
) {
|
|
1151
|
+
colors.H = 'rgb(26, 179, 179)'
|
|
1152
|
+
colors.Y = 'rgb(26, 179, 179)'
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
return colors
|
|
1156
|
+
})
|
|
1157
|
+
},
|
|
1158
|
+
|
|
1159
|
+
/**
|
|
1160
|
+
* #getter
|
|
1161
|
+
* Conservation score per column using Shannon entropy (biojs-msa style).
|
|
1162
|
+
* Conservation = (1 - H/Hmax) * (1 - gapFraction)
|
|
1163
|
+
* Returns values 0-1 where 1 = fully conserved, 0 = no conservation.
|
|
1164
|
+
*/
|
|
1165
|
+
get conservation() {
|
|
1166
|
+
const { colStats, colStatsSums } = this
|
|
1167
|
+
const alphabetSize = 20
|
|
1168
|
+
const maxEntropy = Math.log2(alphabetSize)
|
|
1169
|
+
|
|
1170
|
+
return colStats.map((stats, i) => {
|
|
1171
|
+
const total = colStatsSums[i]
|
|
1172
|
+
if (!total) {
|
|
1173
|
+
return 0
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
const gapCount = (stats['-'] || 0) + (stats['.'] || 0)
|
|
1177
|
+
const nonGapTotal = total - gapCount
|
|
1178
|
+
if (nonGapTotal === 0) {
|
|
1179
|
+
return 0
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
let entropy = 0
|
|
1183
|
+
for (const letter of Object.keys(stats)) {
|
|
1184
|
+
if (letter === '-' || letter === '.') {
|
|
1185
|
+
continue
|
|
1186
|
+
}
|
|
1187
|
+
const count = stats[letter]!
|
|
1188
|
+
const freq = count / nonGapTotal
|
|
1189
|
+
if (freq > 0) {
|
|
1190
|
+
entropy -= freq * Math.log2(freq)
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
const gapFraction = gapCount / total
|
|
1195
|
+
const conservation = Math.max(0, 1 - entropy / maxEntropy)
|
|
1196
|
+
return conservation * (1 - gapFraction)
|
|
1197
|
+
})
|
|
1198
|
+
},
|
|
850
1199
|
/**
|
|
851
1200
|
* #getter
|
|
852
1201
|
* generates a new tree that is clustered with x,y positions
|
|
@@ -881,6 +1230,14 @@ function stateModelFactory() {
|
|
|
881
1230
|
get allBranchesLength0() {
|
|
882
1231
|
return this.hierarchy.links().every(s => !s.source.data.length)
|
|
883
1232
|
},
|
|
1233
|
+
|
|
1234
|
+
/**
|
|
1235
|
+
* #getter
|
|
1236
|
+
* effective showBranchLen accounting for allBranchesLength0
|
|
1237
|
+
*/
|
|
1238
|
+
get showBranchLenEffective() {
|
|
1239
|
+
return this.allBranchesLength0 ? false : self.showBranchLen
|
|
1240
|
+
},
|
|
884
1241
|
}))
|
|
885
1242
|
.views(self => ({
|
|
886
1243
|
/**
|
|
@@ -972,6 +1329,18 @@ function stateModelFactory() {
|
|
|
972
1329
|
self.drawMsaLetters = arg
|
|
973
1330
|
},
|
|
974
1331
|
|
|
1332
|
+
/**
|
|
1333
|
+
* #action
|
|
1334
|
+
* Calculate a neighbor joining tree from the current MSA using BLOSUM62 distances
|
|
1335
|
+
*/
|
|
1336
|
+
calculateNeighborJoiningTreeFromMSA() {
|
|
1337
|
+
if (self.rows.length < 2) {
|
|
1338
|
+
throw new Error('Need at least 2 sequences to build a tree')
|
|
1339
|
+
}
|
|
1340
|
+
const newickTree = calculateNeighborJoiningTree(self.rows)
|
|
1341
|
+
self.setTree(newickTree)
|
|
1342
|
+
},
|
|
1343
|
+
|
|
975
1344
|
/**
|
|
976
1345
|
* #action
|
|
977
1346
|
*/
|
|
@@ -1105,39 +1474,22 @@ function stateModelFactory() {
|
|
|
1105
1474
|
return self.MSA?.seqConsensus
|
|
1106
1475
|
},
|
|
1107
1476
|
|
|
1108
|
-
/**
|
|
1109
|
-
* #getter
|
|
1110
|
-
*/
|
|
1111
|
-
get conservation() {
|
|
1112
|
-
if (self.columns2d.length) {
|
|
1113
|
-
for (let i = 0; i < self.columns2d[0]!.length; i++) {
|
|
1114
|
-
const col = []
|
|
1115
|
-
for (const column of self.columns2d) {
|
|
1116
|
-
col.push(column[i])
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
return ['a']
|
|
1121
|
-
},
|
|
1122
|
-
|
|
1123
1477
|
/**
|
|
1124
1478
|
* #getter
|
|
1125
1479
|
*/
|
|
1126
1480
|
get adapterTrackModels(): BasicTrack[] {
|
|
1127
|
-
const { rowHeight, MSA,
|
|
1481
|
+
const { rowHeight, MSA, hideGapsEffective, blanks } = self
|
|
1128
1482
|
return (
|
|
1129
|
-
MSA?.tracks
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
}
|
|
1139
|
-
ReactComponent: TextTrack,
|
|
1140
|
-
})) || []
|
|
1483
|
+
MSA?.tracks
|
|
1484
|
+
.filter(t => t.data)
|
|
1485
|
+
.map(t => ({
|
|
1486
|
+
model: {
|
|
1487
|
+
...t,
|
|
1488
|
+
data: hideGapsEffective ? skipBlanks(blanks, t.data!) : t.data,
|
|
1489
|
+
height: rowHeight,
|
|
1490
|
+
} as TextTrackModel,
|
|
1491
|
+
ReactComponent: TextTrack,
|
|
1492
|
+
})) || []
|
|
1141
1493
|
)
|
|
1142
1494
|
},
|
|
1143
1495
|
|
|
@@ -1145,7 +1497,15 @@ function stateModelFactory() {
|
|
|
1145
1497
|
* #getter
|
|
1146
1498
|
*/
|
|
1147
1499
|
get tracks(): BasicTrack[] {
|
|
1148
|
-
|
|
1500
|
+
const conservationTrack: BasicTrack = {
|
|
1501
|
+
model: {
|
|
1502
|
+
id: 'conservation',
|
|
1503
|
+
name: 'Conservation',
|
|
1504
|
+
height: 40,
|
|
1505
|
+
},
|
|
1506
|
+
ReactComponent: ConservationTrack,
|
|
1507
|
+
}
|
|
1508
|
+
return [...this.adapterTrackModels, conservationTrack]
|
|
1149
1509
|
},
|
|
1150
1510
|
|
|
1151
1511
|
/**
|
|
@@ -1374,6 +1734,7 @@ function stateModelFactory() {
|
|
|
1374
1734
|
async exportSVG(opts: {
|
|
1375
1735
|
theme: Theme
|
|
1376
1736
|
includeMinimap?: boolean
|
|
1737
|
+
includeTracks?: boolean
|
|
1377
1738
|
exportType: string
|
|
1378
1739
|
}) {
|
|
1379
1740
|
const { renderToSvg } = await import('./renderToSvg')
|
|
@@ -1556,88 +1917,104 @@ function stateModelFactory() {
|
|
|
1556
1917
|
const snap = result as Omit<typeof result, symbol>
|
|
1557
1918
|
const {
|
|
1558
1919
|
data: { tree, msa, treeMetadata },
|
|
1920
|
+
// Main model properties
|
|
1921
|
+
showDomains,
|
|
1922
|
+
hideGaps,
|
|
1923
|
+
allowedGappyness,
|
|
1924
|
+
contrastLettering,
|
|
1925
|
+
subFeatureRows,
|
|
1926
|
+
drawMsaLetters,
|
|
1927
|
+
height,
|
|
1928
|
+
rowHeight,
|
|
1929
|
+
scrollY,
|
|
1930
|
+
scrollX,
|
|
1931
|
+
colWidth,
|
|
1932
|
+
currentAlignment,
|
|
1933
|
+
collapsed,
|
|
1934
|
+
collapsedLeaves,
|
|
1935
|
+
showOnly,
|
|
1936
|
+
turnedOffTracks,
|
|
1937
|
+
featureFilters,
|
|
1938
|
+
relativeTo,
|
|
1939
|
+
// MSA model properties
|
|
1940
|
+
bgColor,
|
|
1941
|
+
colorSchemeName,
|
|
1942
|
+
// Tree model properties
|
|
1943
|
+
drawLabels,
|
|
1944
|
+
labelsAlignRight,
|
|
1945
|
+
treeAreaWidth,
|
|
1946
|
+
treeWidth,
|
|
1947
|
+
treeWidthMatchesArea,
|
|
1948
|
+
showBranchLen,
|
|
1949
|
+
drawTree,
|
|
1950
|
+
drawNodeBubbles,
|
|
1951
|
+
// Always include
|
|
1559
1952
|
...rest
|
|
1560
1953
|
} = snap
|
|
1561
1954
|
|
|
1562
|
-
// Default values to filter out
|
|
1563
|
-
const defaults = {
|
|
1564
|
-
// Main model defaults
|
|
1565
|
-
showDomains: false,
|
|
1566
|
-
hideGaps: true,
|
|
1567
|
-
allowedGappyness: 100,
|
|
1568
|
-
contrastLettering: true,
|
|
1569
|
-
subFeatureRows: false,
|
|
1570
|
-
drawMsaLetters: true,
|
|
1571
|
-
height: 550,
|
|
1572
|
-
rowHeight: defaultRowHeight,
|
|
1573
|
-
scrollY: 0,
|
|
1574
|
-
scrollX: 0,
|
|
1575
|
-
colWidth: defaultColWidth,
|
|
1576
|
-
currentAlignment: 0,
|
|
1577
|
-
// MSA model defaults
|
|
1578
|
-
bgColor: true,
|
|
1579
|
-
colorSchemeName: 'maeditor',
|
|
1580
|
-
// Tree model defaults
|
|
1581
|
-
drawLabels: true,
|
|
1582
|
-
labelsAlignRight: false,
|
|
1583
|
-
treeAreaWidth: 400,
|
|
1584
|
-
treeWidth: 300,
|
|
1585
|
-
treeWidthMatchesArea: true,
|
|
1586
|
-
showBranchLen: true,
|
|
1587
|
-
drawTree: true,
|
|
1588
|
-
drawNodeBubbles: true,
|
|
1589
|
-
}
|
|
1590
|
-
|
|
1591
|
-
// Properties that should always be included even if they match defaults
|
|
1592
|
-
const alwaysInclude = new Set(['id', 'type', 'relativeTo'])
|
|
1593
|
-
|
|
1594
|
-
// Filter out properties that match default values
|
|
1595
|
-
function filterDefaults(obj: Record<string, any>): Record<string, any> {
|
|
1596
|
-
const filtered: Record<string, any> = {}
|
|
1597
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
1598
|
-
// Always include essential properties
|
|
1599
|
-
if (alwaysInclude.has(key)) {
|
|
1600
|
-
filtered[key] = value
|
|
1601
|
-
continue
|
|
1602
|
-
}
|
|
1603
|
-
|
|
1604
|
-
// Skip if value matches default
|
|
1605
|
-
if (defaults[key as keyof typeof defaults] === value) {
|
|
1606
|
-
continue
|
|
1607
|
-
}
|
|
1608
|
-
|
|
1609
|
-
// Handle nested objects
|
|
1610
|
-
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
1611
|
-
const filteredNested = filterDefaults(value)
|
|
1612
|
-
// Only include nested object if it has non-default properties
|
|
1613
|
-
if (Object.keys(filteredNested).length > 0) {
|
|
1614
|
-
filtered[key] = filteredNested
|
|
1615
|
-
}
|
|
1616
|
-
} else if (Array.isArray(value)) {
|
|
1617
|
-
// Only include arrays that aren't empty
|
|
1618
|
-
if (value.length > 0) {
|
|
1619
|
-
filtered[key] = value
|
|
1620
|
-
}
|
|
1621
|
-
} else {
|
|
1622
|
-
// Include non-default primitives
|
|
1623
|
-
filtered[key] = value
|
|
1624
|
-
}
|
|
1625
|
-
}
|
|
1626
|
-
return filtered
|
|
1627
|
-
}
|
|
1628
|
-
|
|
1629
|
-
const filteredRest = filterDefaults(rest)
|
|
1630
|
-
|
|
1631
1955
|
// remove the MSA/tree data from the tree if the filehandle available in
|
|
1632
1956
|
// which case it can be reloaded on refresh
|
|
1633
1957
|
return {
|
|
1958
|
+
...rest,
|
|
1634
1959
|
data: {
|
|
1635
1960
|
...(result.treeFilehandle ? {} : { tree }),
|
|
1636
1961
|
...(result.msaFilehandle ? {} : { msa }),
|
|
1637
1962
|
...(result.treeMetadataFilehandle ? {} : { treeMetadata }),
|
|
1638
1963
|
},
|
|
1639
|
-
|
|
1640
|
-
|
|
1964
|
+
// Main model - only include non-default values
|
|
1965
|
+
...(showDomains !== defaultShowDomains ? { showDomains } : {}),
|
|
1966
|
+
...(hideGaps !== defaultHideGaps ? { hideGaps } : {}),
|
|
1967
|
+
...(allowedGappyness !== defaultAllowedGappyness
|
|
1968
|
+
? { allowedGappyness }
|
|
1969
|
+
: {}),
|
|
1970
|
+
...(contrastLettering !== defaultContrastLettering
|
|
1971
|
+
? { contrastLettering }
|
|
1972
|
+
: {}),
|
|
1973
|
+
...(subFeatureRows !== defaultSubFeatureRows ? { subFeatureRows } : {}),
|
|
1974
|
+
...(drawMsaLetters !== defaultDrawMsaLetters ? { drawMsaLetters } : {}),
|
|
1975
|
+
...(height !== defaultHeight ? { height } : {}),
|
|
1976
|
+
...(rowHeight !== defaultRowHeight ? { rowHeight } : {}),
|
|
1977
|
+
...(scrollY !== defaultScrollY ? { scrollY } : {}),
|
|
1978
|
+
...(scrollX !== defaultScrollX ? { scrollX } : {}),
|
|
1979
|
+
...(colWidth !== defaultColWidth ? { colWidth } : {}),
|
|
1980
|
+
...(currentAlignment !== defaultCurrentAlignment
|
|
1981
|
+
? { currentAlignment }
|
|
1982
|
+
: {}),
|
|
1983
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
1984
|
+
...(collapsed?.length ? { collapsed } : {}),
|
|
1985
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
1986
|
+
...(collapsedLeaves?.length ? { collapsedLeaves } : {}),
|
|
1987
|
+
...(showOnly !== undefined ? { showOnly } : {}),
|
|
1988
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
1989
|
+
...(turnedOffTracks && Object.keys(turnedOffTracks).length > 0
|
|
1990
|
+
? { turnedOffTracks }
|
|
1991
|
+
: {}),
|
|
1992
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
1993
|
+
...(featureFilters && Object.keys(featureFilters).length > 0
|
|
1994
|
+
? { featureFilters }
|
|
1995
|
+
: {}),
|
|
1996
|
+
...(relativeTo !== undefined ? { relativeTo } : {}),
|
|
1997
|
+
// MSA model - only include non-default values
|
|
1998
|
+
...(bgColor !== defaultBgColor ? { bgColor } : {}),
|
|
1999
|
+
...(colorSchemeName !== defaultColorSchemeName
|
|
2000
|
+
? { colorSchemeName }
|
|
2001
|
+
: {}),
|
|
2002
|
+
// Tree model - only include non-default values
|
|
2003
|
+
...(drawLabels !== defaultDrawLabels ? { drawLabels } : {}),
|
|
2004
|
+
...(labelsAlignRight !== defaultLabelsAlignRight
|
|
2005
|
+
? { labelsAlignRight }
|
|
2006
|
+
: {}),
|
|
2007
|
+
...(treeAreaWidth !== defaultTreeAreaWidth ? { treeAreaWidth } : {}),
|
|
2008
|
+
...(treeWidth !== defaultTreeWidth ? { treeWidth } : {}),
|
|
2009
|
+
...(treeWidthMatchesArea !== defaultTreeWidthMatchesArea
|
|
2010
|
+
? { treeWidthMatchesArea }
|
|
2011
|
+
: {}),
|
|
2012
|
+
...(showBranchLen !== defaultShowBranchLen ? { showBranchLen } : {}),
|
|
2013
|
+
...(drawTree !== defaultDrawTree ? { drawTree } : {}),
|
|
2014
|
+
...(drawNodeBubbles !== defaultDrawNodeBubbles
|
|
2015
|
+
? { drawNodeBubbles }
|
|
2016
|
+
: {}),
|
|
2017
|
+
} as typeof snap
|
|
1641
2018
|
})
|
|
1642
2019
|
}
|
|
1643
2020
|
|