@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
@@ -6,110 +6,22 @@ import {
6
6
  import type PluginManager from '@jbrowse/core/PluginManager'
7
7
  import { type AnyConfigurationSchemaType } from '@jbrowse/core/configuration/configurationSchema'
8
8
  import { type MenuItem } from '@jbrowse/core/ui'
9
- import { type Frame, getFrame } from '@jbrowse/core/util'
10
- import { type LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
11
- import { type Theme } from '@mui/material'
12
9
  import { autorun } from 'mobx'
13
10
  import { type Instance, addDisposer } from 'mobx-state-tree'
14
11
  import { type CSSProperties } from 'react'
15
12
 
16
- import { type Coord } from '../components'
17
- import { type Glyph } from '../glyphs/Glyph'
13
+ import {
14
+ type Edge,
15
+ type MousePosition,
16
+ type MousePositionWithFeature,
17
+ getMousePosition,
18
+ getPropagatedLocationChanges,
19
+ isMousePositionWithFeature,
20
+ } from '../../util'
18
21
  import { type CanvasMouseEvent } from '../types'
19
22
 
20
23
  import { renderingModelFactory } from './rendering'
21
24
 
22
- export interface FeatureAndGlyphUnderMouse {
23
- feature: AnnotationFeature
24
- topLevelFeature: AnnotationFeature
25
- glyph: Glyph
26
- }
27
-
28
- /** extended information about the position of the mouse on the canvas, including the refName, bp, and displayedRegion number */
29
- export interface MousePosition {
30
- x: number
31
- y: number
32
- refName: string
33
- bp: number
34
- regionNumber: number
35
- featureAndGlyphUnderMouse?: FeatureAndGlyphUnderMouse
36
- }
37
-
38
- export type MousePositionWithFeatureAndGlyph = Required<MousePosition>
39
-
40
- export function isMousePositionWithFeatureAndGlyph(
41
- mousePosition: MousePosition,
42
- ): mousePosition is MousePositionWithFeatureAndGlyph {
43
- return 'featureAndGlyphUnderMouse' in mousePosition
44
- }
45
-
46
- function getMousePosition(
47
- event: CanvasMouseEvent,
48
- lgv: LinearGenomeViewModel,
49
- ): MousePosition {
50
- const canvas = event.currentTarget
51
- const { clientX, clientY } = event
52
- const { left, top } = canvas.getBoundingClientRect()
53
- const x = clientX - left
54
- const y = clientY - top
55
- const { coord: bp, index: regionNumber, refName } = lgv.pxToBp(x)
56
- return { x, y, refName, bp, regionNumber }
57
- }
58
-
59
- function getTranslationRow(frame: Frame, bpPerPx: number) {
60
- const offset = bpPerPx <= 1 ? 2 : 0
61
- switch (frame) {
62
- case 3: {
63
- return 0
64
- }
65
- case 2: {
66
- return 1
67
- }
68
- case 1: {
69
- return 2
70
- }
71
- case -1: {
72
- return 3 + offset
73
- }
74
- case -2: {
75
- return 4 + offset
76
- }
77
- case -3: {
78
- return 5 + offset
79
- }
80
- }
81
- }
82
-
83
- function getSeqRow(
84
- strand: 1 | -1 | undefined,
85
- bpPerPx: number,
86
- ): number | undefined {
87
- if (bpPerPx > 1 || strand === undefined) {
88
- return
89
- }
90
- return strand === 1 ? 3 : 4
91
- }
92
-
93
- function highlightSeq(
94
- seqTrackOverlayctx: CanvasRenderingContext2D,
95
- theme: Theme | undefined,
96
- startPx: number,
97
- sequenceRowHeight: number,
98
- row: number | undefined,
99
- widthPx: number,
100
- ) {
101
- if (row !== undefined) {
102
- seqTrackOverlayctx.fillStyle =
103
- theme?.palette.action.focus ?? 'rgba(0,0,0,0.04)'
104
- seqTrackOverlayctx.fillRect(
105
- startPx,
106
- sequenceRowHeight * row,
107
- widthPx,
108
- sequenceRowHeight,
109
- )
110
- }
111
- }
112
-
113
25
  export function mouseEventsModelIntermediateFactory(
114
26
  pluginManager: PluginManager,
115
27
  configSchema: AnyConfigurationSchemaType,
@@ -125,13 +37,13 @@ export function mouseEventsModelIntermediateFactory(
125
37
  start: MousePosition
126
38
  current: MousePosition
127
39
  feature: AnnotationFeature
128
- edge: 'min' | 'max'
40
+ edge: Edge
41
+ shrinkParent: boolean
129
42
  } | null,
130
43
  cursor: undefined as CSSProperties['cursor'] | undefined,
131
- apolloHover: undefined as FeatureAndGlyphUnderMouse | undefined,
132
44
  }))
