react-msaview 9.1.0 → 9.1.1
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 +111 -115
- package/bundle/index.js.map +4 -4
- package/dist/blosum62.d.ts +3 -0
- package/dist/blosum62.js +43 -0
- package/dist/blosum62.js.map +1 -0
- package/dist/colorSchemes.js +1 -0
- package/dist/colorSchemes.js.map +1 -1
- package/dist/components/Loading.d.ts +3 -2
- package/dist/components/Loading.js +2 -2
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/MSAView.d.ts +3 -2
- package/dist/components/MSAView.js +7 -4
- package/dist/components/MSAView.js.map +1 -1
- package/dist/components/MSAViewer.d.ts +62 -12
- package/dist/components/MetadataTable.d.ts +4 -1
- package/dist/components/MetadataTable.js +55 -16
- package/dist/components/MetadataTable.js.map +1 -1
- package/dist/components/Track.js +6 -0
- package/dist/components/Track.js.map +1 -1
- package/dist/components/controls.js +2 -1
- package/dist/components/controls.js.map +1 -1
- package/dist/components/dialogs/AnnotationFileDialog.js +7 -2
- package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -1
- package/dist/components/dialogs/FeatureDialog.js +2 -2
- package/dist/components/dialogs/FeatureDialog.js.map +1 -1
- package/dist/components/{header/MSASettingsMenu.d.ts → dialogs/GapThresholdDialog.d.ts} +3 -2
- package/dist/components/dialogs/GapThresholdDialog.js +15 -0
- package/dist/components/dialogs/GapThresholdDialog.js.map +1 -0
- package/dist/components/{header → dialogs}/GappynessSlider.d.ts +1 -1
- package/dist/components/{header → dialogs}/GappynessSlider.js +3 -10
- package/dist/components/dialogs/GappynessSlider.js.map +1 -0
- package/dist/components/{header → dialogs}/GoToBox.d.ts +4 -2
- package/dist/components/dialogs/GoToBox.js +48 -0
- package/dist/components/dialogs/GoToBox.js.map +1 -0
- package/dist/components/dialogs/GoToDialog.d.ts +6 -0
- package/dist/components/dialogs/GoToDialog.js +10 -0
- package/dist/components/dialogs/GoToDialog.js.map +1 -0
- package/dist/components/dialogs/MetadataDialog.js +11 -3
- package/dist/components/dialogs/MetadataDialog.js.map +1 -1
- package/dist/components/dialogs/goTo.d.ts +5 -0
- package/dist/components/{header → dialogs}/goTo.js +1 -1
- package/dist/components/dialogs/goTo.js.map +1 -0
- package/dist/components/dialogs/metadataLabels.d.ts +4 -0
- package/dist/components/dialogs/metadataLabels.js +52 -0
- package/dist/components/dialogs/metadataLabels.js.map +1 -0
- package/dist/components/dialogs/metadataRecord.d.ts +1 -0
- package/dist/components/dialogs/metadataRecord.js +38 -0
- package/dist/components/dialogs/metadataRecord.js.map +1 -0
- package/dist/components/header/Header.d.ts +6 -2
- package/dist/components/header/Header.js +30 -32
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/{TreeSettingsMenu.d.ts → HiddenColumnsChip.d.ts} +2 -2
- package/dist/components/header/HiddenColumnsChip.js +15 -0
- package/dist/components/header/HiddenColumnsChip.js.map +1 -0
- package/dist/components/header/MenuBar.d.ts +6 -0
- package/dist/components/header/MenuBar.js +33 -0
- package/dist/components/header/MenuBar.js.map +1 -0
- package/dist/components/header/ScrollZoomCheckbox.d.ts +7 -0
- package/dist/components/header/ScrollZoomCheckbox.js +19 -0
- package/dist/components/header/ScrollZoomCheckbox.js.map +1 -0
- package/dist/components/header/ZoomControls.js +10 -30
- package/dist/components/header/ZoomControls.js.map +1 -1
- package/dist/components/header/colorSchemeMenuItems.d.ts +4 -0
- package/dist/components/header/colorSchemeMenuItems.js +106 -0
- package/dist/components/header/colorSchemeMenuItems.js.map +1 -0
- package/dist/components/header/getAnnotationsMenu.d.ts +3 -30
- package/dist/components/header/getAnnotationsMenu.js +48 -54
- package/dist/components/header/getAnnotationsMenu.js.map +1 -1
- package/dist/components/header/menuBarItems.d.ts +11 -0
- package/dist/components/header/menuBarItems.js +277 -0
- package/dist/components/header/menuBarItems.js.map +1 -0
- package/dist/components/import/ImportForm.js +12 -6
- package/dist/components/import/ImportForm.js.map +1 -1
- package/dist/components/minimap/Minimap.js +7 -3
- package/dist/components/minimap/Minimap.js.map +1 -1
- package/dist/components/minimap/MinimapSVG.js +2 -1
- package/dist/components/minimap/MinimapSVG.js.map +1 -1
- package/dist/components/minimap/minimapLayout.d.ts +12 -0
- package/dist/components/minimap/minimapLayout.js +20 -0
- package/dist/components/minimap/minimapLayout.js.map +1 -1
- package/dist/components/msa/MSACanvas.js +26 -9
- package/dist/components/msa/MSACanvas.js.map +1 -1
- package/dist/components/msa/MSACanvasBlock.js +15 -11
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/MSAMouseoverCanvas.js +2 -2
- package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
- package/dist/components/msa/WheelZoomHint.d.ts +7 -0
- package/dist/components/msa/WheelZoomHint.js +38 -0
- package/dist/components/msa/WheelZoomHint.js.map +1 -0
- package/dist/components/msa/msaKeys.js +3 -0
- package/dist/components/msa/msaKeys.js.map +1 -1
- package/dist/components/msa/msaRaster.d.ts +4 -5
- package/dist/components/msa/msaRaster.js +5 -5
- package/dist/components/msa/msaRaster.js.map +1 -1
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js +22 -2
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
- package/dist/components/msa/renderHighlights.d.ts +0 -3
- package/dist/components/msa/renderHighlights.js +3 -3
- package/dist/components/msa/renderHighlights.js.map +1 -1
- package/dist/components/msa/renderMSABlock.d.ts +6 -1
- package/dist/components/msa/renderMSABlock.js +44 -17
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/tileColor.js +7 -0
- package/dist/components/msa/tileColor.js.map +1 -1
- package/dist/components/rowpanels/RowPanelBlock.js +2 -2
- package/dist/components/rowpanels/RowPanelBlock.js.map +1 -1
- package/dist/components/settings/MenuItemsForm.d.ts +10 -0
- package/dist/components/settings/MenuItemsForm.js +80 -0
- package/dist/components/settings/MenuItemsForm.js.map +1 -0
- package/dist/components/settings/SettingsPanel.d.ts +12 -0
- package/dist/components/settings/SettingsPanel.js +92 -0
- package/dist/components/settings/SettingsPanel.js.map +1 -0
- package/dist/components/{header/FileMenu.d.ts → settings/TrackList.d.ts} +2 -2
- package/dist/components/settings/TrackList.js +55 -0
- package/dist/components/settings/TrackList.js.map +1 -0
- package/dist/components/tracks/TrackBlocks.js +2 -2
- package/dist/components/tracks/TrackBlocks.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +2 -2
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/TreeMenu.js +4 -6
- package/dist/components/tree/TreeMenu.js.map +1 -1
- package/dist/components/tree/TreeOverview.js +2 -2
- package/dist/components/tree/TreeOverview.js.map +1 -1
- package/dist/constants.d.ts +4 -1
- package/dist/constants.js +7 -1
- package/dist/constants.js.map +1 -1
- package/dist/darkReader.d.ts +7 -0
- package/dist/darkReader.js +42 -0
- package/dist/darkReader.js.map +1 -0
- package/dist/element.js +22 -47
- package/dist/element.js.map +1 -1
- package/dist/featurePanels.d.ts +37 -0
- package/dist/featurePanels.js +187 -0
- package/dist/featurePanels.js.map +1 -0
- package/dist/filehandleLoader.d.ts +41 -0
- package/dist/filehandleLoader.js +99 -0
- package/dist/filehandleLoader.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/model/treeModel.d.ts +9 -0
- package/dist/model/treeModel.js +12 -1
- package/dist/model/treeModel.js.map +1 -1
- package/dist/model.d.ts +142 -32
- package/dist/model.js +268 -569
- package/dist/model.js.map +1 -1
- package/dist/neighborJoining.js +1 -40
- package/dist/neighborJoining.js.map +1 -1
- package/dist/renderToSvg.js +1 -13
- package/dist/renderToSvg.js.map +1 -1
- package/dist/rerootTree.d.ts +1 -0
- package/dist/rerootTree.js +7 -6
- package/dist/rerootTree.js.map +1 -1
- package/dist/schemeResidueColor.d.ts +12 -0
- package/dist/schemeResidueColor.js +33 -0
- package/dist/schemeResidueColor.js.map +1 -0
- package/dist/similarityColors.d.ts +11 -0
- package/dist/similarityColors.js +90 -0
- package/dist/similarityColors.js.map +1 -0
- package/dist/svgTestUtil.d.ts +62 -12
- package/dist/treeDisplay.d.ts +37 -0
- package/dist/treeDisplay.js +209 -0
- package/dist/treeDisplay.js.map +1 -0
- package/dist/types.d.ts +6 -2
- package/dist/useWheelScroll.d.ts +3 -1
- package/dist/useWheelScroll.js +36 -17
- package/dist/useWheelScroll.js.map +1 -1
- package/dist/util.d.ts +2 -0
- package/dist/util.js +2 -0
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/wheelZoom.d.ts +8 -3
- package/dist/wheelZoom.js +44 -32
- package/dist/wheelZoom.js.map +1 -1
- package/dist/zoomSnapshot.d.ts +29 -0
- package/dist/zoomSnapshot.js +47 -0
- package/dist/zoomSnapshot.js.map +1 -0
- package/package.json +11 -11
- package/src/blosum62.ts +45 -0
- package/src/colorSchemes.ts +1 -0
- package/src/components/Loading.tsx +6 -2
- package/src/components/MSAView.tsx +22 -5
- package/src/components/MetadataTable.tsx +83 -26
- package/src/components/Track.tsx +13 -0
- package/src/components/controls.ts +2 -1
- package/src/components/dialogs/AnnotationFileDialog.tsx +12 -1
- package/src/components/dialogs/FeatureDialog.tsx +2 -5
- package/src/components/dialogs/GapThresholdDialog.tsx +36 -0
- package/src/components/{header → dialogs}/GappynessSlider.tsx +3 -10
- package/src/components/{header → dialogs}/GoToBox.tsx +36 -10
- package/src/components/dialogs/GoToDialog.tsx +30 -0
- package/src/components/dialogs/MetadataDialog.tsx +32 -4
- package/src/components/{header → dialogs}/goTo.ts +1 -1
- package/src/components/dialogs/metadataLabels.ts +54 -0
- package/src/components/dialogs/metadataRecord.tsx +61 -0
- package/src/components/header/Header.tsx +75 -42
- package/src/components/header/HiddenColumnsChip.tsx +39 -0
- package/src/components/header/MenuBar.tsx +64 -0
- package/src/components/header/ScrollZoomCheckbox.tsx +46 -0
- package/src/components/header/ZoomControls.tsx +20 -35
- package/src/components/header/colorSchemeMenuItems.ts +121 -0
- package/src/components/header/getAnnotationsMenu.ts +52 -56
- package/src/components/header/menuBarItems.ts +357 -0
- package/src/components/import/ImportForm.tsx +19 -7
- package/src/components/minimap/Minimap.tsx +11 -3
- package/src/components/minimap/MinimapSVG.tsx +17 -1
- package/src/components/minimap/minimapLayout.ts +27 -0
- package/src/components/msa/MSACanvas.tsx +22 -3
- package/src/components/msa/MSACanvasBlock.tsx +18 -11
- package/src/components/msa/MSAMouseoverCanvas.tsx +2 -2
- package/src/components/msa/WheelZoomHint.tsx +56 -0
- package/src/components/msa/msaKeys.ts +3 -0
- package/src/components/msa/msaRaster.ts +6 -8
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +28 -5
- package/src/components/msa/renderHighlights.ts +3 -3
- package/src/components/msa/renderMSABlock.ts +51 -15
- package/src/components/msa/tileColor.ts +7 -0
- package/src/components/rowpanels/RowPanelBlock.tsx +2 -2
- package/src/components/settings/MenuItemsForm.tsx +225 -0
- package/src/components/settings/SettingsPanel.tsx +156 -0
- package/src/components/settings/TrackList.tsx +119 -0
- package/src/components/tracks/TrackBlocks.tsx +2 -2
- package/src/components/tree/TreeCanvasBlock.tsx +2 -2
- package/src/components/tree/TreeMenu.tsx +4 -6
- package/src/components/tree/TreeOverview.tsx +2 -2
- package/src/constants.ts +9 -1
- package/src/darkReader.ts +57 -0
- package/src/element.ts +34 -57
- package/src/featurePanels.ts +269 -0
- package/src/filehandleLoader.ts +136 -0
- package/src/index.ts +4 -0
- package/src/model/treeModel.ts +13 -0
- package/src/model.ts +345 -748
- package/src/neighborJoining.ts +1 -42
- package/src/renderToSvg.tsx +1 -13
- package/src/rerootTree.ts +7 -6
- package/src/schemeResidueColor.ts +37 -0
- package/src/similarityColors.ts +112 -0
- package/src/treeDisplay.ts +278 -0
- package/src/types.ts +6 -3
- package/src/useWheelScroll.ts +43 -22
- package/src/util.ts +3 -0
- package/src/version.ts +1 -1
- package/src/wheelZoom.ts +55 -33
- package/src/zoomSnapshot.ts +80 -0
- package/dist/components/header/ColorSchemeMenu.d.ts +0 -12
- package/dist/components/header/ColorSchemeMenu.js +0 -37
- package/dist/components/header/ColorSchemeMenu.js.map +0 -1
- package/dist/components/header/FileMenu.js +0 -92
- package/dist/components/header/FileMenu.js.map +0 -1
- package/dist/components/header/GappynessSlider.js.map +0 -1
- package/dist/components/header/GoToBox.js +0 -31
- package/dist/components/header/GoToBox.js.map +0 -1
- package/dist/components/header/MSASettingsMenu.js +0 -11
- package/dist/components/header/MSASettingsMenu.js.map +0 -1
- package/dist/components/header/ScrollZoomIcons.d.ts +0 -6
- package/dist/components/header/ScrollZoomIcons.js +0 -35
- package/dist/components/header/ScrollZoomIcons.js.map +0 -1
- package/dist/components/header/TreeSettingsMenu.js +0 -11
- package/dist/components/header/TreeSettingsMenu.js.map +0 -1
- package/dist/components/header/goTo.d.ts +0 -20
- package/dist/components/header/goTo.js.map +0 -1
- package/dist/components/header/settingsMenuItems.d.ts +0 -5
- package/dist/components/header/settingsMenuItems.js +0 -171
- package/dist/components/header/settingsMenuItems.js.map +0 -1
- package/src/components/header/ColorSchemeMenu.tsx +0 -56
- package/src/components/header/FileMenu.tsx +0 -107
- package/src/components/header/MSASettingsMenu.tsx +0 -26
- package/src/components/header/ScrollZoomIcons.tsx +0 -52
- package/src/components/header/TreeSettingsMenu.tsx +0 -30
- package/src/components/header/settingsMenuItems.ts +0 -216
|
@@ -1,21 +1,57 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
3
|
import { makeStyles } from '@jbrowse/core/util/tss-react'
|
|
4
|
-
import { Table, TableBody, TableCell, TableRow } from '@mui/material'
|
|
5
4
|
|
|
6
|
-
const useStyles = makeStyles()({
|
|
5
|
+
const useStyles = makeStyles()(theme => ({
|
|
6
|
+
list: {
|
|
7
|
+
display: 'grid',
|
|
8
|
+
gridTemplateColumns: 'minmax(8em, max-content) minmax(0, 1fr)',
|
|
9
|
+
columnGap: theme.spacing(3),
|
|
10
|
+
margin: 0,
|
|
11
|
+
},
|
|
12
|
+
row: {
|
|
13
|
+
display: 'contents',
|
|
14
|
+
},
|
|
7
15
|
key: {
|
|
8
|
-
fontWeight:
|
|
9
|
-
|
|
16
|
+
fontWeight: 600,
|
|
17
|
+
padding: theme.spacing(0.75, 0),
|
|
18
|
+
borderTop: `1px solid ${theme.palette.divider}`,
|
|
19
|
+
},
|
|
20
|
+
code: {
|
|
21
|
+
marginLeft: theme.spacing(1),
|
|
22
|
+
fontWeight: 400,
|
|
23
|
+
fontSize: '0.75em',
|
|
24
|
+
fontFamily: 'monospace',
|
|
25
|
+
color: theme.palette.text.secondary,
|
|
10
26
|
},
|
|
11
27
|
value: {
|
|
28
|
+
margin: 0,
|
|
29
|
+
padding: theme.spacing(0.75, 0),
|
|
30
|
+
borderTop: `1px solid ${theme.palette.divider}`,
|
|
12
31
|
whiteSpace: 'pre-wrap',
|
|
13
|
-
|
|
32
|
+
overflowWrap: 'anywhere',
|
|
33
|
+
},
|
|
34
|
+
mono: {
|
|
35
|
+
fontFamily: 'monospace',
|
|
36
|
+
fontSize: '0.85em',
|
|
37
|
+
maxHeight: '8em',
|
|
38
|
+
overflow: 'auto',
|
|
14
39
|
},
|
|
15
|
-
|
|
40
|
+
nested: {
|
|
41
|
+
'& > dl': {
|
|
42
|
+
paddingLeft: theme.spacing(2),
|
|
43
|
+
borderLeft: `2px solid ${theme.palette.divider}`,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
}))
|
|
16
47
|
|
|
17
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
18
|
-
return
|
|
48
|
+
export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
49
|
+
return (
|
|
50
|
+
typeof value === 'object' &&
|
|
51
|
+
value !== null &&
|
|
52
|
+
!Array.isArray(value) &&
|
|
53
|
+
!React.isValidElement(value)
|
|
54
|
+
)
|
|
19
55
|
}
|
|
20
56
|
|
|
21
57
|
export function isEmpty(value: unknown) {
|
|
@@ -24,32 +60,53 @@ export function isEmpty(value: unknown) {
|
|
|
24
60
|
|
|
25
61
|
export default function MetadataTable({
|
|
26
62
|
record,
|
|
63
|
+
labels = {},
|
|
64
|
+
monospaceKeys,
|
|
27
65
|
}: {
|
|
28
66
|
record: Record<string, unknown>
|
|
67
|
+
labels?: Record<string, string>
|
|
68
|
+
monospaceKeys?: Set<string>
|
|
29
69
|
}) {
|
|
30
|
-
const { classes } = useStyles()
|
|
70
|
+
const { classes, cx } = useStyles()
|
|
31
71
|
return (
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
{
|
|
43
|
-
<
|
|
72
|
+
<dl className={classes.list}>
|
|
73
|
+
{Object.entries(record)
|
|
74
|
+
.filter(([, value]) => value != null && !isEmpty(value))
|
|
75
|
+
.map(([key, value]) => {
|
|
76
|
+
const label = labels[key]
|
|
77
|
+
const nested = isRecord(value)
|
|
78
|
+
return (
|
|
79
|
+
<div key={key} className={classes.row}>
|
|
80
|
+
<dt className={classes.key}>
|
|
81
|
+
{label ?? key}
|
|
82
|
+
{label && label !== key ? (
|
|
83
|
+
<span className={classes.code}>{key}</span>
|
|
84
|
+
) : null}
|
|
85
|
+
</dt>
|
|
86
|
+
<dd
|
|
87
|
+
className={cx(
|
|
88
|
+
classes.value,
|
|
89
|
+
nested && classes.nested,
|
|
90
|
+
monospaceKeys?.has(key) && classes.mono,
|
|
91
|
+
)}
|
|
92
|
+
>
|
|
93
|
+
{nested ? (
|
|
94
|
+
<MetadataTable
|
|
95
|
+
record={value}
|
|
96
|
+
labels={labels}
|
|
97
|
+
monospaceKeys={monospaceKeys}
|
|
98
|
+
/>
|
|
44
99
|
) : Array.isArray(value) ? (
|
|
45
100
|
value.join('\n')
|
|
101
|
+
) : React.isValidElement(value) ? (
|
|
102
|
+
value
|
|
46
103
|
) : (
|
|
47
104
|
String(value)
|
|
48
105
|
)}
|
|
49
|
-
</
|
|
50
|
-
</
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
</
|
|
106
|
+
</dd>
|
|
107
|
+
</div>
|
|
108
|
+
)
|
|
109
|
+
})}
|
|
110
|
+
</dl>
|
|
54
111
|
)
|
|
55
112
|
}
|
package/src/components/Track.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import React, { lazy, useCallback, useRef, useState } from 'react'
|
|
|
3
3
|
import BaseTooltip from '@jbrowse/core/ui/BaseTooltip'
|
|
4
4
|
import { makeStyles } from '@jbrowse/core/util/tss-react'
|
|
5
5
|
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'
|
|
6
|
+
import CloseIcon from '@mui/icons-material/Close'
|
|
6
7
|
import { IconButton, Menu, MenuItem } from '@mui/material'
|
|
7
8
|
import { observer } from 'mobx-react'
|
|
8
9
|
|
|
@@ -51,6 +52,7 @@ const TrackLabel = observer(function TrackLabel({
|
|
|
51
52
|
} = track
|
|
52
53
|
const { classes } = useStyles()
|
|
53
54
|
const trackLabelHeight = Math.min(height, fontSize)
|
|
55
|
+
const closeSize = Math.min(height, Math.max(fontSize, 16))
|
|
54
56
|
|
|
55
57
|
return (
|
|
56
58
|
// a flex row, not inline text: zooming in grows fontSize against a tree
|
|
@@ -78,6 +80,17 @@ const TrackLabel = observer(function TrackLabel({
|
|
|
78
80
|
>
|
|
79
81
|
{drawLabels ? name : ''}
|
|
80
82
|
</span>
|
|
83
|
+
<IconButton
|
|
84
|
+
className={classes.button}
|
|
85
|
+
aria-label={`Hide ${name} track`}
|
|
86
|
+
title="Hide track"
|
|
87
|
+
style={{ width: closeSize, height: closeSize, flexShrink: 0 }}
|
|
88
|
+
onClick={() => {
|
|
89
|
+
model.toggleTrack(track.model.id)
|
|
90
|
+
}}
|
|
91
|
+
>
|
|
92
|
+
<CloseIcon style={{ fontSize: closeSize * 0.8 }} />
|
|
93
|
+
</IconButton>
|
|
81
94
|
<IconButton
|
|
82
95
|
className={classes.button}
|
|
83
96
|
aria-label={`${name} track menu`}
|
|
@@ -19,6 +19,7 @@ export const keyboardControls: KeyControl[] = [
|
|
|
19
19
|
shiftKey: true,
|
|
20
20
|
},
|
|
21
21
|
{ label: '+ and -', action: 'Zoom in and out', keys: ['+', '=', '-'] },
|
|
22
|
+
{ label: '0', action: 'Reset to the default cell size', keys: ['0'] },
|
|
22
23
|
{
|
|
23
24
|
label: 'Home and End',
|
|
24
25
|
action: 'Jump to the first and last column',
|
|
@@ -36,7 +37,7 @@ export const mouseControls: Control[] = [
|
|
|
36
37
|
{ label: 'Drag', action: 'Pan the alignment' },
|
|
37
38
|
{
|
|
38
39
|
label: 'Wheel',
|
|
39
|
-
action: 'Scroll, or zoom
|
|
40
|
+
action: 'Scroll, or zoom with Zoom on scroll checked',
|
|
40
41
|
},
|
|
41
42
|
{ label: 'Ctrl + wheel', action: 'Zoom at the cursor' },
|
|
42
43
|
selectCells,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
DialogContent,
|
|
8
8
|
FormControl,
|
|
9
9
|
FormControlLabel,
|
|
10
|
+
Link,
|
|
10
11
|
Radio,
|
|
11
12
|
RadioGroup,
|
|
12
13
|
TextField,
|
|
@@ -17,6 +18,8 @@ import { annotationTextToGFF } from 'msa-parsers'
|
|
|
17
18
|
|
|
18
19
|
import type { MsaViewModel } from '../../model.ts'
|
|
19
20
|
|
|
21
|
+
const domainsTutorial = 'https://gmod.org/JBrowseMSA/tutorials/protein_family'
|
|
22
|
+
|
|
20
23
|
async function fetchText(url: string) {
|
|
21
24
|
const response = await fetch(url)
|
|
22
25
|
if (!response.ok) {
|
|
@@ -57,7 +60,14 @@ const AnnotationFileDialog = observer(function ({
|
|
|
57
60
|
{error ? <ErrorMessage error={error} /> : null}
|
|
58
61
|
<Typography>
|
|
59
62
|
Open a GFF3 file of annotations, such as the one `react-msaview-cli
|
|
60
|
-
interpro` writes, or the JSON an InterProScan run returns.
|
|
63
|
+
interpro` writes, or the JSON an InterProScan run returns.{' '}
|
|
64
|
+
<Link
|
|
65
|
+
href={domainsTutorial}
|
|
66
|
+
target="_blank"
|
|
67
|
+
rel="noopener noreferrer"
|
|
68
|
+
>
|
|
69
|
+
How to make one
|
|
70
|
+
</Link>
|
|
61
71
|
</Typography>
|
|
62
72
|
|
|
63
73
|
<div style={{ display: 'flex', margin: 30 }}>
|
|
@@ -126,6 +136,7 @@ const AnnotationFileDialog = observer(function ({
|
|
|
126
136
|
model.setGFFFilehandle(undefined)
|
|
127
137
|
model.setGFF(annotationTextToGFF(text))
|
|
128
138
|
model.setShowDomains(true)
|
|
139
|
+
model.setCellFill('domain')
|
|
129
140
|
handleClose()
|
|
130
141
|
} catch (e) {
|
|
131
142
|
console.error(e)
|
|
@@ -102,12 +102,9 @@ const FeatureTable = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
102
102
|
<TableRow key={accession}>
|
|
103
103
|
<TableCell padding="checkbox">
|
|
104
104
|
<Checkbox
|
|
105
|
-
checked={!model.
|
|
105
|
+
checked={!model.featureOff(accession)}
|
|
106
106
|
onChange={() => {
|
|
107
|
-
model.setFilter(
|
|
108
|
-
accession,
|
|
109
|
-
!!model.turnedOffFeatures.get(accession),
|
|
110
|
-
)
|
|
107
|
+
model.setFilter(accession, model.featureOff(accession))
|
|
111
108
|
}}
|
|
112
109
|
/>
|
|
113
110
|
</TableCell>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { Dialog } from '@jbrowse/core/ui'
|
|
4
|
+
import { Button, DialogActions, DialogContent, Typography } from '@mui/material'
|
|
5
|
+
import { observer } from 'mobx-react'
|
|
6
|
+
|
|
7
|
+
import GappynessSlider from './GappynessSlider.tsx'
|
|
8
|
+
|
|
9
|
+
import type { MsaViewModel } from '../../model.ts'
|
|
10
|
+
|
|
11
|
+
const GapThresholdDialog = observer(function ({
|
|
12
|
+
model,
|
|
13
|
+
onClose,
|
|
14
|
+
}: {
|
|
15
|
+
model: MsaViewModel
|
|
16
|
+
onClose: () => void
|
|
17
|
+
}) {
|
|
18
|
+
return (
|
|
19
|
+
<Dialog open onClose={onClose} title="Hide gappy columns">
|
|
20
|
+
<DialogContent>
|
|
21
|
+
<Typography variant="body2" sx={{ mb: 2 }}>
|
|
22
|
+
The viewer hides each column where at least this share of the rows has
|
|
23
|
+
a gap.
|
|
24
|
+
</Typography>
|
|
25
|
+
<GappynessSlider model={model} />
|
|
26
|
+
</DialogContent>
|
|
27
|
+
<DialogActions>
|
|
28
|
+
<Button variant="contained" onClick={onClose}>
|
|
29
|
+
Close
|
|
30
|
+
</Button>
|
|
31
|
+
</DialogActions>
|
|
32
|
+
</Dialog>
|
|
33
|
+
)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
export default GapThresholdDialog
|
|
@@ -10,19 +10,15 @@ const GappynessSlider = observer(function GappynessSlider({
|
|
|
10
10
|
}: {
|
|
11
11
|
model: MsaViewModel
|
|
12
12
|
}) {
|
|
13
|
-
const { hideGaps, allowedGappyness } = model
|
|
14
13
|
const [dragValue, setDragValue] = useState<number>()
|
|
15
|
-
const value = dragValue ?? allowedGappyness
|
|
16
|
-
if (!hideGaps) {
|
|
17
|
-
return null
|
|
18
|
-
}
|
|
14
|
+
const value = dragValue ?? model.allowedGappyness
|
|
19
15
|
return (
|
|
20
16
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
21
17
|
{/* the threshold is inclusive: blanks are columns whose gap count is at
|
|
22
18
|
least ceil(allowedGappyness% of the rows), so at 100 it hides the
|
|
23
19
|
columns that are entirely gaps rather than nothing */}
|
|
24
20
|
<Typography style={{ whiteSpace: 'nowrap' }}>
|
|
25
|
-
|
|
21
|
+
≥{value}% gaps
|
|
26
22
|
</Typography>
|
|
27
23
|
<Slider
|
|
28
24
|
// In JBrowse other MUI Sliders share the page, so `.MuiSlider-thumb`
|
|
@@ -36,11 +32,8 @@ const GappynessSlider = observer(function GappynessSlider({
|
|
|
36
32
|
// The thumb follows local state while dragging and the model takes the
|
|
37
33
|
// value on release, since each write recomputes the gap columns over
|
|
38
34
|
// the whole alignment.
|
|
39
|
-
//
|
|
40
|
-
// The thumb centers on the track's ends, so the margin keeps its
|
|
41
|
-
// overhang off the label and the row search beside it.
|
|
42
35
|
data-testid="gappyness_slider"
|
|
43
|
-
style={{ width:
|
|
36
|
+
style={{ width: 200, marginInline: 10 }}
|
|
44
37
|
min={1}
|
|
45
38
|
max={100}
|
|
46
39
|
value={value}
|
|
@@ -26,14 +26,32 @@ export function labelledInput(params: AutocompleteRenderInputParams) {
|
|
|
26
26
|
: { inputProps: { ...inputProps, 'aria-label': label } }
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
// the menu that opens the dialog hands focus back to its button as it closes,
|
|
30
|
+
// after an autoFocus input would have taken it
|
|
31
|
+
function focusNextFrame(el: HTMLInputElement | null) {
|
|
32
|
+
if (el) {
|
|
33
|
+
requestAnimationFrame(() => {
|
|
34
|
+
el.focus()
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function canGoTo(model: MsaViewModel) {
|
|
40
|
+
return findableRowNames(model).length > 1 || model.numColumns > 0
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const GoToBox = observer(function ({
|
|
44
|
+
model,
|
|
45
|
+
onFound,
|
|
46
|
+
}: {
|
|
47
|
+
model: MsaViewModel
|
|
48
|
+
onFound?: () => void
|
|
49
|
+
}) {
|
|
30
50
|
const [missed, setMissed] = useState(false)
|
|
31
|
-
|
|
32
|
-
return names.length > 1 || model.numColumns > 0 ? (
|
|
51
|
+
return (
|
|
33
52
|
<Autocomplete
|
|
34
53
|
freeSolo
|
|
35
|
-
|
|
36
|
-
options={names}
|
|
54
|
+
options={findableRowNames(model)}
|
|
37
55
|
filterOptions={(options, { inputValue }) =>
|
|
38
56
|
rowNameSuggestions(options, inputValue)
|
|
39
57
|
}
|
|
@@ -44,21 +62,29 @@ const GoToBox = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
44
62
|
}}
|
|
45
63
|
onChange={(_, text) => {
|
|
46
64
|
if (text) {
|
|
47
|
-
|
|
65
|
+
const found = goTo(model, text)
|
|
66
|
+
setMissed(!found)
|
|
67
|
+
if (found) {
|
|
68
|
+
onFound?.()
|
|
69
|
+
}
|
|
48
70
|
}
|
|
49
71
|
}}
|
|
50
|
-
style={{ width: 180, margin: 'auto 8px' }}
|
|
51
72
|
renderInput={params => (
|
|
52
73
|
<TextField
|
|
53
74
|
{...params}
|
|
75
|
+
inputRef={focusNextFrame}
|
|
54
76
|
error={missed}
|
|
55
|
-
placeholder=
|
|
56
|
-
|
|
77
|
+
placeholder="Row name or column number"
|
|
78
|
+
helperText={
|
|
79
|
+
missed
|
|
80
|
+
? 'No row or column matches'
|
|
81
|
+
: 'Or name:N for residue N of that row'
|
|
82
|
+
}
|
|
57
83
|
{...labelledInput(params)}
|
|
58
84
|
/>
|
|
59
85
|
)}
|
|
60
86
|
/>
|
|
61
|
-
)
|
|
87
|
+
)
|
|
62
88
|
})
|
|
63
89
|
|
|
64
90
|
export default GoToBox
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { Dialog } from '@jbrowse/core/ui'
|
|
4
|
+
import { DialogContent } from '@mui/material'
|
|
5
|
+
|
|
6
|
+
import GoToBox from './GoToBox.tsx'
|
|
7
|
+
|
|
8
|
+
import type { MsaViewModel } from '../../model.ts'
|
|
9
|
+
|
|
10
|
+
export default function GoToDialog({
|
|
11
|
+
model,
|
|
12
|
+
onClose,
|
|
13
|
+
}: {
|
|
14
|
+
model: MsaViewModel
|
|
15
|
+
onClose: () => void
|
|
16
|
+
}) {
|
|
17
|
+
return (
|
|
18
|
+
<Dialog
|
|
19
|
+
open
|
|
20
|
+
onClose={onClose}
|
|
21
|
+
title="Go to row or column"
|
|
22
|
+
maxWidth="xs"
|
|
23
|
+
fullWidth
|
|
24
|
+
>
|
|
25
|
+
<DialogContent>
|
|
26
|
+
<GoToBox model={model} onFound={onClose} />
|
|
27
|
+
</DialogContent>
|
|
28
|
+
</Dialog>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
@@ -4,8 +4,10 @@ import { Dialog } from '@jbrowse/core/ui'
|
|
|
4
4
|
import { DialogContent, Typography } from '@mui/material'
|
|
5
5
|
import { observer } from 'mobx-react'
|
|
6
6
|
|
|
7
|
-
import MetadataTable, { isEmpty } from '../MetadataTable.tsx'
|
|
7
|
+
import MetadataTable, { isEmpty, isRecord } from '../MetadataTable.tsx'
|
|
8
8
|
import SequenceTextArea from '../SequenceTextArea.tsx'
|
|
9
|
+
import { gfLabels, monospaceKeys, sectionLabels } from './metadataLabels.ts'
|
|
10
|
+
import { groupGeneral } from './metadataRecord.tsx'
|
|
9
11
|
|
|
10
12
|
import type { MsaViewModel } from '../../model.ts'
|
|
11
13
|
|
|
@@ -16,7 +18,13 @@ const MetadataDialog = observer(function ({
|
|
|
16
18
|
model: MsaViewModel
|
|
17
19
|
onClose: () => void
|
|
18
20
|
}) {
|
|
19
|
-
const
|
|
21
|
+
const entries = Object.entries(model.header).filter(
|
|
22
|
+
([, value]) => value != null && !isEmpty(value),
|
|
23
|
+
)
|
|
24
|
+
const fields = Object.fromEntries(
|
|
25
|
+
entries.filter(([, value]) => !isRecord(value)),
|
|
26
|
+
)
|
|
27
|
+
const sections = entries.filter(([, value]) => isRecord(value))
|
|
20
28
|
return (
|
|
21
29
|
<Dialog
|
|
22
30
|
onClose={() => {
|
|
@@ -27,10 +35,30 @@ const MetadataDialog = observer(function ({
|
|
|
27
35
|
maxWidth="xl"
|
|
28
36
|
>
|
|
29
37
|
<DialogContent>
|
|
30
|
-
{
|
|
38
|
+
{entries.length === 0 ? (
|
|
31
39
|
<Typography>This alignment has no metadata.</Typography>
|
|
32
40
|
) : (
|
|
33
|
-
|
|
41
|
+
<>
|
|
42
|
+
{Object.keys(fields).length > 0 ? (
|
|
43
|
+
<MetadataTable record={fields} />
|
|
44
|
+
) : null}
|
|
45
|
+
{sections.map(([section, value]) => (
|
|
46
|
+
<div key={section}>
|
|
47
|
+
<Typography variant="h6">
|
|
48
|
+
{sectionLabels[section] ?? section}
|
|
49
|
+
</Typography>
|
|
50
|
+
{section === 'General' ? (
|
|
51
|
+
<MetadataTable
|
|
52
|
+
record={groupGeneral(value)}
|
|
53
|
+
labels={gfLabels}
|
|
54
|
+
monospaceKeys={monospaceKeys}
|
|
55
|
+
/>
|
|
56
|
+
) : (
|
|
57
|
+
<MetadataTable record={value as Record<string, unknown>} />
|
|
58
|
+
)}
|
|
59
|
+
</div>
|
|
60
|
+
))}
|
|
61
|
+
</>
|
|
34
62
|
)}
|
|
35
63
|
<Typography variant="h6">Sequences</Typography>
|
|
36
64
|
<SequenceTextArea str={model.rows} />
|
|
@@ -21,7 +21,7 @@ export function rowNameSuggestions(names: string[], input: string, limit = 50) {
|
|
|
21
21
|
* column the ruler numbers N, or residue N of a row written `name:N`. A name
|
|
22
22
|
* is tried whole first, so a row called `chr1:100` stays reachable.
|
|
23
23
|
*/
|
|
24
|
-
|
|
24
|
+
function resolveGoTo(model: MsaViewModel, text: string) {
|
|
25
25
|
const query = text.trim()
|
|
26
26
|
const row = model.rowNamesSet.get(query)
|
|
27
27
|
if (row !== undefined) {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export const sectionLabels: Record<string, string> = {
|
|
2
|
+
General: 'Alignment',
|
|
3
|
+
Accessions: 'Row accessions',
|
|
4
|
+
Dbxref: 'Row cross-references',
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const gfLabels: Record<string, string> = {
|
|
8
|
+
ID: 'Identifier',
|
|
9
|
+
AC: 'Accession',
|
|
10
|
+
DE: 'Description',
|
|
11
|
+
AU: 'Authors',
|
|
12
|
+
SE: 'Source of seed',
|
|
13
|
+
SS: 'Source of structure',
|
|
14
|
+
BM: 'Build method',
|
|
15
|
+
SM: 'Search method',
|
|
16
|
+
GA: 'Gathering threshold',
|
|
17
|
+
TC: 'Trusted cutoff',
|
|
18
|
+
NC: 'Noise cutoff',
|
|
19
|
+
TP: 'Family type',
|
|
20
|
+
CL: 'Clan',
|
|
21
|
+
MB: 'Clan members',
|
|
22
|
+
PI: 'Previous identifiers',
|
|
23
|
+
DR: 'Database references',
|
|
24
|
+
CC: 'Comment',
|
|
25
|
+
WK: 'Wikipedia',
|
|
26
|
+
NE: 'Pfam family nested in this one',
|
|
27
|
+
NL: 'Nesting location',
|
|
28
|
+
NH: 'Tree (Newick)',
|
|
29
|
+
SQ: 'Number of sequences',
|
|
30
|
+
RC: 'Reference comment',
|
|
31
|
+
RN: 'Reference number',
|
|
32
|
+
RM: 'PubMed ID',
|
|
33
|
+
RT: 'Reference title',
|
|
34
|
+
RA: 'Reference authors',
|
|
35
|
+
RL: 'Reference journal',
|
|
36
|
+
CB: 'Curation: build',
|
|
37
|
+
DC: 'Database comment',
|
|
38
|
+
DW: 'Database web link',
|
|
39
|
+
OS: 'Organism',
|
|
40
|
+
OC: 'Organism classification',
|
|
41
|
+
AM: 'Alignment method',
|
|
42
|
+
FT: 'Feature',
|
|
43
|
+
SO: 'Sequence Ontology term',
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const referenceFieldLabels: Record<string, string> = {
|
|
47
|
+
RT: 'Title',
|
|
48
|
+
RA: 'Authors',
|
|
49
|
+
RL: 'Journal',
|
|
50
|
+
RM: 'PubMed',
|
|
51
|
+
RC: 'Comment',
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const monospaceKeys = new Set(['NH'])
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { referenceFieldLabels } from './metadataLabels.ts'
|
|
4
|
+
|
|
5
|
+
const referenceTags = ['RM', 'RT', 'RA', 'RL', 'RC'] as const
|
|
6
|
+
|
|
7
|
+
function pubmed(id: string) {
|
|
8
|
+
return /^\d+$/.test(id) ? (
|
|
9
|
+
<a
|
|
10
|
+
href={`https://pubmed.ncbi.nlm.nih.gov/${id}/`}
|
|
11
|
+
target="_blank"
|
|
12
|
+
rel="noreferrer"
|
|
13
|
+
>
|
|
14
|
+
{id}
|
|
15
|
+
</a>
|
|
16
|
+
) : (
|
|
17
|
+
id
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function groupReferences(general: Record<string, unknown>) {
|
|
22
|
+
const numbers = general.RN
|
|
23
|
+
if (!Array.isArray(numbers) || numbers.length === 0) {
|
|
24
|
+
return general
|
|
25
|
+
}
|
|
26
|
+
const present = referenceTags.filter(tag => general[tag] != null)
|
|
27
|
+
const aligned = present.every(tag => {
|
|
28
|
+
const values = general[tag]
|
|
29
|
+
return Array.isArray(values) && values.length === numbers.length
|
|
30
|
+
})
|
|
31
|
+
if (!aligned) {
|
|
32
|
+
return general
|
|
33
|
+
}
|
|
34
|
+
const rest = Object.fromEntries(
|
|
35
|
+
Object.entries(general).filter(
|
|
36
|
+
([tag]) =>
|
|
37
|
+
tag !== 'RN' && !(referenceTags as readonly string[]).includes(tag),
|
|
38
|
+
),
|
|
39
|
+
)
|
|
40
|
+
const references = Object.fromEntries(
|
|
41
|
+
numbers.map((number: string, i) => [
|
|
42
|
+
number,
|
|
43
|
+
Object.fromEntries(
|
|
44
|
+
present.map(tag => {
|
|
45
|
+
const value = (general[tag] as string[])[i]!
|
|
46
|
+
return [
|
|
47
|
+
referenceFieldLabels[tag],
|
|
48
|
+
tag === 'RM' ? pubmed(value) : value,
|
|
49
|
+
]
|
|
50
|
+
}),
|
|
51
|
+
),
|
|
52
|
+
]),
|
|
53
|
+
)
|
|
54
|
+
return { ...rest, References: references }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function groupGeneral(general: unknown) {
|
|
58
|
+
return general && typeof general === 'object'
|
|
59
|
+
? groupReferences(general as Record<string, unknown>)
|
|
60
|
+
: {}
|
|
61
|
+
}
|