@apollo-annotation/jbrowse-plugin-apollo 0.3.7 → 0.3.9
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 +11212 -10483
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +11251 -10509
- 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 +7726 -9014
- 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 +18 -18
- package/src/ApolloInternetAccount/model.ts +123 -70
- package/src/ApolloRefNameAliasAdapter/ApolloRefNameAliasAdapter.ts +4 -4
- package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +9 -7
- package/src/BackendDrivers/CollaborationServerDriver.ts +72 -20
- package/src/BackendDrivers/DesktopFileDriver.ts +2 -2
- package/src/ChangeManager.ts +36 -14
- package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +64 -5
- package/src/FeatureDetailsWidget/BasicInformation.tsx +6 -4
- package/src/FeatureDetailsWidget/NumberTextField.tsx +5 -2
- package/src/FeatureDetailsWidget/TranscriptSequence.tsx +70 -73
- package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +72 -234
- package/src/LinearApolloDisplay/components/CheckResultWarnings.tsx +92 -0
- package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +23 -131
- package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +50 -194
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +279 -217
- package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +53 -34
- package/src/LinearApolloDisplay/glyphs/Glyph.ts +7 -9
- package/src/LinearApolloDisplay/glyphs/util.ts +19 -0
- package/src/LinearApolloDisplay/stateModel/base.ts +34 -43
- package/src/LinearApolloDisplay/stateModel/layouts.ts +3 -2
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +32 -261
- package/src/LinearApolloDisplay/stateModel/rendering.ts +43 -343
- package/src/LinearApolloReferenceSequenceDisplay/components/LinearApolloReferenceSequenceDisplay.tsx +87 -0
- package/src/LinearApolloReferenceSequenceDisplay/components/index.ts +1 -0
- package/src/LinearApolloReferenceSequenceDisplay/configSchema.ts +7 -0
- package/src/LinearApolloReferenceSequenceDisplay/drawSequenceOverlay.ts +181 -0
- package/src/LinearApolloReferenceSequenceDisplay/drawSequenceTrack.ts +218 -0
- package/src/LinearApolloReferenceSequenceDisplay/index.ts +3 -0
- package/src/LinearApolloReferenceSequenceDisplay/stateModel/base.ts +227 -0
- package/src/LinearApolloReferenceSequenceDisplay/stateModel/index.ts +25 -0
- package/src/LinearApolloReferenceSequenceDisplay/stateModel/rendering.ts +157 -0
- package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +101 -38
- package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +334 -262
- package/src/LinearApolloSixFrameDisplay/glyphs/Glyph.ts +12 -8
- package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +42 -4
- package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +4 -8
- package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +73 -97
- package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +49 -61
- package/src/TabularEditor/HybridGrid/Feature.tsx +16 -14
- package/src/TabularEditor/HybridGrid/HybridGrid.tsx +7 -5
- package/src/components/AddAssembly.tsx +34 -38
- package/src/components/AddAssemblyAliases.tsx +1 -1
- package/src/components/AddChildFeature.tsx +5 -2
- package/src/components/AddFeature.tsx +30 -21
- package/src/components/AddRefSeqAliases.tsx +64 -50
- package/src/components/CopyFeature.tsx +4 -2
- package/src/components/CreateApolloAnnotation.tsx +22 -9
- package/src/components/DeleteAssembly.tsx +3 -10
- package/src/components/DownloadGFF3.tsx +2 -2
- package/src/components/EditZoomThresholdDialog.tsx +69 -0
- package/src/components/FilterFeatures.tsx +7 -7
- package/src/components/FilterTranscripts.tsx +6 -6
- package/src/components/ImportFeatures.tsx +1 -1
- package/src/components/ManageChecks.tsx +3 -10
- package/src/components/ManageUsers.tsx +23 -22
- package/src/components/MergeTranscripts.tsx +12 -15
- package/src/components/OntologyTermAutocomplete.tsx +1 -8
- package/src/components/OntologyTermMultiSelect.tsx +11 -11
- package/src/components/OpenLocalFile.tsx +11 -7
- package/src/components/ViewChangeLog.tsx +25 -50
- package/src/components/ViewCheckResults.tsx +2 -8
- package/src/components/index.ts +1 -0
- package/src/config.ts +6 -0
- package/src/index.ts +53 -115
- package/src/makeDisplayComponent.tsx +9 -14
- package/src/menus/index.ts +1 -0
- package/src/{ApolloInternetAccount/addMenuItems.ts → menus/topLevelMenu.ts} +56 -47
- package/src/menus/topLevelMenuAdmin.ts +154 -0
- package/src/session/ClientDataStore.ts +32 -14
- package/src/session/session.ts +159 -121
- package/src/util/annotationFeatureUtils.ts +15 -21
- package/src/util/displayUtils.ts +149 -0
- package/src/util/glyphUtils.ts +329 -0
- package/src/util/loadAssemblyIntoClient.ts +3 -2
- package/src/util/mouseEventsUtils.ts +32 -0
|
@@ -10,6 +10,7 @@ import { makeStyles } from 'tss-react/mui'
|
|
|
10
10
|
import { isOntologyClass } from '../../OntologyManager'
|
|
11
11
|
import type OntologyStore from '../../OntologyManager/OntologyStore'
|
|
12
12
|
import { OntologyTermAutocomplete } from '../../components/OntologyTermAutocomplete'
|
|
13
|
+
import { navToFeatureCenter } from '../../util'
|
|
13
14
|
import { type DisplayStateModel } from '../types'
|
|
14
15
|
|
|
15
16
|
import {
|
|
@@ -84,12 +85,13 @@ function makeContextMenuItems(
|
|
|
84
85
|
)
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
function
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
88
|
+
function navigateHere(
|
|
89
|
+
displayState: DisplayStateModel,
|
|
90
|
+
feature: AnnotationFeature,
|
|
91
|
+
) {
|
|
92
|
+
displayState.lgv.navTo(
|
|
93
|
+
navToFeatureCenter(feature, 0.1, displayState.lgv.totalBp),
|
|
94
|
+
)
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
export const Feature = observer(function Feature({
|
|
@@ -111,8 +113,8 @@ export const Feature = observer(function Feature({
|
|
|
111
113
|
}) {
|
|
112
114
|
const { classes } = useStyles()
|
|
113
115
|
const {
|
|
114
|
-
apolloHover,
|
|
115
116
|
changeManager,
|
|
117
|
+
hoveredFeature,
|
|
116
118
|
selectedFeature,
|
|
117
119
|
session,
|
|
118
120
|
tabularEditor: tabularEditorState,
|
|
@@ -134,12 +136,7 @@ export const Feature = observer(function Feature({
|
|
|
134
136
|
<>
|
|
135
137
|
<tr
|
|
136
138
|
onMouseEnter={(_e) => {
|
|
137
|
-
displayState.
|
|
138
|
-
feature,
|
|
139
|
-
topLevelFeature: getTopLevelFeature(feature),
|
|
140
|
-
// @ts-expect-error TODO fix in future when moving hover logic to session.
|
|
141
|
-
glyph: displayState.getGlyph(getTopLevelFeature(feature)),
|
|
142
|
-
})
|
|
139
|
+
displayState.setHoveredFeature({ feature, bp: min })
|
|
143
140
|
}}
|
|
144
141
|
className={
|
|
145
142
|
classes.feature +
|
|
@@ -153,6 +150,10 @@ export const Feature = observer(function Feature({
|
|
|
153
150
|
e.stopPropagation()
|
|
154
151
|
displayState.setSelectedFeature(feature)
|
|
155
152
|
}}
|
|
153
|
+
onDoubleClick={() => {
|
|
154
|
+
displayState.setSelectedFeature(feature)
|
|
155
|
+
navigateHere(displayState, feature)
|
|
156
|
+
}}
|
|
156
157
|
onContextMenu={(e) => {
|
|
157
158
|
e.preventDefault()
|
|
158
159
|
setContextMenu({
|
|
@@ -258,7 +259,8 @@ export const Feature = observer(function Feature({
|
|
|
258
259
|
return text.includes(filterText)
|
|
259
260
|
})
|
|
260
261
|
.map(([featureId, childFeature]) => {
|
|
261
|
-
const childHovered =
|
|
262
|
+
const childHovered =
|
|
263
|
+
hoveredFeature?.feature._id === childFeature._id
|
|
262
264
|
const childSelected = selectedFeature?._id === childFeature._id
|
|
263
265
|
return (
|
|
264
266
|
<Feature
|
|
@@ -37,7 +37,7 @@ const HybridGrid = observer(function HybridGrid({
|
|
|
37
37
|
}: {
|
|
38
38
|
model: DisplayStateModel
|
|
39
39
|
}) {
|
|
40
|
-
const {
|
|
40
|
+
const { hoveredFeature, seenFeatures, selectedFeature, tabularEditor } = model
|
|
41
41
|
const theme = useTheme()
|
|
42
42
|
const { classes } = useStyles()
|
|
43
43
|
const scrollContainerRef = useRef<HTMLDivElement>(null)
|
|
@@ -96,7 +96,7 @@ const HybridGrid = observer(function HybridGrid({
|
|
|
96
96
|
})
|
|
97
97
|
.map(([featureId, feature]) => {
|
|
98
98
|
const isSelected = selectedFeature?._id === featureId
|
|
99
|
-
const isHovered =
|
|
99
|
+
const isHovered = hoveredFeature?.feature._id === featureId
|
|
100
100
|
return (
|
|
101
101
|
<Feature
|
|
102
102
|
key={featureId}
|
|
@@ -121,9 +121,11 @@ const HybridGrid = observer(function HybridGrid({
|
|
|
121
121
|
onClose={() => {
|
|
122
122
|
setContextMenu(null)
|
|
123
123
|
}}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
slotProps={{
|
|
125
|
+
transition: {
|
|
126
|
+
onExit: () => {
|
|
127
|
+
setContextMenu(null)
|
|
128
|
+
},
|
|
127
129
|
},
|
|
128
130
|
}}
|
|
129
131
|
style={{ zIndex: theme.zIndex.tooltip }}
|
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
} from '@mui/material'
|
|
40
40
|
import ObjectID from 'bson-objectid'
|
|
41
41
|
import { getRoot } from 'mobx-state-tree'
|
|
42
|
-
import React, {
|
|
42
|
+
import React, { useState } from 'react'
|
|
43
43
|
import { makeStyles } from 'tss-react/mui'
|
|
44
44
|
|
|
45
45
|
import { type ApolloInternetAccountModel } from '../ApolloInternetAccount/model'
|
|
@@ -80,7 +80,7 @@ const useStyles = makeStyles()((theme) => ({
|
|
|
80
80
|
borderTop: '1px solid rgba(0, 0, 0, .125)',
|
|
81
81
|
},
|
|
82
82
|
radioIcon: {
|
|
83
|
-
color: theme
|
|
83
|
+
color: theme.palette.tertiary.contrastText,
|
|
84
84
|
},
|
|
85
85
|
dialog: {
|
|
86
86
|
// minHeight: 500,
|
|
@@ -149,25 +149,8 @@ export function AddAssembly({
|
|
|
149
149
|
const [fastaGziIndexUrl, setFastaGziIndexUrl] = useState<string>('')
|
|
150
150
|
|
|
151
151
|
const [loading, setLoading] = useState(false)
|
|
152
|
-
const [
|
|
153
|
-
|
|
154
|
-
useEffect(() => {
|
|
155
|
-
setFastaIndexUrl(fastaUrl ? `${fastaUrl}.fai` : '')
|
|
156
|
-
}, [fastaUrl])
|
|
157
|
-
|
|
158
|
-
useEffect(() => {
|
|
159
|
-
setFastaGziIndexUrl(fastaUrl ? `${fastaUrl}.gzi` : '')
|
|
160
|
-
}, [fastaUrl])
|
|
161
|
-
|
|
162
|
-
useEffect(() => {
|
|
163
|
-
if (sequenceIsEditable || fileType === FileType.GFF3) {
|
|
164
|
-
setIsGzip(
|
|
165
|
-
fastaFile?.name.toLocaleLowerCase().endsWith('.gz') ? true : false,
|
|
166
|
-
)
|
|
167
|
-
} else {
|
|
168
|
-
setIsGzip(true)
|
|
169
|
-
}
|
|
170
|
-
}, [fastaFile, sequenceIsEditable, fileType])
|
|
152
|
+
const [fastaGzipChecked, setFastaGzipChecked] = useState<boolean>(false)
|
|
153
|
+
const [gff3GzipChecked, setGff3GzipChecked] = useState<boolean>(false)
|
|
171
154
|
|
|
172
155
|
function checkAssemblyName(assembly: string) {
|
|
173
156
|
const { assemblies } = session as unknown as AbstractSessionModel
|
|
@@ -194,10 +177,13 @@ export function AddAssembly({
|
|
|
194
177
|
const uri = url.href
|
|
195
178
|
const formData = new FormData()
|
|
196
179
|
let filename = file.name
|
|
180
|
+
const isGzip =
|
|
181
|
+
fileType === FileType.BGZIP_FASTA ||
|
|
182
|
+
(fileType === FileType.FASTA &&
|
|
183
|
+
(!sequenceIsEditable || fastaGzipChecked)) ||
|
|
184
|
+
(fileType === FileType.GFF3 && gff3GzipChecked)
|
|
197
185
|
|
|
198
|
-
if (
|
|
199
|
-
filename = `${filename}.txt`
|
|
200
|
-
} else if (isGzip && !file.name.toLocaleLowerCase().endsWith('.gz')) {
|
|
186
|
+
if (isGzip && !file.name.toLocaleLowerCase().endsWith('.gz')) {
|
|
201
187
|
filename = `${filename}.gz`
|
|
202
188
|
} else if (!isGzip && file.name.toLocaleLowerCase().endsWith('.gz')) {
|
|
203
189
|
filename = `${filename}.txt`
|
|
@@ -370,11 +356,6 @@ export function AddAssembly({
|
|
|
370
356
|
if (newExpanded) {
|
|
371
357
|
setExpanded(panel)
|
|
372
358
|
}
|
|
373
|
-
if (panel === 'panelGffInput') {
|
|
374
|
-
setIsGzip(false)
|
|
375
|
-
} else {
|
|
376
|
-
setIsGzip(true)
|
|
377
|
-
}
|
|
378
359
|
}
|
|
379
360
|
|
|
380
361
|
return (
|
|
@@ -497,12 +478,12 @@ export function AddAssembly({
|
|
|
497
478
|
data-testid="fasta-is-gzip-checkbox"
|
|
498
479
|
control={
|
|
499
480
|
<Checkbox
|
|
500
|
-
checked={
|
|
481
|
+
checked={!sequenceIsEditable || fastaGzipChecked}
|
|
501
482
|
onChange={() => {
|
|
502
483
|
if (sequenceIsEditable) {
|
|
503
|
-
|
|
484
|
+
setFastaGzipChecked(!fastaGzipChecked)
|
|
504
485
|
} else {
|
|
505
|
-
|
|
486
|
+
setFastaGzipChecked(true)
|
|
506
487
|
}
|
|
507
488
|
}}
|
|
508
489
|
disabled={!sequenceIsEditable}
|
|
@@ -533,7 +514,13 @@ export function AddAssembly({
|
|
|
533
514
|
onChange={(
|
|
534
515
|
e: React.ChangeEvent<HTMLInputElement>,
|
|
535
516
|
) => {
|
|
536
|
-
|
|
517
|
+
const file = e.target.files?.item(0)
|
|
518
|
+
if (file) {
|
|
519
|
+
setFastaFile(file)
|
|
520
|
+
if (file.name.endsWith('.gz')) {
|
|
521
|
+
setFastaGzipChecked(true)
|
|
522
|
+
}
|
|
523
|
+
}
|
|
537
524
|
}}
|
|
538
525
|
disabled={submitted && !errorMessage}
|
|
539
526
|
/>
|
|
@@ -606,7 +593,10 @@ export function AddAssembly({
|
|
|
606
593
|
onChange={(
|
|
607
594
|
e: React.ChangeEvent<HTMLInputElement>,
|
|
608
595
|
) => {
|
|
609
|
-
|
|
596
|
+
const { value } = e.target
|
|
597
|
+
setFastaUrl(value)
|
|
598
|
+
setFastaIndexUrl(value ? `${value}.fai` : '')
|
|
599
|
+
setFastaGziIndexUrl(value ? `${value}.gzi` : '')
|
|
610
600
|
}}
|
|
611
601
|
disabled={submitted && !errorMessage}
|
|
612
602
|
slotProps={{
|
|
@@ -727,8 +717,14 @@ export function AddAssembly({
|
|
|
727
717
|
type="file"
|
|
728
718
|
disabled={submitted && !errorMessage}
|
|
729
719
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
730
|
-
|
|
731
|
-
|
|
720
|
+
const file = e.target.files?.item(0)
|
|
721
|
+
if (file) {
|
|
722
|
+
setFastaFile(file)
|
|
723
|
+
setFileType(FileType.GFF3)
|
|
724
|
+
if (file.name.endsWith('.gz')) {
|
|
725
|
+
setGff3GzipChecked(true)
|
|
726
|
+
}
|
|
727
|
+
}
|
|
732
728
|
}}
|
|
733
729
|
/>
|
|
734
730
|
<FormGroup style={{ display: 'grid' }}>
|
|
@@ -748,9 +744,9 @@ export function AddAssembly({
|
|
|
748
744
|
data-testid="gff3-is-gzip-checkbox"
|
|
749
745
|
control={
|
|
750
746
|
<Checkbox
|
|
751
|
-
checked={
|
|
747
|
+
checked={gff3GzipChecked}
|
|
752
748
|
onChange={() => {
|
|
753
|
-
|
|
749
|
+
setGff3GzipChecked(!gff3GzipChecked)
|
|
754
750
|
}}
|
|
755
751
|
disabled={submitted && !errorMessage}
|
|
756
752
|
/>
|
|
@@ -57,7 +57,7 @@ export function AddAssemblyAliases({
|
|
|
57
57
|
const rows: AssemblyAlias[] = assemblies.map((assembly) => {
|
|
58
58
|
return {
|
|
59
59
|
id: assembly.name,
|
|
60
|
-
name: assembly.displayName
|
|
60
|
+
name: assembly.displayName,
|
|
61
61
|
aliases: assembly.aliases.join(', '),
|
|
62
62
|
} as AssemblyAlias
|
|
63
63
|
})
|
|
@@ -64,12 +64,13 @@ export function AddChildFeature({
|
|
|
64
64
|
function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
65
65
|
event.preventDefault()
|
|
66
66
|
setErrorMessage('')
|
|
67
|
+
const _id = new ObjectID().toHexString()
|
|
67
68
|
const change = new AddFeatureChange({
|
|
68
69
|
changedIds: [sourceFeature._id],
|
|
69
70
|
typeName: 'AddFeatureChange',
|
|
70
71
|
assembly: sourceAssemblyId,
|
|
71
72
|
addedFeature: {
|
|
72
|
-
_id
|
|
73
|
+
_id,
|
|
73
74
|
refSeq: sourceFeature.refSeq,
|
|
74
75
|
min: Number(start) - 1,
|
|
75
76
|
max: Number(end),
|
|
@@ -77,7 +78,9 @@ export function AddChildFeature({
|
|
|
77
78
|
},
|
|
78
79
|
parentFeatureId: sourceFeature._id,
|
|
79
80
|
})
|
|
80
|
-
void changeManager.submit(change)
|
|
81
|
+
void changeManager.submit(change).then(() => {
|
|
82
|
+
session.apolloSetSelectedFeature(_id)
|
|
83
|
+
})
|
|
81
84
|
handleClose()
|
|
82
85
|
event.preventDefault()
|
|
83
86
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
2
|
-
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
3
2
|
|
|
4
3
|
import { type AnnotationFeatureSnapshot } from '@apollo-annotation/mst'
|
|
5
4
|
import { AddFeatureChange } from '@apollo-annotation/shared'
|
|
@@ -26,6 +25,7 @@ import {
|
|
|
26
25
|
import ObjectID from 'bson-objectid'
|
|
27
26
|
import React, { useState } from 'react'
|
|
28
27
|
|
|
28
|
+
import { CollaborationServerDriver } from '../BackendDrivers'
|
|
29
29
|
import { type ChangeManager } from '../ChangeManager'
|
|
30
30
|
import { isOntologyClass } from '../OntologyManager'
|
|
31
31
|
import { type ApolloSessionModel } from '../session'
|
|
@@ -96,30 +96,32 @@ export function AddFeature({
|
|
|
96
96
|
const [end, setEnd] = useState(String(region.end))
|
|
97
97
|
const [start, setStart] = useState(String(region.start + 1))
|
|
98
98
|
const [type, setType] = useState<NewFeature>(NewFeature.GENE_AND_SUBFEATURES)
|
|
99
|
-
const [customType, setCustomType] = useState<string>()
|
|
99
|
+
const [customType, setCustomType] = useState<string>('')
|
|
100
100
|
const [strand, setStrand] = useState<1 | -1 | undefined>()
|
|
101
101
|
const [errorMessage, setErrorMessage] = useState('')
|
|
102
102
|
|
|
103
|
-
function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
103
|
+
async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
104
104
|
event.preventDefault()
|
|
105
105
|
setErrorMessage('')
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
107
|
+
const backendDriver = session.apolloDataStore.getBackendDriver(
|
|
108
|
+
region.assemblyName,
|
|
109
|
+
)
|
|
110
|
+
if (!backendDriver) {
|
|
111
|
+
setErrorMessage('No backend driver found')
|
|
112
|
+
return
|
|
116
113
|
}
|
|
117
|
-
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
-
|
|
114
|
+
let refSeqId = region.refName
|
|
115
|
+
if (backendDriver instanceof CollaborationServerDriver) {
|
|
116
|
+
const backendRefSeqId = await backendDriver.getRefSeqId(
|
|
117
|
+
region.assemblyName,
|
|
118
|
+
region.refName,
|
|
121
119
|
)
|
|
122
|
-
|
|
120
|
+
if (!backendRefSeqId) {
|
|
121
|
+
setErrorMessage(`Could not find refSeq for "${region.refName}"`)
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
refSeqId = backendRefSeqId
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
if (type === NewFeature.GENE_AND_SUBFEATURES) {
|
|
@@ -147,7 +149,9 @@ export function AddFeature({
|
|
|
147
149
|
children,
|
|
148
150
|
},
|
|
149
151
|
})
|
|
150
|
-
void changeManager.submit(change)
|
|
152
|
+
void changeManager.submit(change).then(() => {
|
|
153
|
+
session.apolloSetSelectedFeature(id)
|
|
154
|
+
})
|
|
151
155
|
handleClose()
|
|
152
156
|
return
|
|
153
157
|
}
|
|
@@ -164,7 +168,9 @@ export function AddFeature({
|
|
|
164
168
|
assembly: region.assemblyName,
|
|
165
169
|
addedFeature: mRNA,
|
|
166
170
|
})
|
|
167
|
-
void changeManager.submit(change)
|
|
171
|
+
void changeManager.submit(change).then(() => {
|
|
172
|
+
session.apolloSetSelectedFeature(mRNA._id)
|
|
173
|
+
})
|
|
168
174
|
handleClose()
|
|
169
175
|
return
|
|
170
176
|
}
|
|
@@ -187,7 +193,9 @@ export function AddFeature({
|
|
|
187
193
|
strand,
|
|
188
194
|
},
|
|
189
195
|
})
|
|
190
|
-
void changeManager.submit(change)
|
|
196
|
+
void changeManager.submit(change).then(() => {
|
|
197
|
+
session.apolloSetSelectedFeature(id)
|
|
198
|
+
})
|
|
191
199
|
handleClose()
|
|
192
200
|
return
|
|
193
201
|
}
|
|
@@ -242,6 +250,7 @@ export function AddFeature({
|
|
|
242
250
|
maxWidth={false}
|
|
243
251
|
data-testid="add-feature-dialog"
|
|
244
252
|
>
|
|
253
|
+
{/* eslint-disable-next-line @typescript-eslint/no-misused-promises */}
|
|
245
254
|
<form onSubmit={onSubmit} data-testid="submit-form">
|
|
246
255
|
<DialogContent style={{ display: 'flex', flexDirection: 'column' }}>
|
|
247
256
|
<TextField
|
|
@@ -338,7 +347,7 @@ export function AddFeature({
|
|
|
338
347
|
session={session}
|
|
339
348
|
ontologyName="Sequence Ontology"
|
|
340
349
|
style={{ width: 170 }}
|
|
341
|
-
value=
|
|
350
|
+
value={customType}
|
|
342
351
|
filterTerms={isOntologyClass}
|
|
343
352
|
renderInput={(params) => (
|
|
344
353
|
<TextField
|
|
@@ -10,18 +10,25 @@ import {
|
|
|
10
10
|
DialogContent,
|
|
11
11
|
DialogContentText,
|
|
12
12
|
FormControl,
|
|
13
|
-
|
|
13
|
+
Grid,
|
|
14
14
|
InputLabel,
|
|
15
15
|
MenuItem,
|
|
16
16
|
Select,
|
|
17
17
|
type SelectChangeEvent,
|
|
18
18
|
} from '@mui/material'
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
DataGrid,
|
|
21
|
+
type GridColDef,
|
|
22
|
+
type GridRowModel,
|
|
23
|
+
type GridRowSelectionModel,
|
|
24
|
+
} from '@mui/x-data-grid'
|
|
25
|
+
import { observer } from 'mobx-react'
|
|
20
26
|
import React, { useEffect, useRef, useState } from 'react'
|
|
21
27
|
|
|
22
28
|
import {
|
|
23
29
|
type ApolloInternetAccount,
|
|
24
30
|
type CollaborationServerDriver,
|
|
31
|
+
type RefNameAliases,
|
|
25
32
|
} from '../BackendDrivers'
|
|
26
33
|
import { type ChangeManager } from '../ChangeManager'
|
|
27
34
|
import { type ApolloSessionModel } from '../session'
|
|
@@ -30,7 +37,7 @@ import { Dialog } from './Dialog'
|
|
|
30
37
|
|
|
31
38
|
const columns: GridColDef[] = [
|
|
32
39
|
{ field: 'refName', headerName: 'Ref Name' },
|
|
33
|
-
{ field: 'aliases', headerName: 'Aliases', editable: true },
|
|
40
|
+
{ field: 'aliases', headerName: 'Aliases', editable: true, flex: 1 },
|
|
34
41
|
]
|
|
35
42
|
|
|
36
43
|
interface AddChildFeatureProps {
|
|
@@ -44,7 +51,7 @@ const isGeneratedObjectId = (key: string): boolean => {
|
|
|
44
51
|
return pattern.test(key)
|
|
45
52
|
}
|
|
46
53
|
|
|
47
|
-
export function AddRefSeqAliases({
|
|
54
|
+
export const AddRefSeqAliases = observer(function AddRefSeqAliases({
|
|
48
55
|
changeManager,
|
|
49
56
|
handleClose,
|
|
50
57
|
session,
|
|
@@ -75,44 +82,50 @@ export function AddRefSeqAliases({
|
|
|
75
82
|
const assemblies = collaborationServerDriver.getAssemblies()
|
|
76
83
|
|
|
77
84
|
useEffect(() => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (!selectedAssembly.refNames) {
|
|
90
|
-
return
|
|
91
|
-
}
|
|
92
|
-
const refNameAliasess = selectedAssembly.refNameAliases
|
|
93
|
-
for (const key in refNameAliasess) {
|
|
94
|
-
const value = refNameAliasess[key]
|
|
95
|
-
if (!value || isGeneratedObjectId(key)) {
|
|
96
|
-
continue
|
|
97
|
-
}
|
|
98
|
-
if (initialMap.has(value)) {
|
|
99
|
-
const aliases = initialMap.get(value) ?? []
|
|
100
|
-
initialMap.set(value, [...aliases, key])
|
|
101
|
-
} else {
|
|
102
|
-
initialMap.set(value, [key])
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
setRefNameAliasMap(initialMap)
|
|
85
|
+
if (assemblies.length > 0) {
|
|
86
|
+
setSelectedAssembly(assemblies[0])
|
|
87
|
+
collaborationServerDriver
|
|
88
|
+
.getRefNameAliases(assemblies[0].name)
|
|
89
|
+
.then((refNameAliases) => {
|
|
90
|
+
initializeRefNameAliasMap(refNameAliases)
|
|
91
|
+
})
|
|
92
|
+
.catch(() => {
|
|
93
|
+
setRefNameAliasMap(new Map())
|
|
94
|
+
setErrorMessage('Error fetching refName aliases for assembly')
|
|
95
|
+
})
|
|
106
96
|
}
|
|
97
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
98
|
+
}, [])
|
|
107
99
|
|
|
108
|
-
|
|
109
|
-
|
|
100
|
+
const initializeRefNameAliasMap = (refNameAliasesList: RefNameAliases[]) => {
|
|
101
|
+
const initialMap = new Map<string, string[]>()
|
|
102
|
+
for (const refNameAliases of refNameAliasesList) {
|
|
103
|
+
const key = refNameAliases.refName
|
|
104
|
+
if (isGeneratedObjectId(key)) {
|
|
105
|
+
continue
|
|
106
|
+
}
|
|
107
|
+
initialMap.set(key, refNameAliases.aliases)
|
|
108
|
+
}
|
|
109
|
+
setRefNameAliasMap(initialMap)
|
|
110
|
+
}
|
|
110
111
|
|
|
111
112
|
const handleChangeAssembly = (e: SelectChangeEvent) => {
|
|
112
113
|
const newAssembly = assemblies.find((asm) => asm.name === e.target.value)
|
|
113
114
|
setSelectedAssembly(newAssembly)
|
|
115
|
+
if (!newAssembly?.name) {
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
collaborationServerDriver
|
|
119
|
+
.getRefNameAliases(newAssembly.name)
|
|
120
|
+
.then((refNameAliases) => {
|
|
121
|
+
initializeRefNameAliasMap(refNameAliases)
|
|
122
|
+
setErrorMessage('')
|
|
123
|
+
})
|
|
124
|
+
.catch(() => {
|
|
125
|
+
setRefNameAliasMap(new Map())
|
|
126
|
+
setErrorMessage('Error fetching refName aliases for assembly')
|
|
127
|
+
})
|
|
114
128
|
setEnableSubmit(false)
|
|
115
|
-
setErrorMessage('')
|
|
116
129
|
if (fileRef.current) {
|
|
117
130
|
fileRef.current.value = ''
|
|
118
131
|
}
|
|
@@ -145,11 +158,12 @@ export function AddRefSeqAliases({
|
|
|
145
158
|
})
|
|
146
159
|
}
|
|
147
160
|
|
|
148
|
-
const rowSelectionChange = (
|
|
149
|
-
|
|
161
|
+
const rowSelectionChange = (gridRowSelectionModel: GridRowSelectionModel) => {
|
|
162
|
+
const { ids } = gridRowSelectionModel
|
|
163
|
+
if (ids.size > 0) {
|
|
150
164
|
setEnableSubmit(true)
|
|
151
|
-
const selectedRows = ids.flatMap((id) =>
|
|
152
|
-
getTableRows().filter((row) => row.id === id),
|
|
165
|
+
const selectedRows = [...ids.values()].flatMap((id) =>
|
|
166
|
+
getTableRows().filter((row) => String(row.id) === String(id)),
|
|
153
167
|
)
|
|
154
168
|
setSelectedRows(selectedRows)
|
|
155
169
|
} else {
|
|
@@ -212,8 +226,8 @@ export function AddRefSeqAliases({
|
|
|
212
226
|
fullWidth
|
|
213
227
|
>
|
|
214
228
|
<DialogContent style={{ display: 'flex', flexDirection: 'column' }}>
|
|
215
|
-
<
|
|
216
|
-
<
|
|
229
|
+
<Grid container spacing={2}>
|
|
230
|
+
<Grid>
|
|
217
231
|
<FormControl disabled={enableSubmit && !errorMessage} fullWidth>
|
|
218
232
|
<InputLabel id="demo-simple-select-label">Assembly</InputLabel>
|
|
219
233
|
<Select
|
|
@@ -222,16 +236,17 @@ export function AddRefSeqAliases({
|
|
|
222
236
|
label="Assembly"
|
|
223
237
|
value={selectedAssembly?.name ?? ''}
|
|
224
238
|
onChange={handleChangeAssembly}
|
|
239
|
+
style={{ minWidth: 150 }}
|
|
225
240
|
>
|
|
226
241
|
{assemblies.map((option) => (
|
|
227
242
|
<MenuItem key={option.name} value={option.name}>
|
|
228
|
-
{option.displayName
|
|
243
|
+
{option.displayName}
|
|
229
244
|
</MenuItem>
|
|
230
245
|
))}
|
|
231
246
|
</Select>
|
|
232
247
|
</FormControl>
|
|
233
|
-
</
|
|
234
|
-
<
|
|
248
|
+
</Grid>
|
|
249
|
+
<Grid>
|
|
235
250
|
<InputLabel>Load RefName alias</InputLabel>
|
|
236
251
|
<input
|
|
237
252
|
type="file"
|
|
@@ -239,8 +254,8 @@ export function AddRefSeqAliases({
|
|
|
239
254
|
ref={fileRef}
|
|
240
255
|
disabled={(enableSubmit && !errorMessage) || !selectedAssembly}
|
|
241
256
|
/>
|
|
242
|
-
</
|
|
243
|
-
</
|
|
257
|
+
</Grid>
|
|
258
|
+
</Grid>
|
|
244
259
|
{selectedAssembly && refNameAliasMap.size > 0 ? (
|
|
245
260
|
<div style={{ height: 200, width: '100%', marginTop: 20 }}>
|
|
246
261
|
<InputLabel>
|
|
@@ -255,11 +270,10 @@ export function AddRefSeqAliases({
|
|
|
255
270
|
},
|
|
256
271
|
}}
|
|
257
272
|
pageSizeOptions={[5, 10]}
|
|
258
|
-
onRowSelectionModelChange={
|
|
259
|
-
rowSelectionChange(ids as number[])
|
|
260
|
-
}}
|
|
273
|
+
onRowSelectionModelChange={rowSelectionChange}
|
|
261
274
|
processRowUpdate={processRowUpdate}
|
|
262
275
|
checkboxSelection
|
|
276
|
+
disableRowSelectionExcludeModel
|
|
263
277
|
></DataGrid>
|
|
264
278
|
</div>
|
|
265
279
|
) : null}
|
|
@@ -284,4 +298,4 @@ export function AddRefSeqAliases({
|
|
|
284
298
|
) : null}
|
|
285
299
|
</Dialog>
|
|
286
300
|
)
|
|
287
|
-
}
|
|
301
|
+
})
|
|
@@ -99,8 +99,8 @@ export function CopyFeature({
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
useEffect(() => {
|
|
102
|
-
setSelectedRefSeqId('')
|
|
103
102
|
async function getRefNames() {
|
|
103
|
+
setSelectedRefSeqId('')
|
|
104
104
|
if (!selectedAssemblyId) {
|
|
105
105
|
setErrorMessage('No assemblies to copy to')
|
|
106
106
|
return
|
|
@@ -203,7 +203,9 @@ export function CopyFeature({
|
|
|
203
203
|
copyFeature: true,
|
|
204
204
|
allIds: featureIds,
|
|
205
205
|
})
|
|
206
|
-
void changeManager.submit(change)
|
|
206
|
+
void changeManager.submit(change).then(() => {
|
|
207
|
+
session.apolloSetSelectedFeature(newFeatureLine._id)
|
|
208
|
+
})
|
|
207
209
|
handleClose()
|
|
208
210
|
event.preventDefault()
|
|
209
211
|
}
|