jbrowse-plugin-protein3d 0.0.2

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.
Files changed (210) hide show
  1. package/README.md +3 -0
  2. package/dist/AddHighlightModel/GenomeMouseoverHighlight.d.ts +6 -0
  3. package/dist/AddHighlightModel/GenomeMouseoverHighlight.js +24 -0
  4. package/dist/AddHighlightModel/GenomeMouseoverHighlight.js.map +1 -0
  5. package/dist/AddHighlightModel/Highlight.d.ts +10 -0
  6. package/dist/AddHighlightModel/Highlight.js +24 -0
  7. package/dist/AddHighlightModel/Highlight.js.map +1 -0
  8. package/dist/AddHighlightModel/HighlightComponents.d.ts +7 -0
  9. package/dist/AddHighlightModel/HighlightComponents.js +14 -0
  10. package/dist/AddHighlightModel/HighlightComponents.js.map +1 -0
  11. package/dist/AddHighlightModel/ProteinToGenomeClickHighlight.d.ts +7 -0
  12. package/dist/AddHighlightModel/ProteinToGenomeClickHighlight.js +13 -0
  13. package/dist/AddHighlightModel/ProteinToGenomeClickHighlight.js.map +1 -0
  14. package/dist/AddHighlightModel/ProteinToGenomeHoverHighlight.d.ts +7 -0
  15. package/dist/AddHighlightModel/ProteinToGenomeHoverHighlight.js +13 -0
  16. package/dist/AddHighlightModel/ProteinToGenomeHoverHighlight.js.map +1 -0
  17. package/dist/AddHighlightModel/index.d.ts +2 -0
  18. package/dist/AddHighlightModel/index.js +14 -0
  19. package/dist/AddHighlightModel/index.js.map +1 -0
  20. package/dist/AddHighlightModel/util.d.ts +9 -0
  21. package/dist/AddHighlightModel/util.js +17 -0
  22. package/dist/AddHighlightModel/util.js.map +1 -0
  23. package/dist/LaunchProteinView/calculateProteinSequence.d.ts +28 -0
  24. package/dist/LaunchProteinView/calculateProteinSequence.js +77 -0
  25. package/dist/LaunchProteinView/calculateProteinSequence.js.map +1 -0
  26. package/dist/LaunchProteinView/components/AlphaFoldDBSearch.d.ts +8 -0
  27. package/dist/LaunchProteinView/components/AlphaFoldDBSearch.js +71 -0
  28. package/dist/LaunchProteinView/components/AlphaFoldDBSearch.js.map +1 -0
  29. package/dist/LaunchProteinView/components/AlphaFoldDBSearchStatus.d.ts +8 -0
  30. package/dist/LaunchProteinView/components/AlphaFoldDBSearchStatus.js +19 -0
  31. package/dist/LaunchProteinView/components/AlphaFoldDBSearchStatus.js.map +1 -0
  32. package/dist/LaunchProteinView/components/HelpButton.d.ts +2 -0
  33. package/dist/LaunchProteinView/components/HelpButton.js +15 -0
  34. package/dist/LaunchProteinView/components/HelpButton.js.map +1 -0
  35. package/dist/LaunchProteinView/components/HelpDialog.d.ts +4 -0
  36. package/dist/LaunchProteinView/components/HelpDialog.js +16 -0
  37. package/dist/LaunchProteinView/components/HelpDialog.js.map +1 -0
  38. package/dist/LaunchProteinView/components/LaunchProteinViewDialog.d.ts +7 -0
  39. package/dist/LaunchProteinView/components/LaunchProteinViewDialog.js +23 -0
  40. package/dist/LaunchProteinView/components/LaunchProteinViewDialog.js.map +1 -0
  41. package/dist/LaunchProteinView/components/PreLoadedStructureMapping.d.ts +8 -0
  42. package/dist/LaunchProteinView/components/PreLoadedStructureMapping.js +72 -0
  43. package/dist/LaunchProteinView/components/PreLoadedStructureMapping.js.map +1 -0
  44. package/dist/LaunchProteinView/components/TabPanel.d.ts +6 -0
  45. package/dist/LaunchProteinView/components/TabPanel.js +6 -0
  46. package/dist/LaunchProteinView/components/TabPanel.js.map +1 -0
  47. package/dist/LaunchProteinView/components/TranscriptSelector.d.ts +9 -0
  48. package/dist/LaunchProteinView/components/TranscriptSelector.js +28 -0
  49. package/dist/LaunchProteinView/components/TranscriptSelector.js.map +1 -0
  50. package/dist/LaunchProteinView/components/UserProvidedStructure.d.ts +8 -0
  51. package/dist/LaunchProteinView/components/UserProvidedStructure.js +118 -0
  52. package/dist/LaunchProteinView/components/UserProvidedStructure.js.map +1 -0
  53. package/dist/LaunchProteinView/components/useCheckAlphaFoldDBExistence.d.ts +7 -0
  54. package/dist/LaunchProteinView/components/useCheckAlphaFoldDBExistence.js +26 -0
  55. package/dist/LaunchProteinView/components/useCheckAlphaFoldDBExistence.js.map +1 -0
  56. package/dist/LaunchProteinView/index.d.ts +2 -0
  57. package/dist/LaunchProteinView/index.js +45 -0
  58. package/dist/LaunchProteinView/index.js.map +1 -0
  59. package/dist/LaunchProteinView/useMyGeneInfo.d.ts +7 -0
  60. package/dist/LaunchProteinView/useMyGeneInfo.js +29 -0
  61. package/dist/LaunchProteinView/useMyGeneInfo.js.map +1 -0
  62. package/dist/LaunchProteinView/useProteinSequences.d.ts +10 -0
  63. package/dist/LaunchProteinView/useProteinSequences.js +30 -0
  64. package/dist/LaunchProteinView/useProteinSequences.js.map +1 -0
  65. package/dist/LaunchProteinView/util.d.ts +18 -0
  66. package/dist/LaunchProteinView/util.js +54 -0
  67. package/dist/LaunchProteinView/util.js.map +1 -0
  68. package/dist/ProteinModelSessionExtension.d.ts +11 -0
  69. package/dist/ProteinModelSessionExtension.js +53 -0
  70. package/dist/ProteinModelSessionExtension.js.map +1 -0
  71. package/dist/ProteinView/clearSelection.d.ts +4 -0
  72. package/dist/ProteinView/clearSelection.js +4 -0
  73. package/dist/ProteinView/clearSelection.js.map +1 -0
  74. package/dist/ProteinView/components/Header.d.ts +6 -0
  75. package/dist/ProteinView/components/Header.js +49 -0
  76. package/dist/ProteinView/components/Header.js.map +1 -0
  77. package/dist/ProteinView/components/ProteinAlignment.d.ts +6 -0
  78. package/dist/ProteinView/components/ProteinAlignment.js +62 -0
  79. package/dist/ProteinView/components/ProteinAlignment.js.map +1 -0
  80. package/dist/ProteinView/components/ProteinAlignmentHelpButton.d.ts +5 -0
  81. package/dist/ProteinView/components/ProteinAlignmentHelpButton.js +14 -0
  82. package/dist/ProteinView/components/ProteinAlignmentHelpButton.js.map +1 -0
  83. package/dist/ProteinView/components/ProteinAlignmentHelpDialog.d.ts +4 -0
  84. package/dist/ProteinView/components/ProteinAlignmentHelpDialog.js +20 -0
  85. package/dist/ProteinView/components/ProteinAlignmentHelpDialog.js.map +1 -0
  86. package/dist/ProteinView/components/ProteinView.d.ts +6 -0
  87. package/dist/ProteinView/components/ProteinView.js +79 -0
  88. package/dist/ProteinView/components/ProteinView.js.map +1 -0
  89. package/dist/ProteinView/components/SplitString.d.ts +9 -0
  90. package/dist/ProteinView/components/SplitString.js +11 -0
  91. package/dist/ProteinView/components/SplitString.js.map +1 -0
  92. package/dist/ProteinView/css/molstar.d.ts +2 -0
  93. package/dist/ProteinView/css/molstar.js +3137 -0
  94. package/dist/ProteinView/css/molstar.js.map +1 -0
  95. package/dist/ProteinView/genomeToProtein.d.ts +4 -0
  96. package/dist/ProteinView/genomeToProtein.js +13 -0
  97. package/dist/ProteinView/genomeToProtein.js.map +1 -0
  98. package/dist/ProteinView/highlightResidue.d.ts +7 -0
  99. package/dist/ProteinView/highlightResidue.js +14 -0
  100. package/dist/ProteinView/highlightResidue.js.map +1 -0
  101. package/dist/ProteinView/index.d.ts +2 -0
  102. package/dist/ProteinView/index.js +15 -0
  103. package/dist/ProteinView/index.js.map +1 -0
  104. package/dist/ProteinView/launchRemotePairwiseAlignment.d.ts +14 -0
  105. package/dist/ProteinView/launchRemotePairwiseAlignment.js +72 -0
  106. package/dist/ProteinView/launchRemotePairwiseAlignment.js.map +1 -0
  107. package/dist/ProteinView/loadStructureFromData.d.ts +17 -0
  108. package/dist/ProteinView/loadStructureFromData.js +20 -0
  109. package/dist/ProteinView/loadStructureFromData.js.map +1 -0
  110. package/dist/ProteinView/loadStructureFromURL.d.ts +17 -0
  111. package/dist/ProteinView/loadStructureFromURL.js +17 -0
  112. package/dist/ProteinView/loadStructureFromURL.js.map +1 -0
  113. package/dist/ProteinView/model.d.ts +226 -0
  114. package/dist/ProteinView/model.js +324 -0
  115. package/dist/ProteinView/model.js.map +1 -0
  116. package/dist/ProteinView/proteinAbbreviationMapping.d.ts +7 -0
  117. package/dist/ProteinView/proteinAbbreviationMapping.js +23 -0
  118. package/dist/ProteinView/proteinAbbreviationMapping.js.map +1 -0
  119. package/dist/ProteinView/proteinToGenomeMapping.d.ts +13 -0
  120. package/dist/ProteinView/proteinToGenomeMapping.js +69 -0
  121. package/dist/ProteinView/proteinToGenomeMapping.js.map +1 -0
  122. package/dist/ProteinView/selectResidue.d.ts +7 -0
  123. package/dist/ProteinView/selectResidue.js +10 -0
  124. package/dist/ProteinView/selectResidue.js.map +1 -0
  125. package/dist/ProteinView/useProteinView.d.ts +11 -0
  126. package/dist/ProteinView/useProteinView.js +57 -0
  127. package/dist/ProteinView/useProteinView.js.map +1 -0
  128. package/dist/ProteinView/useProteinViewClickBehavior.d.ts +8 -0
  129. package/dist/ProteinView/useProteinViewClickBehavior.js +34 -0
  130. package/dist/ProteinView/useProteinViewClickBehavior.js.map +1 -0
  131. package/dist/ProteinView/useProteinViewHoverBehavior.d.ts +6 -0
  132. package/dist/ProteinView/useProteinViewHoverBehavior.js +31 -0
  133. package/dist/ProteinView/useProteinViewHoverBehavior.js.map +1 -0
  134. package/dist/ProteinView/util.d.ts +19 -0
  135. package/dist/ProteinView/util.js +32 -0
  136. package/dist/ProteinView/util.js.map +1 -0
  137. package/dist/fetchUtils.d.ts +5 -0
  138. package/dist/fetchUtils.js +23 -0
  139. package/dist/fetchUtils.js.map +1 -0
  140. package/dist/genomeToTranscriptMapping.d.ts +7 -0
  141. package/dist/genomeToTranscriptMapping.js +36 -0
  142. package/dist/genomeToTranscriptMapping.js.map +1 -0
  143. package/dist/index.d.ts +8 -0
  144. package/dist/index.js +26 -0
  145. package/dist/index.js.map +1 -0
  146. package/dist/jbrowse-plugin-protein3d.umd.production.min.js +9298 -0
  147. package/dist/jbrowse-plugin-protein3d.umd.production.min.js.map +7 -0
  148. package/dist/mappings.d.ts +19 -0
  149. package/dist/mappings.js +67 -0
  150. package/dist/mappings.js.map +1 -0
  151. package/dist/mappings.test.d.ts +1 -0
  152. package/dist/mappings.test.js +27 -0
  153. package/dist/mappings.test.js.map +1 -0
  154. package/dist/test_data/gene.d.ts +67 -0
  155. package/dist/test_data/gene.js +603 -0
  156. package/dist/test_data/gene.js.map +1 -0
  157. package/package.json +70 -0
  158. package/src/AddHighlightModel/GenomeMouseoverHighlight.tsx +45 -0
  159. package/src/AddHighlightModel/Highlight.tsx +46 -0
  160. package/src/AddHighlightModel/HighlightComponents.tsx +26 -0
  161. package/src/AddHighlightModel/ProteinToGenomeClickHighlight.tsx +39 -0
  162. package/src/AddHighlightModel/ProteinToGenomeHoverHighlight.tsx +39 -0
  163. package/src/AddHighlightModel/index.tsx +25 -0
  164. package/src/AddHighlightModel/util.ts +17 -0
  165. package/src/LaunchProteinView/calculateProteinSequence.ts +127 -0
  166. package/src/LaunchProteinView/components/AlphaFoldDBSearch.tsx +141 -0
  167. package/src/LaunchProteinView/components/AlphaFoldDBSearchStatus.tsx +44 -0
  168. package/src/LaunchProteinView/components/HelpButton.tsx +23 -0
  169. package/src/LaunchProteinView/components/HelpDialog.tsx +43 -0
  170. package/src/LaunchProteinView/components/LaunchProteinViewDialog.tsx +57 -0
  171. package/src/LaunchProteinView/components/PreLoadedStructureMapping.tsx +153 -0
  172. package/src/LaunchProteinView/components/TabPanel.tsx +19 -0
  173. package/src/LaunchProteinView/components/TranscriptSelector.tsx +54 -0
  174. package/src/LaunchProteinView/components/UserProvidedStructure.tsx +226 -0
  175. package/src/LaunchProteinView/components/useCheckAlphaFoldDBExistence.ts +31 -0
  176. package/src/LaunchProteinView/index.ts +56 -0
  177. package/src/LaunchProteinView/useMyGeneInfo.ts +37 -0
  178. package/src/LaunchProteinView/useProteinSequences.ts +36 -0
  179. package/src/LaunchProteinView/util.ts +74 -0
  180. package/src/ProteinModelSessionExtension.ts +71 -0
  181. package/src/ProteinView/clearSelection.ts +5 -0
  182. package/src/ProteinView/components/Header.tsx +84 -0
  183. package/src/ProteinView/components/ProteinAlignment.tsx +119 -0
  184. package/src/ProteinView/components/ProteinAlignmentHelpButton.tsx +33 -0
  185. package/src/ProteinView/components/ProteinAlignmentHelpDialog.tsx +59 -0
  186. package/src/ProteinView/components/ProteinView.tsx +131 -0
  187. package/src/ProteinView/components/SplitString.tsx +35 -0
  188. package/src/ProteinView/css/molstar.ts +3136 -0
  189. package/src/ProteinView/genomeToProtein.ts +21 -0
  190. package/src/ProteinView/highlightResidue.ts +23 -0
  191. package/src/ProteinView/index.ts +18 -0
  192. package/src/ProteinView/launchRemotePairwiseAlignment.ts +113 -0
  193. package/src/ProteinView/loadStructureFromData.ts +48 -0
  194. package/src/ProteinView/loadStructureFromURL.ts +50 -0
  195. package/src/ProteinView/model.ts +384 -0
  196. package/src/ProteinView/proteinAbbreviationMapping.ts +24 -0
  197. package/src/ProteinView/proteinToGenomeMapping.ts +99 -0
  198. package/src/ProteinView/selectResidue.ts +19 -0
  199. package/src/ProteinView/useProteinView.ts +70 -0
  200. package/src/ProteinView/useProteinViewClickBehavior.ts +48 -0
  201. package/src/ProteinView/useProteinViewHoverBehavior.ts +44 -0
  202. package/src/ProteinView/util.ts +56 -0
  203. package/src/__snapshots__/mappings.test.ts.snap +1351 -0
  204. package/src/declare.d.ts +1 -0
  205. package/src/fetchUtils.ts +30 -0
  206. package/src/genomeToTranscriptMapping.ts +46 -0
  207. package/src/index.ts +32 -0
  208. package/src/mappings.test.ts +32 -0
  209. package/src/mappings.ts +89 -0
  210. package/src/test_data/gene.ts +604 -0
