react-msaview 5.5.0 → 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/bundle/index.js +173 -162
- package/bundle/index.js.map +4 -4
- package/dist/components/ConservationTrack.js +20 -26
- package/dist/components/ConservationTrack.js.map +1 -1
- package/dist/components/MSAViewer.d.ts +7 -1
- package/dist/components/MSAViewer.js +4 -1
- 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 +1 -1
- package/dist/components/Track.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/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.js +1 -1
- package/dist/components/msa/DomainLegend.js.map +1 -1
- package/dist/components/msa/MSACanvasBlock.js +39 -39
- 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/renderBoxFeatureCanvasBlock.js +3 -1
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +1 -2
- 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/tracks/renderTracksSvg.d.ts +6 -3
- package/dist/components/tracks/renderTracksSvg.js +17 -8
- 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 +1 -0
- package/dist/constants.js +1 -0
- package/dist/constants.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/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 +545 -132
- package/dist/model.js +101 -75
- 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/types.d.ts +4 -1
- 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 +4 -6
- package/src/colorSchemes.docs.test.ts +55 -0
- package/src/columnStats.test.ts +94 -0
- package/src/components/ConservationTrack.tsx +17 -15
- package/src/components/MSAViewer.tsx +12 -0
- package/src/components/SequenceTextArea.tsx +1 -1
- package/src/components/TextTrack.tsx +7 -11
- package/src/components/Track.tsx +1 -1
- 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/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 +5 -1
- package/src/components/msa/MSACanvasBlock.tsx +29 -26
- package/src/components/msa/MSAMouseoverCanvas.tsx +10 -13
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +3 -1
- package/src/components/msa/renderMSAMouseover.ts +2 -2
- package/src/components/overlayColors.ts +12 -0
- package/src/components/tracks/renderTracksSvg.ts +22 -8
- 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 +1 -0
- package/src/fetchUtils.ts +6 -3
- package/src/index.ts +1 -1
- package/src/model/msaModel.ts +21 -3
- package/src/model/treeModel.ts +21 -7
- package/src/model.ts +116 -117
- package/src/propertyConservation.test.ts +47 -0
- package/src/propertyConservation.ts +73 -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/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/src/components/dialogs/SettingsDialog.tsx +0 -234
- package/src/components/header/HeaderMenu.tsx +0 -38
package/src/fetchUtils.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { fetchAndMaybeUnzipText } from '@jbrowse/core/util'
|
|
1
|
+
import { fetchAndMaybeUnzipText, statusMessageText } from '@jbrowse/core/util'
|
|
2
|
+
|
|
3
|
+
import type { RpcStatus } from '@jbrowse/core/util'
|
|
2
4
|
|
|
3
5
|
export interface FetchStatus {
|
|
4
6
|
msg: string
|
|
@@ -10,7 +12,7 @@ type Filehandle = Parameters<typeof fetchAndMaybeUnzipText>[0]
|
|
|
10
12
|
|
|
11
13
|
type ProgressFetcher = (
|
|
12
14
|
loc: Filehandle,
|
|
13
|
-
opts: { signal: AbortSignal; statusCallback: (
|
|
15
|
+
opts: { signal: AbortSignal; statusCallback: (status: RpcStatus) => void },
|
|
14
16
|
) => Promise<string>
|
|
15
17
|
|
|
16
18
|
/**
|
|
@@ -28,7 +30,8 @@ export async function fetchTextWithProgress(
|
|
|
28
30
|
try {
|
|
29
31
|
return await fetcher(loc, {
|
|
30
32
|
signal: controller.signal,
|
|
31
|
-
statusCallback:
|
|
33
|
+
statusCallback: status => {
|
|
34
|
+
const msg = statusMessageText(status)
|
|
32
35
|
setStatus(
|
|
33
36
|
msg
|
|
34
37
|
? {
|
package/src/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ export { renderToSvg } from './renderToSvg.tsx'
|
|
|
9
9
|
export { default as MSAView } from './components/Loading.tsx'
|
|
10
10
|
export { default as MSAViewer } from './components/MSAViewer.tsx'
|
|
11
11
|
export { type MsaViewModel, default as MSAModelF } from './model.ts'
|
|
12
|
-
export type { MSAParserType } from 'msa-parsers'
|
|
12
|
+
export type { MSAFormat, MSAParserType } from 'msa-parsers'
|
|
13
13
|
export type { HierarchyNode } from './hierarchy.ts'
|
|
14
14
|
export type { InterProScanResults } from './launchInterProScan.ts'
|
|
15
15
|
export type {
|
package/src/model/msaModel.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { types } from '@jbrowse/mobx-state-tree'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
defaultBgColor,
|
|
5
|
+
defaultColorSchemeName,
|
|
6
|
+
defaultShowColumnStats,
|
|
7
|
+
} from '../constants.ts'
|
|
4
8
|
|
|
5
9
|
import type { MSAFormat } from 'msa-parsers'
|
|
6
10
|
|
|
@@ -16,13 +20,20 @@ export function MSAModelF() {
|
|
|
16
20
|
* #property
|
|
17
21
|
* draw MSA tiles with a background color
|
|
18
22
|
*/
|
|
19
|
-
bgColor: defaultBgColor,
|
|
23
|
+
bgColor: types.stripDefault(types.boolean, defaultBgColor),
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
26
|
* #property
|
|
23
27
|
* default color scheme name
|
|
24
28
|
*/
|
|
25
|
-
colorSchemeName: defaultColorSchemeName,
|
|
29
|
+
colorSchemeName: types.stripDefault(types.string, defaultColorSchemeName),
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* #property
|
|
33
|
+
* show a per-column statistics tooltip (consensus, conservation, gaps,
|
|
34
|
+
* residue distribution) while hovering the alignment
|
|
35
|
+
*/
|
|
36
|
+
showColumnStats: types.stripDefault(types.boolean, defaultShowColumnStats),
|
|
26
37
|
|
|
27
38
|
/**
|
|
28
39
|
* #property
|
|
@@ -49,6 +60,13 @@ export function MSAModelF() {
|
|
|
49
60
|
self.bgColor = arg
|
|
50
61
|
},
|
|
51
62
|
|
|
63
|
+
/**
|
|
64
|
+
* #action
|
|
65
|
+
*/
|
|
66
|
+
setShowColumnStats(arg: boolean) {
|
|
67
|
+
self.showColumnStats = arg
|
|
68
|
+
},
|
|
69
|
+
|
|
52
70
|
/**
|
|
53
71
|
* #action
|
|
54
72
|
* force a specific MSA parser, or pass undefined to auto-detect
|
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,26 +20,18 @@ 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,
|
|
@@ -72,6 +64,7 @@ import { MSAModelF } from './model/msaModel.ts'
|
|
|
72
64
|
import { TreeModelF } from './model/treeModel.ts'
|
|
73
65
|
import { calculateNeighborJoiningTree } from './neighborJoining.ts'
|
|
74
66
|
import { parseAsn1 } from './parseAsn1.ts'
|
|
67
|
+
import { calculatePropertyConservation } from './propertyConservation.ts'
|
|
75
68
|
import {
|
|
76
69
|
globalColToVisibleCol,
|
|
77
70
|
visibleColToGlobalCol,
|
|
@@ -131,19 +124,22 @@ function stateModelFactory() {
|
|
|
131
124
|
/**
|
|
132
125
|
* #property
|
|
133
126
|
*/
|
|
134
|
-
showDomains: defaultShowDomains,
|
|
127
|
+
showDomains: types.stripDefault(types.boolean, defaultShowDomains),
|
|
135
128
|
/**
|
|
136
129
|
* #property
|
|
137
130
|
*/
|
|
138
|
-
hideGaps: defaultHideGaps,
|
|
131
|
+
hideGaps: types.stripDefault(types.boolean, defaultHideGaps),
|
|
139
132
|
/**
|
|
140
133
|
* #property
|
|
141
134
|
*/
|
|
142
|
-
allowedGappyness:
|
|
135
|
+
allowedGappyness: types.stripDefault(
|
|
136
|
+
types.number,
|
|
137
|
+
defaultAllowedGappyness,
|
|
138
|
+
),
|
|
143
139
|
/**
|
|
144
140
|
* #property
|
|
145
141
|
*/
|
|
146
|
-
subFeatureRows: defaultSubFeatureRows,
|
|
142
|
+
subFeatureRows: types.stripDefault(types.boolean, defaultSubFeatureRows),
|
|
147
143
|
|
|
148
144
|
/**
|
|
149
145
|
* #property
|
|
@@ -154,43 +150,43 @@ function stateModelFactory() {
|
|
|
154
150
|
/**
|
|
155
151
|
* #property
|
|
156
152
|
*/
|
|
157
|
-
drawMsaLetters: defaultDrawMsaLetters,
|
|
153
|
+
drawMsaLetters: types.stripDefault(types.boolean, defaultDrawMsaLetters),
|
|
158
154
|
|
|
159
155
|
/**
|
|
160
156
|
* #property
|
|
161
157
|
* zoom in/out on plain mouse-wheel without holding ctrl
|
|
162
158
|
*/
|
|
163
|
-
scrollZoom: defaultScrollZoom,
|
|
159
|
+
scrollZoom: types.stripDefault(types.boolean, defaultScrollZoom),
|
|
164
160
|
|
|
165
161
|
/**
|
|
166
162
|
* #property
|
|
167
163
|
* height of the div containing the view, px
|
|
168
164
|
*/
|
|
169
|
-
height: types.
|
|
165
|
+
height: types.stripDefault(types.number, defaultHeight),
|
|
170
166
|
|
|
171
167
|
/**
|
|
172
168
|
* #property
|
|
173
169
|
* height of each row, px
|
|
174
170
|
*/
|
|
175
|
-
rowHeight: defaultRowHeight,
|
|
171
|
+
rowHeight: types.stripDefault(types.number, defaultRowHeight),
|
|
176
172
|
|
|
177
173
|
/**
|
|
178
174
|
* #property
|
|
179
175
|
* scroll position, Y-offset, px
|
|
180
176
|
*/
|
|
181
|
-
scrollY: defaultScrollY,
|
|
177
|
+
scrollY: types.stripDefault(types.number, defaultScrollY),
|
|
182
178
|
|
|
183
179
|
/**
|
|
184
180
|
* #property
|
|
185
181
|
* scroll position, X-offset, px
|
|
186
182
|
*/
|
|
187
|
-
scrollX: defaultScrollX,
|
|
183
|
+
scrollX: types.stripDefault(types.number, defaultScrollX),
|
|
188
184
|
|
|
189
185
|
/**
|
|
190
186
|
* #property
|
|
191
187
|
* width of columns, px
|
|
192
188
|
*/
|
|
193
|
-
colWidth: defaultColWidth,
|
|
189
|
+
colWidth: types.stripDefault(types.number, defaultColWidth),
|
|
194
190
|
|
|
195
191
|
/**
|
|
196
192
|
* #property
|
|
@@ -220,14 +216,17 @@ function stateModelFactory() {
|
|
|
220
216
|
/**
|
|
221
217
|
* #property
|
|
222
218
|
*/
|
|
223
|
-
currentAlignment:
|
|
219
|
+
currentAlignment: types.stripDefault(
|
|
220
|
+
types.number,
|
|
221
|
+
defaultCurrentAlignment,
|
|
222
|
+
),
|
|
224
223
|
|
|
225
224
|
/**
|
|
226
225
|
* #property
|
|
227
226
|
* array of tree parent nodes that are 'collapsed' (all children are
|
|
228
227
|
* hidden)
|
|
229
228
|
*/
|
|
230
|
-
collapsed: types.array(types.string),
|
|
229
|
+
collapsed: types.stripDefault(types.array(types.string), []),
|
|
231
230
|
|
|
232
231
|
/**
|
|
233
232
|
* #property
|
|
@@ -238,7 +237,7 @@ function stateModelFactory() {
|
|
|
238
237
|
* #property
|
|
239
238
|
* turned off tracks
|
|
240
239
|
*/
|
|
241
|
-
turnedOffTracks: types.map(types.boolean),
|
|
240
|
+
turnedOffTracks: types.stripDefault(types.map(types.boolean), {}),
|
|
242
241
|
|
|
243
242
|
/**
|
|
244
243
|
* #property
|
|
@@ -254,7 +253,7 @@ function stateModelFactory() {
|
|
|
254
253
|
/**
|
|
255
254
|
* #property
|
|
256
255
|
*/
|
|
257
|
-
featureFilters: types.map(types.boolean),
|
|
256
|
+
featureFilters: types.stripDefault(types.map(types.boolean), {}),
|
|
258
257
|
/**
|
|
259
258
|
* #property
|
|
260
259
|
*/
|
|
@@ -279,8 +278,7 @@ function stateModelFactory() {
|
|
|
279
278
|
* #volatile
|
|
280
279
|
*/
|
|
281
280
|
status: undefined as
|
|
282
|
-
|
|
283
|
-
| undefined,
|
|
281
|
+
{ msg: string; url?: string; onCancel?: () => void } | undefined,
|
|
284
282
|
/**
|
|
285
283
|
* #volatile
|
|
286
284
|
* high resolution scale factor, helps make canvas look better on hi-dpi
|
|
@@ -343,8 +341,7 @@ function stateModelFactory() {
|
|
|
343
341
|
* the currently hovered tree node ID and its descendant leaf names
|
|
344
342
|
*/
|
|
345
343
|
hoveredTreeNode: undefined as
|
|
346
|
-
|
|
347
|
-
| undefined,
|
|
344
|
+
{ nodeId: string; descendantNames: string[] } | undefined,
|
|
348
345
|
|
|
349
346
|
/**
|
|
350
347
|
* #volatile
|
|
@@ -377,8 +374,7 @@ function stateModelFactory() {
|
|
|
377
374
|
* #volatile
|
|
378
375
|
*/
|
|
379
376
|
interProAnnotations: undefined as
|
|
380
|
-
|
|
|
381
|
-
| Record<string, InterProScanResults>,
|
|
377
|
+
undefined | Record<string, InterProScanResults>,
|
|
382
378
|
}))
|
|
383
379
|
.actions(self => ({
|
|
384
380
|
/**
|
|
@@ -1044,6 +1040,17 @@ function stateModelFactory() {
|
|
|
1044
1040
|
return conservation * (1 - gapFraction)
|
|
1045
1041
|
})
|
|
1046
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
|
+
},
|
|
1047
1054
|
/**
|
|
1048
1055
|
* #getter
|
|
1049
1056
|
* generates a new tree that is clustered with x,y positions
|
|
@@ -1466,10 +1473,24 @@ function stateModelFactory() {
|
|
|
1466
1473
|
id: 'conservation',
|
|
1467
1474
|
name: 'Conservation',
|
|
1468
1475
|
height: self.conservationTrackHeight,
|
|
1476
|
+
barColor: 'gray',
|
|
1469
1477
|
},
|
|
1470
1478
|
ReactComponent: ConservationTrack,
|
|
1471
1479
|
}
|
|
1472
|
-
|
|
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
|
+
]
|
|
1473
1494
|
},
|
|
1474
1495
|
|
|
1475
1496
|
/**
|
|
@@ -1743,6 +1764,41 @@ function stateModelFactory() {
|
|
|
1743
1764
|
return []
|
|
1744
1765
|
},
|
|
1745
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
|
+
|
|
1746
1802
|
/**
|
|
1747
1803
|
* #method
|
|
1748
1804
|
*/
|
|
@@ -2046,6 +2102,23 @@ function stateModelFactory() {
|
|
|
2046
2102
|
}),
|
|
2047
2103
|
)
|
|
2048
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
|
+
|
|
2049
2122
|
// autorun synchronizes treeWidth with treeAreaWidth
|
|
2050
2123
|
addDisposer(
|
|
2051
2124
|
self,
|
|
@@ -2060,93 +2133,19 @@ function stateModelFactory() {
|
|
|
2060
2133
|
)
|
|
2061
2134
|
},
|
|
2062
2135
|
}))
|
|
2063
|
-
.postProcessSnapshot(
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
scrollX,
|
|
2077
|
-
colWidth,
|
|
2078
|
-
currentAlignment,
|
|
2079
|
-
collapsed,
|
|
2080
|
-
showOnly,
|
|
2081
|
-
turnedOffTracks,
|
|
2082
|
-
featureFilters,
|
|
2083
|
-
relativeTo,
|
|
2084
|
-
// MSA model properties
|
|
2085
|
-
bgColor,
|
|
2086
|
-
colorSchemeName,
|
|
2087
|
-
// Tree model properties
|
|
2088
|
-
drawLabels,
|
|
2089
|
-
labelsAlignRight,
|
|
2090
|
-
treeAreaWidth,
|
|
2091
|
-
treeWidth,
|
|
2092
|
-
showBranchLen,
|
|
2093
|
-
drawTree,
|
|
2094
|
-
drawNodeBubbles,
|
|
2095
|
-
// Always include
|
|
2096
|
-
...rest
|
|
2097
|
-
} = snap
|
|
2098
|
-
|
|
2099
|
-
const defaults: Record<string, unknown> = {
|
|
2100
|
-
showDomains: defaultShowDomains,
|
|
2101
|
-
hideGaps: defaultHideGaps,
|
|
2102
|
-
allowedGappyness: defaultAllowedGappyness,
|
|
2103
|
-
subFeatureRows: defaultSubFeatureRows,
|
|
2104
|
-
drawMsaLetters: defaultDrawMsaLetters,
|
|
2105
|
-
height: defaultHeight,
|
|
2106
|
-
rowHeight: defaultRowHeight,
|
|
2107
|
-
scrollY: defaultScrollY,
|
|
2108
|
-
scrollX: defaultScrollX,
|
|
2109
|
-
colWidth: defaultColWidth,
|
|
2110
|
-
currentAlignment: defaultCurrentAlignment,
|
|
2111
|
-
bgColor: defaultBgColor,
|
|
2112
|
-
colorSchemeName: defaultColorSchemeName,
|
|
2113
|
-
drawLabels: defaultDrawLabels,
|
|
2114
|
-
labelsAlignRight: defaultLabelsAlignRight,
|
|
2115
|
-
treeAreaWidth: defaultTreeAreaWidth,
|
|
2116
|
-
treeWidth: defaultTreeWidth,
|
|
2117
|
-
showBranchLen: defaultShowBranchLen,
|
|
2118
|
-
drawTree: defaultDrawTree,
|
|
2119
|
-
drawNodeBubbles: defaultDrawNodeBubbles,
|
|
2120
|
-
}
|
|
2121
|
-
|
|
2122
|
-
const nonDefaults = Object.fromEntries(
|
|
2123
|
-
Object.entries(defaults)
|
|
2124
|
-
.filter(([key, def]) => snap[key as keyof typeof snap] !== def)
|
|
2125
|
-
.map(([key]) => [key, snap[key as keyof typeof snap]]),
|
|
2126
|
-
)
|
|
2127
|
-
|
|
2128
|
-
return {
|
|
2129
|
-
...rest,
|
|
2130
|
-
data: {
|
|
2131
|
-
...(result.treeFilehandle ? {} : { tree }),
|
|
2132
|
-
...(result.msaFilehandle ? {} : { msa }),
|
|
2133
|
-
...(result.treeMetadataFilehandle ? {} : { treeMetadata }),
|
|
2134
|
-
},
|
|
2135
|
-
...nonDefaults,
|
|
2136
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
2137
|
-
...(collapsed?.length ? { collapsed } : {}),
|
|
2138
|
-
...(showOnly !== undefined ? { showOnly } : {}),
|
|
2139
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
2140
|
-
...(turnedOffTracks && Object.keys(turnedOffTracks).length > 0
|
|
2141
|
-
? { turnedOffTracks }
|
|
2142
|
-
: {}),
|
|
2143
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
2144
|
-
...(featureFilters && Object.keys(featureFilters).length > 0
|
|
2145
|
-
? { featureFilters }
|
|
2146
|
-
: {}),
|
|
2147
|
-
...(relativeTo !== undefined ? { relativeTo } : {}),
|
|
2148
|
-
} as typeof snap
|
|
2149
|
-
})
|
|
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
|
+
}))
|
|
2150
2149
|
}
|
|
2151
2150
|
|
|
2152
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
|
+
})
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Disjoint physicochemical classes for the 20 standard amino acids. Property
|
|
2
|
+
// conservation asks a different question than identity conservation: a column
|
|
3
|
+
// can vary in exact residue yet stay within one property class (a conservative
|
|
4
|
+
// substitution), which often marks a functionally constrained site that plain
|
|
5
|
+
// identity conservation reports as variable.
|
|
6
|
+
//
|
|
7
|
+
// Histidine is grouped with the aromatics (it is only weakly/conditionally
|
|
8
|
+
// positive at physiological pH); this mirrors common substitution-class schemes.
|
|
9
|
+
const aminoPropertyClass: Record<string, string> = {
|
|
10
|
+
A: 'hydrophobic',
|
|
11
|
+
V: 'hydrophobic',
|
|
12
|
+
L: 'hydrophobic',
|
|
13
|
+
I: 'hydrophobic',
|
|
14
|
+
M: 'hydrophobic',
|
|
15
|
+
C: 'hydrophobic',
|
|
16
|
+
F: 'aromatic',
|
|
17
|
+
W: 'aromatic',
|
|
18
|
+
Y: 'aromatic',
|
|
19
|
+
H: 'aromatic',
|
|
20
|
+
K: 'positive',
|
|
21
|
+
R: 'positive',
|
|
22
|
+
D: 'negative',
|
|
23
|
+
E: 'negative',
|
|
24
|
+
S: 'polar',
|
|
25
|
+
T: 'polar',
|
|
26
|
+
N: 'polar',
|
|
27
|
+
Q: 'polar',
|
|
28
|
+
G: 'special',
|
|
29
|
+
P: 'special',
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const numPropertyClasses = new Set(Object.values(aminoPropertyClass)).size
|
|
33
|
+
const maxPropertyEntropy = Math.log2(numPropertyClasses)
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Per-column property conservation using Shannon entropy over physicochemical
|
|
37
|
+
* classes rather than exact residues. Returns 0-1 where 1 = every non-gap
|
|
38
|
+
* residue shares one property class. Mirrors the identity-conservation formula
|
|
39
|
+
* so the two tracks are directly comparable: (1 - H/Hmax) * (1 - gapFraction).
|
|
40
|
+
*/
|
|
41
|
+
export function calculatePropertyConservation(
|
|
42
|
+
colStats: Record<string, number>[],
|
|
43
|
+
colStatsSums: number[],
|
|
44
|
+
) {
|
|
45
|
+
return colStats.map((stats, i) => {
|
|
46
|
+
const total = colStatsSums[i]
|
|
47
|
+
if (!total) {
|
|
48
|
+
return 0
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let nonGapTotal = 0
|
|
52
|
+
const classCounts: Record<string, number> = {}
|
|
53
|
+
for (const [letter, count] of Object.entries(stats)) {
|
|
54
|
+
const cls = aminoPropertyClass[letter]
|
|
55
|
+
if (cls) {
|
|
56
|
+
classCounts[cls] = (classCounts[cls] ?? 0) + count
|
|
57
|
+
nonGapTotal += count
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (nonGapTotal === 0) {
|
|
61
|
+
return 0
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let entropy = 0
|
|
65
|
+
for (const count of Object.values(classCounts)) {
|
|
66
|
+
const freq = count / nonGapTotal
|
|
67
|
+
entropy -= freq * Math.log2(freq)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const gapFraction = (total - nonGapTotal) / total
|
|
71
|
+
return Math.max(0, 1 - entropy / maxPropertyEntropy) * (1 - gapFraction)
|
|
72
|
+
})
|
|
73
|
+
}
|