@apollo-annotation/jbrowse-plugin-apollo 0.1.18 → 0.1.20

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 (85) hide show
  1. package/dist/index.esm.js +3189 -3575
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/jbrowse-plugin-apollo.cjs.development.js +3185 -3570
  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 +14884 -15905
  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 +33 -33
  12. package/src/ApolloInternetAccount/addMenuItems.ts +18 -0
  13. package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +1 -0
  14. package/src/ApolloInternetAccount/configSchema.ts +5 -2
  15. package/src/ApolloInternetAccount/model.ts +14 -5
  16. package/src/ApolloRefNameAliasAdapter/ApolloRefNameAliasAdapter.ts +94 -0
  17. package/src/ApolloRefNameAliasAdapter/configSchema.ts +12 -0
  18. package/src/ApolloRefNameAliasAdapter/index.ts +21 -0
  19. package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +1 -0
  20. package/src/ApolloSixFrameRenderer/components/ApolloRendering.tsx +10 -10
  21. package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +35 -32
  22. package/src/BackendDrivers/BackendDriver.ts +8 -0
  23. package/src/BackendDrivers/CollaborationServerDriver.ts +49 -1
  24. package/src/BackendDrivers/DesktopFileDriver.ts +14 -1
  25. package/src/BackendDrivers/InMemoryFileDriver.ts +17 -1
  26. package/src/ChangeManager.ts +1 -1
  27. package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +5 -25
  28. package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +82 -0
  29. package/src/FeatureDetailsWidget/Attributes.tsx +11 -3
  30. package/src/FeatureDetailsWidget/BasicInformation.tsx +38 -30
  31. package/src/FeatureDetailsWidget/Sequence.tsx +7 -7
  32. package/src/FeatureDetailsWidget/TranscriptBasic.tsx +446 -0
  33. package/src/FeatureDetailsWidget/TranscriptSequence.tsx +365 -0
  34. package/src/FeatureDetailsWidget/index.ts +2 -0
  35. package/src/FeatureDetailsWidget/model.ts +77 -9
  36. package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +0 -2
  37. package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +453 -380
  38. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +520 -0
  39. package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +138 -134
  40. package/src/LinearApolloDisplay/glyphs/Glyph.ts +38 -370
  41. package/src/LinearApolloDisplay/glyphs/index.ts +1 -2
  42. package/src/LinearApolloDisplay/stateModel/base.ts +3 -6
  43. package/src/LinearApolloDisplay/stateModel/getGlyph.ts +30 -30
  44. package/src/LinearApolloDisplay/stateModel/index.ts +5 -1
  45. package/src/LinearApolloDisplay/stateModel/layouts.ts +32 -24
  46. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +206 -217
  47. package/src/LinearApolloDisplay/stateModel/rendering.ts +43 -67
  48. package/src/OntologyManager/OntologyStore/fulltext.ts +1 -1
  49. package/src/OntologyManager/OntologyStore/index.ts +2 -1
  50. package/src/OntologyManager/index.ts +6 -2
  51. package/src/OntologyManager/util.ts +2 -2
  52. package/src/SixFrameFeatureDisplay/stateModel.ts +15 -10
  53. package/src/TabularEditor/HybridGrid/ChangeHandling.ts +21 -46
  54. package/src/TabularEditor/HybridGrid/Feature.tsx +31 -82
  55. package/src/TabularEditor/HybridGrid/FeatureAttributes.tsx +3 -2
  56. package/src/TabularEditor/HybridGrid/HybridGrid.tsx +2 -3
  57. package/src/TabularEditor/HybridGrid/NumberCell.tsx +1 -0
  58. package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +46 -5
  59. package/src/TabularEditor/model.ts +5 -3
  60. package/src/components/AddAssembly.tsx +15 -9
  61. package/src/components/AddChildFeature.tsx +7 -73
  62. package/src/components/AddFeature.tsx +2 -57
  63. package/src/components/AddRefSeqAliases.tsx +285 -0
  64. package/src/components/CopyFeature.tsx +16 -33
  65. package/src/components/DeleteFeature.tsx +4 -6
  66. package/src/components/ImportFeatures.tsx +6 -3
  67. package/src/components/LogOut.tsx +105 -0
  68. package/src/components/ManageChecks.tsx +1 -0
  69. package/src/components/ManageUsers.tsx +21 -1
  70. package/src/components/ModifyFeatureAttribute.tsx +2 -2
  71. package/src/components/OntologyTermAutocomplete.tsx +0 -2
  72. package/src/components/OntologyTermMultiSelect.tsx +1 -0
  73. package/src/components/OpenLocalFile.tsx +6 -5
  74. package/src/components/ViewChangeLog.tsx +1 -0
  75. package/src/components/ViewCheckResults.tsx +1 -0
  76. package/src/components/index.ts +4 -0
  77. package/src/extensions/annotationFromPileup.ts +10 -16
  78. package/src/index.ts +57 -3
  79. package/src/session/ClientDataStore.ts +49 -46
  80. package/src/session/session.ts +186 -114
  81. package/src/util/loadAssemblyIntoClient.ts +4 -210
  82. package/src/FeatureDetailsWidget/RelatedFeature.tsx +0 -97
  83. package/src/LinearApolloDisplay/glyphs/CanonicalGeneGlyph.ts +0 -1204
  84. package/src/LinearApolloDisplay/glyphs/ImplicitExonGeneGlyph.ts +0 -716
  85. package/src/LinearApolloDisplay/stateModel/glyphs.ts +0 -47
