@apollo-annotation/jbrowse-plugin-apollo 0.3.0 → 0.3.2

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 (47) hide show
  1. package/dist/index.esm.js +2072 -1496
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/jbrowse-plugin-apollo.cjs.development.js +2069 -1493
  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 +2256 -1533
  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 +13 -11
  12. package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +7 -10
  13. package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +3 -0
  14. package/src/FeatureDetailsWidget/Attributes.tsx +27 -27
  15. package/src/FeatureDetailsWidget/FeatureDetailsNavigation.tsx +65 -0
  16. package/src/FeatureDetailsWidget/TranscriptBasic.tsx +6 -1
  17. package/src/FeatureDetailsWidget/TranscriptSequence.tsx +25 -2
  18. package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +0 -1
  19. package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +8 -1
  20. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +88 -40
  21. package/src/LinearApolloDisplay/glyphs/Glyph.ts +8 -1
  22. package/src/LinearApolloDisplay/stateModel/base.ts +28 -2
  23. package/src/LinearApolloDisplay/stateModel/layouts.ts +65 -11
  24. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +25 -6
  25. package/src/LinearApolloDisplay/stateModel/rendering.ts +1 -2
  26. package/src/OntologyManager/OntologyStore/index.ts +6 -2
  27. package/src/OntologyManager/OntologyStore/indexeddb-storage.ts +41 -13
  28. package/src/OntologyManager/index.ts +35 -0
  29. package/src/SixFrameFeatureDisplay/stateModel.ts +11 -2
  30. package/src/TabularEditor/HybridGrid/Feature.tsx +1 -2
  31. package/src/TabularEditor/HybridGrid/HybridGrid.tsx +0 -1
  32. package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +8 -1
  33. package/src/components/AddRefSeqAliases.tsx +7 -8
  34. package/src/components/CopyFeature.tsx +1 -1
  35. package/src/components/CreateApolloAnnotation.tsx +304 -0
  36. package/src/components/DownloadGFF3.tsx +5 -1
  37. package/src/components/FilterFeatures.tsx +120 -0
  38. package/src/components/ModifyFeatureAttribute.tsx +27 -27
  39. package/src/components/OntologyTermMultiSelect.tsx +5 -5
  40. package/src/extensions/annotationFromJBrowseFeature.test.ts +119 -0
  41. package/src/extensions/annotationFromJBrowseFeature.ts +171 -0
  42. package/src/extensions/annotationFromPileup.ts +1 -1
  43. package/src/extensions/index.ts +1 -0
  44. package/src/index.ts +8 -2
  45. package/src/session/ClientDataStore.ts +29 -0
  46. package/src/session/session.ts +2 -5
  47. package/src/LinearApolloDisplay/stateModel/getGlyph.ts +0 -40
@@ -12,6 +12,7 @@ import { CanvasMouseEvent } from '../types'
12
12
  import { Glyph } from './Glyph'
13
13
  import { boxGlyph } from './BoxGlyph'
14
14
  import { LinearApolloDisplayRendering } from '../stateModel/rendering'
15
+ import { OntologyRecord } from '../../OntologyManager'
15
16
 
16
17
  let forwardFillLight: CanvasPattern | null = null
17
18
  let backwardFillLight: CanvasPattern | null = null
@@ -80,6 +81,11 @@ function draw(
80
81
  return
81
82
  }
82
83
  const { apolloSelectedFeature } = session
84
+ const { apolloDataStore } = session
85
+ const { featureTypeOntology } = apolloDataStore.ontologyManager
86
+ if (!featureTypeOntology) {
87
+ throw new Error('featureTypeOntology is undefined')
88
+ }
83
89
 
84
90
  // Draw background for gene
85
91
  const topLevelFeatureMinX =
