@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.
Files changed (84) hide show
  1. package/dist/index.esm.js +4603 -2045
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/jbrowse-plugin-apollo.cjs.development.js +4611 -2039
  4. package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
  5. package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
  6. package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
  7. package/dist/jbrowse-plugin-apollo.umd.development.js +9387 -4016
  8. package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
  9. package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
  10. package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
  11. package/package.json +15 -15
  12. package/src/ApolloInternetAccount/model.ts +48 -13
  13. package/src/BackendDrivers/CollaborationServerDriver.ts +23 -2
  14. package/src/ChangeManager.ts +42 -18
  15. package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +64 -5
  16. package/src/FeatureDetailsWidget/Attributes.tsx +8 -3
  17. package/src/FeatureDetailsWidget/TranscriptSequence.tsx +70 -81
  18. package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +946 -190
  19. package/src/FeatureDetailsWidget/TranscriptWidgetSummary.tsx +4 -0
  20. package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +61 -73
  21. package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +55 -211
  22. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +562 -108
  23. package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +78 -14
  24. package/src/LinearApolloDisplay/glyphs/Glyph.ts +15 -9
  25. package/src/LinearApolloDisplay/stateModel/base.ts +63 -43
  26. package/src/LinearApolloDisplay/stateModel/layouts.ts +3 -2
  27. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +79 -292
  28. package/src/LinearApolloDisplay/stateModel/rendering.ts +45 -344
  29. package/src/LinearApolloReferenceSequenceDisplay/components/LinearApolloReferenceSequenceDisplay.tsx +87 -0
  30. package/src/LinearApolloReferenceSequenceDisplay/components/index.ts +1 -0
  31. package/src/LinearApolloReferenceSequenceDisplay/configSchema.ts +7 -0
  32. package/src/LinearApolloReferenceSequenceDisplay/index.ts +3 -0
  33. package/src/LinearApolloReferenceSequenceDisplay/stateModel/base.ts +227 -0
  34. package/src/LinearApolloReferenceSequenceDisplay/stateModel/index.ts +25 -0
  35. package/src/LinearApolloReferenceSequenceDisplay/stateModel/rendering.ts +481 -0
  36. package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +102 -40
  37. package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +12 -20
  38. package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +382 -243
  39. package/src/LinearApolloSixFrameDisplay/glyphs/Glyph.ts +12 -8
  40. package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +83 -4
  41. package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +23 -11
  42. package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +118 -123
  43. package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +53 -63
  44. package/src/OntologyManager/index.ts +4 -1
  45. package/src/TabularEditor/HybridGrid/Feature.tsx +20 -14
  46. package/src/TabularEditor/HybridGrid/HybridGrid.tsx +7 -5
  47. package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +108 -16
  48. package/src/components/AddAssembly.tsx +1 -1
  49. package/src/components/AddAssemblyAliases.tsx +114 -0
  50. package/src/components/AddChildFeature.tsx +7 -7
  51. package/src/components/AddFeature.tsx +20 -15
  52. package/src/components/AddRefSeqAliases.tsx +9 -9
  53. package/src/components/CopyFeature.tsx +4 -4
  54. package/src/components/CreateApolloAnnotation.tsx +335 -151
  55. package/src/components/DeleteAssembly.tsx +1 -1
  56. package/src/components/DeleteFeature.tsx +358 -11
  57. package/src/components/DownloadGFF3.tsx +20 -1
  58. package/src/components/EditZoomThresholdDialog.tsx +69 -0
  59. package/src/components/FilterFeatures.tsx +7 -7
  60. package/src/components/FilterTranscripts.tsx +86 -0
  61. package/src/components/ImportFeatures.tsx +1 -1
  62. package/src/components/ManageChecks.tsx +1 -1
  63. package/src/components/MergeExons.tsx +193 -0
  64. package/src/components/MergeTranscripts.tsx +182 -0
  65. package/src/components/OntologyTermMultiSelect.tsx +11 -11
  66. package/src/components/OpenLocalFile.tsx +11 -7
  67. package/src/components/SplitExon.tsx +134 -0
  68. package/src/components/ViewCheckResults.tsx +1 -1
  69. package/src/components/index.ts +4 -0
  70. package/src/config.ts +11 -0
  71. package/src/extensions/annotationFromJBrowseFeature.ts +2 -0
  72. package/src/extensions/annotationFromPileup.ts +99 -89
  73. package/src/index.ts +42 -105
  74. package/src/makeDisplayComponent.tsx +0 -1
  75. package/src/menus/index.ts +1 -0
  76. package/src/{ApolloInternetAccount/addMenuItems.ts → menus/topLevelMenu.ts} +60 -33
  77. package/src/menus/topLevelMenuAdmin.ts +154 -0
  78. package/src/session/session.ts +163 -104
  79. package/src/util/annotationFeatureUtils.ts +59 -0
  80. package/src/util/copyToClipboard.ts +21 -0
  81. package/src/util/displayUtils.ts +149 -0
  82. package/src/util/glyphUtils.ts +201 -0
  83. package/src/util/index.ts +2 -0
  84. 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
- sequenceLines: string[]
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
- sequenceLines,
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
- const [previousSegmentFirstLine, ...previousSegmentFollowingLines] =
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
- sequenceLines: [newSegmentFirstLine, ...newSegmentRemainderLines],
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 sequence = getSequence(loc.min, loc.max)
93
+ let locSeq = getSequence(loc.min, loc.max)
116
94
  if (strand === -1) {
117
- sequence = revcom(sequence)
95
+ locSeq = revcom(locSeq)
118
96
  }
119
- wholeSequence += sequence
97
+ wholeSequence += locSeq
120
98
  locs.push({ min: loc.min, max: loc.max })
121
99
  }
122
- const sequenceLines = splitStringIntoChunks(
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 sequence = getSequence(loc.min, loc.max)
108
+ let locSeq = getSequence(loc.min, loc.max)
135
109
  if (strand === -1) {
136
- sequence = revcom(sequence)
110
+ locSeq = revcom(locSeq)
137
111
  }
138
- wholeSequence += sequence
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
- const sequenceLines = splitStringIntoChunks(protein, SEQUENCE_WRAP_LENGTH)
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
- // Function to copy text to clipboard
279
- const copyToClipboard = () => {
251
+ const onCopyClick = () => {
280
252
  const seqDiv = seqRef.current
281
253
  if (!seqDiv) {
282
254
  return
283
255
  }
284
- const textBlob = new Blob([seqDiv.outerText], { type: 'text/plain' })
285
- const htmlBlob = new Blob([seqDiv.outerHTML], { type: 'text/html' })
286
- const clipboardItem = new ClipboardItem({
287
- [textBlob.type]: textBlob,
288
- [htmlBlob.type]: htmlBlob,
289
- })
290
- void navigator.clipboard.write([clipboardItem])
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 key={option} value={option}>
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={copyToClipboard}
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.map((segment, index) => (
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
  )