@apollo-annotation/jbrowse-plugin-apollo 0.1.18 → 0.1.20
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.
- package/dist/index.esm.js +3189 -3575
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +3185 -3570
- package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.development.js +14884 -15905
- package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
- package/package.json +33 -33
- package/src/ApolloInternetAccount/addMenuItems.ts +18 -0
- package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +1 -0
- package/src/ApolloInternetAccount/configSchema.ts +5 -2
- package/src/ApolloInternetAccount/model.ts +14 -5
- package/src/ApolloRefNameAliasAdapter/ApolloRefNameAliasAdapter.ts +94 -0
- package/src/ApolloRefNameAliasAdapter/configSchema.ts +12 -0
- package/src/ApolloRefNameAliasAdapter/index.ts +21 -0
- package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +1 -0
- package/src/ApolloSixFrameRenderer/components/ApolloRendering.tsx +10 -10
- package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +35 -32
- package/src/BackendDrivers/BackendDriver.ts +8 -0
- package/src/BackendDrivers/CollaborationServerDriver.ts +49 -1
- package/src/BackendDrivers/DesktopFileDriver.ts +14 -1
- package/src/BackendDrivers/InMemoryFileDriver.ts +17 -1
- package/src/ChangeManager.ts +1 -1
- package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +5 -25
- package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +82 -0
- package/src/FeatureDetailsWidget/Attributes.tsx +11 -3
- package/src/FeatureDetailsWidget/BasicInformation.tsx +38 -30
- package/src/FeatureDetailsWidget/Sequence.tsx +7 -7
- package/src/FeatureDetailsWidget/TranscriptBasic.tsx +446 -0
- package/src/FeatureDetailsWidget/TranscriptSequence.tsx +365 -0
- package/src/FeatureDetailsWidget/index.ts +2 -0
- package/src/FeatureDetailsWidget/model.ts +77 -9
- package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +0 -2
- package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +453 -380
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +520 -0
- package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +138 -134
- package/src/LinearApolloDisplay/glyphs/Glyph.ts +38 -370
- package/src/LinearApolloDisplay/glyphs/index.ts +1 -2
- package/src/LinearApolloDisplay/stateModel/base.ts +3 -6
- package/src/LinearApolloDisplay/stateModel/getGlyph.ts +30 -30
- package/src/LinearApolloDisplay/stateModel/index.ts +5 -1
- package/src/LinearApolloDisplay/stateModel/layouts.ts +32 -24
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +206 -217
- package/src/LinearApolloDisplay/stateModel/rendering.ts +43 -67
- package/src/OntologyManager/OntologyStore/fulltext.ts +1 -1
- package/src/OntologyManager/OntologyStore/index.ts +2 -1
- package/src/OntologyManager/index.ts +6 -2
- package/src/OntologyManager/util.ts +2 -2
- package/src/SixFrameFeatureDisplay/stateModel.ts +15 -10
- package/src/TabularEditor/HybridGrid/ChangeHandling.ts +21 -46
- package/src/TabularEditor/HybridGrid/Feature.tsx +31 -82
- package/src/TabularEditor/HybridGrid/FeatureAttributes.tsx +3 -2
- package/src/TabularEditor/HybridGrid/HybridGrid.tsx +2 -3
- package/src/TabularEditor/HybridGrid/NumberCell.tsx +1 -0
- package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +46 -5
- package/src/TabularEditor/model.ts +5 -3
- package/src/components/AddAssembly.tsx +15 -9
- package/src/components/AddChildFeature.tsx +7 -73
- package/src/components/AddFeature.tsx +2 -57
- package/src/components/AddRefSeqAliases.tsx +285 -0
- package/src/components/CopyFeature.tsx +16 -33
- package/src/components/DeleteFeature.tsx +4 -6
- package/src/components/ImportFeatures.tsx +6 -3
- package/src/components/LogOut.tsx +105 -0
- package/src/components/ManageChecks.tsx +1 -0
- package/src/components/ManageUsers.tsx +21 -1
- package/src/components/ModifyFeatureAttribute.tsx +2 -2
- package/src/components/OntologyTermAutocomplete.tsx +0 -2
- package/src/components/OntologyTermMultiSelect.tsx +1 -0
- package/src/components/OpenLocalFile.tsx +6 -5
- package/src/components/ViewChangeLog.tsx +1 -0
- package/src/components/ViewCheckResults.tsx +1 -0
- package/src/components/index.ts +4 -0
- package/src/extensions/annotationFromPileup.ts +10 -16
- package/src/index.ts +57 -3
- package/src/session/ClientDataStore.ts +49 -46
- package/src/session/session.ts +186 -114
- package/src/util/loadAssemblyIntoClient.ts +4 -210
- package/src/FeatureDetailsWidget/RelatedFeature.tsx +0 -97
- package/src/LinearApolloDisplay/glyphs/CanonicalGeneGlyph.ts +0 -1204
- package/src/LinearApolloDisplay/glyphs/ImplicitExonGeneGlyph.ts +0 -716
- package/src/LinearApolloDisplay/stateModel/glyphs.ts +0 -47
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import { AnnotationFeature } from '@apollo-annotation/mst'
|
|
2
|
+
import {
|
|
3
|
+
LocationEndChange,
|
|
4
|
+
LocationStartChange,
|
|
5
|
+
} from '@apollo-annotation/shared'
|
|
6
|
+
import { AbstractSessionModel, revcom } from '@jbrowse/core/util'
|
|
7
|
+
import { Typography } from '@mui/material'
|
|
8
|
+
import { observer } from 'mobx-react'
|
|
9
|
+
import React from 'react'
|
|
10
|
+
|
|
11
|
+
import { ApolloSessionModel } from '../session'
|
|
12
|
+
import { CDSInfo } from './TranscriptSequence'
|
|
13
|
+
import { NumberTextField } from './NumberTextField'
|
|
14
|
+
|
|
15
|
+
interface ExonInfo {
|
|
16
|
+
min: number
|
|
17
|
+
max: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get single feature by featureId
|
|
22
|
+
* @param feature -
|
|
23
|
+
* @param featureId -
|
|
24
|
+
* @returns
|
|
25
|
+
*/
|
|
26
|
+
function getFeatureFromId(
|
|
27
|
+
feature: AnnotationFeature,
|
|
28
|
+
featureId: string,
|
|
29
|
+
): AnnotationFeature | undefined {
|
|
30
|
+
if (feature._id === featureId) {
|
|
31
|
+
return feature
|
|
32
|
+
}
|
|
33
|
+
// Check if there is also childFeatures in parent feature and it's not empty
|
|
34
|
+
// Let's get featureId from recursive method
|
|
35
|
+
if (!feature.children) {
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
for (const [, childFeature] of feature.children) {
|
|
39
|
+
const subFeature = getFeatureFromId(childFeature, featureId)
|
|
40
|
+
if (subFeature) {
|
|
41
|
+
return subFeature
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function findExonInRange(
|
|
48
|
+
exons: ExonInfo[],
|
|
49
|
+
pairStart: number,
|
|
50
|
+
pairEnd: number,
|
|
51
|
+
): ExonInfo | null {
|
|
52
|
+
for (const exon of exons) {
|
|
53
|
+
if (Number(exon.min) <= pairStart && Number(exon.max) >= pairEnd) {
|
|
54
|
+
return exon
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function removeMatchingExon(
|
|
61
|
+
exons: ExonInfo[],
|
|
62
|
+
matchStart: number,
|
|
63
|
+
matchEnd: number,
|
|
64
|
+
): ExonInfo[] {
|
|
65
|
+
// Filter the array to remove elements matching the specified start and end
|
|
66
|
+
return exons.filter(
|
|
67
|
+
(exon) => !(exon.min === matchStart && exon.max === matchEnd),
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const TranscriptBasicInformation = observer(
|
|
72
|
+
function TranscriptBasicInformation({
|
|
73
|
+
assembly,
|
|
74
|
+
feature,
|
|
75
|
+
refName,
|
|
76
|
+
session,
|
|
77
|
+
}: {
|
|
78
|
+
feature: AnnotationFeature
|
|
79
|
+
session: ApolloSessionModel
|
|
80
|
+
assembly: string
|
|
81
|
+
refName: string
|
|
82
|
+
}) {
|
|
83
|
+
const { notify } = session as unknown as AbstractSessionModel
|
|
84
|
+
const currentAssembly = session.apolloDataStore.assemblies.get(assembly)
|
|
85
|
+
const refData = currentAssembly?.getByRefName(refName)
|
|
86
|
+
const { changeManager } = session.apolloDataStore
|
|
87
|
+
|
|
88
|
+
function handleStartChange(
|
|
89
|
+
newStart: number,
|
|
90
|
+
featureId: string,
|
|
91
|
+
oldStart: number,
|
|
92
|
+
) {
|
|
93
|
+
newStart--
|
|
94
|
+
oldStart--
|
|
95
|
+
if (newStart < feature.min) {
|
|
96
|
+
notify('Feature start cannot be less than parent starts', 'error')
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
const subFeature = getFeatureFromId(feature, featureId)
|
|
100
|
+
if (!subFeature?.children) {
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
// Let's check CDS start and end values. And possibly update those too
|
|
104
|
+
for (const child of subFeature.children) {
|
|
105
|
+
if (
|
|
106
|
+
(child[1].type === 'CDS' || child[1].type === 'exon') &&
|
|
107
|
+
child[1].min === oldStart
|
|
108
|
+
) {
|
|
109
|
+
const change = new LocationStartChange({
|
|
110
|
+
typeName: 'LocationStartChange',
|
|
111
|
+
changedIds: [child[1]._id],
|
|
112
|
+
featureId,
|
|
113
|
+
oldStart,
|
|
114
|
+
newStart,
|
|
115
|
+
assembly,
|
|
116
|
+
})
|
|
117
|
+
changeManager.submit(change).catch(() => {
|
|
118
|
+
notify('Error updating feature start position', 'error')
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function handleEndChange(
|
|
126
|
+
newEnd: number,
|
|
127
|
+
featureId: string,
|
|
128
|
+
oldEnd: number,
|
|
129
|
+
) {
|
|
130
|
+
const subFeature = getFeatureFromId(feature, featureId)
|
|
131
|
+
if (newEnd > feature.max) {
|
|
132
|
+
notify('Feature start cannot be greater than parent end', 'error')
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
if (!subFeature?.children) {
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
// Let's check CDS start and end values. And possibly update those too
|
|
139
|
+
for (const child of subFeature.children) {
|
|
140
|
+
if (
|
|
141
|
+
(child[1].type === 'CDS' || child[1].type === 'exon') &&
|
|
142
|
+
child[1].max === oldEnd
|
|
143
|
+
) {
|
|
144
|
+
const change = new LocationEndChange({
|
|
145
|
+
typeName: 'LocationEndChange',
|
|
146
|
+
changedIds: [child[1]._id],
|
|
147
|
+
featureId,
|
|
148
|
+
oldEnd,
|
|
149
|
+
newEnd,
|
|
150
|
+
assembly,
|
|
151
|
+
})
|
|
152
|
+
changeManager.submit(change).catch(() => {
|
|
153
|
+
notify('Error updating feature end position', 'error')
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const featureNew = feature
|
|
161
|
+
let exonsArray: ExonInfo[] = []
|
|
162
|
+
const traverse = (currentFeature: AnnotationFeature) => {
|
|
163
|
+
if (currentFeature.type === 'exon') {
|
|
164
|
+
exonsArray.push({
|
|
165
|
+
min: currentFeature.min + 1,
|
|
166
|
+
max: currentFeature.max,
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
if (currentFeature.children) {
|
|
170
|
+
for (const child of currentFeature.children) {
|
|
171
|
+
traverse(child[1])
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
traverse(featureNew)
|
|
176
|
+
|
|
177
|
+
const CDSresult: CDSInfo[] = []
|
|
178
|
+
const CDSData = featureNew.cdsLocations
|
|
179
|
+
if (refData) {
|
|
180
|
+
for (const CDSDatum of CDSData) {
|
|
181
|
+
for (const dataPoint of CDSDatum) {
|
|
182
|
+
let startSeq = refData.getSequence(
|
|
183
|
+
Number(dataPoint.min) - 2,
|
|
184
|
+
Number(dataPoint.min),
|
|
185
|
+
)
|
|
186
|
+
let endSeq = refData.getSequence(
|
|
187
|
+
Number(dataPoint.max),
|
|
188
|
+
Number(dataPoint.max) + 2,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
if (featureNew.strand === -1 && startSeq && endSeq) {
|
|
192
|
+
startSeq = revcom(startSeq)
|
|
193
|
+
endSeq = revcom(endSeq)
|
|
194
|
+
}
|
|
195
|
+
const oneCDS: CDSInfo = {
|
|
196
|
+
id: featureNew._id,
|
|
197
|
+
type: 'CDS',
|
|
198
|
+
strand: Number(featureNew.strand),
|
|
199
|
+
min: dataPoint.min + 1,
|
|
200
|
+
max: dataPoint.max,
|
|
201
|
+
oldMin: dataPoint.min + 1,
|
|
202
|
+
oldMax: dataPoint.max,
|
|
203
|
+
startSeq,
|
|
204
|
+
endSeq,
|
|
205
|
+
}
|
|
206
|
+
// CDSresult.push(oneCDS)
|
|
207
|
+
// Check if there is already an object with the same start and end
|
|
208
|
+
const exists = CDSresult.some(
|
|
209
|
+
(obj) =>
|
|
210
|
+
obj.min === oneCDS.min &&
|
|
211
|
+
obj.max === oneCDS.max &&
|
|
212
|
+
obj.type === oneCDS.type,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
// If no such object exists, add the new object to the array
|
|
216
|
+
if (!exists) {
|
|
217
|
+
CDSresult.push(oneCDS)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Add possible UTRs
|
|
221
|
+
const foundExon = findExonInRange(
|
|
222
|
+
exonsArray,
|
|
223
|
+
dataPoint.min + 1,
|
|
224
|
+
dataPoint.max,
|
|
225
|
+
)
|
|
226
|
+
if (foundExon && Number(foundExon.min) < dataPoint.min) {
|
|
227
|
+
if (feature.strand === 1) {
|
|
228
|
+
const oneCDS: CDSInfo = {
|
|
229
|
+
id: feature._id,
|
|
230
|
+
type: 'five_prime_UTR',
|
|
231
|
+
strand: Number(feature.strand),
|
|
232
|
+
min: foundExon.min,
|
|
233
|
+
max: dataPoint.min,
|
|
234
|
+
oldMin: foundExon.min,
|
|
235
|
+
oldMax: dataPoint.min,
|
|
236
|
+
startSeq: '',
|
|
237
|
+
endSeq: '',
|
|
238
|
+
}
|
|
239
|
+
CDSresult.push(oneCDS)
|
|
240
|
+
} else {
|
|
241
|
+
const oneCDS: CDSInfo = {
|
|
242
|
+
id: feature._id,
|
|
243
|
+
type: 'three_prime_UTR',
|
|
244
|
+
strand: Number(feature.strand),
|
|
245
|
+
min: dataPoint.min + 1,
|
|
246
|
+
max: foundExon.min + 1,
|
|
247
|
+
oldMin: dataPoint.min + 1,
|
|
248
|
+
oldMax: foundExon.min + 1,
|
|
249
|
+
startSeq: '',
|
|
250
|
+
endSeq: '',
|
|
251
|
+
}
|
|
252
|
+
CDSresult.push(oneCDS)
|
|
253
|
+
}
|
|
254
|
+
exonsArray = removeMatchingExon(
|
|
255
|
+
exonsArray,
|
|
256
|
+
foundExon.min,
|
|
257
|
+
foundExon.max,
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
if (foundExon && Number(foundExon.max) > dataPoint.max) {
|
|
261
|
+
if (feature.strand === 1) {
|
|
262
|
+
const oneCDS: CDSInfo = {
|
|
263
|
+
id: feature._id,
|
|
264
|
+
type: 'three_prime_UTR',
|
|
265
|
+
strand: Number(feature.strand),
|
|
266
|
+
min: dataPoint.max + 1,
|
|
267
|
+
max: foundExon.max,
|
|
268
|
+
oldMin: dataPoint.max + 1,
|
|
269
|
+
oldMax: foundExon.max,
|
|
270
|
+
startSeq: '',
|
|
271
|
+
endSeq: '',
|
|
272
|
+
}
|
|
273
|
+
CDSresult.push(oneCDS)
|
|
274
|
+
} else {
|
|
275
|
+
const oneCDS: CDSInfo = {
|
|
276
|
+
id: feature._id,
|
|
277
|
+
type: 'five_prime_UTR',
|
|
278
|
+
strand: Number(feature.strand),
|
|
279
|
+
min: dataPoint.min + 1,
|
|
280
|
+
max: foundExon.max,
|
|
281
|
+
oldMin: dataPoint.min + 1,
|
|
282
|
+
oldMax: foundExon.max,
|
|
283
|
+
startSeq: '',
|
|
284
|
+
endSeq: '',
|
|
285
|
+
}
|
|
286
|
+
CDSresult.push(oneCDS)
|
|
287
|
+
}
|
|
288
|
+
exonsArray = removeMatchingExon(
|
|
289
|
+
exonsArray,
|
|
290
|
+
foundExon.min,
|
|
291
|
+
foundExon.max,
|
|
292
|
+
)
|
|
293
|
+
}
|
|
294
|
+
if (
|
|
295
|
+
dataPoint.min + 1 === foundExon?.min &&
|
|
296
|
+
dataPoint.max === foundExon.max
|
|
297
|
+
) {
|
|
298
|
+
exonsArray = removeMatchingExon(
|
|
299
|
+
exonsArray,
|
|
300
|
+
foundExon.min,
|
|
301
|
+
foundExon.max,
|
|
302
|
+
)
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Add remaining UTRs if any
|
|
309
|
+
if (exonsArray.length > 0) {
|
|
310
|
+
// eslint-disable-next-line unicorn/no-array-for-each
|
|
311
|
+
exonsArray.forEach((element: ExonInfo) => {
|
|
312
|
+
if (featureNew.strand === 1) {
|
|
313
|
+
const oneCDS: CDSInfo = {
|
|
314
|
+
id: featureNew._id,
|
|
315
|
+
type: 'five_prime_UTR',
|
|
316
|
+
strand: Number(featureNew.strand),
|
|
317
|
+
min: element.min + 1,
|
|
318
|
+
max: element.max,
|
|
319
|
+
oldMin: element.min + 1,
|
|
320
|
+
oldMax: element.max,
|
|
321
|
+
startSeq: '',
|
|
322
|
+
endSeq: '',
|
|
323
|
+
}
|
|
324
|
+
CDSresult.push(oneCDS)
|
|
325
|
+
} else {
|
|
326
|
+
const oneCDS: CDSInfo = {
|
|
327
|
+
id: featureNew._id,
|
|
328
|
+
type: 'three_prime_UTR',
|
|
329
|
+
strand: Number(featureNew.strand),
|
|
330
|
+
min: element.min + 1,
|
|
331
|
+
max: element.max + 1,
|
|
332
|
+
oldMin: element.min + 1,
|
|
333
|
+
oldMax: element.max + 1,
|
|
334
|
+
startSeq: '',
|
|
335
|
+
endSeq: '',
|
|
336
|
+
}
|
|
337
|
+
CDSresult.push(oneCDS)
|
|
338
|
+
}
|
|
339
|
+
exonsArray = removeMatchingExon(exonsArray, element.min, element.max)
|
|
340
|
+
})
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
CDSresult.sort((a, b) => {
|
|
344
|
+
// Primary sorting by 'start' property
|
|
345
|
+
const startDifference = Number(a.min) - Number(b.min)
|
|
346
|
+
if (startDifference !== 0) {
|
|
347
|
+
return startDifference
|
|
348
|
+
}
|
|
349
|
+
return Number(a.max) - Number(b.max)
|
|
350
|
+
})
|
|
351
|
+
if (CDSresult.length > 0) {
|
|
352
|
+
CDSresult[0].startSeq = ''
|
|
353
|
+
|
|
354
|
+
// eslint-disable-next-line unicorn/prefer-at
|
|
355
|
+
CDSresult[CDSresult.length - 1].endSeq = ''
|
|
356
|
+
|
|
357
|
+
// Loop through the array and clear "startSeq" or "endSeq" based on the conditions
|
|
358
|
+
for (let i = 0; i < CDSresult.length; i++) {
|
|
359
|
+
if (i > 0 && CDSresult[i].min === CDSresult[i - 1].max) {
|
|
360
|
+
// Clear "startSeq" if the current item's "start" is equal to the previous item's "end"
|
|
361
|
+
CDSresult[i].startSeq = ''
|
|
362
|
+
}
|
|
363
|
+
if (
|
|
364
|
+
i < CDSresult.length - 1 &&
|
|
365
|
+
CDSresult[i].max === CDSresult[i + 1].min
|
|
366
|
+
) {
|
|
367
|
+
// Clear "endSeq" if the next item's "start" is equal to the current item's "end"
|
|
368
|
+
CDSresult[i].endSeq = ''
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const transcriptItems = CDSresult
|
|
374
|
+
|
|
375
|
+
return (
|
|
376
|
+
<>
|
|
377
|
+
<Typography
|
|
378
|
+
variant="h5"
|
|
379
|
+
style={{ marginLeft: '15px', marginBottom: '0' }}
|
|
380
|
+
>
|
|
381
|
+
CDS and UTRs
|
|
382
|
+
</Typography>
|
|
383
|
+
<div>
|
|
384
|
+
{transcriptItems.map((item, index) => (
|
|
385
|
+
<div key={index} style={{ display: 'flex', alignItems: 'center' }}>
|
|
386
|
+
<span style={{ marginLeft: '20px', width: '50px' }}>
|
|
387
|
+
{item.type === 'three_prime_UTR'
|
|
388
|
+
? '3 UTR'
|
|
389
|
+
: // eslint-disable-next-line unicorn/no-nested-ternary
|
|
390
|
+
item.type === 'five_prime_UTR'
|
|
391
|
+
? '5 UTR'
|
|
392
|
+
: 'CDS'}
|
|
393
|
+
</span>
|
|
394
|
+
<span style={{ fontWeight: 'bold', width: '30px' }}>
|
|
395
|
+
{item.startSeq}
|
|
396
|
+
</span>
|
|
397
|
+
<NumberTextField
|
|
398
|
+
margin="dense"
|
|
399
|
+
id={item.id}
|
|
400
|
+
disabled={item.type !== 'CDS'}
|
|
401
|
+
style={{
|
|
402
|
+
width: '150px',
|
|
403
|
+
marginLeft: '8px',
|
|
404
|
+
backgroundColor:
|
|
405
|
+
item.startSeq.trim() === '' && index !== 0
|
|
406
|
+
? 'lightblue'
|
|
407
|
+
: 'inherit',
|
|
408
|
+
}}
|
|
409
|
+
variant="outlined"
|
|
410
|
+
value={item.min}
|
|
411
|
+
onChangeCommitted={(newStart: number) => {
|
|
412
|
+
handleStartChange(newStart, item.id, Number(item.oldMin))
|
|
413
|
+
}}
|
|
414
|
+
/>
|
|
415
|
+
<span style={{ margin: '0 10px' }}>
|
|
416
|
+
{/* eslint-disable-next-line unicorn/no-nested-ternary */}
|
|
417
|
+
{item.strand === -1 ? '-' : item.strand === 1 ? '+' : ''}
|
|
418
|
+
</span>
|
|
419
|
+
<NumberTextField
|
|
420
|
+
margin="dense"
|
|
421
|
+
id={item.id}
|
|
422
|
+
disabled={item.type !== 'CDS'}
|
|
423
|
+
style={{
|
|
424
|
+
width: '150px',
|
|
425
|
+
backgroundColor:
|
|
426
|
+
item.endSeq.trim() === '' &&
|
|
427
|
+
index + 1 !== transcriptItems.length
|
|
428
|
+
? 'lightblue'
|
|
429
|
+
: 'inherit',
|
|
430
|
+
}}
|
|
431
|
+
variant="outlined"
|
|
432
|
+
value={item.max}
|
|
433
|
+
onChangeCommitted={(newEnd: number) => {
|
|
434
|
+
handleEndChange(newEnd, item.id, Number(item.oldMax))
|
|
435
|
+
}}
|
|
436
|
+
/>
|
|
437
|
+
<span style={{ marginLeft: '8px', fontWeight: 'bold' }}>
|
|
438
|
+
{item.endSeq}
|
|
439
|
+
</span>
|
|
440
|
+
</div>
|
|
441
|
+
))}
|
|
442
|
+
</div>
|
|
443
|
+
</>
|
|
444
|
+
)
|
|
445
|
+
},
|
|
446
|
+
)
|