133
45
  .views((self) => ({
134
- getMousePosition(event: CanvasMouseEvent): MousePosition {
46
+ getMousePosition(event: React.MouseEvent): MousePosition {
135
47
  const mousePosition = getMousePosition(event, self.lgv)
136
48
  const { bp, regionNumber, y } = mousePosition
137
49
  const row = Math.floor(y / self.apolloRowHeight)
@@ -169,7 +81,7 @@ export function mouseEventsModelIntermediateFactory(
169
81
  }
170
82
  return {
171
83
  ...mousePosition,
172
- featureAndGlyphUnderMouse: { feature, topLevelFeature, glyph },
84
+ feature,
173
85
  }
174
86
  },
175
87
  }))
@@ -188,14 +100,15 @@ export function mouseEventsModelIntermediateFactory(
188
100
  },
189
101
  }))
190
102
  .actions((self) => ({
191
- setApolloHover(n?: (typeof self)['apolloHover']) {
192
- self.apolloHover = n
193
- },
194
103
  setCursor(cursor?: CSSProperties['cursor']) {
195
104
  if (self.cursor !== cursor) {
196
105
  self.cursor = cursor
197
106
  }
198
107
  },
108
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
109
+ updateFilteredTranscripts(forms: string[]) {
110
+ return
111
+ },
199
112
  }))
200
113
  .actions(() => ({
201
114
  // onClick(event: CanvasMouseEvent) {
@@ -205,171 +118,53 @@ export function mouseEventsModelIntermediateFactory(
205
118
  }))
206
119
  }
207
120
 
