@apollo-annotation/jbrowse-plugin-apollo 0.1.0

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 (116) hide show
  1. package/README.md +76 -0
  2. package/dist/index.esm.js +10248 -0
  3. package/dist/index.esm.js.map +1 -0
  4. package/dist/index.js +7 -0
  5. package/dist/jbrowse-plugin-apollo.cjs.development.js +10298 -0
  6. package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -0
  7. package/dist/jbrowse-plugin-apollo.cjs.production.min.js +2 -0
  8. package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -0
  9. package/dist/jbrowse-plugin-apollo.umd.development.js +46957 -0
  10. package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -0
  11. package/dist/jbrowse-plugin-apollo.umd.production.min.js +2 -0
  12. package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -0
  13. package/package.json +130 -0
  14. package/src/ApolloInternetAccount/addMenuItems.ts +94 -0
  15. package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +121 -0
  16. package/src/ApolloInternetAccount/components/LoginButtons.tsx +62 -0
  17. package/src/ApolloInternetAccount/components/LoginIcons.tsx +74 -0
  18. package/src/ApolloInternetAccount/configSchema.ts +26 -0
  19. package/src/ApolloInternetAccount/index.ts +2 -0
  20. package/src/ApolloInternetAccount/model.ts +448 -0
  21. package/src/ApolloJobModel.ts +117 -0
  22. package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +186 -0
  23. package/src/ApolloSequenceAdapter/configSchema.ts +12 -0
  24. package/src/ApolloSequenceAdapter/index.ts +21 -0
  25. package/src/ApolloSixFrameRenderer/ApolloSixFrameRenderer.tsx +12 -0
  26. package/src/ApolloSixFrameRenderer/components/ApolloRendering.tsx +692 -0
  27. package/src/ApolloSixFrameRenderer/configSchema.ts +7 -0
  28. package/src/ApolloSixFrameRenderer/index.ts +3 -0
  29. package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +64 -0
  30. package/src/ApolloTextSearchAdapter/configSchema.ts +24 -0
  31. package/src/ApolloTextSearchAdapter/index.ts +18 -0
  32. package/src/BackendDrivers/BackendDriver.ts +31 -0
  33. package/src/BackendDrivers/CollaborationServerDriver.ts +318 -0
  34. package/src/BackendDrivers/DesktopFileDriver.ts +170 -0
  35. package/src/BackendDrivers/InMemoryFileDriver.ts +76 -0
  36. package/src/BackendDrivers/index.ts +4 -0
  37. package/src/ChangeManager.ts +148 -0
  38. package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +248 -0
  39. package/src/LinearApolloDisplay/components/index.ts +1 -0
  40. package/src/LinearApolloDisplay/configSchema.ts +16 -0
  41. package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +422 -0
  42. package/src/LinearApolloDisplay/glyphs/CanonicalGeneGlyph.ts +1191 -0
  43. package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +151 -0
  44. package/src/LinearApolloDisplay/glyphs/Glyph.ts +382 -0
  45. package/src/LinearApolloDisplay/glyphs/ImplicitExonGeneGlyph.ts +697 -0
  46. package/src/LinearApolloDisplay/glyphs/index.ts +4 -0
  47. package/src/LinearApolloDisplay/index.ts +2 -0
  48. package/src/LinearApolloDisplay/stateModel/base.ts +146 -0
  49. package/src/LinearApolloDisplay/stateModel/getGlyph.ts +39 -0
  50. package/src/LinearApolloDisplay/stateModel/glyphs.ts +45 -0
  51. package/src/LinearApolloDisplay/stateModel/index.ts +20 -0
  52. package/src/LinearApolloDisplay/stateModel/layouts.ts +230 -0
  53. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +513 -0
  54. package/src/LinearApolloDisplay/stateModel/rendering.ts +441 -0
  55. package/src/LinearApolloDisplay/stateModel/trackHeightMixin.ts +43 -0
  56. package/src/LinearApolloDisplay/types.ts +1 -0
  57. package/src/OntologyManager/OntologyStore/__snapshots__/fulltext.test.ts.snap +208 -0
  58. package/src/OntologyManager/OntologyStore/__snapshots__/index.test.ts.snap +18846 -0
  59. package/src/OntologyManager/OntologyStore/fulltext-stopwords.ts +137 -0
  60. package/src/OntologyManager/OntologyStore/fulltext.test.ts +94 -0
  61. package/src/OntologyManager/OntologyStore/fulltext.ts +264 -0
  62. package/src/OntologyManager/OntologyStore/index.test.ts +130 -0
  63. package/src/OntologyManager/OntologyStore/index.ts +526 -0
  64. package/src/OntologyManager/OntologyStore/indexeddb-schema.ts +89 -0
  65. package/src/OntologyManager/OntologyStore/indexeddb-storage.ts +180 -0
  66. package/src/OntologyManager/OntologyStore/obo-graph-json-schema.ts +110 -0
  67. package/src/OntologyManager/OntologyStore/prefixes.ts +35 -0
  68. package/src/OntologyManager/index.ts +173 -0
  69. package/src/SixFrameFeatureDisplay/components/TrackLines.tsx +19 -0
  70. package/src/SixFrameFeatureDisplay/components/index.ts +1 -0
  71. package/src/SixFrameFeatureDisplay/configSchema.ts +21 -0
  72. package/src/SixFrameFeatureDisplay/index.ts +2 -0
  73. package/src/SixFrameFeatureDisplay/stateModel.ts +413 -0
  74. package/src/TabularEditor/HybridGrid/ChangeHandling.ts +88 -0
  75. package/src/TabularEditor/HybridGrid/Feature.tsx +346 -0
  76. package/src/TabularEditor/HybridGrid/FeatureAttributes.tsx +34 -0
  77. package/src/TabularEditor/HybridGrid/Highlight.tsx +40 -0
  78. package/src/TabularEditor/HybridGrid/HybridGrid.tsx +138 -0
  79. package/src/TabularEditor/HybridGrid/NumberCell.tsx +77 -0
  80. package/src/TabularEditor/HybridGrid/ToolBar.tsx +59 -0
  81. package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +119 -0
  82. package/src/TabularEditor/HybridGrid/index.ts +1 -0
  83. package/src/TabularEditor/TabularEditorPane.tsx +34 -0
  84. package/src/TabularEditor/index.ts +3 -0
  85. package/src/TabularEditor/model.ts +44 -0
  86. package/src/TabularEditor/types.ts +3 -0
  87. package/src/components/AddAssembly.tsx +464 -0
  88. package/src/components/AddChildFeature.tsx +247 -0
  89. package/src/components/AddFeature.tsx +252 -0
  90. package/src/components/CopyFeature.tsx +328 -0
  91. package/src/components/DeleteAssembly.tsx +185 -0
  92. package/src/components/DeleteFeature.tsx +90 -0
  93. package/src/components/Dialog.tsx +47 -0
  94. package/src/components/DownloadGFF3.tsx +213 -0
  95. package/src/components/ImportFeatures.tsx +295 -0
  96. package/src/components/ManageChecks.tsx +280 -0
  97. package/src/components/ManageUsers.tsx +218 -0
  98. package/src/components/ModifyFeatureAttribute.tsx +457 -0
  99. package/src/components/OntologyTermAutocomplete.tsx +240 -0
  100. package/src/components/OntologyTermMultiSelect.tsx +349 -0
  101. package/src/components/OpenLocalFile.tsx +178 -0
  102. package/src/components/ViewChangeLog.tsx +208 -0
  103. package/src/components/ViewCheckResults.tsx +151 -0
  104. package/src/components/index.ts +12 -0
  105. package/src/config.ts +10 -0
  106. package/src/declare.d.ts +3 -0
  107. package/src/extensions/annotationFromPileup.ts +208 -0
  108. package/src/extensions/index.ts +1 -0
  109. package/src/index.ts +394 -0
  110. package/src/makeDisplayComponent.tsx +244 -0
  111. package/src/session/ClientDataStore.ts +282 -0
  112. package/src/session/index.ts +1 -0
  113. package/src/session/session.ts +373 -0
  114. package/src/types.ts +10 -0
  115. package/src/util/index.ts +31 -0
  116. package/src/util/loadAssemblyIntoClient.ts +291 -0
