@apollo-annotation/jbrowse-plugin-apollo 0.3.9 → 0.3.11
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.
- package/dist/index.esm.js +235 -120
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +233 -118
- package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.development.js +562 -298
- package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
- package/package.json +4 -4
- package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +1 -1
- package/src/ApolloInternetAccount/model.ts +6 -2
- package/src/BackendDrivers/CollaborationServerDriver.ts +11 -5
- package/src/ChangeManager.ts +19 -4
- package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +29 -9
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +2 -2
- package/src/LinearApolloDisplay/glyphs/util.ts +17 -0
- package/src/LinearApolloReferenceSequenceDisplay/drawSequenceOverlay.ts +18 -25
- package/src/LinearApolloReferenceSequenceDisplay/drawSequenceTrack.ts +41 -59
- package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +33 -2
- package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +101 -3
- package/src/components/AddAssembly.tsx +1 -1
- package/src/components/ImportFeatures.tsx +1 -1
- package/src/components/OntologyTermAutocomplete.tsx +2 -2
- package/src/components/OntologyTermMultiSelect.tsx +2 -2
- package/src/makeDisplayComponent.tsx +1 -1
- package/src/session/ClientDataStore.ts +1 -1
- package/src/session/session.ts +4 -0
- package/src/util/displayUtils.ts +28 -0
- package/src/util/glyphUtils.ts +18 -0
|
@@ -122,7 +122,7 @@ var TrackChangesIcon__default = /*#__PURE__*/_interopDefaultLegacy(TrackChangesI
|
|
|
122
122
|
var UndoIcon__default = /*#__PURE__*/_interopDefaultLegacy(UndoIcon);
|
|
123
123
|
var SaveIcon__default = /*#__PURE__*/_interopDefaultLegacy(SaveIcon);
|
|
124
124
|
|
|
125
|
-
var version = "0.3.
|
|
125
|
+
var version = "0.3.11";
|
|
126
126
|
|
|
127
127
|
const ApolloConfigSchema = configuration.ConfigurationSchema('ApolloInternetAccount', {
|
|
128
128
|
baseURL: {
|
|
@@ -557,6 +557,19 @@ function getContextMenuItemsForFeature$2(display, sourceFeature) {
|
|
|
557
557
|
]);
|
|
558
558
|
},
|
|
559
559
|
});
|
|
560
|
+
if (util.isSessionModelWithWidgets(session)) {
|
|
561
|
+
menuItems.push({
|
|
562
|
+
label: 'Open feature details',
|
|
563
|
+
onClick: () => {
|
|
564
|
+
const apolloGeneWidget = session.addWidget('ApolloFeatureDetailsWidget', 'apolloFeatureDetailsWidget', {
|
|
565
|
+
feature: sourceFeature,
|
|
566
|
+
assembly: currentAssemblyId,
|
|
567
|
+
refName: region.refName,
|
|
568
|
+
});
|
|
569
|
+
session.showWidget(apolloGeneWidget);
|
|
570
|
+
},
|
|
571
|
+
});
|
|
572
|
+
}
|
|
560
573
|
return menuItems;
|
|
561
574
|
}
|
|
562
575
|
function navToFeatureCenter(feature, paddingPct, refSeqLength) {
|
|
@@ -805,7 +818,7 @@ function AddAssembly({ changeManager, handleClose, session, }) {
|
|
|
805
818
|
statusMessage: 'Pre-validating',
|
|
806
819
|
progressPct: 0,
|
|
807
820
|
cancelCallback: () => {
|
|
808
|
-
controller.abort();
|
|
821
|
+
controller.abort('AddAssembly');
|
|
809
822
|
jobsManager.abortJob(job.name);
|
|
810
823
|
},
|
|
811
824
|
};
|
|
@@ -2203,7 +2216,7 @@ function OntologyTermAutocomplete({ fetchValidTerms, filterTerms: filterTermsPro
|
|
|
2203
2216
|
});
|
|
2204
2217
|
}
|
|
2205
2218
|
return () => {
|
|
2206
|
-
controller.abort();
|
|
2219
|
+
controller.abort('OntologyTermAutocomplete matcher');
|
|
2207
2220
|
};
|
|
2208
2221
|
}, [session, valueString, filterTerms, ontologyStore, needToLoadCurrentTerm]);
|
|
2209
2222
|
// effect for loading term autocompletions
|
|
@@ -2222,7 +2235,7 @@ function OntologyTermAutocomplete({ fetchValidTerms, filterTerms: filterTermsPro
|
|
|
2222
2235
|
});
|
|
2223
2236
|
}
|
|
2224
2237
|
return () => {
|
|
2225
|
-
controller.abort();
|
|
2238
|
+
controller.abort('OntologyTermAutocomplete loader');
|
|
2226
2239
|
};
|
|
2227
2240
|
}, [
|
|
2228
2241
|
needToLoadTermChoices,
|
|
@@ -2374,17 +2387,24 @@ class ChangeManager {
|
|
|
2374
2387
|
// pre-validate
|
|
2375
2388
|
const session = util.getSession(this.dataStore);
|
|
2376
2389
|
const controller = new AbortController();
|
|
2377
|
-
|
|
2390
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
2391
|
+
const { jobsManager, isLocked, changeInProgress, setChangeInProgress } = util.getSession(this.dataStore);
|
|
2378
2392
|
if (isLocked) {
|
|
2379
2393
|
session.notify('Cannot submit changes in locked mode');
|
|
2394
|
+
setChangeInProgress(false);
|
|
2380
2395
|
return;
|
|
2381
2396
|
}
|
|
2397
|
+
if (changeInProgress) {
|
|
2398
|
+
session.notify('Could not submit change, there is another change still in progress');
|
|
2399
|
+
return;
|
|
2400
|
+
}
|
|
2401
|
+
setChangeInProgress(true);
|
|
2382
2402
|
const job = {
|
|
2383
2403
|
name: change.typeName,
|
|
2384
2404
|
statusMessage: 'Pre-validating',
|
|
2385
2405
|
progressPct: 0,
|
|
2386
2406
|
cancelCallback: () => {
|
|
2387
|
-
controller.abort();
|
|
2407
|
+
controller.abort('ChangeManager');
|
|
2388
2408
|
},
|
|
2389
2409
|
};
|
|
2390
2410
|
if (updateJobsManager) {
|
|
@@ -2397,6 +2417,7 @@ class ChangeManager {
|
|
|
2397
2417
|
jobsManager.abortJob(job.name, msg);
|
|
2398
2418
|
}
|
|
2399
2419
|
session.notify(msg, 'error');
|
|
2420
|
+
setChangeInProgress(false);
|
|
2400
2421
|
return;
|
|
2401
2422
|
}
|
|
2402
2423
|
try {
|
|
@@ -2409,6 +2430,7 @@ class ChangeManager {
|
|
|
2409
2430
|
}
|
|
2410
2431
|
console.error(error);
|
|
2411
2432
|
session.notify(`Error encountered in client: ${String(error)}. Data may be out of sync, please refresh the page`, 'error');
|
|
2433
|
+
setChangeInProgress(false);
|
|
2412
2434
|
return;
|
|
2413
2435
|
}
|
|
2414
2436
|
// post-validate
|
|
@@ -2439,6 +2461,7 @@ class ChangeManager {
|
|
|
2439
2461
|
console.error(error);
|
|
2440
2462
|
session.notify(String(error), 'error');
|
|
2441
2463
|
await this.undo(change, false);
|
|
2464
|
+
setChangeInProgress(false);
|
|
2442
2465
|
return;
|
|
2443
2466
|
}
|
|
2444
2467
|
if (!backendResult.ok) {
|
|
@@ -2448,6 +2471,7 @@ class ChangeManager {
|
|
|
2448
2471
|
}
|
|
2449
2472
|
session.notify(msg, 'error');
|
|
2450
2473
|
await this.undo(change, false);
|
|
2474
|
+
setChangeInProgress(false);
|
|
2451
2475
|
return;
|
|
2452
2476
|
}
|
|
2453
2477
|
if (change.notification) {
|
|
@@ -2461,6 +2485,7 @@ class ChangeManager {
|
|
|
2461
2485
|
if (updateJobsManager) {
|
|
2462
2486
|
jobsManager.done(job);
|
|
2463
2487
|
}
|
|
2488
|
+
setChangeInProgress(false);
|
|
2464
2489
|
}
|
|
2465
2490
|
async undo(change, submitToBackend = true) {
|
|
2466
2491
|
const inverseChange = change.getInverse();
|
|
@@ -2566,17 +2591,22 @@ class CollaborationServerDriver extends BackendDriver {
|
|
|
2566
2591
|
checkSocket(assembly, refSeq, internetAccount) {
|
|
2567
2592
|
const { socket } = internetAccount;
|
|
2568
2593
|
const token = internetAccount.retrieveToken();
|
|
2594
|
+
if (!token) {
|
|
2595
|
+
return;
|
|
2596
|
+
}
|
|
2597
|
+
const localSessionId = shared.makeUserSessionId(token);
|
|
2569
2598
|
const channel = `${assembly}-${refSeq}`;
|
|
2570
2599
|
const changeManager = new ChangeManager(this.clientStore);
|
|
2571
2600
|
if (!socket.hasListeners(channel)) {
|
|
2572
2601
|
socket.on(channel, async (message) => {
|
|
2573
2602
|
// Save server last change sequence into session storage
|
|
2574
2603
|
internetAccount.setLastChangeSequenceNumber(Number(message.changeSequence));
|
|
2575
|
-
if (message.userSessionId
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2604
|
+
if (message.userSessionId === localSessionId) {
|
|
2605
|
+
return; // we did this change, no need to apply it again
|
|
2606
|
+
}
|
|
2607
|
+
const change = common.Change.fromJSON(message.changeInfo);
|
|
2608
|
+
if (common.isFeatureChange(change) && this.haveDataForChange(change)) {
|
|
2609
|
+
await changeManager.submit(change, { submitToBackend: false });
|
|
2580
2610
|
}
|
|
2581
2611
|
});
|
|
2582
2612
|
}
|
|
@@ -3989,7 +4019,7 @@ function ImportFeatures({ changeManager, handleClose, session, }) {
|
|
|
3989
4019
|
statusMessage: 'Uploading file, this may take awhile',
|
|
3990
4020
|
progressPct: 0,
|
|
3991
4021
|
cancelCallback: () => {
|
|
3992
|
-
controller.abort();
|
|
4022
|
+
controller.abort('ImportFeatures');
|
|
3993
4023
|
jobsManager.abortJob(job.name);
|
|
3994
4024
|
},
|
|
3995
4025
|
};
|
|
@@ -5219,7 +5249,7 @@ const AuthTypeSelector = ({ baseURL, handleClose, name, }) => {
|
|
|
5219
5249
|
}
|
|
5220
5250
|
});
|
|
5221
5251
|
return () => {
|
|
5222
|
-
controller.abort();
|
|
5252
|
+
controller.abort('AuthTypeSelector');
|
|
5223
5253
|
};
|
|
5224
5254
|
}, [baseURL]);
|
|
5225
5255
|
function handleClick(authType) {
|
|
@@ -5656,8 +5686,13 @@ const stateModelFactory$3 = (configSchema) => {
|
|
|
5656
5686
|
return;
|
|
5657
5687
|
}
|
|
5658
5688
|
if (self.role) {
|
|
5659
|
-
|
|
5660
|
-
|
|
5689
|
+
try {
|
|
5690
|
+
await self.initialize(self.role);
|
|
5691
|
+
reaction.dispose();
|
|
5692
|
+
}
|
|
5693
|
+
catch {
|
|
5694
|
+
// if initialize fails, do nothing so the autorun runs again
|
|
5695
|
+
}
|
|
5661
5696
|
}
|
|
5662
5697
|
}, { name: 'ApolloInternetAccount' });
|
|
5663
5698
|
},
|
|
@@ -6932,6 +6967,7 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
|
|
|
6932
6967
|
const refData = currentAssembly?.getByRefName(refName);
|
|
6933
6968
|
const { changeManager } = session.apolloDataStore;
|
|
6934
6969
|
const seqRef = React.useRef(null);
|
|
6970
|
+
const { changeInProgress } = session;
|
|
6935
6971
|
if (!refData) {
|
|
6936
6972
|
return null;
|
|
6937
6973
|
}
|
|
@@ -7379,10 +7415,13 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
|
|
|
7379
7415
|
// highlight start codon and stop codons
|
|
7380
7416
|
if (codonSeq === 'ATG') {
|
|
7381
7417
|
elements.push(React__default["default"].createElement(material.Typography, { component: 'span', style: {
|
|
7382
|
-
backgroundColor: 'yellow',
|
|
7418
|
+
backgroundColor: changeInProgress ? 'lightgray' : 'yellow',
|
|
7383
7419
|
cursor: 'pointer',
|
|
7384
7420
|
border: '1px solid black',
|
|
7385
7421
|
}, key: codonGenomicPos, onClick: () => {
|
|
7422
|
+
if (changeInProgress) {
|
|
7423
|
+
return;
|
|
7424
|
+
}
|
|
7386
7425
|
// NOTE: codonGenomicPos is important here for calculating the genomic location
|
|
7387
7426
|
// of the start codon. We are using the codonGenomicPos as the key in the typography
|
|
7388
7427
|
// elements to maintain the genomic postion of the codon start
|
|
@@ -7466,20 +7505,21 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
|
|
|
7466
7505
|
}
|
|
7467
7506
|
// Trim any sequence before first start codon and after stop codon
|
|
7468
7507
|
const startCodonIndex = translationSequence.indexOf('M');
|
|
7469
|
-
const stopCodonIndex = translationSequence.indexOf('*')
|
|
7508
|
+
const stopCodonIndex = translationSequence.indexOf('*');
|
|
7470
7509
|
const startCodonPos = translSeqCodonStartGenomicPosArr[startCodonIndex].codonGenomicPos;
|
|
7471
7510
|
const stopCodonPos = translSeqCodonStartGenomicPosArr[stopCodonIndex].codonGenomicPos;
|
|
7472
7511
|
if (!startCodonPos || !stopCodonPos) {
|
|
7473
7512
|
return;
|
|
7474
7513
|
}
|
|
7475
7514
|
const startCodonGenomicLoc = getCodonGenomicLocation(startCodonPos);
|
|
7476
|
-
|
|
7515
|
+
let stopCodonGenomicLoc = getCodonGenomicLocation(stopCodonPos);
|
|
7477
7516
|
if (strand === 1) {
|
|
7478
7517
|
if (startCodonGenomicLoc > stopCodonGenomicLoc) {
|
|
7479
7518
|
notify('Start codon genomic location should be less than stop codon genomic location', 'error');
|
|
7480
7519
|
return;
|
|
7481
7520
|
}
|
|
7482
7521
|
let promise;
|
|
7522
|
+
stopCodonGenomicLoc += 3; // move to end of stop codon
|
|
7483
7523
|
if (startCodonGenomicLoc !== cdsMin) {
|
|
7484
7524
|
promise = new Promise((resolve) => {
|
|
7485
7525
|
updateCDSLocation(cdsMin, startCodonGenomicLoc, feature, true, () => {
|
|
@@ -7505,6 +7545,7 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
|
|
|
7505
7545
|
return;
|
|
7506
7546
|
}
|
|
7507
7547
|
let promise;
|
|
7548
|
+
stopCodonGenomicLoc -= 3; // move to end of stop codon
|
|
7508
7549
|
if (startCodonGenomicLoc !== cdsMax) {
|
|
7509
7550
|
promise = new Promise((resolve) => {
|
|
7510
7551
|
updateCDSLocation(cdsMax, startCodonGenomicLoc, feature, false, () => {
|
|
@@ -7548,27 +7589,29 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
|
|
|
7548
7589
|
gap: 10,
|
|
7549
7590
|
} },
|
|
7550
7591
|
React__default["default"].createElement(material.Tooltip, { title: "Copy" },
|
|
7551
|
-
React__default["default"].createElement(
|
|
7592
|
+
React__default["default"].createElement("button", { onClick: onCopyClick, style: { border: 'none', background: 'none', padding: 0 }, disabled: changeInProgress },
|
|
7593
|
+
React__default["default"].createElement(ContentCopyIcon__default["default"], { style: { fontSize: 15 } }))),
|
|
7552
7594
|
React__default["default"].createElement(material.Tooltip, { title: "Trim" },
|
|
7553
|
-
React__default["default"].createElement(
|
|
7595
|
+
React__default["default"].createElement("button", { onClick: trimTranslationSequence, style: { border: 'none', background: 'none', padding: 0 }, disabled: changeInProgress },
|
|
7596
|
+
React__default["default"].createElement(ContentCutIcon__default["default"], { style: { fontSize: 15 } })))))),
|
|
7554
7597
|
React__default["default"].createElement(material.Grid, { container: true, justifyContent: "center", alignItems: "center", style: { textAlign: 'center', marginTop: 10 } },
|
|
7555
7598
|
React__default["default"].createElement(material.Grid, { size: 1 }),
|
|
7556
7599
|
strand === 1 ? (React__default["default"].createElement(material.Grid, { size: 4 },
|
|
7557
7600
|
React__default["default"].createElement(StyledTextField, { margin: "dense", variant: "outlined", value: cdsMin + 1, onChangeCommitted: (newLocation) => {
|
|
7558
7601
|
return updateCDSLocation(cdsMin, newLocation - 1, feature, true);
|
|
7559
|
-
}, style: { border: '1px solid black', borderRadius: 5 } }))) : (React__default["default"].createElement(material.Grid, { size: 4 },
|
|
7602
|
+
}, style: { border: '1px solid black', borderRadius: 5 }, disabled: changeInProgress }))) : (React__default["default"].createElement(material.Grid, { size: 4 },
|
|
7560
7603
|
React__default["default"].createElement(StyledTextField, { margin: "dense", variant: "outlined", value: cdsMax, onChangeCommitted: (newLocation) => {
|
|
7561
7604
|
return updateCDSLocation(cdsMax, newLocation, feature, false);
|
|
7562
|
-
}, style: { border: '1px solid black', borderRadius: 5 } }))),
|
|
7605
|
+
}, style: { border: '1px solid black', borderRadius: 5 }, disabled: changeInProgress }))),
|
|
7563
7606
|
React__default["default"].createElement(material.Grid, { size: 2 },
|
|
7564
7607
|
React__default["default"].createElement(material.Typography, { component: 'span' }, "CDS")),
|
|
7565
7608
|
strand === 1 ? (React__default["default"].createElement(material.Grid, { size: 4 },
|
|
7566
7609
|
React__default["default"].createElement(StyledTextField, { margin: "dense", variant: "outlined", value: cdsMax, onChangeCommitted: (newLocation) => {
|
|
7567
7610
|
return updateCDSLocation(cdsMax, newLocation, feature, false);
|
|
7568
|
-
}, style: { border: '1px solid black', borderRadius: 5 } }))) : (React__default["default"].createElement(material.Grid, { size: 4 },
|
|
7611
|
+
}, style: { border: '1px solid black', borderRadius: 5 }, disabled: changeInProgress }))) : (React__default["default"].createElement(material.Grid, { size: 4 },
|
|
7569
7612
|
React__default["default"].createElement(StyledTextField, { margin: "dense", variant: "outlined", value: cdsMin + 1, onChangeCommitted: (newLocation) => {
|
|
7570
7613
|
return updateCDSLocation(cdsMin, newLocation - 1, feature, true);
|
|
7571
|
-
}, style: { border: '1px solid black', borderRadius: 5 } }))),
|
|
7614
|
+
}, style: { border: '1px solid black', borderRadius: 5 }, disabled: changeInProgress }))),
|
|
7572
7615
|
React__default["default"].createElement(material.Grid, { size: 1 })))),
|
|
7573
7616
|
React__default["default"].createElement("div", { style: { marginTop: 5 } }, transcriptExonParts.map((loc, index) => {
|
|
7574
7617
|
return (React__default["default"].createElement("div", { key: index }, loc.type === 'exon' && (React__default["default"].createElement(material.Grid, { container: true, justifyContent: "center", alignItems: "center", style: { textAlign: 'center' } },
|
|
@@ -7577,19 +7620,19 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
|
|
|
7577
7620
|
strand === 1 ? (React__default["default"].createElement(material.Grid, { size: 4, style: { padding: 0 } },
|
|
7578
7621
|
React__default["default"].createElement(StyledTextField, { margin: "dense", variant: "outlined", value: loc.min + 1, onChangeCommitted: (newLocation) => {
|
|
7579
7622
|
return handleExonLocationChange(loc.min, newLocation - 1, feature, true);
|
|
7580
|
-
} }))) : (React__default["default"].createElement(material.Grid, { size: 4, style: { padding: 0 } },
|
|
7623
|
+
}, disabled: changeInProgress }))) : (React__default["default"].createElement(material.Grid, { size: 4, style: { padding: 0 } },
|
|
7581
7624
|
React__default["default"].createElement(StyledTextField, { margin: "dense", variant: "outlined", value: loc.max, onChangeCommitted: (newLocation) => {
|
|
7582
7625
|
return handleExonLocationChange(loc.max, newLocation, feature, false);
|
|
7583
|
-
} }))),
|
|
7626
|
+
}, disabled: changeInProgress }))),
|
|
7584
7627
|
React__default["default"].createElement(material.Grid, { size: 2 },
|
|
7585
7628
|
React__default["default"].createElement(Strand, { strand: feature.strand })),
|
|
7586
7629
|
strand === 1 ? (React__default["default"].createElement(material.Grid, { size: 4, style: { padding: 0 } },
|
|
7587
7630
|
React__default["default"].createElement(StyledTextField, { margin: "dense", variant: "outlined", value: loc.max, onChangeCommitted: (newLocation) => {
|
|
7588
7631
|
return handleExonLocationChange(loc.max, newLocation, feature, false);
|
|
7589
|
-
} }))) : (React__default["default"].createElement(material.Grid, { size: 4, style: { padding: 0 } },
|
|
7632
|
+
}, disabled: changeInProgress }))) : (React__default["default"].createElement(material.Grid, { size: 4, style: { padding: 0 } },
|
|
7590
7633
|
React__default["default"].createElement(StyledTextField, { margin: "dense", variant: "outlined", value: loc.min + 1, onChangeCommitted: (newLocation) => {
|
|
7591
7634
|
return handleExonLocationChange(loc.min, newLocation - 1, feature, true);
|
|
7592
|
-
} }))),
|
|
7635
|
+
}, disabled: changeInProgress }))),
|
|
7593
7636
|
React__default["default"].createElement(material.Grid, { size: 1 }, index !== transcriptExonParts.length - 1 &&
|
|
7594
7637
|
getThreePrimeSpliceSite(loc, index).map((site, idx) => (React__default["default"].createElement(material.Typography, { key: idx, component: 'span', color: site.color }, site.spliceSite))))))));
|
|
7595
7638
|
}))));
|
|
@@ -9258,8 +9301,8 @@ function getContextMenuItems$2(display, mousePosition) {
|
|
|
9258
9301
|
},
|
|
9259
9302
|
});
|
|
9260
9303
|
if (util.isSessionModelWithWidgets(session)) {
|
|
9261
|
-
contextMenuItemsForFeature.
|
|
9262
|
-
label: 'Open transcript
|
|
9304
|
+
contextMenuItemsForFeature.splice(1, 0, {
|
|
9305
|
+
label: 'Open transcript editor',
|
|
9263
9306
|
onClick: () => {
|
|
9264
9307
|
const apolloTranscriptWidget = session.addWidget('ApolloTranscriptDetails', 'apolloTranscriptDetails', {
|
|
9265
9308
|
feature,
|
|
@@ -9607,6 +9650,25 @@ function clusterResultByMessage(items, width, touchesAsOverlap) {
|
|
|
9607
9650
|
clusters.sort((a, b) => a.message.localeCompare(b.message) || a.start - b.start);
|
|
9608
9651
|
return clusters;
|
|
9609
9652
|
}
|
|
9653
|
+
function codonColorCode(letter, theme, highContrast) {
|
|
9654
|
+
if (letter === 'M') {
|
|
9655
|
+
return theme.palette.startCodon;
|
|
9656
|
+
}
|
|
9657
|
+
if (letter === '*') {
|
|
9658
|
+
return highContrast ? theme.palette.text.primary : theme.palette.stopCodon;
|
|
9659
|
+
}
|
|
9660
|
+
return;
|
|
9661
|
+
}
|
|
9662
|
+
function colorCode(letter, theme) {
|
|
9663
|
+
const letterUpper = letter.toUpperCase();
|
|
9664
|
+
if (letterUpper === 'A' ||
|
|
9665
|
+
letterUpper === 'C' ||
|
|
9666
|
+
letterUpper === 'G' ||
|
|
9667
|
+
letterUpper === 'T') {
|
|
9668
|
+
return theme.palette.bases[letterUpper].main.toString();
|
|
9669
|
+
}
|
|
9670
|
+
return 'lightgray';
|
|
9671
|
+
}
|
|
9610
9672
|
|
|
9611
9673
|
const minDisplayHeight$2 = 20;
|
|
9612
9674
|
function baseModelFactory$2(_pluginManager, configSchema) {
|
|
@@ -10432,34 +10494,26 @@ function stateModelFactory$2(pluginManager, configSchema) {
|
|
|
10432
10494
|
|
|
10433
10495
|
const configSchema$1 = configuration.ConfigurationSchema('LinearApolloReferenceSequenceDisplay', {}, { explicitIdentifier: 'displayId', explicitlyTyped: true });
|
|
10434
10496
|
|
|
10435
|
-
function getSeqRow(strand, bpPerPx) {
|
|
10497
|
+
function getSeqRow(strand, bpPerPx, reversed) {
|
|
10436
10498
|
if (bpPerPx > 1 || strand === undefined) {
|
|
10437
10499
|
return;
|
|
10438
10500
|
}
|
|
10501
|
+
if (reversed) {
|
|
10502
|
+
return strand === 1 ? 4 : 3;
|
|
10503
|
+
}
|
|
10439
10504
|
return strand === 1 ? 3 : 4;
|
|
10440
10505
|
}
|
|
10441
|
-
function getTranslationRow(frame, bpPerPx) {
|
|
10442
|
-
const
|
|
10443
|
-
|
|
10444
|
-
|
|
10445
|
-
return 0;
|
|
10446
|
-
}
|
|
10447
|
-
case 2: {
|
|
10448
|
-
return 1;
|
|
10449
|
-
}
|
|
10450
|
-
case 1: {
|
|
10451
|
-
return 2;
|
|
10452
|
-
}
|
|
10453
|
-
case -1: {
|
|
10454
|
-
return 3 + offset;
|
|
10455
|
-
}
|
|
10456
|
-
case -2: {
|
|
10457
|
-
return 4 + offset;
|
|
10458
|
-
}
|
|
10459
|
-
case -3: {
|
|
10460
|
-
return 5 + offset;
|
|
10461
|
-
}
|
|
10506
|
+
function getTranslationRow(frame, bpPerPx, reversed) {
|
|
10507
|
+
const frameRows = bpPerPx <= 1 ? [2, 1, 0, 7, 6, 5] : [2, 1, 0, 5, 4, 3];
|
|
10508
|
+
if (reversed) {
|
|
10509
|
+
frameRows.reverse();
|
|
10462
10510
|
}
|
|
10511
|
+
frameRows.unshift(0);
|
|
10512
|
+
const row = frameRows.at(frame);
|
|
10513
|
+
if (row === undefined) {
|
|
10514
|
+
throw new Error('could not find row');
|
|
10515
|
+
}
|
|
10516
|
+
return row;
|
|
10463
10517
|
}
|
|
10464
10518
|
function getLeftPx$1(feature, bpPerPx, offsetPx, block) {
|
|
10465
10519
|
const blockLeftPx = block.offsetPx - offsetPx;
|
|
@@ -10481,7 +10535,7 @@ function fillAndStrokeRect(ctx, left, top, width, height, theme, selected = fals
|
|
|
10481
10535
|
ctx.strokeRect(left, top, width, height);
|
|
10482
10536
|
}
|
|
10483
10537
|
function drawHighlight(ctx, feature, bpPerPx, offsetPx, rowHeight, block, theme, selected = false) {
|
|
10484
|
-
const row = getSeqRow(feature.strand, bpPerPx);
|
|
10538
|
+
const row = getSeqRow(feature.strand, bpPerPx, block.reversed);
|
|
10485
10539
|
if (!row) {
|
|
10486
10540
|
return;
|
|
10487
10541
|
}
|
|
@@ -10505,7 +10559,7 @@ function drawCDSHighlight(ctx, feature, bpPerPx, offsetPx, rowHeight, block, the
|
|
|
10505
10559
|
}
|
|
10506
10560
|
for (const loc of cdsLocs) {
|
|
10507
10561
|
const frame = util.getFrame(loc.min, loc.max, feature.strand ?? 1, loc.phase);
|
|
10508
|
-
const row = getTranslationRow(frame, bpPerPx);
|
|
10562
|
+
const row = getTranslationRow(frame, bpPerPx, block.reversed);
|
|
10509
10563
|
const left = getLeftPx$1(loc, bpPerPx, offsetPx, block);
|
|
10510
10564
|
const top = row * rowHeight;
|
|
10511
10565
|
const width = (loc.max - loc.min) / bpPerPx;
|
|
@@ -10528,76 +10582,71 @@ function drawSequenceOverlay(canvas, ctx, hoveredFeature, selectedFeature, rowHe
|
|
|
10528
10582
|
hoveredFeature?.feature,
|
|
10529
10583
|
].filter((f) => f !== undefined)) {
|
|
10530
10584
|
if (featureTypeOntology.isTypeOf(feature.type, 'CDS')) {
|
|
10531
|
-
drawCDSHighlight(ctx, feature, bpPerPx, offsetPx, rowHeight, block, theme,
|
|
10585
|
+
drawCDSHighlight(ctx, feature, bpPerPx, offsetPx, rowHeight, block, theme, feature._id === selectedFeature?._id);
|
|
10532
10586
|
}
|
|
10533
10587
|
else {
|
|
10534
|
-
drawHighlight(ctx, feature, bpPerPx, offsetPx, rowHeight, block, theme,
|
|
10588
|
+
drawHighlight(ctx, feature, bpPerPx, offsetPx, rowHeight, block, theme, feature._id === selectedFeature?._id);
|
|
10535
10589
|
}
|
|
10536
10590
|
}
|
|
10537
10591
|
ctx.restore();
|
|
10538
10592
|
}
|
|
10539
10593
|
}
|
|
10540
10594
|
|
|
10541
|
-
function
|
|
10542
|
-
const
|
|
10543
|
-
|
|
10544
|
-
|
|
10545
|
-
|
|
10546
|
-
|
|
10547
|
-
|
|
10548
|
-
|
|
10549
|
-
return
|
|
10595
|
+
function getLeftPx(display, feature, block) {
|
|
10596
|
+
const { lgv } = display;
|
|
10597
|
+
const { bpPerPx, offsetPx } = lgv;
|
|
10598
|
+
const blockLeftPx = block.offsetPx - offsetPx;
|
|
10599
|
+
const featureLeftBpDistanceFromBlockLeftBp = block.reversed
|
|
10600
|
+
? block.end - feature.max
|
|
10601
|
+
: feature.min - block.start;
|
|
10602
|
+
const featureLeftPxDistanceFromBlockLeftPx = featureLeftBpDistanceFromBlockLeftBp / bpPerPx;
|
|
10603
|
+
return blockLeftPx + featureLeftPxDistanceFromBlockLeftPx;
|
|
10550
10604
|
}
|
|
10551
|
-
|
|
10552
|
-
|
|
10553
|
-
|
|
10554
|
-
|
|
10555
|
-
|
|
10556
|
-
|
|
10557
|
-
|
|
10558
|
-
|
|
10605
|
+
/**
|
|
10606
|
+
* Perform a canvas strokeRect, but have the stroke be contained within the
|
|
10607
|
+
* given rect instead of centered on it.
|
|
10608
|
+
*/
|
|
10609
|
+
function strokeRectInner(ctx, left, top, width, height, color) {
|
|
10610
|
+
ctx.strokeStyle = color;
|
|
10611
|
+
ctx.lineWidth = 1;
|
|
10612
|
+
ctx.strokeRect(left + 0.5, top + 0.5, width - 1, height - 1);
|
|
10559
10613
|
}
|
|
10614
|
+
|
|
10560
10615
|
function drawLetter(seqTrackctx, left, top, width, letter) {
|
|
10561
10616
|
const fontSize = Math.min(width, 10);
|
|
10562
10617
|
seqTrackctx.fillStyle = '#000';
|
|
10563
10618
|
seqTrackctx.font = `${fontSize}px`;
|
|
10564
10619
|
const textWidth = seqTrackctx.measureText(letter).width;
|
|
10565
|
-
const textX = left + (width - textWidth) / 2;
|
|
10620
|
+
const textX = Math.round(left + (width - textWidth) / 2);
|
|
10566
10621
|
seqTrackctx.fillText(letter, textX, top + 10);
|
|
10567
10622
|
}
|
|
10568
|
-
function drawTranslationFrameBackgrounds(
|
|
10623
|
+
function drawTranslationFrameBackgrounds(ctx, bpPerPx, theme, highContrast, left, width, sequenceRowHeight, reversed) {
|
|
10569
10624
|
const frames = bpPerPx <= 1 ? [3, 2, 1, 0, 0, -1, -2, -3] : [3, 2, 1, -1, -2, -3];
|
|
10625
|
+
if (reversed) {
|
|
10626
|
+
frames.reverse();
|
|
10627
|
+
}
|
|
10570
10628
|
for (const [idx, frame] of frames.entries()) {
|
|
10571
10629
|
const frameColor = theme.palette.framesCDS.at(frame)?.main;
|
|
10572
10630
|
if (!frameColor) {
|
|
10573
10631
|
continue;
|
|
10574
10632
|
}
|
|
10575
10633
|
const top = idx * sequenceRowHeight;
|
|
10576
|
-
const { offsetPx } = dynamicBlocks;
|
|
10577
|
-
const left = Math.max(0, -offsetPx);
|
|
10578
|
-
const width = dynamicBlocks.totalWidthPx;
|
|
10579
10634
|
ctx.fillStyle = highContrast ? theme.palette.background.default : frameColor;
|
|
10580
10635
|
ctx.fillRect(left, top, width, sequenceRowHeight);
|
|
10581
10636
|
if (highContrast) {
|
|
10582
10637
|
// eslint-disable-next-line prefer-destructuring
|
|
10583
|
-
|
|
10584
|
-
ctx
|
|
10585
|
-
}
|
|
10586
|
-
}
|
|
10587
|
-
// allows inter-region padding lines to show through
|
|
10588
|
-
for (const block of dynamicBlocks.getBlocks()) {
|
|
10589
|
-
if (block.type === 'InterRegionPaddingBlock') {
|
|
10590
|
-
const left = block.offsetPx - dynamicBlocks.offsetPx;
|
|
10591
|
-
ctx.clearRect(left, 0, block.widthPx, canvas.height);
|
|
10638
|
+
const strokeStyle = theme.palette.grey[200];
|
|
10639
|
+
strokeRectInner(ctx, left, top, width, sequenceRowHeight, strokeStyle);
|
|
10592
10640
|
}
|
|
10593
10641
|
}
|
|
10594
10642
|
}
|
|
10595
10643
|
function drawBase(ctx, base, index, leftPx, bpPerPx, rowHeight, theme) {
|
|
10596
|
-
|
|
10597
|
-
if (width < 1) {
|
|
10644
|
+
if (1 / bpPerPx < 1) {
|
|
10598
10645
|
return;
|
|
10599
10646
|
}
|
|
10600
|
-
const left = leftPx + index / bpPerPx;
|
|
10647
|
+
const left = Math.round(leftPx + index / bpPerPx);
|
|
10648
|
+
const nextLeft = Math.round(leftPx + (index + 1) / bpPerPx);
|
|
10649
|
+
const width = nextLeft - left;
|
|
10601
10650
|
const strands = [-1, 1];
|
|
10602
10651
|
for (const strand of strands) {
|
|
10603
10652
|
const top = (strand === 1 ? 3 : 4) * rowHeight;
|
|
@@ -10605,13 +10654,13 @@ function drawBase(ctx, base, index, leftPx, bpPerPx, rowHeight, theme) {
|
|
|
10605
10654
|
ctx.fillStyle = colorCode(baseCode, theme);
|
|
10606
10655
|
ctx.fillRect(left, top, width, rowHeight);
|
|
10607
10656
|
if (1 / bpPerPx >= 12) {
|
|
10608
|
-
|
|
10609
|
-
ctx
|
|
10657
|
+
const strokeStyle = theme.palette.text.disabled;
|
|
10658
|
+
strokeRectInner(ctx, left, top, width, rowHeight, strokeStyle);
|
|
10610
10659
|
drawLetter(ctx, left, top, width, baseCode);
|
|
10611
10660
|
}
|
|
10612
10661
|
}
|
|
10613
10662
|
}
|
|
10614
|
-
function drawCodon(ctx, codon, leftPx, index, theme, highContrast, bpPerPx, bp, rowHeight, showStartCodons, showStopCodons) {
|
|
10663
|
+
function drawCodon$1(ctx, codon, leftPx, index, theme, highContrast, bpPerPx, bp, rowHeight, showStartCodons, showStopCodons) {
|
|
10615
10664
|
const frameOffsets = (bpPerPx <= 1 ? [0, 2, 1, 0, 7, 6, 5] : [0, 2, 1, 0, 5, 4, 3]).map((b) => b * rowHeight);
|
|
10616
10665
|
const strands = [-1, 1];
|
|
10617
10666
|
for (const strand of strands) {
|
|
@@ -10621,7 +10670,8 @@ function drawCodon(ctx, codon, leftPx, index, theme, highContrast, bpPerPx, bp,
|
|
|
10621
10670
|
continue;
|
|
10622
10671
|
}
|
|
10623
10672
|
const left = Math.round(leftPx + index / bpPerPx);
|
|
10624
|
-
const
|
|
10673
|
+
const nextLeft = Math.round(leftPx + (index + 3) / bpPerPx);
|
|
10674
|
+
const width = nextLeft - left;
|
|
10625
10675
|
const codonCode = strand === 1 ? codon : util.revcom(codon);
|
|
10626
10676
|
const aminoAcidCode = util.defaultCodonTable[codonCode];
|
|
10627
10677
|
const fillColor = codonColorCode(aminoAcidCode, theme, highContrast);
|
|
@@ -10632,8 +10682,8 @@ function drawCodon(ctx, codon, leftPx, index, theme, highContrast, bpPerPx, bp,
|
|
|
10632
10682
|
ctx.fillRect(left, top, width, rowHeight);
|
|
10633
10683
|
}
|
|
10634
10684
|
if (1 / bpPerPx >= 4) {
|
|
10635
|
-
|
|
10636
|
-
ctx
|
|
10685
|
+
const strokeStyle = theme.palette.text.disabled;
|
|
10686
|
+
strokeRectInner(ctx, left, top, width, rowHeight, strokeStyle);
|
|
10637
10687
|
drawLetter(ctx, left, top, width, aminoAcidCode);
|
|
10638
10688
|
}
|
|
10639
10689
|
}
|
|
@@ -10644,9 +10694,10 @@ function drawSequenceTrack(canvas, theme, bpPerPx, offsetPx, dynamicBlocks, high
|
|
|
10644
10694
|
return;
|
|
10645
10695
|
}
|
|
10646
10696
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
10647
|
-
drawTranslationFrameBackgrounds(canvas, ctx, bpPerPx, theme, dynamicBlocks, highContrast, sequenceRowHeight);
|
|
10648
10697
|
const { apolloDataStore } = session;
|
|
10649
10698
|
for (const block of dynamicBlocks.contentBlocks) {
|
|
10699
|
+
const totalOffsetPx = block.offsetPx - offsetPx;
|
|
10700
|
+
drawTranslationFrameBackgrounds(ctx, bpPerPx, theme, highContrast, totalOffsetPx, block.widthPx, sequenceRowHeight, block.reversed);
|
|
10650
10701
|
const assembly = apolloDataStore.assemblies.get(block.assemblyName);
|
|
10651
10702
|
const ref = assembly?.getByRefName(block.refName);
|
|
10652
10703
|
const roundedStart = Math.floor(block.start);
|
|
@@ -10656,16 +10707,20 @@ function drawSequenceTrack(canvas, theme, bpPerPx, offsetPx, dynamicBlocks, high
|
|
|
10656
10707
|
return;
|
|
10657
10708
|
}
|
|
10658
10709
|
seq = seq.toUpperCase();
|
|
10659
|
-
|
|
10660
|
-
|
|
10710
|
+
if (block.reversed) {
|
|
10711
|
+
seq = util.revcom(seq);
|
|
10712
|
+
}
|
|
10713
|
+
const baseOffsetPx = (block.reversed ? roundedEnd - block.end : block.start - roundedStart) /
|
|
10714
|
+
bpPerPx;
|
|
10715
|
+
const seqLeftPx = totalOffsetPx - baseOffsetPx;
|
|
10661
10716
|
for (let i = 0; i < seq.length; i++) {
|
|
10662
|
-
const bp = roundedStart + i;
|
|
10717
|
+
const bp = block.reversed ? roundedEnd - i : roundedStart + i;
|
|
10663
10718
|
const codon = seq.slice(i, i + 3);
|
|
10664
10719
|
drawBase(ctx, seq[i], i, seqLeftPx, bpPerPx, sequenceRowHeight, theme);
|
|
10665
10720
|
if (codon.length !== 3) {
|
|
10666
10721
|
continue;
|
|
10667
10722
|
}
|
|
10668
|
-
drawCodon(ctx, codon, seqLeftPx, i, theme, highContrast, bpPerPx, bp, sequenceRowHeight, showStartCodons, showStopCodons);
|
|
10723
|
+
drawCodon$1(ctx, codon, seqLeftPx, i, theme, highContrast, bpPerPx, bp, sequenceRowHeight, showStartCodons, showStopCodons);
|
|
10669
10724
|
}
|
|
10670
10725
|
}
|
|
10671
10726
|
}
|
|
@@ -11729,6 +11784,8 @@ function baseModelFactory(_pluginManager, configSchema) {
|
|
|
11729
11784
|
graphical: true,
|
|
11730
11785
|
table: false,
|
|
11731
11786
|
showFeatureLabels: true,
|
|
11787
|
+
showStartCodons: false,
|
|
11788
|
+
showStopCodons: true,
|
|
11732
11789
|
showCheckResults: true,
|
|
11733
11790
|
zoomThreshold: 200,
|
|
11734
11791
|
heightPreConfig: mobxStateTree.types.maybe(mobxStateTree.types.refinement('displayHeight', mobxStateTree.types.number, (n) => n >= minDisplayHeight)),
|
|
@@ -11857,6 +11914,12 @@ function baseModelFactory(_pluginManager, configSchema) {
|
|
|
11857
11914
|
toggleShowFeatureLabels() {
|
|
11858
11915
|
self.showFeatureLabels = !self.showFeatureLabels;
|
|
11859
11916
|
},
|
|
11917
|
+
toggleShowStartCodons() {
|
|
11918
|
+
self.showStartCodons = !self.showStartCodons;
|
|
11919
|
+
},
|
|
11920
|
+
toggleShowStopCodons() {
|
|
11921
|
+
self.showStopCodons = !self.showStopCodons;
|
|
11922
|
+
},
|
|
11860
11923
|
toggleShowCheckResults() {
|
|
11861
11924
|
self.showCheckResults = !self.showCheckResults;
|
|
11862
11925
|
},
|
|
@@ -11871,7 +11934,7 @@ function baseModelFactory(_pluginManager, configSchema) {
|
|
|
11871
11934
|
const { filteredFeatureTypes, trackMenuItems: superTrackMenuItems } = self;
|
|
11872
11935
|
return {
|
|
11873
11936
|
trackMenuItems() {
|
|
11874
|
-
const { graphical, table, showFeatureLabels, showCheckResults } = self;
|
|
11937
|
+
const { graphical, table, showFeatureLabels, showStartCodons, showStopCodons, showCheckResults, } = self;
|
|
11875
11938
|
return [
|
|
11876
11939
|
...superTrackMenuItems(),
|
|
11877
11940
|
{
|
|
@@ -11910,6 +11973,22 @@ function baseModelFactory(_pluginManager, configSchema) {
|
|
|
11910
11973
|
self.toggleShowFeatureLabels();
|
|
11911
11974
|
},
|
|
11912
11975
|
},
|
|
11976
|
+
{
|
|
11977
|
+
label: 'Show start codons',
|
|
11978
|
+
type: 'checkbox',
|
|
11979
|
+
checked: showStartCodons,
|
|
11980
|
+
onClick: () => {
|
|
11981
|
+
self.toggleShowStartCodons();
|
|
11982
|
+
},
|
|
11983
|
+
},
|
|
11984
|
+
{
|
|
11985
|
+
label: 'Show stop codons',
|
|
11986
|
+
type: 'checkbox',
|
|
11987
|
+
checked: showStopCodons,
|
|
11988
|
+
onClick: () => {
|
|
11989
|
+
self.toggleShowStopCodons();
|
|
11990
|
+
},
|
|
11991
|
+
},
|
|
11913
11992
|
{
|
|
11914
11993
|
label: 'Check Results',
|
|
11915
11994
|
type: 'checkbox',
|
|
@@ -11986,7 +12065,7 @@ function baseModelFactory(_pluginManager, configSchema) {
|
|
|
11986
12065
|
return;
|
|
11987
12066
|
}
|
|
11988
12067
|
void self.session.apolloDataStore.loadFeatures(self.regions);
|
|
11989
|
-
if (self.lgv.bpPerPx <=
|
|
12068
|
+
if (self.lgv.bpPerPx <= self.zoomThreshold) {
|
|
11990
12069
|
void self.session.apolloDataStore.loadRefSeq(self.regions);
|
|
11991
12070
|
}
|
|
11992
12071
|
}, { name: 'LinearApolloSixFrameDisplayLoadFeatures', delay: 1000 }));
|
|
@@ -12178,6 +12257,28 @@ function layoutsModelFactory(pluginManager, configSchema) {
|
|
|
12178
12257
|
}));
|
|
12179
12258
|
}
|
|
12180
12259
|
|
|
12260
|
+
function drawCodon(ctx, codon, leftPx, index, theme, highContrast, bpPerPx, bp, rowHeight, showFeatureLabels, showStartCodons, showStopCodons) {
|
|
12261
|
+
const frameOffsets = (showFeatureLabels ? [0, 4, 2, 0, 14, 12, 10] : [0, 2, 1, 0, 7, 6, 5]).map((b) => b * rowHeight);
|
|
12262
|
+
const strands = [-1, 1];
|
|
12263
|
+
for (const strand of strands) {
|
|
12264
|
+
const frame = util.getFrame(bp, bp + 3, strand, 0);
|
|
12265
|
+
const top = frameOffsets.at(frame);
|
|
12266
|
+
if (top === undefined) {
|
|
12267
|
+
continue;
|
|
12268
|
+
}
|
|
12269
|
+
const left = Math.round(leftPx + index / bpPerPx);
|
|
12270
|
+
const width = Math.round(3 / bpPerPx) === 0 ? 1 : Math.round(3 / bpPerPx);
|
|
12271
|
+
const codonCode = strand === 1 ? codon : util.revcom(codon);
|
|
12272
|
+
const aminoAcidCode = util.defaultCodonTable[codonCode];
|
|
12273
|
+
const fillColor = codonColorCode(aminoAcidCode, theme, highContrast);
|
|
12274
|
+
if (fillColor &&
|
|
12275
|
+
((showStopCodons && aminoAcidCode == '*') ||
|
|
12276
|
+
(showStartCodons && aminoAcidCode != '*'))) {
|
|
12277
|
+
ctx.fillStyle = fillColor;
|
|
12278
|
+
ctx.fillRect(left, top, width, rowHeight);
|
|
12279
|
+
}
|
|
12280
|
+
}
|
|
12281
|
+
}
|
|
12181
12282
|
function renderingModelFactory(pluginManager, configSchema) {
|
|
12182
12283
|
const LinearApolloSixFrameDisplayLayouts = layoutsModelFactory(pluginManager, configSchema);
|
|
12183
12284
|
return LinearApolloSixFrameDisplayLayouts.named('LinearApolloSixFrameDisplayRendering')
|
|
@@ -12267,11 +12368,11 @@ function renderingModelFactory(pluginManager, configSchema) {
|
|
|
12267
12368
|
}
|
|
12268
12369
|
}, { name: 'LinearApolloSixFrameDisplayRenderCollaborators' }));
|
|
12269
12370
|
mobxStateTree.addDisposer(self, mobx.autorun(() => {
|
|
12270
|
-
const { canvas, featureLayouts, featuresHeight, lgv } = self;
|
|
12371
|
+
const { apolloRowHeight, canvas, featureLayouts, featuresHeight, lgv, session, theme, showFeatureLabels, showStartCodons, showStopCodons, } = self;
|
|
12271
12372
|
if (!lgv.initialized || self.regionCannotBeRendered()) {
|
|
12272
12373
|
return;
|
|
12273
12374
|
}
|
|
12274
|
-
const { displayedRegions, dynamicBlocks } = lgv;
|
|
12375
|
+
const { bpPerPx, offsetPx, displayedRegions, dynamicBlocks } = lgv;
|
|
12275
12376
|
const ctx = canvas?.getContext('2d');
|
|
12276
12377
|
if (!ctx) {
|
|
12277
12378
|
return;
|
|
@@ -12295,6 +12396,27 @@ function renderingModelFactory(pluginManager, configSchema) {
|
|
|
12295
12396
|
}
|
|
12296
12397
|
}
|
|
12297
12398
|
}
|
|
12399
|
+
if (showStartCodons || showStopCodons) {
|
|
12400
|
+
const { apolloDataStore } = session;
|
|
12401
|
+
for (const block of dynamicBlocks.contentBlocks) {
|
|
12402
|
+
const assembly = apolloDataStore.assemblies.get(block.assemblyName);
|
|
12403
|
+
const ref = assembly?.getByRefName(block.refName);
|
|
12404
|
+
const roundedStart = Math.floor(block.start);
|
|
12405
|
+
const roundedEnd = Math.ceil(block.end);
|
|
12406
|
+
let seq = ref?.getSequence(roundedStart, roundedEnd);
|
|
12407
|
+
if (!seq) {
|
|
12408
|
+
break;
|
|
12409
|
+
}
|
|
12410
|
+
seq = seq.toUpperCase();
|
|
12411
|
+
const baseOffsetPx = (block.start - roundedStart) / bpPerPx;
|
|
12412
|
+
const seqLeftPx = Math.round(block.offsetPx - offsetPx - baseOffsetPx);
|
|
12413
|
+
for (let i = 0; i < seq.length; i++) {
|
|
12414
|
+
const bp = roundedStart + i;
|
|
12415
|
+
const codon = seq.slice(i, i + 3);
|
|
12416
|
+
drawCodon(ctx, codon, seqLeftPx, i, theme, true, bpPerPx, bp, apolloRowHeight, showFeatureLabels, showStartCodons, showStopCodons);
|
|
12417
|
+
}
|
|
12418
|
+
}
|
|
12419
|
+
}
|
|
12298
12420
|
}, { name: 'LinearApolloSixFrameDisplayRenderFeatures' }));
|
|
12299
12421
|
},
|
|
12300
12422
|
}));
|
|
@@ -13245,17 +13367,6 @@ function annotationFromJBrowseFeature(pluggableElement) {
|
|
|
13245
13367
|
return pluggableElement;
|
|
13246
13368
|
}
|
|
13247
13369
|
|
|
13248
|
-
function getLeftPx(display, feature, block) {
|
|
13249
|
-
const { lgv } = display;
|
|
13250
|
-
const { bpPerPx, offsetPx } = lgv;
|
|
13251
|
-
const blockLeftPx = block.offsetPx - offsetPx;
|
|
13252
|
-
const featureLeftBpDistanceFromBlockLeftBp = block.reversed
|
|
13253
|
-
? block.end - feature.max
|
|
13254
|
-
: feature.min - block.start;
|
|
13255
|
-
const featureLeftPxDistanceFromBlockLeftPx = featureLeftBpDistanceFromBlockLeftBp / bpPerPx;
|
|
13256
|
-
return blockLeftPx + featureLeftPxDistanceFromBlockLeftPx;
|
|
13257
|
-
}
|
|
13258
|
-
|
|
13259
13370
|
const CheckResultWarnings = mobxReact.observer(function CheckResultWarnings({ display, }) {
|
|
13260
13371
|
const { classes } = useStyles$1();
|
|
13261
13372
|
const { apolloDragging, apolloRowHeight, lgv, session, showCheckResults } = display;
|
|
@@ -13587,7 +13698,7 @@ const ResizeHandle = ({ onResize, }) => {
|
|
|
13587
13698
|
const controller = new AbortController();
|
|
13588
13699
|
const { signal } = controller;
|
|
13589
13700
|
function abortDrag() {
|
|
13590
|
-
controller.abort();
|
|
13701
|
+
controller.abort('makeDisplayComponent');
|
|
13591
13702
|
}
|
|
13592
13703
|
globalThis.addEventListener('mousemove', mouseMove, { signal });
|
|
13593
13704
|
globalThis.addEventListener('mouseup', abortDrag, { signal });
|
|
@@ -13991,7 +14102,7 @@ function clientDataStoreFactory(AnnotationFeatureExtended) {
|
|
|
13991
14102
|
statusMessage: `Loading ontology "${name}", version "${version}", this may take a while`,
|
|
13992
14103
|
progressPct: 0,
|
|
13993
14104
|
cancelCallback: () => {
|
|
13994
|
-
controller.abort();
|
|
14105
|
+
controller.abort('ClientDataStore');
|
|
13995
14106
|
jobsManager.abortJob(job.name);
|
|
13996
14107
|
},
|
|
13997
14108
|
};
|
|
@@ -14130,6 +14241,7 @@ function extendSession(pluginManager, sessionModel) {
|
|
|
14130
14241
|
apolloSelectedFeature: mobxStateTree.types.safeReference(AnnotationFeatureExtended),
|
|
14131
14242
|
jobsManager: mobxStateTree.types.optional(ApolloJobModel, {}),
|
|
14132
14243
|
isLocked: mobxStateTree.types.optional(mobxStateTree.types.boolean, false),
|
|
14244
|
+
changeInProgress: mobxStateTree.types.optional(mobxStateTree.types.boolean, false),
|
|
14133
14245
|
})
|
|
14134
14246
|
.volatile(() => ({
|
|
14135
14247
|
apolloHoveredFeature: undefined,
|
|
@@ -14192,6 +14304,9 @@ function extendSession(pluginManager, sessionModel) {
|
|
|
14192
14304
|
toggleLocked() {
|
|
14193
14305
|
self.isLocked = !self.isLocked;
|
|
14194
14306
|
},
|
|
14307
|
+
setChangeInProgress(changeInProgress) {
|
|
14308
|
+
self.changeInProgress = changeInProgress;
|
|
14309
|
+
},
|
|
14195
14310
|
getPluginConfiguration() {
|
|
14196
14311
|
const { jbrowse } = mobxStateTree.getRoot(self);
|
|
14197
14312
|
const pluginConfiguration =
|