@apollo-annotation/jbrowse-plugin-apollo 0.3.4 → 0.3.5

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 (44) hide show
  1. package/dist/index.esm.js +1513 -1074
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/jbrowse-plugin-apollo.cjs.development.js +1510 -1065
  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 +4681 -2097
  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/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +13 -0
  13. package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +88 -17
  14. package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +144 -22
  15. package/src/FeatureDetailsWidget/Attributes.tsx +93 -43
  16. package/src/FeatureDetailsWidget/BasicInformation.tsx +2 -4
  17. package/src/FeatureDetailsWidget/FeatureDetailsNavigation.tsx +6 -4
  18. package/src/FeatureDetailsWidget/Sequence.tsx +16 -33
  19. package/src/FeatureDetailsWidget/TranscriptSequence.tsx +137 -92
  20. package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +600 -0
  21. package/src/FeatureDetailsWidget/TranscriptWidgetSummary.tsx +54 -0
  22. package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +13 -6
  23. package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +6 -27
  24. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +35 -13
  25. package/src/LinearApolloDisplay/stateModel/layouts.ts +7 -2
  26. package/src/LinearApolloDisplay/stateModel/rendering.ts +4 -0
  27. package/src/OntologyManager/OntologyStore/fulltext-stopwords.ts +10 -1
  28. package/src/OntologyManager/OntologyStore/index.test.ts +1 -0
  29. package/src/OntologyManager/index.ts +2 -0
  30. package/src/SixFrameFeatureDisplay/stateModel.ts +5 -1
  31. package/src/TabularEditor/HybridGrid/Feature.tsx +0 -1
  32. package/src/TabularEditor/HybridGrid/NumberCell.tsx +8 -1
  33. package/src/TabularEditor/HybridGrid/ToolBar.tsx +14 -12
  34. package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +3 -27
  35. package/src/components/AddAssembly.tsx +608 -291
  36. package/src/components/CreateApolloAnnotation.tsx +144 -37
  37. package/src/components/OntologyTermMultiSelect.tsx +3 -0
  38. package/src/components/index.ts +0 -1
  39. package/src/extensions/annotationFromJBrowseFeature.ts +3 -2
  40. package/src/makeDisplayComponent.tsx +3 -4
  41. package/src/util/annotationFeatureUtils.ts +53 -0
  42. package/src/util/index.ts +1 -0
  43. package/src/FeatureDetailsWidget/TranscriptBasic.tsx +0 -200
  44. package/src/components/ModifyFeatureAttribute.tsx +0 -460
@@ -45,8 +45,27 @@ const isGeneOrTranscript = (
45
45
  }
46
46
  return (
47
47
  featureTypeOntology.isTypeOf(annotationFeature.type, 'gene') ||
48
- featureTypeOntology.isTypeOf(annotationFeature.type, 'mRNA') ||
49
- featureTypeOntology.isTypeOf(annotationFeature.type, 'transcript')
48
+ featureTypeOntology.isTypeOf(annotationFeature.type, 'transcript') ||
49
+ featureTypeOntology.isTypeOf(annotationFeature.type, 'pseudogene') ||
50
+ featureTypeOntology.isTypeOf(
51
+ annotationFeature.type,
52
+ 'pseudogenic_transcript',
53
+ )
54
+ )
55
+ }
56
+
57
+ const isGene = (
58
+ annotationFeature: AnnotationFeatureSnapshot,
59
+ apolloSessionModel: ApolloSessionModel,
60
+ ) => {
61
+ const { featureTypeOntology } =
62
+ apolloSessionModel.apolloDataStore.ontologyManager
63
+ if (!featureTypeOntology) {
64
+ throw new Error('featureTypeOntology is undefined')
65
+ }
66
+ return (
67
+ featureTypeOntology.isTypeOf(annotationFeature.type, 'gene') ||
68
+ featureTypeOntology.isTypeOf(annotationFeature.type, 'pseudogene')
50
69
  )
51
70
  }
52
71
 
@@ -60,11 +79,51 @@ const isTranscript = (
60
79
  throw new Error('featureTypeOntology is undefined')
61
80
  }
62
81
  return (
63
- featureTypeOntology.isTypeOf(annotationFeature.type, 'mRNA') ||
64
- featureTypeOntology.isTypeOf(annotationFeature.type, 'transcript')
82
+ featureTypeOntology.isTypeOf(annotationFeature.type, 'transcript') ||
83
+ featureTypeOntology.isTypeOf(
84
+ annotationFeature.type,
85
+ 'pseudogenic_transcript',
86
+ )
65
87
  )