208
- export function mouseEventsSeqHightlightModelFactory(
209
- pluginManager: PluginManager,
210
- configSchema: AnyConfigurationSchemaType,
211
- ) {
212
- const LinearApolloDisplayRendering = mouseEventsModelIntermediateFactory(
213
- pluginManager,
214
- configSchema,
215
- )
216
-
217
- return LinearApolloDisplayRendering.actions((self) => ({
218
- afterAttach() {
219
- addDisposer(
220
- self,
221
- autorun(
222
- () => {
223
- // This type is wrong in @jbrowse/core
224
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
225
- if (!self.lgv.initialized || self.regionCannotBeRendered()) {
226
- return
227
- }
228
- const seqTrackOverlayctx =
229
- self.seqTrackOverlayCanvas?.getContext('2d')
230
- if (!seqTrackOverlayctx) {
231
- return
232
- }
233
-
234
- seqTrackOverlayctx.clearRect(
235
- 0,
236
- 0,
237
- self.lgv.dynamicBlocks.totalWidthPx,
238
- self.lgv.bpPerPx <= 1 ? 125 : 95,
239
- )
240
-
241
- const {
242
- apolloHover,
243
- lgv,
244
- regions,
245
- sequenceRowHeight,
246
- session,
247
- theme,
248
- } = self
249
-
250
- if (!apolloHover) {
251
- return
252
- }
253
- const { feature } = apolloHover
254
-
255
- const { featureTypeOntology } =
256
- session.apolloDataStore.ontologyManager
257
- if (!featureTypeOntology) {
258
- throw new Error('featureTypeOntology is undefined')
259
- }
260
- for (const [idx, region] of regions.entries()) {
261
- if (featureTypeOntology.isTypeOf(feature.type, 'CDS')) {
262
- const parentFeature = feature.parent
263
- if (!parentFeature) {
264
- continue
265
- }
266
- const cdsLocs = parentFeature.cdsLocations.find(
267
- (loc) =>
268
- feature.min === loc.at(0)?.min &&
269
- feature.max === loc.at(-1)?.max,
270
- )
271
- if (!cdsLocs) {
272
- continue
273
- }
274
- for (const dl of cdsLocs) {
275
- const frame = getFrame(
276
- dl.min,
277
- dl.max,
278
- feature.strand ?? 1,
279
- dl.phase,
280
- )
281
- const row = getTranslationRow(frame, lgv.bpPerPx)
282
- const offset =
283
- (lgv.bpToPx({
284
- refName: region.refName,
285
- coord: dl.min,
286
- regionNumber: idx,
287
- })?.offsetPx ?? 0) - lgv.offsetPx
288
- const widthPx = (dl.max - dl.min) / lgv.bpPerPx
289
- const startPx = lgv.displayedRegions[idx].reversed
290
- ? offset - widthPx
291
- : offset
292
-
293
- highlightSeq(
294
- seqTrackOverlayctx,
295
- theme,
296
- startPx,
297
- sequenceRowHeight,
298
- row,
299
- widthPx,
300
- )
301
- }
302
- } else {
303
- const row = getSeqRow(feature.strand, lgv.bpPerPx)
304
- const offset =
305
- (lgv.bpToPx({
306
- refName: region.refName,
307
- coord: feature.min,
308
- regionNumber: idx,
309
- })?.offsetPx ?? 0) - lgv.offsetPx
310
- const widthPx = feature.length / lgv.bpPerPx
311
- const startPx = lgv.displayedRegions[idx].reversed
312
- ? offset - widthPx
313
- : offset
314
-
315
- highlightSeq(
316
- seqTrackOverlayctx,
317
- theme,
318
- startPx,
319
- sequenceRowHeight,
320
- row,
321
- widthPx,
322
- )
323
- }
324
- }
325
- },
326
- { name: 'LinearApolloDisplayRenderSeqHighlight' },
327
- ),
328
- )
329
- },
330
- }))
331
- }
332
-
333
121
  export function mouseEventsModelFactory(
334
122
  pluginManager: PluginManager,
335
123
  configSchema: AnyConfigurationSchemaType,
336
124
  ) {
337
- const LinearApolloDisplayMouseEvents = mouseEventsSeqHightlightModelFactory(
125
+ const LinearApolloDisplayMouseEvents = mouseEventsModelIntermediateFactory(
338
126
  pluginManager,
339
127
  configSchema,
340
128
  )
341
129
 
342
130
  return LinearApolloDisplayMouseEvents.views((self) => ({
343
- contextMenuItems(contextCoord?: Coord): MenuItem[] {
344
- const { apolloHover } = self
345
- if (!(apolloHover && contextCoord)) {
131
+ contextMenuItems(event: React.MouseEvent<HTMLDivElement>): MenuItem[] {
132
+ const { hoveredFeature } = self
133
+ if (!hoveredFeature) {
346
134
  return []
347
135
  }
348
- const { topLevelFeature } = apolloHover
136
+ const mousePosition = self.getMousePosition(event)
137
+ const { topLevelFeature } = hoveredFeature.feature
349
138
  const glyph = self.getGlyph(topLevelFeature)
350
- return glyph.getContextMenuItems(self)
139
+ if (isMousePositionWithFeature(mousePosition)) {
140
+ return glyph.getContextMenuItems(self, mousePosition)
141
+ }
142
+ return []
351
143
  },
352
144
  }))
353
145
  .actions((self) => ({
354
146
  // explicitly pass in a feature in case it's not the same as the one in
355
147
  // mousePosition (e.g. if features are drawn overlapping).
356
148
  startDrag(
357
- mousePosition: MousePositionWithFeatureAndGlyph,
149
+ mousePosition: MousePositionWithFeature,
358
150
  feature: AnnotationFeature,
359
- edge: 'min' | 'max',
151
+ edge: Edge,
152
+ shrinkParent = false,
360
153
  ) {
361
154
  self.apolloDragging = {
362
155
  start: mousePosition,
363
156
  current: mousePosition,
364
157
  feature,
365
158
  edge,
159
+ shrinkParent,
366
160
  }
367
161
  },
368
162
  endDrag() {
369
163
  if (!self.apolloDragging) {
370
164
  throw new Error('endDrag() called with no current drag in progress')
371
165
  }
372
- const { current, edge, feature, start } = self.apolloDragging
166
+ const { current, edge, feature, start, shrinkParent } =
167
+ self.apolloDragging
373
168
  // don't do anything if it was only dragged a tiny bit
374
169
  if (Math.abs(current.x - start.x) <= 4) {
375
170
  self.setDragging()
@@ -379,33 +174,35 @@ export function mouseEventsModelFactory(
379
174
  const { displayedRegions } = self.lgv
380
175
  const region = displayedRegions[start.regionNumber]
381
176
  const assembly = self.getAssemblyId(region.assemblyName)
177
+ const changes = getPropagatedLocationChanges(
178
+ feature,
179
+ current.bp,
180
+ edge,
181
+ shrinkParent,
182
+ )
382
183
 
383
- let change: LocationEndChange | LocationStartChange
384
- if (edge === 'max') {
385
- const featureId = feature._id
386
- const oldEnd = feature.max
387
- const newEnd = current.bp
388
- change = new LocationEndChange({
389
- typeName: 'LocationEndChange',
390
- changedIds: [featureId],
391
- featureId,
392
- oldEnd,
393
- newEnd,
394
- assembly,
395
- })
396
- } else {
397
- const featureId = feature._id
398
- const oldStart = feature.min
399
- const newStart = current.bp
400
- change = new LocationStartChange({
401
- typeName: 'LocationStartChange',
402
- changedIds: [featureId],
403
- featureId,
404
- oldStart,
405
- newStart,
406
- assembly,
407
- })
408
- }
184
+ const change: LocationEndChange | LocationStartChange =
185
+ edge === 'max'
186
+ ? new LocationEndChange({
187
+ typeName: 'LocationEndChange',
188
+ changedIds: changes.map((c) => c.featureId),
189
+ changes: changes.map((c) => ({
190
+ featureId: c.featureId,
191
+ oldEnd: c.oldLocation,
192
+ newEnd: c.newLocation,
193
+ })),
194
+ assembly,
195
+ })
196
+ : new LocationStartChange({
197
+ typeName: 'LocationStartChange',
198
+ changedIds: changes.map((c) => c.featureId),
199
+ changes: changes.map((c) => ({
200
+ featureId: c.featureId,
201
+ oldStart: c.oldLocation,
202
+ newStart: c.newLocation,
203
+ })),
204
+ assembly,
205
+ })
409
206
  void self.changeManager.submit(change)
410
207
  self.setDragging()
411
208
  self.setCursor()
@@ -414,12 +211,9 @@ export function mouseEventsModelFactory(
414
211
  .actions((self) => ({
415
212
  onMouseDown(event: CanvasMouseEvent) {
416
213
  const mousePosition = self.getMousePosition(event)
417
- if (isMousePositionWithFeatureAndGlyph(mousePosition)) {
418
- mousePosition.featureAndGlyphUnderMouse.glyph.onMouseDown(
419
- self,
420
- mousePosition,
421
- event,
422
- )
214
+ if (isMousePositionWithFeature(mousePosition)) {
215
+ const glyph = self.getGlyph(mousePosition.feature)
216
+ glyph.onMouseDown(self, mousePosition, event)
423
217
  }
424
218
  },
425
219
  onMouseMove(event: CanvasMouseEvent) {
@@ -429,38 +223,31 @@ export function mouseEventsModelFactory(
429
223
  self.continueDrag(mousePosition, event)
430
224
  return
431
225
  }
432
- if (isMousePositionWithFeatureAndGlyph(mousePosition)) {
433
- mousePosition.featureAndGlyphUnderMouse.glyph.onMouseMove(
434
- self,
435
- mousePosition,
436
- event,
437
- )
226
+ if (isMousePositionWithFeature(mousePosition)) {
227
+ const glyph = self.getGlyph(mousePosition.feature)
228
+ glyph.onMouseMove(self, mousePosition, event)
438
229
  } else {
439
- self.setApolloHover()
230
+ self.setHoveredFeature()
440
231
  self.setCursor()
441
232
  }
442
233
  },
443
234
  onMouseLeave(event: CanvasMouseEvent) {
444
235
  self.setDragging()
445
- self.setApolloHover()
236
+ self.setHoveredFeature()
446
237
 
447
238
  const mousePosition = self.getMousePosition(event)
448
- if (isMousePositionWithFeatureAndGlyph(mousePosition)) {
449
- mousePosition.featureAndGlyphUnderMouse.glyph.onMouseLeave(
450
- self,
451
- mousePosition,
452
- event,
453
- )
239
+ if (isMousePositionWithFeature(mousePosition)) {
240
+ const glyph = self.getGlyph(mousePosition.feature)
241
+ glyph.onMouseLeave(self, mousePosition, event)
454
242
  }
455
243
  },
456
244
  onMouseUp(event: CanvasMouseEvent) {
457
245
  const mousePosition = self.getMousePosition(event)
458
- if (isMousePositionWithFeatureAndGlyph(mousePosition)) {
459
- mousePosition.featureAndGlyphUnderMouse.glyph.onMouseUp(
460
- self,
461
- mousePosition,
462
- event,
463
- )
246
+ if (isMousePositionWithFeature(mousePosition)) {
247
+ const glyph = self.getGlyph(mousePosition.feature)
248
+ glyph.onMouseUp(self, mousePosition, event)
249
+ } else {
250
+ self.setSelectedFeature()
464
251
  }
465
252
 
466
253
  if (self.apolloDragging) {
@@ -490,11 +277,11 @@ export function mouseEventsModelFactory(
490
277
  self.featuresHeight,
491
278
  )
492
279
 
493
- const { apolloDragging, apolloHover } = self
494
- if (!apolloHover) {
280
+ const { apolloDragging, hoveredFeature } = self
281
+ if (!hoveredFeature) {
495
282
  return
496
283
  }
497
- const { glyph } = apolloHover
284
+ const glyph = self.getGlyph(hoveredFeature.feature)
498
285
 
499
286
  // draw mouseover hovers
500
287
  glyph.drawHover(self, ctx)