@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
@@ -0,0 +1,481 @@
1
+ /* eslint-disable @typescript-eslint/no-unnecessary-condition */
2
+ import type PluginManager from '@jbrowse/core/PluginManager'
3
+ import { type AnyConfigurationSchemaType } from '@jbrowse/core/configuration/configurationSchema'
4
+ import {
5
+ type Frame,
6
+ defaultCodonTable,
7
+ getFrame,
8
+ revcom,
9
+ } from '@jbrowse/core/util'
10
+ import { type Theme, createTheme } from '@mui/material'
11
+ import { autorun } from 'mobx'
12
+ import { type Instance, addDisposer } from 'mobx-state-tree'
13
+
14
+ import { type ApolloSessionModel } from '../../session'
15
+
16
+ import { baseModelFactory } from './base'
17
+
18
+ function colorCode(letter: string, theme: Theme) {
19
+ return (
20
+ theme.palette.bases[
21
+ letter.toUpperCase() as keyof Theme['palette']['bases']
22
+ ].main.toString() ?? 'lightgray'
23
+ )
24
+ }
25
+
26
+ function codonColorCode(letter: string, highContrast?: boolean) {
27
+ const colorMap: Record<string, string | undefined> = {
28
+ M: '#33ee33',
29
+ '*': highContrast ? '#000000' : '#f44336',
30
+ }
31
+
32
+ return colorMap[letter.toUpperCase()]
33
+ }
34
+
35
+ function reverseCodonSeq(seq: string): string {
36
+ // disable because sequence is all ascii
37
+ // eslint-disable-next-line @typescript-eslint/no-misused-spread
38
+ return [...seq]
39
+ .map((c) => revcom(c))
40
+ .reverse()
41
+ .join('')
42
+ }
43
+
44
+ function drawLetter(
45
+ seqTrackctx: CanvasRenderingContext2D,
46
+ startPx: number,
47
+ widthPx: number,
48
+ letter: string,
49
+ textY: number,
50
+ ) {
51
+ const fontSize = Math.min(widthPx, 10)
52
+ seqTrackctx.fillStyle = '#000'
53
+ seqTrackctx.font = `${fontSize}px`
54
+ const textWidth = seqTrackctx.measureText(letter).width
55
+ const textX = startPx + (widthPx - textWidth) / 2
56
+ seqTrackctx.fillText(letter, textX, textY + 10)
57
+ }
58
+
59
+ function drawTranslation(
60
+ seqTrackctx: CanvasRenderingContext2D,
61
+ bpPerPx: number,
62
+ trnslStartPx: number,
63
+ trnslY: number,
64
+ trnslWidthPx: number,
65
+ sequenceRowHeight: number,
66
+ seq: string,
67
+ i: number,
68
+ reverse: boolean,
69
+ showStartCodons: boolean,
70
+ showStopCodons: boolean,
71
+ highContrast: boolean,
72
+ ) {
73
+ let codonSeq: string = seq.slice(i, i + 3).toUpperCase()
74
+ if (reverse) {
75
+ codonSeq = reverseCodonSeq(codonSeq)
76
+ }
77
+ const codonLetter =
78
+ defaultCodonTable[codonSeq as keyof typeof defaultCodonTable]
79
+ if (!codonLetter) {
80
+ return
81
+ }
82
+ const fillColor = codonColorCode(codonLetter, highContrast)
83
+ if (
84
+ fillColor &&
85
+ ((showStopCodons && codonLetter == '*') ||
86
+ (showStartCodons && codonLetter != '*'))
87
+ ) {
88
+ seqTrackctx.fillStyle = fillColor
89
+ seqTrackctx.fillRect(trnslStartPx, trnslY, trnslWidthPx, sequenceRowHeight)
90
+ }
91
+ if (bpPerPx <= 0.1) {
92
+ seqTrackctx.rect(trnslStartPx, trnslY, trnslWidthPx, sequenceRowHeight)
93
+ seqTrackctx.stroke()
94
+ drawLetter(seqTrackctx, trnslStartPx, trnslWidthPx, codonLetter, trnslY)
95
+ }
96
+ }
97
+
98
+ function getTranslationRow(frame: Frame, bpPerPx: number) {
99
+ const offset = bpPerPx <= 1 ? 2 : 0
100
+ switch (frame) {
101
+ case 3: {
102
+ return 0
103
+ }
104
+ case 2: {
105
+ return 1
106
+ }
107
+ case 1: {
108
+ return 2
109
+ }
110
+ case -1: {
111
+ return 3 + offset
112
+ }
113
+ case -2: {
114
+ return 4 + offset
115
+ }
116
+ case -3: {
117
+ return 5 + offset
118
+ }
119
+ }
120
+ }
121
+
122
+ function getSeqRow(
123
+ strand: 1 | -1 | undefined,
124
+ bpPerPx: number,
125
+ ): number | undefined {
126
+ if (bpPerPx > 1 || strand === undefined) {
127
+ return
128
+ }
129
+ return strand === 1 ? 3 : 4
130
+ }
131
+
132
+ function highlightSeq(
133
+ seqTrackOverlayctx: CanvasRenderingContext2D,
134
+ theme: Theme,
135
+ startPx: number,
136
+ sequenceRowHeight: number,
137
+ row: number | undefined,
138
+ widthPx: number,
139
+ ) {
140
+ if (row !== undefined) {
141
+ seqTrackOverlayctx.fillStyle = theme.palette.action.focus
142
+ seqTrackOverlayctx.fillRect(
143
+ startPx,
144
+ sequenceRowHeight * row,
145
+ widthPx,
146
+ sequenceRowHeight,
147
+ )
148
+ }
149
+ }
150
+
151
+ export function renderingModelFactory(
152
+ pluginManager: PluginManager,
153
+ configSchema: AnyConfigurationSchemaType,
154
+ ) {
155
+ const BaseLinearApolloReferenceSequenceDisplay = baseModelFactory(
156
+ pluginManager,
157
+ configSchema,
158
+ )
159
+
160
+ return BaseLinearApolloReferenceSequenceDisplay.named(
161
+ 'LinearApolloReferenceSequenceDisplayRendering',
162
+ )
163
+ .volatile(() => ({
164
+ seqTrackCanvas: null as HTMLCanvasElement | null,
165
+ seqTrackOverlayCanvas: null as HTMLCanvasElement | null,
166
+ theme: createTheme(),
167
+ }))
168
+ .actions((self) => ({
169
+ setSeqTrackCanvas(canvas: HTMLCanvasElement | null) {
170
+ self.seqTrackCanvas = canvas
171
+ },
172
+ setSeqTrackOverlayCanvas(canvas: HTMLCanvasElement | null) {
173
+ self.seqTrackOverlayCanvas = canvas
174
+ },
175
+ setTheme(theme: Theme) {
176
+ self.theme = theme
177
+ },
178
+ afterAttach() {
179
+ addDisposer(
180
+ self,
181
+ autorun(
182
+ () => {
183
+ const { theme } = self
184
+ if (!self.lgv.initialized || self.regionCannotBeRendered()) {
185
+ return
186
+ }
187
+ const trnslWidthPx = 3 / self.lgv.bpPerPx
188
+ if (trnslWidthPx < 1) {
189
+ return
190
+ }
191
+ const seqTrackctx = self.seqTrackCanvas?.getContext('2d')
192
+ if (!seqTrackctx) {
193
+ return
194
+ }
195
+
196
+ seqTrackctx.clearRect(
197
+ 0,
198
+ 0,
199
+ self.lgv.dynamicBlocks.totalWidthPx,
200
+ self.height,
201
+ )
202
+ const frames =
203
+ self.lgv.bpPerPx <= 1
204
+ ? [3, 2, 1, 0, 0, -1, -2, -3]
205
+ : [3, 2, 1, -1, -2, -3]
206
+ let height = 0
207
+ if (theme) {
208
+ for (const frame of frames) {
209
+ let frameColor = theme.palette.framesCDS.at(frame)?.main
210
+ if (frameColor) {
211
+ let offsetPx = 0
212
+ if (self.highContrast) {
213
+ frameColor = 'white'
214
+ offsetPx = 1
215
+ // eslint-disable-next-line prefer-destructuring
216
+ seqTrackctx.fillStyle = theme.palette.grey[200]
217
+ seqTrackctx.fillRect(
218
+ 0,
219
+ height,
220
+ self.lgv.dynamicBlocks.totalWidthPx,
221
+ self.sequenceRowHeight,
222
+ )
223
+ }
224
+ seqTrackctx.fillStyle = frameColor
225
+ seqTrackctx.fillRect(
226
+ 0 + offsetPx,
227
+ height + offsetPx,
228
+ self.lgv.dynamicBlocks.totalWidthPx - 2 * offsetPx,
229
+ self.sequenceRowHeight - 2 * offsetPx,
230
+ )
231
+ }
232
+ height += self.sequenceRowHeight
233
+ }
234
+ }
235
+
236
+ for (const [idx, region] of self.regions.entries()) {
237
+ const { apolloDataStore } =
238
+ self.session as unknown as ApolloSessionModel
239
+ const assembly = apolloDataStore.assemblies.get(
240
+ region.assemblyName,
241
+ )
242
+ const ref = assembly?.getByRefName(region.refName)
243
+ const seq = ref?.getSequence(region.start, region.end)
244
+ if (!seq) {
245
+ return
246
+ }
247
+ // disable because sequence is all ascii
248
+ // eslint-disable-next-line @typescript-eslint/no-misused-spread
249
+ for (const [i, letter] of [...seq].entries()) {
250
+ const trnslXOffset =
251
+ (self.lgv.bpToPx({
252
+ refName: region.refName,
253
+ coord: region.start + i,
254
+ regionNumber: idx,
255
+ })?.offsetPx ?? 0) - self.lgv.offsetPx
256
+ const trnslStartPx = self.lgv.displayedRegions[idx].reversed
257
+ ? trnslXOffset - trnslWidthPx
258
+ : trnslXOffset
259
+
260
+ // Draw translation forward
261
+ for (let j = 2; j >= 0; j--) {
262
+ if ((region.start + i) % 3 === j) {
263
+ drawTranslation(
264
+ seqTrackctx,
265
+ self.lgv.bpPerPx,
266
+ trnslStartPx,
267
+ self.sequenceRowHeight * (2 - j),
268
+ trnslWidthPx,
269
+ self.sequenceRowHeight,
270
+ seq,
271
+ i,
272
+ false,
273
+ self.showStartCodons,
274
+ self.showStopCodons,
275
+ self.highContrast,
276
+ )
277
+ }
278
+ }
279
+
280
+ if (self.lgv.bpPerPx <= 1) {
281
+ const xOffset =
282
+ (self.lgv.bpToPx({
283
+ refName: region.refName,
284
+ coord: region.start + i,
285
+ regionNumber: idx,
286
+ })?.offsetPx ?? 0) - self.lgv.offsetPx
287
+ const widthPx = 1 / self.lgv.bpPerPx
288
+ const startPx = self.lgv.displayedRegions[idx].reversed
289
+ ? xOffset - widthPx
290
+ : xOffset
291
+
292
+ // Draw forward
293
+ seqTrackctx.beginPath()
294
+ seqTrackctx.fillStyle = colorCode(letter, self.theme)
295
+ seqTrackctx.rect(
296
+ startPx,
297
+ self.sequenceRowHeight * 3,
298
+ widthPx,
299
+ self.sequenceRowHeight,
300
+ )
301
+ seqTrackctx.fill()
302
+ if (self.lgv.bpPerPx <= 0.1) {
303
+ seqTrackctx.stroke()
304
+ drawLetter(
305
+ seqTrackctx,
306
+ startPx,
307
+ widthPx,
308
+ letter,
309
+ self.sequenceRowHeight * 3,
310
+ )
311
+ }
312
+
313
+ // Draw reverse
314
+ const revLetter = revcom(letter)
315
+ seqTrackctx.beginPath()
316
+ seqTrackctx.fillStyle = colorCode(revLetter, self.theme)
317
+ seqTrackctx.rect(
318
+ startPx,
319
+ self.sequenceRowHeight * 4,
320
+ widthPx,
321
+ self.sequenceRowHeight,
322
+ )
323
+ seqTrackctx.fill()
324
+ if (self.lgv.bpPerPx <= 0.1) {
325
+ seqTrackctx.stroke()
326
+ drawLetter(
327
+ seqTrackctx,
328
+ startPx,
329
+ widthPx,
330
+ revLetter,
331
+ self.sequenceRowHeight * 4,
332
+ )
333
+ }
334
+ }
335
+
336
+ // Draw translation reverse
337
+ for (let k = 0; k <= 2; k++) {
338
+ const rowOffset = self.lgv.bpPerPx <= 1 ? 5 : 3
339
+ if ((region.start + i) % 3 === k) {
340
+ drawTranslation(
341
+ seqTrackctx,
342
+ self.lgv.bpPerPx,
343
+ trnslStartPx,
344
+ self.sequenceRowHeight * (rowOffset + k),
345
+ trnslWidthPx,
346
+ self.sequenceRowHeight,
347
+ seq,
348
+ i,
349
+ true,
350
+ self.showStartCodons,
351
+ self.showStopCodons,
352
+ self.highContrast,
353
+ )
354
+ }
355
+ }
356
+ }
357
+ }
358
+ },
359
+ { name: 'LinearApolloReferenceSequenceDisplayRenderSequence' },
360
+ ),
361
+ )
362
+ addDisposer(
363
+ self,
364
+ autorun(
365
+ () => {
366
+ if (!self.lgv.initialized || self.regionCannotBeRendered()) {
367
+ return
368
+ }
369
+ const seqTrackOverlayctx =
370
+ self.seqTrackOverlayCanvas?.getContext('2d')
371
+ if (!seqTrackOverlayctx) {
372
+ return
373
+ }
374
+
375
+ seqTrackOverlayctx.clearRect(
376
+ 0,
377
+ 0,
378
+ self.lgv.dynamicBlocks.totalWidthPx,
379
+ self.height,
380
+ )
381
+
382
+ const {
383
+ hoveredFeature,
384
+ lgv,
385
+ regions,
386
+ sequenceRowHeight,
387
+ session,
388
+ theme,
389
+ } = self
390
+
391
+ if (!hoveredFeature) {
392
+ return
393
+ }
394
+ const { feature } = hoveredFeature
395
+
396
+ const { featureTypeOntology } =
397
+ session.apolloDataStore.ontologyManager
398
+ if (!featureTypeOntology) {
399
+ throw new Error('featureTypeOntology is undefined')
400
+ }
401
+ for (const [idx, region] of regions.entries()) {
402
+ if (featureTypeOntology.isTypeOf(feature.type, 'CDS')) {
403
+ const parentFeature = feature.parent
404
+ if (!parentFeature) {
405
+ continue
406
+ }
407
+ const cdsLocs = parentFeature.cdsLocations.find(
408
+ (loc) =>
409
+ feature.min === loc.at(0)?.min &&
410
+ feature.max === loc.at(-1)?.max,
411
+ )
412
+ if (!cdsLocs) {
413
+ continue
414
+ }
415
+ for (const dl of cdsLocs) {
416
+ const frame = getFrame(
417
+ dl.min,
418
+ dl.max,
419
+ feature.strand ?? 1,
420
+ dl.phase,
421
+ )
422
+ const row = getTranslationRow(frame, lgv.bpPerPx)
423
+ const offset =
424
+ (lgv.bpToPx({
425
+ refName: region.refName,
426
+ coord: dl.min,
427
+ regionNumber: idx,
428
+ })?.offsetPx ?? 0) - lgv.offsetPx
429
+ const widthPx = (dl.max - dl.min) / lgv.bpPerPx
430
+ const startPx = lgv.displayedRegions[idx].reversed
431
+ ? offset - widthPx
432
+ : offset
433
+
434
+ highlightSeq(
435
+ seqTrackOverlayctx,
436
+ theme,
437
+ startPx,
438
+ sequenceRowHeight,
439
+ row,
440
+ widthPx,
441
+ )
442
+ }
443
+ } else {
444
+ const row = getSeqRow(feature.strand, lgv.bpPerPx)
445
+ const offset =
446
+ (lgv.bpToPx({
447
+ refName: region.refName,
448
+ coord: feature.min,
449
+ regionNumber: idx,
450
+ })?.offsetPx ?? 0) - lgv.offsetPx
451
+ const widthPx = feature.length / lgv.bpPerPx
452
+ const startPx = lgv.displayedRegions[idx].reversed
453
+ ? offset - widthPx
454
+ : offset
455
+
456
+ highlightSeq(
457
+ seqTrackOverlayctx,
458
+ theme,
459
+ startPx,
460
+ sequenceRowHeight,
461
+ row,
462
+ widthPx,
463
+ )
464
+ }
465
+ }
466
+ },
467
+ { name: 'LinearApolloDisplayRenderSeqHighlight' },
468
+ ),
469
+ )
470
+ },
471
+ }))
472
+ }
473
+
474
+ export type LinearApolloReferenceSequenceDisplayRenderingModel = ReturnType<
475
+ typeof renderingModelFactory
476
+ >
477
+ // eslint disable because of
478
+ // https://mobx-state-tree.js.org/tips/typescript#using-a-mst-type-at-design-time
479
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
480
+ export interface LinearApolloReferenceSequenceDisplayRendering
481
+ extends Instance<LinearApolloReferenceSequenceDisplayRenderingModel> {}