66
88
  }
67
89
 
90
+ const getFeatureId = (feature: AnnotationFeatureSnapshot) => {
91
+ const { attributes } = feature
92
+ const id = attributes?.id
93
+ if (id) {
94
+ return id[0]
95
+ }
96
+ return feature.type
97
+ }
98
+
99
+ const getFeatureNameOrId = (
100
+ feature: AnnotationFeatureSnapshot,
101
+ apolloSessionModel: ApolloSessionModel,
102
+ ) => {
103
+ const { featureTypeOntology } =
104
+ apolloSessionModel.apolloDataStore.ontologyManager
105
+ if (!featureTypeOntology) {
106
+ return getFeatureId(feature)
107
+ }
108
+
109
+ let attrName = ''
110
+
111
+ if (featureTypeOntology.isTypeOf(feature.type, 'gene')) {
112
+ attrName = 'gene_name'
113
+ }
114
+
115
+ if (featureTypeOntology.isTypeOf(feature.type, 'transcript')) {
116
+ attrName = 'transcript_name'
117
+ }
118
+
119
+ const { attributes } = feature
120
+ const name = attributes?.[attrName]
121
+ if (name) {
122
+ return name[0]
123
+ }
124
+ return getFeatureId(feature)
125
+ }
126
+
68
127
  export function CreateApolloAnnotation({
69
128
  annotationFeature,
70
129
  assembly,
@@ -104,6 +163,9 @@ export function CreateApolloAnnotation({
104
163
  const filteredFeatures: AnnotationFeatureSnapshot[] = []
105
164
 
106
165
  for (const [, f] of features) {
166
+ if (f.type === 'chromosome') {
167
+ continue
168
+ }
107
169
  const featureSnapshot = getSnapshot(f)
108
170
  if (min >= featureSnapshot.min && max <= featureSnapshot.max) {
109
171
  filteredFeatures.push(featureSnapshot)
@@ -115,33 +177,34 @@ export function CreateApolloAnnotation({
115
177
 
116
178
  useEffect(() => {
117
179
  setErrorMessage('')
118
- if (checkedChildrens.length === 0) {
119
- setParentFeatureChecked(false)
120
- return
121
- }
122
-
180
+ let mins: number[] = []
181
+ let maxes: number[] = []
123
182
  if (annotationFeature.children) {
124
183
  const checkedAnnotationFeatureChildren = Object.values(
125
184
  annotationFeature.children,
126
185
  )
127
186
  .filter((child) => isTranscript(child, apolloSessionModel))
128
187
  .filter((child) => checkedChildrens.includes(child._id))
129
- const mins = checkedAnnotationFeatureChildren.map((f) => f.min)
130
- const maxes = checkedAnnotationFeatureChildren.map((f) => f.max)
131
- const min = Math.min(...mins)
132
- const max = Math.max(...maxes)
133
- const filteredFeatures = getFeatures(min, max)
134
- setDestinationFeatures(filteredFeatures)
188
+ mins = checkedAnnotationFeatureChildren.map((f) => f.min)
189
+ maxes = checkedAnnotationFeatureChildren.map((f) => f.max)
190
+ }
135
191
 
136
- if (
137
- filteredFeatures.length === 0 &&
138
- checkedChildrens.length > 0 &&
139
- !parentFeatureChecked
140
- ) {
141
- setErrorMessage('No destination features found')
142
- }
192
+ const { featureTypeOntology } =
193
+ apolloSessionModel.apolloDataStore.ontologyManager
194
+ if (
195
+ featureTypeOntology &&
196
+ featureTypeOntology.isTypeOf(annotationFeature.type, 'transcript')
197
+ ) {
198
+ mins = [annotationFeature.min, ...mins]
199
+ maxes = [annotationFeature.max, ...maxes]
143
200
  }
144
- }, [checkedChildrens])
201
+
202
+ const min = Math.min(...mins)
203
+ const max = Math.max(...maxes)
204
+ const filteredFeatures = getFeatures(min, max)
205
+ setDestinationFeatures(filteredFeatures)
206
+ setSelectedDestinationFeature(filteredFeatures[0])
207
+ }, [checkedChildrens, parentFeatureChecked])
145
208
 
146
209
  const handleParentFeatureCheck = (
147
210
  event: React.ChangeEvent<HTMLInputElement>,
@@ -171,12 +234,55 @@ export function CreateApolloAnnotation({
171
234
 
172
235
  const handleCreateApolloAnnotation = async () => {
173
236
  if (parentFeatureChecked) {
174
- const change = new AddFeatureChange({
175
- changedIds: [annotationFeature._id],
176
- typeName: 'AddFeatureChange',
177
- assembly: assembly.name,
178
- addedFeature: annotationFeature,
179
- })
237
+ let change
238
+ if (isGene(annotationFeature, apolloSessionModel)) {
239
+ if (
240
+ annotationFeature.children &&
241
+ checkedChildrens.length !==
242
+ Object.values(annotationFeature.children).length
243
+ ) {
244
+ const childrens: Record<string, AnnotationFeatureSnapshot> = {}
245
+ for (const childId of checkedChildrens) {
246
+ childrens[childId] = annotationFeature.children[childId]
247
+ }
248
+ change = new AddFeatureChange({
249
+ changedIds: [annotationFeature._id],
250
+ typeName: 'AddFeatureChange',
251
+ assembly: assembly.name,
252
+ addedFeature: {
253
+ ...annotationFeature,
254
+ children: childrens,
255
+ },
256
+ })
257
+ } else {
258
+ change = new AddFeatureChange({
259
+ changedIds: [annotationFeature._id],
260
+ typeName: 'AddFeatureChange',
261
+ assembly: assembly.name,
262
+ addedFeature: annotationFeature,
263
+ })
264
+ }
265
+ }
266
+
267
+ if (isTranscript(annotationFeature, apolloSessionModel)) {
268
+ if (selectedDestinationFeature) {
269
+ change = new AddFeatureChange({
270
+ parentFeatureId: selectedDestinationFeature._id,
271
+ changedIds: [selectedDestinationFeature._id],
272
+ typeName: 'AddFeatureChange',
273
+ assembly: assembly.name,
274
+ addedFeature: annotationFeature,
275
+ })
276
+ } else {
277
+ setErrorMessage('There is no destination gene for this transcript')
278
+ return
279
+ }
280
+ }
281
+
282
+ if (!change) {
283
+ return
284
+ }
285
+
180
286
  await apolloSessionModel.apolloDataStore.changeManager.submit(change)
181
287
  session.notify('Annotation added successfully', 'success')
182
288
  handleClose()
@@ -198,9 +304,9 @@ export function CreateApolloAnnotation({
198
304
  addedFeature: child,
199
305
  })
200
306
  await apolloSessionModel.apolloDataStore.changeManager.submit(change)
201
- session.notify('Annotation added successfully', 'success')
202
- handleClose()
203
307
  }
308
+ session.notify('Annotation added successfully', 'success')
309
+ handleClose()
204
310
  }
205
311
  }
206
312
 
@@ -226,7 +332,7 @@ export function CreateApolloAnnotation({
226
332
  onChange={handleParentFeatureCheck}
227
333
  />
228
334
  }
229
- label={`${annotationFeature.type}:${annotationFeature.min}..${annotationFeature.max}`}
335
+ label={`${getFeatureNameOrId(annotationFeature, apolloSessionModel)} (${annotationFeature.min + 1}..${annotationFeature.max})`}
230
336
  />
231
337
  )}
232
338
  {annotationFeature.children && (
@@ -245,15 +351,16 @@ export function CreateApolloAnnotation({
245
351
  }}
246
352
  />
247
353
  }
248
- label={`${child.type}:${child.min}..${child.max}`}
354
+ label={`${getFeatureNameOrId(child, apolloSessionModel)} (${child.min + 1}..${child.max})`}
249
355
  />
250
356
  ))}
251
357
  </Box>
252
358
  )}
253
359
  </Box>
254
- {!parentFeatureChecked &&
255
- checkedChildrens.length > 0 &&
256
- destinationFeatures.length > 0 && (
360
+ {destinationFeatures.length > 0 &&
361
+ ((!parentFeatureChecked && checkedChildrens.length > 0) ||
362
+ (parentFeatureChecked &&
363
+ isTranscript(annotationFeature, apolloSessionModel))) && (
257
364
  <Box sx={{ ml: 3 }}>
258
365
  <Typography variant="caption" fontSize={12}>
259
366
  Select the destination feature to copy the selected features
@@ -268,7 +375,7 @@ export function CreateApolloAnnotation({
268
375
  >
269
376
  {destinationFeatures.map((f) => (
270
377
  <MenuItem key={f._id} value={f._id}>
271
- {`${f.type}:${f.min}..${f.max}`}
378
+ {`${getFeatureNameOrId(f, apolloSessionModel)} (${f.min}..${f.max})`}
272
379
  </MenuItem>
273
380
  ))}
274
381
  </Select>
@@ -114,6 +114,7 @@ export function OntologyTermMultiSelect({
114
114
  ontologyVersion,
115
115
  session,
116
116
  value: initialValue,
117
+ label,
117
118
  }: {
118
119
  session: ApolloSessionModel
119
120
  value: string[]
@@ -122,6 +123,7 @@ export function OntologyTermMultiSelect({
122
123
  /** if true, include deprecated/obsolete terms */
123
124
  includeDeprecated?: boolean
124
125
  onChange(newValue: string[]): void
126
+ label?: string
125
127
  }) {
126
128
  const { ontologyManager } = session.apolloDataStore
127
129
  const ontology = ontologyManager.findOntology(ontologyName, ontologyVersion)
@@ -256,6 +258,7 @@ export function OntologyTermMultiSelect({
256
258
  {...params}
257
259
  {...extraTextFieldParams}
258
260
  variant="outlined"
261
+ label={label}
259
262
  fullWidth
260
263
  />
261
264
  )}
@@ -9,7 +9,6 @@ export * from './ImportFeatures'
9
9
  export * from './LogOut'
10
10
  export * from './ManageChecks'
11
11
  export * from './ManageUsers'
12
- export * from './ModifyFeatureAttribute'
13
12
  export * from './OpenLocalFile'
14
13
  export * from './ViewChangeLog'
15
14
  export * from './AddRefSeqAliases'
@@ -23,6 +23,7 @@ function simpleFeatureToGFF3Feature(
23
23
  feature: Feature,
24
24
  refSeqId: string,
25
25
  ): GFF3Feature {
26
+ // eslint-disable-next-line unicorn/prefer-structured-clone
26
27
  const xfeature = JSON.parse(JSON.stringify(feature))
27
28
  const children = xfeature.subfeatures
28
29
  const gff3Feature = [
@@ -130,11 +131,11 @@ export function annotationFromJBrowseFeature(
130
131
  }))
131
132
  .views((self) => {
132
133
  const superContextMenuItems = self.contextMenuItems
133
- const session = getSession(self)
134
- const assembly = self.getAssembly()
135
134
 
136
135
  return {
137
136
  contextMenuItems() {
137
+ const session = getSession(self)
138
+ const assembly = self.getAssembly()
138
139
  const feature = self.contextMenuFeature
139
140
  if (!feature) {
140
141
  return superContextMenuItems()
@@ -171,7 +171,6 @@ export const DisplayComponent = observer(function DisplayComponent({
171
171
  const { classes } = useStyles()
172
172
 
173
173
  const {
174
- detailsHeight,
175
174
  graphical,
176
175
  height: overallHeight,
177
176
  isShown,
@@ -187,7 +186,7 @@ export const DisplayComponent = observer(function DisplayComponent({
187
186
  }, [model, selectedFeature])
188
187
 
189
188
  const onDetailsResize = (delta: number) => {
190
- model.setDetailsHeight(detailsHeight - delta)
189
+ model.setDetailsHeight(model.detailsHeight - delta)
191
190
  }
192
191
 
193
192
  if (!ontologyStore) {
@@ -199,9 +198,9 @@ export const DisplayComponent = observer(function DisplayComponent({
199
198
  }
200
199
 
201
200
  if (graphical && table) {
202
- const tabularHeight = tabularEditor.isShown ? detailsHeight : 0
201
+ const tabularHeight = tabularEditor.isShown ? model.detailsHeight : 0
203
202
  const featureAreaHeight = isShown
204
- ? overallHeight - detailsHeight - accordionControlHeight * 2
203
+ ? overallHeight - model.detailsHeight - accordionControlHeight * 2
205
204
  : 0
206
205
  return (
207
206
  <div style={{ height: overallHeight }}>
@@ -0,0 +1,53 @@
1
+ import { AnnotationFeature } from '@apollo-annotation/mst'
2
+
3
+ export function getFeatureName(feature: AnnotationFeature) {
4
+ const { attributes } = feature
5
+ const name = attributes.get('gff_name')
6
+ if (name) {
7
+ return name[0]
8
+ }
9
+ return ''
10
+ }
11
+
12
+ export function getFeatureId(feature: AnnotationFeature) {
13
+ const { attributes } = feature
14
+ const id = attributes.get('gff_id')
15
+ const transcript_id = attributes.get('transcript_id')
16
+ const exon_id = attributes.get('exon_id')
17
+ const protein_id = attributes.get('protein_id')
18
+ if (id) {
19
+ return id[0]
20
+ }
21
+ if (transcript_id) {
22
+ return transcript_id[0]
23
+ }
24
+ if (exon_id) {
25
+ return exon_id[0]
26
+ }
27
+ if (protein_id) {
28
+ return protein_id[0]
29
+ }
30
+ return ''
31
+ }
32
+
33
+ export function getFeatureNameOrId(feature: AnnotationFeature) {
34
+ const name = getFeatureName(feature)
35
+ const id = getFeatureId(feature)
36
+ if (name) {
37
+ return `: ${name}`
38
+ }
39
+ if (id) {
40
+ return `: ${id}`
41
+ }
42
+ return ''
43
+ }
44
+
45
+ export function getStrand(strand: number | undefined) {
46
+ if (strand === 1) {
47
+ return 'Forward'
48
+ }
49
+ if (strand === -1) {
50
+ return 'Reverse'
51
+ }
52
+ return ''
53
+ }
package/src/util/index.ts CHANGED
@@ -29,3 +29,4 @@ export function getApolloInternetAccount(session: ApolloSessionModel) {
29
29
  }
30
30
 
31
31
  export * from './loadAssemblyIntoClient'
32
+ export * from './annotationFeatureUtils'
@@ -1,200 +0,0 @@
1
- import { AnnotationFeature } from '@apollo-annotation/mst'
2
- import {
3
- LocationEndChange,
4
- LocationStartChange,
5
- } from '@apollo-annotation/shared'
6
- import { AbstractSessionModel, getFrame, revcom } from '@jbrowse/core/util'
7
- import {
8
- Paper,
9
- Typography,
10
- Table,
11
- TableBody,
12
- TableCell,
13
- TableContainer,
14
- TableRow,
15
- useTheme,
16
- } from '@mui/material'
17
- import { observer } from 'mobx-react'
18
- import React from 'react'
19
-
20
- import { ApolloSessionModel } from '../session'
21
- import { NumberTextField } from './NumberTextField'
22
-
23
- export const TranscriptBasicInformation = observer(
24
- function TranscriptBasicInformation({
25
- assembly,
26
- feature,
27
- refName,
28
- session,
29
- }: {
30
- feature: AnnotationFeature
31
- session: ApolloSessionModel
32
- assembly: string
33
- refName: string
34
- }) {
35
- const { notify } = session as unknown as AbstractSessionModel
36
- const currentAssembly = session.apolloDataStore.assemblies.get(assembly)
37
- const refData = currentAssembly?.getByRefName(refName)
38
- const { changeManager } = session.apolloDataStore
39
- const theme = useTheme()
40
-
41
- function handleLocationChange(
42
- oldLocation: number,
43
- newLocation: number,
44
- feature: AnnotationFeature,
45
- isMin: boolean,
46
- ) {
47
- if (!feature.children) {
48
- throw new Error('Transcript should have child features')
49
- }
50
- for (const [, child] of feature.children) {
51
- if (isMin && oldLocation - 1 === child.min) {
52
- const change = new LocationStartChange({
53
- typeName: 'LocationStartChange',
54
- changedIds: [child._id],
55
- featureId: feature._id,
56
- oldStart: oldLocation - 1,
57
- newStart: newLocation - 1,
58
- assembly,
59
- })
60
- changeManager.submit(change).catch(() => {
61
- notify('Error updating feature start position', 'error')
62
- })
63
- return
64
- }
65
- if (!isMin && newLocation === child.max) {
66
- const change = new LocationEndChange({
67
- typeName: 'LocationEndChange',
68
- changedIds: [child._id],
69
- featureId: feature._id,
70
- oldEnd: child.max,
71
- newEnd: newLocation,
72
- assembly,
73
- })
74
- changeManager.submit(change).catch(() => {
75
- notify('Error updating feature start position', 'error')
76
- })
77
- return
78
- }
79
- }
80
- }
81
-
82
- if (!refData) {
83
- return null
84
- }
85
-
86
- let strand, transcriptParts
87
- try {
88
- ;({ strand, transcriptParts } = feature)
89
- } catch {
90
- return null
91
- }
92
- const [firstLocation] = transcriptParts
93
-
94
- const locationData = firstLocation
95
- .map((loc, idx) => {
96
- const { max, min, type } = loc
97
- let label: string = type
98
- if (label === 'threePrimeUTR') {
99
- label = '3` UTR'
100
- } else if (label === 'fivePrimeUTR') {
101
- label = '5` UTR'
102
- }
103
- let fivePrimeSpliceSite
104
- let threePrimeSpliceSite
105
- let frameColor
106
- if (type === 'CDS') {
107
- const { phase } = loc
108
- const frame = getFrame(min, max, strand ?? 1, phase)
109
- frameColor = theme.palette.framesCDS.at(frame)?.main
110
- const previousLoc = firstLocation.at(idx - 1)
111
- const nextLoc = firstLocation.at(idx + 1)
112
- if (strand === 1) {
113
- if (previousLoc?.type === 'intron') {
114
- fivePrimeSpliceSite = refData.getSequence(min - 2, min)
115
- }
116
- if (nextLoc?.type === 'intron') {
117
- threePrimeSpliceSite = refData.getSequence(max, max + 2)
118
- }
119
- } else {
120
- if (previousLoc?.type === 'intron') {
121
- fivePrimeSpliceSite = revcom(refData.getSequence(max, max + 2))
122
- }
123
- if (nextLoc?.type === 'intron') {
124
- threePrimeSpliceSite = revcom(refData.getSequence(min - 2, min))
125
- }
126
- }
127
- }
128
- return {
129
- min,
130
- max,
131
- label,
132
- fivePrimeSpliceSite,
133
- threePrimeSpliceSite,
134
- frameColor,
135
- }
136
- })
137
- .filter((loc) => loc.label !== 'intron')
138
-
139
- return (
140
- <>
141
- <Typography variant="h5">Structure</Typography>
142
- <Typography variant="h6">
143
- {strand === 1 ? 'Forward' : 'Reverse'} strand
144
- </Typography>
145
- <TableContainer component={Paper}>
146
- <Table size="small">
147
- <TableBody>
148
- {locationData.map((loc) => (
149
- <TableRow key={`${loc.label}:${loc.min}-${loc.max}`}>
150
- <TableCell
151
- component="th"
152
- scope="row"
153
- style={{ background: loc.frameColor }}
154
- >
155
- {loc.label}
156
- </TableCell>
157
- <TableCell>{loc.fivePrimeSpliceSite ?? ''}</TableCell>
158
- <TableCell padding="none">
159
- <NumberTextField
160
- margin="dense"
161
- variant="outlined"
162
- value={strand === 1 ? loc.min + 1 : loc.max}
163
- onChangeCommitted={(newLocation: number) => {
164
- handleLocationChange(
165
- strand === 1 ? loc.min + 1 : loc.max,
166
- newLocation,
167
- feature,
168
- strand === 1,
169
- )
170
- }}
171
- />
172
- {/* {strand === 1 ? loc.min : loc.max} */}
173
- </TableCell>
174
- <TableCell padding="none">
175
- <NumberTextField
176
- margin="dense"
177
- // disabled={item.type !== 'CDS'}
178
- variant="outlined"
179
- value={strand === 1 ? loc.max : loc.min + 1}
180
- onChangeCommitted={(newLocation: number) => {
181
- handleLocationChange(
182
- strand === 1 ? loc.max : loc.min + 1,
183
- newLocation,
184
- feature,
185
- strand !== 1,
186
- )
187
- }}
188
- />
189
- {/* {strand === 1 ? loc.max : loc.min} */}
190
- </TableCell>
191
- <TableCell>{loc.threePrimeSpliceSite ?? ''}</TableCell>
192
- </TableRow>
193
- ))}
194
- </TableBody>
195
- </Table>
196
- </TableContainer>
197
- </>
198
- )
199
- },
200
- )