jbrowse-plugin-msaview 1.0.18 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/CHANGELOG.md +6 -4
  2. package/README.md +6 -4
  3. package/dist/AddHighlightModel/GenomeMouseoverHighlight.d.ts +6 -0
  4. package/dist/AddHighlightModel/GenomeMouseoverHighlight.js +30 -0
  5. package/dist/AddHighlightModel/GenomeMouseoverHighlight.js.map +1 -0
  6. package/dist/AddHighlightModel/HighlightComponents.d.ts +7 -0
  7. package/dist/AddHighlightModel/HighlightComponents.js +12 -0
  8. package/dist/AddHighlightModel/HighlightComponents.js.map +1 -0
  9. package/dist/AddHighlightModel/MsaToGenomeHighlight.d.ts +7 -0
  10. package/dist/AddHighlightModel/MsaToGenomeHighlight.js +26 -0
  11. package/dist/AddHighlightModel/MsaToGenomeHighlight.js.map +1 -0
  12. package/dist/AddHighlightModel/index.d.ts +2 -0
  13. package/dist/AddHighlightModel/index.js +14 -0
  14. package/dist/AddHighlightModel/index.js.map +1 -0
  15. package/dist/AddHighlightModel/util.d.ts +9 -0
  16. package/dist/AddHighlightModel/util.js +14 -0
  17. package/dist/AddHighlightModel/util.js.map +1 -0
  18. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.d.ts +7 -0
  19. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js +20 -0
  20. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js.map +1 -0
  21. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/NcbiBlastPanel.d.ts +8 -0
  22. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/NcbiBlastPanel.js +76 -0
  23. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/NcbiBlastPanel.js.map +1 -0
  24. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/calculateProteinSequence.d.ts +26 -0
  25. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/calculateProteinSequence.js +48 -0
  26. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/calculateProteinSequence.js.map +1 -0
  27. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/index.d.ts +2 -0
  28. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/index.js +3 -0
  29. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/index.js.map +1 -0
  30. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/ncbiBlastLaunchView.d.ts +8 -0
  31. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/ncbiBlastLaunchView.js +17 -0
  32. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/ncbiBlastLaunchView.js.map +1 -0
  33. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/useFeatureSequence.d.ts +20 -0
  34. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/useFeatureSequence.js +64 -0
  35. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/useFeatureSequence.js.map +1 -0
  36. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/util.d.ts +5 -0
  37. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/util.js +7 -0
  38. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/util.js.map +1 -0
  39. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.d.ts +8 -0
  40. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +90 -0
  41. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js.map +1 -0
  42. package/dist/LaunchMsaView/components/PreLoadedMSA/fetchGeneList.d.ts +1 -0
  43. package/dist/LaunchMsaView/components/PreLoadedMSA/fetchGeneList.js +12 -0
  44. package/dist/LaunchMsaView/components/PreLoadedMSA/fetchGeneList.js.map +1 -0
  45. package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.d.ts +9 -0
  46. package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.js +36 -0
  47. package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.js.map +1 -0
  48. package/dist/LaunchMsaView/components/TabUtils.d.ts +12 -0
  49. package/dist/LaunchMsaView/components/TabUtils.js +13 -0
  50. package/dist/LaunchMsaView/components/TabUtils.js.map +1 -0
  51. package/dist/LaunchMsaView/index.d.ts +2 -0
  52. package/dist/LaunchMsaView/index.js +50 -0
  53. package/dist/LaunchMsaView/index.js.map +1 -0
  54. package/dist/LaunchMsaView/util.d.ts +5 -0
  55. package/dist/LaunchMsaView/util.js +25 -0
  56. package/dist/LaunchMsaView/util.js.map +1 -0
  57. package/dist/MsaViewPanel/components/LoadingBLAST.d.ts +6 -0
  58. package/dist/MsaViewPanel/components/LoadingBLAST.js +36 -0
  59. package/dist/MsaViewPanel/components/LoadingBLAST.js.map +1 -0
  60. package/dist/MsaViewPanel/components/MsaViewPanel.d.ts +6 -0
  61. package/dist/MsaViewPanel/components/MsaViewPanel.js +10 -0
  62. package/dist/MsaViewPanel/components/MsaViewPanel.js.map +1 -0
  63. package/dist/MsaViewPanel/components/RIDLink.d.ts +5 -0
  64. package/dist/MsaViewPanel/components/RIDLink.js +17 -0
  65. package/dist/MsaViewPanel/components/RIDLink.js.map +1 -0
  66. package/dist/MsaViewPanel/doLaunchBlast.d.ts +7 -0
  67. package/dist/MsaViewPanel/doLaunchBlast.js +27 -0
  68. package/dist/MsaViewPanel/doLaunchBlast.js.map +1 -0
  69. package/dist/MsaViewPanel/genomeToMSA.d.ts +4 -0
  70. package/dist/MsaViewPanel/genomeToMSA.js +18 -0
  71. package/dist/MsaViewPanel/genomeToMSA.js.map +1 -0
  72. package/dist/MsaViewPanel/index.d.ts +2 -0
  73. package/dist/MsaViewPanel/index.js +16 -0
  74. package/dist/MsaViewPanel/index.js.map +1 -0
  75. package/dist/MsaViewPanel/model.d.ts +596 -0
  76. package/dist/MsaViewPanel/model.js +235 -0
  77. package/dist/MsaViewPanel/model.js.map +1 -0
  78. package/dist/MsaViewPanel/msaCoordToGenomeCoord.d.ts +9 -0
  79. package/dist/MsaViewPanel/msaCoordToGenomeCoord.js +20 -0
  80. package/dist/MsaViewPanel/msaCoordToGenomeCoord.js.map +1 -0
  81. package/dist/MsaViewPanel/util.d.ts +8 -0
  82. package/dist/MsaViewPanel/util.js +7 -0
  83. package/dist/MsaViewPanel/util.js.map +1 -0
  84. package/dist/OpenInNewIcon.d.ts +3 -0
  85. package/dist/OpenInNewIcon.js +8 -0
  86. package/dist/OpenInNewIcon.js.map +1 -0
  87. package/dist/TextField2.d.ts +4 -0
  88. package/dist/TextField2.js +8 -0
  89. package/dist/TextField2.js.map +1 -0
  90. package/dist/index.d.ts +8 -8
  91. package/dist/index.js +32 -7
  92. package/dist/index.js.map +1 -0
  93. package/dist/jbrowse-plugin-msaview.umd.production.min.js +109 -1
  94. package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +7 -1
  95. package/dist/utils/fetch.d.ts +4 -0
  96. package/dist/utils/fetch.js +19 -0
  97. package/dist/utils/fetch.js.map +1 -0
  98. package/dist/utils/msa.d.ts +8 -0
  99. package/dist/utils/msa.js +75 -0
  100. package/dist/utils/msa.js.map +1 -0
  101. package/dist/utils/ncbiBlast.d.ts +20 -0
  102. package/dist/utils/ncbiBlast.js +66 -0
  103. package/dist/utils/ncbiBlast.js.map +1 -0
  104. package/package.json +51 -40
  105. package/src/AddHighlightModel/GenomeMouseoverHighlight.tsx +44 -0
  106. package/src/AddHighlightModel/HighlightComponents.tsx +24 -0
  107. package/src/AddHighlightModel/MsaToGenomeHighlight.tsx +50 -0
  108. package/src/AddHighlightModel/index.tsx +27 -0
  109. package/src/AddHighlightModel/util.ts +14 -0
  110. package/src/LaunchMsaView/components/LaunchMsaViewDialog.tsx +52 -0
  111. package/src/LaunchMsaView/components/NewNCBIBlastQuery/NcbiBlastPanel.tsx +160 -0
  112. package/src/LaunchMsaView/components/NewNCBIBlastQuery/calculateProteinSequence.ts +92 -0
  113. package/src/LaunchMsaView/components/NewNCBIBlastQuery/index.tsx +2 -0
  114. package/src/LaunchMsaView/components/NewNCBIBlastQuery/ncbiBlastLaunchView.ts +28 -0
  115. package/src/LaunchMsaView/components/NewNCBIBlastQuery/useFeatureSequence.ts +85 -0
  116. package/src/LaunchMsaView/components/NewNCBIBlastQuery/util.ts +7 -0
  117. package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +149 -0
  118. package/src/LaunchMsaView/components/PreLoadedMSA/fetchGeneList.ts +13 -0
  119. package/src/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.ts +54 -0
  120. package/src/LaunchMsaView/components/TabUtils.tsx +30 -0
  121. package/src/LaunchMsaView/index.ts +61 -0
  122. package/src/LaunchMsaView/util.ts +30 -0
  123. package/src/MsaViewPanel/components/LoadingBLAST.tsx +59 -0
  124. package/src/MsaViewPanel/components/MsaViewPanel.tsx +22 -0
  125. package/src/MsaViewPanel/components/RIDLink.tsx +18 -0
  126. package/src/MsaViewPanel/doLaunchBlast.ts +38 -0
  127. package/src/MsaViewPanel/genomeToMSA.ts +21 -0
  128. package/src/MsaViewPanel/index.ts +19 -0
  129. package/src/MsaViewPanel/model.ts +290 -0
  130. package/src/MsaViewPanel/msaCoordToGenomeCoord.ts +29 -0
  131. package/src/MsaViewPanel/util.ts +13 -0
  132. package/src/OpenInNewIcon.tsx +11 -0
  133. package/src/TextField2.tsx +12 -0
  134. package/src/index.ts +12 -14
  135. package/src/utils/fetch.ts +25 -0
  136. package/src/utils/msa.ts +111 -0
  137. package/src/utils/ncbiBlast.ts +105 -0
  138. package/dist/config.json +0 -8
  139. package/dist/jbrowse-plugin-msaview.cjs.development.js +0 -168
  140. package/dist/jbrowse-plugin-msaview.cjs.development.js.map +0 -1
  141. package/dist/jbrowse-plugin-msaview.cjs.production.min.js +0 -2
  142. package/dist/jbrowse-plugin-msaview.cjs.production.min.js.map +0 -1
  143. package/dist/jbrowse-plugin-msaview.esm.js +0 -163
  144. package/dist/jbrowse-plugin-msaview.esm.js.map +0 -1
  145. package/dist/jbrowse-plugin-msaview.umd.development.js +0 -8659
  146. package/dist/jbrowse-plugin-msaview.umd.development.js.map +0 -1
  147. package/src/index.test.ts +0 -3
