@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.
Files changed (131) hide show
  1. package/dist/index.esm.js +5466 -4490
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/jbrowse-plugin-apollo.cjs.development.js +5283 -4318
  4. package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
  5. package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
  6. package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
  7. package/dist/jbrowse-plugin-apollo.umd.development.js +6806 -4088
  8. package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
  9. package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
  10. package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
  11. package/package.json +4 -4
  12. package/src/ApolloInternetAccount/addMenuItems.ts +5 -2
  13. package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +1 -0
  14. package/src/ApolloInternetAccount/components/LoginButtons.tsx +1 -1
  15. package/src/ApolloInternetAccount/components/LoginIcons.tsx +1 -1
  16. package/src/ApolloInternetAccount/configSchema.ts +1 -1
  17. package/src/ApolloInternetAccount/model.ts +11 -10
  18. package/src/ApolloJobModel.ts +1 -1
  19. package/src/ApolloRefNameAliasAdapter/ApolloRefNameAliasAdapter.ts +8 -6
  20. package/src/ApolloRefNameAliasAdapter/index.ts +2 -2
  21. package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +17 -4
  22. package/src/ApolloSequenceAdapter/index.ts +1 -1
  23. package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +8 -7
  24. package/src/ApolloTextSearchAdapter/index.ts +1 -1
  25. package/src/BackendDrivers/BackendDriver.ts +7 -7
  26. package/src/BackendDrivers/CollaborationServerDriver.ts +14 -10
  27. package/src/BackendDrivers/DesktopFileDriver.ts +11 -10
  28. package/src/BackendDrivers/InMemoryFileDriver.ts +10 -6
  29. package/src/ChangeManager.ts +5 -5
  30. package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +92 -20
  31. package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +170 -27
  32. package/src/FeatureDetailsWidget/AttributeKey.tsx +50 -0
  33. package/src/FeatureDetailsWidget/AttributeKeySelector.tsx +104 -0
  34. package/src/FeatureDetailsWidget/Attributes.tsx +213 -320
  35. package/src/FeatureDetailsWidget/BasicInformation.tsx +8 -9
  36. package/src/FeatureDetailsWidget/DefaultAttributeEditor.tsx +104 -0
  37. package/src/FeatureDetailsWidget/DefaultAttributeViewer.tsx +22 -0
  38. package/src/FeatureDetailsWidget/FeatureDetailsNavigation.tsx +10 -8
  39. package/src/FeatureDetailsWidget/NumberTextField.tsx +1 -1
  40. package/src/FeatureDetailsWidget/Sequence.tsx +18 -35
  41. package/src/FeatureDetailsWidget/StringTextField.tsx +1 -1
  42. package/src/FeatureDetailsWidget/TranscriptSequence.tsx +140 -95
  43. package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +600 -0
  44. package/src/FeatureDetailsWidget/TranscriptWidgetSummary.tsx +54 -0
  45. package/src/FeatureDetailsWidget/model.ts +8 -3
  46. package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +19 -12
  47. package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +19 -41
  48. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +44 -22
  49. package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +6 -5
  50. package/src/LinearApolloDisplay/glyphs/Glyph.ts +7 -7
  51. package/src/LinearApolloDisplay/stateModel/base.ts +52 -10
  52. package/src/LinearApolloDisplay/stateModel/index.ts +4 -3
  53. package/src/LinearApolloDisplay/stateModel/layouts.ts +8 -34
  54. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +13 -12
  55. package/src/LinearApolloDisplay/stateModel/rendering.ts +63 -31
  56. package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +221 -0
  57. package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +40 -0
  58. package/src/LinearApolloSixFrameDisplay/components/index.ts +2 -0
  59. package/src/LinearApolloSixFrameDisplay/configSchema.ts +7 -0
  60. package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +821 -0
  61. package/src/LinearApolloSixFrameDisplay/glyphs/Glyph.ts +63 -0
  62. package/src/LinearApolloSixFrameDisplay/glyphs/index.ts +1 -0
  63. package/src/LinearApolloSixFrameDisplay/index.ts +2 -0
  64. package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +261 -0
  65. package/src/LinearApolloSixFrameDisplay/stateModel/index.ts +27 -0
  66. package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +236 -0
  67. package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +349 -0
  68. package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +199 -0
  69. package/src/LinearApolloSixFrameDisplay/types.ts +1 -0
  70. package/src/OntologyManager/OntologyStore/fulltext-stopwords.ts +10 -1
  71. package/src/OntologyManager/OntologyStore/fulltext.test.ts +1 -1
  72. package/src/OntologyManager/OntologyStore/fulltext.ts +8 -3
  73. package/src/OntologyManager/OntologyStore/index.test.ts +4 -1
  74. package/src/OntologyManager/OntologyStore/index.ts +19 -14
  75. package/src/OntologyManager/OntologyStore/indexeddb-schema.ts +6 -5
  76. package/src/OntologyManager/OntologyStore/indexeddb-storage.ts +11 -5
  77. package/src/OntologyManager/index.ts +10 -6
  78. package/src/OntologyManager/util.ts +3 -2
  79. package/src/TabularEditor/HybridGrid/ChangeHandling.ts +2 -2
  80. package/src/TabularEditor/HybridGrid/Feature.tsx +9 -8
  81. package/src/TabularEditor/HybridGrid/FeatureAttributes.tsx +1 -1
  82. package/src/TabularEditor/HybridGrid/HybridGrid.tsx +3 -2
  83. package/src/TabularEditor/HybridGrid/NumberCell.tsx +8 -1
  84. package/src/TabularEditor/HybridGrid/ToolBar.tsx +15 -13
  85. package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +9 -33
  86. package/src/TabularEditor/TabularEditorPane.tsx +1 -1
  87. package/src/TabularEditor/model.ts +2 -2
  88. package/src/TabularEditor/types.ts +5 -2
  89. package/src/components/AddAssembly.tsx +611 -291
  90. package/src/components/AddChildFeature.tsx +6 -5
  91. package/src/components/AddFeature.tsx +211 -38
  92. package/src/components/AddRefSeqAliases.tsx +14 -12
  93. package/src/components/CopyFeature.tsx +8 -7
  94. package/src/components/CreateApolloAnnotation.tsx +154 -46
  95. package/src/components/DeleteAssembly.tsx +9 -8
  96. package/src/components/DeleteFeature.tsx +5 -4
  97. package/src/components/Dialog.tsx +1 -1
  98. package/src/components/DownloadGFF3.tsx +11 -10
  99. package/src/components/FilterFeatures.tsx +6 -4
  100. package/src/components/ImportFeatures.tsx +7 -6
  101. package/src/components/LogOut.tsx +5 -4
  102. package/src/components/ManageChecks.tsx +9 -8
  103. package/src/components/ManageUsers.tsx +11 -10
  104. package/src/components/OntologyTermAutocomplete.tsx +5 -5
  105. package/src/components/OntologyTermMultiSelect.tsx +9 -6
  106. package/src/components/OpenLocalFile.tsx +4 -3
  107. package/src/components/ViewChangeLog.tsx +7 -6
  108. package/src/components/ViewCheckResults.tsx +8 -7
  109. package/src/components/index.ts +0 -1
  110. package/src/extensions/annotationFromJBrowseFeature.test.ts +1 -0
  111. package/src/extensions/annotationFromJBrowseFeature.ts +14 -12
  112. package/src/extensions/annotationFromPileup.ts +6 -6
  113. package/src/index.ts +33 -50
  114. package/src/makeDisplayComponent.tsx +93 -41
  115. package/src/session/ClientDataStore.ts +21 -17
  116. package/src/session/session.ts +20 -26
  117. package/src/types.ts +4 -4
  118. package/src/util/annotationFeatureUtils.ts +53 -0
  119. package/src/util/index.ts +4 -3
  120. package/src/util/loadAssemblyIntoClient.ts +10 -3
  121. package/src/ApolloSixFrameRenderer/ApolloSixFrameRenderer.tsx +0 -13
  122. package/src/ApolloSixFrameRenderer/components/ApolloRendering.tsx +0 -707
  123. package/src/ApolloSixFrameRenderer/configSchema.ts +0 -7
  124. package/src/ApolloSixFrameRenderer/index.ts +0 -3
  125. package/src/FeatureDetailsWidget/TranscriptBasic.tsx +0 -200
  126. package/src/SixFrameFeatureDisplay/components/TrackLines.tsx +0 -19
  127. package/src/SixFrameFeatureDisplay/components/index.ts +0 -1
  128. package/src/SixFrameFeatureDisplay/configSchema.ts +0 -21
  129. package/src/SixFrameFeatureDisplay/index.ts +0 -2
  130. package/src/SixFrameFeatureDisplay/stateModel.ts +0 -439
  131. 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 React from 'react'
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 { AnnotationFeature } from '@apollo-annotation/mst'
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} ({parent.min}..{parent.max})
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} ({child.min}..{child.max})
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, { useState } from '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 (showSequence) {
60
- sequence = refSeq.getSequence(min, max)
61
- if (sequence) {
62
- sequence = formatSequence(sequence, refName, min, max)
63
- } else {
64
- void session.apolloDataStore.loadRefSeq([
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
- <Typography variant="h5">Sequence</Typography>
73
- <Button variant="contained" onClick={onButtonClick}>
74
- {showSequence ? 'Hide sequence' : 'Show sequence'}
75
- </Button>
76
- <div>
77
- {showSequence && (
78
- <textarea
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 = 'upOrDownstream' | 'UTR' | 'CDS' | 'intron' | 'protein'
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 [showSequence, setShowSequence] = useState(false)
183
- const [selectedOption, setSelectedOption] = useState<SegmentListType>('CDS')
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 (feature.type !== 'mRNA') {
195
- return null
254
+ if (!featureTypeOntology) {
255
+ throw new Error('featureTypeOntology is undefined')
196
256
  }
197
-
198
- const handleSeqButtonClick = () => {
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
- <Typography variant="h5">Sequence</Typography>
248
- <div>
249
- <Button variant="contained" onClick={handleSeqButtonClick}>
250
- {showSequence ? 'Hide sequence' : 'Show sequence'}
251
- </Button>
252
- </div>
253
- {showSequence && (
254
- <>
255
- <Select
256
- defaultValue="CDS"
257
- value={selectedOption}
258
- onChange={handleChangeSeqOption}
259
- >
260
- <MenuItem value="CDS">CDS</MenuItem>
261
- <MenuItem value="cDNA">cDNA</MenuItem>
262
- <MenuItem value="genomic">Genomic</MenuItem>
263
- <MenuItem value="protein">Protein</MenuItem>
264
- </Select>
265
- <Paper
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
+ &gt;{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
- fontFamily: 'monospace',
268
- padding: theme.spacing(),
269
- overflowX: 'auto',
337
+ background: getSegmentColor(segment.type),
338
+ color: theme.palette.getContrastText(
339
+ getSegmentColor(segment.type),
340
+ ),
270
341
  }}
271
- ref={seqRef}
272
342
  >
273
- &gt;{refSeq.name}:
274
- {locationIntervals
275
- .map((interval) =>
276
- feature.strand === 1
277
- ? `${interval.min + 1}-${interval.max}`
278
- : `${interval.max}-${interval.min + 1}`,
279
- )
280
- .join(';')}
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
- </Paper>
305
- <Button variant="contained" onClick={copyToClipboard}>
306
- Copy sequence
307
- </Button>
308
- </>
309
- )}
352
+ </span>
353
+ ))}
354
+ </Paper>
310
355
  </>
311
356
  )
312
357
  })