@apollo-annotation/jbrowse-plugin-apollo 0.3.4 → 0.3.6
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/dist/index.esm.js +5466 -4490
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +5283 -4318
- package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.development.js +6806 -4088
- package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
- package/package.json +4 -4
- package/src/ApolloInternetAccount/addMenuItems.ts +5 -2
- package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +1 -0
- package/src/ApolloInternetAccount/components/LoginButtons.tsx +1 -1
- package/src/ApolloInternetAccount/components/LoginIcons.tsx +1 -1
- package/src/ApolloInternetAccount/configSchema.ts +1 -1
- package/src/ApolloInternetAccount/model.ts +11 -10
- package/src/ApolloJobModel.ts +1 -1
- package/src/ApolloRefNameAliasAdapter/ApolloRefNameAliasAdapter.ts +8 -6
- package/src/ApolloRefNameAliasAdapter/index.ts +2 -2
- package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +17 -4
- package/src/ApolloSequenceAdapter/index.ts +1 -1
- package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +8 -7
- package/src/ApolloTextSearchAdapter/index.ts +1 -1
- package/src/BackendDrivers/BackendDriver.ts +7 -7
- package/src/BackendDrivers/CollaborationServerDriver.ts +14 -10
- package/src/BackendDrivers/DesktopFileDriver.ts +11 -10
- package/src/BackendDrivers/InMemoryFileDriver.ts +10 -6
- package/src/ChangeManager.ts +5 -5
- package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +92 -20
- package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +170 -27
- package/src/FeatureDetailsWidget/AttributeKey.tsx +50 -0
- package/src/FeatureDetailsWidget/AttributeKeySelector.tsx +104 -0
- package/src/FeatureDetailsWidget/Attributes.tsx +213 -320
- package/src/FeatureDetailsWidget/BasicInformation.tsx +8 -9
- package/src/FeatureDetailsWidget/DefaultAttributeEditor.tsx +104 -0
- package/src/FeatureDetailsWidget/DefaultAttributeViewer.tsx +22 -0
- package/src/FeatureDetailsWidget/FeatureDetailsNavigation.tsx +10 -8
- package/src/FeatureDetailsWidget/NumberTextField.tsx +1 -1
- package/src/FeatureDetailsWidget/Sequence.tsx +18 -35
- package/src/FeatureDetailsWidget/StringTextField.tsx +1 -1
- package/src/FeatureDetailsWidget/TranscriptSequence.tsx +140 -95
- package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +600 -0
- package/src/FeatureDetailsWidget/TranscriptWidgetSummary.tsx +54 -0
- package/src/FeatureDetailsWidget/model.ts +8 -3
- package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +19 -12
- package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +19 -41
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +44 -22
- package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +6 -5
- package/src/LinearApolloDisplay/glyphs/Glyph.ts +7 -7
- package/src/LinearApolloDisplay/stateModel/base.ts +52 -10
- package/src/LinearApolloDisplay/stateModel/index.ts +4 -3
- package/src/LinearApolloDisplay/stateModel/layouts.ts +8 -34
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +13 -12
- package/src/LinearApolloDisplay/stateModel/rendering.ts +63 -31
- package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +221 -0
- package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +40 -0
- package/src/LinearApolloSixFrameDisplay/components/index.ts +2 -0
- package/src/LinearApolloSixFrameDisplay/configSchema.ts +7 -0
- package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +821 -0
- package/src/LinearApolloSixFrameDisplay/glyphs/Glyph.ts +63 -0
- package/src/LinearApolloSixFrameDisplay/glyphs/index.ts +1 -0
- package/src/LinearApolloSixFrameDisplay/index.ts +2 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +261 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/index.ts +27 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +236 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +349 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +199 -0
- package/src/LinearApolloSixFrameDisplay/types.ts +1 -0
- package/src/OntologyManager/OntologyStore/fulltext-stopwords.ts +10 -1
- package/src/OntologyManager/OntologyStore/fulltext.test.ts +1 -1
- package/src/OntologyManager/OntologyStore/fulltext.ts +8 -3
- package/src/OntologyManager/OntologyStore/index.test.ts +4 -1
- package/src/OntologyManager/OntologyStore/index.ts +19 -14
- package/src/OntologyManager/OntologyStore/indexeddb-schema.ts +6 -5
- package/src/OntologyManager/OntologyStore/indexeddb-storage.ts +11 -5
- package/src/OntologyManager/index.ts +10 -6
- package/src/OntologyManager/util.ts +3 -2
- package/src/TabularEditor/HybridGrid/ChangeHandling.ts +2 -2
- package/src/TabularEditor/HybridGrid/Feature.tsx +9 -8
- package/src/TabularEditor/HybridGrid/FeatureAttributes.tsx +1 -1
- package/src/TabularEditor/HybridGrid/HybridGrid.tsx +3 -2
- package/src/TabularEditor/HybridGrid/NumberCell.tsx +8 -1
- package/src/TabularEditor/HybridGrid/ToolBar.tsx +15 -13
- package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +9 -33
- package/src/TabularEditor/TabularEditorPane.tsx +1 -1
- package/src/TabularEditor/model.ts +2 -2
- package/src/TabularEditor/types.ts +5 -2
- package/src/components/AddAssembly.tsx +611 -291
- package/src/components/AddChildFeature.tsx +6 -5
- package/src/components/AddFeature.tsx +211 -38
- package/src/components/AddRefSeqAliases.tsx +14 -12
- package/src/components/CopyFeature.tsx +8 -7
- package/src/components/CreateApolloAnnotation.tsx +154 -46
- package/src/components/DeleteAssembly.tsx +9 -8
- package/src/components/DeleteFeature.tsx +5 -4
- package/src/components/Dialog.tsx +1 -1
- package/src/components/DownloadGFF3.tsx +11 -10
- package/src/components/FilterFeatures.tsx +6 -4
- package/src/components/ImportFeatures.tsx +7 -6
- package/src/components/LogOut.tsx +5 -4
- package/src/components/ManageChecks.tsx +9 -8
- package/src/components/ManageUsers.tsx +11 -10
- package/src/components/OntologyTermAutocomplete.tsx +5 -5
- package/src/components/OntologyTermMultiSelect.tsx +9 -6
- package/src/components/OpenLocalFile.tsx +4 -3
- package/src/components/ViewChangeLog.tsx +7 -6
- package/src/components/ViewCheckResults.tsx +8 -7
- package/src/components/index.ts +0 -1
- package/src/extensions/annotationFromJBrowseFeature.test.ts +1 -0
- package/src/extensions/annotationFromJBrowseFeature.ts +14 -12
- package/src/extensions/annotationFromPileup.ts +6 -6
- package/src/index.ts +33 -50
- package/src/makeDisplayComponent.tsx +93 -41
- package/src/session/ClientDataStore.ts +21 -17
- package/src/session/session.ts +20 -26
- package/src/types.ts +4 -4
- package/src/util/annotationFeatureUtils.ts +53 -0
- package/src/util/index.ts +4 -3
- package/src/util/loadAssemblyIntoClient.ts +10 -3
- package/src/ApolloSixFrameRenderer/ApolloSixFrameRenderer.tsx +0 -13
- package/src/ApolloSixFrameRenderer/components/ApolloRendering.tsx +0 -707
- package/src/ApolloSixFrameRenderer/configSchema.ts +0 -7
- package/src/ApolloSixFrameRenderer/index.ts +0 -3
- package/src/FeatureDetailsWidget/TranscriptBasic.tsx +0 -200
- package/src/SixFrameFeatureDisplay/components/TrackLines.tsx +0 -19
- package/src/SixFrameFeatureDisplay/components/index.ts +0 -1
- package/src/SixFrameFeatureDisplay/configSchema.ts +0 -21
- package/src/SixFrameFeatureDisplay/index.ts +0 -2
- package/src/SixFrameFeatureDisplay/stateModel.ts +0 -439
- package/src/components/ModifyFeatureAttribute.tsx +0 -460
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import AddBoxIcon from '@mui/icons-material/AddBox'
|
|
2
|
+
import DeleteIcon from '@mui/icons-material/Delete'
|
|
3
|
+
import { Button, DialogActions, IconButton } from '@mui/material'
|
|
4
|
+
import { observer } from 'mobx-react'
|
|
5
|
+
import React, { useState } from 'react'
|
|
6
|
+
|
|
7
|
+
import { type ApolloSessionModel } from '../session'
|
|
8
|
+
|
|
9
|
+
import { StringTextField } from './StringTextField'
|
|
10
|
+
|
|
11
|
+
export interface AttributeEditorProps {
|
|
12
|
+
session: ApolloSessionModel
|
|
13
|
+
attributeValues?: string[]
|
|
14
|
+
setAttribute: (newAttribute?: string[]) => void
|
|
15
|
+
isNew?: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const DefaultAttributeEditor = observer(function DefaultAttributeEditor({
|
|
19
|
+
attributeValues,
|
|
20
|
+
setAttribute,
|
|
21
|
+
isNew = false,
|
|
22
|
+
}: AttributeEditorProps) {
|
|
23
|
+
const [newValues, setNewValues] = useState<string[]>(
|
|
24
|
+
attributeValues && attributeValues.length > 0 ? attributeValues : [''],
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
function updateValue(idx: number, newValue: string) {
|
|
28
|
+
setNewValues((oldValues) => {
|
|
29
|
+
const newValues = [...oldValues]
|
|
30
|
+
newValues[idx] = newValue
|
|
31
|
+
return newValues
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
function deleteValue(idx: number) {
|
|
35
|
+
setNewValues((oldValues) => {
|
|
36
|
+
const newValues = [...oldValues]
|
|
37
|
+
newValues.splice(idx, 1)
|
|
38
|
+
return newValues
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
function addValue() {
|
|
42
|
+
setNewValues((oldValues) => {
|
|
43
|
+
const newValues = [...oldValues]
|
|
44
|
+
newValues.push('')
|
|
45
|
+
return newValues
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<>
|
|
51
|
+
{newValues.map((value, idx) => (
|
|
52
|
+
<div key={`${idx}-${value}`} style={{ display: 'flex' }}>
|
|
53
|
+
<StringTextField
|
|
54
|
+
value={value}
|
|
55
|
+
onChangeCommitted={(editedValue) => {
|
|
56
|
+
updateValue(idx, editedValue)
|
|
57
|
+
}}
|
|
58
|
+
variant="outlined"
|
|
59
|
+
fullWidth
|
|
60
|
+
/>
|
|
61
|
+
<IconButton
|
|
62
|
+
aria-label="delete"
|
|
63
|
+
size="medium"
|
|
64
|
+
edge="end"
|
|
65
|
+
onClick={() => {
|
|
66
|
+
deleteValue(idx)
|
|
67
|
+
}}
|
|
68
|
+
>
|
|
69
|
+
<DeleteIcon fontSize="inherit" />
|
|
70
|
+
</IconButton>
|
|
71
|
+
</div>
|
|
72
|
+
))}
|
|
73
|
+
<IconButton
|
|
74
|
+
aria-label="add"
|
|
75
|
+
size="medium"
|
|
76
|
+
color="secondary"
|
|
77
|
+
edge="start"
|
|
78
|
+
onClick={addValue}
|
|
79
|
+
>
|
|
80
|
+
<AddBoxIcon fontSize="inherit" />
|
|
81
|
+
</IconButton>
|
|
82
|
+
<DialogActions>
|
|
83
|
+
<Button
|
|
84
|
+
color="primary"
|
|
85
|
+
variant="contained"
|
|
86
|
+
onClick={() => {
|
|
87
|
+
setAttribute(newValues.filter(Boolean))
|
|
88
|
+
}}
|
|
89
|
+
>
|
|
90
|
+
{isNew ? 'Add' : 'Update'}
|
|
91
|
+
</Button>
|
|
92
|
+
<Button
|
|
93
|
+
variant="outlined"
|
|
94
|
+
type="submit"
|
|
95
|
+
onClick={() => {
|
|
96
|
+
setAttribute()
|
|
97
|
+
}}
|
|
98
|
+
>
|
|
99
|
+
Cancel
|
|
100
|
+
</Button>
|
|
101
|
+
</DialogActions>
|
|
102
|
+
</>
|
|
103
|
+
)
|
|
104
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Typography } from '@mui/material'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
export interface AttributeViewerProps {
|
|
5
|
+
values: string[] | undefined
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function DefaultAttributeViewer({ values }: AttributeViewerProps) {
|
|
9
|
+
return (
|
|
10
|
+
<>
|
|
11
|
+
{values?.map((value, idx) => (
|
|
12
|
+
<Typography
|
|
13
|
+
key={`${idx}.${value}`}
|
|
14
|
+
variant="body2"
|
|
15
|
+
color="textSecondary"
|
|
16
|
+
>
|
|
17
|
+
{value}
|
|
18
|
+
</Typography>
|
|
19
|
+
))}
|
|
20
|
+
</>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { type AnnotationFeature } from '@apollo-annotation/mst'
|
|
3
2
|
import { Button, Typography } from '@mui/material'
|
|
4
3
|
import { observer } from 'mobx-react'
|
|
4
|
+
import React from 'react'
|
|
5
|
+
|
|
6
|
+
import { getFeatureNameOrId } from '../util'
|
|
5
7
|
|
|
6
|
-
import {
|
|
7
|
-
import { ApolloFeatureDetailsWidget as ApolloFeatureDetails } from './model'
|
|
8
|
+
import { type ApolloFeatureDetailsWidget as ApolloFeatureDetails } from './model'
|
|
8
9
|
|
|
9
10
|
export const FeatureDetailsNavigation = observer(
|
|
10
11
|
function FeatureDetailsNavigation(props: {
|
|
@@ -25,8 +26,7 @@ export const FeatureDetailsNavigation = observer(
|
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
return (
|
|
28
|
-
<div>
|
|
29
|
-
<Typography variant="h5">Go to related feature</Typography>
|
|
29
|
+
<div style={{ marginTop: 10 }}>
|
|
30
30
|
{parent && (
|
|
31
31
|
<div>
|
|
32
32
|
<Typography variant="h6">Parent:</Typography>
|
|
@@ -36,7 +36,8 @@ export const FeatureDetailsNavigation = observer(
|
|
|
36
36
|
model.setFeature(parent)
|
|
37
37
|
}}
|
|
38
38
|
>
|
|
39
|
-
{parent.type}
|
|
39
|
+
{parent.type}
|
|
40
|
+
{getFeatureNameOrId(parent)} ({parent.min}..{parent.max})
|
|
40
41
|
</Button>
|
|
41
42
|
</div>
|
|
42
43
|
)}
|
|
@@ -53,7 +54,8 @@ export const FeatureDetailsNavigation = observer(
|
|
|
53
54
|
model.setFeature(child)
|
|
54
55
|
}}
|
|
55
56
|
>
|
|
56
|
-
{child.type}
|
|
57
|
+
{child.type}
|
|
58
|
+
{getFeatureNameOrId(child)} ({child.min}..{child.max})
|
|
57
59
|
</Button>
|
|
58
60
|
</div>
|
|
59
61
|
))}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
3
|
-
import { TextField, TextFieldProps } from '@mui/material'
|
|
3
|
+
import { TextField, type TextFieldProps } from '@mui/material'
|
|
4
4
|
import { observer } from 'mobx-react'
|
|
5
5
|
import React, { useEffect, useState } from 'react'
|
|
6
6
|
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
2
|
-
import { AnnotationFeature } from '@apollo-annotation/mst'
|
|
2
|
+
import { type AnnotationFeature } from '@apollo-annotation/mst'
|
|
3
3
|
import { splitStringIntoChunks } from '@apollo-annotation/shared'
|
|
4
|
-
import { Button, Typography } from '@mui/material'
|
|
5
4
|
import { observer } from 'mobx-react'
|
|
6
|
-
import React
|
|
5
|
+
import React from 'react'
|
|
7
6
|
import { makeStyles } from 'tss-react/mui'
|
|
8
7
|
|
|
9
|
-
import { ApolloSessionModel } from '../session'
|
|
8
|
+
import { type ApolloSessionModel } from '../session'
|
|
10
9
|
|
|
11
10
|
function formatSequence(
|
|
12
11
|
seq: string,
|
|
@@ -40,13 +39,8 @@ export const Sequence = observer(function Sequence({
|
|
|
40
39
|
session: ApolloSessionModel
|
|
41
40
|
}) {
|
|
42
41
|
const currentAssembly = session.apolloDataStore.assemblies.get(assembly)
|
|
43
|
-
const [showSequence, setShowSequence] = useState(false)
|
|
44
42
|
const { classes } = useStyles()
|
|
45
43
|
|
|
46
|
-
const onButtonClick = () => {
|
|
47
|
-
setShowSequence(!showSequence)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
44
|
if (!(feature && currentAssembly)) {
|
|
51
45
|
return null
|
|
52
46
|
}
|
|
@@ -55,35 +49,24 @@ export const Sequence = observer(function Sequence({
|
|
|
55
49
|
return null
|
|
56
50
|
}
|
|
57
51
|
const { max, min } = feature
|
|
58
|
-
let sequence =
|
|
59
|
-
if (
|
|
60
|
-
sequence =
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
{ assemblyName: assembly, refName, start: min, end: max },
|
|
66
|
-
])
|
|
67
|
-
}
|
|
52
|
+
let sequence = refSeq.getSequence(min, max)
|
|
53
|
+
if (sequence) {
|
|
54
|
+
sequence = formatSequence(sequence, refName, min, max)
|
|
55
|
+
} else {
|
|
56
|
+
void session.apolloDataStore.loadRefSeq([
|
|
57
|
+
{ assemblyName: assembly, refName, start: min, end: max },
|
|
58
|
+
])
|
|
68
59
|
}
|
|
69
60
|
|
|
70
61
|
return (
|
|
71
|
-
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
{
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
readOnly
|
|
80
|
-
rows={20}
|
|
81
|
-
className={classes.sequence}
|
|
82
|
-
value={sequence}
|
|
83
|
-
/>
|
|
84
|
-
)}
|
|
85
|
-
</div>
|
|
86
|
-
</>
|
|
62
|
+
<div>
|
|
63
|
+
<textarea
|
|
64
|
+
readOnly
|
|
65
|
+
rows={20}
|
|
66
|
+
className={classes.sequence}
|
|
67
|
+
value={sequence}
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
87
70
|
)
|
|
88
71
|
})
|
|
89
72
|
export default Sequence
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
3
|
-
import { TextField, TextFieldProps } from '@mui/material'
|
|
3
|
+
import { TextField, type TextFieldProps } from '@mui/material'
|
|
4
4
|
import { observer } from 'mobx-react'
|
|
5
5
|
import React, { useEffect, useState } from 'react'
|
|
6
6
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnnotationFeature } from '@apollo-annotation/mst'
|
|
1
|
+
import { type AnnotationFeature } from '@apollo-annotation/mst'
|
|
2
2
|
import { splitStringIntoChunks } from '@apollo-annotation/shared'
|
|
3
3
|
import { defaultCodonTable, revcom } from '@jbrowse/core/util'
|
|
4
4
|
import {
|
|
@@ -6,18 +6,23 @@ import {
|
|
|
6
6
|
MenuItem,
|
|
7
7
|
Paper,
|
|
8
8
|
Select,
|
|
9
|
-
SelectChangeEvent,
|
|
10
|
-
Typography,
|
|
9
|
+
type SelectChangeEvent,
|
|
11
10
|
useTheme,
|
|
12
11
|
} from '@mui/material'
|
|
13
12
|
import { observer } from 'mobx-react'
|
|
14
|
-
import React, { useRef, useState } from 'react'
|
|
13
|
+
import React, { useEffect, useRef, useState } from 'react'
|
|
15
14
|
|
|
16
|
-
import { ApolloSessionModel } from '../session'
|
|
15
|
+
import { type ApolloSessionModel } from '../session'
|
|
17
16
|
|
|
18
17
|
const SEQUENCE_WRAP_LENGTH = 60
|
|
19
18
|
|
|
20
|
-
type SegmentType =
|
|
19
|
+
type SegmentType =
|
|
20
|
+
| 'upOrDownstream'
|
|
21
|
+
| 'UTR'
|
|
22
|
+
| 'CDS'
|
|
23
|
+
| 'intron'
|
|
24
|
+
| 'protein'
|
|
25
|
+
| 'exon'
|
|
21
26
|
type SegmentListType = 'CDS' | 'cDNA' | 'genomic' | 'protein'
|
|
22
27
|
|
|
23
28
|
interface SequenceSegment {
|
|
@@ -151,6 +156,7 @@ function getSegmentColor(type: SegmentType) {
|
|
|
151
156
|
case 'upOrDownstream': {
|
|
152
157
|
return 'rgb(255,255,255)'
|
|
153
158
|
}
|
|
159
|
+
case 'exon':
|
|
154
160
|
case 'UTR': {
|
|
155
161
|
return 'rgb(194,106,119)'
|
|
156
162
|
}
|
|
@@ -166,6 +172,25 @@ function getSegmentColor(type: SegmentType) {
|
|
|
166
172
|
}
|
|
167
173
|
}
|
|
168
174
|
|
|
175
|
+
function getLocationIntervals(seqSegments: SequenceSegment[]) {
|
|
176
|
+
const locIntervals: { min: number; max: number }[] = []
|
|
177
|
+
const allLocs = seqSegments.flatMap((segment) => segment.locs)
|
|
178
|
+
let [previous] = allLocs
|
|
179
|
+
for (let i = 1; i < allLocs.length; i++) {
|
|
180
|
+
if (previous.min === allLocs[i].max || previous.max === allLocs[i].min) {
|
|
181
|
+
previous = {
|
|
182
|
+
min: Math.min(previous.min, allLocs[i].min),
|
|
183
|
+
max: Math.max(previous.max, allLocs[i].max),
|
|
184
|
+
}
|
|
185
|
+
} else {
|
|
186
|
+
locIntervals.push(previous)
|
|
187
|
+
previous = allLocs[i]
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
locIntervals.push(previous)
|
|
191
|
+
return locIntervals
|
|
192
|
+
}
|
|
193
|
+
|
|
169
194
|
export const TranscriptSequence = observer(function TranscriptSequence({
|
|
170
195
|
assembly,
|
|
171
196
|
feature,
|
|
@@ -179,11 +204,46 @@ export const TranscriptSequence = observer(function TranscriptSequence({
|
|
|
179
204
|
}) {
|
|
180
205
|
const currentAssembly = session.apolloDataStore.assemblies.get(assembly)
|
|
181
206
|
const refData = currentAssembly?.getByRefName(refName)
|
|
182
|
-
const
|
|
183
|
-
|
|
207
|
+
const { featureTypeOntology } = session.apolloDataStore.ontologyManager
|
|
208
|
+
|
|
209
|
+
const defaultSelectedOption: SegmentListType = 'genomic'
|
|
210
|
+
const defaultSequenceOptions: SegmentListType[] = ['genomic', 'cDNA']
|
|
211
|
+
const [sequenceOptions, setSequenceOptions] = useState<SegmentListType[]>(
|
|
212
|
+
defaultSequenceOptions,
|
|
213
|
+
)
|
|
214
|
+
const [selectedOption, setSelectedOption] = useState<SegmentListType>(
|
|
215
|
+
defaultSelectedOption,
|
|
216
|
+
)
|
|
217
|
+
const [sequenceSegments, setSequenceSegments] = useState<SequenceSegment[]>(
|
|
218
|
+
() => {
|
|
219
|
+
return refData
|
|
220
|
+
? getSequenceSegments(
|
|
221
|
+
defaultSelectedOption,
|
|
222
|
+
feature,
|
|
223
|
+
(min: number, max: number) => refData.getSequence(min, max),
|
|
224
|
+
)
|
|
225
|
+
: []
|
|
226
|
+
},
|
|
227
|
+
)
|
|
228
|
+
const [locationIntervals, setLocationIntervals] = useState<
|
|
229
|
+
{ min: number; max: number }[]
|
|
230
|
+
>(() => {
|
|
231
|
+
return getLocationIntervals(sequenceSegments)
|
|
232
|
+
})
|
|
184
233
|
const theme = useTheme()
|
|
185
234
|
const seqRef = useRef<HTMLDivElement>(null)
|
|
186
235
|
|
|
236
|
+
useEffect(() => {
|
|
237
|
+
const { cdsLocations } = feature
|
|
238
|
+
const [firstLocation] = cdsLocations
|
|
239
|
+
if (firstLocation.length > 0) {
|
|
240
|
+
setSequenceOptions([...defaultSequenceOptions, 'CDS', 'protein'])
|
|
241
|
+
} else {
|
|
242
|
+
setSequenceOptions(defaultSequenceOptions)
|
|
243
|
+
}
|
|
244
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
245
|
+
}, [feature])
|
|
246
|
+
|
|
187
247
|
if (!(currentAssembly && refData)) {
|
|
188
248
|
return null
|
|
189
249
|
}
|
|
@@ -191,17 +251,28 @@ export const TranscriptSequence = observer(function TranscriptSequence({
|
|
|
191
251
|
if (!refSeq) {
|
|
192
252
|
return null
|
|
193
253
|
}
|
|
194
|
-
if (
|
|
195
|
-
|
|
254
|
+
if (!featureTypeOntology) {
|
|
255
|
+
throw new Error('featureTypeOntology is undefined')
|
|
196
256
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
setShowSequence(!showSequence)
|
|
257
|
+
if (!featureTypeOntology.isTypeOf(feature.type, 'transcript')) {
|
|
258
|
+
return null
|
|
200
259
|
}
|
|
201
260
|
|
|
202
261
|
function handleChangeSeqOption(e: SelectChangeEvent) {
|
|
203
262
|
const option = e.target.value
|
|
204
263
|
setSelectedOption(option as SegmentListType)
|
|
264
|
+
|
|
265
|
+
const seqSegments = refData
|
|
266
|
+
? getSequenceSegments(
|
|
267
|
+
option as SegmentListType,
|
|
268
|
+
feature,
|
|
269
|
+
(min: number, max: number) => refData.getSequence(min, max),
|
|
270
|
+
)
|
|
271
|
+
: []
|
|
272
|
+
const locIntervals: { min: number; max: number }[] =
|
|
273
|
+
getLocationIntervals(seqSegments)
|
|
274
|
+
setSequenceSegments(seqSegments)
|
|
275
|
+
setLocationIntervals(locIntervals)
|
|
205
276
|
}
|
|
206
277
|
|
|
207
278
|
// Function to copy text to clipboard
|
|
@@ -219,94 +290,68 @@ export const TranscriptSequence = observer(function TranscriptSequence({
|
|
|
219
290
|
void navigator.clipboard.write([clipboardItem])
|
|
220
291
|
}
|
|
221
292
|
|
|
222
|
-
const sequenceSegments = showSequence
|
|
223
|
-
? getSequenceSegments(selectedOption, feature, (min: number, max: number) =>
|
|
224
|
-
refData.getSequence(min, max),
|
|
225
|
-
)
|
|
226
|
-
: []
|
|
227
|
-
const locationIntervals: { min: number; max: number }[] = []
|
|
228
|
-
if (showSequence) {
|
|
229
|
-
const allLocs = sequenceSegments.flatMap((segment) => segment.locs)
|
|
230
|
-
let [previous] = allLocs
|
|
231
|
-
for (let i = 1; i < allLocs.length; i++) {
|
|
232
|
-
if (previous.min === allLocs[i].max || previous.max === allLocs[i].min) {
|
|
233
|
-
previous = {
|
|
234
|
-
min: Math.min(previous.min, allLocs[i].min),
|
|
235
|
-
max: Math.max(previous.max, allLocs[i].max),
|
|
236
|
-
}
|
|
237
|
-
} else {
|
|
238
|
-
locationIntervals.push(previous)
|
|
239
|
-
previous = allLocs[i]
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
locationIntervals.push(previous)
|
|
243
|
-
}
|
|
244
|
-
|
|
245
293
|
return (
|
|
246
294
|
<>
|
|
247
|
-
<
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
295
|
+
<Select
|
|
296
|
+
defaultValue="genomic"
|
|
297
|
+
value={selectedOption}
|
|
298
|
+
onChange={handleChangeSeqOption}
|
|
299
|
+
size="small"
|
|
300
|
+
>
|
|
301
|
+
{sequenceOptions.map((option) => (
|
|
302
|
+
<MenuItem key={option} value={option}>
|
|
303
|
+
{option}
|
|
304
|
+
</MenuItem>
|
|
305
|
+
))}
|
|
306
|
+
</Select>
|
|
307
|
+
<Button
|
|
308
|
+
variant="contained"
|
|
309
|
+
onClick={copyToClipboard}
|
|
310
|
+
style={{ marginLeft: 10 }}
|
|
311
|
+
size="medium"
|
|
312
|
+
>
|
|
313
|
+
Copy sequence
|
|
314
|
+
</Button>
|
|
315
|
+
<Paper
|
|
316
|
+
style={{
|
|
317
|
+
fontFamily: 'monospace',
|
|
318
|
+
padding: theme.spacing(),
|
|
319
|
+
overflowX: 'auto',
|
|
320
|
+
}}
|
|
321
|
+
ref={seqRef}
|
|
322
|
+
>
|
|
323
|
+
>{refSeq.name}:
|
|
324
|
+
{locationIntervals
|
|
325
|
+
.map((interval) =>
|
|
326
|
+
feature.strand === 1
|
|
327
|
+
? `${interval.min + 1}-${interval.max}`
|
|
328
|
+
: `${interval.max}-${interval.min + 1}`,
|
|
329
|
+
)
|
|
330
|
+
.join(';')}
|
|
331
|
+
({feature.strand === 1 ? '+' : '-'})
|
|
332
|
+
<br />
|
|
333
|
+
{sequenceSegments.map((segment, index) => (
|
|
334
|
+
<span
|
|
335
|
+
key={`${segment.type}-${index}`}
|
|
266
336
|
style={{
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
337
|
+
background: getSegmentColor(segment.type),
|
|
338
|
+
color: theme.palette.getContrastText(
|
|
339
|
+
getSegmentColor(segment.type),
|
|
340
|
+
),
|
|
270
341
|
}}
|
|
271
|
-
ref={seqRef}
|
|
272
342
|
>
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
.
|
|
281
|
-
({feature.strand === 1 ? '+' : '-'})
|
|
282
|
-
<br />
|
|
283
|
-
{sequenceSegments.map((segment, index) => (
|
|
284
|
-
<span
|
|
285
|
-
key={`${segment.type}-${index}`}
|
|
286
|
-
style={{
|
|
287
|
-
background: getSegmentColor(segment.type),
|
|
288
|
-
color: theme.palette.getContrastText(
|
|
289
|
-
getSegmentColor(segment.type),
|
|
290
|
-
),
|
|
291
|
-
}}
|
|
292
|
-
>
|
|
293
|
-
{segment.sequenceLines.map((sequenceLine, idx) => (
|
|
294
|
-
<React.Fragment key={`${sequenceLine.slice(0, 5)}-${idx}`}>
|
|
295
|
-
{sequenceLine}
|
|
296
|
-
{idx === segment.sequenceLines.length - 1 &&
|
|
297
|
-
sequenceLine.length !== SEQUENCE_WRAP_LENGTH ? null : (
|
|
298
|
-
<br />
|
|
299
|
-
)}
|
|
300
|
-
</React.Fragment>
|
|
301
|
-
))}
|
|
302
|
-
</span>
|
|
343
|
+
{segment.sequenceLines.map((sequenceLine, idx) => (
|
|
344
|
+
<React.Fragment key={`${sequenceLine.slice(0, 5)}-${idx}`}>
|
|
345
|
+
{sequenceLine}
|
|
346
|
+
{idx === segment.sequenceLines.length - 1 &&
|
|
347
|
+
sequenceLine.length !== SEQUENCE_WRAP_LENGTH ? null : (
|
|
348
|
+
<br />
|
|
349
|
+
)}
|
|
350
|
+
</React.Fragment>
|
|
303
351
|
))}
|
|
304
|
-
</
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
</Button>
|
|
308
|
-
</>
|
|
309
|
-
)}
|
|
352
|
+
</span>
|
|
353
|
+
))}
|
|
354
|
+
</Paper>
|
|
310
355
|
</>
|
|
311
356
|
)
|
|
312
357
|
})
|