@@ -0,0 +1,513 @@
1
+ import { AnyConfigurationSchemaType } from '@jbrowse/core/configuration/configurationSchema'
2
+ import PluginManager from '@jbrowse/core/PluginManager'
3
+ import { MenuItem } from '@jbrowse/core/ui'
4
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
5
+ import { Theme } from '@mui/material'
6
+ import { AnnotationFeatureI } from 'apollo-mst'
7
+ import { autorun } from 'mobx'
8
+ import { Instance, addDisposer } from 'mobx-state-tree'
9
+ import type { CSSProperties } from 'react'
10
+
11
+ import { Coord } from '../components'
12
+ import { Glyph } from '../glyphs/Glyph'
13
+ import { CanvasMouseEvent } from '../types'
14
+ import { getGlyph } from './getGlyph'
15
+ import { renderingModelFactory } from './rendering'
16
+
17
+ /** extended information about the position of the mouse on the canvas, including the refName, bp, and displayedRegion number */
18
+ export interface MousePosition {
19
+ x: number
20
+ y: number
21
+ refName: string
22
+ bp: number
23
+ regionNumber: number
24
+ }
25
+
26
+ export interface FeatureAndGlyphInfo {
27
+ feature?: AnnotationFeatureI
28
+ topLevelFeature?: AnnotationFeatureI
29
+ glyph?: Glyph
30
+ mousePosition?: MousePosition
31
+ }
32
+
33
+ export interface CDSDiscontinuousLocation {
34
+ start: number
35
+ end: number
36
+ phase: 0 | 1 | 2 | undefined
37
+ idx?: number
38
+ }
39
+
40
+ function getMousePosition(
41
+ event: CanvasMouseEvent,
42
+ lgv: LinearGenomeViewModel,
43
+ ): MousePosition {
44
+ const canvas = event.currentTarget
45
+ const { clientX, clientY } = event
46
+ const { left, top } = canvas.getBoundingClientRect() || { left: 0, top: 0 }
47
+ const x = clientX - left
48
+ const y = clientY - top
49
+ const { coord: bp, index: regionNumber, refName } = lgv.pxToBp(x)
50
+ return { x, y, refName, bp, regionNumber }
51
+ }
52
+
53
+ function getSeqRow(feature: AnnotationFeatureI, bpPerPx: number) {
54
+ const rowOffset = bpPerPx <= 1 ? 5 : 3
55
+ if (feature.type === 'CDS' && feature.phase !== undefined) {
56
+ return feature.strand === -1
57
+ ? ((feature.end - feature.phase) % 3) + rowOffset
58
+ : Math.abs(((feature.start + feature.phase) % 3) - 2)
59
+ }
60
+
61
+ if (bpPerPx <= 1) {
62
+ return feature.strand === -1 ? 4 : 3
63
+ }
64
+
65
+ return
66
+ }
67
+
68
+ function highlightSeq(
69
+ seqTrackOverlayctx: CanvasRenderingContext2D,
70
+ theme: Theme | undefined,
71
+ startPx: number,
72
+ sequenceRowHeight: number,
73
+ row: number | undefined,
74
+ widthPx: number,
75
+ ) {
76
+ if (row !== undefined) {
77
+ seqTrackOverlayctx.fillStyle =
78
+ theme?.palette.action.focus ?? 'rgba(0,0,0,0.04)'
79
+ seqTrackOverlayctx.fillRect(
80
+ startPx,
81
+ sequenceRowHeight * row,
82
+ widthPx,
83
+ sequenceRowHeight,
84
+ )
85
+ }
86
+ }
87
+
88
+ export function mouseEventsModelIntermediateFactory(
89
+ pluginManager: PluginManager,
90
+ configSchema: AnyConfigurationSchemaType,
91
+ ) {
92
+ const LinearApolloDisplayRendering = renderingModelFactory(
93
+ pluginManager,
94
+ configSchema,
95
+ )
96
+
97
+ return LinearApolloDisplayRendering.named('LinearApolloDisplayMouseEvents')
98
+ .volatile(() => ({
99
+ apolloDragging: null as {
100
+ start: {
101
+ glyph?: Glyph
102
+ feature?: AnnotationFeatureI
103
+ topLevelFeature?: AnnotationFeatureI
104
+ discontinuousLocation?: CDSDiscontinuousLocation
105
+ mousePosition: MousePosition
106
+ }
107
+ current: {
108
+ glyph?: Glyph
109
+ feature?: AnnotationFeatureI
110
+ topLevelFeature?: AnnotationFeatureI
111
+ mousePosition: MousePosition
112
+ }
113
+ } | null,
114
+ cursor: undefined as CSSProperties['cursor'] | undefined,
115
+ apolloHover: null as FeatureAndGlyphInfo | null,
116
+ }))
117
+ .views((self) => ({
118
+ getFeatureAndGlyphUnderMouse(
119
+ event: CanvasMouseEvent,
120
+ ): FeatureAndGlyphInfo {
121
+ const mousePosition = getMousePosition(event, self.lgv)
122
+ const { bp, regionNumber, y } = mousePosition
123
+ const row = Math.floor(y / self.apolloRowHeight)
124
+ const featureLayout = self.featureLayouts[regionNumber]
125
+ const layoutRow = featureLayout.get(row)
126
+ if (!layoutRow) {
127
+ return { mousePosition }
128
+ }
129
+ const foundFeature = layoutRow.find(
130
+ (f) => bp >= f[1].min && bp <= f[1].max,
131
+ )
132
+ if (!foundFeature) {
133
+ return { mousePosition }
134
+ }
135
+ const [featureRow, topLevelFeature] = foundFeature
136
+ const glyph = getGlyph(topLevelFeature, self.lgv.bpPerPx)
137
+ const feature = glyph.getFeatureFromLayout(
138
+ topLevelFeature,
139
+ bp,
140
+ featureRow,
141
+ )
142
+ return { feature, topLevelFeature, glyph, mousePosition }
143
+ },
144
+ }))
145
+ .actions((self) => ({
146
+ continueDrag(event: CanvasMouseEvent) {
147
+ if (!self.apolloDragging) {
148
+ throw new Error(
149
+ 'continueDrag() called with no current drag in progress',
150
+ )
151
+ }
152
+ event.stopPropagation()
153
+ const { glyph } = self.apolloDragging.start
154
+ const { mousePosition } = self.getFeatureAndGlyphUnderMouse(event)
155
+ if (!(mousePosition && glyph)) {
156
+ return
157
+ }
158
+ glyph.continueDrag(self, mousePosition)
159
+ },
160
+ setDragging(dragInfo?: typeof self.apolloDragging) {
161
+ self.apolloDragging = dragInfo ?? null
162
+ },
163
+ }))
164
+ .actions((self) => ({
165
+ setApolloHover(n: (typeof self)['apolloHover']) {
166
+ self.apolloHover = n
167
+ },
168
+ setCursor(cursor?: CSSProperties['cursor']) {
169
+ if (self.cursor !== cursor) {
170
+ self.cursor = cursor
171
+ }
172
+ },
173
+ }))
174
+ .actions(() => ({
175
+ // onClick(event: CanvasMouseEvent) {
176
+ onClick() {
177
+ // TODO: set the selected feature
178
+ },
179
+ }))
180
+ }
181
+
182
+ export function mouseEventsSeqHightlightModelFactory(
183
+ pluginManager: PluginManager,
184
+ configSchema: AnyConfigurationSchemaType,
185
+ ) {
186
+ const LinearApolloDisplayRendering = mouseEventsModelIntermediateFactory(
187
+ pluginManager,
188
+ configSchema,
189
+ )
190
+
191
+ return LinearApolloDisplayRendering.actions((self) => ({
192
+ afterAttach() {
193
+ addDisposer(
194
+ self,
195
+ autorun(
196
+ async () => {
197
+ if (!self.lgv.initialized || self.regionCannotBeRendered()) {
198
+ return
199
+ }
200
+ const seqTrackOverlayctx =
201
+ self.seqTrackOverlayCanvas?.getContext('2d')
202
+ if (!seqTrackOverlayctx) {
203
+ return
204
+ }
205
+
206
+ seqTrackOverlayctx.clearRect(
207
+ 0,
208
+ 0,
209
+ self.lgv.dynamicBlocks.totalWidthPx,
210
+ self.lgv.bpPerPx <= 1 ? 125 : 95,
211
+ )
212
+
213
+ const {
214
+ apolloHover,
215
+ displayedRegions,
216
+ lgv,
217
+ regions,
218
+ sequenceRowHeight,
219
+ theme,
220
+ } = self
221
+
222
+ if (!apolloHover) {
223
+ return
224
+ }
225
+ const { feature, mousePosition } = apolloHover
226
+ if (!feature || !mousePosition) {
227
+ return
228
+ }
229
+
230
+ for (const [idx, region] of regions.entries()) {
231
+ const row = getSeqRow(feature, lgv.bpPerPx)
232
+ if (
233
+ feature.discontinuousLocations &&
234
+ feature.discontinuousLocations.length > 0
235
+ ) {
236
+ for (const dl of feature.discontinuousLocations) {
237
+ const offset =
238
+ (lgv.bpToPx({
239
+ refName: region.refName,
240
+ coord: dl.start,
241
+ regionNumber: idx,
242
+ })?.offsetPx ?? 0) - lgv.offsetPx
243
+ const widthPx = (dl.end - dl.start) / lgv.bpPerPx
244
+ const startPx = displayedRegions[idx].reversed
245
+ ? offset - widthPx
246
+ : offset
247
+
248
+ highlightSeq(
249
+ seqTrackOverlayctx,
250
+ theme,
251
+ startPx,
252
+ sequenceRowHeight,
253
+ row,
254
+ widthPx,
255
+ )
256
+ }
257
+ } else {
258
+ const offset =
259
+ (lgv.bpToPx({
260
+ refName: region.refName,
261
+ coord: feature.start,
262
+ regionNumber: idx,
263
+ })?.offsetPx ?? 0) - lgv.offsetPx
264
+ const widthPx = feature.length / lgv.bpPerPx
265
+ const startPx = displayedRegions[idx].reversed
266
+ ? offset - widthPx
267
+ : offset
268
+
269
+ highlightSeq(
270
+ seqTrackOverlayctx,
271
+ theme,
272
+ startPx,
273
+ sequenceRowHeight,
274
+ row,
275
+ widthPx,
276
+ )
277
+ }
278
+ }
279
+ },
280
+ { name: 'LinearApolloDisplayRenderSeqHighlight' },
281
+ ),
282
+ )
283
+ },
284
+ }))
285
+ }
286
+
287
+ export function mouseEventsModelFactory(
288
+ pluginManager: PluginManager,
289
+ configSchema: AnyConfigurationSchemaType,
290
+ ) {
291
+ const LinearApolloDisplayMouseEvents = mouseEventsSeqHightlightModelFactory(
292
+ pluginManager,
293
+ configSchema,
294
+ )
295
+
296
+ return LinearApolloDisplayMouseEvents.views((self) => ({
297
+ contextMenuItems(contextCoord?: Coord): MenuItem[] {
298
+ const { apolloHover, lgv } = self
299
+ const { topLevelFeature } = apolloHover ?? {}
300
+ if (!(topLevelFeature && contextCoord)) {
301
+ return []
302
+ }
303
+ const glyph = getGlyph(topLevelFeature, lgv.bpPerPx)
304
+ return glyph.getContextMenuItems(self)
305
+ },
306
+ }))
307
+ .actions((self) => ({
308
+ startDrag(event: CanvasMouseEvent) {
309
+ const { feature, glyph, mousePosition, topLevelFeature } =
310
+ self.getFeatureAndGlyphUnderMouse(event)
311
+ if (feature && topLevelFeature && glyph && mousePosition) {
312
+ let dl, idx
313
+ if (
314
+ feature.discontinuousLocations &&
315
+ feature.discontinuousLocations.length > 0
316
+ ) {
317
+ for (let i = 0; i < feature.discontinuousLocations.length; i++) {
318
+ if (
319
+ mousePosition.bp >= feature.discontinuousLocations[i].start &&
320
+ mousePosition.bp <= feature.discontinuousLocations[i].end
321
+ ) {
322
+ idx = i
323
+ dl = feature.discontinuousLocations[idx]
324
+ break
325
+ }
326
+ }
327
+ }
328
+ self.apolloDragging = {
329
+ start: {
330
+ glyph,
331
+ feature,
332
+ topLevelFeature,
333
+ discontinuousLocation: dl
334
+ ? {
335
+ start: dl.start,
336
+ end: dl.end,
337
+ phase: dl.phase,
338
+ idx,
339
+ }
340
+ : undefined,
341
+ mousePosition,
342
+ },
343
+ current: { glyph, feature, topLevelFeature, mousePosition },
344
+ }
345
+ if (!glyph.startDrag(self, event)) {
346
+ self.apolloDragging = null
347
+ }
348
+ }
349
+ },
350
+ endDrag(event: CanvasMouseEvent) {
351
+ self.continueDrag(event)
352
+ self.apolloDragging?.start.glyph?.executeDrag(self, event)
353
+ self.setDragging()
354
+ },
355
+ }))
356
+ .actions((self) => ({
357
+ onMouseDown(event: CanvasMouseEvent) {
358
+ const { feature, glyph, topLevelFeature } =
359
+ self.getFeatureAndGlyphUnderMouse(event)
360
+ if (glyph && feature && topLevelFeature) {
361
+ glyph.onMouseDown(self, event)
362
+ }
363
+ },
364
+ onMouseMove(event: CanvasMouseEvent) {
365
+ const { buttons } = event
366
+ const hover = self.getFeatureAndGlyphUnderMouse(event)
367
+ const { glyph } = hover
368
+ if (glyph) {
369
+ glyph.onMouseMove(self, event)
370
+ }
371
+
372
+ if (buttons) {
373
+ // if button 1 is being held down while moving, we must be dragging
374
+ if (buttons === 1) {
375
+ if (self.apolloDragging) {
376
+ // otherwise update the drag state
377
+ self.continueDrag(event)
378
+ } else {
379
+ // start drag if not already dragging
380
+ self.startDrag(event)
381
+ }
382
+ }
383
+ } else {
384
+ // if no buttons, update mouseover hover
385
+ const { feature, topLevelFeature } = hover
386
+ if (feature && glyph && topLevelFeature) {
387
+ self.setApolloHover(hover)
388
+ } else {
389
+ self.setApolloHover(null)
390
+ self.setCursor()
391
+ }
392
+ }
393
+ },
394
+ onMouseLeave(event: CanvasMouseEvent) {
395
+ self.setDragging()
396
+
397
+ const { glyph } = self.getFeatureAndGlyphUnderMouse(event)
398
+ if (glyph) {
399
+ glyph.onMouseLeave(self, event)
400
+ }
401
+ },
402
+ onMouseUp(event: CanvasMouseEvent) {
403
+ const { glyph } = self.getFeatureAndGlyphUnderMouse(event)
404
+ if (glyph) {
405
+ glyph.onMouseUp(self, event)
406
+ }
407
+
408
+ if (self.apolloDragging) {
409
+ self.endDrag(event)
410
+ }
411
+ },
412
+ }))
413
+ .actions((self) => ({
414
+ afterAttach() {
415
+ addDisposer(
416
+ self,
417
+ autorun(
418
+ () => {
419
+ if (!self.lgv.initialized || self.regionCannotBeRendered()) {
420
+ return
421
+ }
422
+ const ctx = self.overlayCanvas?.getContext('2d')
423
+ if (!ctx) {
424
+ return
425
+ }
426
+ ctx.clearRect(
427
+ 0,
428
+ 0,
429
+ self.lgv.dynamicBlocks.totalWidthPx,
430
+ self.featuresHeight,
431
+ )
432
+
433
+ const {
434
+ apolloDragging,
435
+ apolloHover,
436
+ displayedRegions,
437
+ featureLayouts,
438
+ lgv,
439
+ } = self
440
+ if (!apolloHover) {
441
+ return
442
+ }
443
+ const { feature, glyph } = apolloHover
444
+ if (!feature) {
445
+ return
446
+ }
447
+ let rowNum = 0
448
+ let xOffset = 0
449
+ let reversed = false
450
+ for (const [idx, featureLayout] of featureLayouts.entries()) {
451
+ const displayedRegion = displayedRegions[idx]
452
+ for (const [row, featureLayoutRow] of featureLayout.entries()) {
453
+ if (rowNum !== 0) {
454
+ continue
455
+ }
456
+ for (const [, f] of featureLayoutRow) {
457
+ for (const [, cf] of f.children ?? new Map()) {
458
+ if (rowNum !== 0) {
459
+ continue
460
+ }
461
+ xOffset =
462
+ (lgv.bpToPx({
463
+ refName: displayedRegion.refName,
464
+ coord: feature.min,
465
+ regionNumber: idx,
466
+ })?.offsetPx ?? 0) - lgv.offsetPx
467
+ ;({ reversed } = displayedRegion)
468
+
469
+ if (cf._id === feature._id) {
470
+ rowNum = row
471
+ continue
472
+ }
473
+ for (const [, annotationFeature] of cf.children ??
474
+ new Map()) {
475
+ if (rowNum !== 0) {
476
+ continue
477
+ }
478
+ if (annotationFeature._id === feature._id) {
479
+ rowNum = row
480
+ continue
481
+ }
482
+ }
483
+ }
484
+ }
485
+ }
486
+ }
487
+
488
+ // draw mouseover hovers
489
+ glyph?.drawHover(self, ctx, rowNum, xOffset, reversed)
490
+
491
+ // draw tooltip on hover
492
+ glyph?.drawTooltip(self, ctx)
493
+
494
+ // dragging previews
495
+ if (apolloDragging) {
496
+ // NOTE: the glyph where the drag started is responsible for drawing the preview.
497
+ // it can call methods in other glyphs to help with this though.
498
+
499
+ apolloDragging.start.glyph?.drawDragPreview(self, ctx)
500
+ }
501
+ },
502
+ { name: 'LinearApolloDisplayRenderMouseoverAndDrag' },
503
+ ),
504
+ )
505
+ },
506
+ }))
507
+ }
508
+
509
+ export type LinearApolloDisplayMouseEventsModel = ReturnType<
510
+ typeof mouseEventsModelIntermediateFactory
511
+ >
512
+ export type LinearApolloDisplayMouseEvents =
513
+ Instance<LinearApolloDisplayMouseEventsModel>