@@ -93,7 +99,8 @@ function draw(
93
99
  ? topLevelFeatureMinX - topLevelFeatureWidthPx
94
100
  : topLevelFeatureMinX
95
101
  const topLevelFeatureTop = row * rowHeight
96
- const topLevelFeatureHeight = getRowCount(feature) * rowHeight
102
+ const topLevelFeatureHeight =
103
+ getRowCount(feature, featureTypeOntology) * rowHeight
97
104
 
98
105
  ctx.fillStyle = alpha(theme?.palette.background.paper ?? '#ffffff', 0.6)
99
106
  ctx.fillRect(
@@ -103,19 +110,23 @@ function draw(
103
110
  topLevelFeatureHeight,
104
111
  )
105
112
 
106
- // Draw lines on different rows for each mRNA
113
+ // Draw lines on different rows for each transcript
107
114
  let currentRow = 0
108
- for (const [, mrna] of children) {
109
- if (mrna.type !== 'mRNA') {
115
+ for (const [, transcript] of children) {
116
+ const isTranscript = featureTypeOntology.isTypeOf(
117
+ transcript.type,
118
+ 'transcript',
119
+ )
120
+ if (!isTranscript) {
110
121
  currentRow += 1
111
122
  continue
112
123
  }
113
- const { children: childrenOfmRNA, min } = mrna
114
- if (!childrenOfmRNA) {
124
+ const { children: childrenOfTranscript, min } = transcript
125
+ if (!childrenOfTranscript) {
115
126
  continue
116
127
  }
117
- for (const [, cds] of childrenOfmRNA) {
118
- if (cds.type !== 'CDS') {
128
+ for (const [, cds] of childrenOfTranscript) {
129
+ if (!featureTypeOntology.isTypeOf(cds.type, 'CDS')) {
119
130
  continue
120
131
  }
121
132
  const minX =
@@ -124,7 +135,7 @@ function draw(
124
135
  coord: min,
125
136
  regionNumber: displayedRegionIndex,
126
137
  })?.offsetPx ?? 0) - offsetPx
127
- const widthPx = mrna.length / bpPerPx
138
+ const widthPx = transcript.length / bpPerPx
128
139
  const startPx = reversed ? minX - widthPx : minX
129
140
  const height =
130
141
  Math.round((currentRow + 1 / 2) * rowHeight) + row * rowHeight
@@ -141,21 +152,21 @@ function draw(
141
152
  theme?.palette.mode === 'dark' ? forwardFillDark : forwardFillLight
142
153
  const backwardFill =
143
154
  theme?.palette.mode === 'dark' ? backwardFillDark : backwardFillLight
144
- // Draw exon and CDS for each mRNA
155
+ // Draw exon and CDS for each transcript
145
156
  currentRow = 0
146
157
  for (const [, child] of children) {
147
- if (child.type !== 'mRNA') {
158
+ if (!featureTypeOntology.isTypeOf(child.type, 'transcript')) {
148
159
  boxGlyph.draw(ctx, child, row, stateModel, displayedRegionIndex)
149
160
  currentRow += 1
150
161
  continue
151
162
  }
152
163
  for (const cdsRow of child.cdsLocations) {
153
- const { _id, children: childrenOfmRNA } = child
154
- if (!childrenOfmRNA) {
164
+ const { _id, children: childrenOfTranscript } = child
165
+ if (!childrenOfTranscript) {
155
166
  continue
156
167
  }
157
- for (const [, exon] of childrenOfmRNA) {
158
- if (exon.type !== 'exon') {
168
+ for (const [, exon] of childrenOfTranscript) {
169
+ if (!featureTypeOntology.isTypeOf(exon.type, 'exon')) {
159
170
  continue
160
171
  }
161
172
  const minX =
@@ -296,7 +307,9 @@ function drawHover(
296
307
  stateModel: LinearApolloDisplay,
297
308
  ctx: CanvasRenderingContext2D,
298
309
  ) {
299
- const { apolloHover, apolloRowHeight, lgv, theme } = stateModel
310
+ const { apolloHover, apolloRowHeight, lgv, session, theme } = stateModel
311
+ const { featureTypeOntology } = session.apolloDataStore.ontologyManager
312
+
300
313
  if (!apolloHover) {
301
314
  return
302
315
  }
@@ -320,16 +333,26 @@ function drawHover(
320
333
  const top = row * apolloRowHeight
321
334
  const widthPx = length / bpPerPx
322
335
  ctx.fillStyle = theme?.palette.action.selected ?? 'rgba(0,0,0,04)'
323
- ctx.fillRect(startPx, top, widthPx, apolloRowHeight * getRowCount(feature))
336
+
337
+ if (!featureTypeOntology) {
338
+ throw new Error('featureTypeOntology is undefined')
339
+ }
340
+ ctx.fillRect(
341
+ startPx,
342
+ top,
343
+ widthPx,
344
+ apolloRowHeight * getRowCount(feature, featureTypeOntology),
345
+ )
324
346
  }
325
347
 
326
348
  function getFeatureFromLayout(
327
349
  feature: AnnotationFeature,
328
350
  bp: number,
329
351
  row: number,
352
+ featureTypeOntology: OntologyRecord,
330
353
  ): AnnotationFeature | undefined {
331
354
  const featureInThisRow: AnnotationFeature[] =
332
- featuresForRow(feature)[row] || []
355
+ featuresForRow(feature, featureTypeOntology)[row] || []
333
356
  for (const f of featureInThisRow) {
334
357
  let featureObj
335
358
  if (bp >= f.min && bp <= f.max && f.parent) {
@@ -339,9 +362,9 @@ function getFeatureFromLayout(
339
362
  continue
340
363
  }
341
364
  if (
342
- featureObj.type === 'CDS' &&
365
+ featureTypeOntology.isTypeOf(featureObj.type, 'CDS') &&
343
366
  featureObj.parent &&
344
- featureObj.parent.type === 'mRNA'
367
+ featureTypeOntology.isTypeOf(featureObj.parent.type, 'transcript')
345
368
  ) {
346
369
  const { cdsLocations } = featureObj.parent
347
370
  for (const cdsLoc of cdsLocations) {
@@ -352,7 +375,7 @@ function getFeatureFromLayout(
352
375
  }
353
376
  }
354
377
 
355
- // If mouse position is in the intron region, return the mRNA
378
+ // If mouse position is in the intron region, return the transcript
356
379
  return featureObj.parent
357
380
  }
358
381
  // If mouse position is in a feature that is not a CDS, return the feature
@@ -361,22 +384,28 @@ function getFeatureFromLayout(
361
384
  return feature
362
385
  }
363
386
 
364
- function getRowCount(feature: AnnotationFeature, _bpPerPx?: number): number {
387
+ function getRowCount(
388
+ feature: AnnotationFeature,
389
+ featureTypeOntology: OntologyRecord,
390
+ _bpPerPx?: number,
391
+ ): number {
365
392
  const { children, type } = feature
366
393
  if (!children) {
367
394
  return 1
368
395
  }
396
+ const isTranscript = featureTypeOntology.isTypeOf(type, 'transcript')
369
397
  let rowCount = 0
370
- if (type === 'mRNA') {
398
+ if (isTranscript) {
371
399
  for (const [, child] of children) {
372
- if (child.type === 'CDS') {
400
+ const isCds = featureTypeOntology.isTypeOf(child.type, 'CDS')
401
+ if (isCds) {
373
402
  rowCount += 1
374
403
  }
375
404
  }
376
405
  return rowCount
377
406
  }
378
407
  for (const [, child] of children) {
379
- rowCount += getRowCount(child)
408
+ rowCount += getRowCount(child, featureTypeOntology)
380
409
  }
381
410
  return rowCount
382
411
  }
@@ -384,11 +413,15 @@ function getRowCount(feature: AnnotationFeature, _bpPerPx?: number): number {
384
413
  /**
385
414
  * A list of all the subfeatures for each row for a given feature, as well as
386
415
  * the feature itself.
387
- * If the row contains an mRNA, the order is CDS -\> exon -\> mRNA -\> gene
388
- * If the row does not contain an mRNA, the order is subfeature -\> gene
416
+ * If the row contains a transcript, the order is CDS -\> exon -\> transcript -\> gene
417
+ * If the row does not contain an transcript, the order is subfeature -\> gene
389
418
  */
390
- function featuresForRow(feature: AnnotationFeature): AnnotationFeature[][] {
391
- if (feature.type !== 'gene') {
419
+ function featuresForRow(
420
+ feature: AnnotationFeature,
421
+ featureTypeOntology: OntologyRecord,
422
+ ): AnnotationFeature[][] {
423
+ const isGene = featureTypeOntology.isTypeOf(feature.type, 'gene')
424
+ if (!isGene) {
392
425
  throw new Error('Top level feature for GeneGlyph must have type "gene"')
393
426
  }
394
427
  const { children } = feature
@@ -397,7 +430,7 @@ function featuresForRow(feature: AnnotationFeature): AnnotationFeature[][] {
397
430
  }
398
431
  const features: AnnotationFeature[][] = []
399
432
  for (const [, child] of children) {
400
- if (child.type !== 'mRNA') {
433
+ if (!featureTypeOntology.isTypeOf(child.type, 'transcript')) {
401
434
  features.push([child, feature])
402
435
  continue
403
436
  }
@@ -407,9 +440,9 @@ function featuresForRow(feature: AnnotationFeature): AnnotationFeature[][] {
407
440
  const cdss: AnnotationFeature[] = []
408
441
  const exons: AnnotationFeature[] = []
409
442
  for (const [, grandchild] of child.children) {
410
- if (grandchild.type === 'CDS') {
443
+ if (featureTypeOntology.isTypeOf(grandchild.type, 'CDS')) {
411
444
  cdss.push(grandchild)
412
- } else if (grandchild.type === 'exon') {
445
+ } else if (featureTypeOntology.isTypeOf(grandchild.type, 'exon')) {
413
446
  exons.push(grandchild)
414
447
  }
415
448
  }
@@ -423,8 +456,9 @@ function featuresForRow(feature: AnnotationFeature): AnnotationFeature[][] {
423
456
  function getRowForFeature(
424
457
  feature: AnnotationFeature,
425
458
  childFeature: AnnotationFeature,
459
+ featureTypeOntology: OntologyRecord,
426
460
  ) {
427
- const rows = featuresForRow(feature)
461
+ const rows = featuresForRow(feature, featureTypeOntology)
428
462
  for (const [idx, row] of rows.entries()) {
429
463
  if (row.some((feature) => feature._id === childFeature._id)) {
430
464
  return idx
@@ -496,7 +530,16 @@ function getDraggableFeatureInfo(
496
530
  feature: AnnotationFeature,
497
531
  stateModel: LinearApolloDisplay,
498
532
  ): { feature: AnnotationFeature; edge: 'min' | 'max' } | undefined {
499
- if (feature.type === 'gene' || feature.type === 'mRNA') {
533
+ const { session } = stateModel
534
+ const { apolloDataStore } = session
535
+ const { featureTypeOntology } = apolloDataStore.ontologyManager
536
+ if (!featureTypeOntology) {
537
+ throw new Error('featureTypeOntology is undefined')
538
+ }
539
+ const isGene = featureTypeOntology.isTypeOf(feature.type, 'gene')
540
+ const isTranscript = featureTypeOntology.isTypeOf(feature.type, 'transcript')
541
+ const isCds = featureTypeOntology.isTypeOf(feature.type, 'CDS')
542
+ if (isGene || isTranscript) {
500
543
  return
501
544
  }
502
545
  const { bp, refName, regionNumber, x } = mousePosition
@@ -519,14 +562,19 @@ function getDraggableFeatureInfo(
519
562
  if (Math.abs(maxPx - x) < 4) {
520
563
  return { feature, edge: 'max' }
521
564
  }
522
- if (feature.type === 'CDS') {
523
- const mRNA = feature.parent
524
- if (!mRNA?.children) {
565
+ if (isCds) {
566
+ const transcript = feature.parent
567
+ if (!transcript?.children) {
525
568
  return
526
569
  }
527
- const exonChildren = [...mRNA.children.values()].filter(
528
- (child) => child.type === 'exon',
529
- )
570
+ const exonChildren: AnnotationFeature[] = []
571
+ for (const child of transcript.children.values()) {
572
+ const childIsExon = featureTypeOntology.isTypeOf(child.type, 'exon')
573
+ if (childIsExon) {
574
+ exonChildren.push(child)
575
+ }
576
+ }
577
+
530
578
  const overlappingExon = exonChildren.find((child) => {
531
579
  const [start, end] = intersection2(bp, bp + 1, child.min, child.max)
532
580
  return start !== undefined && end !== undefined
@@ -7,10 +7,15 @@ import {
7
7
  } from '../stateModel/mouseEvents'
8
8
  import { LinearApolloDisplayRendering } from '../stateModel/rendering'
9
9
  import { CanvasMouseEvent } from '../types'
10
+ import { OntologyRecord } from '../../OntologyManager'
10
11
 
11
12
  export interface Glyph {
12
13
  /** @returns number of layout rows used by this glyph with this feature and zoom level */
13
- getRowCount(feature: AnnotationFeature, bpPerPx: number): number
14
+ getRowCount(
15
+ feature: AnnotationFeature,
16
+ featureTypeOntology: OntologyRecord,
17
+ bpPerPx: number,
18
+ ): number
14
19
  /** draw the feature's primary rendering on the canvas */
15
20
  draw(
16
21
  ctx: CanvasRenderingContext2D,
@@ -24,10 +29,12 @@ export interface Glyph {
24
29
  feature: AnnotationFeature,
25
30
  bp: number,
26
31
  row: number,
32
+ featureTypeOntology: OntologyRecord,
27
33
  ): AnnotationFeature | undefined
28
34
  getRowForFeature(
29
35
  feature: AnnotationFeature,
30
36
  childFeature: AnnotationFeature,
37
+ featureTypeOntology: OntologyRecord,
31
38
  ): number | undefined
32
39
 
33
40
  drawHover(
@@ -17,11 +17,12 @@ import { getParentRenderProps } from '@jbrowse/core/util/tracks'
17
17
  // import type LinearGenomeViewPlugin from '@jbrowse/plugin-linear-genome-view'
18
18
  import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
19
19
  import { autorun } from 'mobx'
20
- import { addDisposer, getRoot, types } from 'mobx-state-tree'
20
+ import { addDisposer, cast, getRoot, types, getSnapshot } from 'mobx-state-tree'
21
21
 
22
22
  import { ApolloInternetAccountModel } from '../../ApolloInternetAccount/model'
23
23
  import { ApolloSessionModel } from '../../session'
24
24
  import { ApolloRootModel } from '../../types'
25
+ import { FilterFeatures } from '../../components/FilterFeatures'
25
26
 
26
27
  const minDisplayHeight = 20
27
28
 
@@ -42,6 +43,7 @@ export function baseModelFactory(
42
43
  (n) => n >= minDisplayHeight,
43
44
  ),
44
45
  ),
46
+ filteredFeatureTypes: types.array(types.string),
45
47
  })
46
48
  .views((self) => {
47
49
  const { configuration, renderProps: superRenderProps } = self
@@ -162,9 +164,12 @@ export function baseModelFactory(
162
164
  self.graphical = true
163
165
  self.table = true
164
166
  },
167
+ updateFilteredFeatureTypes(types: string[]) {
168
+ self.filteredFeatureTypes = cast(types)
169
+ },
165
170
  }))
166
171
  .views((self) => {
167
- const { trackMenuItems: superTrackMenuItems } = self
172
+ const { filteredFeatureTypes, trackMenuItems: superTrackMenuItems } = self
168
173
  return {
169
174
  trackMenuItems() {
170
175
  const { graphical, table } = self
@@ -200,6 +205,27 @@ export function baseModelFactory(
200
205
  },
201
206
  ],
202
207
  },
208
+ {
209
+ label: 'Filter features by type',
210
+ onClick: () => {
211
+ const session = self.session as unknown as ApolloSessionModel
212
+ ;(self.session as unknown as AbstractSessionModel).queueDialog(
213
+ (doneCallback) => [
214
+ FilterFeatures,
215
+ {
216
+ session,
217
+ handleClose: () => {
218
+ doneCallback()
219
+ },
220
+ featureTypes: getSnapshot(filteredFeatureTypes),
221
+ onUpdate: (types: string[]) => {
222
+ self.updateFilteredFeatureTypes(types)
223
+ },
224
+ },
225
+ ],
226
+ )
227
+ },
228
+ },
203
229
  ]
204
230
  },
205
231
  }
@@ -9,7 +9,7 @@ import { addDisposer, isAlive } from 'mobx-state-tree'
9
9
 
10
10
  import { ApolloSessionModel } from '../../session'
11
11
  import { baseModelFactory } from './base'
12
- import { getGlyph } from './getGlyph'
12
+ import { boxGlyph, geneGlyph, genericChildGlyph } from '../glyphs'
13
13
 
14
14
  export function layoutsModelFactory(
15
15
  pluginManager: PluginManager,
@@ -37,7 +37,9 @@ export function layoutsModelFactory(
37
37
  if (
38
38
  refName !== assembly?.getCanonicalRefName(feature.refSeq) ||
39
39
  !doesIntersect2(start, end, feature.min, feature.max) ||
40
- feature.length > self.featuresMinMaxLimit
40
+ feature.length > self.featuresMinMaxLimit ||
41
+ (self.filteredFeatureTypes.length > 0 &&
42
+ !self.filteredFeatureTypes.includes(feature.type))
41
43
  ) {
42
44
  continue
43
45
  }
@@ -60,6 +62,48 @@ export function layoutsModelFactory(
60
62
  return
61
63
  })
62
64
  },
65
+ getGlyph(feature: AnnotationFeature) {
66
+ if (this.looksLikeGene(feature)) {
67
+ return geneGlyph
68
+ }
69
+ if (feature.children?.size) {
70
+ return genericChildGlyph
71
+ }
72
+ return boxGlyph
73
+ },
74
+ looksLikeGene(feature: AnnotationFeature): boolean {
75
+ const { featureTypeOntology } =
76
+ self.session.apolloDataStore.ontologyManager
77
+ if (!featureTypeOntology) {
78
+ return false
79
+ }
80
+ const { children } = feature
81
+ if (!children?.size) {
82
+ return false
83
+ }
84
+ const isGene = featureTypeOntology.isTypeOf(feature.type, 'gene')
85
+ if (!isGene) {
86
+ return false
87
+ }
88
+ for (const [, child] of children) {
89
+ if (featureTypeOntology.isTypeOf(child.type, 'transcript')) {
90
+ const { children: grandChildren } = child
91
+ if (!grandChildren?.size) {
92
+ return false
93
+ }
94
+ const hasCDS = [...grandChildren.values()].some((grandchild) =>
95
+ featureTypeOntology.isTypeOf(grandchild.type, 'CDS'),
96
+ )
97
+ const hasExon = [...grandChildren.values()].some((grandchild) =>
98
+ featureTypeOntology.isTypeOf(grandchild.type, 'exon'),
99
+ )
100
+ if (hasCDS && hasExon) {
101
+ return true
102
+ }
103
+ }
104
+ }
105
+ return false
106
+ },
63
107
  }))
64
108
  .actions((self) => ({
65
109
  addSeenFeature(feature: AnnotationFeature) {
@@ -90,14 +134,20 @@ export function layoutsModelFactory(
90
134
  }
91
135
  if (
92
136
  refName !== assembly?.getCanonicalRefName(feature.refSeq) ||
93
- !doesIntersect2(start, end, feature.min, feature.max)
137
+ !doesIntersect2(start, end, feature.min, feature.max) ||
138
+ (self.filteredFeatureTypes.length > 0 &&
139
+ !self.filteredFeatureTypes.includes(feature.type))
94
140
  ) {
95
141
  continue
96
142
  }
97
- const rowCount = getGlyph(feature).getRowCount(
98
- feature,
99
- self.lgv.bpPerPx,
100
- )
143
+ const { featureTypeOntology } =
144
+ self.session.apolloDataStore.ontologyManager
145
+ if (!featureTypeOntology) {
146
+ throw new Error('featureTypeOntology is undefined')
147
+ }
148
+ const rowCount = self
149
+ .getGlyph(feature)
150
+ .getRowCount(feature, featureTypeOntology, self.lgv.bpPerPx)
101
151
  let startingRow = 0
102
152
  let placed = false
103
153
  while (!placed) {
@@ -158,6 +208,8 @@ export function layoutsModelFactory(
158
208
  },
159
209
  getFeatureLayoutPosition(feature: AnnotationFeature) {
160
210
  const { featureLayouts } = this
211
+ const { featureTypeOntology } =
212
+ self.session.apolloDataStore.ontologyManager
161
213
  for (const [idx, layout] of featureLayouts.entries()) {
162
214
  for (const [layoutRowNum, layoutRow] of layout) {
163
215
  for (const [featureRowNum, layoutFeature] of layoutRow) {
@@ -174,10 +226,12 @@ export function layoutsModelFactory(
174
226
  }
175
227
  }
176
228
  if (layoutFeature.hasDescendant(feature._id)) {
177
- const row = getGlyph(layoutFeature).getRowForFeature(
178
- layoutFeature,
179
- feature,
180
- )
229
+ if (!featureTypeOntology) {
230
+ throw new Error('featureTypeOntology is undefined')
231
+ }
232
+ const row = self
233
+ .getGlyph(layoutFeature)
234
+ .getRowForFeature(layoutFeature, feature, featureTypeOntology)
181
235
  if (row !== undefined) {
182
236
  return {
183
237
  layoutIndex: idx,
@@ -15,7 +15,6 @@ import type { CSSProperties } from 'react'
15
15
  import { Coord } from '../components'
16
16
  import { Glyph } from '../glyphs/Glyph'
17
17
  import { CanvasMouseEvent } from '../types'
18
- import { getGlyph } from './getGlyph'
19
18
  import { renderingModelFactory } from './rendering'
20
19
  import { Frame, getFrame } from '@jbrowse/core/util'
21
20
 
@@ -147,11 +146,17 @@ export function mouseEventsModelIntermediateFactory(
147
146
  return mousePosition
148
147
  }
149
148
  const [featureRow, topLevelFeature] = foundFeature
150
- const glyph = getGlyph(topLevelFeature)
149
+ const glyph = self.getGlyph(topLevelFeature)
150
+ const { featureTypeOntology } =
151
+ self.session.apolloDataStore.ontologyManager
152
+ if (!featureTypeOntology) {
153
+ throw new Error('featureTypeOntology is undefined')
154
+ }
151
155
  const feature = glyph.getFeatureFromLayout(
152
156
  topLevelFeature,
153
157
  bp,
154
158
  featureRow,
159
+ featureTypeOntology,
155
160
  )
156
161
  if (!feature) {
157
162
  return mousePosition
@@ -227,15 +232,27 @@ export function mouseEventsSeqHightlightModelFactory(
227
232
  self.lgv.bpPerPx <= 1 ? 125 : 95,
228
233
  )
229
234
 
230
- const { apolloHover, lgv, regions, sequenceRowHeight, theme } = self
235
+ const {
236
+ apolloHover,
237
+ lgv,
238
+ regions,
239
+ sequenceRowHeight,
240
+ session,
241
+ theme,
242
+ } = self
231
243
 
232
244
  if (!apolloHover) {
233
245
  return
234
246
  }
235
247
  const { feature } = apolloHover
236
248
 
249
+ const { featureTypeOntology } =
250
+ session.apolloDataStore.ontologyManager
251
+ if (!featureTypeOntology) {
252
+ throw new Error('featureTypeOntology is undefined')
253
+ }
237
254
  for (const [idx, region] of regions.entries()) {
238
- if (feature.type === 'CDS') {
255
+ if (featureTypeOntology.isTypeOf(feature.type, 'CDS')) {
239
256
  const parentFeature = feature.parent
240
257
  if (!parentFeature) {
241
258
  continue
@@ -323,7 +340,7 @@ export function mouseEventsModelFactory(
323
340
  return []
324
341
  }
325
342
  const { topLevelFeature } = apolloHover
326
- const glyph = getGlyph(topLevelFeature)
343
+ const glyph = self.getGlyph(topLevelFeature)
327
344
  return glyph.getContextMenuItems(self)
328
345
  },
329
346
  }))
@@ -483,7 +500,9 @@ export function mouseEventsModelFactory(
483
500
  if (apolloDragging) {
484
501
  // NOTE: the glyph where the drag started is responsible for drawing the preview.
485
502
  // it can call methods in other glyphs to help with this though.
486
- const glyph = getGlyph(apolloDragging.feature.topLevelFeature)
503
+ const glyph = self.getGlyph(
504
+ apolloDragging.feature.topLevelFeature,
505
+ )
487
506
  glyph.drawDragPreview(self, ctx)
488
507
  }
489
508
  },
@@ -7,7 +7,6 @@ import { autorun } from 'mobx'
7
7
  import { Instance, addDisposer } from 'mobx-state-tree'
8
8
 
9
9
  import { ApolloSessionModel } from '../../session'
10
- import { getGlyph } from './getGlyph'
11
10
  import { layoutsModelFactory } from './layouts'
12
11
 
13
12
  export function renderingModelIntermediateFactory(
@@ -428,7 +427,7 @@ export function renderingModelFactory(
428
427
  ) {
429
428
  continue
430
429
  }
431
- getGlyph(feature).draw(ctx, feature, row, self, idx)
430
+ self.getGlyph(feature).draw(ctx, feature, row, self, idx)
432
431
  }
433
432
  }
434
433
  }
@@ -68,6 +68,7 @@ export interface OntologyStoreOptions {
68
68
  indexFields?: { displayName: string; jsonPath: string }[]
69
69
  }
70
70
  maxSearchResults?: number
71
+ update?(message: string, progress: number): void
71
72
  }
72
73
 
73
74
  export interface PropertiesOptions {
@@ -107,8 +108,8 @@ export default class OntologyStore {
107
108
  this.ontologyName = name
108
109
  this.ontologyVersion = version
109
110
  this.sourceLocation = source
110
- this.db = this.prepareDatabase()
111
111
  this.options = options ?? {}
112
+ this.db = this.prepareDatabase()
112
113
  }
113
114
 
114
115
  /**
@@ -174,9 +175,12 @@ export default class OntologyStore {
174
175
  }
175
176
 
176
177
  try {
177
- const { sourceLocation, sourceType } = this
178
+ const { options, sourceLocation, sourceType } = this
178
179
  if (sourceType === 'obo-graph-json') {
180
+ options.update?.('', 0)
181
+ // add more updates inside `loadOboGraphJson`
179
182
  await this.loadOboGraphJson(db)
183
+ options.update?.('', 100)
180
184
  } else {
181
185
  throw new Error(
182
186
  `ontology source file ${JSON.stringify(