jbrowse-plugin-protein3d 0.0.2 → 0.0.3

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 (84) hide show
  1. package/dist/LaunchProteinView/components/AlphaFoldDBSearch.js +44 -27
  2. package/dist/LaunchProteinView/components/AlphaFoldDBSearch.js.map +1 -1
  3. package/dist/LaunchProteinView/components/AlphaFoldDBSearchStatus.d.ts +7 -4
  4. package/dist/LaunchProteinView/components/AlphaFoldDBSearchStatus.js +30 -11
  5. package/dist/LaunchProteinView/components/AlphaFoldDBSearchStatus.js.map +1 -1
  6. package/dist/LaunchProteinView/components/HelpDialog.js +10 -3
  7. package/dist/LaunchProteinView/components/HelpDialog.js.map +1 -1
  8. package/dist/LaunchProteinView/components/LaunchProteinViewDialog.js +3 -7
  9. package/dist/LaunchProteinView/components/LaunchProteinViewDialog.js.map +1 -1
  10. package/dist/LaunchProteinView/components/MSATable.d.ts +10 -0
  11. package/dist/LaunchProteinView/components/MSATable.js +53 -0
  12. package/dist/LaunchProteinView/components/MSATable.js.map +1 -0
  13. package/dist/LaunchProteinView/components/TranscriptSelector.d.ts +7 -3
  14. package/dist/LaunchProteinView/components/TranscriptSelector.js +22 -7
  15. package/dist/LaunchProteinView/components/TranscriptSelector.js.map +1 -1
  16. package/dist/LaunchProteinView/components/UserProvidedStructure.js +56 -43
  17. package/dist/LaunchProteinView/components/UserProvidedStructure.js.map +1 -1
  18. package/dist/LaunchProteinView/components/calculateProteinSequence.js.map +1 -0
  19. package/dist/LaunchProteinView/components/useIsoformProteinSequences.d.ts +14 -0
  20. package/dist/LaunchProteinView/{useProteinSequences.js → components/useIsoformProteinSequences.js} +11 -6
  21. package/dist/LaunchProteinView/components/useIsoformProteinSequences.js.map +1 -0
  22. package/dist/LaunchProteinView/components/useLocalStructureFileSequence.d.ts +7 -0
  23. package/dist/LaunchProteinView/components/useLocalStructureFileSequence.js +44 -0
  24. package/dist/LaunchProteinView/components/useLocalStructureFileSequence.js.map +1 -0
  25. package/dist/LaunchProteinView/{useMyGeneInfo.d.ts → components/useMyGeneInfoUniprotIdLookup.d.ts} +2 -2
  26. package/dist/LaunchProteinView/{useMyGeneInfo.js → components/useMyGeneInfoUniprotIdLookup.js} +15 -10
  27. package/dist/LaunchProteinView/components/useMyGeneInfoUniprotIdLookup.js.map +1 -0
  28. package/dist/LaunchProteinView/components/useRemoteStructureFileSequence.d.ts +7 -0
  29. package/dist/LaunchProteinView/components/useRemoteStructureFileSequence.js +42 -0
  30. package/dist/LaunchProteinView/components/useRemoteStructureFileSequence.js.map +1 -0
  31. package/dist/LaunchProteinView/{util.d.ts → components/util.d.ts} +0 -10
  32. package/dist/LaunchProteinView/{util.js → components/util.js} +3 -24
  33. package/dist/LaunchProteinView/components/util.js.map +1 -0
  34. package/dist/LaunchProteinView/index.js +1 -1
  35. package/dist/LaunchProteinView/index.js.map +1 -1
  36. package/dist/ProteinView/components/Header.js +1 -1
  37. package/dist/ProteinView/components/Header.js.map +1 -1
  38. package/dist/ProteinView/components/SplitString.js +1 -1
  39. package/dist/ProteinView/components/SplitString.js.map +1 -1
  40. package/dist/ProteinView/model.d.ts +8 -4
  41. package/dist/ProteinView/model.js +39 -13
  42. package/dist/ProteinView/model.js.map +1 -1
  43. package/dist/index.js +0 -9
  44. package/dist/index.js.map +1 -1
  45. package/dist/jbrowse-plugin-protein3d.umd.production.min.js +147 -148
  46. package/dist/jbrowse-plugin-protein3d.umd.production.min.js.map +4 -4
  47. package/package.json +2 -2
  48. package/src/LaunchProteinView/components/AlphaFoldDBSearch.tsx +98 -47
  49. package/src/LaunchProteinView/components/AlphaFoldDBSearchStatus.tsx +65 -25
  50. package/src/LaunchProteinView/components/HelpDialog.tsx +35 -7
  51. package/src/LaunchProteinView/components/LaunchProteinViewDialog.tsx +1 -10
  52. package/src/LaunchProteinView/components/MSATable.tsx +96 -0
  53. package/src/LaunchProteinView/components/TranscriptSelector.tsx +36 -10
  54. package/src/LaunchProteinView/components/UserProvidedStructure.tsx +110 -64
  55. package/src/LaunchProteinView/{useProteinSequences.ts → components/useIsoformProteinSequences.ts} +12 -7
  56. package/src/LaunchProteinView/components/useLocalStructureFileSequence.ts +53 -0
  57. package/src/LaunchProteinView/{useMyGeneInfo.ts → components/useMyGeneInfoUniprotIdLookup.ts} +16 -11
  58. package/src/LaunchProteinView/components/useRemoteStructureFileSequence.ts +44 -0
  59. package/src/LaunchProteinView/{util.ts → components/util.ts} +3 -35
  60. package/src/LaunchProteinView/index.ts +1 -1
  61. package/src/ProteinView/components/Header.tsx +7 -5
  62. package/src/ProteinView/components/SplitString.tsx +1 -1
  63. package/src/ProteinView/model.ts +40 -12
  64. package/src/index.ts +1 -12
  65. package/dist/LaunchProteinView/calculateProteinSequence.js.map +0 -1
  66. package/dist/LaunchProteinView/components/PreLoadedStructureMapping.d.ts +0 -8
  67. package/dist/LaunchProteinView/components/PreLoadedStructureMapping.js +0 -72
  68. package/dist/LaunchProteinView/components/PreLoadedStructureMapping.js.map +0 -1
  69. package/dist/LaunchProteinView/components/useCheckAlphaFoldDBExistence.d.ts +0 -7
  70. package/dist/LaunchProteinView/components/useCheckAlphaFoldDBExistence.js +0 -26
  71. package/dist/LaunchProteinView/components/useCheckAlphaFoldDBExistence.js.map +0 -1
  72. package/dist/LaunchProteinView/useMyGeneInfo.js.map +0 -1
  73. package/dist/LaunchProteinView/useProteinSequences.d.ts +0 -10
  74. package/dist/LaunchProteinView/useProteinSequences.js.map +0 -1
  75. package/dist/LaunchProteinView/util.js.map +0 -1
  76. package/dist/ProteinModelSessionExtension.d.ts +0 -11
  77. package/dist/ProteinModelSessionExtension.js +0 -53
  78. package/dist/ProteinModelSessionExtension.js.map +0 -1
  79. package/src/LaunchProteinView/components/PreLoadedStructureMapping.tsx +0 -153
  80. package/src/LaunchProteinView/components/useCheckAlphaFoldDBExistence.ts +0 -31
  81. package/src/ProteinModelSessionExtension.ts +0 -71
  82. /package/dist/LaunchProteinView/{calculateProteinSequence.d.ts → components/calculateProteinSequence.d.ts} +0 -0
  83. /package/dist/LaunchProteinView/{calculateProteinSequence.js → components/calculateProteinSequence.js} +0 -0
  84. /package/src/LaunchProteinView/{calculateProteinSequence.ts → components/calculateProteinSequence.ts} +0 -0
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.0.2",
2
+ "version": "0.0.3",
3
3
  "name": "jbrowse-plugin-protein3d",
