@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
@@ -1,716 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-unsafe-member-access */
2
- /* eslint-disable @typescript-eslint/no-unsafe-argument */
3
- /* eslint-disable @typescript-eslint/no-unsafe-assignment */
4
- /* eslint-disable @typescript-eslint/no-unsafe-call */
5
- /* eslint-disable @typescript-eslint/unbound-method */
6
- /* eslint-disable @typescript-eslint/no-unnecessary-condition */
7
- import { AnnotationFeatureI } from '@apollo-annotation/mst'
8
- import {
9
- LocationEndChange,
10
- LocationStartChange,
11
- } from '@apollo-annotation/shared'
12
- import { alpha } from '@mui/material'
13
-
14
- import { LinearApolloDisplay } from '../stateModel'
15
- import { MousePosition } from '../stateModel/mouseEvents'
16
- import { frameColors, getFrame } from '../stateModel/rendering'
17
- import { CanvasMouseEvent } from '../types'
18
- import { Glyph } from './Glyph'
19
-
20
- let forwardFill: CanvasPattern | null = null
21
- let backwardFill: CanvasPattern | null = null
22
- if ('document' in window) {
23
- for (const direction of ['forward', 'backward']) {
24
- const canvas = document.createElement('canvas')
25
- const canvasSize = 10
26
- canvas.width = canvas.height = canvasSize
27
- const ctx = canvas.getContext('2d')
28
- if (ctx) {
29
- const stripeColor1 = 'rgba(0,0,0,0)'
30
- const stripeColor2 = 'rgba(255,255,255,0.25)'
31
- const gradient =
32
- direction === 'forward'
33
- ? ctx.createLinearGradient(0, canvasSize, canvasSize, 0)
34
- : ctx.createLinearGradient(0, 0, canvasSize, canvasSize)
35
- gradient.addColorStop(0, stripeColor1)
36
- gradient.addColorStop(0.25, stripeColor1)
37
- gradient.addColorStop(0.25, stripeColor2)
38
- gradient.addColorStop(0.5, stripeColor2)
39
- gradient.addColorStop(0.5, stripeColor1)
40
- gradient.addColorStop(0.75, stripeColor1)
41
- gradient.addColorStop(0.75, stripeColor2)
42
- gradient.addColorStop(1, stripeColor2)
43
- ctx.fillStyle = gradient
44
- ctx.fillRect(0, 0, 10, 10)
45
- if (direction === 'forward') {
46
- forwardFill = ctx.createPattern(canvas, 'repeat')
47
- } else {
48
- backwardFill = ctx.createPattern(canvas, 'repeat')
49
- }
50
- }
51
- }
52
- }
53
-
54
- export class ImplicitExonGeneGlyph extends Glyph {
55
- featuresForRow(feature: AnnotationFeatureI): AnnotationFeatureI[][] {
56
- const features: AnnotationFeatureI[][] = []
57
- for (const [, child] of feature.children ?? new Map()) {
58
- const childFeatures: AnnotationFeatureI[] = []
59
- for (const [, annotationFeature] of child.children ?? new Map()) {
60
- childFeatures.push(annotationFeature)
61
- }
62
- childFeatures.push(child)
63
- features.push(childFeatures)
64
- }
65
- return features
66
- }
67
-
68
- getRowCount(feature: AnnotationFeatureI): number {
69
- let mrnaCount = 0
70
- for (const [, child] of feature.children ?? new Map()) {
71
- if (child.type === 'mRNA') {
72
- mrnaCount += 1
73
- }
74
- }
75
- return mrnaCount
76
- }
77
-
78
- draw(
79
- stateModel: LinearApolloDisplay,
80
- ctx: CanvasRenderingContext2D,
81
- feature: AnnotationFeatureI,
82
- xOffset: number,
83
- row: number,
84
- reversed?: boolean,
85
- ): void {
86
- const { apolloRowHeight, lgv, session, theme } = stateModel
87
- const { bpPerPx } = lgv
88
- const rowHeight = apolloRowHeight
89
- const utrHeight = Math.round(0.6 * rowHeight)
90
- const cdsHeight = Math.round(0.9 * rowHeight)
91
- const { _id, children, min, strand } = feature
92
- const { apolloSelectedFeature } = session
93
- let currentMRNA = 0
94
- for (const [, mrna] of children ?? new Map()) {
95
- if (mrna.type !== 'mRNA') {
96
- continue
97
- }
98
- const offsetPx = (mrna.start - min) / bpPerPx
99
- const widthPx = mrna.length / bpPerPx
100
- const startPx = reversed
101
- ? xOffset - offsetPx - widthPx
102
- : xOffset + offsetPx
103
- const height =
104
- Math.round((currentMRNA + 1 / 2) * rowHeight) + row * rowHeight
105
- ctx.strokeStyle = theme?.palette.text.primary ?? 'black'
106
- ctx.beginPath()
107
- ctx.moveTo(startPx, height)
108
- ctx.lineTo(startPx + widthPx, height)
109
- ctx.stroke()
110
- currentMRNA += 1
111
- }
112
- currentMRNA = 0
113
- for (const [, mrna] of children ?? new Map()) {
114
- if (mrna.type !== 'mRNA') {
115
- continue
116
- }
117
- const cdsCount = [...(mrna.children ?? [])].filter(
118
- ([, exonOrCDS]) => exonOrCDS.type === 'CDS',
119
- ).length
120
- for (let count = 0; count < cdsCount; count++) {
121
- for (const [, cdsOrUTR] of mrna.children ?? new Map()) {
122
- const isCDS = cdsOrUTR.type === 'CDS'
123
- const isUTR = cdsOrUTR.type.endsWith('UTR')
124
- if (!(isCDS || isUTR)) {
125
- continue
126
- }
127
- const offsetPx = (cdsOrUTR.start - min) / bpPerPx
128
- const widthPx = cdsOrUTR.length / bpPerPx
129
- const startPx = reversed
130
- ? xOffset - offsetPx - widthPx
131
- : xOffset + offsetPx
132
- ctx.fillStyle = theme?.palette.text.primary ?? 'black'
133
- const top = (row + currentMRNA) * rowHeight
134
- const height = isCDS ? cdsHeight : utrHeight
135
- const cdsOrUTRTop = top + (rowHeight - height) / 2
136
- ctx.fillRect(startPx, cdsOrUTRTop, widthPx, height)
137
- if (widthPx > 2) {
138
- ctx.clearRect(startPx + 1, cdsOrUTRTop + 1, widthPx - 2, height - 2)
139
- let colorCode = 'rgb(211,211,211)'
140
- if (isCDS) {
141
- const frame = getFrame(
142
- cdsOrUTR.start,
143
- cdsOrUTR.end,
144
- cdsOrUTR.strand,
145
- cdsOrUTR.phase,
146
- )
147
- const color = frameColors.at(frame)
148
- colorCode = color ?? 'rgb(171,71,188)'
149
- }
150
- ctx.fillStyle =
151
- apolloSelectedFeature &&
152
- cdsOrUTR._id === apolloSelectedFeature._id
153
- ? 'rgb(0,0,0)'
154
- : colorCode
155
- ctx.fillRect(startPx + 1, cdsOrUTRTop + 1, widthPx - 2, height - 2)
156
- if (forwardFill && backwardFill && strand) {
157
- const reversal = reversed ? -1 : 1
158
- const [topFill, bottomFill] =
159
- strand * reversal === 1
160
- ? [forwardFill, backwardFill]
161
- : [backwardFill, forwardFill]
162
- ctx.fillStyle = topFill
163
- ctx.fillRect(
164
- startPx + 1,
165
- cdsOrUTRTop + 1,
166
- widthPx - 2,
167
- (height - 2) / 2,
168
- )
169
- ctx.fillStyle = bottomFill
170
- ctx.fillRect(
171
- startPx + 1,
172
- cdsOrUTRTop + 1 + (height - 2) / 2,
173
- widthPx - 2,
174
- (height - 2) / 2,
175
- )
176
- }
177
- }
178
- }
179
- }
180
- currentMRNA += 1
181
- }
182
-
183
- if (apolloSelectedFeature) {
184
- if (_id === apolloSelectedFeature._id) {
185
- const widthPx = feature.length / bpPerPx
186
- const startPx = reversed ? xOffset - widthPx : xOffset
187
- const top = row * rowHeight
188
- const height = this.getRowCount(feature) * rowHeight
189
- ctx.fillStyle = theme?.palette.action.selected ?? 'rgba(0,0,0,0.08)'
190
- ctx.fillRect(startPx, top, widthPx, height)
191
- } else {
192
- let featureEntry: AnnotationFeatureI | undefined
193
- let featureRow: number | undefined
194
- let i = 0
195
- for (const [, f] of children ?? new Map()) {
196
- if (f._id === apolloSelectedFeature._id) {
197
- featureEntry = f
198
- featureRow = i
199
- }
200
- i++
201
- }
202
-
203
- if (featureEntry === undefined || featureRow === undefined) {
204
- return
205
- }
206
- const widthPx = featureEntry.length / bpPerPx
207
- const offsetPx = (featureEntry.start - min) / bpPerPx
208
- const startPx = reversed ? xOffset - widthPx : xOffset + offsetPx
209
- const top = (row + featureRow) * rowHeight
210
- ctx.fillStyle = theme?.palette.action.selected ?? 'rgba(0,0,0,08)'
211
- ctx.fillRect(startPx, top, widthPx, rowHeight)
212
- }
213
- }
214
- }
215
-
216
- drawHover(stateModel: LinearApolloDisplay, ctx: CanvasRenderingContext2D) {
217
- const { apolloHover, apolloRowHeight, displayedRegions, lgv, theme } =
218
- stateModel
219
- if (!apolloHover) {
220
- return
221
- }
222
- const { feature, mousePosition } = apolloHover
223
- if (!(feature && mousePosition)) {
224
- return
225
- }
226
- const { regionNumber, y } = mousePosition
227
- const { bpPerPx, bpToPx, offsetPx } = lgv
228
- const rowHeight = apolloRowHeight
229
- const rowNumber = Math.floor(y / rowHeight)
230
- const displayedRegion = displayedRegions[regionNumber]
231
- const { refName, reversed } = displayedRegion
232
- const startPx =
233
- (bpToPx({
234
- refName,
235
- coord: reversed ? feature.end : feature.start,
236
- regionNumber,
237
- })?.offsetPx ?? 0) - offsetPx
238
- const top = rowNumber * rowHeight
239
- const widthPx = feature.length / bpPerPx
240
- ctx.fillStyle = theme?.palette.action.focus ?? 'rgba(0,0,0,0.04)'
241
- ctx.fillRect(startPx, top, widthPx, rowHeight)
242
- }
243
-
244
- drawDragPreview(
245
- stateModel: LinearApolloDisplay,
246
- overlayCtx: CanvasRenderingContext2D,
247
- ) {
248
- const { apolloDragging, apolloRowHeight, displayedRegions, lgv, theme } =
249
- stateModel
250
- const { bpPerPx, offsetPx } = lgv
251
- if (!apolloDragging) {
252
- return
253
- }
254
- const {
255
- feature,
256
- glyph,
257
- mousePosition: startingMousePosition,
258
- } = apolloDragging.start
259
- if (!feature) {
260
- throw new Error('no feature for drag preview??')
261
- }
262
- if (glyph !== this) {
263
- throw new Error('drawDragPreview() called on wrong glyph?')
264
- }
265
- const { mousePosition: currentMousePosition } = apolloDragging.current
266
- const edge = this.isMouseOnFeatureEdge(
267
- startingMousePosition,
268
- feature,
269
- stateModel,
270
- )
271
- if (!edge) {
272
- return
273
- }
274
-
275
- const row = Math.floor(startingMousePosition.y / apolloRowHeight)
276
- const region = displayedRegions[startingMousePosition.regionNumber]
277
- const rowCount = 1
278
-
279
- const featureEdgeBp = region.reversed
280
- ? region.end - feature[edge]
281
- : feature[edge] - region.start
282
- const featureEdgePx = featureEdgeBp / bpPerPx - offsetPx
283
-
284
- const rectX = Math.min(currentMousePosition.x, featureEdgePx)
285
- const rectY = row * apolloRowHeight
286
- const rectWidth = Math.abs(currentMousePosition.x - featureEdgePx)
287
- const rectHeight = apolloRowHeight * rowCount
288
-
289
- overlayCtx.strokeStyle = theme?.palette.info.main ?? 'rgb(255,0,0)'
290
- overlayCtx.setLineDash([6])
291
- overlayCtx.strokeRect(rectX, rectY, rectWidth, rectHeight)
292
- overlayCtx.fillStyle = alpha(
293
- theme?.palette.info.main ?? 'rgb(255,0,0)',
294
- 0.2,
295
- )
296
- overlayCtx.fillRect(rectX, rectY, rectWidth, rectHeight)
297
- }
298
-
299
- /**
300
- * Check If the mouse position is on the edge of the selected feature
301
- */
302
- isMouseOnFeatureEdge(
303
- mousePosition: MousePosition,
304
- feature: AnnotationFeatureI,
305
- stateModel: LinearApolloDisplay,
306
- topLevelFeature?: AnnotationFeatureI,
307
- ) {
308
- if (!mousePosition) {
309
- return
310
- }
311
-
312
- const { refName, regionNumber, x } = mousePosition
313
- const { lgv } = stateModel
314
- const { bpToPx, offsetPx } = lgv
315
- const startPxInfo = bpToPx({ refName, coord: feature.start, regionNumber })
316
- const endPxInfo = bpToPx({ refName, coord: feature.end, regionNumber })
317
- if (startPxInfo !== undefined && endPxInfo !== undefined) {
318
- const startPx = startPxInfo.offsetPx - offsetPx
319
- const endPx = endPxInfo.offsetPx - offsetPx
320
- if (Math.abs(endPx - startPx) < 8) {
321
- return
322
- }
323
- const parentFeature = this.getParentFeature(feature, topLevelFeature)
324
- // Limit dragging till parent feature end
325
- if (
326
- parentFeature &&
327
- feature.start <= parentFeature.start &&
328
- Math.abs(startPx - x) < 4
329
- ) {
330
- return
331
- }
332
- if (
333
- parentFeature &&
334
- feature.end >= parentFeature.end &&
335
- Math.abs(endPx - x) < 4
336
- ) {
337
- return
338
- }
339
- if (Math.abs(startPx - x) < 4) {
340
- return 'start'
341
- }
342
- if (Math.abs(endPx - x) < 4) {
343
- return 'end'
344
- }
345
- }
346
- return
347
- }
348
-
349
- onMouseMove(stateModel: LinearApolloDisplay, event: CanvasMouseEvent) {
350
- const { feature, mousePosition, topLevelFeature } =
351
- stateModel.getFeatureAndGlyphUnderMouse(event)
352
- if (stateModel.apolloDragging) {
353
- stateModel.setCursor('col-resize')
354
- return
355
- }
356
- if (feature && mousePosition) {
357
- const edge = this.isMouseOnFeatureEdge(
358
- mousePosition,
359
- feature,
360
- stateModel,
361
- topLevelFeature,
362
- )
363
- if (edge) {
364
- stateModel.setCursor('col-resize')
365
- } else {
366
- stateModel.setCursor()
367
- }
368
- }
369
- }
370
-
371
- onMouseDown(stateModel: LinearApolloDisplay, event: CanvasMouseEvent) {
372
- // swallow the mouseDown if we are on the edge of the feature
373
- const { feature, mousePosition } =
374
- stateModel.getFeatureAndGlyphUnderMouse(event)
375
- if (feature && mousePosition) {
376
- const edge = this.isMouseOnFeatureEdge(mousePosition, feature, stateModel)
377
- if (edge) {
378
- event.stopPropagation()
379
- }
380
- }
381
- }
382
-
383
- onMouseUp(stateModel: LinearApolloDisplay, event: CanvasMouseEvent) {
384
- if (stateModel.apolloDragging ?? event.button !== 0) {
385
- return
386
- }
387
- const { feature } = stateModel.getFeatureAndGlyphUnderMouse(event)
388
- if (feature) {
389
- stateModel.setSelectedFeature(feature)
390
- }
391
- }
392
-
393
- startDrag(stateModel: LinearApolloDisplay): boolean {
394
- // only accept the drag if we are on the edge of the feature
395
- const { feature, mousePosition, topLevelFeature } =
396
- stateModel.apolloDragging?.start ?? {}
397
- const { mousePosition: currentMousePosition } =
398
- stateModel.apolloDragging?.current ?? {}
399
- if (feature && mousePosition && currentMousePosition) {
400
- const edge = this.isMouseOnFeatureEdge(
401
- mousePosition,
402
- feature,
403
- stateModel,
404
- topLevelFeature,
405
- )
406
- if (edge) {
407
- return true
408
- }
409
- }
410
- return false
411
- }
412
-
413
- continueDrag(
414
- stateModel: LinearApolloDisplay,
415
- currentMousePosition: MousePosition,
416
- ): void {
417
- const { feature, glyph, mousePosition, topLevelFeature } =
418
- stateModel.apolloDragging?.start ?? {}
419
- if (!(currentMousePosition && mousePosition)) {
420
- return
421
- }
422
- const parentFeature = this.getParentFeature(feature, topLevelFeature)
423
- const adjacentFeatures: {
424
- prevFeature?: AnnotationFeatureI
425
- nextFeature?: AnnotationFeatureI
426
- } = this.getAdjacentFeatures(feature, parentFeature)
427
- if (!feature || !currentMousePosition) {
428
- return
429
- }
430
- const { bp } = currentMousePosition
431
- const edge = this.isMouseOnFeatureEdge(
432
- mousePosition,
433
- feature,
434
- stateModel,
435
- topLevelFeature,
436
- )
437
- if (
438
- edge &&
439
- ((edge === 'start' && bp >= feature.end - 1) ||
440
- (edge === 'end' && bp <= feature.start + 1))
441
- ) {
442
- return
443
- }
444
- if (feature.type !== 'CDS') {
445
- if (adjacentFeatures.prevFeature && !adjacentFeatures.nextFeature) {
446
- if (
447
- adjacentFeatures.prevFeature.type === 'CDS' &&
448
- bp <= adjacentFeatures.prevFeature.start + 1
449
- ) {
450
- return
451
- }
452
- if (
453
- adjacentFeatures.prevFeature.type !== 'CDS' &&
454
- bp <= adjacentFeatures.prevFeature.end + 1
455
- ) {
456
- return
457
- }
458
- }
459
- if (!adjacentFeatures.prevFeature && adjacentFeatures.nextFeature) {
460
- if (
461
- adjacentFeatures.nextFeature.type === 'CDS' &&
462
- bp >= adjacentFeatures.nextFeature.end - 1
463
- ) {
464
- return
465
- }
466
- if (
467
- adjacentFeatures.nextFeature.type !== 'CDS' &&
468
- bp >= adjacentFeatures.nextFeature.start - 1
469
- ) {
470
- return
471
- }
472
- }
473
- }
474
-
475
- if (adjacentFeatures.prevFeature && adjacentFeatures.nextFeature) {
476
- if (feature.type === 'CDS') {
477
- if (
478
- adjacentFeatures.nextFeature.type !== 'CDS' &&
479
- bp >= adjacentFeatures.nextFeature.end - 1
480
- ) {
481
- return
482
- }
483
- if (
484
- adjacentFeatures.nextFeature.type === 'CDS' &&
485
- bp >= adjacentFeatures.nextFeature.start - 1
486
- ) {
487
- return
488
- }
489
- if (
490
- adjacentFeatures.prevFeature.type !== 'CDS' &&
491
- bp <= adjacentFeatures.prevFeature.start + 1
492
- ) {
493
- return
494
- }
495
- if (
496
- adjacentFeatures.prevFeature.type === 'CDS' &&
497
- bp <= adjacentFeatures.prevFeature.end + 1
498
- ) {
499
- return
500
- }
501
- } else {
502
- if (
503
- adjacentFeatures.prevFeature.type === 'CDS' &&
504
- bp <= adjacentFeatures.prevFeature.start + 1
505
- ) {
506
- return
507
- }
508
- if (
509
- adjacentFeatures.prevFeature.type !== 'CDS' &&
510
- bp <= adjacentFeatures.prevFeature.end + 1
511
- ) {
512
- return
513
- }
514
- if (
515
- adjacentFeatures.nextFeature.type !== 'CDS' &&
516
- bp >= adjacentFeatures.nextFeature.start - 1
517
- ) {
518
- return
519
- }
520
- if (
521
- adjacentFeatures.nextFeature.type === 'CDS' &&
522
- bp >= adjacentFeatures.nextFeature.end - 1
523
- ) {
524
- return
525
- }
526
- }
527
- }
528
- stateModel.setDragging({
529
- start: {
530
- feature,
531
- topLevelFeature,
532
- glyph,
533
- mousePosition,
534
- },
535
- current: {
536
- feature,
537
- topLevelFeature,
538
- glyph,
539
- mousePosition: currentMousePosition,
540
- },
541
- })
542
- }
543
-
544
- getFeatureFromLayout(
545
- feature: AnnotationFeatureI,
546
- bp: number,
547
- row: number,
548
- ): AnnotationFeatureI | undefined {
549
- const layoutRow = this.featuresForRow(feature)[row]
550
- return layoutRow.find((f) => bp >= f.start && bp <= f.end)
551
- }
552
-
553
- getRowForFeature(
554
- feature: AnnotationFeatureI,
555
- childFeature: AnnotationFeatureI,
556
- ) {
557
- const rows = this.featuresForRow(feature)
558
- for (const [idx, row] of rows.entries()) {
559
- if (row.some((feature) => feature._id === childFeature._id)) {
560
- return idx
561
- }
562
- }
563
- return
564
- }
565
-
566
- async executeDrag(stateModel: LinearApolloDisplay) {
567
- const {
568
- apolloDragging,
569
- changeManager,
570
- displayedRegions,
571
- getAssemblyId,
572
- setCursor,
573
- } = stateModel
574
- if (!apolloDragging) {
575
- return
576
- }
577
- const {
578
- feature,
579
- glyph,
580
- mousePosition: startingMousePosition,
581
- topLevelFeature,
582
- } = apolloDragging.start
583
- if (!feature) {
584
- throw new Error('no feature for drag preview??')
585
- }
586
- if (glyph !== this) {
587
- throw new Error('drawDragPreview() called on wrong glyph?')
588
- }
589
- const edge = this.isMouseOnFeatureEdge(
590
- startingMousePosition,
591
- feature,
592
- stateModel,
593
- )
594
- if (!edge) {
595
- return
596
- }
597
-
598
- const { mousePosition: currentMousePosition } = apolloDragging.current
599
- const region = displayedRegions[startingMousePosition.regionNumber]
600
- const newBp = currentMousePosition.bp
601
- const assembly = getAssemblyId(region.assemblyName)
602
-
603
- const parentFeature = this.getParentFeature(feature, topLevelFeature)
604
- const adjacentFeatures: {
605
- prevFeature?: AnnotationFeatureI
606
- nextFeature?: AnnotationFeatureI
607
- } = this.getAdjacentFeatures(feature, parentFeature)
608
- const changes: (LocationStartChange | LocationEndChange)[] = []
609
-
610
- if (edge === 'end') {
611
- this.addEndLocation(changes, feature, newBp, assembly)
612
- const { nextFeature } = adjacentFeatures
613
- if (!nextFeature) {
614
- return
615
- }
616
- if (
617
- (feature.type !== 'CDS' && nextFeature.type === 'CDS') ||
618
- (feature.type === 'CDS' && nextFeature.type !== 'CDS')
619
- ) {
620
- this.addStartLocation(changes, nextFeature, newBp + 1, assembly)
621
- }
622
- } else {
623
- this.addStartLocation(changes, feature, newBp, assembly)
624
- const { prevFeature } = adjacentFeatures
625
- if (!prevFeature) {
626
- return
627
- }
628
- if (
629
- (feature.type !== 'CDS' && prevFeature.type === 'CDS') ||
630
- (feature.type === 'CDS' && prevFeature.type !== 'CDS')
631
- ) {
632
- this.addEndLocation(changes, prevFeature, newBp - 1, assembly)
633
- }
634
- }
635
- if (!changeManager) {
636
- throw new Error('no change manager')
637
- }
638
- for (const change of changes) {
639
- await changeManager.submit(change)
640
- }
641
- setCursor()
642
- }
643
-
644
- getAdjacentFeatures(
645
- feature?: AnnotationFeatureI,
646
- parentFeature?: AnnotationFeatureI,
647
- ): {
648
- prevFeature?: AnnotationFeatureI
649
- nextFeature?: AnnotationFeatureI
650
- } {
651
- let prevFeature: AnnotationFeatureI | undefined
652
- let nextFeature: AnnotationFeatureI | undefined
653
- let i = 0
654
- if (!feature || !(parentFeature && parentFeature.children)) {
655
- return { prevFeature, nextFeature }
656
- }
657
- for (const [, f] of parentFeature.children) {
658
- if (f._id === feature._id) {
659
- break
660
- }
661
- i++
662
- }
663
- const keys = [...parentFeature.children.keys()]
664
- if (i > 0) {
665
- const key = keys[i - 1]
666
- prevFeature = parentFeature.children.get(key)
667
- }
668
- if (i < keys.length - 1) {
669
- const key = keys[i + 1]
670
- nextFeature = parentFeature.children.get(key)
671
- }
672
- return { prevFeature, nextFeature }
673
- }
674
-
675
- addEndLocation(
676
- changes: (LocationStartChange | LocationEndChange)[] = [],
677
- feature: AnnotationFeatureI,
678
- newBp: number,
679
- assembly: string,
680
- ) {
681
- const featureId = feature._id
682
- const oldEnd = feature.end
683
- const newEnd = newBp
684
- changes.push(
685
- new LocationEndChange({
686
- typeName: 'LocationEndChange',
687
- changedIds: [featureId],
688
- featureId,
689
- oldEnd,
690
- newEnd,
691
- assembly,
692
- }),
693
- )
694
- }
695
-
696
- addStartLocation(
697
- changes: (LocationStartChange | LocationEndChange)[] = [],
698
- feature: AnnotationFeatureI,
699
- newBp: number,
700
- assembly: string,
701
- ) {
702
- const featureId = feature._id
703
- const oldStart = feature.start
704
- const newStart = newBp
705
- changes.push(
706
- new LocationStartChange({
707
- typeName: 'LocationStartChange',
708
- changedIds: [featureId],
709
- featureId,
710
- oldStart,
711
- newStart,
712
- assembly,
713
- }),
714
- )
715
- }
716
- }