@apollo-annotation/jbrowse-plugin-apollo 0.3.5 → 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 +6964 -4598
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +6610 -4261
- 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 +11563 -7493
- 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 +23 -2
- package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +1 -0
- package/src/ApolloInternetAccount/components/LoginButtons.tsx +1 -1
- package/src/ApolloInternetAccount/components/LoginIcons.tsx +1 -1
- package/src/ApolloInternetAccount/configSchema.ts +1 -1
- package/src/ApolloInternetAccount/model.ts +11 -10
- package/src/ApolloJobModel.ts +1 -1
- package/src/ApolloRefNameAliasAdapter/ApolloRefNameAliasAdapter.ts +8 -6
- package/src/ApolloRefNameAliasAdapter/index.ts +2 -2
- package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +4 -4
- package/src/ApolloSequenceAdapter/index.ts +1 -1
- package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +8 -7
- package/src/ApolloTextSearchAdapter/index.ts +1 -1
- package/src/BackendDrivers/BackendDriver.ts +7 -7
- package/src/BackendDrivers/CollaborationServerDriver.ts +14 -10
- package/src/BackendDrivers/DesktopFileDriver.ts +11 -10
- package/src/BackendDrivers/InMemoryFileDriver.ts +10 -6
- package/src/ChangeManager.ts +15 -11
- package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +8 -7
- package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +35 -14
- package/src/FeatureDetailsWidget/AttributeKey.tsx +50 -0
- package/src/FeatureDetailsWidget/AttributeKeySelector.tsx +104 -0
- package/src/FeatureDetailsWidget/Attributes.tsx +215 -367
- package/src/FeatureDetailsWidget/BasicInformation.tsx +6 -5
- package/src/FeatureDetailsWidget/DefaultAttributeEditor.tsx +104 -0
- package/src/FeatureDetailsWidget/DefaultAttributeViewer.tsx +22 -0
- package/src/FeatureDetailsWidget/FeatureDetailsNavigation.tsx +4 -4
- package/src/FeatureDetailsWidget/NumberTextField.tsx +1 -1
- package/src/FeatureDetailsWidget/Sequence.tsx +2 -2
- package/src/FeatureDetailsWidget/StringTextField.tsx +1 -1
- package/src/FeatureDetailsWidget/TranscriptSequence.tsx +15 -23
- package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +950 -196
- package/src/FeatureDetailsWidget/TranscriptWidgetSummary.tsx +8 -4
- package/src/FeatureDetailsWidget/model.ts +8 -3
- package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +7 -7
- package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +59 -72
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +253 -60
- package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +52 -6
- package/src/LinearApolloDisplay/glyphs/Glyph.ts +16 -8
- package/src/LinearApolloDisplay/stateModel/base.ts +81 -10
- package/src/LinearApolloDisplay/stateModel/index.ts +4 -3
- package/src/LinearApolloDisplay/stateModel/layouts.ts +8 -39
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +63 -46
- package/src/LinearApolloDisplay/stateModel/rendering.ts +60 -31
- package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +226 -0
- package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +32 -0
- package/src/LinearApolloSixFrameDisplay/components/index.ts +2 -0
- package/src/LinearApolloSixFrameDisplay/configSchema.ts +7 -0
- package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +940 -0
- package/src/LinearApolloSixFrameDisplay/glyphs/Glyph.ts +63 -0
- package/src/LinearApolloSixFrameDisplay/glyphs/index.ts +1 -0
- package/src/LinearApolloSixFrameDisplay/index.ts +2 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +302 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/index.ts +27 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +252 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +368 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +201 -0
- package/src/LinearApolloSixFrameDisplay/types.ts +1 -0
- package/src/OntologyManager/OntologyStore/fulltext.test.ts +1 -1
- package/src/OntologyManager/OntologyStore/fulltext.ts +8 -3
- package/src/OntologyManager/OntologyStore/index.test.ts +3 -1
- package/src/OntologyManager/OntologyStore/index.ts +19 -14
- package/src/OntologyManager/OntologyStore/indexeddb-schema.ts +6 -5
- package/src/OntologyManager/OntologyStore/indexeddb-storage.ts +11 -5
- package/src/OntologyManager/index.ts +12 -7
- package/src/OntologyManager/util.ts +3 -2
- package/src/TabularEditor/HybridGrid/ChangeHandling.ts +2 -2
- package/src/TabularEditor/HybridGrid/Feature.tsx +13 -7
- package/src/TabularEditor/HybridGrid/FeatureAttributes.tsx +1 -1
- package/src/TabularEditor/HybridGrid/HybridGrid.tsx +3 -2
- package/src/TabularEditor/HybridGrid/ToolBar.tsx +1 -1
- package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +114 -22
- package/src/TabularEditor/TabularEditorPane.tsx +1 -1
- package/src/TabularEditor/model.ts +2 -2
- package/src/TabularEditor/types.ts +5 -2
- package/src/components/AddAssembly.tsx +182 -179
- package/src/components/AddAssemblyAliases.tsx +114 -0
- package/src/components/AddChildFeature.tsx +8 -10
- package/src/components/AddFeature.tsx +216 -44
- package/src/components/AddRefSeqAliases.tsx +14 -12
- package/src/components/CopyFeature.tsx +10 -11
- package/src/components/CreateApolloAnnotation.tsx +342 -158
- package/src/components/DeleteAssembly.tsx +9 -8
- package/src/components/DeleteFeature.tsx +362 -14
- package/src/components/Dialog.tsx +1 -1
- package/src/components/DownloadGFF3.tsx +31 -11
- package/src/components/FilterFeatures.tsx +6 -4
- package/src/components/FilterTranscripts.tsx +86 -0
- package/src/components/ImportFeatures.tsx +7 -6
- package/src/components/LogOut.tsx +5 -4
- package/src/components/ManageChecks.tsx +9 -8
- package/src/components/ManageUsers.tsx +11 -10
- package/src/components/MergeExons.tsx +193 -0
- package/src/components/MergeTranscripts.tsx +185 -0
- package/src/components/OntologyTermAutocomplete.tsx +5 -5
- package/src/components/OntologyTermMultiSelect.tsx +6 -6
- package/src/components/OpenLocalFile.tsx +4 -3
- package/src/components/SplitExon.tsx +134 -0
- package/src/components/ViewChangeLog.tsx +7 -6
- package/src/components/ViewCheckResults.tsx +8 -7
- package/src/components/index.ts +3 -0
- package/src/config.ts +5 -0
- package/src/extensions/annotationFromJBrowseFeature.test.ts +1 -0
- package/src/extensions/annotationFromJBrowseFeature.ts +13 -10
- package/src/extensions/annotationFromPileup.ts +104 -94
- package/src/index.ts +33 -50
- package/src/makeDisplayComponent.tsx +90 -37
- package/src/session/ClientDataStore.ts +21 -17
- package/src/session/session.ts +46 -39
- package/src/types.ts +4 -4
- package/src/util/annotationFeatureUtils.ts +66 -1
- package/src/util/copyToClipboard.ts +21 -0
- package/src/util/glyphUtils.ts +49 -0
- package/src/util/index.ts +5 -3
- package/src/util/loadAssemblyIntoClient.ts +10 -3
- package/src/util/mouseEventsUtils.ts +113 -0
- package/src/ApolloSixFrameRenderer/ApolloSixFrameRenderer.tsx +0 -13
- package/src/ApolloSixFrameRenderer/components/ApolloRendering.tsx +0 -707
- package/src/ApolloSixFrameRenderer/configSchema.ts +0 -7
- package/src/ApolloSixFrameRenderer/index.ts +0 -3
- package/src/SixFrameFeatureDisplay/components/TrackLines.tsx +0 -19
- package/src/SixFrameFeatureDisplay/components/index.ts +0 -1
- package/src/SixFrameFeatureDisplay/configSchema.ts +0 -21
- package/src/SixFrameFeatureDisplay/index.ts +0 -2
- package/src/SixFrameFeatureDisplay/stateModel.ts +0 -443
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
2
|
-
|
|
3
|
-
import { AnnotationFeature } from '@apollo-annotation/mst'
|
|
4
|
-
import {
|
|
5
|
-
|
|
2
|
+
|
|
3
|
+
import { type AnnotationFeature } from '@apollo-annotation/mst'
|
|
4
|
+
import {
|
|
5
|
+
DeleteFeatureChange,
|
|
6
|
+
LocationEndChange,
|
|
7
|
+
LocationStartChange,
|
|
8
|
+
} from '@apollo-annotation/shared'
|
|
6
9
|
import {
|
|
7
10
|
Button,
|
|
8
11
|
DialogActions,
|
|
@@ -12,10 +15,19 @@ import {
|
|
|
12
15
|
import { getSnapshot } from 'mobx-state-tree'
|
|
13
16
|
import React, { useState } from 'react'
|
|
14
17
|
|
|
15
|
-
import { ChangeManager } from '../ChangeManager'
|
|
16
|
-
import { ApolloSessionModel } from '../session'
|
|
18
|
+
import { type ChangeManager } from '../ChangeManager'
|
|
19
|
+
import { type ApolloSessionModel } from '../session'
|
|
20
|
+
|
|
17
21
|
import { Dialog } from './Dialog'
|
|
18
22
|
|
|
23
|
+
interface LocationChange {
|
|
24
|
+
typeName: 'LocationStartChange' | 'LocationEndChange'
|
|
25
|
+
changedId: string
|
|
26
|
+
featureId: string
|
|
27
|
+
oldLocation: number
|
|
28
|
+
newLocation: number
|
|
29
|
+
}
|
|
30
|
+
|
|
19
31
|
interface DeleteFeatureProps {
|
|
20
32
|
session: ApolloSessionModel
|
|
21
33
|
handleClose(): void
|
|
@@ -26,6 +38,60 @@ interface DeleteFeatureProps {
|
|
|
26
38
|
setSelectedFeature(feature?: AnnotationFeature): void
|
|
27
39
|
}
|
|
28
40
|
|
|
41
|
+
function lumpLocationChanges(
|
|
42
|
+
changes: LocationChange[],
|
|
43
|
+
assembly: string,
|
|
44
|
+
): LocationStartChange | LocationEndChange | undefined {
|
|
45
|
+
if (changes.length === 0) {
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
const locationStartChange = new LocationStartChange({
|
|
49
|
+
typeName: 'LocationStartChange',
|
|
50
|
+
changedIds: [],
|
|
51
|
+
changes: [],
|
|
52
|
+
assembly,
|
|
53
|
+
})
|
|
54
|
+
const locationEndChange = new LocationEndChange({
|
|
55
|
+
typeName: 'LocationEndChange',
|
|
56
|
+
changedIds: [],
|
|
57
|
+
changes: [],
|
|
58
|
+
assembly,
|
|
59
|
+
})
|
|
60
|
+
for (const change of changes) {
|
|
61
|
+
if (change.typeName === 'LocationStartChange') {
|
|
62
|
+
locationStartChange.changedIds.push(change.changedId)
|
|
63
|
+
const cc = {
|
|
64
|
+
featureId: change.featureId,
|
|
65
|
+
oldStart: change.oldLocation,
|
|
66
|
+
newStart: change.newLocation,
|
|
67
|
+
}
|
|
68
|
+
locationStartChange.changes.push(cc)
|
|
69
|
+
}
|
|
70
|
+
if (change.typeName === 'LocationEndChange') {
|
|
71
|
+
locationEndChange.changedIds.push(change.changedId)
|
|
72
|
+
const cc = {
|
|
73
|
+
featureId: change.featureId,
|
|
74
|
+
oldEnd: change.oldLocation,
|
|
75
|
+
newEnd: change.newLocation,
|
|
76
|
+
}
|
|
77
|
+
locationEndChange.changes.push(cc)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (
|
|
81
|
+
locationStartChange.changedIds.length > 0 &&
|
|
82
|
+
locationEndChange.changedIds.length === 0
|
|
83
|
+
) {
|
|
84
|
+
return locationStartChange
|
|
85
|
+
}
|
|
86
|
+
if (
|
|
87
|
+
locationEndChange.changedIds.length > 0 &&
|
|
88
|
+
locationStartChange.changedIds.length === 0
|
|
89
|
+
) {
|
|
90
|
+
return locationEndChange
|
|
91
|
+
}
|
|
92
|
+
throw new Error('Unexpected list of changes')
|
|
93
|
+
}
|
|
94
|
+
|
|
29
95
|
export function DeleteFeature({
|
|
30
96
|
changeManager,
|
|
31
97
|
handleClose,
|
|
@@ -35,8 +101,198 @@ export function DeleteFeature({
|
|
|
35
101
|
sourceAssemblyId,
|
|
36
102
|
sourceFeature,
|
|
37
103
|
}: DeleteFeatureProps) {
|
|
38
|
-
const { notify } = session as unknown as AbstractSessionModel
|
|
39
104
|
const [errorMessage, setErrorMessage] = useState('')
|
|
105
|
+
const { ontologyManager } = session.apolloDataStore
|
|
106
|
+
const { featureTypeOntology } = ontologyManager
|
|
107
|
+
|
|
108
|
+
function trimCDS(
|
|
109
|
+
sourceFeature: AnnotationFeature,
|
|
110
|
+
): DeleteFeatureChange | LocationChange | undefined {
|
|
111
|
+
if (!featureTypeOntology) {
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
if (!featureTypeOntology.isTypeOf(sourceFeature.type, 'exon')) {
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
if (
|
|
118
|
+
!sourceFeature.parent?.cdsLocations ||
|
|
119
|
+
sourceFeature.parent.cdsLocations.length === 0 ||
|
|
120
|
+
sourceFeature.parent.cdsLocations[0].length === 0
|
|
121
|
+
) {
|
|
122
|
+
// No CDS - parent of this exon is a non-coding transcript
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
if (!sourceFeature.parent.children) {
|
|
126
|
+
throw new Error('Unable to find parent of CDS')
|
|
127
|
+
}
|
|
128
|
+
if (sourceFeature.parent.cdsLocations.length != 1) {
|
|
129
|
+
throw new Error('Unable to handle a transcript with multiple CDSs')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const _cdsLocations = sourceFeature.parent.cdsLocations.at(0) ?? []
|
|
133
|
+
const cdsLocations = _cdsLocations.sort(({ min: a }, { min: b }) => a - b)
|
|
134
|
+
let cdsFeature
|
|
135
|
+
for (const child of sourceFeature.parent.children.values()) {
|
|
136
|
+
if (child.type === cdsLocations[0].type) {
|
|
137
|
+
cdsFeature = child
|
|
138
|
+
break
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (!cdsFeature) {
|
|
142
|
+
throw new Error('Unable to find CDS')
|
|
143
|
+
}
|
|
144
|
+
const cdsStart = cdsLocations[0].min
|
|
145
|
+
// eslint-disable-next-line unicorn/prefer-at
|
|
146
|
+
const cdsEnd = cdsLocations[cdsLocations.length - 1].max
|
|
147
|
+
if (
|
|
148
|
+
(sourceFeature.min > cdsStart && sourceFeature.max < cdsEnd) ||
|
|
149
|
+
sourceFeature.max < cdsStart ||
|
|
150
|
+
sourceFeature.min > cdsEnd
|
|
151
|
+
) {
|
|
152
|
+
// No adjustment if the exon being deleted is fully contained in the CDS
|
|
153
|
+
// or completely outside of the CDS
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
if (sourceFeature.min <= cdsStart && sourceFeature.max >= cdsEnd) {
|
|
157
|
+
// CDS is fully contained in the exon, delete CDS
|
|
158
|
+
return new DeleteFeatureChange({
|
|
159
|
+
changedIds: [cdsFeature._id],
|
|
160
|
+
typeName: 'DeleteFeatureChange',
|
|
161
|
+
assembly: sourceAssemblyId,
|
|
162
|
+
changes: [
|
|
163
|
+
{
|
|
164
|
+
deletedFeature: getSnapshot(cdsFeature),
|
|
165
|
+
parentFeatureId: cdsFeature.parent?._id,
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
if (sourceFeature.min <= cdsStart && sourceFeature.max > cdsStart) {
|
|
171
|
+
// Exon overlaps the start of the CDS so we need to move the CDS start
|
|
172
|
+
let newCdsStart
|
|
173
|
+
for (const cdsLocation of cdsLocations) {
|
|
174
|
+
if (cdsLocation.min > sourceFeature.max) {
|
|
175
|
+
newCdsStart = cdsLocation.min
|
|
176
|
+
break
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (!newCdsStart) {
|
|
180
|
+
throw new Error('Error setting new CDS start')
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
typeName: 'LocationStartChange',
|
|
184
|
+
changedId: cdsFeature._id,
|
|
185
|
+
featureId: cdsFeature._id,
|
|
186
|
+
oldLocation: cdsFeature.min,
|
|
187
|
+
newLocation: newCdsStart,
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (sourceFeature.min < cdsEnd && sourceFeature.max >= cdsEnd) {
|
|
191
|
+
// Exon overlaps the end of the CDS so we need to move the CDS end
|
|
192
|
+
let newCdsEnd
|
|
193
|
+
for (const cdsLocation of cdsLocations.reverse()) {
|
|
194
|
+
if (cdsLocation.max < sourceFeature.min) {
|
|
195
|
+
newCdsEnd = cdsLocation.max
|
|
196
|
+
break
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (!newCdsEnd) {
|
|
200
|
+
throw new Error('Error setting new CDS end')
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
typeName: 'LocationEndChange',
|
|
204
|
+
changedId: cdsFeature._id,
|
|
205
|
+
featureId: cdsFeature._id,
|
|
206
|
+
oldLocation: cdsFeature.max,
|
|
207
|
+
newLocation: newCdsEnd,
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
throw new Error('Unexpected relationship between exon and CDS')
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function trimParent(
|
|
214
|
+
featureToDelete: AnnotationFeature,
|
|
215
|
+
): LocationChange | undefined {
|
|
216
|
+
if (
|
|
217
|
+
!featureToDelete.parent?.children ||
|
|
218
|
+
featureToDelete.parent.children.size === 1
|
|
219
|
+
) {
|
|
220
|
+
// Do not resize if this parent has only one child (i.e. the feature being deleted)
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
const childrenByStart = []
|
|
224
|
+
for (const x of featureToDelete.parent.children.values()) {
|
|
225
|
+
if (!featureTypeOntology?.isTypeOf(x.type, 'CDS')) {
|
|
226
|
+
// CDS has been already handled so don't use it to resize parent
|
|
227
|
+
childrenByStart.push(x)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
childrenByStart.sort((a, b) => a.min - b.min)
|
|
231
|
+
|
|
232
|
+
const childrenByEnd = []
|
|
233
|
+
for (const x of featureToDelete.parent.children.values()) {
|
|
234
|
+
if (!featureTypeOntology?.isTypeOf(x.type, 'CDS')) {
|
|
235
|
+
// CDS has been already handled so don't use it to resize parent
|
|
236
|
+
childrenByEnd.push(x)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
childrenByEnd.sort((a, b) => b.max - a.max)
|
|
240
|
+
|
|
241
|
+
if (featureToDelete.min === childrenByStart[0].min) {
|
|
242
|
+
// The feature to delete has the lowest start coordinate of all children
|
|
243
|
+
// Find the next lowest coordinate and reset parent to this new start
|
|
244
|
+
let newParentFeatureStart
|
|
245
|
+
for (const child of childrenByStart) {
|
|
246
|
+
if (
|
|
247
|
+
child._id !== featureToDelete._id &&
|
|
248
|
+
child.min >= featureToDelete.min
|
|
249
|
+
) {
|
|
250
|
+
newParentFeatureStart = child.min
|
|
251
|
+
break
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (
|
|
255
|
+
newParentFeatureStart &&
|
|
256
|
+
newParentFeatureStart != featureToDelete.parent.min
|
|
257
|
+
) {
|
|
258
|
+
return {
|
|
259
|
+
typeName: 'LocationStartChange',
|
|
260
|
+
changedId: featureToDelete.parent._id,
|
|
261
|
+
featureId: featureToDelete.parent._id,
|
|
262
|
+
oldLocation: featureToDelete.parent.min,
|
|
263
|
+
newLocation: newParentFeatureStart,
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (featureToDelete.max === childrenByEnd[0].max) {
|
|
269
|
+
// The feature to delete has the highest end coordinate of all children
|
|
270
|
+
// Find the next highest coordinate and reset parent to this new end
|
|
271
|
+
let newParentFeatureEnd
|
|
272
|
+
for (const child of childrenByEnd) {
|
|
273
|
+
if (
|
|
274
|
+
child._id != featureToDelete._id &&
|
|
275
|
+
child.max <= featureToDelete.max
|
|
276
|
+
) {
|
|
277
|
+
newParentFeatureEnd = child.max
|
|
278
|
+
break
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (
|
|
282
|
+
newParentFeatureEnd &&
|
|
283
|
+
newParentFeatureEnd != featureToDelete.parent.max
|
|
284
|
+
) {
|
|
285
|
+
return {
|
|
286
|
+
typeName: 'LocationEndChange',
|
|
287
|
+
changedId: featureToDelete.parent._id,
|
|
288
|
+
featureId: featureToDelete.parent._id,
|
|
289
|
+
oldLocation: featureToDelete.parent.max,
|
|
290
|
+
newLocation: newParentFeatureEnd,
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return
|
|
295
|
+
}
|
|
40
296
|
|
|
41
297
|
async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
42
298
|
event.preventDefault()
|
|
@@ -45,16 +301,104 @@ export function DeleteFeature({
|
|
|
45
301
|
setSelectedFeature()
|
|
46
302
|
}
|
|
47
303
|
|
|
48
|
-
|
|
49
|
-
const
|
|
304
|
+
const locationChanges: LocationChange[] = []
|
|
305
|
+
// const deleteChanges: DeleteFeatureChange = []
|
|
306
|
+
|
|
307
|
+
const deleteChanges = new DeleteFeatureChange({
|
|
50
308
|
changedIds: [sourceFeature._id],
|
|
51
309
|
typeName: 'DeleteFeatureChange',
|
|
52
310
|
assembly: sourceAssemblyId,
|
|
53
|
-
|
|
54
|
-
|
|
311
|
+
changes: [
|
|
312
|
+
{
|
|
313
|
+
deletedFeature: getSnapshot(sourceFeature),
|
|
314
|
+
parentFeatureId: sourceFeature.parent?._id,
|
|
315
|
+
},
|
|
316
|
+
],
|
|
55
317
|
})
|
|
56
|
-
|
|
57
|
-
|
|
318
|
+
|
|
319
|
+
if (
|
|
320
|
+
featureTypeOntology &&
|
|
321
|
+
(featureTypeOntology.isTypeOf(sourceFeature.type, 'transcript') ||
|
|
322
|
+
featureTypeOntology.isTypeOf(
|
|
323
|
+
sourceFeature.type,
|
|
324
|
+
'pseudogenic_transcript',
|
|
325
|
+
))
|
|
326
|
+
) {
|
|
327
|
+
const geneChange = trimParent(sourceFeature)
|
|
328
|
+
if (geneChange) {
|
|
329
|
+
locationChanges.push(geneChange)
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (
|
|
334
|
+
featureTypeOntology &&
|
|
335
|
+
featureTypeOntology.isTypeOf(sourceFeature.type, 'exon')
|
|
336
|
+
) {
|
|
337
|
+
const cdsChange = trimCDS(sourceFeature)
|
|
338
|
+
if (cdsChange) {
|
|
339
|
+
if (cdsChange.typeName === 'DeleteFeatureChange') {
|
|
340
|
+
deleteChanges.changedIds.push(...cdsChange.changedIds)
|
|
341
|
+
deleteChanges.changes.push(...cdsChange.changes)
|
|
342
|
+
} else {
|
|
343
|
+
locationChanges.push(cdsChange)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const txChange = trimParent(sourceFeature)
|
|
348
|
+
if (txChange) {
|
|
349
|
+
locationChanges.push(txChange)
|
|
350
|
+
// Parent transcript has changed. See if we need to resize the parent gene
|
|
351
|
+
const gene = sourceFeature.parent?.parent
|
|
352
|
+
if (gene?.children) {
|
|
353
|
+
if (txChange.typeName === 'LocationStartChange') {
|
|
354
|
+
let newGeneStart = txChange.newLocation
|
|
355
|
+
for (const [, tx] of gene.children) {
|
|
356
|
+
if (tx._id != txChange.featureId && tx.min < newGeneStart) {
|
|
357
|
+
// Reset to longest child (tx)
|
|
358
|
+
newGeneStart = tx.min
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
if (newGeneStart != gene.min) {
|
|
362
|
+
locationChanges.push({
|
|
363
|
+
typeName: txChange.typeName,
|
|
364
|
+
changedId: gene._id,
|
|
365
|
+
featureId: gene._id,
|
|
366
|
+
oldLocation: gene.min,
|
|
367
|
+
newLocation: newGeneStart,
|
|
368
|
+
})
|
|
369
|
+
}
|
|
370
|
+
} else {
|
|
371
|
+
let newGeneEnd = txChange.newLocation
|
|
372
|
+
for (const [, tx] of gene.children) {
|
|
373
|
+
if (tx._id != txChange.featureId && tx.max > newGeneEnd) {
|
|
374
|
+
// Reset to longest child (tx)
|
|
375
|
+
newGeneEnd = tx.max
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (newGeneEnd != gene.max) {
|
|
379
|
+
locationChanges.push({
|
|
380
|
+
typeName: txChange.typeName,
|
|
381
|
+
changedId: gene._id,
|
|
382
|
+
featureId: gene._id,
|
|
383
|
+
oldLocation: gene.max,
|
|
384
|
+
newLocation: newGeneEnd,
|
|
385
|
+
})
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const lumpedLocChanges = lumpLocationChanges(
|
|
393
|
+
locationChanges,
|
|
394
|
+
sourceAssemblyId,
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
await changeManager.submit(deleteChanges)
|
|
398
|
+
if (lumpedLocChanges) {
|
|
399
|
+
await changeManager.submit(lumpedLocChanges)
|
|
400
|
+
}
|
|
401
|
+
|
|
58
402
|
handleClose()
|
|
59
403
|
event.preventDefault()
|
|
60
404
|
}
|
|
@@ -67,7 +411,11 @@ export function DeleteFeature({
|
|
|
67
411
|
maxWidth={false}
|
|
68
412
|
data-testid="delete-feature"
|
|
69
413
|
>
|
|
70
|
-
<form
|
|
414
|
+
<form
|
|
415
|
+
onSubmit={(event) => {
|
|
416
|
+
void onSubmit(event)
|
|
417
|
+
}}
|
|
418
|
+
>
|
|
71
419
|
<DialogContent style={{ display: 'flex', flexDirection: 'column' }}>
|
|
72
420
|
<DialogContentText>
|
|
73
421
|
Are you sure you want to delete the selected feature?
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
2
2
|
import { Dialog as JBDialog } from '@jbrowse/core/ui'
|
|
3
3
|
import CloseIcon from '@mui/icons-material/Close'
|
|
4
|
-
import { DialogProps, DialogTitle, IconButton } from '@mui/material'
|
|
4
|
+
import { type DialogProps, DialogTitle, IconButton } from '@mui/material'
|
|
5
5
|
import { observer } from 'mobx-react'
|
|
6
6
|
import React from 'react'
|
|
7
7
|
import { makeStyles } from 'tss-react/mui'
|
|
@@ -3,32 +3,36 @@
|
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
5
5
|
/* eslint-disable @typescript-eslint/no-misused-promises */
|
|
6
|
-
import { ApolloAssembly } from '@apollo-annotation/mst'
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
6
|
+
import { type ApolloAssembly } from '@apollo-annotation/mst'
|
|
7
|
+
import { annotationFeatureToGFF3 } from '@apollo-annotation/shared'
|
|
8
|
+
import gff, { type GFF3Item } from '@gmod/gff'
|
|
9
|
+
import { type Assembly } from '@jbrowse/core/assemblyManager/assembly'
|
|
9
10
|
import { getConf } from '@jbrowse/core/configuration'
|
|
10
11
|
import {
|
|
11
12
|
Button,
|
|
13
|
+
Checkbox,
|
|
12
14
|
DialogActions,
|
|
13
15
|
DialogContent,
|
|
14
16
|
DialogContentText,
|
|
17
|
+
FormControlLabel,
|
|
18
|
+
FormGroup,
|
|
15
19
|
MenuItem,
|
|
16
20
|
Select,
|
|
17
|
-
SelectChangeEvent,
|
|
21
|
+
type SelectChangeEvent,
|
|
18
22
|
} from '@mui/material'
|
|
19
23
|
import { saveAs } from 'file-saver'
|
|
20
|
-
import { IMSTMap, getSnapshot } from 'mobx-state-tree'
|
|
24
|
+
import { type IMSTMap, getSnapshot } from 'mobx-state-tree'
|
|
21
25
|
import React, { useState } from 'react'
|
|
22
26
|
|
|
23
27
|
import {
|
|
24
|
-
ApolloInternetAccount,
|
|
25
|
-
CollaborationServerDriver,
|
|
26
|
-
InMemoryFileDriver,
|
|
28
|
+
type ApolloInternetAccount,
|
|
29
|
+
type CollaborationServerDriver,
|
|
30
|
+
type InMemoryFileDriver,
|
|
27
31
|
} from '../BackendDrivers'
|
|
28
|
-
import { ApolloSessionModel } from '../session'
|
|
32
|
+
import { type ApolloSessionModel } from '../session'
|
|
29
33
|
import { createFetchErrorMessage } from '../util'
|
|
34
|
+
|
|
30
35
|
import { Dialog } from './Dialog'
|
|
31
|
-
import { annotationFeatureToGFF3 } from '@apollo-annotation/shared'
|
|
32
36
|
|
|
33
37
|
interface DownloadGFF3Props {
|
|
34
38
|
session: ApolloSessionModel
|
|
@@ -36,6 +40,7 @@ interface DownloadGFF3Props {
|
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
export function DownloadGFF3({ handleClose, session }: DownloadGFF3Props) {
|
|
43
|
+
const [includeFASTA, setincludeFASTA] = useState(false)
|
|
39
44
|
const [selectedAssembly, setSelectedAssembly] = useState<Assembly>()
|
|
40
45
|
const [errorMessage, setErrorMessage] = useState('')
|
|
41
46
|
|
|
@@ -113,7 +118,7 @@ export function DownloadGFF3({ handleClose, session }: DownloadGFF3Props) {
|
|
|
113
118
|
const exportURL = new URL('export', internetAccount.baseURL)
|
|
114
119
|
const params: Record<string, string> = {
|
|
115
120
|
exportID,
|
|
116
|
-
includeFASTA: 'true',
|
|
121
|
+
includeFASTA: includeFASTA ? 'true' : 'false',
|
|
117
122
|
}
|
|
118
123
|
const exportSearchParams = new URLSearchParams(params)
|
|
119
124
|
exportURL.search = exportSearchParams.toString()
|
|
@@ -198,6 +203,21 @@ export function DownloadGFF3({ handleClose, session }: DownloadGFF3Props) {
|
|
|
198
203
|
<DialogContentText>
|
|
199
204
|
Select assembly to export to GFF3
|
|
200
205
|
</DialogContentText>
|
|
206
|
+
|
|
207
|
+
<FormGroup>
|
|
208
|
+
<FormControlLabel
|
|
209
|
+
data-testid="include-fasta-checkbox"
|
|
210
|
+
control={
|
|
211
|
+
<Checkbox
|
|
212
|
+
checked={includeFASTA}
|
|
213
|
+
onChange={() => {
|
|
214
|
+
setincludeFASTA(!includeFASTA)
|
|
215
|
+
}}
|
|
216
|
+
/>
|
|
217
|
+
}
|
|
218
|
+
label="Include fasta sequence in GFF output"
|
|
219
|
+
/>
|
|
220
|
+
</FormGroup>
|
|
201
221
|
</DialogContent>
|
|
202
222
|
<DialogActions>
|
|
203
223
|
<Button
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import React, { useState } from 'react'
|
|
2
|
-
import { ApolloSessionModel } from '../session'
|
|
3
|
-
import { Dialog } from './Dialog'
|
|
4
1
|
import {
|
|
5
2
|
Box,
|
|
6
3
|
Button,
|
|
@@ -10,9 +7,14 @@ import {
|
|
|
10
7
|
Grid2,
|
|
11
8
|
TextField,
|
|
12
9
|
} from '@mui/material'
|
|
10
|
+
import { observer } from 'mobx-react'
|
|
11
|
+
import React, { useState } from 'react'
|
|
12
|
+
|
|
13
13
|
import { isOntologyClass } from '../OntologyManager'
|
|
14
|
+
import { type ApolloSessionModel } from '../session'
|
|
15
|
+
|
|
16
|
+
import { Dialog } from './Dialog'
|
|
14
17
|
import { OntologyTermAutocomplete } from './OntologyTermAutocomplete'
|
|
15
|
-
import { observer } from 'mobx-react'
|
|
16
18
|
|
|
17
19
|
interface FilterFeaturesProps {
|
|
18
20
|
onUpdate: (types: string[]) => void
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { type AnnotationFeature } from '@apollo-annotation/mst'
|
|
2
|
+
import {
|
|
3
|
+
Checkbox,
|
|
4
|
+
DialogContent,
|
|
5
|
+
DialogContentText,
|
|
6
|
+
FormControlLabel,
|
|
7
|
+
FormGroup,
|
|
8
|
+
Grid2,
|
|
9
|
+
} from '@mui/material'
|
|
10
|
+
import { observer } from 'mobx-react'
|
|
11
|
+
import React, { useState } from 'react'
|
|
12
|
+
|
|
13
|
+
import { Dialog } from './Dialog'
|
|
14
|
+
|
|
15
|
+
interface FilterTranscriptsProps {
|
|
16
|
+
onUpdate: (forms: string[]) => void
|
|
17
|
+
sourceFeature: AnnotationFeature
|
|
18
|
+
filteredTranscripts: string[]
|
|
19
|
+
handleClose: () => void
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const FilterTranscripts = observer(function FilterTranscripts({
|
|
23
|
+
sourceFeature,
|
|
24
|
+
filteredTranscripts,
|
|
25
|
+
handleClose,
|
|
26
|
+
onUpdate,
|
|
27
|
+
}: FilterTranscriptsProps) {
|
|
28
|
+
const allTranscripts: string[] = []
|
|
29
|
+
if (sourceFeature.children) {
|
|
30
|
+
for (const [, child] of sourceFeature.children) {
|
|
31
|
+
const childID: string | undefined = child.attributes
|
|
32
|
+
.get('gff_id')
|
|
33
|
+
?.toString()
|
|
34
|
+
if (childID) {
|
|
35
|
+
allTranscripts.push(childID)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const [excludedTranscripts, setExcludedTranscripts] =
|
|
40
|
+
useState<string[]>(filteredTranscripts)
|
|
41
|
+
const handleChange = (value: string) => {
|
|
42
|
+
const newForms = excludedTranscripts.includes(value)
|
|
43
|
+
? excludedTranscripts.filter((form) => form !== value)
|
|
44
|
+
: [...excludedTranscripts, value]
|
|
45
|
+
onUpdate(newForms)
|
|
46
|
+
setExcludedTranscripts(newForms)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<Dialog
|
|
51
|
+
open
|
|
52
|
+
maxWidth={false}
|
|
53
|
+
data-testid="filter-transcripts-dialog"
|
|
54
|
+
title="Filter transcripts by ID"
|
|
55
|
+
handleClose={handleClose}
|
|
56
|
+
>
|
|
57
|
+
<DialogContent>
|
|
58
|
+
<DialogContentText>
|
|
59
|
+
Select the alternate transcripts you want to display in the apollo
|
|
60
|
+
track
|
|
61
|
+
</DialogContentText>
|
|
62
|
+
<Grid2 container spacing={2}>
|
|
63
|
+
<Grid2 size={8}>
|
|
64
|
+
<FormGroup>
|
|
65
|
+
{allTranscripts.map((item) => (
|
|
66
|
+
// eslint-disable-next-line react/jsx-key
|
|
67
|
+
<FormControlLabel
|
|
68
|
+
control={
|
|
69
|
+
<Checkbox
|
|
70
|
+
checked={!excludedTranscripts.includes(item)}
|
|
71
|
+
onChange={() => {
|
|
72
|
+
handleChange(item)
|
|
73
|
+
}}
|
|
74
|
+
inputProps={{ 'aria-label': 'controlled' }}
|
|
75
|
+
/>
|
|
76
|
+
}
|
|
77
|
+
label={item}
|
|
78
|
+
/>
|
|
79
|
+
))}
|
|
80
|
+
</FormGroup>
|
|
81
|
+
</Grid2>
|
|
82
|
+
</Grid2>
|
|
83
|
+
</DialogContent>
|
|
84
|
+
</Dialog>
|
|
85
|
+
)
|
|
86
|
+
})
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
7
7
|
/* eslint-disable @typescript-eslint/no-misused-promises */
|
|
8
8
|
import { AddFeaturesFromFileChange } from '@apollo-annotation/shared'
|
|
9
|
-
import { Assembly } from '@jbrowse/core/assemblyManager/assembly'
|
|
9
|
+
import { type Assembly } from '@jbrowse/core/assemblyManager/assembly'
|
|
10
10
|
import { getConf } from '@jbrowse/core/configuration'
|
|
11
11
|
import {
|
|
12
12
|
Button,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
DialogContentText,
|
|
16
16
|
MenuItem,
|
|
17
17
|
Select,
|
|
18
|
-
SelectChangeEvent,
|
|
18
|
+
type SelectChangeEvent,
|
|
19
19
|
} from '@mui/material'
|
|
20
20
|
import Checkbox from '@mui/material/Checkbox'
|
|
21
21
|
import FormControlLabel from '@mui/material/FormControlLabel'
|
|
@@ -23,12 +23,13 @@ import LinearProgress from '@mui/material/LinearProgress'
|
|
|
23
23
|
import React, { useEffect, useState } from 'react'
|
|
24
24
|
|
|
25
25
|
import {
|
|
26
|
-
ApolloInternetAccount,
|
|
27
|
-
CollaborationServerDriver,
|
|
26
|
+
type ApolloInternetAccount,
|
|
27
|
+
type CollaborationServerDriver,
|
|
28
28
|
} from '../BackendDrivers'
|
|
29
|
-
import { ChangeManager } from '../ChangeManager'
|
|
30
|
-
import { ApolloSessionModel } from '../session'
|
|
29
|
+
import { type ChangeManager } from '../ChangeManager'
|
|
30
|
+
import { type ApolloSessionModel } from '../session'
|
|
31
31
|
import { createFetchErrorMessage } from '../util'
|
|
32
|
+
|
|
32
33
|
import { Dialog } from './Dialog'
|
|
33
34
|
|
|
34
35
|
interface ImportFeaturesProps {
|
|
@@ -6,14 +6,15 @@ import {
|
|
|
6
6
|
DialogContentText,
|
|
7
7
|
MenuItem,
|
|
8
8
|
Select,
|
|
9
|
-
SelectChangeEvent,
|
|
9
|
+
type SelectChangeEvent,
|
|
10
10
|
} from '@mui/material'
|
|
11
11
|
import { getRoot } from 'mobx-state-tree'
|
|
12
12
|
import React, { useState } from 'react'
|
|
13
13
|
|
|
14
|
-
import { ApolloInternetAccountModel } from '../ApolloInternetAccount/model'
|
|
15
|
-
import { ApolloSessionModel } from '../session'
|
|
16
|
-
import { ApolloRootModel } from '../types'
|
|
14
|
+
import { type ApolloInternetAccountModel } from '../ApolloInternetAccount/model'
|
|
15
|
+
import { type ApolloSessionModel } from '../session'
|
|
16
|
+
import { type ApolloRootModel } from '../types'
|
|
17
|
+
|
|
17
18
|
import { Dialog } from './Dialog'
|
|
18
19
|
|
|
19
20
|
interface DeleteAssemblyProps {
|