4
4
  "keywords": [
5
5
  "jbrowse",
@@ -28,7 +28,7 @@
28
28
  "pako": "^2.1.0"
29
29
  },
30
30
  "devDependencies": {
31
- "@eslint/compat": "^1.1.0",
31
+ "@eslint/compat": "^1.1.1",
32
32
  "@fal-works/esbuild-plugin-global-externals": "^2.1.2",
33
33
  "@jbrowse/core": "^2.0.0",
34
34
  "@jbrowse/plugin-linear-genome-view": "^2.4.2",
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect, useState } from 'react'
2
2
  import { observer } from 'mobx-react'
3
- import { Button, DialogActions, DialogContent } from '@mui/material'
3
+ import { Button, DialogActions, DialogContent, Typography } from '@mui/material'
4
4
  import { makeStyles } from 'tss-react/mui'
5
5
  import {
6
6
  AbstractTrackModel,
@@ -18,15 +18,17 @@ import {
18
18
  getId,
19
19
  getTranscriptDisplayName,
20
20
  getTranscriptFeatures,
21
- } from '../util'
21
+ } from './util'
22
+
23
+ // components
22
24
  import TranscriptSelector from './TranscriptSelector'
23
25
  import HelpButton from './HelpButton'
26
+ import AlphaFoldDBSearchStatus from './AlphaFoldDBSearchStatus'
24
27
 
