@apollo-annotation/jbrowse-plugin-apollo 0.3.6 → 0.3.7
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 +2679 -850
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +2676 -847
- 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 +5194 -1258
- 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 +4 -4
- package/src/ApolloInternetAccount/addMenuItems.ts +18 -0
- package/src/ChangeManager.ts +10 -6
- package/src/FeatureDetailsWidget/Attributes.tsx +8 -3
- package/src/FeatureDetailsWidget/TranscriptSequence.tsx +12 -20
- package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +929 -175
- package/src/FeatureDetailsWidget/TranscriptWidgetSummary.tsx +4 -0
- package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +1 -1
- package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +48 -60
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +244 -51
- package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +46 -1
- package/src/LinearApolloDisplay/glyphs/Glyph.ts +9 -1
- package/src/LinearApolloDisplay/stateModel/base.ts +29 -0
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +51 -35
- package/src/LinearApolloDisplay/stateModel/rendering.ts +2 -1
- package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +7 -2
- package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +12 -20
- package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +243 -124
- package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +42 -1
- package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +19 -3
- package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +53 -34
- package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +4 -2
- package/src/OntologyManager/index.ts +4 -1
- package/src/TabularEditor/HybridGrid/Feature.tsx +4 -0
- package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +108 -16
- package/src/components/AddAssemblyAliases.tsx +114 -0
- package/src/components/AddChildFeature.tsx +3 -6
- package/src/components/AddFeature.tsx +14 -15
- package/src/components/CopyFeature.tsx +2 -4
- package/src/components/CreateApolloAnnotation.tsx +334 -151
- package/src/components/DeleteFeature.tsx +358 -11
- package/src/components/DownloadGFF3.tsx +20 -1
- package/src/components/FilterTranscripts.tsx +86 -0
- package/src/components/MergeExons.tsx +193 -0
- package/src/components/MergeTranscripts.tsx +185 -0
- package/src/components/SplitExon.tsx +134 -0
- package/src/components/index.ts +3 -0
- package/src/config.ts +5 -0
- package/src/extensions/annotationFromJBrowseFeature.ts +2 -0
- package/src/extensions/annotationFromPileup.ts +99 -89
- package/src/session/session.ts +26 -13
- package/src/util/annotationFeatureUtils.ts +65 -0
- package/src/util/copyToClipboard.ts +21 -0
- package/src/util/glyphUtils.ts +49 -0
- package/src/util/index.ts +2 -0
- package/src/util/mouseEventsUtils.ts +113 -0
|
@@ -82,6 +82,9 @@ export function layoutsModelFactory(
|
|
|
82
82
|
getGlyph(_feature: AnnotationFeature) {
|
|
83
83
|
return geneGlyph
|
|
84
84
|
},
|
|
85
|
+
featureLabelSpacer(elem: number): number {
|
|
86
|
+
return self.showFeatureLabels ? elem * 2 - 1 : elem
|
|
87
|
+
},
|
|
85
88
|
}))
|
|
86
89
|
.actions((self) => ({
|
|
87
90
|
addSeenFeature(feature: AnnotationFeature) {
|
|
@@ -91,6 +94,11 @@ export function layoutsModelFactory(
|
|
|
91
94
|
self.seenFeatures.delete(featureId)
|
|
92
95
|
},
|
|
93
96
|
}))
|
|
97
|
+
.views((self) => ({
|
|
98
|
+
get geneTrackRowNums() {
|
|
99
|
+
return [4, 5].map((elem) => self.featureLabelSpacer(elem))
|
|
100
|
+
},
|
|
101
|
+
}))
|
|
94
102
|
.views((self) => ({
|
|
95
103
|
get featureLayouts() {
|
|
96
104
|
const { assemblyManager } =
|
|
@@ -120,7 +128,10 @@ export function layoutsModelFactory(
|
|
|
120
128
|
throw new Error('featureTypeOntology is undefined')
|
|
121
129
|
}
|
|
122
130
|
if (feature.looksLikeGene) {
|
|
123
|
-
const rowNum =
|
|
131
|
+
const rowNum =
|
|
132
|
+
feature.strand == 1
|
|
133
|
+
? self.geneTrackRowNums[0]
|
|
134
|
+
: self.geneTrackRowNums[1]
|
|
124
135
|
if (!featureLayout.get(rowNum)) {
|
|
125
136
|
featureLayout.set(rowNum, [])
|
|
126
137
|
}
|
|
@@ -142,7 +153,10 @@ export function layoutsModelFactory(
|
|
|
142
153
|
if (!featureTypeOntology.isTypeOf(exon.type, 'exon')) {
|
|
143
154
|
continue
|
|
144
155
|
}
|
|
145
|
-
const rowNum =
|
|
156
|
+
const rowNum =
|
|
157
|
+
exon.strand == 1
|
|
158
|
+
? self.geneTrackRowNums[0]
|
|
159
|
+
: self.geneTrackRowNums[1]
|
|
146
160
|
const layoutRow = featureLayout.get(rowNum)
|
|
147
161
|
layoutRow?.push({ rowNum, feature: exon, cds: null })
|
|
148
162
|
}
|
|
@@ -155,7 +169,9 @@ export function layoutsModelFactory(
|
|
|
155
169
|
strand ?? 1,
|
|
156
170
|
cds.phase,
|
|
157
171
|
)
|
|
158
|
-
rowNum =
|
|
172
|
+
rowNum = self.featureLabelSpacer(
|
|
173
|
+
rowNum < 0 ? -1 * rowNum + 5 : rowNum,
|
|
174
|
+
)
|
|
159
175
|
if (!featureLayout.get(rowNum)) {
|
|
160
176
|
featureLayout.set(rowNum, [])
|
|
161
177
|
}
|
|
@@ -11,9 +11,10 @@ import { type AnyConfigurationSchemaType } from '@jbrowse/core/configuration/con
|
|
|
11
11
|
import { type MenuItem } from '@jbrowse/core/ui'
|
|
12
12
|
import { type LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
|
|
13
13
|
import { autorun } from 'mobx'
|
|
14
|
-
import { type Instance, addDisposer } from 'mobx-state-tree'
|
|
14
|
+
import { type Instance, addDisposer, cast } from 'mobx-state-tree'
|
|
15
15
|
import { type CSSProperties } from 'react'
|
|
16
16
|
|
|
17
|
+
import { type Edge, getPropagatedLocationChanges } from '../../util'
|
|
17
18
|
import { type Coord } from '../components'
|
|
18
19
|
import { type Glyph } from '../glyphs/Glyph'
|
|
19
20
|
import { type CanvasMouseEvent } from '../types'
|
|
@@ -75,7 +76,8 @@ export function mouseEventsModelIntermediateFactory(
|
|
|
75
76
|
start: MousePosition
|
|
76
77
|
current: MousePosition
|
|
77
78
|
feature: AnnotationFeature
|
|
78
|
-
edge:
|
|
79
|
+
edge: Edge
|
|
80
|
+
shrinkParent: boolean
|
|
79
81
|
} | null,
|
|
80
82
|
cursor: undefined as CSSProperties['cursor'] | undefined,
|
|
81
83
|
apolloHover: undefined as FeatureAndGlyphUnderMouse | undefined,
|
|
@@ -91,7 +93,7 @@ export function mouseEventsModelIntermediateFactory(
|
|
|
91
93
|
return mousePosition
|
|
92
94
|
}
|
|
93
95
|
let foundFeature
|
|
94
|
-
if (
|
|
96
|
+
if (self.geneTrackRowNums.includes(row)) {
|
|
95
97
|
foundFeature = layoutRow.find(
|
|
96
98
|
(f) =>
|
|
97
99
|
f.feature.type == 'exon' &&
|
|
@@ -104,9 +106,16 @@ export function mouseEventsModelIntermediateFactory(
|
|
|
104
106
|
)
|
|
105
107
|
}
|
|
106
108
|
} else {
|
|
107
|
-
foundFeature = layoutRow.find(
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
foundFeature = layoutRow.find((f) => {
|
|
110
|
+
const featureID = f.feature.attributes.get('gff_id')?.toString()
|
|
111
|
+
return (
|
|
112
|
+
f.cds != null &&
|
|
113
|
+
bp >= f.cds.min &&
|
|
114
|
+
bp <= f.cds.max &&
|
|
115
|
+
(featureID === undefined ||
|
|
116
|
+
!self.filteredTranscripts.includes(featureID))
|
|
117
|
+
)
|
|
118
|
+
})
|
|
110
119
|
}
|
|
111
120
|
if (!foundFeature) {
|
|
112
121
|
return mousePosition
|
|
@@ -143,6 +152,9 @@ export function mouseEventsModelIntermediateFactory(
|
|
|
143
152
|
self.cursor = cursor
|
|
144
153
|
}
|
|
145
154
|
},
|
|
155
|
+
updateFilteredTranscripts(forms: string[]): void {
|
|
156
|
+
self.filteredTranscripts = cast(forms)
|
|
157
|
+
},
|
|
146
158
|
}))
|
|
147
159
|
.actions(() => ({
|
|
148
160
|
// onClick(event: CanvasMouseEvent) {
|
|
@@ -176,20 +188,23 @@ export function mouseEventsModelFactory(
|
|
|
176
188
|
startDrag(
|
|
177
189
|
mousePosition: MousePositionWithFeatureAndGlyph,
|
|
178
190
|
feature: AnnotationFeature,
|
|
179
|
-
edge:
|
|
191
|
+
edge: Edge,
|
|
192
|
+
shrinkParent = false,
|
|
180
193
|
) {
|
|
181
194
|
self.apolloDragging = {
|
|
182
195
|
start: mousePosition,
|
|
183
196
|
current: mousePosition,
|
|
184
197
|
feature,
|
|
185
198
|
edge,
|
|
199
|
+
shrinkParent,
|
|
186
200
|
}
|
|
187
201
|
},
|
|
188
202
|
endDrag() {
|
|
189
203
|
if (!self.apolloDragging) {
|
|
190
204
|
throw new Error('endDrag() called with no current drag in progress')
|
|
191
205
|
}
|
|
192
|
-
const { current, edge, feature, start } =
|
|
206
|
+
const { current, edge, feature, start, shrinkParent } =
|
|
207
|
+
self.apolloDragging
|
|
193
208
|
// don't do anything if it was only dragged a tiny bit
|
|
194
209
|
if (Math.abs(current.x - start.x) <= 4) {
|
|
195
210
|
self.setDragging()
|
|
@@ -199,33 +214,35 @@ export function mouseEventsModelFactory(
|
|
|
199
214
|
const { displayedRegions } = self.lgv
|
|
200
215
|
const region = displayedRegions[start.regionNumber]
|
|
201
216
|
const assembly = self.getAssemblyId(region.assemblyName)
|
|
217
|
+
const changes = getPropagatedLocationChanges(
|
|
218
|
+
feature,
|
|
219
|
+
current.bp,
|
|
220
|
+
edge,
|
|
221
|
+
shrinkParent,
|
|
222
|
+
)
|
|
202
223
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
newStart,
|
|
226
|
-
assembly,
|
|
227
|
-
})
|
|
228
|
-
}
|
|
224
|
+
const change: LocationEndChange | LocationStartChange =
|
|
225
|
+
edge === 'max'
|
|
226
|
+
? new LocationEndChange({
|
|
227
|
+
typeName: 'LocationEndChange',
|
|
228
|
+
changedIds: changes.map((c) => c.featureId),
|
|
229
|
+
changes: changes.map((c) => ({
|
|
230
|
+
featureId: c.featureId,
|
|
231
|
+
oldEnd: c.oldLocation,
|
|
232
|
+
newEnd: c.newLocation,
|
|
233
|
+
})),
|
|
234
|
+
assembly,
|
|
235
|
+
})
|
|
236
|
+
: new LocationStartChange({
|
|
237
|
+
typeName: 'LocationStartChange',
|
|
238
|
+
changedIds: changes.map((c) => c.featureId),
|
|
239
|
+
changes: changes.map((c) => ({
|
|
240
|
+
featureId: c.featureId,
|
|
241
|
+
oldStart: c.oldLocation,
|
|
242
|
+
newStart: c.newLocation,
|
|
243
|
+
})),
|
|
244
|
+
assembly,
|
|
245
|
+
})
|
|
229
246
|
void self.changeManager.submit(change)
|
|
230
247
|
self.setDragging()
|
|
231
248
|
self.setCursor()
|
|
@@ -281,6 +298,8 @@ export function mouseEventsModelFactory(
|
|
|
281
298
|
mousePosition,
|
|
282
299
|
event,
|
|
283
300
|
)
|
|
301
|
+
} else {
|
|
302
|
+
self.setSelectedFeature()
|
|
284
303
|
}
|
|
285
304
|
|
|
286
305
|
if (self.apolloDragging) {
|
|
@@ -4,7 +4,7 @@ import { type AnyConfigurationSchemaType } from '@jbrowse/core/configuration/con
|
|
|
4
4
|
import { doesIntersect2 } from '@jbrowse/core/util'
|
|
5
5
|
import { type Theme } from '@mui/material'
|
|
6
6
|
import { autorun } from 'mobx'
|
|
7
|
-
import { type Instance, addDisposer } from 'mobx-state-tree'
|
|
7
|
+
import { type Instance, addDisposer, types } from 'mobx-state-tree'
|
|
8
8
|
|
|
9
9
|
import { type ApolloSessionModel } from '../../session'
|
|
10
10
|
|
|
@@ -29,6 +29,7 @@ export function renderingModelIntermediateFactory(
|
|
|
29
29
|
detailsHeight: 200,
|
|
30
30
|
lastRowTooltipBufferHeight: 80,
|
|
31
31
|
isShown: true,
|
|
32
|
+
filteredTranscripts: types.array(types.string),
|
|
32
33
|
})
|
|
33
34
|
.volatile(() => ({
|
|
34
35
|
canvas: null as HTMLCanvasElement | null,
|
|
@@ -38,8 +39,9 @@ export function renderingModelIntermediateFactory(
|
|
|
38
39
|
}))
|
|
39
40
|
.views((self) => ({
|
|
40
41
|
get featuresHeight() {
|
|
42
|
+
const featureLabelSpacer = self.showFeatureLabels ? 2 : 1
|
|
41
43
|
return (
|
|
42
|
-
(self.highestRow + 1) * self.apolloRowHeight +
|
|
44
|
+
featureLabelSpacer * ((self.highestRow + 1) * self.apolloRowHeight) +
|
|
43
45
|
self.lastRowTooltipBufferHeight
|
|
44
46
|
)
|
|
45
47
|
},
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
flow,
|
|
16
16
|
getRoot,
|
|
17
17
|
getSnapshot,
|
|
18
|
+
isAlive,
|
|
18
19
|
types,
|
|
19
20
|
} from 'mobx-state-tree'
|
|
20
21
|
|
|
@@ -79,7 +80,9 @@ export const OntologyRecordType = types
|
|
|
79
80
|
const equivalents: string[] = terms
|
|
80
81
|
.map((term) => term.lbl)
|
|
81
82
|
.filter((term) => term != undefined)
|
|
82
|
-
self
|
|
83
|
+
if (isAlive(self)) {
|
|
84
|
+
self.setEquivalentTypes(type, equivalents)
|
|
85
|
+
}
|
|
83
86
|
}),
|
|
84
87
|
}))
|
|
85
88
|
.actions((self) => ({
|
|
@@ -68,6 +68,8 @@ function makeContextMenuItems(
|
|
|
68
68
|
selectedFeature,
|
|
69
69
|
session,
|
|
70
70
|
setSelectedFeature,
|
|
71
|
+
filteredTranscripts,
|
|
72
|
+
updateFilteredTranscripts,
|
|
71
73
|
} = display
|
|
72
74
|
return featureContextMenuItems(
|
|
73
75
|
feature,
|
|
@@ -77,6 +79,8 @@ function makeContextMenuItems(
|
|
|
77
79
|
setSelectedFeature,
|
|
78
80
|
session,
|
|
79
81
|
changeManager,
|
|
82
|
+
filteredTranscripts,
|
|
83
|
+
updateFilteredTranscripts,
|
|
80
84
|
)
|
|
81
85
|
}
|
|
82
86
|
|
|
@@ -7,7 +7,14 @@ import {
|
|
|
7
7
|
} from '@jbrowse/core/util'
|
|
8
8
|
|
|
9
9
|
import { type ChangeManager } from '../../ChangeManager'
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
AddChildFeature,
|
|
12
|
+
CopyFeature,
|
|
13
|
+
DeleteFeature,
|
|
14
|
+
MergeExons,
|
|
15
|
+
MergeTranscripts,
|
|
16
|
+
SplitExon,
|
|
17
|
+
} from '../../components'
|
|
11
18
|
import { type ApolloSessionModel } from '../../session'
|
|
12
19
|
import { getApolloInternetAccount } from '../../util'
|
|
13
20
|
|
|
@@ -19,6 +26,8 @@ export function featureContextMenuItems(
|
|
|
19
26
|
setSelectedFeature: (f: AnnotationFeature | undefined) => void,
|
|
20
27
|
session: ApolloSessionModel,
|
|
21
28
|
changeManager: ChangeManager,
|
|
29
|
+
filteredTranscripts: string[],
|
|
30
|
+
updateFilteredTranscripts: (forms: string[]) => void,
|
|
22
31
|
) {
|
|
23
32
|
const internetAccount = getApolloInternetAccount(session)
|
|
24
33
|
const role = internetAccount ? internetAccount.role : 'admin'
|
|
@@ -26,6 +35,7 @@ export function featureContextMenuItems(
|
|
|
26
35
|
const readOnly = !(role && ['admin', 'user'].includes(role))
|
|
27
36
|
const menuItems: MenuItem[] = []
|
|
28
37
|
if (feature) {
|
|
38
|
+
const featureID = feature.attributes.get('gff_id')?.toString()
|
|
29
39
|
const sourceAssemblyId = getAssemblyId(region.assemblyName)
|
|
30
40
|
const currentAssemblyId = getAssemblyId(region.assemblyName)
|
|
31
41
|
menuItems.push(
|
|
@@ -111,6 +121,72 @@ export function featureContextMenuItems(
|
|
|
111
121
|
)
|
|
112
122
|
},
|
|
113
123
|
},
|
|
124
|
+
{
|
|
125
|
+
label: 'Merge transcripts',
|
|
126
|
+
disabled: !admin,
|
|
127
|
+
onClick: () => {
|
|
128
|
+
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
129
|
+
(doneCallback) => [
|
|
130
|
+
MergeTranscripts,
|
|
131
|
+
{
|
|
132
|
+
session,
|
|
133
|
+
handleClose: () => {
|
|
134
|
+
doneCallback()
|
|
135
|
+
},
|
|
136
|
+
changeManager,
|
|
137
|
+
sourceFeature: feature,
|
|
138
|
+
sourceAssemblyId: currentAssemblyId,
|
|
139
|
+
selectedFeature,
|
|
140
|
+
setSelectedFeature,
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
)
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
label: 'Merge exons',
|
|
148
|
+
disabled: !admin,
|
|
149
|
+
onClick: () => {
|
|
150
|
+
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
151
|
+
(doneCallback) => [
|
|
152
|
+
MergeExons,
|
|
153
|
+
{
|
|
154
|
+
session,
|
|
155
|
+
handleClose: () => {
|
|
156
|
+
doneCallback()
|
|
157
|
+
},
|
|
158
|
+
changeManager,
|
|
159
|
+
sourceFeature: feature,
|
|
160
|
+
sourceAssemblyId: currentAssemblyId,
|
|
161
|
+
selectedFeature,
|
|
162
|
+
setSelectedFeature,
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
)
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
label: 'Split exon',
|
|
170
|
+
disabled: !admin,
|
|
171
|
+
onClick: () => {
|
|
172
|
+
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
173
|
+
(doneCallback) => [
|
|
174
|
+
SplitExon,
|
|
175
|
+
{
|
|
176
|
+
session,
|
|
177
|
+
handleClose: () => {
|
|
178
|
+
doneCallback()
|
|
179
|
+
},
|
|
180
|
+
changeManager,
|
|
181
|
+
sourceFeature: feature,
|
|
182
|
+
sourceAssemblyId: currentAssemblyId,
|
|
183
|
+
selectedFeature,
|
|
184
|
+
setSelectedFeature,
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
)
|
|
188
|
+
},
|
|
189
|
+
},
|
|
114
190
|
)
|
|
115
191
|
const { featureTypeOntology } = session.apolloDataStore.ontologyManager
|
|
116
192
|
if (!featureTypeOntology) {
|
|
@@ -121,22 +197,38 @@ export function featureContextMenuItems(
|
|
|
121
197
|
featureTypeOntology.isTypeOf(feature.type, 'pseudogenic_transcript')) &&
|
|
122
198
|
isSessionModelWithWidgets(session)
|
|
123
199
|
) {
|
|
124
|
-
menuItems.push(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
200
|
+
menuItems.push(
|
|
201
|
+
{
|
|
202
|
+
label: 'Edit transcript details',
|
|
203
|
+
onClick: () => {
|
|
204
|
+
const apolloTranscriptWidget = session.addWidget(
|
|
205
|
+
'ApolloTranscriptDetails',
|
|
206
|
+
'apolloTranscriptDetails',
|
|
207
|
+
{
|
|
208
|
+
feature,
|
|
209
|
+
assembly: currentAssemblyId,
|
|
210
|
+
changeManager,
|
|
211
|
+
refName: region.refName,
|
|
212
|
+
},
|
|
213
|
+
)
|
|
214
|
+
session.showWidget(apolloTranscriptWidget)
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
label: 'Visible',
|
|
219
|
+
type: 'checkbox',
|
|
220
|
+
checked:
|
|
221
|
+
featureID && filteredTranscripts.includes(featureID) ? false : true,
|
|
222
|
+
onClick: () => {
|
|
223
|
+
if (featureID) {
|
|
224
|
+
const newForms = filteredTranscripts.includes(featureID)
|
|
225
|
+
? filteredTranscripts.filter((form) => form !== featureID)
|
|
226
|
+
: [...filteredTranscripts, featureID]
|
|
227
|
+
updateFilteredTranscripts(newForms)
|
|
228
|
+
}
|
|
229
|
+
},
|
|
138
230
|
},
|
|
139
|
-
|
|
231
|
+
)
|
|
140
232
|
}
|
|
141
233
|
}
|
|
142
234
|
return menuItems
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { AddAssemblyAliasesChange } from '@apollo-annotation/shared'
|
|
2
|
+
import { Box, DialogContent, DialogContentText } from '@mui/material'
|
|
3
|
+
import { DataGrid, type GridColDef, type GridRowModel } from '@mui/x-data-grid'
|
|
4
|
+
import React from 'react'
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
type ApolloInternetAccount,
|
|
8
|
+
type CollaborationServerDriver,
|
|
9
|
+
} from '../BackendDrivers'
|
|
10
|
+
import { type ChangeManager } from '../ChangeManager'
|
|
11
|
+
import { type ApolloSessionModel } from '../session'
|
|
12
|
+
|
|
13
|
+
import { Dialog } from './Dialog'
|
|
14
|
+
|
|
15
|
+
interface AddAssemblyAliasProps {
|
|
16
|
+
session: ApolloSessionModel
|
|
17
|
+
handleClose: () => void
|
|
18
|
+
changeManager: ChangeManager
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const columns: GridColDef[] = [
|
|
22
|
+
{
|
|
23
|
+
field: 'name',
|
|
24
|
+
headerName: 'Assembly Name',
|
|
25
|
+
width: 150,
|
|
26
|
+
editable: false,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
field: 'aliases',
|
|
30
|
+
headerName: 'Aliases',
|
|
31
|
+
width: 300,
|
|
32
|
+
editable: true,
|
|
33
|
+
},
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
interface AssemblyAlias {
|
|
37
|
+
id: string
|
|
38
|
+
name: string
|
|
39
|
+
aliases: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function AddAssemblyAliases({
|
|
43
|
+
changeManager,
|
|
44
|
+
handleClose,
|
|
45
|
+
session,
|
|
46
|
+
}: AddAssemblyAliasProps) {
|
|
47
|
+
const { apolloDataStore } = session
|
|
48
|
+
const { collaborationServerDriver } = apolloDataStore as {
|
|
49
|
+
collaborationServerDriver: CollaborationServerDriver
|
|
50
|
+
getInternetAccount(
|
|
51
|
+
assemblyName?: string,
|
|
52
|
+
internetAccountId?: string,
|
|
53
|
+
): ApolloInternetAccount
|
|
54
|
+
}
|
|
55
|
+
const assemblies = collaborationServerDriver.getAssemblies()
|
|
56
|
+
|
|
57
|
+
const rows: AssemblyAlias[] = assemblies.map((assembly) => {
|
|
58
|
+
return {
|
|
59
|
+
id: assembly.name,
|
|
60
|
+
name: assembly.displayName ?? assembly.name,
|
|
61
|
+
aliases: assembly.aliases.join(', '),
|
|
62
|
+
} as AssemblyAlias
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const [errorMessage, setErrorMessage] = React.useState('')
|
|
66
|
+
|
|
67
|
+
const processRowUpdate = (newRow: GridRowModel, _oldRow: GridRowModel) => {
|
|
68
|
+
const change = new AddAssemblyAliasesChange({
|
|
69
|
+
typeName: 'AddAssemblyAliasesChange',
|
|
70
|
+
assembly: newRow.id as string,
|
|
71
|
+
aliases: (newRow.aliases as string).split(','),
|
|
72
|
+
})
|
|
73
|
+
void changeManager.submit(change).catch(() => {
|
|
74
|
+
setErrorMessage('Error submitting change')
|
|
75
|
+
})
|
|
76
|
+
handleClose()
|
|
77
|
+
return newRow
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<Dialog
|
|
82
|
+
open
|
|
83
|
+
title="Add assembly aliases"
|
|
84
|
+
handleClose={handleClose}
|
|
85
|
+
maxWidth={'sm'}
|
|
86
|
+
data-testid="add-assembly-alias"
|
|
87
|
+
fullWidth
|
|
88
|
+
>
|
|
89
|
+
<DialogContent style={{ display: 'flex', flexDirection: 'column' }}>
|
|
90
|
+
<Box sx={{ height: 400, width: '100%' }}>
|
|
91
|
+
<DataGrid
|
|
92
|
+
rows={rows}
|
|
93
|
+
columns={columns}
|
|
94
|
+
initialState={{
|
|
95
|
+
pagination: {
|
|
96
|
+
paginationModel: {
|
|
97
|
+
pageSize: 5,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
}}
|
|
101
|
+
pageSizeOptions={[5]}
|
|
102
|
+
processRowUpdate={processRowUpdate}
|
|
103
|
+
disableRowSelectionOnClick
|
|
104
|
+
/>
|
|
105
|
+
</Box>
|
|
106
|
+
</DialogContent>
|
|
107
|
+
{errorMessage ? (
|
|
108
|
+
<DialogContent>
|
|
109
|
+
<DialogContentText color="error">{errorMessage}</DialogContentText>
|
|
110
|
+
</DialogContent>
|
|
111
|
+
) : null}
|
|
112
|
+
</Dialog>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import { type AnnotationFeature } from '@apollo-annotation/mst'
|
|
4
4
|
import { AddFeatureChange } from '@apollo-annotation/shared'
|
|
5
|
-
import { type AbstractSessionModel } from '@jbrowse/core/util'
|
|
6
5
|
import {
|
|
7
6
|
Button,
|
|
8
7
|
DialogActions,
|
|
@@ -37,7 +36,6 @@ export function AddChildFeature({
|
|
|
37
36
|
sourceAssemblyId,
|
|
38
37
|
sourceFeature,
|
|
39
38
|
}: AddChildFeatureProps) {
|
|
40
|
-
const { notify } = session as unknown as AbstractSessionModel
|
|
41
39
|
const [end, setEnd] = useState(String(sourceFeature.max))
|
|
42
40
|
const [start, setStart] = useState(String(sourceFeature.min + 1))
|
|
43
41
|
const [type, setType] = useState('')
|
|
@@ -63,7 +61,7 @@ export function AddChildFeature({
|
|
|
63
61
|
return terms
|
|
64
62
|
}
|
|
65
63
|
|
|
66
|
-
|
|
64
|
+
function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
67
65
|
event.preventDefault()
|
|
68
66
|
setErrorMessage('')
|
|
69
67
|
const change = new AddFeatureChange({
|
|
@@ -79,8 +77,7 @@ export function AddChildFeature({
|
|
|
79
77
|
},
|
|
80
78
|
parentFeatureId: sourceFeature._id,
|
|
81
79
|
})
|
|
82
|
-
|
|
83
|
-
notify('Feature added successfully', 'success')
|
|
80
|
+
void changeManager.submit(change)
|
|
84
81
|
handleClose()
|
|
85
82
|
event.preventDefault()
|
|
86
83
|
}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
import { type AnnotationFeatureSnapshot } from '@apollo-annotation/mst'
|
|
5
5
|
import { AddFeatureChange } from '@apollo-annotation/shared'
|
|
6
|
-
import {
|
|
7
|
-
type AbstractSessionModel,
|
|
8
|
-
type Region,
|
|
9
|
-
} from '@jbrowse/core/util/types'
|
|
6
|
+
import { type Region } from '@jbrowse/core/util/types'
|
|
10
7
|
import InfoIcon from '@mui/icons-material/Info'
|
|
11
8
|
import {
|
|
12
9
|
Box,
|
|
@@ -96,7 +93,6 @@ export function AddFeature({
|
|
|
96
93
|
region,
|
|
97
94
|
session,
|
|
98
95
|
}: AddFeatureProps) {
|
|
99
|
-
const { notify } = session as unknown as AbstractSessionModel
|
|
100
96
|
const [end, setEnd] = useState(String(region.end))
|
|
101
97
|
const [start, setStart] = useState(String(region.start + 1))
|
|
102
98
|
const [type, setType] = useState<NewFeature>(NewFeature.GENE_AND_SUBFEATURES)
|
|
@@ -104,7 +100,7 @@ export function AddFeature({
|
|
|
104
100
|
const [strand, setStrand] = useState<1 | -1 | undefined>()
|
|
105
101
|
const [errorMessage, setErrorMessage] = useState('')
|
|
106
102
|
|
|
107
|
-
|
|
103
|
+
function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
108
104
|
event.preventDefault()
|
|
109
105
|
setErrorMessage('')
|
|
110
106
|
|
|
@@ -120,7 +116,9 @@ export function AddFeature({
|
|
|
120
116
|
}
|
|
121
117
|
|
|
122
118
|
if (!refSeqId) {
|
|
123
|
-
setErrorMessage(
|
|
119
|
+
setErrorMessage(
|
|
120
|
+
'Invalid refseq id. Make sure you have the Apollo annotation track open',
|
|
121
|
+
)
|
|
124
122
|
return
|
|
125
123
|
}
|
|
126
124
|
|
|
@@ -149,8 +147,7 @@ export function AddFeature({
|
|
|
149
147
|
children,
|
|
150
148
|
},
|
|
151
149
|
})
|
|
152
|
-
|
|
153
|
-
notify('Feature added successfully', 'success')
|
|
150
|
+
void changeManager.submit(change)
|
|
154
151
|
handleClose()
|
|
155
152
|
return
|
|
156
153
|
}
|
|
@@ -167,8 +164,7 @@ export function AddFeature({
|
|
|
167
164
|
assembly: region.assemblyName,
|
|
168
165
|
addedFeature: mRNA,
|
|
169
166
|
})
|
|
170
|
-
|
|
171
|
-
notify('Feature added successfully', 'success')
|
|
167
|
+
void changeManager.submit(change)
|
|
172
168
|
handleClose()
|
|
173
169
|
return
|
|
174
170
|
}
|
|
@@ -191,8 +187,7 @@ export function AddFeature({
|
|
|
191
187
|
strand,
|
|
192
188
|
},
|
|
193
189
|
})
|
|
194
|
-
|
|
195
|
-
notify('Feature added successfully', 'success')
|
|
190
|
+
void changeManager.submit(change)
|
|
196
191
|
handleClose()
|
|
197
192
|
return
|
|
198
193
|
}
|
|
@@ -231,7 +226,11 @@ export function AddFeature({
|
|
|
231
226
|
}
|
|
232
227
|
|
|
233
228
|
let submitDisabled: boolean = Boolean(error) || !(start && end && type)
|
|
234
|
-
if (
|
|
229
|
+
if (
|
|
230
|
+
(type === NewFeature.CUSTOM && !customType) ||
|
|
231
|
+
(!strand && type === NewFeature.GENE_AND_SUBFEATURES) ||
|
|
232
|
+
(!strand && type === NewFeature.TRANSCRIPT_AND_SUBFEATURES)
|
|
233
|
+
) {
|
|
235
234
|
submitDisabled = true
|
|
236
235
|
}
|
|
237
236
|
|