@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
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AnnotationFeature } from '@apollo-annotation/mst'
|
|
2
2
|
import { MenuItem } from '@jbrowse/core/ui'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
AbstractSessionModel,
|
|
5
|
+
SessionWithWidgets,
|
|
6
|
+
isSessionModelWithWidgets,
|
|
7
|
+
} from '@jbrowse/core/util'
|
|
4
8
|
|
|
5
9
|
import { ChangeManager } from '../../ChangeManager'
|
|
6
10
|
import {
|
|
@@ -13,11 +17,11 @@ import { ApolloSessionModel } from '../../session'
|
|
|
13
17
|
import { getApolloInternetAccount } from '../../util'
|
|
14
18
|
|
|
15
19
|
export function featureContextMenuItems(
|
|
16
|
-
feature:
|
|
20
|
+
feature: AnnotationFeature | undefined,
|
|
17
21
|
region: { assemblyName: string; refName: string; start: number; end: number },
|
|
18
22
|
getAssemblyId: (assemblyName: string) => string,
|
|
19
|
-
selectedFeature:
|
|
20
|
-
setSelectedFeature: (f:
|
|
23
|
+
selectedFeature: AnnotationFeature | undefined,
|
|
24
|
+
setSelectedFeature: (f: AnnotationFeature | undefined) => void,
|
|
21
25
|
session: ApolloSessionModel,
|
|
22
26
|
changeManager: ChangeManager,
|
|
23
27
|
) {
|
|
@@ -30,6 +34,25 @@ export function featureContextMenuItems(
|
|
|
30
34
|
const sourceAssemblyId = getAssemblyId(region.assemblyName)
|
|
31
35
|
const currentAssemblyId = getAssemblyId(region.assemblyName)
|
|
32
36
|
menuItems.push(
|
|
37
|
+
{
|
|
38
|
+
label: 'Edit feature details',
|
|
39
|
+
onClick: () => {
|
|
40
|
+
const apolloFeatureWidget = (
|
|
41
|
+
session as unknown as SessionWithWidgets
|
|
42
|
+
).addWidget(
|
|
43
|
+
'ApolloFeatureDetailsWidget',
|
|
44
|
+
'apolloFeatureDetailsWidget',
|
|
45
|
+
{
|
|
46
|
+
feature,
|
|
47
|
+
assembly: currentAssemblyId,
|
|
48
|
+
refName: region.refName,
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
;(session as unknown as SessionWithWidgets).showWidget(
|
|
52
|
+
apolloFeatureWidget,
|
|
53
|
+
)
|
|
54
|
+
},
|
|
55
|
+
},
|
|
33
56
|
{
|
|
34
57
|
label: 'Add child feature',
|
|
35
58
|
disabled: readOnly,
|
|
@@ -114,6 +137,24 @@ export function featureContextMenuItems(
|
|
|
114
137
|
},
|
|
115
138
|
},
|
|
116
139
|
)
|
|
140
|
+
if (feature.type === 'mRNA' && isSessionModelWithWidgets(session)) {
|
|
141
|
+
menuItems.push({
|
|
142
|
+
label: 'Edit transcript details',
|
|
143
|
+
onClick: () => {
|
|
144
|
+
const apolloTranscriptWidget = session.addWidget(
|
|
145
|
+
'ApolloTranscriptDetails',
|
|
146
|
+
'apolloTranscriptDetails',
|
|
147
|
+
{
|
|
148
|
+
feature,
|
|
149
|
+
assembly: currentAssemblyId,
|
|
150
|
+
changeManager,
|
|
151
|
+
refName: region.refName,
|
|
152
|
+
},
|
|
153
|
+
)
|
|
154
|
+
session.showWidget(apolloTranscriptWidget)
|
|
155
|
+
},
|
|
156
|
+
})
|
|
157
|
+
}
|
|
117
158
|
}
|
|
118
159
|
return menuItems
|
|
119
160
|
}
|
|
@@ -39,6 +39,8 @@ export const TabularEditorStateModelType = types
|
|
|
39
39
|
// },
|
|
40
40
|
}))
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
// eslint disable because of
|
|
43
|
+
// https://mobx-state-tree.js.org/tips/typescript#using-a-mst-type-at-design-time
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
45
|
+
export interface TabularEditorStateModel
|
|
46
|
+
extends Instance<typeof TabularEditorStateModelType> {}
|
|
@@ -103,15 +103,19 @@ export function AddAssembly({
|
|
|
103
103
|
}
|
|
104
104
|
const selectedFile = e.target.files.item(0)
|
|
105
105
|
setFile(selectedFile)
|
|
106
|
+
if (!selectedFile) {
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
const fileNameLower = selectedFile.name.toLowerCase()
|
|
106
110
|
if (
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
111
|
+
fileNameLower.endsWith('.fasta') ||
|
|
112
|
+
fileNameLower.endsWith('.fna') ||
|
|
113
|
+
fileNameLower.endsWith('.fa')
|
|
110
114
|
) {
|
|
111
115
|
setFileType(FileType.FASTA)
|
|
112
116
|
} else if (
|
|
113
|
-
|
|
114
|
-
|
|
117
|
+
fileNameLower.endsWith('.gff3') ||
|
|
118
|
+
fileNameLower.endsWith('.gff')
|
|
115
119
|
) {
|
|
116
120
|
setFileType(FileType.GFF3)
|
|
117
121
|
}
|
|
@@ -168,19 +172,21 @@ export function AddAssembly({
|
|
|
168
172
|
const { baseURL, getFetcher, internetAccountId } = selectedInternetAccount
|
|
169
173
|
if (fileType !== FileType.EXTERNAL && file) {
|
|
170
174
|
// First upload file
|
|
171
|
-
const url = new URL('files', baseURL)
|
|
175
|
+
const url = new URL('files', baseURL)
|
|
176
|
+
url.searchParams.set('type', fileType)
|
|
177
|
+
const uri = url.href
|
|
172
178
|
const formData = new FormData()
|
|
173
179
|
formData.append('file', file)
|
|
174
180
|
formData.append('fileName', file.name)
|
|
175
181
|
formData.append('type', fileType)
|
|
176
182
|
const apolloFetchFile = getFetcher({
|
|
177
183
|
locationType: 'UriLocation',
|
|
178
|
-
uri
|
|
184
|
+
uri,
|
|
179
185
|
})
|
|
180
186
|
if (apolloFetchFile) {
|
|
181
187
|
jobsManager.update(job.name, 'Uploading file, this may take awhile')
|
|
182
188
|
const { signal } = controller
|
|
183
|
-
const response = await apolloFetchFile(
|
|
189
|
+
const response = await apolloFetchFile(uri, {
|
|
184
190
|
method: 'POST',
|
|
185
191
|
body: formData,
|
|
186
192
|
signal,
|
|
@@ -219,7 +225,7 @@ export function AddAssembly({
|
|
|
219
225
|
const fileUploadChangeBase = {
|
|
220
226
|
assembly: new ObjectID().toHexString(),
|
|
221
227
|
assemblyName,
|
|
222
|
-
fileId,
|
|
228
|
+
fileIds: { fa: fileId },
|
|
223
229
|
}
|
|
224
230
|
change =
|
|
225
231
|
fileType === FileType.GFF3 && importFeatures
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-misused-promises */
|
|
2
|
-
import {
|
|
2
|
+
import { AnnotationFeature } from '@apollo-annotation/mst'
|
|
3
3
|
import { AddFeatureChange } from '@apollo-annotation/shared'
|
|
4
4
|
import { AbstractSessionModel } from '@jbrowse/core/util'
|
|
5
5
|
import {
|
|
@@ -7,11 +7,6 @@ import {
|
|
|
7
7
|
DialogActions,
|
|
8
8
|
DialogContent,
|
|
9
9
|
DialogContentText,
|
|
10
|
-
FormControl,
|
|
11
|
-
InputLabel,
|
|
12
|
-
MenuItem,
|
|
13
|
-
Select,
|
|
14
|
-
SelectChangeEvent,
|
|
15
10
|
TextField,
|
|
16
11
|
} from '@mui/material'
|
|
17
12
|
import ObjectID from 'bson-objectid'
|
|
@@ -28,17 +23,11 @@ import { OntologyTermAutocomplete } from './OntologyTermAutocomplete'
|
|
|
28
23
|
interface AddChildFeatureProps {
|
|
29
24
|
session: ApolloSessionModel
|
|
30
25
|
handleClose(): void
|
|
31
|
-
sourceFeature:
|
|
26
|
+
sourceFeature: AnnotationFeature
|
|
32
27
|
sourceAssemblyId: string
|
|
33
28
|
changeManager: ChangeManager
|
|
34
29
|
}
|
|
35
30
|
|
|
36
|
-
enum PhaseEnum {
|
|
37
|
-
zero = 0,
|
|
38
|
-
one = 1,
|
|
39
|
-
two = 2,
|
|
40
|
-
}
|
|
41
|
-
|
|
42
31
|
export function AddChildFeature({
|
|
43
32
|
changeManager,
|
|
44
33
|
handleClose,
|
|
@@ -47,17 +36,14 @@ export function AddChildFeature({
|
|
|
47
36
|
sourceFeature,
|
|
48
37
|
}: AddChildFeatureProps) {
|
|
49
38
|
const { notify } = session as unknown as AbstractSessionModel
|
|
50
|
-
const [end, setEnd] = useState(String(sourceFeature.
|
|
51
|
-
const [start, setStart] = useState(String(sourceFeature.
|
|
39
|
+
const [end, setEnd] = useState(String(sourceFeature.max))
|
|
40
|
+
const [start, setStart] = useState(String(sourceFeature.min + 1))
|
|
52
41
|
const [type, setType] = useState('')
|
|
53
|
-
const [phase, setPhase] = useState('')
|
|
54
|
-
const [phaseAsNumber, setPhaseAsNumber] = useState<PhaseEnum>()
|
|
55
|
-
const [showPhase, setShowPhase] = useState<boolean>(false)
|
|
56
42
|
const [errorMessage, setErrorMessage] = useState('')
|
|
57
43
|
const [typeWarningText, setTypeWarningText] = useState('')
|
|
58
44
|
|
|
59
45
|
async function fetchValidTerms(
|
|
60
|
-
parentFeature:
|
|
46
|
+
parentFeature: AnnotationFeature | undefined,
|
|
61
47
|
ontologyStore: OntologyStore,
|
|
62
48
|
_signal: AbortSignal,
|
|
63
49
|
) {
|
|
@@ -78,22 +64,16 @@ export function AddChildFeature({
|
|
|
78
64
|
async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
79
65
|
event.preventDefault()
|
|
80
66
|
setErrorMessage('')
|
|
81
|
-
if (showPhase && phase === '') {
|
|
82
|
-
setErrorMessage('The phase is REQUIRED for all CDS features.')
|
|
83
|
-
return
|
|
84
|
-
}
|
|
85
67
|
const change = new AddFeatureChange({
|
|
86
68
|
changedIds: [sourceFeature._id],
|
|
87
69
|
typeName: 'AddFeatureChange',
|
|
88
70
|
assembly: sourceAssemblyId,
|
|
89
71
|
addedFeature: {
|
|
90
72
|
_id: new ObjectID().toHexString(),
|
|
91
|
-
gffId: '',
|
|
92
73
|
refSeq: sourceFeature.refSeq,
|
|
93
|
-
|
|
94
|
-
|
|
74
|
+
min: Number(start) - 1,
|
|
75
|
+
max: Number(end),
|
|
95
76
|
type,
|
|
96
|
-
phase: phaseAsNumber,
|
|
97
77
|
},
|
|
98
78
|
parentFeatureId: sourceFeature._id,
|
|
99
79
|
})
|
|
@@ -105,35 +85,6 @@ export function AddChildFeature({
|
|
|
105
85
|
function handleChangeType(newType: string) {
|
|
106
86
|
setErrorMessage('')
|
|
107
87
|
setType(newType)
|
|
108
|
-
if (newType.startsWith('CDS')) {
|
|
109
|
-
setShowPhase(true)
|
|
110
|
-
setPhase('')
|
|
111
|
-
} else {
|
|
112
|
-
setShowPhase(false)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
function handleChangePhase(e: SelectChangeEvent) {
|
|
116
|
-
setErrorMessage('')
|
|
117
|
-
setPhase(e.target.value)
|
|
118
|
-
|
|
119
|
-
switch (Number(e.target.value)) {
|
|
120
|
-
case 0: {
|
|
121
|
-
setPhaseAsNumber(PhaseEnum.zero)
|
|
122
|
-
break
|
|
123
|
-
}
|
|
124
|
-
case 1: {
|
|
125
|
-
setPhaseAsNumber(PhaseEnum.one)
|
|
126
|
-
break
|
|
127
|
-
}
|
|
128
|
-
case 2: {
|
|
129
|
-
setPhaseAsNumber(PhaseEnum.two)
|
|
130
|
-
break
|
|
131
|
-
}
|
|
132
|
-
default: {
|
|
133
|
-
// eslint-disable-next-line unicorn/no-useless-undefined
|
|
134
|
-
setPhaseAsNumber(undefined)
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
88
|
}
|
|
138
89
|
const error = Number(end) <= Number(start)
|
|
139
90
|
return (
|
|
@@ -172,13 +123,6 @@ export function AddChildFeature({
|
|
|
172
123
|
error={error}
|
|
173
124
|
helperText={error ? '"End" must be greater than "Start"' : null}
|
|
174
125
|
/>
|
|
175
|
-
{/* <Select value={type} onChange={handleChangeType} label="Type">
|
|
176
|
-
{(possibleChildTypes ?? []).map((option) => (
|
|
177
|
-
<MenuItem key={option} value={option}>
|
|
178
|
-
{option}
|
|
179
|
-
</MenuItem>
|
|
180
|
-
))}
|
|
181
|
-
</Select> */}
|
|
182
126
|
<OntologyTermAutocomplete
|
|
183
127
|
session={session}
|
|
184
128
|
ontologyName="Sequence Ontology"
|
|
@@ -202,16 +146,6 @@ export function AddChildFeature({
|
|
|
202
146
|
}
|
|
203
147
|
}}
|
|
204
148
|
/>
|
|
205
|
-
{showPhase ? (
|
|
206
|
-
<FormControl>
|
|
207
|
-
<InputLabel>Phase</InputLabel>
|
|
208
|
-
<Select value={phase} onChange={handleChangePhase}>
|
|
209
|
-
<MenuItem value={0}>0</MenuItem>
|
|
210
|
-
<MenuItem value={1}>1</MenuItem>
|
|
211
|
-
<MenuItem value={2}>2</MenuItem>
|
|
212
|
-
</Select>
|
|
213
|
-
</FormControl>
|
|
214
|
-
) : null}
|
|
215
149
|
</DialogContent>
|
|
216
150
|
<DialogActions>
|
|
217
151
|
<Button
|
|
@@ -30,12 +30,6 @@ interface AddFeatureProps {
|
|
|
30
30
|
changeManager: ChangeManager
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
enum PhaseEnum {
|
|
34
|
-
zero = 0,
|
|
35
|
-
one = 1,
|
|
36
|
-
two = 2,
|
|
37
|
-
}
|
|
38
|
-
|
|
39
33
|
export function AddFeature({
|
|
40
34
|
changeManager,
|
|
41
35
|
handleClose,
|
|
@@ -46,19 +40,12 @@ export function AddFeature({
|
|
|
46
40
|
const [end, setEnd] = useState(String(region.end))
|
|
47
41
|
const [start, setStart] = useState(String(region.start + 1))
|
|
48
42
|
const [type, setType] = useState('')
|
|
49
|
-
const [phase, setPhase] = useState('')
|
|
50
43
|
const [strand, setStrand] = useState<1 | -1 | undefined>()
|
|
51
|
-
const [phaseAsNumber, setPhaseAsNumber] = useState<PhaseEnum>()
|
|
52
|
-
const [showPhase, setShowPhase] = useState<boolean>(false)
|
|
53
44
|
const [errorMessage, setErrorMessage] = useState('')
|
|
54
45
|
|
|
55
46
|
async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
56
47
|
event.preventDefault()
|
|
57
48
|
setErrorMessage('')
|
|
58
|
-
if (showPhase && phase === '') {
|
|
59
|
-
setErrorMessage('The phase is REQUIRED for all CDS features.')
|
|
60
|
-
return
|
|
61
|
-
}
|
|
62
49
|
|
|
63
50
|
let refSeqId
|
|
64
51
|
for (const [, asm] of session.apolloDataStore.assemblies ?? new Map()) {
|
|
@@ -83,12 +70,10 @@ export function AddFeature({
|
|
|
83
70
|
assembly: region.assemblyName,
|
|
84
71
|
addedFeature: {
|
|
85
72
|
_id: id,
|
|
86
|
-
gffId: '',
|
|
87
73
|
refSeq: refSeqId,
|
|
88
|
-
|
|
89
|
-
|
|
74
|
+
min: Number(start) - 1,
|
|
75
|
+
max: Number(end),
|
|
90
76
|
type,
|
|
91
|
-
phase: phaseAsNumber,
|
|
92
77
|
strand,
|
|
93
78
|
},
|
|
94
79
|
})
|
|
@@ -101,12 +86,6 @@ export function AddFeature({
|
|
|
101
86
|
function handleChangeType(newType: string) {
|
|
102
87
|
setErrorMessage('')
|
|
103
88
|
setType(newType)
|
|
104
|
-
if (newType.startsWith('CDS')) {
|
|
105
|
-
setShowPhase(true)
|
|
106
|
-
setPhase('')
|
|
107
|
-
} else {
|
|
108
|
-
setShowPhase(false)
|
|
109
|
-
}
|
|
110
89
|
}
|
|
111
90
|
|
|
112
91
|
function handleChangeStrand(e: SelectChangeEvent) {
|
|
@@ -128,30 +107,6 @@ export function AddFeature({
|
|
|
128
107
|
}
|
|
129
108
|
}
|
|
130
109
|
|
|
131
|
-
function handleChangePhase(e: SelectChangeEvent) {
|
|
132
|
-
setErrorMessage('')
|
|
133
|
-
setPhase(e.target.value)
|
|
134
|
-
|
|
135
|
-
switch (Number(e.target.value)) {
|
|
136
|
-
case 0: {
|
|
137
|
-
setPhaseAsNumber(PhaseEnum.zero)
|
|
138
|
-
break
|
|
139
|
-
}
|
|
140
|
-
case 1: {
|
|
141
|
-
setPhaseAsNumber(PhaseEnum.one)
|
|
142
|
-
break
|
|
143
|
-
}
|
|
144
|
-
case 2: {
|
|
145
|
-
setPhaseAsNumber(PhaseEnum.two)
|
|
146
|
-
break
|
|
147
|
-
}
|
|
148
|
-
default: {
|
|
149
|
-
// eslint-disable-next-line unicorn/no-useless-undefined
|
|
150
|
-
setPhaseAsNumber(undefined)
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
110
|
const error = Number(end) <= Number(start)
|
|
156
111
|
|
|
157
112
|
return (
|
|
@@ -224,16 +179,6 @@ export function AddFeature({
|
|
|
224
179
|
<MenuItem value={-1}>-</MenuItem>
|
|
225
180
|
</Select>
|
|
226
181
|
</FormControl>
|
|
227
|
-
{showPhase ? (
|
|
228
|
-
<FormControl>
|
|
229
|
-
<InputLabel>Phase</InputLabel>
|
|
230
|
-
<Select value={phase} onChange={handleChangePhase}>
|
|
231
|
-
<MenuItem value={0}>0</MenuItem>
|
|
232
|
-
<MenuItem value={1}>1</MenuItem>
|
|
233
|
-
<MenuItem value={2}>2</MenuItem>
|
|
234
|
-
</Select>
|
|
235
|
-
</FormControl>
|
|
236
|
-
) : null}
|
|
237
182
|
</DialogContent>
|
|
238
183
|
<DialogActions>
|
|
239
184
|
<Button
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { ChangeManager } from '../ChangeManager'
|
|
2
|
+
import { ApolloSessionModel } from '../session'
|
|
3
|
+
import { Dialog } from './Dialog'
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
DialogActions,
|
|
7
|
+
DialogContent,
|
|
8
|
+
DialogContentText,
|
|
9
|
+
FormControl,
|
|
10
|
+
Grid,
|
|
11
|
+
InputLabel,
|
|
12
|
+
MenuItem,
|
|
13
|
+
Select,
|
|
14
|
+
SelectChangeEvent,
|
|
15
|
+
} from '@mui/material'
|
|
16
|
+
import React, { useEffect, useRef, useState } from 'react'
|
|
17
|
+
import {
|
|
18
|
+
CollaborationServerDriver,
|
|
19
|
+
ApolloInternetAccount,
|
|
20
|
+
} from '../BackendDrivers'
|
|
21
|
+
import { Assembly } from '@jbrowse/core/assemblyManager/assembly'
|
|
22
|
+
import { DataGrid, GridColDef, GridRowModel } from '@mui/x-data-grid'
|
|
23
|
+
import {
|
|
24
|
+
AddRefSeqAliasesChange,
|
|
25
|
+
SerializedRefSeqAliases,
|
|
26
|
+
} from '@apollo-annotation/shared'
|
|
27
|
+
|
|
28
|
+
const columns: GridColDef[] = [
|
|
29
|
+
{ field: 'refName', headerName: 'Ref Name' },
|
|
30
|
+
{ field: 'aliases', headerName: 'Aliases', editable: true },
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
interface AddChildFeatureProps {
|
|
34
|
+
session: ApolloSessionModel
|
|
35
|
+
handleClose(): void
|
|
36
|
+
changeManager: ChangeManager
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const isGeneratedObjectId = (key: string): boolean => {
|
|
40
|
+
const pattern = /^[\da-f]{24}$/i
|
|
41
|
+
return pattern.test(key)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function AddRefSeqAliases({
|
|
45
|
+
changeManager,
|
|
46
|
+
handleClose,
|
|
47
|
+
session,
|
|
48
|
+
}: AddChildFeatureProps) {
|
|
49
|
+
const fileRef = useRef<HTMLInputElement>(null)
|
|
50
|
+
const [errorMessage, setErrorMessage] = useState('')
|
|
51
|
+
const [enableSubmit, setEnableSubmit] = useState(false)
|
|
52
|
+
const [selectedAssembly, setSelectedAssembly] = useState<Assembly>()
|
|
53
|
+
const [selectedRows, setSelectedRows] = useState<
|
|
54
|
+
{
|
|
55
|
+
id: number
|
|
56
|
+
refName: string
|
|
57
|
+
aliases: string
|
|
58
|
+
}[]
|
|
59
|
+
>([])
|
|
60
|
+
const [refNameAliasMap, setRefNameAliasMap] = useState<Map<string, string[]>>(
|
|
61
|
+
new Map(),
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
const { apolloDataStore } = session
|
|
65
|
+
const { collaborationServerDriver } = apolloDataStore as {
|
|
66
|
+
collaborationServerDriver: CollaborationServerDriver
|
|
67
|
+
getInternetAccount(
|
|
68
|
+
assemblyName?: string,
|
|
69
|
+
internetAccountId?: string,
|
|
70
|
+
): ApolloInternetAccount
|
|
71
|
+
}
|
|
72
|
+
const assemblies = collaborationServerDriver.getAssemblies()
|
|
73
|
+
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
let retry = 0
|
|
76
|
+
const maxRetries = 2
|
|
77
|
+
const initializeRefNameAliasMap = () => {
|
|
78
|
+
if (!selectedAssembly) {
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
const initialMap = new Map<string, string[]>()
|
|
82
|
+
if (retry < maxRetries && !selectedAssembly.refNames) {
|
|
83
|
+
retry++
|
|
84
|
+
setTimeout(initializeRefNameAliasMap, 50)
|
|
85
|
+
}
|
|
86
|
+
if (!selectedAssembly.refNames) {
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
const refNameAliasess = selectedAssembly.refNameAliases
|
|
90
|
+
for (const key in refNameAliasess) {
|
|
91
|
+
const value = refNameAliasess[key]
|
|
92
|
+
if (!value || isGeneratedObjectId(key)) {
|
|
93
|
+
continue
|
|
94
|
+
}
|
|
95
|
+
if (initialMap.has(value)) {
|
|
96
|
+
const aliases = initialMap.get(value) ?? []
|
|
97
|
+
initialMap.set(value, [...aliases, key])
|
|
98
|
+
} else {
|
|
99
|
+
initialMap.set(value, [key])
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
setRefNameAliasMap(initialMap)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
initializeRefNameAliasMap()
|
|
106
|
+
}, [selectedAssembly])
|
|
107
|
+
|
|
108
|
+
const handleChangeAssembly = (e: SelectChangeEvent) => {
|
|
109
|
+
const newAssembly = assemblies.find((asm) => asm.name === e.target.value)
|
|
110
|
+
setSelectedAssembly(newAssembly)
|
|
111
|
+
setEnableSubmit(false)
|
|
112
|
+
setErrorMessage('')
|
|
113
|
+
if (fileRef.current) {
|
|
114
|
+
fileRef.current.value = ''
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const handleChangeFile = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
119
|
+
if (!e.target.files) {
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
// eslint-disable-next-line prefer-destructuring
|
|
123
|
+
const file = e.target.files[0]
|
|
124
|
+
const fileContent = await file.text()
|
|
125
|
+
const lines = fileContent.split('\n')
|
|
126
|
+
const newMap = new Map(refNameAliasMap)
|
|
127
|
+
setErrorMessage('')
|
|
128
|
+
for (const line of lines) {
|
|
129
|
+
const aliases = line.split('\t')
|
|
130
|
+
for (const alias of aliases) {
|
|
131
|
+
if (newMap.has(alias)) {
|
|
132
|
+
newMap.set(alias, [...(newMap.get(alias) ?? []), ...aliases])
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
setRefNameAliasMap(newMap)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const handleChangeFileHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
140
|
+
handleChangeFile(e).catch(() => {
|
|
141
|
+
setErrorMessage('Error reading file')
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const rowSelectionChange = (ids: number[]) => {
|
|
146
|
+
if (ids.length > 0) {
|
|
147
|
+
setEnableSubmit(true)
|
|
148
|
+
const selectedRows = ids.flatMap((id) =>
|
|
149
|
+
getTableRows().filter((row) => row.id === id),
|
|
150
|
+
)
|
|
151
|
+
setSelectedRows(selectedRows)
|
|
152
|
+
} else {
|
|
153
|
+
setEnableSubmit(false)
|
|
154
|
+
setSelectedRows([])
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const getTableRows = () => {
|
|
159
|
+
return [...refNameAliasMap].map((ele, i) => ({
|
|
160
|
+
id: i,
|
|
161
|
+
refName: ele[0],
|
|
162
|
+
aliases: ele[1].filter((alias) => alias !== ele[0]).join(', '),
|
|
163
|
+
}))
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const processRowUpdate = (newRow: GridRowModel, _oldRow: GridRowModel) => {
|
|
167
|
+
const newMap = new Map(refNameAliasMap)
|
|
168
|
+
newMap.set(newRow.refName as string, (newRow.aliases as string).split(','))
|
|
169
|
+
setRefNameAliasMap(newMap)
|
|
170
|
+
return newRow
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const handleSubmit = () => {
|
|
174
|
+
const refSeqAliases: SerializedRefSeqAliases[] = []
|
|
175
|
+
for (const row of selectedRows) {
|
|
176
|
+
const { refName } = row
|
|
177
|
+
const aliases: string[] = row.aliases
|
|
178
|
+
.split(',')
|
|
179
|
+
.map((alias) => alias.trim())
|
|
180
|
+
.filter((alias) => alias.length > 0)
|
|
181
|
+
refSeqAliases.push({
|
|
182
|
+
refName,
|
|
183
|
+
aliases,
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
setErrorMessage('')
|
|
187
|
+
if (!selectedAssembly) {
|
|
188
|
+
setErrorMessage('No assembly selected')
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
const change = new AddRefSeqAliasesChange({
|
|
192
|
+
typeName: 'AddRefSeqAliasesChange',
|
|
193
|
+
assembly: selectedAssembly.name,
|
|
194
|
+
refSeqAliases,
|
|
195
|
+
})
|
|
196
|
+
changeManager.submit(change).catch(() => {
|
|
197
|
+
setErrorMessage('Error submitting change')
|
|
198
|
+
})
|
|
199
|
+
handleClose()
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<Dialog
|
|
204
|
+
open
|
|
205
|
+
title="Add reference sequence aliases"
|
|
206
|
+
handleClose={handleClose}
|
|
207
|
+
maxWidth={'sm'}
|
|
208
|
+
data-testid="add-refseq-alias"
|
|
209
|
+
fullWidth
|
|
210
|
+
>
|
|
211
|
+
<DialogContent style={{ display: 'flex', flexDirection: 'column' }}>
|
|
212
|
+
<Grid container spacing={2}>
|
|
213
|
+
<Grid item xs={4}>
|
|
214
|
+
<FormControl disabled={enableSubmit && !errorMessage} fullWidth>
|
|
215
|
+
<InputLabel id="demo-simple-select-label">Assembly</InputLabel>
|
|
216
|
+
<Select
|
|
217
|
+
labelId="demo-simple-select-label"
|
|
218
|
+
id="demo-simple-select"
|
|
219
|
+
label="Assembly"
|
|
220
|
+
value={selectedAssembly?.name ?? ''}
|
|
221
|
+
onChange={handleChangeAssembly}
|
|
222
|
+
>
|
|
223
|
+
{assemblies.map((option) => (
|
|
224
|
+
<MenuItem key={option.name} value={option.name}>
|
|
225
|
+
{option.displayName ?? option.name}
|
|
226
|
+
</MenuItem>
|
|
227
|
+
))}
|
|
228
|
+
</Select>
|
|
229
|
+
</FormControl>
|
|
230
|
+
</Grid>
|
|
231
|
+
<Grid item xs={1}></Grid>
|
|
232
|
+
<Grid item xs={7}>
|
|
233
|
+
<InputLabel>Load RefName alias</InputLabel>
|
|
234
|
+
<input
|
|
235
|
+
type="file"
|
|
236
|
+
onChange={handleChangeFileHandler}
|
|
237
|
+
ref={fileRef}
|
|
238
|
+
disabled={(enableSubmit && !errorMessage) || !selectedAssembly}
|
|
239
|
+
/>
|
|
240
|
+
</Grid>
|
|
241
|
+
</Grid>
|
|
242
|
+
{selectedAssembly && refNameAliasMap.size > 0 ? (
|
|
243
|
+
<div style={{ height: 200, width: '100%', marginTop: 20 }}>
|
|
244
|
+
<InputLabel>
|
|
245
|
+
Refname aliases found for selected assembly.
|
|
246
|
+
</InputLabel>
|
|
247
|
+
<DataGrid
|
|
248
|
+
rows={getTableRows()}
|
|
249
|
+
columns={columns}
|
|
250
|
+
initialState={{
|
|
251
|
+
pagination: {
|
|
252
|
+
paginationModel: { page: 0, pageSize: 5 },
|
|
253
|
+
},
|
|
254
|
+
}}
|
|
255
|
+
pageSizeOptions={[5, 10]}
|
|
256
|
+
onRowSelectionModelChange={(ids) => {
|
|
257
|
+
rowSelectionChange(ids as number[])
|
|
258
|
+
}}
|
|
259
|
+
processRowUpdate={processRowUpdate}
|
|
260
|
+
checkboxSelection
|
|
261
|
+
></DataGrid>
|
|
262
|
+
</div>
|
|
263
|
+
) : null}
|
|
264
|
+
</DialogContent>
|
|
265
|
+
<DialogActions>
|
|
266
|
+
<Button
|
|
267
|
+
variant="contained"
|
|
268
|
+
type="submit"
|
|
269
|
+
disabled={!enableSubmit}
|
|
270
|
+
onClick={handleSubmit}
|
|
271
|
+
>
|
|
272
|
+
Submit
|
|
273
|
+
</Button>
|
|
274
|
+
<Button variant="outlined" type="submit" onClick={handleClose}>
|
|
275
|
+
Close
|
|
276
|
+
</Button>
|
|
277
|
+
</DialogActions>
|
|
278
|
+
{errorMessage ? (
|
|
279
|
+
<DialogContent>
|
|
280
|
+
<DialogContentText color="error">{errorMessage}</DialogContentText>
|
|
281
|
+
</DialogContent>
|
|
282
|
+
) : null}
|
|
283
|
+
</Dialog>
|
|
284
|
+
)
|
|
285
|
+
}
|