@apollo-annotation/jbrowse-plugin-apollo 0.3.6 → 0.3.8
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 +4603 -2045
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +4611 -2039
- 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 +9387 -4016
- 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 +15 -15
- package/src/ApolloInternetAccount/model.ts +48 -13
- package/src/BackendDrivers/CollaborationServerDriver.ts +23 -2
- package/src/ChangeManager.ts +42 -18
- package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +64 -5
- package/src/FeatureDetailsWidget/Attributes.tsx +8 -3
- package/src/FeatureDetailsWidget/TranscriptSequence.tsx +70 -81
- package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +946 -190
- package/src/FeatureDetailsWidget/TranscriptWidgetSummary.tsx +4 -0
- package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +61 -73
- package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +55 -211
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +562 -108
- package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +78 -14
- package/src/LinearApolloDisplay/glyphs/Glyph.ts +15 -9
- package/src/LinearApolloDisplay/stateModel/base.ts +63 -43
- package/src/LinearApolloDisplay/stateModel/layouts.ts +3 -2
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +79 -292
- package/src/LinearApolloDisplay/stateModel/rendering.ts +45 -344
- 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/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 +481 -0
- package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +102 -40
- package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +12 -20
- package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +382 -243
- package/src/LinearApolloSixFrameDisplay/glyphs/Glyph.ts +12 -8
- package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +83 -4
- package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +23 -11
- package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +118 -123
- package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +53 -63
- package/src/OntologyManager/index.ts +4 -1
- package/src/TabularEditor/HybridGrid/Feature.tsx +20 -14
- package/src/TabularEditor/HybridGrid/HybridGrid.tsx +7 -5
- package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +108 -16
- package/src/components/AddAssembly.tsx +1 -1
- package/src/components/AddAssemblyAliases.tsx +114 -0
- package/src/components/AddChildFeature.tsx +7 -7
- package/src/components/AddFeature.tsx +20 -15
- package/src/components/AddRefSeqAliases.tsx +9 -9
- package/src/components/CopyFeature.tsx +4 -4
- package/src/components/CreateApolloAnnotation.tsx +335 -151
- package/src/components/DeleteAssembly.tsx +1 -1
- package/src/components/DeleteFeature.tsx +358 -11
- package/src/components/DownloadGFF3.tsx +20 -1
- package/src/components/EditZoomThresholdDialog.tsx +69 -0
- package/src/components/FilterFeatures.tsx +7 -7
- package/src/components/FilterTranscripts.tsx +86 -0
- package/src/components/ImportFeatures.tsx +1 -1
- package/src/components/ManageChecks.tsx +1 -1
- package/src/components/MergeExons.tsx +193 -0
- package/src/components/MergeTranscripts.tsx +182 -0
- package/src/components/OntologyTermMultiSelect.tsx +11 -11
- package/src/components/OpenLocalFile.tsx +11 -7
- package/src/components/SplitExon.tsx +134 -0
- package/src/components/ViewCheckResults.tsx +1 -1
- package/src/components/index.ts +4 -0
- package/src/config.ts +11 -0
- package/src/extensions/annotationFromJBrowseFeature.ts +2 -0
- package/src/extensions/annotationFromPileup.ts +99 -89
- package/src/index.ts +42 -105
- package/src/makeDisplayComponent.tsx +0 -1
- package/src/menus/index.ts +1 -0
- package/src/{ApolloInternetAccount/addMenuItems.ts → menus/topLevelMenu.ts} +60 -33
- package/src/menus/topLevelMenuAdmin.ts +154 -0
- package/src/session/session.ts +163 -104
- package/src/util/annotationFeatureUtils.ts +59 -0
- package/src/util/copyToClipboard.ts +21 -0
- package/src/util/displayUtils.ts +149 -0
- package/src/util/glyphUtils.ts +201 -0
- package/src/util/index.ts +2 -0
- package/src/util/mouseEventsUtils.ts +145 -0
|
@@ -13,6 +13,7 @@ import { observer } from 'mobx-react'
|
|
|
13
13
|
import React, { useEffect, useRef, useState } from 'react'
|
|
14
14
|
|
|
15
15
|
import { type ApolloSessionModel } from '../session'
|
|
16
|
+
import { copyToClipboard } from '../util/copyToClipboard'
|
|
16
17
|
|
|
17
18
|
const SEQUENCE_WRAP_LENGTH = 60
|
|
18
19
|
|
|
@@ -27,10 +28,18 @@ type SegmentListType = 'CDS' | 'cDNA' | 'genomic' | 'protein'
|
|
|
27
28
|
|
|
28
29
|
interface SequenceSegment {
|
|
29
30
|
type: SegmentType
|
|
30
|
-
|
|
31
|
+
sequence: string
|
|
31
32
|
locs: { min: number; max: number }[]
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
function getSequenceLength(segments: SequenceSegment[]): number {
|
|
36
|
+
let length = 0
|
|
37
|
+
for (const segment of segments) {
|
|
38
|
+
length += segment.sequence.length
|
|
39
|
+
}
|
|
40
|
+
return length
|
|
41
|
+
}
|
|
42
|
+
|
|
34
43
|
function getSequenceSegments(
|
|
35
44
|
segmentType: SegmentListType,
|
|
36
45
|
feature: AnnotationFeature,
|
|
@@ -56,51 +65,20 @@ function getSequenceSegments(
|
|
|
56
65
|
: loc.type
|
|
57
66
|
const previousSegment = segments.at(-1)
|
|
58
67
|
if (!previousSegment) {
|
|
59
|
-
const sequenceLines = splitStringIntoChunks(
|
|
60
|
-
sequence,
|
|
61
|
-
SEQUENCE_WRAP_LENGTH,
|
|
62
|
-
)
|
|
63
68
|
segments.push({
|
|
64
69
|
type,
|
|
65
|
-
|
|
70
|
+
sequence,
|
|
66
71
|
locs: [{ min: loc.min, max: loc.max }],
|
|
67
72
|
})
|
|
68
73
|
continue
|
|
69
74
|
}
|
|
70
75
|
if (previousSegment.type === type) {
|
|
71
|
-
|
|
72
|
-
previousSegment.sequenceLines
|
|
73
|
-
const newSequence = previousSegmentFollowingLines.join('') + sequence
|
|
74
|
-
previousSegment.sequenceLines = [
|
|
75
|
-
previousSegmentFirstLine,
|
|
76
|
-
...splitStringIntoChunks(newSequence, SEQUENCE_WRAP_LENGTH),
|
|
77
|
-
]
|
|
76
|
+
previousSegment.sequence += sequence
|
|
78
77
|
previousSegment.locs.push({ min: loc.min, max: loc.max })
|
|
79
78
|
} else {
|
|
80
|
-
const count = segments.reduce(
|
|
81
|
-
(accumulator, currentSegment) =>
|
|
82
|
-
accumulator +
|
|
83
|
-
currentSegment.sequenceLines.reduce(
|
|
84
|
-
(subAccumulator, currentLine) =>
|
|
85
|
-
subAccumulator + currentLine.length,
|
|
86
|
-
0,
|
|
87
|
-
),
|
|
88
|
-
0,
|
|
89
|
-
)
|
|
90
|
-
const previousLineLength = count % SEQUENCE_WRAP_LENGTH
|
|
91
|
-
const newSegmentFirstLineLength =
|
|
92
|
-
SEQUENCE_WRAP_LENGTH - previousLineLength
|
|
93
|
-
const newSegmentFirstLine = sequence.slice(
|
|
94
|
-
0,
|
|
95
|
-
newSegmentFirstLineLength,
|
|
96
|
-
)
|
|
97
|
-
const newSegmentRemainderLines = splitStringIntoChunks(
|
|
98
|
-
sequence.slice(newSegmentFirstLineLength),
|
|
99
|
-
SEQUENCE_WRAP_LENGTH,
|
|
100
|
-
)
|
|
101
79
|
segments.push({
|
|
102
80
|
type,
|
|
103
|
-
|
|
81
|
+
sequence,
|
|
104
82
|
locs: [{ min: loc.min, max: loc.max }],
|
|
105
83
|
})
|
|
106
84
|
}
|
|
@@ -112,18 +90,14 @@ function getSequenceSegments(
|
|
|
112
90
|
const [firstLocation] = cdsLocations
|
|
113
91
|
const locs: { min: number; max: number }[] = []
|
|
114
92
|
for (const loc of firstLocation) {
|
|
115
|
-
let
|
|
93
|
+
let locSeq = getSequence(loc.min, loc.max)
|
|
116
94
|
if (strand === -1) {
|
|
117
|
-
|
|
95
|
+
locSeq = revcom(locSeq)
|
|
118
96
|
}
|
|
119
|
-
wholeSequence +=
|
|
97
|
+
wholeSequence += locSeq
|
|
120
98
|
locs.push({ min: loc.min, max: loc.max })
|
|
121
99
|
}
|
|
122
|
-
|
|
123
|
-
wholeSequence,
|
|
124
|
-
SEQUENCE_WRAP_LENGTH,
|
|
125
|
-
)
|
|
126
|
-
segments.push({ type: 'CDS', sequenceLines, locs })
|
|
100
|
+
segments.push({ type: 'CDS', sequence: wholeSequence, locs })
|
|
127
101
|
return segments
|
|
128
102
|
}
|
|
129
103
|
case 'protein': {
|
|
@@ -131,11 +105,11 @@ function getSequenceSegments(
|
|
|
131
105
|
const [firstLocation] = cdsLocations
|
|
132
106
|
const locs: { min: number; max: number }[] = []
|
|
133
107
|
for (const loc of firstLocation) {
|
|
134
|
-
let
|
|
108
|
+
let locSeq = getSequence(loc.min, loc.max)
|
|
135
109
|
if (strand === -1) {
|
|
136
|
-
|
|
110
|
+
locSeq = revcom(locSeq)
|
|
137
111
|
}
|
|
138
|
-
wholeSequence +=
|
|
112
|
+
wholeSequence += locSeq
|
|
139
113
|
locs.push({ min: loc.min, max: loc.max })
|
|
140
114
|
}
|
|
141
115
|
let protein = ''
|
|
@@ -144,8 +118,7 @@ function getSequenceSegments(
|
|
|
144
118
|
protein +=
|
|
145
119
|
defaultCodonTable[codonSeq as keyof typeof defaultCodonTable] || '&'
|
|
146
120
|
}
|
|
147
|
-
|
|
148
|
-
segments.push({ type: 'protein', sequenceLines, locs })
|
|
121
|
+
segments.push({ type: 'protein', sequence: protein, locs })
|
|
149
122
|
return segments
|
|
150
123
|
}
|
|
151
124
|
}
|
|
@@ -275,19 +248,49 @@ export const TranscriptSequence = observer(function TranscriptSequence({
|
|
|
275
248
|
setLocationIntervals(locIntervals)
|
|
276
249
|
}
|
|
277
250
|
|
|
278
|
-
|
|
279
|
-
const copyToClipboard = () => {
|
|
251
|
+
const onCopyClick = () => {
|
|
280
252
|
const seqDiv = seqRef.current
|
|
281
253
|
if (!seqDiv) {
|
|
282
254
|
return
|
|
283
255
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
256
|
+
void copyToClipboard(seqDiv)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function wrapSequence(
|
|
260
|
+
sequenceSegments: SequenceSegment[],
|
|
261
|
+
sequenceWrapLength: number,
|
|
262
|
+
): React.ReactNode[] {
|
|
263
|
+
const seqElements: React.ReactNode[] = []
|
|
264
|
+
let processedChars = 0
|
|
265
|
+
for (const [index, segment] of sequenceSegments.entries()) {
|
|
266
|
+
const lastLineLength = processedChars % sequenceWrapLength
|
|
267
|
+
const segmentLineBreak =
|
|
268
|
+
processedChars > 0 && lastLineLength === 0 ? '\n' : ''
|
|
269
|
+
processedChars += segment.sequence.length
|
|
270
|
+
const firstLine =
|
|
271
|
+
segmentLineBreak +
|
|
272
|
+
segment.sequence.slice(0, sequenceWrapLength - lastLineLength)
|
|
273
|
+
const remainingLines = splitStringIntoChunks(
|
|
274
|
+
segment.sequence.slice(firstLine.length),
|
|
275
|
+
sequenceWrapLength,
|
|
276
|
+
)
|
|
277
|
+
const printLines = [firstLine, ...remainingLines]
|
|
278
|
+
|
|
279
|
+
const span = (
|
|
280
|
+
<span
|
|
281
|
+
key={`${segment.type}-${index}`}
|
|
282
|
+
style={{
|
|
283
|
+
background: getSegmentColor(segment.type),
|
|
284
|
+
color: theme.palette.getContrastText(getSegmentColor(segment.type)),
|
|
285
|
+
whiteSpace: 'pre-line',
|
|
286
|
+
}}
|
|
287
|
+
>
|
|
288
|
+
{printLines.join('\n')}
|
|
289
|
+
</span>
|
|
290
|
+
)
|
|
291
|
+
seqElements.push(span)
|
|
292
|
+
}
|
|
293
|
+
return seqElements
|
|
291
294
|
}
|
|
292
295
|
|
|
293
296
|
return (
|
|
@@ -297,16 +300,21 @@ export const TranscriptSequence = observer(function TranscriptSequence({
|
|
|
297
300
|
value={selectedOption}
|
|
298
301
|
onChange={handleChangeSeqOption}
|
|
299
302
|
size="small"
|
|
303
|
+
data-testid="sequenceOptionSelector"
|
|
300
304
|
>
|
|
301
305
|
{sequenceOptions.map((option) => (
|
|
302
|
-
<MenuItem
|
|
306
|
+
<MenuItem
|
|
307
|
+
key={option}
|
|
308
|
+
value={option}
|
|
309
|
+
data-testid={`sequenceOption-${option}`}
|
|
310
|
+
>
|
|
303
311
|
{option}
|
|
304
312
|
</MenuItem>
|
|
305
313
|
))}
|
|
306
314
|
</Select>
|
|
307
315
|
<Button
|
|
308
316
|
variant="contained"
|
|
309
|
-
onClick={
|
|
317
|
+
onClick={onCopyClick}
|
|
310
318
|
style={{ marginLeft: 10 }}
|
|
311
319
|
size="medium"
|
|
312
320
|
>
|
|
@@ -328,29 +336,10 @@ export const TranscriptSequence = observer(function TranscriptSequence({
|
|
|
328
336
|
: `${interval.max}-${interval.min + 1}`,
|
|
329
337
|
)
|
|
330
338
|
.join(';')}
|
|
331
|
-
({feature.strand === 1 ? '+' : '-'}
|
|
339
|
+
(strand={feature.strand === 1 ? '+' : '-'};length=
|
|
340
|
+
{getSequenceLength(sequenceSegments)})
|
|
332
341
|
<br />
|
|
333
|
-
{sequenceSegments
|
|
334
|
-
<span
|
|
335
|
-
key={`${segment.type}-${index}`}
|
|
336
|
-
style={{
|
|
337
|
-
background: getSegmentColor(segment.type),
|
|
338
|
-
color: theme.palette.getContrastText(
|
|
339
|
-
getSegmentColor(segment.type),
|
|
340
|
-
),
|
|
341
|
-
}}
|
|
342
|
-
>
|
|
343
|
-
{segment.sequenceLines.map((sequenceLine, idx) => (
|
|
344
|
-
<React.Fragment key={`${sequenceLine.slice(0, 5)}-${idx}`}>
|
|
345
|
-
{sequenceLine}
|
|
346
|
-
{idx === segment.sequenceLines.length - 1 &&
|
|
347
|
-
sequenceLine.length !== SEQUENCE_WRAP_LENGTH ? null : (
|
|
348
|
-
<br />
|
|
349
|
-
)}
|
|
350
|
-
</React.Fragment>
|
|
351
|
-
))}
|
|
352
|
-
</span>
|
|
353
|
-
))}
|
|
342
|
+
{wrapSequence(sequenceSegments, SEQUENCE_WRAP_LENGTH)}
|
|
354
343
|
</Paper>
|
|
355
344
|
</>
|
|
356
345
|
)
|