react-msaview 4.1.1 → 4.2.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 +7 -7
- package/dist/components/TextTrack.d.ts +2 -1
- package/dist/components/TextTrack.js.map +1 -1
- package/dist/components/Track.js.map +1 -1
- package/dist/components/header/Header.js +6 -1
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/HeaderMenuExtra.js +8 -2
- package/dist/components/header/HeaderMenuExtra.js.map +1 -1
- package/dist/components/header/ZoomControls.d.ts +1 -1
- package/dist/components/header/ZoomControls.js +1 -46
- package/dist/components/header/ZoomControls.js.map +1 -1
- package/dist/components/header/ZoomMenu.d.ts +6 -0
- package/dist/components/header/ZoomMenu.js +33 -0
- package/dist/components/header/ZoomMenu.js.map +1 -0
- package/dist/components/header/ZoomStar.d.ts +6 -0
- package/dist/components/header/ZoomStar.js +40 -0
- package/dist/components/header/ZoomStar.js.map +1 -0
- package/dist/components/tree/renderTreeCanvas.js +6 -3
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/flatToTree.d.ts +17 -0
- package/dist/flatToTree.js +41 -0
- package/dist/flatToTree.js.map +1 -0
- package/dist/model.d.ts +19 -24
- package/dist/model.js +42 -20
- package/dist/model.js.map +1 -1
- package/dist/parseAsn1.d.ts +34 -0
- package/dist/parseAsn1.js +385 -0
- package/dist/parseAsn1.js.map +1 -0
- package/dist/parseAsn1.test.d.ts +1 -0
- package/dist/parseAsn1.test.js +8 -0
- package/dist/parseAsn1.test.js.map +1 -0
- package/dist/parsers/ClustalMSA.d.ts +1 -1
- package/dist/parsers/ClustalMSA.js.map +1 -1
- package/dist/parsers/EmfMSA.d.ts +1 -1
- package/dist/parsers/FastaMSA.d.ts +1 -1
- package/dist/parsers/StockholmMSA.d.ts +1 -1
- package/dist/parsers/StockholmMSA.js.map +1 -1
- package/dist/renderToSvg.d.ts +3 -2
- package/dist/renderToSvg.js.map +1 -1
- package/dist/reparseTree.d.ts +1 -1
- package/dist/reparseTree.js +2 -0
- package/dist/reparseTree.js.map +1 -1
- package/dist/types.d.ts +38 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +6 -20
- package/dist/util.js +19 -0
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -2
- package/src/__snapshots__/parseAsn1.test.ts.snap +2400 -0
- package/src/components/TextTrack.tsx +2 -1
- package/src/components/Track.tsx +0 -2
- package/src/components/header/Header.tsx +7 -1
- package/src/components/header/HeaderMenuExtra.tsx +8 -2
- package/src/components/header/ZoomControls.tsx +1 -52
- package/src/components/header/ZoomMenu.tsx +42 -0
- package/src/components/header/ZoomStar.tsx +75 -0
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +1 -1
- package/src/components/msa/renderMSABlock.ts +1 -1
- package/src/components/tree/renderTreeCanvas.ts +13 -3
- package/src/flatToTree.ts +57 -0
- package/src/model.ts +70 -49
- package/src/parseAsn1.test.ts +11 -0
- package/src/parseAsn1.ts +494 -0
- package/src/parsers/ClustalMSA.ts +2 -1
- package/src/parsers/EmfMSA.ts +1 -1
- package/src/parsers/FastaMSA.ts +1 -1
- package/src/parsers/StockholmMSA.ts +4 -1
- package/src/renderToSvg.tsx +6 -4
- package/src/reparseTree.ts +3 -1
- package/src/types.ts +44 -0
- package/src/util.ts +26 -22
- package/src/version.ts +1 -1
|
@@ -5,7 +5,8 @@ import { observer } from 'mobx-react'
|
|
|
5
5
|
|
|
6
6
|
import { colorContrast } from '../util'
|
|
7
7
|
|
|
8
|
-
import type {
|
|
8
|
+
import type { MsaViewModel } from '../model'
|
|
9
|
+
import type { ITextTrack } from '../types'
|
|
9
10
|
|
|
10
11
|
const AnnotationBlock = observer(function ({
|
|
11
12
|
track,
|
package/src/components/Track.tsx
CHANGED
|
@@ -10,6 +10,8 @@ import HeaderMenuExtra from './HeaderMenuExtra'
|
|
|
10
10
|
import HeaderStatusArea from './HeaderStatusArea'
|
|
11
11
|
import MultiAlignmentSelector from './MultiAlignmentSelector'
|
|
12
12
|
import ZoomControls from './ZoomControls'
|
|
13
|
+
import ZoomMenu from './ZoomMenu'
|
|
14
|
+
import ZoomStar from './ZoomStar'
|
|
13
15
|
|
|
14
16
|
import type { MsaViewModel } from '../../model'
|
|
15
17
|
|
|
@@ -24,7 +26,11 @@ const Header = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
24
26
|
<div ref={ref} style={{ display: 'flex' }}>
|
|
25
27
|
<HeaderMenuExtra model={model} />
|
|
26
28
|
<ZoomControls model={model} />
|
|
27
|
-
<
|
|
29
|
+
{model.showZoomStar ? <ZoomStar model={model} /> : null}
|
|
30
|
+
<ZoomMenu model={model} />
|
|
31
|
+
<div style={{ margin: 'auto' }}>
|
|
32
|
+
<MultiAlignmentSelector model={model} />
|
|
33
|
+
</div>
|
|
28
34
|
<HeaderInfoArea model={model} />
|
|
29
35
|
<Spacer />
|
|
30
36
|
<HeaderStatusArea model={model} />
|
|
@@ -77,7 +77,10 @@ const HeaderMenuExtra = observer(({ model }: { model: MsaViewModel }) => {
|
|
|
77
77
|
onClick: () => {
|
|
78
78
|
model.queueDialog(handleClose => [
|
|
79
79
|
UserProvidedDomainsDialog,
|
|
80
|
-
{
|
|
80
|
+
{
|
|
81
|
+
handleClose,
|
|
82
|
+
model,
|
|
83
|
+
},
|
|
81
84
|
])
|
|
82
85
|
},
|
|
83
86
|
},
|
|
@@ -118,7 +121,10 @@ const HeaderMenuExtra = observer(({ model }: { model: MsaViewModel }) => {
|
|
|
118
121
|
onClick: () => {
|
|
119
122
|
model.queueDialog(onClose => [
|
|
120
123
|
FeatureFilterDialog,
|
|
121
|
-
{
|
|
124
|
+
{
|
|
125
|
+
onClose,
|
|
126
|
+
model,
|
|
127
|
+
},
|
|
122
128
|
])
|
|
123
129
|
},
|
|
124
130
|
},
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
|
-
import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton'
|
|
4
|
-
import MoreVert from '@mui/icons-material/MoreVert'
|
|
5
|
-
import RestartAlt from '@mui/icons-material/RestartAlt'
|
|
6
3
|
import ZoomIn from '@mui/icons-material/ZoomIn'
|
|
7
4
|
import ZoomOut from '@mui/icons-material/ZoomOut'
|
|
8
5
|
import { IconButton } from '@mui/material'
|
|
9
6
|
import { observer } from 'mobx-react'
|
|
10
7
|
|
|
11
|
-
import { MsaViewModel } from '../../model'
|
|
12
|
-
|
|
13
|
-
// icons
|
|
8
|
+
import type { MsaViewModel } from '../../model'
|
|
14
9
|
|
|
15
10
|
const ZoomControls = observer(function ZoomControls({
|
|
16
11
|
model,
|
|
@@ -33,52 +28,6 @@ const ZoomControls = observer(function ZoomControls({
|
|
|
33
28
|
>
|
|
34
29
|
<ZoomOut />
|
|
35
30
|
</IconButton>
|
|
36
|
-
<CascadingMenuButton
|
|
37
|
-
menuItems={[
|
|
38
|
-
{
|
|
39
|
-
label: 'Zoom in horizontal',
|
|
40
|
-
onClick: () => {
|
|
41
|
-
model.zoomInHorizontal()
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
label: 'Zoom in vertical',
|
|
46
|
-
onClick: () => {
|
|
47
|
-
model.zoomInVertical()
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
label: 'Zoom out horizontal',
|
|
52
|
-
onClick: () => {
|
|
53
|
-
model.zoomOutHorizontal()
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
{
|
|
58
|
-
label: 'Zoom out vertical',
|
|
59
|
-
onClick: () => {
|
|
60
|
-
model.zoomOutVertical()
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
{
|
|
65
|
-
label: 'Show entire view',
|
|
66
|
-
onClick: () => {
|
|
67
|
-
model.showEntire()
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
label: 'Reset zoom to default',
|
|
72
|
-
icon: RestartAlt,
|
|
73
|
-
onClick: () => {
|
|
74
|
-
model.setColWidth(16)
|
|
75
|
-
model.setRowHeight(20)
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
]}
|
|
79
|
-
>
|
|
80
|
-
<MoreVert />
|
|
81
|
-
</CascadingMenuButton>
|
|
82
31
|
</>
|
|
83
32
|
)
|
|
84
33
|
})
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton'
|
|
4
|
+
import MoreVert from '@mui/icons-material/MoreVert'
|
|
5
|
+
import RestartAlt from '@mui/icons-material/RestartAlt'
|
|
6
|
+
import { observer } from 'mobx-react'
|
|
7
|
+
|
|
8
|
+
import type { MsaViewModel } from '../../model'
|
|
9
|
+
|
|
10
|
+
const ZoomMenu = observer(function ({ model }: { model: MsaViewModel }) {
|
|
11
|
+
return (
|
|
12
|
+
<CascadingMenuButton
|
|
13
|
+
menuItems={[
|
|
14
|
+
{
|
|
15
|
+
label: 'Fit to view',
|
|
16
|
+
onClick: () => {
|
|
17
|
+
model.showEntire()
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: 'Reset zoom to default',
|
|
22
|
+
icon: RestartAlt,
|
|
23
|
+
onClick: () => {
|
|
24
|
+
model.resetZoom()
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
label: 'Show extra zoom options',
|
|
29
|
+
checked: model.showZoomStar,
|
|
30
|
+
type: 'checkbox',
|
|
31
|
+
onClick: () => {
|
|
32
|
+
model.setShowZoomStar(!model.showZoomStar)
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
]}
|
|
36
|
+
>
|
|
37
|
+
<MoreVert />
|
|
38
|
+
</CascadingMenuButton>
|
|
39
|
+
)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
export default ZoomMenu
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { IconButton } from '@mui/material'
|
|
4
|
+
import { observer } from 'mobx-react'
|
|
5
|
+
import { makeStyles } from 'tss-react/mui'
|
|
6
|
+
|
|
7
|
+
import { RestartAlt } from '@mui/icons-material'
|
|
8
|
+
|
|
9
|
+
import type { MsaViewModel } from '../../model'
|
|
10
|
+
|
|
11
|
+
const useStyles = makeStyles()(theme => ({
|
|
12
|
+
dpad: {
|
|
13
|
+
display: 'grid',
|
|
14
|
+
gridTemplateColumns: 'repeat(3, 1fr)',
|
|
15
|
+
},
|
|
16
|
+
icon: {
|
|
17
|
+
padding: theme.spacing(0.5),
|
|
18
|
+
},
|
|
19
|
+
}))
|
|
20
|
+
|
|
21
|
+
const ZoomStar = observer(function ({ model }: { model: MsaViewModel }) {
|
|
22
|
+
const { classes } = useStyles()
|
|
23
|
+
return (
|
|
24
|
+
<div className={classes.dpad}>
|
|
25
|
+
<div />
|
|
26
|
+
<IconButton
|
|
27
|
+
className={classes.icon}
|
|
28
|
+
onClick={() => {
|
|
29
|
+
model.zoomInVertical()
|
|
30
|
+
}}
|
|
31
|
+
>
|
|
32
|
+
Y+
|
|
33
|
+
</IconButton>
|
|
34
|
+
<div />
|
|
35
|
+
|
|
36
|
+
<IconButton
|
|
37
|
+
className={classes.icon}
|
|
38
|
+
onClick={() => {
|
|
39
|
+
model.zoomOutHorizontal()
|
|
40
|
+
}}
|
|
41
|
+
>
|
|
42
|
+
X-
|
|
43
|
+
</IconButton>
|
|
44
|
+
<IconButton
|
|
45
|
+
className={classes.icon}
|
|
46
|
+
onClick={() => {
|
|
47
|
+
model.resetZoom()
|
|
48
|
+
}}
|
|
49
|
+
>
|
|
50
|
+
<RestartAlt />
|
|
51
|
+
</IconButton>
|
|
52
|
+
<IconButton
|
|
53
|
+
className={classes.icon}
|
|
54
|
+
onClick={() => {
|
|
55
|
+
model.zoomInHorizontal()
|
|
56
|
+
}}
|
|
57
|
+
>
|
|
58
|
+
X+
|
|
59
|
+
</IconButton>
|
|
60
|
+
|
|
61
|
+
<div />
|
|
62
|
+
<IconButton
|
|
63
|
+
className={classes.icon}
|
|
64
|
+
onClick={() => {
|
|
65
|
+
model.zoomOutVertical()
|
|
66
|
+
}}
|
|
67
|
+
>
|
|
68
|
+
Y-
|
|
69
|
+
</IconButton>
|
|
70
|
+
<div />
|
|
71
|
+
</div>
|
|
72
|
+
)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
export default ZoomStar
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getClustalXColor, getPercentIdentityColor } from '../../colorSchemes'
|
|
2
2
|
|
|
3
3
|
import type { MsaViewModel } from '../../model'
|
|
4
|
-
import type { NodeWithIdsAndLength } from '../../
|
|
4
|
+
import type { NodeWithIdsAndLength } from '../../types'
|
|
5
5
|
import type { Theme } from '@mui/material'
|
|
6
6
|
import type { HierarchyNode } from 'd3-hierarchy'
|
|
7
7
|
|
|
@@ -31,9 +31,15 @@ export function renderTree({
|
|
|
31
31
|
theme: Theme
|
|
32
32
|
blockSizeYOverride?: number
|
|
33
33
|
}) {
|
|
34
|
-
const {
|
|
34
|
+
const {
|
|
35
|
+
hierarchy,
|
|
36
|
+
allBranchesLength0,
|
|
37
|
+
showBranchLen: showBranchLenPre,
|
|
38
|
+
blockSize,
|
|
39
|
+
} = model
|
|
35
40
|
const by = blockSizeYOverride || blockSize
|
|
36
41
|
ctx.strokeStyle = theme.palette.text.primary
|
|
42
|
+
const showBranchLen = allBranchesLength0 ? false : showBranchLenPre
|
|
37
43
|
for (const link of hierarchy.links()) {
|
|
38
44
|
const { source, target } = link
|
|
39
45
|
if (target.height === 0 && !showBranchLen) {
|
|
@@ -77,12 +83,14 @@ export function renderNodeBubbles({
|
|
|
77
83
|
}) {
|
|
78
84
|
const {
|
|
79
85
|
hierarchy,
|
|
80
|
-
showBranchLen,
|
|
86
|
+
showBranchLen: showBranchLenPre,
|
|
87
|
+
allBranchesLength0,
|
|
81
88
|
collapsed,
|
|
82
89
|
blockSize,
|
|
83
90
|
marginLeft: ml,
|
|
84
91
|
} = model
|
|
85
92
|
const by = blockSizeYOverride || blockSize
|
|
93
|
+
const showBranchLen = allBranchesLength0 ? false : showBranchLenPre
|
|
86
94
|
for (const node of hierarchy.descendants()) {
|
|
87
95
|
const val = showBranchLen ? 'len' : 'y'
|
|
88
96
|
// @ts-expect-error
|
|
@@ -131,7 +139,8 @@ export function renderTreeLabels({
|
|
|
131
139
|
}) {
|
|
132
140
|
const {
|
|
133
141
|
fontSize,
|
|
134
|
-
showBranchLen,
|
|
142
|
+
showBranchLen: showBranchLenPre,
|
|
143
|
+
allBranchesLength0,
|
|
135
144
|
treeMetadata,
|
|
136
145
|
hierarchy,
|
|
137
146
|
collapsed,
|
|
@@ -153,6 +162,7 @@ export function renderTreeLabels({
|
|
|
153
162
|
} else {
|
|
154
163
|
ctx.textAlign = 'start'
|
|
155
164
|
}
|
|
165
|
+
const showBranchLen = allBranchesLength0 ? false : showBranchLenPre
|
|
156
166
|
for (const node of leaves) {
|
|
157
167
|
const {
|
|
158
168
|
data: { name, id },
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Define the input item interface
|
|
2
|
+
interface FlatItem {
|
|
3
|
+
id: number
|
|
4
|
+
parent?: number
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Define the tree node interface
|
|
8
|
+
interface TreeNode {
|
|
9
|
+
id: string
|
|
10
|
+
name: string
|
|
11
|
+
parent?: string
|
|
12
|
+
children: TreeNode[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Parses a flat list of items into a tree structure
|
|
17
|
+
* @param items - Array of flat items with id and optional parent
|
|
18
|
+
* @returns Array of root tree nodes
|
|
19
|
+
*/
|
|
20
|
+
export function flatToTree(items: FlatItem[]): TreeNode {
|
|
21
|
+
// Create a map to store all nodes by their id for quick lookup
|
|
22
|
+
const nodeMap = new Map<number, TreeNode>()
|
|
23
|
+
|
|
24
|
+
// First pass: Create all tree nodes
|
|
25
|
+
items.forEach(item => {
|
|
26
|
+
nodeMap.set(item.id, {
|
|
27
|
+
...item,
|
|
28
|
+
id: `${item.id}`,
|
|
29
|
+
name: `${item.id}`,
|
|
30
|
+
parent: item.parent !== undefined ? `${item.parent}` : undefined,
|
|
31
|
+
children: [],
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// Second pass: Build parent-child relationships
|
|
36
|
+
const roots: TreeNode[] = []
|
|
37
|
+
|
|
38
|
+
items.forEach(item => {
|
|
39
|
+
const node = nodeMap.get(item.id)!
|
|
40
|
+
|
|
41
|
+
if (item.parent !== undefined) {
|
|
42
|
+
// This item has a parent, add it to parent's children
|
|
43
|
+
const parentNode = nodeMap.get(item.parent)
|
|
44
|
+
if (parentNode) {
|
|
45
|
+
parentNode.children.push(node)
|
|
46
|
+
} else {
|
|
47
|
+
// Parent doesn't exist, treat as root
|
|
48
|
+
roots.push(node)
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
// This item has no parent, it's a root node
|
|
52
|
+
roots.push(node)
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
return roots[0]!
|
|
57
|
+
}
|
package/src/model.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
|
|
3
1
|
import { groupBy, notEmpty, sum } from '@jbrowse/core/util'
|
|
4
2
|
import { openLocation } from '@jbrowse/core/util/io'
|
|
5
3
|
import { ElementId, FileLocation } from '@jbrowse/core/util/types/mst'
|
|
6
4
|
import { colord } from 'colord'
|
|
7
5
|
import { ascending } from 'd3-array'
|
|
8
6
|
import { cluster, hierarchy } from 'd3-hierarchy'
|
|
7
|
+
import { parseEmfTree } from 'emf-js'
|
|
9
8
|
import { saveAs } from 'file-saver'
|
|
10
9
|
import { autorun, transaction } from 'mobx'
|
|
11
10
|
import { addDisposer, cast, types } from 'mobx-state-tree'
|
|
@@ -15,16 +14,17 @@ import Stockholm from 'stockholm-js'
|
|
|
15
14
|
import { blocksX, blocksY } from './calculateBlocks'
|
|
16
15
|
import colorSchemes from './colorSchemes'
|
|
17
16
|
import TextTrack from './components/TextTrack'
|
|
17
|
+
import { flatToTree } from './flatToTree'
|
|
18
18
|
import palettes from './ggplotPalettes'
|
|
19
19
|
import { measureTextCanvas } from './measureTextCanvas'
|
|
20
20
|
import { DataModelF } from './model/DataModel'
|
|
21
21
|
import { DialogQueueSessionMixin } from './model/DialogQueue'
|
|
22
22
|
import { MSAModelF } from './model/msaModel'
|
|
23
23
|
import { TreeModelF } from './model/treeModel'
|
|
24
|
+
import { parseAsn1 } from './parseAsn1'
|
|
24
25
|
import parseNewick from './parseNewick'
|
|
25
26
|
import ClustalMSA from './parsers/ClustalMSA'
|
|
26
27
|
import EmfMSA from './parsers/EmfMSA'
|
|
27
|
-
import EmfTree from './parsers/EmfTree'
|
|
28
28
|
import FastaMSA from './parsers/FastaMSA'
|
|
29
29
|
import StockholmMSA from './parsers/StockholmMSA'
|
|
30
30
|
import { reparseTree } from './reparseTree'
|
|
@@ -36,46 +36,31 @@ import {
|
|
|
36
36
|
clamp,
|
|
37
37
|
collapse,
|
|
38
38
|
generateNodeIds,
|
|
39
|
+
isGzip,
|
|
39
40
|
len,
|
|
41
|
+
localStorageGetBoolean,
|
|
42
|
+
localStorageSetBoolean,
|
|
40
43
|
maxLength,
|
|
41
44
|
setBrLength,
|
|
42
45
|
skipBlanks,
|
|
43
46
|
} from './util'
|
|
44
47
|
|
|
45
48
|
import type { InterProScanResults } from './launchInterProScan'
|
|
46
|
-
import type {
|
|
49
|
+
import type {
|
|
50
|
+
Accession,
|
|
51
|
+
BasicTrack,
|
|
52
|
+
NodeWithIds,
|
|
53
|
+
NodeWithIdsAndLength,
|
|
54
|
+
TextTrackModel,
|
|
55
|
+
} from './types'
|
|
47
56
|
import type { FileLocation as FileLocationType } from '@jbrowse/core/util/types'
|
|
48
57
|
import type { Theme } from '@mui/material'
|
|
49
58
|
import type { HierarchyNode } from 'd3-hierarchy'
|
|
50
59
|
import type { Instance } from 'mobx-state-tree'
|
|
51
60
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
description: string
|
|
56
|
-
}
|
|
57
|
-
export interface BasicTrackModel {
|
|
58
|
-
id: string
|
|
59
|
-
name: string
|
|
60
|
-
associatedRowName?: string
|
|
61
|
-
height: number
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export interface TextTrackModel extends BasicTrackModel {
|
|
65
|
-
customColorScheme?: Record<string, string>
|
|
66
|
-
data: string
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface ITextTrack {
|
|
70
|
-
ReactComponent: React.FC<any>
|
|
71
|
-
model: TextTrackModel
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export type BasicTrack = ITextTrack
|
|
75
|
-
|
|
76
|
-
export function isGzip(buf: Uint8Array) {
|
|
77
|
-
return buf[0] === 31 && buf[1] === 139 && buf[2] === 8
|
|
78
|
-
}
|
|
61
|
+
const defaultRowHeight = 16
|
|
62
|
+
const defaultColWidth = 12
|
|
63
|
+
const showZoomStarKey = 'msa-showZoomStar'
|
|
79
64
|
|
|
80
65
|
/**
|
|
81
66
|
* #stateModel MsaView
|
|
@@ -145,7 +130,7 @@ function stateModelFactory() {
|
|
|
145
130
|
* #property
|
|
146
131
|
* height of each row, px
|
|
147
132
|
*/
|
|
148
|
-
rowHeight:
|
|
133
|
+
rowHeight: defaultRowHeight,
|
|
149
134
|
|
|
150
135
|
/**
|
|
151
136
|
* #property
|
|
@@ -163,7 +148,7 @@ function stateModelFactory() {
|
|
|
163
148
|
* #property
|
|
164
149
|
* width of columns, px
|
|
165
150
|
*/
|
|
166
|
-
colWidth:
|
|
151
|
+
colWidth: defaultColWidth,
|
|
167
152
|
|
|
168
153
|
/**
|
|
169
154
|
* #property
|
|
@@ -250,6 +235,12 @@ function stateModelFactory() {
|
|
|
250
235
|
* screens
|
|
251
236
|
*/
|
|
252
237
|
highResScaleFactor: 2,
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* #volatile
|
|
241
|
+
* obtained from localStorage
|
|
242
|
+
*/
|
|
243
|
+
showZoomStar: localStorageGetBoolean(showZoomStarKey, true),
|
|
253
244
|
/**
|
|
254
245
|
* #volatile
|
|
255
246
|
*/
|
|
@@ -363,6 +354,12 @@ function stateModelFactory() {
|
|
|
363
354
|
setLoadingMSA(arg: boolean) {
|
|
364
355
|
self.loadingMSA = arg
|
|
365
356
|
},
|
|
357
|
+
/**
|
|
358
|
+
* #volatile
|
|
359
|
+
*/
|
|
360
|
+
setShowZoomStar(arg: boolean) {
|
|
361
|
+
self.showZoomStar = arg
|
|
362
|
+
},
|
|
366
363
|
/**
|
|
367
364
|
* #action
|
|
368
365
|
*/
|
|
@@ -635,26 +632,28 @@ function stateModelFactory() {
|
|
|
635
632
|
*/
|
|
636
633
|
get tree(): NodeWithIds {
|
|
637
634
|
const text = self.data.tree
|
|
638
|
-
let ret: NodeWithIds
|
|
639
635
|
if (text) {
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
636
|
+
return reparseTree(
|
|
637
|
+
generateNodeIds(
|
|
638
|
+
text.startsWith('BioTreeContainer')
|
|
639
|
+
? flatToTree(parseAsn1(text))
|
|
640
|
+
: parseNewick(
|
|
641
|
+
text.startsWith('SEQ') ? parseEmfTree(text).tree : text,
|
|
642
|
+
),
|
|
643
|
+
),
|
|
644
|
+
)
|
|
648
645
|
} else {
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
646
|
+
return reparseTree(
|
|
647
|
+
this.MSA?.getTree() || {
|
|
648
|
+
noTree: true,
|
|
649
|
+
children: [],
|
|
650
|
+
id: 'empty',
|
|
651
|
+
name: 'empty',
|
|
652
|
+
},
|
|
653
|
+
)
|
|
655
654
|
}
|
|
656
|
-
return reparseTree(ret)
|
|
657
655
|
},
|
|
656
|
+
|
|
658
657
|
/**
|
|
659
658
|
* #getter
|
|
660
659
|
*/
|
|
@@ -846,6 +845,13 @@ function stateModelFactory() {
|
|
|
846
845
|
get leaves() {
|
|
847
846
|
return this.hierarchy.leaves()
|
|
848
847
|
},
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* #getter
|
|
851
|
+
*/
|
|
852
|
+
get allBranchesLength0() {
|
|
853
|
+
return this.hierarchy.links().every(s => !s.source.data.length)
|
|
854
|
+
},
|
|
849
855
|
}))
|
|
850
856
|
.views(self => ({
|
|
851
857
|
/**
|
|
@@ -937,6 +943,13 @@ function stateModelFactory() {
|
|
|
937
943
|
self.drawMsaLetters = arg
|
|
938
944
|
},
|
|
939
945
|
|
|
946
|
+
/**
|
|
947
|
+
* #action
|
|
948
|
+
*/
|
|
949
|
+
resetZoom() {
|
|
950
|
+
self.setColWidth(defaultColWidth)
|
|
951
|
+
self.setRowHeight(defaultRowHeight)
|
|
952
|
+
},
|
|
940
953
|
/**
|
|
941
954
|
* #action
|
|
942
955
|
*/
|
|
@@ -1379,6 +1392,14 @@ function stateModelFactory() {
|
|
|
1379
1392
|
}),
|
|
1380
1393
|
)
|
|
1381
1394
|
|
|
1395
|
+
// autorun saves local settings
|
|
1396
|
+
addDisposer(
|
|
1397
|
+
self,
|
|
1398
|
+
autorun(() => {
|
|
1399
|
+
localStorageSetBoolean(showZoomStarKey, self.showZoomStar)
|
|
1400
|
+
}),
|
|
1401
|
+
)
|
|
1402
|
+
|
|
1382
1403
|
// autorun opens treeFilehandle
|
|
1383
1404
|
addDisposer(
|
|
1384
1405
|
self,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
|
|
3
|
+
import { expect, test } from 'vitest'
|
|
4
|
+
|
|
5
|
+
import { parseAsn1 } from './parseAsn1'
|
|
6
|
+
|
|
7
|
+
const r = fs.readFileSync(require.resolve('../test/data/tree.asn'), 'utf8')
|
|
8
|
+
|
|
9
|
+
test('real data file', () => {
|
|
10
|
+
expect(parseAsn1(r)).toMatchSnapshot()
|
|
11
|
+
})
|