@@ -0,0 +1,446 @@
1
+ import { AnnotationFeature } from '@apollo-annotation/mst'
2
+ import {
3
+ LocationEndChange,
4
+ LocationStartChange,
5
+ } from '@apollo-annotation/shared'
6
+ import { AbstractSessionModel, revcom } from '@jbrowse/core/util'
7
+ import { Typography } from '@mui/material'
8
+ import { observer } from 'mobx-react'
9
+ import React from 'react'
10
+
11
+ import { ApolloSessionModel } from '../session'
12
+ import { CDSInfo } from './TranscriptSequence'
13
+ import { NumberTextField } from './NumberTextField'
14
+
15
+ interface ExonInfo {
16
+ min: number
17
+ max: number
18
+ }
19
+
20
+ /**
21
+ * Get single feature by featureId
22
+ * @param feature -
23
+ * @param featureId -
24
+ * @returns
25
+ */
26
+ function getFeatureFromId(
27
+ feature: AnnotationFeature,
28
+ featureId: string,
29
+ ): AnnotationFeature | undefined {
30
+ if (feature._id === featureId) {
31
+ return feature
32
+ }
33
+ // Check if there is also childFeatures in parent feature and it's not empty
34
+ // Let's get featureId from recursive method
35
+ if (!feature.children) {
36
+ return
37
+ }
38
+ for (const [, childFeature] of feature.children) {
39
+ const subFeature = getFeatureFromId(childFeature, featureId)
40
+ if (subFeature) {
41
+ return subFeature
42
+ }
43
+ }
44
+ return
45
+ }
46
+
47
+ function findExonInRange(
48
+ exons: ExonInfo[],
49
+ pairStart: number,
50
+ pairEnd: number,
51
+ ): ExonInfo | null {
52
+ for (const exon of exons) {
53
+ if (Number(exon.min) <= pairStart && Number(exon.max) >= pairEnd) {
54
+ return exon
55
+ }
56
+ }
57
+ return null
58
+ }
59
+
60
+ function removeMatchingExon(
61
+ exons: ExonInfo[],
62
+ matchStart: number,
63
+ matchEnd: number,
64
+ ): ExonInfo[] {
65
+ // Filter the array to remove elements matching the specified start and end
66
+ return exons.filter(
67
+ (exon) => !(exon.min === matchStart && exon.max === matchEnd),
68
+ )
69
+ }
70
+
71
+ export const TranscriptBasicInformation = observer(
72
+ function TranscriptBasicInformation({
73
+ assembly,
74
+ feature,
75
+ refName,
76
+ session,
77
+ }: {
78
+ feature: AnnotationFeature
79
+ session: ApolloSessionModel
80
+ assembly: string
81
+ refName: string
82
+ }) {
83
+ const { notify } = session as unknown as AbstractSessionModel
84
+ const currentAssembly = session.apolloDataStore.assemblies.get(assembly)
85
+ const refData = currentAssembly?.getByRefName(refName)
86
+ const { changeManager } = session.apolloDataStore
87
+
88
+ function handleStartChange(
89
+ newStart: number,
90
+ featureId: string,
91
+ oldStart: number,
92
+ ) {
93
+ newStart--
94
+ oldStart--
95
+ if (newStart < feature.min) {
96
+ notify('Feature start cannot be less than parent starts', 'error')
97
+ return
98
+ }
99
+ const subFeature = getFeatureFromId(feature, featureId)
100
+ if (!subFeature?.children) {
101
+ return
102
+ }
103
+ // Let's check CDS start and end values. And possibly update those too
104
+ for (const child of subFeature.children) {
105
+ if (
106
+ (child[1].type === 'CDS' || child[1].type === 'exon') &&
107
+ child[1].min === oldStart
108
+ ) {
109
+ const change = new LocationStartChange({
110
+ typeName: 'LocationStartChange',
111
+ changedIds: [child[1]._id],
112
+ featureId,
113
+ oldStart,
114
+ newStart,
115
+ assembly,
116
+ })
117
+ changeManager.submit(change).catch(() => {
118
+ notify('Error updating feature start position', 'error')
119
+ })
120
+ }
121
+ }
122
+ return
123
+ }
124
+
125
+ function handleEndChange(
126
+ newEnd: number,
127
+ featureId: string,
128
+ oldEnd: number,
129
+ ) {
130
+ const subFeature = getFeatureFromId(feature, featureId)
131
+ if (newEnd > feature.max) {
132
+ notify('Feature start cannot be greater than parent end', 'error')
133
+ return
134
+ }
135
+ if (!subFeature?.children) {
136
+ return
137
+ }
138
+ // Let's check CDS start and end values. And possibly update those too
139
+ for (const child of subFeature.children) {
140
+ if (
141
+ (child[1].type === 'CDS' || child[1].type === 'exon') &&
142
+ child[1].max === oldEnd
143
+ ) {
144
+ const change = new LocationEndChange({
145
+ typeName: 'LocationEndChange',
146
+ changedIds: [child[1]._id],
147
+ featureId,
148
+ oldEnd,
149
+ newEnd,
150
+ assembly,
151
+ })
152
+ changeManager.submit(change).catch(() => {
153
+ notify('Error updating feature end position', 'error')
154
+ })
155
+ }
156
+ }
157
+ return
158
+ }
159
+
160
+ const featureNew = feature
161
+ let exonsArray: ExonInfo[] = []
162
+ const traverse = (currentFeature: AnnotationFeature) => {
163
+ if (currentFeature.type === 'exon') {
164
+ exonsArray.push({
165
+ min: currentFeature.min + 1,
166
+ max: currentFeature.max,
167
+ })
168
+ }
169
+ if (currentFeature.children) {
170
+ for (const child of currentFeature.children) {
171
+ traverse(child[1])
172
+ }
173
+ }
174
+ }
175
+ traverse(featureNew)
176
+
177
+ const CDSresult: CDSInfo[] = []
178
+ const CDSData = featureNew.cdsLocations
179
+ if (refData) {
180
+ for (const CDSDatum of CDSData) {
181
+ for (const dataPoint of CDSDatum) {
182
+ let startSeq = refData.getSequence(
183
+ Number(dataPoint.min) - 2,
184
+ Number(dataPoint.min),
185
+ )
186
+ let endSeq = refData.getSequence(
187
+ Number(dataPoint.max),
188
+ Number(dataPoint.max) + 2,
189
+ )
190
+
191
+ if (featureNew.strand === -1 && startSeq && endSeq) {
192
+ startSeq = revcom(startSeq)
193
+ endSeq = revcom(endSeq)
194
+ }
195
+ const oneCDS: CDSInfo = {
196
+ id: featureNew._id,
197
+ type: 'CDS',
198
+ strand: Number(featureNew.strand),
199
+ min: dataPoint.min + 1,
200
+ max: dataPoint.max,
201
+ oldMin: dataPoint.min + 1,
202
+ oldMax: dataPoint.max,
203
+ startSeq,
204
+ endSeq,
205
+ }
206
+ // CDSresult.push(oneCDS)
207
+ // Check if there is already an object with the same start and end
208
+ const exists = CDSresult.some(
209
+ (obj) =>
210
+ obj.min === oneCDS.min &&
211
+ obj.max === oneCDS.max &&
212
+ obj.type === oneCDS.type,
213
+ )
214
+
215
+ // If no such object exists, add the new object to the array
216
+ if (!exists) {
217
+ CDSresult.push(oneCDS)
218
+ }
219
+
220
+ // Add possible UTRs
221
+ const foundExon = findExonInRange(
222
+ exonsArray,
223
+ dataPoint.min + 1,
224
+ dataPoint.max,
225
+ )
226
+ if (foundExon && Number(foundExon.min) < dataPoint.min) {
227
+ if (feature.strand === 1) {
228
+ const oneCDS: CDSInfo = {
229
+ id: feature._id,
230
+ type: 'five_prime_UTR',
231
+ strand: Number(feature.strand),
232
+ min: foundExon.min,
233
+ max: dataPoint.min,
234
+ oldMin: foundExon.min,
235
+ oldMax: dataPoint.min,
236
+ startSeq: '',
237
+ endSeq: '',
238
+ }
239
+ CDSresult.push(oneCDS)
240
+ } else {
241
+ const oneCDS: CDSInfo = {
242
+ id: feature._id,
243
+ type: 'three_prime_UTR',
244
+ strand: Number(feature.strand),
245
+ min: dataPoint.min + 1,
246
+ max: foundExon.min + 1,
247
+ oldMin: dataPoint.min + 1,
248
+ oldMax: foundExon.min + 1,
249
+ startSeq: '',
250
+ endSeq: '',
251
+ }
252
+ CDSresult.push(oneCDS)
253
+ }
254
+ exonsArray = removeMatchingExon(
255
+ exonsArray,
256
+ foundExon.min,
257
+ foundExon.max,
258
+ )
259
+ }
260
+ if (foundExon && Number(foundExon.max) > dataPoint.max) {
261
+ if (feature.strand === 1) {
262
+ const oneCDS: CDSInfo = {
263
+ id: feature._id,
264
+ type: 'three_prime_UTR',
265
+ strand: Number(feature.strand),
266
+ min: dataPoint.max + 1,
267
+ max: foundExon.max,
268
+ oldMin: dataPoint.max + 1,
269
+ oldMax: foundExon.max,
270
+ startSeq: '',
271
+ endSeq: '',
272
+ }
273
+ CDSresult.push(oneCDS)
274
+ } else {
275
+ const oneCDS: CDSInfo = {
276
+ id: feature._id,
277
+ type: 'five_prime_UTR',
278
+ strand: Number(feature.strand),
279
+ min: dataPoint.min + 1,
280
+ max: foundExon.max,
281
+ oldMin: dataPoint.min + 1,
282
+ oldMax: foundExon.max,
283
+ startSeq: '',
284
+ endSeq: '',
285
+ }
286
+ CDSresult.push(oneCDS)
287
+ }
288
+ exonsArray = removeMatchingExon(
289
+ exonsArray,
290
+ foundExon.min,
291
+ foundExon.max,
292
+ )
293
+ }
294
+ if (
295
+ dataPoint.min + 1 === foundExon?.min &&
296
+ dataPoint.max === foundExon.max
297
+ ) {
298
+ exonsArray = removeMatchingExon(
299
+ exonsArray,
300
+ foundExon.min,
301
+ foundExon.max,
302
+ )
303
+ }
304
+ }
305
+ }
306
+ }
307
+
308
+ // Add remaining UTRs if any
309
+ if (exonsArray.length > 0) {
310
+ // eslint-disable-next-line unicorn/no-array-for-each
311
+ exonsArray.forEach((element: ExonInfo) => {
312
+ if (featureNew.strand === 1) {
313
+ const oneCDS: CDSInfo = {
314
+ id: featureNew._id,
315
+ type: 'five_prime_UTR',
316
+ strand: Number(featureNew.strand),
317
+ min: element.min + 1,
318
+ max: element.max,
319
+ oldMin: element.min + 1,
320
+ oldMax: element.max,
321
+ startSeq: '',
322
+ endSeq: '',
323
+ }
324
+ CDSresult.push(oneCDS)
325
+ } else {
326
+ const oneCDS: CDSInfo = {
327
+ id: featureNew._id,
328
+ type: 'three_prime_UTR',
329
+ strand: Number(featureNew.strand),
330
+ min: element.min + 1,
331
+ max: element.max + 1,
332
+ oldMin: element.min + 1,
333
+ oldMax: element.max + 1,
334
+ startSeq: '',
335
+ endSeq: '',
336
+ }
337
+ CDSresult.push(oneCDS)
338
+ }
339
+ exonsArray = removeMatchingExon(exonsArray, element.min, element.max)
340
+ })
341
+ }
342
+
343
+ CDSresult.sort((a, b) => {
344
+ // Primary sorting by 'start' property
345
+ const startDifference = Number(a.min) - Number(b.min)
346
+ if (startDifference !== 0) {
347
+ return startDifference
348
+ }
349
+ return Number(a.max) - Number(b.max)
350
+ })
351
+ if (CDSresult.length > 0) {
352
+ CDSresult[0].startSeq = ''
353
+
354
+ // eslint-disable-next-line unicorn/prefer-at
355
+ CDSresult[CDSresult.length - 1].endSeq = ''
356
+
357
+ // Loop through the array and clear "startSeq" or "endSeq" based on the conditions
358
+ for (let i = 0; i < CDSresult.length; i++) {
359
+ if (i > 0 && CDSresult[i].min === CDSresult[i - 1].max) {
360
+ // Clear "startSeq" if the current item's "start" is equal to the previous item's "end"
361
+ CDSresult[i].startSeq = ''
362
+ }
363
+ if (
364
+ i < CDSresult.length - 1 &&
365
+ CDSresult[i].max === CDSresult[i + 1].min
366
+ ) {
367
+ // Clear "endSeq" if the next item's "start" is equal to the current item's "end"
368
+ CDSresult[i].endSeq = ''
369
+ }
370
+ }
371
+ }
372
+
373
+ const transcriptItems = CDSresult
374
+
375
+ return (
376
+ <>
377
+ <Typography
378
+ variant="h5"
379
+ style={{ marginLeft: '15px', marginBottom: '0' }}
380
+ >
381
+ CDS and UTRs
382
+ </Typography>
383
+ <div>
384
+ {transcriptItems.map((item, index) => (
385
+ <div key={index} style={{ display: 'flex', alignItems: 'center' }}>
386
+ <span style={{ marginLeft: '20px', width: '50px' }}>
387
+ {item.type === 'three_prime_UTR'
388
+ ? '3 UTR'
389
+ : // eslint-disable-next-line unicorn/no-nested-ternary
390
+ item.type === 'five_prime_UTR'
391
+ ? '5 UTR'
392
+ : 'CDS'}
393
+ </span>
394
+ <span style={{ fontWeight: 'bold', width: '30px' }}>
395
+ {item.startSeq}
396
+ </span>
397
+ <NumberTextField
398
+ margin="dense"
399
+ id={item.id}
400
+ disabled={item.type !== 'CDS'}
401
+ style={{
402
+ width: '150px',
403
+ marginLeft: '8px',
404
+ backgroundColor:
405
+ item.startSeq.trim() === '' && index !== 0
406
+ ? 'lightblue'
407
+ : 'inherit',
408
+ }}
409
+ variant="outlined"
410
+ value={item.min}
411
+ onChangeCommitted={(newStart: number) => {
412
+ handleStartChange(newStart, item.id, Number(item.oldMin))
413
+ }}
414
+ />
415
+ <span style={{ margin: '0 10px' }}>
416
+ {/* eslint-disable-next-line unicorn/no-nested-ternary */}
417
+ {item.strand === -1 ? '-' : item.strand === 1 ? '+' : ''}
418
+ </span>
419
+ <NumberTextField
420
+ margin="dense"
421
+ id={item.id}
422
+ disabled={item.type !== 'CDS'}
423
+ style={{
424
+ width: '150px',
425
+ backgroundColor:
426
+ item.endSeq.trim() === '' &&
427
+ index + 1 !== transcriptItems.length
428
+ ? 'lightblue'
429
+ : 'inherit',
430
+ }}
431
+ variant="outlined"
432
+ value={item.max}
433
+ onChangeCommitted={(newEnd: number) => {
434
+ handleEndChange(newEnd, item.id, Number(item.oldMax))
435
+ }}
436
+ />
437
+ <span style={{ marginLeft: '8px', fontWeight: 'bold' }}>
438
+ {item.endSeq}
439
+ </span>
440
+ </div>
441
+ ))}
442
+ </div>
443
+ </>
444
+ )
445
+ },
446
+ )