25
28
  // hooks
26
- import useMyGeneInfo from '../useMyGeneInfo'
27
- import useAllSequences from '../useProteinSequences'
28
- import { useCheckAlphaFoldDBExistence } from './useCheckAlphaFoldDBExistence'
29
- import AlphaFoldDBSearchStatus from './AlphaFoldDBSearchStatus'
29
+ import useMyGeneInfoUniprotIdLookup from './useMyGeneInfoUniprotIdLookup'
30
+ import useRemoteStructureFileSequence from './useRemoteStructureFileSequence'
31
+ import useIsoformProteinSequences from './useIsoformProteinSequences'
30
32
 
31
33
  const useStyles = makeStyles()(theme => ({
32
34
  dialogContent: {
@@ -35,9 +37,7 @@ const useStyles = makeStyles()(theme => ({
35
37
  },
36
38
  }))
37
39
 
38
- type LGV = LinearGenomeViewModel
39
-
40
- const AlphaFoldDBSearch = observer(function AlphaFoldDBSearch({
40
+ const AlphaFoldDBSearch = observer(function ({
41
41
  feature,
42
42
  model,
43
43
  handleClose,
@@ -53,59 +53,108 @@ const AlphaFoldDBSearch = observer(function AlphaFoldDBSearch({
53
53
  // finding exon/CDS subfeatures. we want to select from transcript names
54
54
  const options = getTranscriptFeatures(feature)
55
55
  const [userSelection, setUserSelection] = useState<string>()
56
- const view = getContainingView(model) as LGV
56
+ const view = getContainingView(model) as LinearGenomeViewModel
57
57
  const selectedTranscript = options.find(val => getId(val) === userSelection)
58
- const { seqs, error: error2 } = useAllSequences({ feature, view })
59
- const protein = seqs?.[userSelection ?? '']
60
- const { result: foundStructureId, error } = useMyGeneInfo({
61
- id: selectedTranscript ? getDisplayName(selectedTranscript) : '',
58
+ const {
59
+ isoformSequences,
60
+ isLoading: isIsoformProteinSequencesLoading,
61
+ error: isoformProteinSequencesError,
62
+ } = useIsoformProteinSequences({
63
+ feature,
64
+ view,
65
+ })
66
+ const userSelectedProteinSequence = isoformSequences?.[userSelection ?? '']
67
+ const {
68
+ uniprotId,
69
+ isLoading: isMyGeneLoading,
70
+ error: myGeneError,
71
+ } = useMyGeneInfoUniprotIdLookup({
72
+ id: selectedTranscript
73
+ ? getDisplayName(selectedTranscript)
74
+ : getDisplayName(feature),
62
75
  })
63
76
 
77
+ const url = uniprotId
78
+ ? `https://alphafold.ebi.ac.uk/files/AF-${uniprotId}-F1-model_v4.cif`
79
+ : undefined
80
+ const {
81
+ seq: structureSequence,
82
+ isLoading: isRemoteStructureSequenceLoading,
83
+ error: remoteStructureSequenceError,
84
+ } = useRemoteStructureFileSequence({ url })
85
+ const e =
86
+ myGeneError || isoformProteinSequencesError || remoteStructureSequenceError
87
+
64
88
  useEffect(() => {
65
- if (userSelection === undefined && seqs !== undefined) {
66
- setUserSelection(options.find(f => !!seqs[f.id()])?.id())
89
+ if (isoformSequences !== undefined) {
90
+ const ret =
91
+ options.find(
92
+ f =>
93
+ isoformSequences[f.id()]?.seq.replaceAll('*', '') ==
94
+ structureSequence,
95
+ ) ?? options.find(f => !!isoformSequences[f.id()])
96
+ setUserSelection(ret?.id())
67
97
  }
68
- }, [options, userSelection, seqs])
69
- const {
70
- success,
71
- loading,
72
- error: error3,
73
- } = useCheckAlphaFoldDBExistence({
74
- foundStructureId,
75
- })
98
+ }, [options, structureSequence, isoformSequences])
76
99
 
77
- const e = error || error2 || error3
78
- const url = `https://alphafold.ebi.ac.uk/files/AF-${foundStructureId}-F1-model_v4.cif`
79
100
  return (
80
101
  <>
81
102
  <DialogContent className={classes.dialogContent}>
82
103
  {e ? <ErrorMessage error={e} /> : null}
83
- <div>
84
- Look up AlphaFoldDB structure for given transcript <HelpButton />
85
- </div>
86
- {seqs ? (
104
+ <Typography>
105
+ Automatically find AlphaFoldDB entry for given transcript{' '}
106
+ <HelpButton />
107
+ </Typography>
108
+ {isRemoteStructureSequenceLoading ? (
109
+ <LoadingEllipses
110
+ variant="h6"
111
+ message="Loading sequence from remote structure file"
112
+ />
113
+ ) : null}
114
+ {isMyGeneLoading ? (
115
+ <LoadingEllipses
116
+ variant="h6"
117
+ message="Looking up UniProt ID from mygene.info"
118
+ />
119
+ ) : !uniprotId ? (
120
+ <div>
121
+ UniProt ID not found. Search sequence on AlphaFoldDB{' '}
122
+ <a
123
+ href={`https://alphafold.ebi.ac.uk/search/sequence/${userSelectedProteinSequence?.seq.replaceAll('*', '')}`}
124
+ target="_blank"
125
+ rel="noreferrer"
126
+ >
127
+ here
128
+ </a>{' '}
129
+ <br />
130
+ After visiting the above link, then paste the structure URL into the
131
+ Manual tab
132
+ </div>
133
+ ) : null}
134
+ {isIsoformProteinSequencesLoading ? (
135
+ <LoadingEllipses
136
+ variant="h6"
137
+ message="Loading protein sequences from transcript isoforms"
138
+ />
139
+ ) : null}
140
+ {isoformSequences && structureSequence && selectedTranscript ? (
87
141
  <>
88
142
  <TranscriptSelector
89
143
  val={userSelection ?? ''}
90
144
  setVal={setUserSelection}
91
- options={options}
145
+ structureSequence={structureSequence}
92
146
  feature={feature}
93
- seqs={seqs}
147
+ isoforms={options}
148
+ isoformSequences={isoformSequences}
149
+ />
150
+ <AlphaFoldDBSearchStatus
151
+ uniprotId={uniprotId}
152
+ selectedTranscript={selectedTranscript}
153
+ structureSequence={structureSequence}
154
+ isoformSequences={isoformSequences}
94
155
  />
95
- {selectedTranscript ? (
96
- <AlphaFoldDBSearchStatus
97
- foundStructureId={foundStructureId}
98
- selectedTranscript={selectedTranscript}
99
- success={success}
100
- loading={loading}
101
- />
102
- ) : null}
103
156
  </>
104
- ) : (
105
- <div style={{ margin: 20 }}>
106
- <LoadingEllipses message="Loading protein sequences" variant="h6" />
107
- </div>
108
- )}
157
+ ) : null}
109
158
  </DialogContent>
110
159
  <DialogActions>
111
160
  <Button
@@ -118,12 +167,14 @@ const AlphaFoldDBSearch = observer(function AlphaFoldDBSearch({
118
167
  <Button
119
168
  variant="contained"
120
169
  color="primary"
121
- disabled={!foundStructureId || !protein || !selectedTranscript}
170
+ disabled={
171
+ !uniprotId || !userSelectedProteinSequence || !selectedTranscript
172
+ }
122
173
  onClick={() => {
123
174
  session.addView('ProteinView', {
124
175
  type: 'ProteinView',
125
176
  url,
126
- seq2: protein,
177
+ seq2: userSelectedProteinSequence?.seq,
127
178
  feature: selectedTranscript?.toJSON(),
128
179
  connectedViewId: view.id,
129
180
  displayName: `Protein view ${getGeneDisplayName(feature)} - ${getTranscriptDisplayName(selectedTranscript)}`,
@@ -1,43 +1,83 @@
1
- import React from 'react'
2
- import { Link, Typography } from '@mui/material'
1
+ import React, { useState } from 'react'
2
+ import { Button, Link, Typography } from '@mui/material'
3
3
  import { Feature } from '@jbrowse/core/util'
4
- import { LoadingEllipses } from '@jbrowse/core/ui'
5
4
 
6
5
  // locals
7
- import { getDisplayName } from '../util'
6
+ import { getDisplayName } from './util'
7
+ import MSATable from './MSATable'
8
+
9
+ function NotFound({ uniprotId }: { uniprotId: string }) {
10
+ return (
11
+ <Typography>
12
+ No structure found for this UniProtID in AlphaFoldDB{' '}
13
+ <Link
14
+ target="_blank"
15
+ href={`https://alphafold.ebi.ac.uk/search/text/${uniprotId}`}
16
+ >
17
+ (search for results)
18
+ </Link>
19
+ </Typography>
20
+ )
21
+ }
8
22
 
9
23
  export default function AlphaFoldDBSearchStatus({
10
- foundStructureId,
24
+ uniprotId,
11
25
  selectedTranscript,
12
- success,
13
- loading,
26
+ structureSequence,
27
+ isoformSequences,
14
28
  }: {
15
- foundStructureId?: string
29
+ uniprotId?: string
16
30
  selectedTranscript: Feature
17
- success: boolean
18
- loading: boolean
31
+ structureSequence?: string
32
+ isoformSequences: Record<string, { feature: Feature; seq: string }>
19
33
  }) {
20
- return !foundStructureId ? (
34
+ const url = uniprotId
35
+ ? `https://alphafold.ebi.ac.uk/files/AF-${uniprotId}-F1-model_v4.cif`
36
+ : undefined
37
+ const url2 = uniprotId
38
+ ? `https://www.uniprot.org/uniprotkb/${uniprotId}/entry`
39
+ : undefined
40
+ const [showAllProteinSequences, setShowAllProteinSequences] = useState(false)
41
+
42
+ return !uniprotId ? (
21
43
  <Typography>
22
44
  Searching {getDisplayName(selectedTranscript)} for UniProt ID
23
45
  </Typography>
24
46
  ) : (
25
47
  <>
26
- <Typography>Found Uniprot ID: {foundStructureId}</Typography>
27
- {loading ? (
28
- <LoadingEllipses title="Looking up structure in AlphaFoldDB" />
29
- ) : success ? (
30
- <Typography>Found structure in AlphaFoldDB</Typography>
31
- ) : (
32
- <Typography>
33
- No structure found for this UniProtID in AlphaFoldDB{' '}
34
- <Link
35
- target="_blank"
36
- href={`https://alphafold.ebi.ac.uk/search/text/${foundStructureId}`}
48
+ <Typography>
49
+ Found Uniprot ID:{' '}
50
+ <a href={url2} target="_blank" rel="noreferrer">
51
+ {uniprotId}
52
+ </a>
53
+ </Typography>
54
+ <Typography>
55
+ AlphaFoldDB link:{' '}
56
+ <a href={url} target="_blank" rel="noreferrer">
57
+ {url}
58
+ </a>
59
+ </Typography>
60
+ {structureSequence ? (
61
+ <div style={{ margin: 20 }}>
62
+ <Button
63
+ variant="contained"
64
+ color="primary"
65
+ onClick={() => setShowAllProteinSequences(!showAllProteinSequences)}
37
66
  >
38
- (search for results)
39
- </Link>
40
- </Typography>
67
+ {showAllProteinSequences
68
+ ? 'Hide all isoform protein sequences'
69
+ : 'Show all isoform protein sequences'}
70
+ </Button>
71
+ {showAllProteinSequences ? (
72
+ <MSATable
73
+ structureSequence={structureSequence}
74
+ structureName={uniprotId}
75
+ isoformSequences={isoformSequences}
76
+ />
77
+ ) : null}
78
+ </div>
79
+ ) : (
80
+ <NotFound uniprotId={uniprotId} />
41
81
  )}
42
82
  </>
43
83
  )
@@ -18,18 +18,46 @@ export default function HelpDialog({
18
18
  handleClose: () => void
19
19
  }) {
20
20
  return (
21
- <Dialog open maxWidth="lg" onClose={handleClose} title="Protein alignment">
21
+ <Dialog open maxWidth="lg" onClose={handleClose} title="Help">
22
22
  <DialogContent>
23
23
  <Typography2>
24
- This process searches mygene.info for the transcript ID, in order to
25
- retrieve the UniProt ID associated with a given transcript ID. Then,
26
- it uses that UniProt ID to lookup the structure in AlphaFoldDB because
27
- every UniProt ID has been processed by AlphaFold.
24
+ The procedure for the protein lookup is as follows:
25
+ <ul>
26
+ <li>
27
+ (Automatic lookup) Searches mygene.info for the transcript ID, in
28
+ order to retrieve the UniProt ID associated with a given
29
+ transcript ID and then, the UniProt ID is used to lookup the
30
+ structure in AlphaFoldDB
31
+ </li>
32
+ <li>
33
+ (Manual) Allows you to choose your own structure file from your
34
+ local machine (e.g. a PDB file predicted by e.g. ColabFold) or
35
+ supply a specific URL
36
+ </li>
37
+ <li>
38
+ The residues from the structure are downloaded, and then you can
39
+ choose the transcript isoform from the selected gene that best
40
+ represents the structure. Asterisks are displayed if there is an
41
+ exact sequence match
42
+ </li>
43
+ <li>
44
+ The residues from the structure are finally aligned to the to the
45
+ selected transcript&apos;s protein sequence representation, and
46
+ this creates a mapping from the reference genome coordinates to
47
+ positions in the 3-D structure
48
+ </li>
49
+ <li>
50
+ Finally the molstar panel is opened, and this contains many
51
+ specialized features features, plus additional mouseover and
52
+ selection features supplied by the plugin to connect mouse click
53
+ actions and mouse hover with coordinates on the linear genome view
54
+ </li>
55
+ </ul>
28
56
  </Typography2>
29
57
  <Typography2>
30
58
  If you run into challenges with this workflow e.g. your transcripts
31
- are not being found in mygene.info and you are interested in using
32
- this plugin, contact colin.diesh@gmail.com
59
+ are not being found in mygene.info then you can use the Manual import
60
+ form, or contact colin.diesh@gmail.com for troubleshooting
33
61
  </Typography2>
34
62
  </DialogContent>
35
63
  <Divider />
@@ -4,7 +4,6 @@ import { Tab, Tabs } from '@mui/material'
4
4
  import { AbstractTrackModel, Feature } from '@jbrowse/core/util'
5
5
 
6
6
  // locals
7
- import PreLoadedStructureMapping from './PreLoadedStructureMapping'
8
7
  import AlphaFoldDBSearch from './AlphaFoldDBSearch'
9
8
  import UserProvidedStructure from './UserProvidedStructure'
10
9
  import TabPanel from './TabPanel'
@@ -27,9 +26,8 @@ export default function LaunchProteinViewDialog({
27
26
  open
28
27
  >
29
28
  <Tabs value={choice} onChange={(_, val) => setChoice(val)}>
30
- <Tab value={0} label="AlphaFoldDB search" />
29
+ <Tab value={0} label="Automatic lookup" />
31
30
  <Tab value={1} label="Manual" />
32
- <Tab value={2} label="Pre-configured" />
33
31
  </Tabs>
34
32
  <TabPanel value={choice} index={0}>
35
33
  <AlphaFoldDBSearch
@@ -45,13 +43,6 @@ export default function LaunchProteinViewDialog({
45
43
  handleClose={handleClose}
46
44
  />
47
45
  </TabPanel>
48
- <TabPanel value={choice} index={2}>
49
- <PreLoadedStructureMapping
50
- feature={feature}
51
- model={model}
52
- handleClose={handleClose}
53
- />
54
- </TabPanel>
55
46
  </Dialog>
56
47
  )
57
48
  }
@@ -0,0 +1,96 @@
1
+ import React, { useState } from 'react'
2
+ import { Checkbox, FormControlLabel, TextField } from '@mui/material'
3
+ import { Feature, max } from '@jbrowse/core/util'
4
+ import { makeStyles } from 'tss-react/mui'
5
+
6
+ // locals
7
+ import { getTranscriptDisplayName } from './util'
8
+
9
+ const useStyles = makeStyles()({
10
+ textAreaFont: {
11
+ fontFamily: 'Courier New',
12
+ whiteSpace: 'pre',
13
+ },
14
+ margin: {
15
+ marginLeft: 20,
16
+ },
17
+ })
18
+
19
+ export default function MSATable({
20
+ structureName,
21
+ structureSequence,
22
+ isoformSequences,
23
+ }: {
24
+ structureName: string
25
+ structureSequence: string
26
+ isoformSequences: Record<string, { feature: Feature; seq: string }>
27
+ }) {
28
+ const { classes } = useStyles()
29
+ const [showInFastaFormat, setShowInFastaFormat] = useState(false)
30
+ const removedStars = Object.fromEntries(
31
+ Object.entries(isoformSequences).map(([key, val]) => [
32
+ key,
33
+ { ...val, seq: val.seq.replaceAll('*', '') },
34
+ ]),
35
+ )
36
+ const exactMatchIsoformAndStructureSeq = Object.entries(removedStars).find(
37
+ ([_, val]) => structureSequence === val.seq,
38
+ )
39
+ const sname = `${structureName || ''} (structure residues)`
40
+ const maxKeyLen = max([
41
+ sname.length,
42
+ ...Object.entries(removedStars).map(
43
+ ([_, val]) => getTranscriptDisplayName(val.feature).length,
44
+ ),
45
+ ])
46
+
47
+ const l1 = [
48
+ `${sname.padEnd(maxKeyLen)}${exactMatchIsoformAndStructureSeq ? '*' : ' '} ${structureSequence}`,
49
+ exactMatchIsoformAndStructureSeq
50
+ ? `${getTranscriptDisplayName(exactMatchIsoformAndStructureSeq[1].feature).padEnd(maxKeyLen)}* ${exactMatchIsoformAndStructureSeq[1].seq}`
51
+ : undefined,
52
+ ...Object.entries(removedStars)
53
+ .map(
54
+ ([_, val]) =>
55
+ `${getTranscriptDisplayName(val.feature).padEnd(maxKeyLen)} ${val.seq}`,
56
+ )
57
+ .filter(([k]) => k !== exactMatchIsoformAndStructureSeq?.[0]),
58
+ ]
59
+ .filter(f => !!f)
60
+ .join('\n')
61
+
62
+ const l2 = [
63
+ `>${sname}\n${structureSequence}`,
64
+ ...Object.values(removedStars).map(
65
+ ({ feature, seq }) => `>${getTranscriptDisplayName(feature)}\n${seq}`,
66
+ ),
67
+ ].join('\n')
68
+ return (
69
+ <>
70
+ <FormControlLabel
71
+ className={classes.margin}
72
+ control={
73
+ <Checkbox
74
+ onChange={event => setShowInFastaFormat(event.target.checked)}
75
+ checked={showInFastaFormat}
76
+ />
77
+ }
78
+ label="Show in FASTA format?"
79
+ />
80
+ <TextField
81
+ variant="outlined"
82
+ multiline
83
+ minRows={5}
84
+ maxRows={10}
85
+ fullWidth
86
+ value={showInFastaFormat ? l2 : l1}
87
+ InputProps={{
88
+ readOnly: true,
89
+ classes: {
90
+ input: classes.textAreaFont,
91
+ },
92
+ }}
93
+ />
94
+ </>
95
+ )
96
+ }
@@ -3,7 +3,7 @@ import { MenuItem, TextField, TextFieldProps } from '@mui/material'
3
3
  import { Feature } from '@jbrowse/core/util'
4
4
 
5
5
  // locals
6
- import { getGeneDisplayName, getTranscriptDisplayName } from '../util'
6
+ import { getGeneDisplayName, getTranscriptDisplayName } from './util'
7
7
 
8
8
  function TextField2({ children, ...rest }: TextFieldProps) {
9
9
  return (
@@ -16,15 +16,17 @@ function TextField2({ children, ...rest }: TextFieldProps) {
16
16
  export default function TranscriptSelector({
17
17
  val,
18
18
  setVal,
19
- options,
19
+ isoforms,
20
+ isoformSequences,
21
+ structureSequence,
20
22
  feature,
21
- seqs,
22
23
  }: {
23
- options: Feature[]
24
+ isoforms: Feature[]
24
25
  feature: Feature
25
26
  val: string
26
27
  setVal: (str: string) => void
27
- seqs: Record<string, string>
28
+ structureSequence: string
29
+ isoformSequences: Record<string, { feature: Feature; seq: string }>
28
30
  }) {
29
31
  return (
30
32
  <TextField2
@@ -33,16 +35,40 @@ export default function TranscriptSelector({
33
35
  label="Choose transcript isoform"
34
36
  select
35
37
  >
36
- {options
37
- .filter(f => !!seqs[f.id()])
38
+ {isoforms
39
+ .filter(f => !!isoformSequences[f.id()])
40
+ .filter(
41
+ f =>
42
+ isoformSequences[f.id()].seq.replaceAll('*', '') ===
43
+ structureSequence,
44
+ )
38
45
  .map(f => (
39
46
  <MenuItem value={f.id()} key={f.id()}>
40
47
  {getGeneDisplayName(feature)} - {getTranscriptDisplayName(f)} (
41
- {seqs[f.id()].length}aa)
48
+ {isoformSequences[f.id()].seq.length}aa) (matches structure
49
+ residues)
42
50
  </MenuItem>
43
51
  ))}
44
- {options
45
- .filter(f => !seqs[f.id()])
52
+ {isoforms
53
+ .filter(f => !!isoformSequences[f.id()])
54
+ .filter(
55
+ f =>
56
+ isoformSequences[f.id()].seq.replaceAll('*', '') !==
57
+ structureSequence,
58
+ )
59
+ .sort(
60
+ (a, b) =>
61
+ isoformSequences[b.id()].seq.length -
62
+ isoformSequences[a.id()].seq.length,
63
+ )
64
+ .map(f => (
65
+ <MenuItem value={f.id()} key={f.id()}>
66
+ {getGeneDisplayName(feature)} - {getTranscriptDisplayName(f)} (
67
+ {isoformSequences[f.id()].seq.length}aa)
68
+ </MenuItem>
69
+ ))}
70
+ {isoforms
71
+ .filter(f => !isoformSequences[f.id()])
46
72
  .map(f => (
47
73
  <MenuItem value={f.id()} key={f.id()} disabled>
48
74
  {getGeneDisplayName(feature)} - {getTranscriptDisplayName(f)} (no