@@ -0,0 +1,57 @@
1
+ import React, { useState } from 'react'
2
+ import { Dialog } from '@jbrowse/core/ui'
3
+ import { Tab, Tabs } from '@mui/material'
4
+ import { AbstractTrackModel, Feature } from '@jbrowse/core/util'
5
+
6
+ // locals
7
+ import PreLoadedStructureMapping from './PreLoadedStructureMapping'
8
+ import AlphaFoldDBSearch from './AlphaFoldDBSearch'
9
+ import UserProvidedStructure from './UserProvidedStructure'
10
+ import TabPanel from './TabPanel'
11
+
12
+ export default function LaunchProteinViewDialog({
13
+ handleClose,
14
+ feature,
15
+ model,
16
+ }: {
17
+ handleClose: () => void
18
+ feature: Feature
19
+ model: AbstractTrackModel
20
+ }) {
21
+ const [choice, setChoice] = useState(0)
22
+ return (
23
+ <Dialog
24
+ maxWidth="xl"
25
+ title="Launch protein view"
26
+ onClose={() => handleClose()}
27
+ open
28
+ >
29
+ <Tabs value={choice} onChange={(_, val) => setChoice(val)}>
30
+ <Tab value={0} label="AlphaFoldDB search" />
31
+ <Tab value={1} label="Manual" />
32
+ <Tab value={2} label="Pre-configured" />
33
+ </Tabs>
34
+ <TabPanel value={choice} index={0}>
35
+ <AlphaFoldDBSearch
36
+ model={model}
37
+ feature={feature}
38
+ handleClose={handleClose}
39
+ />
40
+ </TabPanel>
41
+ <TabPanel value={choice} index={1}>
42
+ <UserProvidedStructure
43
+ model={model}
44
+ feature={feature}
45
+ handleClose={handleClose}
46
+ />
47
+ </TabPanel>
48
+ <TabPanel value={choice} index={2}>
49
+ <PreLoadedStructureMapping
50
+ feature={feature}
51
+ model={model}
52
+ handleClose={handleClose}
53
+ />
54
+ </TabPanel>
55
+ </Dialog>
56
+ )
57
+ }
@@ -0,0 +1,153 @@
1
+ import React, { useEffect, useMemo, useState } from 'react'
2
+ import { observer } from 'mobx-react'
3
+ import {
4
+ Button,
5
+ DialogActions,
6
+ DialogContent,
7
+ Link,
8
+ MenuItem,
9
+ TextField,
10
+ Typography,
11
+ } from '@mui/material'
12
+ import { makeStyles } from 'tss-react/mui'
13
+ import { ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui'
14
+ import { AbstractTrackModel, Feature, getSession } from '@jbrowse/core/util'
15
+
16
+ // locals
17
+ import {
18
+ createMapFromData,
19
+ getDisplayName,
20
+ getTranscriptFeatures,
21
+ stripTrailingVersion,
22
+ } from '../util'
23
+ import { genomeToTranscriptMapping } from '../../genomeToTranscriptMapping'
24
+
25
+ const useStyles = makeStyles()(theme => ({
26
+ section: {
27
+ marginTop: theme.spacing(6),
28
+ },
29
+
30
+ dialogContent: {
31
+ width: '80em',
32
+ },
33
+ }))
34
+
35
+ function foundF(f: Feature | undefined, map: Map<string, string>) {
36
+ return (
37
+ map.get(stripTrailingVersion(f?.get('name')) ?? '') ??
38
+ map.get(stripTrailingVersion(f?.get('id')) ?? '')
39
+ )
40
+ }
41
+
42
+ const AutoForm = observer(function AutoForm({
43
+ model,
44
+ feature,
45
+ handleClose,
46
+ }: {
47
+ model: AbstractTrackModel
48
+ feature: Feature
49
+ handleClose: () => void
50
+ }) {
51
+ const { classes } = useStyles()
52
+ const session = getSession(model)
53
+ // @ts-expect-error
54
+ const { proteinModel } = session
55
+ const { data, error } = proteinModel
56
+ // check if we are looking at a 'two-level' or 'three-level' feature by
57
+ // finding exon/CDS subfeatures. we want to select from transcript names
58
+ const options = getTranscriptFeatures(feature)
59
+ const transcriptIdToStructureMap = useMemo(
60
+ () => createMapFromData(data),
61
+ [data],
62
+ )
63
+
64
+ const hasDataForFeatures = useMemo(
65
+ () => options.filter(f => foundF(f, transcriptIdToStructureMap)),
66
+ [transcriptIdToStructureMap, options],
67
+ )
68
+ const [userSelection, setUserSelection] = useState('')
69
+ const userSelectionFeat = options.find(f => f.id() === userSelection)
70
+ const foundStructureId = foundF(userSelectionFeat, transcriptIdToStructureMap)
71
+
72
+ useEffect(() => {
73
+ setUserSelection(hasDataForFeatures[0]?.id())
74
+ }, [hasDataForFeatures])
75
+
76
+ const mapping =
77
+ foundStructureId && userSelectionFeat
78
+ ? genomeToTranscriptMapping(userSelectionFeat)
79
+ : []
80
+ const url = foundStructureId
81
+ ? `https://files.rcsb.org/view/${foundStructureId}.cif`
82
+ : undefined
83
+
84
+ return (
85
+ <>
86
+ <DialogContent className={classes.dialogContent}>
87
+ <div className={classes.section}>
88
+ {error ? (
89
+ <ErrorMessage error={error} />
90
+ ) : data ? (
91
+ <div>
92
+ <Intro />
93
+ {hasDataForFeatures.length === 0 ? (
94
+ <Typography color="error">No data for feature</Typography>
95
+ ) : (
96
+ <>
97
+ <div className={classes.section}>
98
+ <TextField
99
+ value={userSelection}
100
+ onChange={event => setUserSelection(event.target.value)}
101
+ label="Choose isoform"
102
+ select
103
+ >
104
+ {hasDataForFeatures.map(val => (
105
+ <MenuItem value={val.id()} key={val.id()}>
106
+ {getDisplayName(val)} (has data)
107
+ </MenuItem>
108
+ ))}
109
+ </TextField>
110
+ </div>
111
+ </>
112
+ )}
113
+ </div>
114
+ ) : (
115
+ <LoadingEllipses />
116
+ )}
117
+ </div>
118
+ </DialogContent>
119
+ <DialogActions>
120
+ <Button variant="contained" color="secondary" onClick={handleClose}>
121
+ Cancel
122
+ </Button>
123
+ <Button
124
+ variant="contained"
125
+ color="primary"
126
+ onClick={() => {
127
+ session.addView('ProteinView', {
128
+ type: 'ProteinView',
129
+ url,
130
+ mapping,
131
+ })
132
+ handleClose()
133
+ }}
134
+ >
135
+ Submit
136
+ </Button>
137
+ </DialogActions>
138
+ </>
139
+ )
140
+ })
141
+
142
+ function Intro() {
143
+ return (
144
+ <div>
145
+ Find structure associated with gene ID:{' '}
146
+ <Link href="http://useast.ensembl.org/biomart/martview/4b20effd49654183333b81e98757976f?VIRTUALSCHEMANAME=default&ATTRIBUTES=hsapiens_gene_ensembl.default.feature_page.ensembl_gene_id|hsapiens_gene_ensembl.default.feature_page.ensembl_gene_id_version|hsapiens_gene_ensembl.default.feature_page.ensembl_transcript_id|hsapiens_gene_ensembl.default.feature_page.ensembl_transcript_id_version|hsapiens_gene_ensembl.default.feature_page.pdb|hsapiens_gene_ensembl.default.feature_page.refseq_mrna|hsapiens_gene_ensembl.default.feature_page.refseq_mrna_predicted&FILTERS=&VISIBLEPANEL=attributepanel">
147
+ Human mappings generated from BioMart (April 13, 2023)
148
+ </Link>
149
+ </div>
150
+ )
151
+ }
152
+
153
+ export default AutoForm
@@ -0,0 +1,19 @@
1
+ import React from 'react'
2
+
3
+ // this is from MUI example
4
+ export default function TabPanel({
5
+ children,
6
+ value,
7
+ index,
8
+ ...other
9
+ }: {
10
+ children?: React.ReactNode
11
+ index: number
12
+ value: number
13
+ }) {
14
+ return (
15
+ <div role="tabpanel" hidden={value !== index} {...other}>
16
+ {value === index && <div>{children}</div>}
17
+ </div>
18
+ )
19
+ }
@@ -0,0 +1,54 @@
1
+ import React from 'react'
2
+ import { MenuItem, TextField, TextFieldProps } from '@mui/material'
3
+ import { Feature } from '@jbrowse/core/util'
4
+
5
+ // locals
6
+ import { getGeneDisplayName, getTranscriptDisplayName } from '../util'
7
+
8
+ function TextField2({ children, ...rest }: TextFieldProps) {
9
+ return (
10
+ <div>
11
+ <TextField {...rest}>{children}</TextField>
12
+ </div>
13
+ )
14
+ }
15
+
16
+ export default function TranscriptSelector({
17
+ val,
18
+ setVal,
19
+ options,
20
+ feature,
21
+ seqs,
22
+ }: {
23
+ options: Feature[]
24
+ feature: Feature
25
+ val: string
26
+ setVal: (str: string) => void
27
+ seqs: Record<string, string>
28
+ }) {
29
+ return (
30
+ <TextField2
31
+ value={val}
32
+ onChange={event => setVal(event.target.value)}
33
+ label="Choose transcript isoform"
34
+ select
35
+ >
36
+ {options
37
+ .filter(f => !!seqs[f.id()])
38
+ .map(f => (
39
+ <MenuItem value={f.id()} key={f.id()}>
40
+ {getGeneDisplayName(feature)} - {getTranscriptDisplayName(f)} (
41
+ {seqs[f.id()].length}aa)
42
+ </MenuItem>
43
+ ))}
44
+ {options
45
+ .filter(f => !seqs[f.id()])
46
+ .map(f => (
47
+ <MenuItem value={f.id()} key={f.id()} disabled>
48
+ {getGeneDisplayName(feature)} - {getTranscriptDisplayName(f)} (no
49
+ data)
50
+ </MenuItem>
51
+ ))}
52
+ </TextField2>
53
+ )
54
+ }
@@ -0,0 +1,226 @@
1
+ import React, { useEffect, useState } from 'react'
2
+ import { observer } from 'mobx-react'
3
+ import {
4
+ Button,
5
+ DialogActions,
6
+ Radio,
7
+ RadioGroup,
8
+ DialogContent,
9
+ TextField,
10
+ FormControlLabel,
11
+ FormControl,
12
+ Link,
13
+ Typography,
14
+ } from '@mui/material'
15
+ import { makeStyles } from 'tss-react/mui'
16
+ import {
17
+ AbstractTrackModel,
18
+ Feature,
19
+ getContainingView,
20
+ getSession,
21
+ } from '@jbrowse/core/util'
22
+ import { ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui'
23
+ import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
24
+
25
+ // locals
26
+ import {
27
+ getGeneDisplayName,
28
+ getId,
29
+ getTranscriptDisplayName,
30
+ getTranscriptFeatures,
31
+ } from '../util'
32
+ import TranscriptSelector from './TranscriptSelector'
33
+
34
+ // hooks
35
+ import useAllSequences from '../useProteinSequences'
36
+
37
+ const useStyles = makeStyles()(theme => ({
38
+ dialogContent: {
39
+ marginTop: theme.spacing(6),
40
+ width: '80em',
41
+ },
42
+ textAreaFont: {
43
+ fontFamily: 'Courier New',
44
+ },
45
+ }))
46
+
47
+ type LGV = LinearGenomeViewModel
48
+
49
+ function HelpText() {
50
+ return (
51
+ <div style={{ marginBottom: 20 }}>
52
+ Manually supply a protein structure (PDB, mmCIF, etc) for a given
53
+ transcript. You can open the file from the result of running, for example,{' '}
54
+ <Link target="_blank" href="https://github.com/sokrypton/ColabFold">
55
+ ColabFold
56
+ </Link>
57
+ . This plugin will align the protein sequence calculated from the genome
58
+ to the protein sequence embedded in the structure file which allows for
59
+ slight differences in these two representations.
60
+ </div>
61
+ )
62
+ }
63
+
64
+ const UserProvidedStructure = observer(function ({
65
+ feature,
66
+ model,
67
+ handleClose,
68
+ }: {
69
+ feature: Feature
70
+ model: AbstractTrackModel
71
+ handleClose: () => void
72
+ }) {
73
+ const { classes } = useStyles()
74
+ const session = getSession(model)
75
+ const [file, setFile] = useState<File>()
76
+ const [choice, setChoice] = useState('file')
77
+ const [error2, setError] = useState<unknown>()
78
+ const [structureURL, setStructureURL] = useState('')
79
+ const [selection, setSelection] = useState<string>()
80
+
81
+ // check if we are looking at a 'two-level' or 'three-level' feature by
82
+ // finding exon/CDS subfeatures. we want to select from transcript names
83
+ const options = getTranscriptFeatures(feature)
84
+ const view = getContainingView(model) as LGV
85
+ const selectedTranscript = options.find(val => getId(val) === selection)
86
+ const { seqs, error } = useAllSequences({ feature, view })
87
+ const protein = seqs?.[selection ?? '']
88
+ useEffect(() => {
89
+ if (selection === undefined && seqs !== undefined) {
90
+ setSelection(options.find(f => !!seqs[f.id()])?.id())
91
+ }
92
+ }, [options, selection, seqs])
93
+
94
+ const e = error || error2
95
+ return (
96
+ <>
97
+ <DialogContent className={classes.dialogContent}>
98
+ {e ? <ErrorMessage error={e} /> : null}
99
+ <HelpText />
100
+ {seqs ? (
101
+ <>
102
+ <TranscriptSelector
103
+ val={selection ?? ''}
104
+ setVal={setSelection}
105
+ options={options}
106
+ feature={feature}
107
+ seqs={seqs}
108
+ />
109
+ {selectedTranscript ? (
110
+ <TextField
111
+ variant="outlined"
112
+ multiline
113
+ minRows={5}
114
+ maxRows={10}
115
+ fullWidth
116
+ value={`>${selectedTranscript.get('name') || selectedTranscript.get('id')}\n${protein}`}
117
+ InputProps={{
118
+ readOnly: true,
119
+ classes: {
120
+ input: classes.textAreaFont,
121
+ },
122
+ }}
123
+ />
124
+ ) : null}
125
+ </>
126
+ ) : (
127
+ <div style={{ margin: 20 }}>
128
+ <LoadingEllipses title="Loading protein sequences" variant="h6" />
129
+ </div>
130
+ )}
131
+ <div style={{ display: 'flex', margin: 30 }}>
132
+ <FormControl component="fieldset">
133
+ <RadioGroup
134
+ value={choice}
135
+ onChange={event => setChoice(event.target.value)}
136
+ >
137
+ <FormControlLabel value="url" control={<Radio />} label="URL" />
138
+ <FormControlLabel value="file" control={<Radio />} label="File" />
139
+ </RadioGroup>
140
+ </FormControl>
141
+ {choice === 'url' ? (
142
+ <div>
143
+ <Typography>
144
+ Open a PDB/mmCIF/etc. file from remote URL
145
+ </Typography>
146
+ <TextField
147
+ label="URL"
148
+ value={structureURL}
149
+ onChange={event => setStructureURL(event.target.value)}
150
+ />
151
+ </div>
152
+ ) : null}
153
+ {choice === 'file' ? (
154
+ <div style={{ paddingTop: 20 }}>
155
+ <Typography>
156
+ Open a PDB/mmCIF/etc. file from your local drive
157
+ </Typography>
158
+ <Button variant="outlined" component="label">
159
+ Choose File
160
+ <input
161
+ type="file"
162
+ hidden
163
+ onChange={({ target }) => {
164
+ const file = target?.files?.[0]
165
+ if (file) {
166
+ setFile(file)
167
+ }
168
+ }}
169
+ />
170
+ </Button>
171
+ </div>
172
+ ) : null}
173
+ </div>
174
+ </DialogContent>
175
+ <DialogActions>
176
+ <Button
177
+ variant="contained"
178
+ color="secondary"
179
+ onClick={() => handleClose()}
180
+ >
181
+ Cancel
182
+ </Button>
183
+ <Button
184
+ variant="contained"
185
+ color="primary"
186
+ disabled={!(structureURL || file) || !protein || !selectedTranscript}
187
+ onClick={() => {
188
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
189
+ ;(async () => {
190
+ try {
191
+ if (file) {
192
+ const data = await file.text()
193
+ session.addView('ProteinView', {
194
+ type: 'ProteinView',
195
+ data,
196
+ seq2: protein,
197
+ feature: selectedTranscript?.toJSON(),
198
+ connectedViewId: view.id,
199
+ displayName: `Protein view ${getGeneDisplayName(feature)} - ${getTranscriptDisplayName(selectedTranscript)}`,
200
+ })
201
+ } else if (structureURL) {
202
+ session.addView('ProteinView', {
203
+ type: 'ProteinView',
204
+ url: structureURL,
205
+ seq2: protein,
206
+ feature: selectedTranscript?.toJSON(),
207
+ connectedViewId: view.id,
208
+ displayName: `Protein view ${getGeneDisplayName(feature)} - ${getTranscriptDisplayName(selectedTranscript)}`,
209
+ })
210
+ }
211
+ handleClose()
212
+ } catch (e) {
213
+ console.error(e)
214
+ setError(e)
215
+ }
216
+ })()
217
+ }}
218
+ >
219
+ Submit
220
+ </Button>
221
+ </DialogActions>
222
+ </>
223
+ )
224
+ })
225
+
226
+ export default UserProvidedStructure
@@ -0,0 +1,31 @@
1
+ import { useEffect, useState } from 'react'
2
+
3
+ export function useCheckAlphaFoldDBExistence({
4
+ foundStructureId,
5
+ }: {
6
+ foundStructureId?: string
7
+ }) {
8
+ const [error, setError] = useState<unknown>()
9
+ const [loading, setLoading] = useState(false)
10
+ const [success, setSuccess] = useState(false)
11
+ useEffect(() => {
12
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
13
+ ;(async () => {
14
+ try {
15
+ if (foundStructureId) {
16
+ setLoading(true)
17
+ await fetch(
18
+ `https://alphafold.ebi.ac.uk/files/AF-${foundStructureId}-F1-model_v4.cif`,
19
+ { method: 'HEAD' },
20
+ )
21
+ setLoading(false)
22
+ setSuccess(true)
23
+ }
24
+ } catch (e) {
25
+ console.error(e)
26
+ setError(e)
27
+ }
28
+ })()
29
+ }, [foundStructureId])
30
+ return { error, loading, success }
31
+ }
@@ -0,0 +1,56 @@
1
+ import PluginManager from '@jbrowse/core/PluginManager'
2
+ import DisplayType from '@jbrowse/core/pluggableElementTypes/DisplayType'
3
+ import { PluggableElementType } from '@jbrowse/core/pluggableElementTypes'
4
+ import { IAnyModelType } from 'mobx-state-tree'
5
+ import { getSession, getContainingTrack } from '@jbrowse/core/util'
6
+
7
+ // icons
8
+ import AddIcon from '@mui/icons-material/Add'
9
+
10
+ // locals
11
+ import LaunchProteinViewDialog from './components/LaunchProteinViewDialog'
12
+
13
+ function isDisplay(elt: { name: string }): elt is DisplayType {
14
+ return elt.name === 'LinearBasicDisplay'
15
+ }
16
+
17
+ function extendStateModel(stateModel: IAnyModelType) {
18
+ return stateModel.views(self => {
19
+ const superContextMenuItems = self.contextMenuItems
20
+ return {
21
+ contextMenuItems() {
22
+ const feature = self.contextMenuFeature
23
+ const track = getContainingTrack(self)
24
+ return [
25
+ ...superContextMenuItems(),
26
+ ...(feature
27
+ ? [
28
+ {
29
+ label: 'Launch protein view',
30
+ icon: AddIcon,
31
+ onClick: () => {
32
+ getSession(track).queueDialog(handleClose => [
33
+ LaunchProteinViewDialog,
34
+ { model: track, handleClose, feature },
35
+ ])
36
+ },
37
+ },
38
+ ]
39
+ : []),
40
+ ]
41
+ },
42
+ }
43
+ })
44
+ }
45
+
46
+ export default function LaunchProteinViewF(pluginManager: PluginManager) {
47
+ pluginManager.addToExtensionPoint(
48
+ 'Core-extendPluggableElement',
49
+ (elt: PluggableElementType) => {
50
+ if (isDisplay(elt)) {
51
+ elt.stateModel = extendStateModel(elt.stateModel)
52
+ }
53
+ return elt
54
+ },
55
+ )
56
+ }
@@ -0,0 +1,37 @@
1
+ import { useEffect, useState } from 'react'
2
+ import { jsonfetch } from '../fetchUtils'
3
+ import { stripTrailingVersion } from './util'
4
+
5
+ interface MyGeneInfoResults {
6
+ hits: {
7
+ uniprot: {
8
+ 'Swiss-Prot': string
9
+ }
10
+ }[]
11
+ }
12
+
13
+ export default function useMyGeneInfo({ id }: { id: string }) {
14
+ const [result, setResult] = useState<MyGeneInfoResults>()
15
+ const [error, setError] = useState<unknown>()
16
+ const [loading, setLoading] = useState(false)
17
+ useEffect(() => {
18
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
19
+ ;(async () => {
20
+ try {
21
+ if (!id) {
22
+ return
23
+ }
24
+ setLoading(true)
25
+ const res = await jsonfetch(
26
+ `https://mygene.info/v3/query?q=${stripTrailingVersion(id)}&fields=uniprot,symbol`,
27
+ )
28
+ setLoading(false)
29
+ setResult(res)
30
+ } catch (e) {
31
+ console.error(e)
32
+ setError(e)
33
+ }
34
+ })()
35
+ }, [id])
36
+ return { loading, result: result?.hits[0]?.uniprot['Swiss-Prot'], error }
37
+ }
@@ -0,0 +1,36 @@
1
+ import { useEffect, useState } from 'react'
2
+ import { Feature } from '@jbrowse/core/util'
3
+
4
+ // locals
5
+ import { getTranscriptFeatures } from './util'
6
+ import { fetchProteinSeq } from './calculateProteinSequence'
7
+
8
+ export default function useAllSequences({
9
+ feature,
10
+ view,
11
+ }: {
12
+ feature: Feature
13
+ view: { assemblyNames?: string[] } | undefined
14
+ }) {
15
+ const [error, setError] = useState<unknown>()
16
+ const [seqs, setSeqs] = useState<Record<string, string>>()
17
+ useEffect(() => {
18
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
19
+ ;(async () => {
20
+ try {
21
+ const ret = [] as [string, string][]
22
+ for (const f of getTranscriptFeatures(feature)) {
23
+ const seq = await fetchProteinSeq({ view, feature: f })
24
+ if (seq) {
25
+ ret.push([f.id(), seq])
26
+ }
27
+ }
28
+ setSeqs(Object.fromEntries(ret))
29
+ } catch (e) {
30
+ console.error(e)
31
+ setError(e)
32
+ }
33
+ })()
34
+ }, [feature, view])
35
+ return { seqs, error }
36
+ }