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

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.
@@ -21068,7 +21068,7 @@
21068
21068
  }), 'Add');
21069
21069
  default_1$a = Add["default"] = _default$c;
21070
21070
 
21071
- var version = "0.3.2";
21071
+ var version = "0.3.4";
21072
21072
 
21073
21073
  const ApolloConfigSchema = configuration.ConfigurationSchema('ApolloInternetAccount', {
21074
21074
  baseURL: {
@@ -46619,6 +46619,7 @@
46619
46619
  })
46620
46620
  .volatile((_self) => ({
46621
46621
  dataStore: undefined,
46622
+ startedEquivalentTypeRequests: new Set(),
46622
46623
  }))
46623
46624
  .actions((self) => ({
46624
46625
  /** does nothing, just used to access the model to force its lifecycle hooks to run */
@@ -46642,12 +46643,33 @@
46642
46643
  if (!self.dataStore) {
46643
46644
  return;
46644
46645
  }
46646
+ if (self.startedEquivalentTypeRequests.has(type)) {
46647
+ return;
46648
+ }
46649
+ self.startedEquivalentTypeRequests.add(type);
46645
46650
  const terms = (yield self.dataStore.getTermsWithLabelOrSynonym(type));
46646
46651
  const equivalents = terms
46647
46652
  .map((term) => term.lbl)
46648
46653
  .filter((term) => term != undefined);
46649
46654
  self.setEquivalentTypes(type, equivalents);
46650
46655
  }),
46656
+ }))
46657
+ .actions((self) => ({
46658
+ afterCreate() {
46659
+ mobx.autorun((reaction) => {
46660
+ if (!self.dataStore) {
46661
+ return;
46662
+ }
46663
+ void self.loadEquivalentTypes('gene');
46664
+ void self.loadEquivalentTypes('transcript');
46665
+ void self.loadEquivalentTypes('CDS');
46666
+ void self.loadEquivalentTypes('mRNA');
46667
+ reaction.dispose();
46668
+ });
46669
+ },
46670
+ setEquivalentTypes(type, equivalentTypes) {
46671
+ self.equivalentTypes.set(type, equivalentTypes);
46672
+ },
46651
46673
  }))
46652
46674
  .views((self) => ({
46653
46675
  isTypeOf(queryType, typeOf) {
@@ -52614,7 +52636,6 @@
52614
52636
  })),
52615
52637
  assembly: require$$1$3.types.string,
52616
52638
  refName: require$$1$3.types.string,
52617
- changeManager: require$$1$3.types.frozen(),
52618
52639
  })
