react-msaview 5.4.1 → 5.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/LICENSE +21 -0
- package/bundle/index.js +487 -149
- package/bundle/index.js.map +7 -1
- package/dist/colorSchemes.js +11 -7
- package/dist/colorSchemes.js.map +1 -1
- package/dist/components/ConservationTrack.js +20 -26
- package/dist/components/ConservationTrack.js.map +1 -1
- package/dist/components/MSAViewer.d.ts +13 -1
- package/dist/components/MSAViewer.js +13 -7
- package/dist/components/MSAViewer.js.map +1 -1
- package/dist/components/SequenceTextArea.js +1 -1
- package/dist/components/SequenceTextArea.js.map +1 -1
- package/dist/components/TextTrack.js +20 -27
- package/dist/components/TextTrack.js.map +1 -1
- package/dist/components/Track.js +9 -6
- package/dist/components/Track.js.map +1 -1
- package/dist/components/dialogs/FeatureDialog.js +27 -23
- package/dist/components/dialogs/FeatureDialog.js.map +1 -1
- package/dist/components/dialogs/TrackInfoDialog.js +1 -1
- package/dist/components/dialogs/TrackInfoDialog.js.map +1 -1
- package/dist/components/header/ColorSchemeMenu.js +1 -1
- package/dist/components/header/ColorSchemeMenu.js.map +1 -1
- package/dist/components/header/FileMenu.js +1 -16
- package/dist/components/header/FileMenu.js.map +1 -1
- package/dist/components/header/Header.js +6 -5
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/HeaderInfoArea.js +1 -1
- package/dist/components/header/HeaderInfoArea.js.map +1 -1
- package/dist/components/header/HeaderStatusArea.js +1 -1
- package/dist/components/header/HeaderStatusArea.js.map +1 -1
- package/dist/components/header/MSASettingsMenu.js +1 -1
- package/dist/components/header/MSASettingsMenu.js.map +1 -1
- package/dist/components/header/MultiAlignmentSelector.js +0 -0
- package/dist/components/header/MultiAlignmentSelector.js.map +1 -1
- package/dist/components/header/TreeSettingsMenu.js +1 -1
- package/dist/components/header/TreeSettingsMenu.js.map +1 -1
- package/dist/components/header/ZoomControls.js +14 -12
- package/dist/components/header/ZoomControls.js.map +1 -1
- package/dist/components/header/ZoomMenu.js +1 -10
- package/dist/components/header/ZoomMenu.js.map +1 -1
- package/dist/components/header/settingsMenuItems.js +9 -1
- package/dist/components/header/settingsMenuItems.js.map +1 -1
- package/dist/components/{header/HeaderMenu.d.ts → msa/ColumnStats.d.ts} +3 -3
- package/dist/components/msa/ColumnStats.js +42 -0
- package/dist/components/msa/ColumnStats.js.map +1 -0
- package/dist/components/msa/DomainLegend.d.ts +6 -0
- package/dist/components/msa/DomainLegend.js +41 -0
- package/dist/components/msa/DomainLegend.js.map +1 -0
- package/dist/components/msa/MSACanvasBlock.js +41 -29
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/MSAMouseoverCanvas.js +6 -12
- package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
- package/dist/components/msa/MSAPanel.js +3 -1
- package/dist/components/msa/MSAPanel.js.map +1 -1
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js +60 -14
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
- package/dist/components/msa/renderMSABlock.js +3 -3
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +33 -6
- package/dist/components/msa/renderMSAMouseover.js.map +1 -1
- package/dist/components/overlayColors.d.ts +3 -0
- package/dist/components/overlayColors.js +11 -0
- package/dist/components/overlayColors.js.map +1 -0
- package/dist/components/renderCtx.d.ts +1 -1
- package/dist/components/tracks/renderTracksSvg.d.ts +6 -3
- package/dist/components/tracks/renderTracksSvg.js +20 -11
- package/dist/components/tracks/renderTracksSvg.js.map +1 -1
- package/dist/components/tree/TreeCanvas.js +31 -38
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +12 -47
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/clickMap.d.ts +24 -0
- package/dist/components/tree/clickMap.js +33 -0
- package/dist/components/tree/clickMap.js.map +1 -0
- package/dist/components/tree/renderTreeCanvas.d.ts +1 -21
- package/dist/components/tree/renderTreeCanvas.js +8 -7
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +17 -0
- package/dist/constants.js.map +1 -1
- package/dist/createPaletteMap.d.ts +5 -2
- package/dist/createPaletteMap.js +15 -5
- package/dist/createPaletteMap.js.map +1 -1
- package/dist/fetchUtils.d.ts +2 -1
- package/dist/fetchUtils.js +3 -2
- package/dist/fetchUtils.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/launchInterProScan.d.ts +2 -0
- package/dist/launchInterProScan.js.map +1 -1
- package/dist/model/msaModel.d.ts +25 -2
- package/dist/model/msaModel.js +15 -3
- package/dist/model/msaModel.js.map +1 -1
- package/dist/model/treeModel.d.ts +46 -5
- package/dist/model/treeModel.js +20 -7
- package/dist/model/treeModel.js.map +1 -1
- package/dist/model.d.ts +641 -133
- package/dist/model.js +191 -77
- package/dist/model.js.map +1 -1
- package/dist/propertyConservation.d.ts +7 -0
- package/dist/propertyConservation.js +66 -0
- package/dist/propertyConservation.js.map +1 -0
- package/dist/renderToSvg.js +104 -74
- package/dist/renderToSvg.js.map +1 -1
- package/dist/types.d.ts +4 -1
- package/dist/{webpack.js → umd.js} +1 -1
- package/dist/umd.js.map +1 -0
- package/dist/useCanvasAutorun.d.ts +2 -0
- package/dist/useCanvasAutorun.js +23 -0
- package/dist/useCanvasAutorun.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -8
- package/src/colorSchemes.docs.test.ts +55 -0
- package/src/colorSchemes.ts +12 -7
- package/src/columnStats.test.ts +94 -0
- package/src/components/ConservationTrack.tsx +17 -15
- package/src/components/MSAViewer.tsx +42 -18
- package/src/components/SequenceTextArea.tsx +1 -1
- package/src/components/TextTrack.tsx +7 -11
- package/src/components/Track.tsx +9 -6
- package/src/components/dialogs/FeatureDialog.tsx +62 -48
- package/src/components/dialogs/TrackInfoDialog.tsx +1 -1
- package/src/components/header/ColorSchemeMenu.tsx +1 -0
- package/src/components/header/FileMenu.tsx +1 -15
- package/src/components/header/Header.tsx +11 -8
- package/src/components/header/HeaderInfoArea.tsx +1 -1
- package/src/components/header/HeaderStatusArea.tsx +1 -1
- package/src/components/header/MSASettingsMenu.tsx +1 -0
- package/src/components/header/MultiAlignmentSelector.tsx +0 -0
- package/src/components/header/TreeSettingsMenu.tsx +1 -0
- package/src/components/header/ZoomControls.tsx +24 -17
- package/src/components/header/ZoomMenu.tsx +1 -10
- package/src/components/header/settingsMenuItems.ts +9 -1
- package/src/components/msa/ColumnStats.tsx +54 -0
- package/src/components/msa/DomainLegend.tsx +78 -0
- package/src/components/msa/MSACanvasBlock.tsx +46 -25
- package/src/components/msa/MSAMouseoverCanvas.tsx +10 -13
- package/src/components/msa/MSAPanel.tsx +2 -0
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +75 -17
- package/src/components/msa/renderMSABlock.ts +3 -3
- package/src/components/msa/renderMSAMouseover.ts +34 -6
- package/src/components/overlayColors.ts +12 -0
- package/src/components/renderCtx.ts +1 -0
- package/src/components/tracks/renderTracksSvg.ts +25 -11
- package/src/components/tree/TreeCanvas.tsx +47 -54
- package/src/components/tree/TreeCanvasBlock.tsx +10 -68
- package/src/components/tree/clickMap.ts +55 -0
- package/src/components/tree/renderTreeCanvas.ts +10 -34
- package/src/constants.ts +19 -0
- package/src/createPaletteMap.ts +18 -5
- package/src/fetchUtils.ts +6 -3
- package/src/index.ts +1 -1
- package/src/launchInterProScan.ts +3 -1
- package/src/model/msaModel.ts +21 -3
- package/src/model/treeModel.ts +21 -7
- package/src/model.ts +221 -119
- package/src/propertyConservation.test.ts +47 -0
- package/src/propertyConservation.ts +73 -0
- package/src/renderDomainLegendSvg.test.tsx +134 -0
- package/src/renderToSvg.tsx +210 -147
- package/src/segmentOverlay.test.ts +70 -0
- package/src/stripDefaultSnapshot.test.ts +54 -0
- package/src/types.ts +7 -1
- package/src/useCanvasAutorun.ts +28 -0
- package/src/version.ts +1 -1
- package/bundle/index.js.LICENSE.txt +0 -100
- package/dist/components/dialogs/SettingsDialog.d.ts +0 -7
- package/dist/components/dialogs/SettingsDialog.js +0 -100
- package/dist/components/dialogs/SettingsDialog.js.map +0 -1
- package/dist/components/header/HeaderMenu.js +0 -27
- package/dist/components/header/HeaderMenu.js.map +0 -1
- package/dist/components/util.d.ts +0 -1
- package/dist/components/util.js +0 -7
- package/dist/components/util.js.map +0 -1
- package/dist/webpack.js.map +0 -1
- package/src/components/dialogs/SettingsDialog.tsx +0 -234
- package/src/components/header/HeaderMenu.tsx +0 -38
- package/src/components/util.ts +0 -9
- /package/dist/{webpack.d.ts → umd.d.ts} +0 -0
- /package/src/{webpack.ts → umd.ts} +0 -0
package/src/model/treeModel.ts
CHANGED
|
@@ -19,25 +19,25 @@ export function TreeModelF() {
|
|
|
19
19
|
/**
|
|
20
20
|
* #property
|
|
21
21
|
*/
|
|
22
|
-
drawLabels: defaultDrawLabels,
|
|
22
|
+
drawLabels: types.stripDefault(types.boolean, defaultDrawLabels),
|
|
23
23
|
/**
|
|
24
24
|
* #property
|
|
25
25
|
* right-align the labels
|
|
26
26
|
*/
|
|
27
|
-
labelsAlignRight: defaultLabelsAlignRight,
|
|
27
|
+
labelsAlignRight: types.stripDefault(types.boolean, defaultLabelsAlignRight),
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* #property
|
|
31
31
|
* width of the area the tree is drawn in, px
|
|
32
32
|
*/
|
|
33
|
-
treeAreaWidth: types.
|
|
33
|
+
treeAreaWidth: types.stripDefault(types.number, defaultTreeAreaWidth),
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* #property
|
|
37
37
|
* width of the tree within the treeArea, px. automatically synced to
|
|
38
38
|
* fit within treeAreaWidth
|
|
39
39
|
*/
|
|
40
|
-
treeWidth: types.
|
|
40
|
+
treeWidth: types.stripDefault(types.number, defaultTreeWidth),
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* #property
|
|
@@ -45,19 +45,27 @@ export function TreeModelF() {
|
|
|
45
45
|
* lengths. if false, the layout is a "cladogram" that does not take into
|
|
46
46
|
* account evolutionary distances
|
|
47
47
|
*/
|
|
48
|
-
showBranchLen: defaultShowBranchLen,
|
|
48
|
+
showBranchLen: types.stripDefault(types.boolean, defaultShowBranchLen),
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* #property
|
|
52
52
|
* draw tree, boolean
|
|
53
53
|
*/
|
|
54
|
-
drawTree: defaultDrawTree,
|
|
54
|
+
drawTree: types.stripDefault(types.boolean, defaultDrawTree),
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* #property
|
|
58
58
|
* draw clickable node bubbles on the tree
|
|
59
59
|
*/
|
|
60
|
-
drawNodeBubbles: defaultDrawNodeBubbles,
|
|
60
|
+
drawNodeBubbles: types.stripDefault(types.boolean, defaultDrawNodeBubbles),
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* #property
|
|
64
|
+
* auto-size treeAreaWidth to fit the row labels (plus the tree, if drawn)
|
|
65
|
+
* instead of using a fixed width. useful when there is no tree, so the
|
|
66
|
+
* label gutter isn't padded out to the default 400px
|
|
67
|
+
*/
|
|
68
|
+
autoTreeAreaWidth: types.stripDefault(types.boolean, false),
|
|
61
69
|
})
|
|
62
70
|
.actions(self => ({
|
|
63
71
|
/**
|
|
@@ -87,6 +95,12 @@ export function TreeModelF() {
|
|
|
87
95
|
setDrawTree(arg: boolean) {
|
|
88
96
|
self.drawTree = arg
|
|
89
97
|
},
|
|
98
|
+
/**
|
|
99
|
+
* #action
|
|
100
|
+
*/
|
|
101
|
+
setAutoTreeAreaWidth(arg: boolean) {
|
|
102
|
+
self.autoTreeAreaWidth = arg
|
|
103
|
+
},
|
|
90
104
|
|
|
91
105
|
/**
|
|
92
106
|
* #action
|
package/src/model.ts
CHANGED
|
@@ -20,29 +20,23 @@ import ConservationTrack from './components/ConservationTrack.tsx'
|
|
|
20
20
|
import TextTrack from './components/TextTrack.tsx'
|
|
21
21
|
import {
|
|
22
22
|
defaultAllowedGappyness,
|
|
23
|
-
defaultBgColor,
|
|
24
23
|
defaultColWidth,
|
|
25
24
|
defaultColorSchemeName,
|
|
26
25
|
defaultCurrentAlignment,
|
|
27
|
-
defaultDrawLabels,
|
|
28
26
|
defaultDrawMsaLetters,
|
|
29
|
-
defaultDrawNodeBubbles,
|
|
30
|
-
defaultDrawTree,
|
|
31
27
|
defaultHeight,
|
|
32
28
|
defaultHideGaps,
|
|
33
|
-
defaultLabelsAlignRight,
|
|
34
29
|
defaultRowHeight,
|
|
35
30
|
defaultScrollX,
|
|
36
31
|
defaultScrollY,
|
|
37
32
|
defaultScrollZoom,
|
|
38
|
-
defaultShowBranchLen,
|
|
39
33
|
defaultShowDomains,
|
|
40
34
|
defaultSubFeatureRows,
|
|
41
|
-
defaultTreeAreaWidth,
|
|
42
|
-
defaultTreeWidth,
|
|
43
35
|
maxCellSize,
|
|
44
36
|
minColWidth,
|
|
45
37
|
minRowHeight,
|
|
38
|
+
segmentFeatureTypes,
|
|
39
|
+
segmentShades,
|
|
46
40
|
} from './constants.ts'
|
|
47
41
|
import { createPaletteMap } from './createPaletteMap.ts'
|
|
48
42
|
import { fetchTextWithProgress, isAbortError } from './fetchUtils.ts'
|
|
@@ -70,6 +64,7 @@ import { MSAModelF } from './model/msaModel.ts'
|
|
|
70
64
|
import { TreeModelF } from './model/treeModel.ts'
|
|
71
65
|
import { calculateNeighborJoiningTree } from './neighborJoining.ts'
|
|
72
66
|
import { parseAsn1 } from './parseAsn1.ts'
|
|
67
|
+
import { calculatePropertyConservation } from './propertyConservation.ts'
|
|
73
68
|
import {
|
|
74
69
|
globalColToVisibleCol,
|
|
75
70
|
visibleColToGlobalCol,
|
|
@@ -95,6 +90,14 @@ function parseTreeText(text: string) {
|
|
|
95
90
|
|
|
96
91
|
/**
|
|
97
92
|
* #stateModel MsaView
|
|
93
|
+
*
|
|
94
|
+
* The main MSAView state model. Holds the loaded alignment, tree, and optional
|
|
95
|
+
* InterProScan domain annotations, plus all display state (color scheme, zoom,
|
|
96
|
+
* scroll, collapsed clades). It composes in members from `DialogQueueSessionMixin`,
|
|
97
|
+
* `Tree`, and `MSAModel` (see Inherited members below). Data is loaded reactively
|
|
98
|
+
* from the `msaFilehandle` / `treeFilehandle` / `gffFilehandle` properties, or set
|
|
99
|
+
* directly with `setData`. Most state is persisted into the shareable URL.
|
|
100
|
+
*
|
|
98
101
|
* #example
|
|
99
102
|
* ```js
|
|
100
103
|
* import { MSAModelF } from 'react-msaview'
|
|
@@ -121,19 +124,22 @@ function stateModelFactory() {
|
|
|
121
124
|
/**
|
|
122
125
|
* #property
|
|
123
126
|
*/
|
|
124
|
-
showDomains: defaultShowDomains,
|
|
127
|
+
showDomains: types.stripDefault(types.boolean, defaultShowDomains),
|
|
125
128
|
/**
|
|
126
129
|
* #property
|
|
127
130
|
*/
|
|
128
|
-
hideGaps: defaultHideGaps,
|
|
131
|
+
hideGaps: types.stripDefault(types.boolean, defaultHideGaps),
|
|
129
132
|
/**
|
|
130
133
|
* #property
|
|
131
134
|
*/
|
|
132
|
-
allowedGappyness:
|
|
135
|
+
allowedGappyness: types.stripDefault(
|
|
136
|
+
types.number,
|
|
137
|
+
defaultAllowedGappyness,
|
|
138
|
+
),
|
|
133
139
|
/**
|
|
134
140
|
* #property
|
|
135
141
|
*/
|
|
136
|
-
subFeatureRows: defaultSubFeatureRows,
|
|
142
|
+
subFeatureRows: types.stripDefault(types.boolean, defaultSubFeatureRows),
|
|
137
143
|
|
|
138
144
|
/**
|
|
139
145
|
* #property
|
|
@@ -144,43 +150,43 @@ function stateModelFactory() {
|
|
|
144
150
|
/**
|
|
145
151
|
* #property
|
|
146
152
|
*/
|
|
147
|
-
drawMsaLetters: defaultDrawMsaLetters,
|
|
153
|
+
drawMsaLetters: types.stripDefault(types.boolean, defaultDrawMsaLetters),
|
|
148
154
|
|
|
149
155
|
/**
|
|
150
156
|
* #property
|
|
151
157
|
* zoom in/out on plain mouse-wheel without holding ctrl
|
|
152
158
|
*/
|
|
153
|
-
scrollZoom: defaultScrollZoom,
|
|
159
|
+
scrollZoom: types.stripDefault(types.boolean, defaultScrollZoom),
|
|
154
160
|
|
|
155
161
|
/**
|
|
156
162
|
* #property
|
|
157
163
|
* height of the div containing the view, px
|
|
158
164
|
*/
|
|
159
|
-
height: types.
|
|
165
|
+
height: types.stripDefault(types.number, defaultHeight),
|
|
160
166
|
|
|
161
167
|
/**
|
|
162
168
|
* #property
|
|
163
169
|
* height of each row, px
|
|
164
170
|
*/
|
|
165
|
-
rowHeight: defaultRowHeight,
|
|
171
|
+
rowHeight: types.stripDefault(types.number, defaultRowHeight),
|
|
166
172
|
|
|
167
173
|
/**
|
|
168
174
|
* #property
|
|
169
175
|
* scroll position, Y-offset, px
|
|
170
176
|
*/
|
|
171
|
-
scrollY: defaultScrollY,
|
|
177
|
+
scrollY: types.stripDefault(types.number, defaultScrollY),
|
|
172
178
|
|
|
173
179
|
/**
|
|
174
180
|
* #property
|
|
175
181
|
* scroll position, X-offset, px
|
|
176
182
|
*/
|
|
177
|
-
scrollX: defaultScrollX,
|
|
183
|
+
scrollX: types.stripDefault(types.number, defaultScrollX),
|
|
178
184
|
|
|
179
185
|
/**
|
|
180
186
|
* #property
|
|
181
187
|
* width of columns, px
|
|
182
188
|
*/
|
|
183
|
-
colWidth: defaultColWidth,
|
|
189
|
+
colWidth: types.stripDefault(types.number, defaultColWidth),
|
|
184
190
|
|
|
185
191
|
/**
|
|
186
192
|
* #property
|
|
@@ -210,14 +216,17 @@ function stateModelFactory() {
|
|
|
210
216
|
/**
|
|
211
217
|
* #property
|
|
212
218
|
*/
|
|
213
|
-
currentAlignment:
|
|
219
|
+
currentAlignment: types.stripDefault(
|
|
220
|
+
types.number,
|
|
221
|
+
defaultCurrentAlignment,
|
|
222
|
+
),
|
|
214
223
|
|
|
215
224
|
/**
|
|
216
225
|
* #property
|
|
217
226
|
* array of tree parent nodes that are 'collapsed' (all children are
|
|
218
227
|
* hidden)
|
|
219
228
|
*/
|
|
220
|
-
collapsed: types.array(types.string),
|
|
229
|
+
collapsed: types.stripDefault(types.array(types.string), []),
|
|
221
230
|
|
|
222
231
|
/**
|
|
223
232
|
* #property
|
|
@@ -228,7 +237,7 @@ function stateModelFactory() {
|
|
|
228
237
|
* #property
|
|
229
238
|
* turned off tracks
|
|
230
239
|
*/
|
|
231
|
-
turnedOffTracks: types.map(types.boolean),
|
|
240
|
+
turnedOffTracks: types.stripDefault(types.map(types.boolean), {}),
|
|
232
241
|
|
|
233
242
|
/**
|
|
234
243
|
* #property
|
|
@@ -244,7 +253,7 @@ function stateModelFactory() {
|
|
|
244
253
|
/**
|
|
245
254
|
* #property
|
|
246
255
|
*/
|
|
247
|
-
featureFilters: types.map(types.boolean),
|
|
256
|
+
featureFilters: types.stripDefault(types.map(types.boolean), {}),
|
|
248
257
|
/**
|
|
249
258
|
* #property
|
|
250
259
|
*/
|
|
@@ -269,8 +278,7 @@ function stateModelFactory() {
|
|
|
269
278
|
* #volatile
|
|
270
279
|
*/
|
|
271
280
|
status: undefined as
|
|
272
|
-
|
|
273
|
-
| undefined,
|
|
281
|
+
{ msg: string; url?: string; onCancel?: () => void } | undefined,
|
|
274
282
|
/**
|
|
275
283
|
* #volatile
|
|
276
284
|
* high resolution scale factor, helps make canvas look better on hi-dpi
|
|
@@ -333,8 +341,7 @@ function stateModelFactory() {
|
|
|
333
341
|
* the currently hovered tree node ID and its descendant leaf names
|
|
334
342
|
*/
|
|
335
343
|
hoveredTreeNode: undefined as
|
|
336
|
-
|
|
337
|
-
| undefined,
|
|
344
|
+
{ nodeId: string; descendantNames: string[] } | undefined,
|
|
338
345
|
|
|
339
346
|
/**
|
|
340
347
|
* #volatile
|
|
@@ -367,18 +374,20 @@ function stateModelFactory() {
|
|
|
367
374
|
* #volatile
|
|
368
375
|
*/
|
|
369
376
|
interProAnnotations: undefined as
|
|
370
|
-
|
|
|
371
|
-
| Record<string, InterProScanResults>,
|
|
377
|
+
undefined | Record<string, InterProScanResults>,
|
|
372
378
|
}))
|
|
373
379
|
.actions(self => ({
|
|
374
380
|
/**
|
|
375
381
|
* #action
|
|
382
|
+
* draw the alignment with positions numbered relative to the given row's
|
|
383
|
+
* sequence (its node id), instead of in raw MSA-column coordinates
|
|
376
384
|
*/
|
|
377
385
|
drawRelativeTo(id: string | undefined) {
|
|
378
386
|
self.relativeTo = id
|
|
379
387
|
},
|
|
380
388
|
/**
|
|
381
389
|
* #action
|
|
390
|
+
* hide columns that are entirely (or mostly, see allowedGappyness) gaps
|
|
382
391
|
*/
|
|
383
392
|
setHideGaps(arg: boolean) {
|
|
384
393
|
self.hideGaps = arg
|
|
@@ -457,6 +466,7 @@ function stateModelFactory() {
|
|
|
457
466
|
},
|
|
458
467
|
/**
|
|
459
468
|
* #action
|
|
469
|
+
* toggle the InterProScan protein-domain overlay on the alignment
|
|
460
470
|
*/
|
|
461
471
|
setShowDomains(arg: boolean) {
|
|
462
472
|
self.showDomains = arg
|
|
@@ -510,6 +520,7 @@ function stateModelFactory() {
|
|
|
510
520
|
|
|
511
521
|
/**
|
|
512
522
|
* #action
|
|
523
|
+
* collapse or un-collapse the subtree rooted at the given tree node id
|
|
513
524
|
*/
|
|
514
525
|
toggleCollapsed(node: string) {
|
|
515
526
|
if (self.collapsed.includes(node)) {
|
|
@@ -521,6 +532,8 @@ function stateModelFactory() {
|
|
|
521
532
|
|
|
522
533
|
/**
|
|
523
534
|
* #action
|
|
535
|
+
* show only the subtree rooted at the given node id (pass undefined to
|
|
536
|
+
* show the whole tree again)
|
|
524
537
|
*/
|
|
525
538
|
setShowOnly(node?: string) {
|
|
526
539
|
self.showOnly = node
|
|
@@ -528,6 +541,8 @@ function stateModelFactory() {
|
|
|
528
541
|
|
|
529
542
|
/**
|
|
530
543
|
* #action
|
|
544
|
+
* set the alignment/tree/metadata/domain data directly from strings,
|
|
545
|
+
* bypassing the filehandle loaders
|
|
531
546
|
*/
|
|
532
547
|
setData(data: {
|
|
533
548
|
msa?: string
|
|
@@ -1025,6 +1040,17 @@ function stateModelFactory() {
|
|
|
1025
1040
|
return conservation * (1 - gapFraction)
|
|
1026
1041
|
})
|
|
1027
1042
|
},
|
|
1043
|
+
/**
|
|
1044
|
+
* #getter
|
|
1045
|
+
* Per-column conservation of physicochemical property class (amino acids
|
|
1046
|
+
* only). Surfaces conservative-substitution sites that identity-based
|
|
1047
|
+
* conservation misses. Empty for nucleotide alignments.
|
|
1048
|
+
*/
|
|
1049
|
+
get propertyConservation() {
|
|
1050
|
+
return this.sequenceType === 'amino'
|
|
1051
|
+
? calculatePropertyConservation(this.colStats, this.colStatsSums)
|
|
1052
|
+
: []
|
|
1053
|
+
},
|
|
1028
1054
|
/**
|
|
1029
1055
|
* #getter
|
|
1030
1056
|
* generates a new tree that is clustered with x,y positions
|
|
@@ -1232,6 +1258,7 @@ function stateModelFactory() {
|
|
|
1232
1258
|
|
|
1233
1259
|
/**
|
|
1234
1260
|
* #action
|
|
1261
|
+
* restore the default column width and row height
|
|
1235
1262
|
*/
|
|
1236
1263
|
resetZoom() {
|
|
1237
1264
|
self.setColWidth(defaultColWidth)
|
|
@@ -1446,10 +1473,24 @@ function stateModelFactory() {
|
|
|
1446
1473
|
id: 'conservation',
|
|
1447
1474
|
name: 'Conservation',
|
|
1448
1475
|
height: self.conservationTrackHeight,
|
|
1476
|
+
barColor: 'gray',
|
|
1449
1477
|
},
|
|
1450
1478
|
ReactComponent: ConservationTrack,
|
|
1451
1479
|
}
|
|
1452
|
-
|
|
1480
|
+
const propertyConservationTrack: BasicTrack = {
|
|
1481
|
+
model: {
|
|
1482
|
+
id: 'property-conservation',
|
|
1483
|
+
name: 'Property conservation',
|
|
1484
|
+
height: self.conservationTrackHeight,
|
|
1485
|
+
barColor: '#6a51a3',
|
|
1486
|
+
},
|
|
1487
|
+
ReactComponent: ConservationTrack,
|
|
1488
|
+
}
|
|
1489
|
+
return [
|
|
1490
|
+
...this.adapterTrackModels,
|
|
1491
|
+
conservationTrack,
|
|
1492
|
+
...(self.sequenceType === 'amino' ? [propertyConservationTrack] : []),
|
|
1493
|
+
]
|
|
1453
1494
|
},
|
|
1454
1495
|
|
|
1455
1496
|
/**
|
|
@@ -1592,13 +1633,15 @@ function stateModelFactory() {
|
|
|
1592
1633
|
val.matches.flatMap(({ signature, locations }) => {
|
|
1593
1634
|
const { entry } = signature
|
|
1594
1635
|
return entry
|
|
1595
|
-
? locations.map(({ start, end }) => ({
|
|
1636
|
+
? locations.map(({ start, end, strand }) => ({
|
|
1596
1637
|
id,
|
|
1597
1638
|
name: entry.name,
|
|
1598
1639
|
accession: entry.accession,
|
|
1599
1640
|
description: entry.description,
|
|
1641
|
+
featureType: entry.featureType,
|
|
1600
1642
|
start,
|
|
1601
1643
|
end,
|
|
1644
|
+
strand,
|
|
1602
1645
|
}))
|
|
1603
1646
|
: []
|
|
1604
1647
|
}),
|
|
@@ -1630,8 +1673,38 @@ function stateModelFactory() {
|
|
|
1630
1673
|
get verticalScrollbarWidth() {
|
|
1631
1674
|
return self.showVerticalScrollbar ? 20 : 0
|
|
1632
1675
|
},
|
|
1676
|
+
/**
|
|
1677
|
+
* #getter
|
|
1678
|
+
* ordinal segment types (exons etc.), ordered by sequence position so
|
|
1679
|
+
* exon-1..exon-14 read left-to-right; colored by alternating shade and
|
|
1680
|
+
* labeled by number rather than each getting a distinct hue + legend row
|
|
1681
|
+
*/
|
|
1682
|
+
get segmentDomainTypes() {
|
|
1683
|
+
return [...self.tidyInterProAnnotationTypes.values()]
|
|
1684
|
+
.filter(d => segmentFeatureTypes.has(d.featureType ?? ''))
|
|
1685
|
+
.toSorted((a, b) => a.start - b.start)
|
|
1686
|
+
},
|
|
1687
|
+
/**
|
|
1688
|
+
* #getter
|
|
1689
|
+
* categorical feature types (InterPro domains and the like) that each get
|
|
1690
|
+
* their own color and a legend entry
|
|
1691
|
+
*/
|
|
1692
|
+
get categoricalDomainTypes() {
|
|
1693
|
+
return [...self.tidyInterProAnnotationTypes.values()].filter(
|
|
1694
|
+
d => !segmentFeatureTypes.has(d.featureType ?? ''),
|
|
1695
|
+
)
|
|
1696
|
+
},
|
|
1633
1697
|
get fillPalette() {
|
|
1634
|
-
|
|
1698
|
+
const segments = Object.fromEntries(
|
|
1699
|
+
this.segmentDomainTypes.map((d, i) => [
|
|
1700
|
+
d.accession,
|
|
1701
|
+
segmentShades[i % segmentShades.length]!,
|
|
1702
|
+
]),
|
|
1703
|
+
)
|
|
1704
|
+
const categorical = createPaletteMap(
|
|
1705
|
+
this.categoricalDomainTypes.map(d => d.accession),
|
|
1706
|
+
)
|
|
1707
|
+
return { ...segments, ...categorical }
|
|
1635
1708
|
},
|
|
1636
1709
|
get strokePalette() {
|
|
1637
1710
|
return transform(this.fillPalette, ([key, val]) => [
|
|
@@ -1640,6 +1713,92 @@ function stateModelFactory() {
|
|
|
1640
1713
|
])
|
|
1641
1714
|
},
|
|
1642
1715
|
|
|
1716
|
+
/**
|
|
1717
|
+
* #getter
|
|
1718
|
+
* accession -> number drawn on each segment band: the trailing number of
|
|
1719
|
+
* the feature name ("exon-3" -> "3"), else its 1-based position
|
|
1720
|
+
*/
|
|
1721
|
+
get segmentLabels() {
|
|
1722
|
+
return new Map(
|
|
1723
|
+
this.segmentDomainTypes.map((d, i) => {
|
|
1724
|
+
const m = /(\d+)\s*$/.exec(d.name)
|
|
1725
|
+
return [d.accession, m ? m[1]! : `${i + 1}`]
|
|
1726
|
+
}),
|
|
1727
|
+
)
|
|
1728
|
+
},
|
|
1729
|
+
|
|
1730
|
+
/**
|
|
1731
|
+
* #getter
|
|
1732
|
+
* the domain types currently drawn on the alignment (filtered-on), shared
|
|
1733
|
+
* by the on-screen legend and the SVG export legend. Ordinal segments
|
|
1734
|
+
* (exons) are excluded — they read as a numbered gene model, not a color
|
|
1735
|
+
* key — so this is the categorical types ordered by sequence position
|
|
1736
|
+
*/
|
|
1737
|
+
get visibleDomainTypes() {
|
|
1738
|
+
return this.categoricalDomainTypes
|
|
1739
|
+
.filter(d => self.featureFilters.get(d.accession))
|
|
1740
|
+
.toSorted((a, b) => a.start - b.start)
|
|
1741
|
+
},
|
|
1742
|
+
|
|
1743
|
+
/**
|
|
1744
|
+
* #getter
|
|
1745
|
+
* domain annotations under the mouse, hit-tested against the exact visible
|
|
1746
|
+
* column span each box is drawn at (so it matches the overlay across gaps)
|
|
1747
|
+
*/
|
|
1748
|
+
get mouseOverDomains() {
|
|
1749
|
+
const { mouseCol } = self
|
|
1750
|
+
const name = self.mouseOverRowName
|
|
1751
|
+
if (name !== undefined && mouseCol !== undefined) {
|
|
1752
|
+
const entry = self.tidyFilteredGatheredInterProAnnotations[name] ?? []
|
|
1753
|
+
return entry.filter(d => {
|
|
1754
|
+
const m1 = self.seqPosToVisibleCol(name, d.start - 1)
|
|
1755
|
+
const m2 = self.seqPosToVisibleCol(name, d.end)
|
|
1756
|
+
return (
|
|
1757
|
+
m1 !== undefined &&
|
|
1758
|
+
m2 !== undefined &&
|
|
1759
|
+
mouseCol >= m1 &&
|
|
1760
|
+
mouseCol < m2
|
|
1761
|
+
)
|
|
1762
|
+
})
|
|
1763
|
+
}
|
|
1764
|
+
return []
|
|
1765
|
+
},
|
|
1766
|
+
|
|
1767
|
+
/**
|
|
1768
|
+
* #getter
|
|
1769
|
+
* per-column summary statistics for the hovered column: consensus residue
|
|
1770
|
+
* and its identity fraction, conservation score, gap fraction, and the
|
|
1771
|
+
* sorted non-gap residue distribution. undefined when nothing is hovered.
|
|
1772
|
+
*/
|
|
1773
|
+
get mouseOverColumnStats() {
|
|
1774
|
+
const { mouseCol } = self
|
|
1775
|
+
if (mouseCol === undefined) {
|
|
1776
|
+
return undefined
|
|
1777
|
+
}
|
|
1778
|
+
const stats = self.colStats[mouseCol]
|
|
1779
|
+
const total = self.colStatsSums[mouseCol]
|
|
1780
|
+
if (!stats || !total) {
|
|
1781
|
+
return undefined
|
|
1782
|
+
}
|
|
1783
|
+
const gaps = (stats['-'] ?? 0) + (stats['.'] ?? 0)
|
|
1784
|
+
const distribution = Object.entries(stats)
|
|
1785
|
+
.filter(([letter]) => letter !== '-' && letter !== '.')
|
|
1786
|
+
.sort((a, b) => b[1] - a[1])
|
|
1787
|
+
const consensus = distribution[0]
|
|
1788
|
+
return {
|
|
1789
|
+
col: mouseCol,
|
|
1790
|
+
total,
|
|
1791
|
+
gaps,
|
|
1792
|
+
gapFraction: gaps / total,
|
|
1793
|
+
conservation: self.conservation[mouseCol] ?? 0,
|
|
1794
|
+
propertyConservation: self.propertyConservation[mouseCol],
|
|
1795
|
+
consensusLetter: consensus?.[0] ?? '',
|
|
1796
|
+
consensusCount: consensus?.[1] ?? 0,
|
|
1797
|
+
consensusFraction: consensus ? consensus[1] / total : 0,
|
|
1798
|
+
distribution,
|
|
1799
|
+
}
|
|
1800
|
+
},
|
|
1801
|
+
|
|
1643
1802
|
/**
|
|
1644
1803
|
* #method
|
|
1645
1804
|
*/
|
|
@@ -1943,6 +2102,23 @@ function stateModelFactory() {
|
|
|
1943
2102
|
}),
|
|
1944
2103
|
)
|
|
1945
2104
|
|
|
2105
|
+
// autorun: when autoTreeAreaWidth is set and no tree is drawn, shrink the
|
|
2106
|
+
// tree area to fit the row labels rather than padding it to the fixed
|
|
2107
|
+
// default. Gated on noTree/!drawTree so it never fights the treeWidth sync
|
|
2108
|
+
// below (treeAreaWidth here depends only on labelsWidth, not treeWidth).
|
|
2109
|
+
addDisposer(
|
|
2110
|
+
self,
|
|
2111
|
+
autorun(() => {
|
|
2112
|
+
if (
|
|
2113
|
+
self.autoTreeAreaWidth &&
|
|
2114
|
+
(self.noTree || !self.drawTree) &&
|
|
2115
|
+
self.labelsWidth
|
|
2116
|
+
) {
|
|
2117
|
+
self.setTreeAreaWidth(self.labelsWidth + self.marginLeft + 12)
|
|
2118
|
+
}
|
|
2119
|
+
}),
|
|
2120
|
+
)
|
|
2121
|
+
|
|
1946
2122
|
// autorun synchronizes treeWidth with treeAreaWidth
|
|
1947
2123
|
addDisposer(
|
|
1948
2124
|
self,
|
|
@@ -1957,93 +2133,19 @@ function stateModelFactory() {
|
|
|
1957
2133
|
)
|
|
1958
2134
|
},
|
|
1959
2135
|
}))
|
|
1960
|
-
.postProcessSnapshot(
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
scrollX,
|
|
1974
|
-
colWidth,
|
|
1975
|
-
currentAlignment,
|
|
1976
|
-
collapsed,
|
|
1977
|
-
showOnly,
|
|
1978
|
-
turnedOffTracks,
|
|
1979
|
-
featureFilters,
|
|
1980
|
-
relativeTo,
|
|
1981
|
-
// MSA model properties
|
|
1982
|
-
bgColor,
|
|
1983
|
-
colorSchemeName,
|
|
1984
|
-
// Tree model properties
|
|
1985
|
-
drawLabels,
|
|
1986
|
-
labelsAlignRight,
|
|
1987
|
-
treeAreaWidth,
|
|
1988
|
-
treeWidth,
|
|
1989
|
-
showBranchLen,
|
|
1990
|
-
drawTree,
|
|
1991
|
-
drawNodeBubbles,
|
|
1992
|
-
// Always include
|
|
1993
|
-
...rest
|
|
1994
|
-
} = snap
|
|
1995
|
-
|
|
1996
|
-
const defaults: Record<string, unknown> = {
|
|
1997
|
-
showDomains: defaultShowDomains,
|
|
1998
|
-
hideGaps: defaultHideGaps,
|
|
1999
|
-
allowedGappyness: defaultAllowedGappyness,
|
|
2000
|
-
subFeatureRows: defaultSubFeatureRows,
|
|
2001
|
-
drawMsaLetters: defaultDrawMsaLetters,
|
|
2002
|
-
height: defaultHeight,
|
|
2003
|
-
rowHeight: defaultRowHeight,
|
|
2004
|
-
scrollY: defaultScrollY,
|
|
2005
|
-
scrollX: defaultScrollX,
|
|
2006
|
-
colWidth: defaultColWidth,
|
|
2007
|
-
currentAlignment: defaultCurrentAlignment,
|
|
2008
|
-
bgColor: defaultBgColor,
|
|
2009
|
-
colorSchemeName: defaultColorSchemeName,
|
|
2010
|
-
drawLabels: defaultDrawLabels,
|
|
2011
|
-
labelsAlignRight: defaultLabelsAlignRight,
|
|
2012
|
-
treeAreaWidth: defaultTreeAreaWidth,
|
|
2013
|
-
treeWidth: defaultTreeWidth,
|
|
2014
|
-
showBranchLen: defaultShowBranchLen,
|
|
2015
|
-
drawTree: defaultDrawTree,
|
|
2016
|
-
drawNodeBubbles: defaultDrawNodeBubbles,
|
|
2017
|
-
}
|
|
2018
|
-
|
|
2019
|
-
const nonDefaults = Object.fromEntries(
|
|
2020
|
-
Object.entries(defaults)
|
|
2021
|
-
.filter(([key, def]) => snap[key as keyof typeof snap] !== def)
|
|
2022
|
-
.map(([key]) => [key, snap[key as keyof typeof snap]]),
|
|
2023
|
-
)
|
|
2024
|
-
|
|
2025
|
-
return {
|
|
2026
|
-
...rest,
|
|
2027
|
-
data: {
|
|
2028
|
-
...(result.treeFilehandle ? {} : { tree }),
|
|
2029
|
-
...(result.msaFilehandle ? {} : { msa }),
|
|
2030
|
-
...(result.treeMetadataFilehandle ? {} : { treeMetadata }),
|
|
2031
|
-
},
|
|
2032
|
-
...nonDefaults,
|
|
2033
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
2034
|
-
...(collapsed?.length ? { collapsed } : {}),
|
|
2035
|
-
...(showOnly !== undefined ? { showOnly } : {}),
|
|
2036
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
2037
|
-
...(turnedOffTracks && Object.keys(turnedOffTracks).length > 0
|
|
2038
|
-
? { turnedOffTracks }
|
|
2039
|
-
: {}),
|
|
2040
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
2041
|
-
...(featureFilters && Object.keys(featureFilters).length > 0
|
|
2042
|
-
? { featureFilters }
|
|
2043
|
-
: {}),
|
|
2044
|
-
...(relativeTo !== undefined ? { relativeTo } : {}),
|
|
2045
|
-
} as typeof snap
|
|
2046
|
-
})
|
|
2136
|
+
.postProcessSnapshot(({ data, ...rest }) => ({
|
|
2137
|
+
// per-property defaults are stripped by types.stripDefault; the only thing
|
|
2138
|
+
// it can't express is this cross-field rule: drop inline tree/msa/metadata
|
|
2139
|
+
// when a sibling filehandle can refetch them, keeping sessions/URLs small
|
|
2140
|
+
...rest,
|
|
2141
|
+
data: {
|
|
2142
|
+
...(rest.treeFilehandle ? {} : { tree: data.tree }),
|
|
2143
|
+
...(rest.msaFilehandle ? {} : { msa: data.msa }),
|
|
2144
|
+
...(rest.treeMetadataFilehandle
|
|
2145
|
+
? {}
|
|
2146
|
+
: { treeMetadata: data.treeMetadata }),
|
|
2147
|
+
},
|
|
2148
|
+
}))
|
|
2047
2149
|
}
|
|
2048
2150
|
|
|
2049
2151
|
export default stateModelFactory
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { calculatePropertyConservation } from './propertyConservation.ts'
|
|
4
|
+
|
|
5
|
+
function counts(letters: string) {
|
|
6
|
+
const stats: Record<string, number> = {}
|
|
7
|
+
for (const c of letters) {
|
|
8
|
+
stats[c] = (stats[c] ?? 0) + 1
|
|
9
|
+
}
|
|
10
|
+
return stats
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function propertyConservation(columns: string[]) {
|
|
14
|
+
const colStats = columns.map(counts)
|
|
15
|
+
const colStatsSums = colStats.map(s =>
|
|
16
|
+
Object.values(s).reduce((a, b) => a + b, 0),
|
|
17
|
+
)
|
|
18
|
+
return calculatePropertyConservation(colStats, colStatsSums)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('property conservation', () => {
|
|
22
|
+
test('an invariant column scores 1', () => {
|
|
23
|
+
expect(propertyConservation(['LLLL'])[0]).toBe(1)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('a column varying only within one property class stays fully conserved', () => {
|
|
27
|
+
// L/I/V/M are all hydrophobic: identity varies but the property does not
|
|
28
|
+
expect(propertyConservation(['LIVM'])[0]).toBe(1)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('mixing opposite-charge classes lowers the score', () => {
|
|
32
|
+
// K/R (positive) vs D/E (negative): two equally-sized classes
|
|
33
|
+
const score = propertyConservation(['KRDE'])[0]!
|
|
34
|
+
expect(score).toBeGreaterThan(0)
|
|
35
|
+
expect(score).toBeLessThan(1)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('gaps reduce the score proportionally', () => {
|
|
39
|
+
const full = propertyConservation(['LLLL'])[0]!
|
|
40
|
+
const halfGapped = propertyConservation(['LL--'])[0]!
|
|
41
|
+
expect(halfGapped).toBeCloseTo(full * 0.5)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('an all-gap column scores 0', () => {
|
|
45
|
+
expect(propertyConservation(['----'])[0]).toBe(0)
|
|
46
|
+
})
|
|
47
|
+
})
|