@@ -0,0 +1,160 @@
1
+ import React, { useState } from 'react'
2
+ import { observer } from 'mobx-react'
3
+ import { Button, DialogActions, DialogContent, MenuItem } from '@mui/material'
4
+ import { makeStyles } from 'tss-react/mui'
5
+ import {
6
+ AbstractTrackModel,
7
+ Feature,
8
+ getContainingView,
9
+ } from '@jbrowse/core/util'
10
+ import { ErrorMessage } from '@jbrowse/core/ui'
11
+ import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
12
+
13
+ // locals
14
+ import {
15
+ getId,
16
+ getTranscriptDisplayName,
17
+ getTranscriptFeatures,
18
+ getGeneDisplayName,
19
+ } from '../../util'
20
+ import { getProteinSequence } from './calculateProteinSequence'
21
+ import { useFeatureSequence } from './useFeatureSequence'
22
+ import TextField2 from '../../../TextField2'
23
+ import { ncbiBlastLaunchView } from './ncbiBlastLaunchView'
24
+
25
+ const useStyles = makeStyles()({
26
+ dialogContent: {
27
+ width: '80em',
28
+ },
29
+ textAreaFont: {
30
+ fontFamily: 'Courier New',
31
+ },
32
+ })
33
+
34
+ const NcbiBlastPanel = observer(function NcbiBlastPanel2({
35
+ handleClose,
36
+ feature,
37
+ model,
38
+ }: {
39
+ model: AbstractTrackModel
40
+ feature: Feature
41
+ handleClose: () => void
42
+ }) {
43
+ const { classes } = useStyles()
44
+ const view = getContainingView(model) as LinearGenomeViewModel
45
+ const [blastDatabase, setBlastDatabase] = useState('nr')
46
+ const [msaAlgorithm, setMsaAlgorithm] = useState('clustalo')
47
+
48
+ const options = getTranscriptFeatures(feature)
49
+ const [userSelection, setUserSelection] = useState(getId(options[0]))
50
+ const selectedTranscript = options.find(val => getId(val) === userSelection)!
51
+ const { sequence, error } = useFeatureSequence({
52
+ view,
53
+ feature: selectedTranscript,
54
+ })
55
+ const proteinSequence =
56
+ sequence && !('error' in sequence)
57
+ ? getProteinSequence({
58
+ seq: sequence.seq,
59
+ selectedTranscript,
60
+ })
61
+ : ''
62
+
63
+ const blastDatabaseOptions = ['nr', 'nr_cluster_seq']
64
+ const msaAlgorithms = ['clustalo', 'muscle', 'kalign', 'mafft']
65
+ return (
66
+ <DialogContent className={classes.dialogContent}>
67
+ {error ? <ErrorMessage error={error} /> : null}
68
+ <TextField2
69
+ value={blastDatabase}
70
+ onChange={event => setBlastDatabase(event.target.value)}
71
+ label="BLAST blastDatabase"
72
+ select
73
+ >
74
+ {blastDatabaseOptions.map(val => (
75
+ <MenuItem value={val} key={val}>
76
+ {val}
77
+ </MenuItem>
78
+ ))}
79
+ </TextField2>
80
+
81
+ <TextField2
82
+ value={msaAlgorithm}
83
+ onChange={event => setMsaAlgorithm(event.target.value)}
84
+ label="MSA Algorithm"
85
+ select
86
+ >
87
+ {msaAlgorithms.map(val => (
88
+ <MenuItem value={val} key={val}>
89
+ {val}
90
+ </MenuItem>
91
+ ))}
92
+ </TextField2>
93
+
94
+ <TextField2
95
+ value={userSelection}
96
+ onChange={event => setUserSelection(event.target.value)}
97
+ label="Choose isoform to BLAST"
98
+ select
99
+ >
100
+ {options.map(val => (
101
+ <MenuItem value={getId(val)} key={val.id()}>
102
+ {getGeneDisplayName(feature)} - {getTranscriptDisplayName(val)}
103
+ </MenuItem>
104
+ ))}
105
+ </TextField2>
106
+ <TextField2
107
+ variant="outlined"
108
+ multiline
109
+ minRows={5}
110
+ maxRows={10}
111
+ fullWidth
112
+ value={
113
+ !proteinSequence
114
+ ? 'Loading...'
115
+ : `>${getTranscriptDisplayName(selectedTranscript)}\n${proteinSequence}`
116
+ }
117
+ InputProps={{
118
+ readOnly: true,
119
+ classes: {
120
+ input: classes.textAreaFont,
121
+ },
122
+ }}
123
+ />
124
+
125
+ <DialogActions>
126
+ <Button
127
+ color="primary"
128
+ variant="contained"
129
+ onClick={() => {
130
+ const newView = ncbiBlastLaunchView({
131
+ feature: selectedTranscript,
132
+ view,
133
+ newViewTitle: `NCBI BLAST - ${getGeneDisplayName(feature)} - ${getTranscriptDisplayName(selectedTranscript)} - ${msaAlgorithm}`,
134
+ })
135
+ newView.setBlastParams({
136
+ blastProgram: 'blastp',
137
+ blastDatabase,
138
+ msaAlgorithm,
139
+ selectedTranscript,
140
+ proteinSequence,
141
+ })
142
+ handleClose()
143
+ }}
144
+ disabled={!proteinSequence}
145
+ >
146
+ Submit
147
+ </Button>
148
+ <Button
149
+ color="secondary"
150
+ variant="contained"
151
+ onClick={() => handleClose()}
152
+ >
153
+ Cancel
154
+ </Button>
155
+ </DialogActions>
156
+ </DialogContent>
157
+ )
158
+ })
159
+
160
+ export default NcbiBlastPanel
@@ -0,0 +1,92 @@
1
+ import {
2
+ Feature,
3
+ defaultCodonTable,
4
+ generateCodonTable,
5
+ revcom,
6
+ } from '@jbrowse/core/util'
7
+
8
+ export interface Feat {
9
+ start: number
10
+ end: number
11
+ type: string
12
+ }
13
+
14
+ export function stitch(subfeats: Feat[], sequence: string) {
15
+ return subfeats.map(sub => sequence.slice(sub.start, sub.end)).join('')
16
+ }
17
+
18
+ export function calculateProteinSequence({
19
+ cds,
20
+ sequence,
21
+ codonTable,
22
+ }: {
23
+ cds: Feat[]
24
+ sequence: string
25
+ codonTable: Record<string, string>
26
+ }) {
27
+ const str = stitch(cds, sequence)
28
+ let protein = ''
29
+ for (let i = 0; i < str.length; i += 3) {
30
+ // use & symbol for undefined codon, or partial slice
31
+ protein += codonTable[str.slice(i, i + 3)] || '&'
32
+ }
33
+ return protein
34
+ }
35
+
36
+ export function revlist(
37
+ list: { start: number; end: number; type: string }[],
38
+ seqlen: number,
39
+ ) {
40
+ return list
41
+ .map(sub => ({
42
+ ...sub,
43
+ start: seqlen - sub.end,
44
+ end: seqlen - sub.start,
45
+ }))
46
+ .sort((a, b) => a.start - b.start)
47
+ }
48
+
49
+ // filter items if they have the same "ID" or location
50
+ function getItemId(feat: Feat) {
51
+ return `${feat.start}-${feat.end}`
52
+ }
53
+
54
+ // filters if successive elements share same start/end
55
+ export function dedupe(list: Feat[]) {
56
+ return list.filter(
57
+ (item, pos, ary) => !pos || getItemId(item) !== getItemId(ary[pos - 1]),
58
+ )
59
+ }
60
+
61
+ export function getProteinSequence({
62
+ selectedTranscript,
63
+ seq,
64
+ }: {
65
+ seq: string
66
+ selectedTranscript: Feature
67
+ }) {
68
+ // @ts-expect-error
69
+ const f = selectedTranscript.toJSON() as {
70
+ start: number
71
+ end: number
72
+ strand: number
73
+ type: string
74
+ subfeatures: { start: number; end: number; type: string }[]
75
+ }
76
+ const cds = dedupe(
77
+ f.subfeatures
78
+ .sort((a, b) => a.start - b.start)
79
+ .map(sub => ({
80
+ ...sub,
81
+ start: sub.start - f.start,
82
+ end: sub.end - f.start,
83
+ }))
84
+ .filter(f => f.type === 'CDS'),
85
+ )
86
+
87
+ return calculateProteinSequence({
88
+ cds: f.strand === -1 ? revlist(cds, seq.length) : cds,
89
+ sequence: f.strand === -1 ? revcom(seq) : seq,
90
+ codonTable: generateCodonTable(defaultCodonTable),
91
+ })
92
+ }
@@ -0,0 +1,2 @@
1
+ import NcbiBlastPanel from './NcbiBlastPanel'
2
+ export default NcbiBlastPanel
@@ -0,0 +1,28 @@
1
+ import { Feature, getSession } from '@jbrowse/core/util'
2
+ import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
3
+ // locals
4
+ import { JBrowsePluginMsaViewModel } from '../../../MsaViewPanel/model'
5
+
6
+ export function ncbiBlastLaunchView({
7
+ newViewTitle,
8
+ view,
9
+ feature,
10
+ }: {
11
+ newViewTitle: string
12
+ view: LinearGenomeViewModel
13
+ feature: Feature
14
+ }) {
15
+ return getSession(view).addView('MsaView', {
16
+ type: 'MsaView',
17
+ displayName: newViewTitle,
18
+ connectedViewId: view.id,
19
+ connectedFeature: feature.toJSON(),
20
+ treeAreaWidth: 250,
21
+ treeWidth: 100,
22
+ drawNodeBubbles: true,
23
+ labelsAlignRight: true,
24
+ colWidth: 10,
25
+ rowHeight: 12,
26
+ colorSchemeName: 'percent_identity_dynamic',
27
+ }) as JBrowsePluginMsaViewModel
28
+ }
@@ -0,0 +1,85 @@
1
+ import { useEffect, useState } from 'react'
2
+
3
+ import { Feature, getSession } from '@jbrowse/core/util'
4
+ import { getConf } from '@jbrowse/core/configuration'
5
+
6
+ export interface SeqState {
7
+ seq: string
8
+ upstream?: string
9
+ downstream?: string
10
+ }
11
+
12
+ export interface ErrorState {
13
+ error: string
14
+ }
15
+ const BPLIMIT = 500_000
16
+
17
+ export function useFeatureSequence({
18
+ view,
19
+ feature,
20
+ upDownBp = 0,
21
+ forceLoad = true,
22
+ }: {
23
+ view: { assemblyNames?: string[] } | undefined
24
+ feature: Feature
25
+ upDownBp?: number
26
+ forceLoad?: boolean
27
+ }) {
28
+ const [sequence, setSequence] = useState<SeqState | ErrorState>()
29
+ const [error, setError] = useState<unknown>()
30
+ useEffect(() => {
31
+ if (view) {
32
+ const { assemblyManager, rpcManager } = getSession(view)
33
+ const [assemblyName] = view?.assemblyNames || []
34
+ async function fetchSeq(start: number, end: number, refName: string) {
35
+ const assembly = await assemblyManager.waitForAssembly(assemblyName)
36
+ if (!assembly) {
37
+ throw new Error('assembly not found')
38
+ }
39
+ const sessionId = 'getSequence'
40
+ const feats = await rpcManager.call(sessionId, 'CoreGetFeatures', {
41
+ adapterConfig: getConf(assembly, ['sequence', 'adapter']),
42
+ sessionId,
43
+ regions: [
44
+ {
45
+ start,
46
+ end,
47
+ refName: assembly.getCanonicalRefName(refName),
48
+ assemblyName,
49
+ },
50
+ ],
51
+ })
52
+
53
+ const [feat] = feats as Feature[]
54
+ return (feat?.get('seq') as string | undefined) || ''
55
+ }
56
+
57
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
58
+ ;(async () => {
59
+ try {
60
+ setError(undefined)
61
+ setSequence(undefined)
62
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
+ const { start, end, refName } = feature.toJSON() as any
64
+
65
+ if (!forceLoad && end - start > BPLIMIT) {
66
+ setSequence({
67
+ error: `Genomic sequence larger than ${BPLIMIT}bp, use "force load" button to display`,
68
+ })
69
+ } else {
70
+ const b = start - upDownBp
71
+ const e = end + upDownBp
72
+ const seq = await fetchSeq(start, end, refName)
73
+ const up = await fetchSeq(Math.max(0, b), start, refName)
74
+ const down = await fetchSeq(end, e, refName)
75
+ setSequence({ seq, upstream: up, downstream: down })
76
+ }
77
+ } catch (e) {
78
+ console.error(e)
79
+ setError(e)
80
+ }
81
+ })()
82
+ }
83
+ }, [feature, view, upDownBp, forceLoad])
84
+ return { sequence, error }
85
+ }
@@ -0,0 +1,7 @@
1
+ export function makeId(h: { accession: string; sciname: string }) {
2
+ return `${h.accession}-${h.sciname.replaceAll(' ', '_')}`
3
+ }
4
+
5
+ export function strip(s: string) {
6
+ return s.replace('-', '')
7
+ }
@@ -0,0 +1,149 @@
1
+ import React, { useEffect, useState } from 'react'
2
+ import { observer } from 'mobx-react'
3
+ import { ErrorMessage } from '@jbrowse/core/ui'
4
+ import {
5
+ Button,
6
+ DialogActions,
7
+ DialogContent,
8
+ MenuItem,
9
+ TextField,
10
+ Typography,
11
+ } from '@mui/material'
12
+ import {
13
+ AbstractTrackModel,
14
+ Feature,
15
+ getContainingView,
16
+ getSession,
17
+ } from '@jbrowse/core/util'
18
+ import { makeStyles } from 'tss-react/mui'
19
+ import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
20
+
21
+ // locals
22
+ import {
23
+ getTranscriptDisplayName,
24
+ getId,
25
+ getTranscriptFeatures,
26
+ } from '../../util'
27
+ import { fetchGeneList } from './fetchGeneList'
28
+ import { preCalculatedLaunchView } from './preCalculatedLaunchView'
29
+
30
+ const useStyles = makeStyles()({
31
+ dialogContent: {
32
+ width: '80em',
33
+ },
34
+ })
35
+
36
+ const PreLoadedMSA = observer(function PreLoadedMSA2({
37
+ model,
38
+ feature,
39
+ handleClose,
40
+ }: {
41
+ model: AbstractTrackModel
42
+ feature: Feature
43
+ handleClose: () => void
44
+ }) {
45
+ const session = getSession(model)
46
+ const view = getContainingView(model) as LinearGenomeViewModel
47
+ const { classes } = useStyles()
48
+ const [error, setError] = useState<unknown>()
49
+ const [geneNameList, setGeneNameList] = useState<string[]>()
50
+ useEffect(() => {
51
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
52
+ ;(async () => {
53
+ try {
54
+ const data = await fetchGeneList()
55
+ setGeneNameList(data)
56
+ const set = new Set(data)
57
+ const options = getTranscriptFeatures(feature)
58
+ const ret = options.find(val => set.has(getId(val)))
59
+ if (ret) {
60
+ setUserSelection(getId(ret))
61
+ }
62
+ } catch (e) {
63
+ console.error(e)
64
+ setError(e)
65
+ }
66
+ })()
67
+ }, [feature])
68
+ const set = new Set(geneNameList)
69
+ const options = getTranscriptFeatures(feature)
70
+ const ret = options.find(val => set.has(getId(val)))
71
+ const [userSelection, setUserSelection] = useState(getId(options[0]))
72
+
73
+ return (
74
+ <>
75
+ <DialogContent className={classes.dialogContent}>
76
+ <Typography>
77
+ The source data for these multiple sequence alignments is from{' '}
78
+ <a href="https://hgdownload.soe.ucsc.edu/goldenPath/hg38/multiz100way/alignments/">
79
+ knownCanonical.multiz100way.protAA.fa.gz
80
+ </a>
81
+ </Typography>
82
+ {error ? <ErrorMessage error={error} /> : null}
83
+ {geneNameList && !ret ? (
84
+ <div style={{ color: 'red' }}>No MSA data for this gene found</div>
85
+ ) : null}
86
+ <TextField
87
+ value={userSelection}
88
+ onChange={event => setUserSelection(event.target.value)}
89
+ label="Choose isoform to view MSA for"
90
+ select
91
+ >
92
+ {options
93
+ .filter(val => set.has(getId(val)))
94
+ .map(val => (
95
+ <MenuItem value={getId(val)} key={val.id()}>
96
+ {getTranscriptDisplayName(val)} (has data)
97
+ </MenuItem>
98
+ ))}
99
+ {options
100
+ .filter(val => !set.has(getId(val)))
101
+ .map(val => (
102
+ <MenuItem value={getId(val)} key={val.id()} disabled>
103
+ {getTranscriptDisplayName(val)}
104
+ </MenuItem>
105
+ ))}
106
+ </TextField>
107
+ </DialogContent>
108
+
109
+ <DialogActions>
110
+ <Button
111
+ color="primary"
112
+ variant="contained"
113
+ onClick={() => {
114
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
115
+ ;(async () => {
116
+ try {
117
+ if (!ret) {
118
+ return
119
+ }
120
+ await preCalculatedLaunchView({
121
+ userSelection,
122
+ session,
123
+ newViewTitle: '',
124
+ view,
125
+ feature: ret,
126
+ })
127
+ handleClose()
128
+ } catch (e) {
129
+ console.error(e)
130
+ setError(e)
131
+ }
132
+ })()
133
+ }}
134
+ >
135
+ Submit
136
+ </Button>
137
+ <Button
138
+ color="secondary"
139
+ variant="contained"
140
+ onClick={() => handleClose()}
141
+ >
142
+ Cancel
143
+ </Button>
144
+ </DialogActions>
145
+ </>
146
+ )
147
+ })
148
+
149
+ export default PreLoadedMSA
@@ -0,0 +1,13 @@
1
+ export async function fetchGeneList() {
2
+ const res = await fetch(
3
+ 'https://jbrowse.org/demos/msaview/knownCanonical/list.txt',
4
+ )
5
+ if (!res.ok) {
6
+ throw new Error(`HTTP ${res.status} fetching list ${await res.text()}`)
7
+ }
8
+ const result = await res.text()
9
+ return result
10
+ .split('\n')
11
+ .map(f => f.trim())
12
+ .filter(f => !!f)
13
+ }
@@ -0,0 +1,54 @@
1
+ import { AbstractSessionModel, Feature } from '@jbrowse/core/util'
2
+ import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
3
+ import { ungzip } from 'pako'
4
+
5
+ async function myfetch(url: string) {
6
+ const res = await fetch(url)
7
+ if (!res.ok) {
8
+ throw new Error(`HTTP ${res.status} fetching ${await res.text()}`)
9
+ }
10
+ const data = await res.arrayBuffer()
11
+ return new TextDecoder().decode(ungzip(data))
12
+ }
13
+
14
+ export async function preCalculatedLaunchView({
15
+ userSelection,
16
+ session,
17
+ newViewTitle,
18
+ view,
19
+ feature,
20
+ }: {
21
+ session: AbstractSessionModel
22
+ userSelection: string
23
+ newViewTitle: string
24
+ view: LinearGenomeViewModel
25
+ feature: Feature
26
+ }) {
27
+ const d = await myfetch(
28
+ `https://jbrowse.org/demos/msaview/knownCanonical/${userSelection}.mfa.gz`,
29
+ )
30
+
31
+ session.addView('MsaView', {
32
+ type: 'MsaView',
33
+ displayName: newViewTitle,
34
+ treeAreaWidth: 200,
35
+ treeWidth: 100,
36
+ drawNodeBubbles: false,
37
+ labelsAlignRight: true,
38
+ showBranchLen: false,
39
+ colWidth: 10,
40
+ rowHeight: 12,
41
+ colorSchemeName: 'percent_identity_dynamic',
42
+ treeFilehandle: {
43
+ uri: 'https://hgdownload.soe.ucsc.edu/goldenPath/hg38/multiz100way/hg38.100way.nh',
44
+ },
45
+ treeMetadataFilehandle: {
46
+ uri: 'https://s3.amazonaws.com/jbrowse.org/demos/app/species.json',
47
+ },
48
+ data: {
49
+ msa: d,
50
+ },
51
+ connectedViewId: view.id,
52
+ connectedFeature: feature.toJSON(),
53
+ })
54
+ }
@@ -0,0 +1,30 @@
1
+ import React from 'react'
2
+ import { Box } from '@mui/material'
3
+
4
+ interface TabPanelProps {
5
+ children?: React.ReactNode
6
+ index: number
7
+ value: number
8
+ }
9
+
10
+ export function CustomTabPanel(props: TabPanelProps) {
11
+ const { children, value, index, ...other } = props
12
+
13
+ return (
14
+ <div
15
+ role="tabpanel"
16
+ hidden={value !== index}
17
+ id={`gtabpanel-${index}`}
18
+ aria-labelledby={`gtab-${index}`}
19
+ {...other}
20
+ >
21
+ {value === index && <Box sx={{ p: 3 }}>{children}</Box>}
22
+ </div>
23
+ )
24
+ }
25
+ export function a11yProps(index: number) {
26
+ return {
27
+ id: `gtab-${index}`,
28
+ 'aria-controls': `gtabpanel-${index}`,
29
+ }
30
+ }
@@ -0,0 +1,61 @@
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 LaunchMsaViewDialog from './components/LaunchMsaViewDialog'
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 MSA view',
30
+ icon: AddIcon,
31
+ onClick: () => {
32
+ const session = getSession(track)
33
+ session.queueDialog(handleClose => [
34
+ LaunchMsaViewDialog,
35
+ {
36
+ model: track,
37
+ handleClose,
38
+ feature,
39
+ },
40
+ ])
41
+ },
42
+ },
43
+ ]
44
+ : []),
45
+ ]
46
+ },
47
+ }
48
+ })
49
+ }
50
+
51
+ export default function LaunchMsaViewF(pluginManager: PluginManager) {
52
+ pluginManager.addToExtensionPoint(
53
+ 'Core-extendPluggableElement',
54
+ (elt: PluggableElementType) => {
55
+ if (isDisplay(elt)) {
56
+ elt.stateModel = extendStateModel(elt.stateModel)
57
+ }
58
+ return elt
59
+ },
60
+ )
61
+ }