52619
52640
  .volatile(() => ({
52620
52641
  tryReload: undefined,
@@ -53625,13 +53646,13 @@
53625
53646
  React__default["default"].createElement(material.DialogContent, null,
53626
53647
  React__default["default"].createElement(material.DialogContentText, null, "Select the feature types you want to display in the apollo track"),
53627
53648
  React__default["default"].createElement(material.Grid2, { container: true, spacing: 2 },
53628
- React__default["default"].createElement(material.Grid2, null,
53649
+ React__default["default"].createElement(material.Grid2, { size: 8 },
53629
53650
  React__default["default"].createElement(OntologyTermAutocomplete, { session: session, ontologyName: "Sequence Ontology", style: { width: '100%' }, value: type, filterTerms: isOntologyClass, renderInput: (params) => (React__default["default"].createElement(material.TextField, { ...params, label: "Feature type", variant: "outlined", fullWidth: true })), onChange: (oldValue, newValue) => {
53630
53651
  if (newValue) {
53631
53652
  handleChange(newValue);
53632
53653
  }
53633
53654
  } })),
53634
- React__default["default"].createElement(material.Grid2, null,
53655
+ React__default["default"].createElement(material.Grid2, { size: 4 },
53635
53656
  React__default["default"].createElement(material.Button, { variant: "contained", onClick: handleAddFeatureType, disabled: !type, style: { marginTop: 9 }, size: "medium" }, "Add"))),
53636
53657
  selectedFeatureTypes.length > 0 && (React__default["default"].createElement("div", null,
53637
53658
  React__default["default"].createElement("hr", null),
@@ -53652,6 +53673,7 @@
53652
53673
  table: false,
53653
53674
  heightPreConfig: require$$1$3.types.maybe(require$$1$3.types.refinement('displayHeight', require$$1$3.types.number, (n) => n >= minDisplayHeight)),
53654
53675
  filteredFeatureTypes: require$$1$3.types.array(require$$1$3.types.string),
53676
+ loadingState: false,
53655
53677
  })
53656
53678
  .views((self) => {
53657
53679
  const { configuration, renderProps: superRenderProps } = self;
@@ -53684,6 +53706,9 @@
53684
53706
  }
53685
53707
  return 300;
53686
53708
  },
53709
+ get loading() {
53710
+ return self.loadingState;
53711
+ },
53687
53712
  }))
53688
53713
  .views((self) => ({
53689
53714
  get rendererTypeName() {
@@ -53769,6 +53794,9 @@
53769
53794
  updateFilteredFeatureTypes(types) {
53770
53795
  self.filteredFeatureTypes = require$$1$3.cast(types);
53771
53796
  },
53797
+ setLoading(loading) {
53798
+ self.loadingState = loading;
53799
+ },
53772
53800
  }))
53773
53801
  .views((self) => {
53774
53802
  const { filteredFeatureTypes, trackMenuItems: superTrackMenuItems } = self;
@@ -53839,7 +53867,14 @@
53839
53867
  if (!self.lgv.initialized || self.regionCannotBeRendered()) {
53840
53868
  return;
53841
53869
  }
53842
- void self.session.apolloDataStore.loadFeatures(self.regions);
53870
+ self.setLoading(true);
53871
+ void self.session.apolloDataStore
53872
+ .loadFeatures(self.regions)
53873
+ .then(() => {
53874
+ setTimeout(() => {
53875
+ self.setLoading(false);
53876
+ }, 1000);
53877
+ });
53843
53878
  if (self.lgv.bpPerPx <= 3) {
53844
53879
  void self.session.apolloDataStore.loadRefSeq(self.regions);
53845
53880
  }
@@ -54268,7 +54303,6 @@
54268
54303
  const displayedRegion = displayedRegions[displayedRegionIndex];
54269
54304
  const { refName, reversed } = displayedRegion;
54270
54305
  const rowHeight = apolloRowHeight;
54271
- const exonHeight = Math.round(0.6 * rowHeight);
54272
54306
  const cdsHeight = Math.round(0.9 * rowHeight);
54273
54307
  const { children, min, strand } = feature;
54274
54308
  if (!children) {
@@ -54302,27 +54336,20 @@
54302
54336
  currentRow += 1;
54303
54337
  continue;
54304
54338
  }
54305
- const { children: childrenOfTranscript, min } = transcript;
54306
- if (!childrenOfTranscript) {
54339
+ const { children: transcriptChildren } = transcript;
54340
+ if (!transcriptChildren) {
54307
54341
  continue;
54308
54342
  }
54309
- for (const [, cds] of childrenOfTranscript) {
54310
- if (!featureTypeOntology.isTypeOf(cds.type, 'CDS')) {
54343
+ const cdsCount = getCDSCount(transcript, featureTypeOntology);
54344
+ for (const [, childFeature] of transcriptChildren) {
54345
+ if (!featureTypeOntology.isTypeOf(childFeature.type, 'CDS')) {
54311
54346
  continue;
54312
54347
  }
54313
- const minX = (lgv.bpToPx({
54314
- refName,
54315
- coord: min,
54316
- regionNumber: displayedRegionIndex,
54317
- })?.offsetPx ?? 0) - offsetPx;
54318
- const widthPx = transcript.length / bpPerPx;
54319
- const startPx = reversed ? minX - widthPx : minX;
54320
- const height = Math.round((currentRow + 1 / 2) * rowHeight) + row * rowHeight;
54321
- ctx.strokeStyle = theme?.palette.text.primary ?? 'black';
54322
- ctx.beginPath();
54323
- ctx.moveTo(startPx, height);
54324
- ctx.lineTo(startPx + widthPx, height);
54325
- ctx.stroke();
54348
+ drawLine(ctx, stateModel, displayedRegionIndex, row, transcript, currentRow);
54349
+ currentRow += 1;
54350
+ }
54351
+ if (cdsCount === 0) {
54352
+ drawLine(ctx, stateModel, displayedRegionIndex, row, transcript, currentRow);
54326
54353
  currentRow += 1;
54327
54354
  }
54328
54355
  }
@@ -54336,83 +54363,125 @@
54336
54363
  currentRow += 1;
54337
54364
  continue;
54338
54365
  }
54339
- for (const cdsRow of child.cdsLocations) {
54340
- const { _id, children: childrenOfTranscript } = child;
54341
- if (!childrenOfTranscript) {
54342
- continue;
54343
- }
54344
- for (const [, exon] of childrenOfTranscript) {
54345
- if (!featureTypeOntology.isTypeOf(exon.type, 'exon')) {
54366
+ const cdsCount = getCDSCount(child, featureTypeOntology);
54367
+ if (cdsCount != 0) {
54368
+ for (const cdsRow of child.cdsLocations) {
54369
+ const { _id, children: transcriptChildren } = child;
54370
+ if (!transcriptChildren) {
54346
54371
  continue;
54347
54372
  }
54348
- const minX = (lgv.bpToPx({
54349
- refName,
54350
- coord: exon.min,
54351
- regionNumber: displayedRegionIndex,
54352
- })?.offsetPx ?? 0) - offsetPx;
54353
- const widthPx = exon.length / bpPerPx;
54354
- const startPx = reversed ? minX - widthPx : minX;
54355
- const top = (row + currentRow) * rowHeight;
54356
- const exonTop = top + (rowHeight - exonHeight) / 2;
54357
- ctx.fillStyle = theme?.palette.text.primary ?? 'black';
54358
- ctx.fillRect(startPx, exonTop, widthPx, exonHeight);
54359
- if (widthPx > 2) {
54360
- ctx.clearRect(startPx + 1, exonTop + 1, widthPx - 2, exonHeight - 2);
54361
- ctx.fillStyle =
54362
- apolloSelectedFeature && exon._id === apolloSelectedFeature._id
54363
- ? 'rgb(0,0,0)'
54364
- : 'rgb(211,211,211)';
54365
- ctx.fillRect(startPx + 1, exonTop + 1, widthPx - 2, exonHeight - 2);
54366
- if (forwardFill && backwardFill && strand) {
54367
- const reversal = reversed ? -1 : 1;
54368
- const [topFill, bottomFill] = strand * reversal === 1
54369
- ? [forwardFill, backwardFill]
54370
- : [backwardFill, forwardFill];
54371
- ctx.fillStyle = topFill;
54372
- ctx.fillRect(startPx + 1, exonTop + 1, widthPx - 2, (exonHeight - 2) / 2);
54373
- ctx.fillStyle = bottomFill;
54374
- ctx.fillRect(startPx + 1, exonTop + 1 + (exonHeight - 2) / 2, widthPx - 2, (exonHeight - 2) / 2);
54373
+ for (const [, exon] of transcriptChildren) {
54374
+ if (!featureTypeOntology.isTypeOf(exon.type, 'exon')) {
54375
+ continue;
54376
+ }
54377
+ drawExon(ctx, stateModel, displayedRegionIndex, row, exon, currentRow, strand, forwardFill, backwardFill);
54378
+ }
54379
+ for (const cds of cdsRow) {
54380
+ const cdsWidthPx = (cds.max - cds.min) / bpPerPx;
54381
+ const minX = (lgv.bpToPx({
54382
+ refName,
54383
+ coord: cds.min,
54384
+ regionNumber: displayedRegionIndex,
54385
+ })?.offsetPx ?? 0) - offsetPx;
54386
+ const cdsStartPx = reversed ? minX - cdsWidthPx : minX;
54387
+ ctx.fillStyle = theme?.palette.text.primary ?? 'black';
54388
+ const cdsTop = (row + currentRow) * rowHeight + (rowHeight - cdsHeight) / 2;
54389
+ ctx.fillRect(cdsStartPx, cdsTop, cdsWidthPx, cdsHeight);
54390
+ if (cdsWidthPx > 2) {
54391
+ ctx.clearRect(cdsStartPx + 1, cdsTop + 1, cdsWidthPx - 2, cdsHeight - 2);
54392
+ const frame = require$$1$2.getFrame(cds.min, cds.max, child.strand ?? 1, cds.phase);
54393
+ const frameColor = theme?.palette.framesCDS.at(frame)?.main;
54394
+ const cdsColorCode = frameColor ?? 'rgb(171,71,188)';
54395
+ ctx.fillStyle =
54396
+ apolloSelectedFeature && _id === apolloSelectedFeature._id
54397
+ ? 'rgb(0,0,0)'
54398
+ : cdsColorCode;
54399
+ ctx.fillStyle = cdsColorCode;
54400
+ ctx.fillRect(cdsStartPx + 1, cdsTop + 1, cdsWidthPx - 2, cdsHeight - 2);
54401
+ if (forwardFill && backwardFill && strand) {
54402
+ const reversal = reversed ? -1 : 1;
54403
+ const [topFill, bottomFill] = strand * reversal === 1
54404
+ ? [forwardFill, backwardFill]
54405
+ : [backwardFill, forwardFill];
54406
+ ctx.fillStyle = topFill;
54407
+ ctx.fillRect(cdsStartPx + 1, cdsTop + 1, cdsWidthPx - 2, (cdsHeight - 2) / 2);
54408
+ ctx.fillStyle = bottomFill;
54409
+ ctx.fillRect(cdsStartPx + 1, cdsTop + (cdsHeight - 2) / 2, cdsWidthPx - 2, (cdsHeight - 2) / 2);
54410
+ }
54375
54411
  }
54376
54412
  }
54413
+ currentRow += 1;
54377
54414
  }
54378
- for (const cds of cdsRow) {
54379
- const cdsWidthPx = (cds.max - cds.min) / bpPerPx;
54380
- const minX = (lgv.bpToPx({
54381
- refName,
54382
- coord: cds.min,
54383
- regionNumber: displayedRegionIndex,
54384
- })?.offsetPx ?? 0) - offsetPx;
54385
- const cdsStartPx = reversed ? minX - cdsWidthPx : minX;
54386
- ctx.fillStyle = theme?.palette.text.primary ?? 'black';
54387
- const cdsTop = (row + currentRow) * rowHeight + (rowHeight - cdsHeight) / 2;
54388
- ctx.fillRect(cdsStartPx, cdsTop, cdsWidthPx, cdsHeight);
54389
- if (cdsWidthPx > 2) {
54390
- ctx.clearRect(cdsStartPx + 1, cdsTop + 1, cdsWidthPx - 2, cdsHeight - 2);
54391
- const frame = require$$1$2.getFrame(cds.min, cds.max, child.strand ?? 1, cds.phase);
54392
- const frameColor = theme?.palette.framesCDS.at(frame)?.main;
54393
- const cdsColorCode = frameColor ?? 'rgb(171,71,188)';
54394
- ctx.fillStyle =
54395
- apolloSelectedFeature && _id === apolloSelectedFeature._id
54396
- ? 'rgb(0,0,0)'
54397
- : cdsColorCode;
54398
- ctx.fillStyle = cdsColorCode;
54399
- ctx.fillRect(cdsStartPx + 1, cdsTop + 1, cdsWidthPx - 2, cdsHeight - 2);
54400
- if (forwardFill && backwardFill && strand) {
54401
- const reversal = reversed ? -1 : 1;
54402
- const [topFill, bottomFill] = strand * reversal === 1
54403
- ? [forwardFill, backwardFill]
54404
- : [backwardFill, forwardFill];
54405
- ctx.fillStyle = topFill;
54406
- ctx.fillRect(cdsStartPx + 1, cdsTop + 1, cdsWidthPx - 2, (cdsHeight - 2) / 2);
54407
- ctx.fillStyle = bottomFill;
54408
- ctx.fillRect(cdsStartPx + 1, cdsTop + (cdsHeight - 2) / 2, cdsWidthPx - 2, (cdsHeight - 2) / 2);
54409
- }
54415
+ }
54416
+ const { children: transcriptChildren } = child;
54417
+ // Draw exons for non-coding genes
54418
+ if (cdsCount === 0 && transcriptChildren) {
54419
+ for (const [, exon] of transcriptChildren) {
54420
+ if (!featureTypeOntology.isTypeOf(exon.type, 'exon')) {
54421
+ continue;
54410
54422
  }
54423
+ drawExon(ctx, stateModel, displayedRegionIndex, row, exon, currentRow, strand, forwardFill, backwardFill);
54411
54424
  }
54412
54425
  currentRow += 1;
54413
54426
  }
54414
54427
  }
54415
54428
  }
54429
+ function drawExon(ctx, stateModel, displayedRegionIndex, row, exon, currentRow, strand, forwardFill, backwardFill) {
54430
+ const { apolloRowHeight, lgv, session, theme } = stateModel;
54431
+ const { bpPerPx, displayedRegions, offsetPx } = lgv;
54432
+ const displayedRegion = displayedRegions[displayedRegionIndex];
54433
+ const { refName, reversed } = displayedRegion;
54434
+ const { apolloSelectedFeature } = session;
54435
+ const minX = (lgv.bpToPx({
54436
+ refName,
54437
+ coord: exon.min,
54438
+ regionNumber: displayedRegionIndex,
54439
+ })?.offsetPx ?? 0) - offsetPx;
54440
+ const widthPx = exon.length / bpPerPx;
54441
+ const startPx = reversed ? minX - widthPx : minX;
54442
+ const top = (row + currentRow) * apolloRowHeight;
54443
+ const exonHeight = Math.round(0.6 * apolloRowHeight);
54444
+ const exonTop = top + (apolloRowHeight - exonHeight) / 2;
54445
+ ctx.fillStyle = theme?.palette.text.primary ?? 'black';
54446
+ ctx.fillRect(startPx, exonTop, widthPx, exonHeight);
54447
+ if (widthPx > 2) {
54448
+ ctx.clearRect(startPx + 1, exonTop + 1, widthPx - 2, exonHeight - 2);
54449
+ ctx.fillStyle =
54450
+ apolloSelectedFeature && exon._id === apolloSelectedFeature._id
54451
+ ? 'rgb(0,0,0)'
54452
+ : 'rgb(211,211,211)';
54453
+ ctx.fillRect(startPx + 1, exonTop + 1, widthPx - 2, exonHeight - 2);
54454
+ if (forwardFill && backwardFill && strand) {
54455
+ const reversal = reversed ? -1 : 1;
54456
+ const [topFill, bottomFill] = strand * reversal === 1
54457
+ ? [forwardFill, backwardFill]
54458
+ : [backwardFill, forwardFill];
54459
+ ctx.fillStyle = topFill;
54460
+ ctx.fillRect(startPx + 1, exonTop + 1, widthPx - 2, (exonHeight - 2) / 2);
54461
+ ctx.fillStyle = bottomFill;
54462
+ ctx.fillRect(startPx + 1, exonTop + 1 + (exonHeight - 2) / 2, widthPx - 2, (exonHeight - 2) / 2);
54463
+ }
54464
+ }
54465
+ }
54466
+ function drawLine(ctx, stateModel, displayedRegionIndex, row, transcript, currentRow) {
54467
+ const { apolloRowHeight, lgv, theme } = stateModel;
54468
+ const { bpPerPx, displayedRegions, offsetPx } = lgv;
54469
+ const displayedRegion = displayedRegions[displayedRegionIndex];
54470
+ const { refName, reversed } = displayedRegion;
54471
+ const minX = (lgv.bpToPx({
54472
+ refName,
54473
+ coord: transcript.min,
54474
+ regionNumber: displayedRegionIndex,
54475
+ })?.offsetPx ?? 0) - offsetPx;
54476
+ const widthPx = transcript.length / bpPerPx;
54477
+ const startPx = reversed ? minX - widthPx : minX;
54478
+ const height = Math.round((currentRow + 1 / 2) * apolloRowHeight) + row * apolloRowHeight;
54479
+ ctx.strokeStyle = theme?.palette.text.primary ?? 'black';
54480
+ ctx.beginPath();
54481
+ ctx.moveTo(startPx, height);
54482
+ ctx.lineTo(startPx + widthPx, height);
54483
+ ctx.stroke();
54484
+ }
54416
54485
  function drawDragPreview$1(stateModel, overlayCtx) {
54417
54486
  const { apolloDragging, apolloRowHeight, lgv, theme } = stateModel;
54418
54487
  const { bpPerPx, displayedRegions, offsetPx } = lgv;
@@ -54496,6 +54565,22 @@
54496
54565
  }
54497
54566
  return feature;
54498
54567
  }
54568
+ function getCDSCount(feature, featureTypeOntology) {
54569
+ const { children, type } = feature;
54570
+ if (!children) {
54571
+ return 0;
54572
+ }
54573
+ const isMrna = featureTypeOntology.isTypeOf(type, 'mRNA');
54574
+ let cdsCount = 0;
54575
+ if (isMrna) {
54576
+ for (const [, child] of children) {
54577
+ if (featureTypeOntology.isTypeOf(child.type, 'CDS')) {
54578
+ cdsCount += 1;
54579
+ }
54580
+ }
54581
+ }
54582
+ return cdsCount;
54583
+ }
54499
54584
  function getRowCount$1(feature, featureTypeOntology, _bpPerPx) {
54500
54585
  const { children, type } = feature;
54501
54586
  if (!children) {
@@ -54505,12 +54590,12 @@
54505
54590
  let rowCount = 0;
54506
54591
  if (isTranscript) {
54507
54592
  for (const [, child] of children) {
54508
- const isCds = featureTypeOntology.isTypeOf(child.type, 'CDS');
54509
- if (isCds) {
54593
+ if (featureTypeOntology.isTypeOf(child.type, 'CDS')) {
54510
54594
  rowCount += 1;
54511
54595
  }
54512
54596
  }
54513
- return rowCount;
54597
+ // return 1 if there are no CDSs for non coding genes
54598
+ return rowCount === 0 ? 1 : rowCount;
54514
54599
  }
54515
54600
  for (const [, child] of children) {
54516
54601
  rowCount += getRowCount$1(child, featureTypeOntology);
@@ -54554,6 +54639,9 @@
54554
54639
  for (const cds of cdss) {
54555
54640
  features.push([cds, ...exons, child, feature]);
54556
54641
  }
54642
+ if (cdss.length === 0) {
54643
+ features.push([...exons, child, feature]);
54644
+ }
54557
54645
  }
54558
54646
  return features;
54559
54647
  }
@@ -54801,49 +54889,39 @@
54801
54889
  };
54802
54890
 
54803
54891
  /* eslint-disable @typescript-eslint/no-unnecessary-condition */
54892
+ function getRowsForFeature(startingRow, rowCount, filledRowLocations) {
54893
+ const rowsForFeature = [];
54894
+ for (let i = startingRow; i < startingRow + rowCount; i++) {
54895
+ const row = filledRowLocations.get(i);
54896
+ if (row) {
54897
+ rowsForFeature.push(row);
54898
+ }
54899
+ }
54900
+ return rowsForFeature;
54901
+ }
54902
+ function canPlaceFeatureInRows(rowsForFeature, feature) {
54903
+ for (const rowForFeature of rowsForFeature) {
54904
+ for (const [rowStart, rowEnd] of rowForFeature) {
54905
+ if (require$$1$2.doesIntersect2(feature.min, feature.max, rowStart, rowEnd) ||
54906
+ require$$1$2.doesIntersect2(rowStart, rowEnd, feature.min, feature.max)) {
54907
+ return false;
54908
+ }
54909
+ }
54910
+ }
54911
+ return true;
54912
+ }
54804
54913
  function layoutsModelFactory(pluginManager, configSchema) {
54805
54914
  const BaseLinearApolloDisplay = baseModelFactory(pluginManager, configSchema);
54806
54915
  return BaseLinearApolloDisplay.named('LinearApolloDisplayLayouts')
54807
54916
  .props({
54808
- featuresMinMaxLimit: 500_000,
54917
+ cleanupBoundary: 200_000,
54809
54918
  })
54810
54919
  .volatile(() => ({
54811
54920
  seenFeatures: mobx.observable.map(),
54812
54921
  }))
54813
54922
  .views((self) => ({
54814
- get featuresMinMax() {
54815
- const { assemblyManager } = self.session;
54816
- return self.lgv.displayedRegions.map((region) => {
54817
- const assembly = assemblyManager.get(region.assemblyName);
54818
- let min;
54819
- let max;
54820
- const { end, refName, start } = region;
54821
- for (const [, feature] of self.seenFeatures) {
54822
- if (refName !== assembly?.getCanonicalRefName(feature.refSeq) ||
54823
- !require$$1$2.doesIntersect2(start, end, feature.min, feature.max) ||
54824
- feature.length > self.featuresMinMaxLimit ||
54825
- (self.filteredFeatureTypes.length > 0 &&
54826
- !self.filteredFeatureTypes.includes(feature.type))) {
54827
- continue;
54828
- }
54829
- if (min === undefined) {
54830
- ({ min } = feature);
54831
- }
54832
- if (max === undefined) {
54833
- ({ max } = feature);
54834
- }
54835
- if (feature.minWithChildren < min) {
54836
- ({ min } = feature);
54837
- }
54838
- if (feature.maxWithChildren > max) {
54839
- ({ max } = feature);
54840
- }
54841
- }
54842
- if (min !== undefined && max !== undefined) {
54843
- return [min, max];
54844
- }
54845
- return;
54846
- });
54923
+ getAnnotationFeatureById(id) {
54924
+ return self.seenFeatures.get(id);
54847
54925
  },
54848
54926
  getGlyph(feature) {
54849
54927
  if (this.looksLikeGene(feature)) {
@@ -54873,11 +54951,7 @@
54873
54951
  if (!grandChildren?.size) {
54874
54952
  return false;
54875
54953
  }
54876
- const hasCDS = [...grandChildren.values()].some((grandchild) => featureTypeOntology.isTypeOf(grandchild.type, 'CDS'));
54877
- const hasExon = [...grandChildren.values()].some((grandchild) => featureTypeOntology.isTypeOf(grandchild.type, 'exon'));
54878
- if (hasCDS && hasExon) {
54879
- return true;
54880
- }
54954
+ return [...grandChildren.values()].some((grandchild) => featureTypeOntology.isTypeOf(grandchild.type, 'exon'));
54881
54955
  }
54882
54956
  }
54883
54957
  return false;
@@ -54894,15 +54968,11 @@
54894
54968
  .views((self) => ({
54895
54969
  get featureLayouts() {
54896
54970
  const { assemblyManager } = self.session;
54897
- return self.lgv.displayedRegions.map((region, idx) => {
54971
+ return self.lgv.displayedRegions.map((region) => {
54898
54972
  const assembly = assemblyManager.get(region.assemblyName);
54899
54973
  const featureLayout = new Map();
54900
- const minMax = self.featuresMinMax[idx];
54901
- if (!minMax) {
54902
- return featureLayout;
54903
- }
54904
- const [min, max] = minMax;
54905
- const rows = [];
54974
+ // Track the occupied coordinates in each row
54975
+ const filledRowLocations = new Map();
54906
54976
  const { end, refName, start } = region;
54907
54977
  for (const [id, feature] of self.seenFeatures.entries()) {
54908
54978
  if (!require$$1$3.isAlive(feature)) {
@@ -54925,42 +54995,23 @@
54925
54995
  let startingRow = 0;
54926
54996
  let placed = false;
54927
54997
  while (!placed) {
54928
- let rowsForFeature = rows.slice(startingRow, startingRow + rowCount);
54998
+ let rowsForFeature = getRowsForFeature(startingRow, rowCount, filledRowLocations);
54929
54999
  if (rowsForFeature.length < rowCount) {
54930
55000
  for (let i = 0; i < rowCount - rowsForFeature.length; i++) {
54931
- const newRowNumber = rows.length;
54932
- rows[newRowNumber] = Array.from({ length: max - min });
55001
+ const newRowNumber = filledRowLocations.size;
55002
+ filledRowLocations.set(newRowNumber, []);
54933
55003
  featureLayout.set(newRowNumber, []);
54934
55004
  }
54935
- rowsForFeature = rows.slice(startingRow, startingRow + rowCount);
55005
+ rowsForFeature = getRowsForFeature(startingRow, rowCount, filledRowLocations);
54936
55006
  }
54937
- if (rowsForFeature
54938
- .map((rowForFeature) => {
54939
- // zero-length features are allowed in the spec
54940
- const featureMax = feature.max - feature.min === 0
54941
- ? feature.min + 1
54942
- : feature.max;
54943
- let start = feature.min - min, end = featureMax - min;
54944
- if (feature.min - min < 0) {
54945
- start = 0;
54946
- end = featureMax - feature.min;
54947
- }
54948
- return rowForFeature.slice(start, end).some(Boolean);
54949
- })
54950
- .some(Boolean)) {
55007
+ if (!canPlaceFeatureInRows(rowsForFeature, feature)) {
54951
55008
  startingRow += 1;
54952
55009
  continue;
54953
55010
  }
54954
55011
  for (let rowNum = startingRow; rowNum < startingRow + rowCount; rowNum++) {
54955
- const row = rows[rowNum];
54956
- let start = feature.min - min, end = feature.max - min;
54957
- if (feature.min - min < 0) {
54958
- start = 0;
54959
- end = feature.max - feature.min;
54960
- }
54961
- row.fill(true, start, end);
55012
+ filledRowLocations.get(rowNum)?.push([feature.min, feature.max]);
54962
55013
  const layoutRow = featureLayout.get(rowNum);
54963
- layoutRow?.push([rowNum - startingRow, feature]);
55014
+ layoutRow?.push([rowNum - startingRow, feature._id]);
54964
55015
  }
54965
55016
  placed = true;
54966
55017
  }
@@ -54973,12 +55024,16 @@
54973
55024
  const { featureTypeOntology } = self.session.apolloDataStore.ontologyManager;
54974
55025
  for (const [idx, layout] of featureLayouts.entries()) {
54975
55026
  for (const [layoutRowNum, layoutRow] of layout) {
54976
- for (const [featureRowNum, layoutFeature] of layoutRow) {
55027
+ for (const [featureRowNum, layoutFeatureId] of layoutRow) {
54977
55028
  if (featureRowNum !== 0) {
54978
55029
  // Same top-level feature in all feature rows, so only need to
54979
55030
  // check the first one
54980
55031
  continue;
54981
55032
  }
55033
+ const layoutFeature = self.getAnnotationFeatureById(layoutFeatureId);
55034
+ if (!layoutFeature) {
55035
+ continue;
55036
+ }
54982
55037
  if (feature._id === layoutFeature._id) {
54983
55038
  return {
54984
55039
  layoutIndex: idx,
@@ -55018,6 +55073,23 @@
55018
55073
  if (!self.lgv.initialized || self.regionCannotBeRendered()) {
55019
55074
  return;
55020
55075
  }
55076
+ // Clear out features that are no longer in the view and out of the cleanup boundary
55077
+ // cleanup boundary + region boundary + cleanup boundary
55078
+ for (const [id, feature] of self.seenFeatures.entries()) {
55079
+ let shouldKeep = false;
55080
+ for (const region of self.regions) {
55081
+ const extendedStart = region.start - self.cleanupBoundary;
55082
+ const extendedEnd = region.end + self.cleanupBoundary;
55083
+ if (require$$1$2.doesIntersect2(extendedStart, extendedEnd, feature.min, feature.max)) {
55084
+ shouldKeep = true;
55085
+ break;
55086
+ }
55087
+ }
55088
+ if (!shouldKeep) {
55089
+ self.deleteSeenFeature(id);
55090
+ }
55091
+ }
55092
+ // Add features that are in the current view
55021
55093
  for (const region of self.regions) {
55022
55094
  const assembly = self.session.apolloDataStore.assemblies.get(region.assemblyName);
55023
55095
  const ref = assembly?.getByRefName(region.refName);
@@ -55291,8 +55363,9 @@
55291
55363
  for (const [idx, featureLayout] of featureLayouts.entries()) {
55292
55364
  const displayedRegion = displayedRegions[idx];
55293
55365
  for (const [row, featureLayoutRow] of featureLayout.entries()) {
55294
- for (const [featureRow, feature] of featureLayoutRow) {
55295
- if (featureRow > 0) {
55366
+ for (const [featureRow, featureId] of featureLayoutRow) {
55367
+ const feature = self.getAnnotationFeatureById(featureId);
55368
+ if (featureRow > 0 || !feature) {
55296
55369
  continue;
55297
55370
  }
55298
55371
  if (!require$$1$2.doesIntersect2(displayedRegion.start, displayedRegion.end, feature.min, feature.max)) {
@@ -55373,11 +55446,18 @@
55373
55446
  if (!layoutRow) {
55374
55447
  return mousePosition;
55375
55448
  }
55376
- const foundFeature = layoutRow.find((f) => bp >= f[1].min && bp <= f[1].max);
55449
+ const foundFeature = layoutRow.find((f) => {
55450
+ const feature = self.getAnnotationFeatureById(f[1]);
55451
+ return feature && bp >= feature.min && bp <= feature.max;
55452
+ });
55377
55453
  if (!foundFeature) {
55378
55454
  return mousePosition;
55379
55455
  }
55380
- const [featureRow, topLevelFeature] = foundFeature;
55456
+ const [featureRow, topLevelFeatureId] = foundFeature;
55457
+ const topLevelFeature = self.getAnnotationFeatureById(topLevelFeatureId);
55458
+ if (!topLevelFeature) {
55459
+ return mousePosition;
55460
+ }
55381
55461
  const glyph = self.getGlyph(topLevelFeature);
55382
55462
  const { featureTypeOntology } = self.session.apolloDataStore.ontologyManager;
55383
55463
  if (!featureTypeOntology) {
@@ -55703,11 +55783,18 @@
55703
55783
  color: theme.palette.warning.light,
55704
55784
  backgroundColor: theme.palette.warning.contrastText,
55705
55785
  },
55786
+ loading: {
55787
+ position: 'absolute',
55788
+ right: theme.spacing(3),
55789
+ zIndex: 10,
55790
+ pointerEvents: 'none',
55791
+ textAlign: 'right',
55792
+ },
55706
55793
  }));
55707
55794
  const LinearApolloDisplay = mobxReact.observer(function LinearApolloDisplay(props) {
55708
55795
  const theme = material.useTheme();
55709
55796
  const { model } = props;
55710
- const { apolloRowHeight, contextMenuItems: getContextMenuItems, cursor, featuresHeight, isShown, onMouseDown, onMouseLeave, onMouseMove, onMouseUp, regionCannotBeRendered, session, setCanvas, setCollaboratorCanvas, setOverlayCanvas, setSeqTrackCanvas, setSeqTrackOverlayCanvas, setTheme, } = model;
55797
+ const { loading, apolloRowHeight, contextMenuItems: getContextMenuItems, cursor, featuresHeight, isShown, onMouseDown, onMouseLeave, onMouseMove, onMouseUp, regionCannotBeRendered, session, setCanvas, setCollaboratorCanvas, setOverlayCanvas, setSeqTrackCanvas, setSeqTrackOverlayCanvas, setTheme, } = model;
55711
55798
  const { classes } = useStyles$1();
55712
55799
  const lgv = require$$1$2.getContainingView(model);
55713
55800
  React.useEffect(() => {
@@ -55747,65 +55834,68 @@
55747
55834
  setContextCoord(coord);
55748
55835
  setContextMenuItems(getContextMenuItems(coord));
55749
55836
  }
55750
- } }, message ? (React__default["default"].createElement(material.Alert, { severity: "warning", classes: { message: classes.ellipses } },
55751
- React__default["default"].createElement(material.Tooltip, { title: message },
55752
- React__default["default"].createElement("div", null, message)))) : (
55753
- // Promise.resolve() in these 3 callbacks is to avoid infinite rendering loop
55754
- // https://github.com/mobxjs/mobx/issues/3728#issuecomment-1715400931
55755
- React__default["default"].createElement(React__default["default"].Fragment, null,
55756
- React__default["default"].createElement("canvas", { ref: async (node) => {
55757
- await Promise.resolve();
55758
- setCollaboratorCanvas(node);
55759
- }, width: lgv.dynamicBlocks.totalWidthPx, height: featuresHeight, className: classes.canvas, "data-testid": "collaboratorCanvas" }),
55760
- React__default["default"].createElement("canvas", { ref: async (node) => {
55761
- await Promise.resolve();
55762
- setCanvas(node);
55763
- }, width: lgv.dynamicBlocks.totalWidthPx, height: featuresHeight, className: classes.canvas, "data-testid": "canvas" }),
55764
- React__default["default"].createElement("canvas", { ref: async (node) => {
55765
- await Promise.resolve();
55766
- setOverlayCanvas(node);
55767
- }, width: lgv.dynamicBlocks.totalWidthPx, height: featuresHeight, onMouseMove: onMouseMove, onMouseLeave: onMouseLeave, onMouseDown: onMouseDown, onMouseUp: onMouseUp, className: classes.canvas, style: { cursor: cursor ?? 'default' }, "data-testid": "overlayCanvas" }),
55768
- lgv.displayedRegions.flatMap((region, idx) => {
55769
- const assembly = assemblyManager.get(region.assemblyName);
55770
- return [...session.apolloDataStore.checkResults.values()]
55771
- .filter((checkResult) => assembly?.isValidRefName(checkResult.refSeq) &&
55772
- assembly.getCanonicalRefName(checkResult.refSeq) ===
55773
- region.refName &&
55774
- require$$1$2.doesIntersect2(region.start, region.end, checkResult.start, checkResult.end))
55775
- .map((checkResult) => {
55776
- const left = (lgv.bpToPx({
55777
- refName: region.refName,
55778
- coord: checkResult.start,
55779
- regionNumber: idx,
55780
- })?.offsetPx ?? 0) - lgv.offsetPx;
55781
- const [feature] = checkResult.ids;
55782
- if (!feature) {
55783
- return null;
55784
- }
55785
- const { topLevelFeature } = feature;
55786
- const row = parent
55787
- ? model.getFeatureLayoutPosition(topLevelFeature)
55788
- ?.layoutRow ?? 0
55789
- : 0;
55790
- const top = row * apolloRowHeight;
55791
- const height = apolloRowHeight;
55792
- return (React__default["default"].createElement(material.Tooltip, { key: checkResult._id, title: checkResult.message },
55793
- React__default["default"].createElement(material.Avatar, { className: classes.avatar, style: { top, left, height, width: height } },
55794
- React__default["default"].createElement(default_1$1, null))));
55795
- });
55796
- }),
55797
- React__default["default"].createElement(ui.Menu, { open: contextMenuItems.length > 0, onMenuItemClick: (_, callback) => {
55798
- callback();
55799
- setContextMenuItems([]);
55800
- }, onClose: () => {
55801
- setContextMenuItems([]);
55802
- }, TransitionProps: {
55803
- onExit: () => {
55837
+ } },
55838
+ loading ? (React__default["default"].createElement("div", { className: classes.loading },
55839
+ React__default["default"].createElement(material.CircularProgress, { size: "18px" }))) : null,
55840
+ message ? (React__default["default"].createElement(material.Alert, { severity: "warning", classes: { message: classes.ellipses } },
55841
+ React__default["default"].createElement(material.Tooltip, { title: message },
55842
+ React__default["default"].createElement("div", null, message)))) : (
55843
+ // Promise.resolve() in these 3 callbacks is to avoid infinite rendering loop
55844
+ // https://github.com/mobxjs/mobx/issues/3728#issuecomment-1715400931
55845
+ React__default["default"].createElement(React__default["default"].Fragment, null,
55846
+ React__default["default"].createElement("canvas", { ref: async (node) => {
55847
+ await Promise.resolve();
55848
+ setCollaboratorCanvas(node);
55849
+ }, width: lgv.dynamicBlocks.totalWidthPx, height: featuresHeight, className: classes.canvas, "data-testid": "collaboratorCanvas" }),
55850
+ React__default["default"].createElement("canvas", { ref: async (node) => {
55851
+ await Promise.resolve();
55852
+ setCanvas(node);
55853
+ }, width: lgv.dynamicBlocks.totalWidthPx, height: featuresHeight, className: classes.canvas, "data-testid": "canvas" }),
55854
+ React__default["default"].createElement("canvas", { ref: async (node) => {
55855
+ await Promise.resolve();
55856
+ setOverlayCanvas(node);
55857
+ }, width: lgv.dynamicBlocks.totalWidthPx, height: featuresHeight, onMouseMove: onMouseMove, onMouseLeave: onMouseLeave, onMouseDown: onMouseDown, onMouseUp: onMouseUp, className: classes.canvas, style: { cursor: cursor ?? 'default' }, "data-testid": "overlayCanvas" }),
55858
+ lgv.displayedRegions.flatMap((region, idx) => {
55859
+ const assembly = assemblyManager.get(region.assemblyName);
55860
+ return [...session.apolloDataStore.checkResults.values()]
55861
+ .filter((checkResult) => assembly?.isValidRefName(checkResult.refSeq) &&
55862
+ assembly.getCanonicalRefName(checkResult.refSeq) ===
55863
+ region.refName &&
55864
+ require$$1$2.doesIntersect2(region.start, region.end, checkResult.start, checkResult.end))
55865
+ .map((checkResult) => {
55866
+ const left = (lgv.bpToPx({
55867
+ refName: region.refName,
55868
+ coord: checkResult.start,
55869
+ regionNumber: idx,
55870
+ })?.offsetPx ?? 0) - lgv.offsetPx;
55871
+ const [feature] = checkResult.ids;
55872
+ if (!feature) {
55873
+ return null;
55874
+ }
55875
+ const { topLevelFeature } = feature;
55876
+ const row = parent
55877
+ ? model.getFeatureLayoutPosition(topLevelFeature)
55878
+ ?.layoutRow ?? 0
55879
+ : 0;
55880
+ const top = row * apolloRowHeight;
55881
+ const height = apolloRowHeight;
55882
+ return (React__default["default"].createElement(material.Tooltip, { key: checkResult._id, title: checkResult.message },
55883
+ React__default["default"].createElement(material.Avatar, { className: classes.avatar, style: { top, left, height, width: height } },
55884
+ React__default["default"].createElement(default_1$1, null))));
55885
+ });
55886
+ }),
55887
+ React__default["default"].createElement(ui.Menu, { open: contextMenuItems.length > 0, onMenuItemClick: (_, callback) => {
55888
+ callback();
55804
55889
  setContextMenuItems([]);
55805
- },
55806
- }, anchorReference: "anchorPosition", anchorPosition: contextCoord
55807
- ? { top: contextCoord[1], left: contextCoord[0] }
55808
- : undefined, style: { zIndex: theme.zIndex.tooltip }, menuItems: contextMenuItems }))))));
55890
+ }, onClose: () => {
55891
+ setContextMenuItems([]);
55892
+ }, TransitionProps: {
55893
+ onExit: () => {
55894
+ setContextMenuItems([]);
55895
+ },
55896
+ }, anchorReference: "anchorPosition", anchorPosition: contextCoord
55897
+ ? { top: contextCoord[1], left: contextCoord[0] }
55898
+ : undefined, style: { zIndex: theme.zIndex.tooltip }, menuItems: contextMenuItems }))))));
55809
55899
  });
55810
55900
 
55811
55901
  const TrackLines = mobxReact.observer(function TrackLines({ model, }) {