@apollo-annotation/jbrowse-plugin-apollo 1.0.1 → 1.1.1
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/ApolloInternetAccount/components/AuthTypeSelector.d.ts +8 -2
- package/dist/ApolloInternetAccount/components/AuthTypeSelector.d.ts.map +1 -1
- package/dist/ApolloInternetAccount/components/LoginButtons.d.ts +8 -3
- package/dist/ApolloInternetAccount/components/LoginButtons.d.ts.map +1 -1
- package/dist/ApolloInternetAccount/configSchema.d.ts +44 -0
- package/dist/ApolloInternetAccount/configSchema.d.ts.map +1 -0
- package/dist/ApolloInternetAccount/model.d.ts +174 -0
- package/dist/ApolloInternetAccount/model.d.ts.map +1 -0
- package/dist/ApolloJobModel.d.ts.map +1 -1
- package/dist/ApolloRefNameAliasAdapter/configSchema.d.ts +8 -0
- package/dist/ApolloRefNameAliasAdapter/configSchema.d.ts.map +1 -0
- package/dist/ApolloSequenceAdapter/configSchema.d.ts +8 -0
- package/dist/ApolloSequenceAdapter/configSchema.d.ts.map +1 -0
- package/dist/ApolloTextSearchAdapter/configSchema.d.ts +20 -0
- package/dist/ApolloTextSearchAdapter/configSchema.d.ts.map +1 -0
- package/dist/BackendDrivers/CollaborationServerDriver.d.ts +1 -1
- package/dist/BackendDrivers/CollaborationServerDriver.d.ts.map +1 -1
- package/dist/BackendDrivers/LocalDriver/LocalDriver.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/ApolloFeatureDetailsWidget.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/Attributes.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/DefaultAttributeEditor.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/DefaultAttributeViewer.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/NumberTextField.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/StringTextField.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/TranscriptSequence.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/TranscriptWidgetEditLocation.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/Translation.d.ts +14 -0
- package/dist/FeatureDetailsWidget/Translation.d.ts.map +1 -0
- package/dist/LinearApolloDisplay/components/CheckResultWarnings.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/configSchema.d.ts +2 -0
- package/dist/LinearApolloDisplay/configSchema.d.ts.map +1 -0
- package/dist/LinearApolloDisplay/glyphs/ExonGlyph.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/glyphs/GeneGlyph.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/glyphs/TranscriptGlyph.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/stateModel/base.d.ts +1 -18
- package/dist/LinearApolloDisplay/stateModel/base.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/stateModel/index.d.ts +1 -17
- package/dist/LinearApolloDisplay/stateModel/index.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/stateModel/layouts.d.ts +2 -19
- package/dist/LinearApolloDisplay/stateModel/layouts.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/stateModel/mouseEvents.d.ts +2 -34
- package/dist/LinearApolloDisplay/stateModel/mouseEvents.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/stateModel/rendering.d.ts +2 -19
- package/dist/LinearApolloDisplay/stateModel/rendering.d.ts.map +1 -1
- package/dist/LinearApolloReferenceSequenceDisplay/configSchema.d.ts +2 -0
- package/dist/LinearApolloReferenceSequenceDisplay/configSchema.d.ts.map +1 -0
- package/dist/LinearApolloReferenceSequenceDisplay/stateModel/base.d.ts +1 -1
- package/dist/LinearApolloReferenceSequenceDisplay/stateModel/base.d.ts.map +1 -1
- package/dist/LinearApolloReferenceSequenceDisplay/stateModel/index.d.ts +1 -1
- package/dist/LinearApolloReferenceSequenceDisplay/stateModel/rendering.d.ts +1 -1
- package/dist/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/configSchema.d.ts +2 -0
- package/dist/LinearApolloSixFrameDisplay/configSchema.d.ts.map +1 -0
- package/dist/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/base.d.ts +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/base.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/index.d.ts +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/index.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/layouts.d.ts +2 -3
- package/dist/LinearApolloSixFrameDisplay/stateModel/layouts.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/mouseEvents.d.ts +2 -2
- package/dist/LinearApolloSixFrameDisplay/stateModel/mouseEvents.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/rendering.d.ts +2 -3
- package/dist/LinearApolloSixFrameDisplay/stateModel/rendering.d.ts.map +1 -1
- package/dist/OntologyManager/OntologyStore/fulltext.d.ts.map +1 -1
- package/dist/OntologyManager/index.d.ts +734 -0
- package/dist/OntologyManager/index.d.ts.map +1 -0
- package/dist/TabularEditor/HybridGrid/NumberCell.d.ts.map +1 -1
- package/dist/components/AddCodingSequence.d.ts +14 -0
- package/dist/components/AddCodingSequence.d.ts.map +1 -0
- package/dist/components/AddRefSeqAliases.d.ts.map +1 -1
- package/dist/components/CopyFeature.d.ts.map +1 -1
- package/dist/components/CreateApolloAnnotation.d.ts.map +1 -1
- package/dist/components/DeleteFeature.d.ts.map +1 -1
- package/dist/components/DownloadGFF3.d.ts.map +1 -1
- package/dist/components/DuplicateFeatureDialog.d.ts +12 -0
- package/dist/components/DuplicateFeatureDialog.d.ts.map +1 -0
- package/dist/components/ImportAnnotations.d.ts +9 -0
- package/dist/components/ImportAnnotations.d.ts.map +1 -0
- package/dist/components/ManageUsers.d.ts.map +1 -1
- package/dist/components/OntologyTermMultiSelect.d.ts.map +1 -1
- package/dist/components/ViewChangeLog.d.ts.map +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/extensions/annotationFromPileup.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +835 -434
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +830 -429
- 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 +868 -468
- 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/dist/session/ClientDataStore.d.ts +2194 -0
- package/dist/session/ClientDataStore.d.ts.map +1 -0
- package/dist/session/handleApolloFeaturesUrlParam.d.ts +8 -0
- package/dist/session/handleApolloFeaturesUrlParam.d.ts.map +1 -0
- package/dist/session/session.d.ts +6788 -0
- package/dist/session/session.d.ts.map +1 -0
- package/dist/util/displayUtils.d.ts +36 -0
- package/dist/util/displayUtils.d.ts.map +1 -0
- package/dist/util/sequenceUtils.d.ts +2 -0
- package/dist/util/sequenceUtils.d.ts.map +1 -0
- package/package.json +14 -14
- package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +73 -38
- package/src/ApolloInternetAccount/components/LoginButtons.tsx +31 -10
- package/src/ApolloInternetAccount/model.ts +8 -5
- package/src/ApolloJobModel.ts +3 -8
- package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +1 -1
- package/src/BackendDrivers/CollaborationServerDriver.ts +7 -11
- package/src/BackendDrivers/LocalDriver/LocalDriver.ts +9 -3
- package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +1 -0
- package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +4 -2
- package/src/FeatureDetailsWidget/Attributes.tsx +1 -0
- package/src/FeatureDetailsWidget/DefaultAttributeEditor.tsx +1 -0
- package/src/FeatureDetailsWidget/DefaultAttributeViewer.tsx +1 -0
- package/src/FeatureDetailsWidget/NumberTextField.tsx +1 -0
- package/src/FeatureDetailsWidget/StringTextField.tsx +1 -0
- package/src/FeatureDetailsWidget/TranscriptSequence.tsx +2 -1
- package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +17 -354
- package/src/FeatureDetailsWidget/Translation.tsx +366 -0
- package/src/LinearApolloDisplay/components/CheckResultWarnings.tsx +4 -1
- package/src/LinearApolloDisplay/glyphs/ExonGlyph.ts +2 -5
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +4 -6
- package/src/LinearApolloDisplay/glyphs/TranscriptGlyph.ts +39 -6
- package/src/LinearApolloDisplay/stateModel/base.ts +47 -23
- package/src/LinearApolloDisplay/stateModel/layouts.ts +7 -5
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +4 -1
- package/src/LinearApolloDisplay/stateModel/rendering.ts +1 -5
- package/src/LinearApolloReferenceSequenceDisplay/stateModel/base.ts +10 -10
- package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +5 -3
- package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +1 -1
- package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +2 -3
- package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +12 -20
- package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +10 -6
- package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +38 -45
- package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +1 -4
- package/src/OntologyManager/OntologyStore/fulltext.ts +0 -2
- package/src/OntologyManager/OntologyStore/index.ts +1 -1
- package/src/TabularEditor/HybridGrid/ChangeHandling.ts +2 -2
- package/src/TabularEditor/HybridGrid/NumberCell.tsx +2 -1
- package/src/components/AddAssemblyAliases.tsx +1 -1
- package/src/components/AddCodingSequence.tsx +255 -0
- package/src/components/AddFeature.tsx +1 -1
- package/src/components/AddRefSeqAliases.tsx +3 -2
- package/src/components/CopyFeature.tsx +9 -7
- package/src/components/CreateApolloAnnotation.tsx +32 -24
- package/src/components/DeleteAssembly.tsx +2 -2
- package/src/components/DeleteFeature.tsx +1 -4
- package/src/components/DownloadGFF3.tsx +4 -3
- package/src/components/DuplicateFeatureDialog.tsx +154 -0
- package/src/components/DuplicateTranscript.tsx +1 -1
- package/src/components/FilterTranscripts.tsx +1 -1
- package/src/components/ImportAnnotations.tsx +108 -0
- package/src/components/ManageUsers.tsx +1 -2
- package/src/components/OntologyTermAutocomplete.tsx +1 -3
- package/src/components/OntologyTermMultiSelect.tsx +3 -1
- package/src/components/ViewChangeLog.tsx +2 -1
- package/src/components/index.ts +2 -0
- package/src/extensions/annotationFromPileup.ts +34 -26
- package/src/index.ts +9 -8
- package/src/session/ClientDataStore.ts +16 -21
- package/src/session/handleApolloFeaturesUrlParam.ts +122 -0
- package/src/session/session.ts +49 -9
- package/src/util/annotationFeatureUtils.ts +1 -1
- package/src/util/displayUtils.ts +1 -1
- package/src/util/glyphUtils.ts +3 -3
- package/src/util/sequenceUtils.ts +29 -0
|
@@ -41,6 +41,7 @@ var xDataGrid = require('@mui/x-data-grid');
|
|
|
41
41
|
var gff = require('@gmod/gff');
|
|
42
42
|
var fileSaver = require('file-saver');
|
|
43
43
|
var AccountCircleIcon = require('@mui/icons-material/AccountCircle');
|
|
44
|
+
var BusinessIcon = require('@mui/icons-material/Business');
|
|
44
45
|
var AdapterType = require('@jbrowse/core/pluggableElementTypes/AdapterType');
|
|
45
46
|
var BaseAdapter = require('@jbrowse/core/data_adapters/BaseAdapter');
|
|
46
47
|
var nanoid = require('nanoid');
|
|
@@ -53,9 +54,9 @@ var MoreHorizIcon = require('@mui/icons-material/MoreHoriz');
|
|
|
53
54
|
var AddBoxIcon = require('@mui/icons-material/AddBox');
|
|
54
55
|
var mst$1 = require('@apollo-annotation/mst');
|
|
55
56
|
var styled = require('@emotion/styled');
|
|
57
|
+
var RemoveIcon = require('@mui/icons-material/Remove');
|
|
56
58
|
var ContentCopyIcon = require('@mui/icons-material/ContentCopy');
|
|
57
59
|
var ContentCutIcon = require('@mui/icons-material/ContentCut');
|
|
58
|
-
var RemoveIcon = require('@mui/icons-material/Remove');
|
|
59
60
|
var ClearIcon = require('@mui/icons-material/Clear');
|
|
60
61
|
var UnfoldLessIcon = require('@mui/icons-material/UnfoldLess');
|
|
61
62
|
var tracks = require('@jbrowse/core/util/tracks');
|
|
@@ -69,7 +70,7 @@ var RedoIcon = require('@mui/icons-material/Redo');
|
|
|
69
70
|
var UndoIcon = require('@mui/icons-material/Undo');
|
|
70
71
|
var SaveIcon = require('@mui/icons-material/Save');
|
|
71
72
|
|
|
72
|
-
var version = "1.
|
|
73
|
+
var version = "1.1.1";
|
|
73
74
|
|
|
74
75
|
const ApolloConfigSchema = configuration.ConfigurationSchema('ApolloInternetAccount', {
|
|
75
76
|
baseURL: {
|
|
@@ -1372,9 +1373,7 @@ async function getValidTerms(ontologyStore, fetchValidTerms, filterTerms, signal
|
|
|
1372
1373
|
result = customTermList;
|
|
1373
1374
|
}
|
|
1374
1375
|
}
|
|
1375
|
-
|
|
1376
|
-
result = await ontologyStore.getAllTerms();
|
|
1377
|
-
}
|
|
1376
|
+
result ??= await ontologyStore.getAllTerms();
|
|
1378
1377
|
return filterTerms ? result.filter((element) => filterTerms(element)) : result;
|
|
1379
1378
|
}
|
|
1380
1379
|
|
|
@@ -1555,7 +1554,7 @@ function CopyFeature({ changeManager, handleClose, session, sourceAssemblyId, so
|
|
|
1555
1554
|
setErrorMessage('No assemblies to copy to');
|
|
1556
1555
|
return;
|
|
1557
1556
|
}
|
|
1558
|
-
const assembly = await assemblyManager.waitForAssembly(selectedAssemblyId);
|
|
1557
|
+
const assembly = (await assemblyManager.waitForAssembly(selectedAssemblyId));
|
|
1559
1558
|
if (!assembly) {
|
|
1560
1559
|
return;
|
|
1561
1560
|
}
|
|
@@ -1584,7 +1583,7 @@ function CopyFeature({ changeManager, handleClose, session, sourceAssemblyId, so
|
|
|
1584
1583
|
event.preventDefault();
|
|
1585
1584
|
setErrorMessage('');
|
|
1586
1585
|
const featureLength = sourceFeature.length;
|
|
1587
|
-
const assembly = await assemblyManager.waitForAssembly(selectedAssemblyId);
|
|
1586
|
+
const assembly = (await assemblyManager.waitForAssembly(selectedAssemblyId));
|
|
1588
1587
|
if (!assembly) {
|
|
1589
1588
|
setErrorMessage(`Assembly not found: ${selectedAssemblyId}.`);
|
|
1590
1589
|
return;
|
|
@@ -1922,8 +1921,7 @@ function DeleteFeature({ changeManager, handleClose, selectedFeature, session, s
|
|
|
1922
1921
|
locationChanges.push(geneChange);
|
|
1923
1922
|
}
|
|
1924
1923
|
}
|
|
1925
|
-
if (featureTypeOntology
|
|
1926
|
-
featureTypeOntology.isTypeOf(sourceFeature.type, 'exon')) {
|
|
1924
|
+
if (featureTypeOntology?.isTypeOf(sourceFeature.type, 'exon')) {
|
|
1927
1925
|
const cdsChange = trimCDS(sourceFeature);
|
|
1928
1926
|
if (cdsChange) {
|
|
1929
1927
|
if (cdsChange.typeName === 'DeleteFeatureChange') {
|
|
@@ -2176,7 +2174,7 @@ function getOverlappingEdge(feature, x, minMax) {
|
|
|
2176
2174
|
return;
|
|
2177
2175
|
}
|
|
2178
2176
|
function isSelectedFeature(feature, selectedFeature) {
|
|
2179
|
-
return
|
|
2177
|
+
return feature._id === selectedFeature?._id;
|
|
2180
2178
|
}
|
|
2181
2179
|
function makeFeatureLabel(feature) {
|
|
2182
2180
|
let name;
|
|
@@ -2782,6 +2780,153 @@ function AddAssemblyAliases({ changeManager, handleClose, session, }) {
|
|
|
2782
2780
|
}, pageSizeOptions: [5], processRowUpdate: processRowUpdate, disableRowSelectionOnClick: true }) }) }), errorMessage ? (jsxRuntime.jsx(material.DialogContent, { children: jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage }) })) : null] }));
|
|
2783
2781
|
}
|
|
2784
2782
|
|
|
2783
|
+
const START_CODON = 'ATG';
|
|
2784
|
+
const STOP_CODONS = new Set(['TAA', 'TAG', 'TGA']);
|
|
2785
|
+
function findLongestOrf(sequence) {
|
|
2786
|
+
const starts = [
|
|
2787
|
+
null,
|
|
2788
|
+
null,
|
|
2789
|
+
null,
|
|
2790
|
+
];
|
|
2791
|
+
let longest;
|
|
2792
|
+
for (let start = 0; start < sequence.length - 2; start++) {
|
|
2793
|
+
const codon = sequence.slice(start, start + 3).toUpperCase();
|
|
2794
|
+
const frame = (start % 3);
|
|
2795
|
+
if (codon === START_CODON && starts[frame] === null) {
|
|
2796
|
+
starts[frame] = start;
|
|
2797
|
+
}
|
|
2798
|
+
else if (STOP_CODONS.has(codon)) {
|
|
2799
|
+
const inFrameStart = starts[frame];
|
|
2800
|
+
if (inFrameStart === null) {
|
|
2801
|
+
continue;
|
|
2802
|
+
}
|
|
2803
|
+
const length = start - inFrameStart;
|
|
2804
|
+
if (!longest || longest[1] - longest[0] < length) {
|
|
2805
|
+
longest = [inFrameStart, start];
|
|
2806
|
+
}
|
|
2807
|
+
starts[frame] = null;
|
|
2808
|
+
}
|
|
2809
|
+
}
|
|
2810
|
+
return longest;
|
|
2811
|
+
}
|
|
2812
|
+
|
|
2813
|
+
// Returns the genomic half-open boundary for stitched position `pos`.
|
|
2814
|
+
// For strand +1: the inclusive start (min) of the nucleotide.
|
|
2815
|
+
// For strand -1: the exclusive end (max) of the nucleotide.
|
|
2816
|
+
// Symmetric usage:
|
|
2817
|
+
// plus: cdsMin = helper(orf[0]), cdsMax = helper(orf[1]+3)
|
|
2818
|
+
// minus: cdsMax = helper(orf[0]), cdsMin = helper(orf[1]+3)
|
|
2819
|
+
function stitchedToGenomicBound(pos, exons, strand) {
|
|
2820
|
+
let cum = 0;
|
|
2821
|
+
for (const exon of exons) {
|
|
2822
|
+
const len = exon.max - exon.min;
|
|
2823
|
+
if (pos <= cum + len) {
|
|
2824
|
+
const off = pos - cum;
|
|
2825
|
+
return strand === 1 ? exon.min + off : exon.max - off;
|
|
2826
|
+
}
|
|
2827
|
+
cum += len;
|
|
2828
|
+
}
|
|
2829
|
+
const last = exons.at(-1);
|
|
2830
|
+
if (!last) {
|
|
2831
|
+
throw new Error('No exons found');
|
|
2832
|
+
}
|
|
2833
|
+
return strand === 1 ? last.max : last.min;
|
|
2834
|
+
}
|
|
2835
|
+
function AddCodingSequence({ changeManager, handleClose, refName, session, sourceAssemblyId, sourceFeature, }) {
|
|
2836
|
+
const [method, setMethod] = React.useState('longest-orf');
|
|
2837
|
+
const [minInput, setMinInput] = React.useState('');
|
|
2838
|
+
const [maxInput, setMaxInput] = React.useState('');
|
|
2839
|
+
const [errorMessage, setErrorMessage] = React.useState('');
|
|
2840
|
+
async function onSubmit(event) {
|
|
2841
|
+
event.preventDefault();
|
|
2842
|
+
setErrorMessage('');
|
|
2843
|
+
let cdsMin;
|
|
2844
|
+
let cdsMax;
|
|
2845
|
+
try {
|
|
2846
|
+
if (method === 'longest-orf') {
|
|
2847
|
+
const exonParts = sourceFeature.transcriptExonParts.filter((p) => p.type === 'exon');
|
|
2848
|
+
if (exonParts.length === 0) {
|
|
2849
|
+
setErrorMessage('No exons found in this transcript');
|
|
2850
|
+
return;
|
|
2851
|
+
}
|
|
2852
|
+
const backendDriver = session.apolloDataStore.getBackendDriver(sourceAssemblyId);
|
|
2853
|
+
if (!backendDriver) {
|
|
2854
|
+
setErrorMessage('No backend driver found for this assembly');
|
|
2855
|
+
return;
|
|
2856
|
+
}
|
|
2857
|
+
let stitchedSequence = '';
|
|
2858
|
+
for (const exon of exonParts) {
|
|
2859
|
+
const { seq } = await backendDriver.getSequence({
|
|
2860
|
+
assemblyName: sourceAssemblyId,
|
|
2861
|
+
refName,
|
|
2862
|
+
start: exon.min,
|
|
2863
|
+
end: exon.max,
|
|
2864
|
+
});
|
|
2865
|
+
stitchedSequence += sourceFeature.strand === -1 ? util.revcom(seq) : seq;
|
|
2866
|
+
}
|
|
2867
|
+
const orf = findLongestOrf(stitchedSequence);
|
|
2868
|
+
if (!orf) {
|
|
2869
|
+
;
|
|
2870
|
+
session.notify('No open reading frame found in this transcript', 'error');
|
|
2871
|
+
handleClose();
|
|
2872
|
+
return;
|
|
2873
|
+
}
|
|
2874
|
+
const strand = sourceFeature.strand ?? 1;
|
|
2875
|
+
if (strand === 1) {
|
|
2876
|
+
cdsMin = stitchedToGenomicBound(orf[0], exonParts, 1);
|
|
2877
|
+
cdsMax = stitchedToGenomicBound(orf[1] + 3, exonParts, 1);
|
|
2878
|
+
}
|
|
2879
|
+
else {
|
|
2880
|
+
cdsMax = stitchedToGenomicBound(orf[0], exonParts, -1);
|
|
2881
|
+
cdsMin = stitchedToGenomicBound(orf[1] + 3, exonParts, -1);
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
else {
|
|
2885
|
+
cdsMin = Number(minInput) - 1;
|
|
2886
|
+
cdsMax = Number(maxInput);
|
|
2887
|
+
}
|
|
2888
|
+
const _id = new ObjectID().toHexString();
|
|
2889
|
+
const addedFeature = {
|
|
2890
|
+
_id,
|
|
2891
|
+
refSeq: sourceFeature.refSeq,
|
|
2892
|
+
min: cdsMin,
|
|
2893
|
+
max: cdsMax,
|
|
2894
|
+
type: 'CDS',
|
|
2895
|
+
};
|
|
2896
|
+
if (sourceFeature.strand) {
|
|
2897
|
+
addedFeature.strand = sourceFeature.strand;
|
|
2898
|
+
}
|
|
2899
|
+
const change = new shared.AddFeatureChange({
|
|
2900
|
+
changedIds: [sourceFeature._id],
|
|
2901
|
+
typeName: 'AddFeatureChange',
|
|
2902
|
+
assembly: sourceAssemblyId,
|
|
2903
|
+
addedFeature,
|
|
2904
|
+
parentFeatureId: sourceFeature._id,
|
|
2905
|
+
});
|
|
2906
|
+
await changeManager.submit(change);
|
|
2907
|
+
session.apolloSetSelectedFeature(_id);
|
|
2908
|
+
handleClose();
|
|
2909
|
+
}
|
|
2910
|
+
catch (error) {
|
|
2911
|
+
setErrorMessage(error instanceof Error ? error.message : 'An unknown error occurred');
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
const manualError = method === 'manual' &&
|
|
2915
|
+
Boolean(minInput) &&
|
|
2916
|
+
Boolean(maxInput) &&
|
|
2917
|
+
Number(maxInput) <= Number(minInput);
|
|
2918
|
+
const submitDisabled = method === 'manual' ? !(minInput && maxInput) || manualError : false;
|
|
2919
|
+
return (jsxRuntime.jsxs(Dialog, { open: true, title: "Add coding sequence", handleClose: handleClose, maxWidth: false, "data-testid": "add-coding-sequence", children: [jsxRuntime.jsxs("form", { onSubmit: (event) => {
|
|
2920
|
+
void onSubmit(event);
|
|
2921
|
+
}, children: [jsxRuntime.jsxs(material.DialogContent, { style: { display: 'flex', flexDirection: 'column' }, children: [jsxRuntime.jsx(material.FormControl, { children: jsxRuntime.jsxs(material.RadioGroup, { value: method, onChange: (e) => {
|
|
2922
|
+
setMethod(e.target.value);
|
|
2923
|
+
}, children: [jsxRuntime.jsx(material.FormControlLabel, { value: "longest-orf", control: jsxRuntime.jsx(material.Radio, {}), label: "Calculate longest open reading frame" }), jsxRuntime.jsx(material.FormControlLabel, { value: "manual", control: jsxRuntime.jsx(material.Radio, {}), label: "Manual" })] }) }), method === 'manual' ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(material.TextField, { margin: "dense", id: "cds-min", label: "Min", type: "number", fullWidth: true, variant: "outlined", value: minInput, onChange: (e) => {
|
|
2924
|
+
setMinInput(e.target.value);
|
|
2925
|
+
} }), jsxRuntime.jsx(material.TextField, { margin: "dense", id: "cds-max", label: "Max", type: "number", fullWidth: true, variant: "outlined", value: maxInput, onChange: (e) => {
|
|
2926
|
+
setMaxInput(e.target.value);
|
|
2927
|
+
}, error: manualError, helperText: manualError ? '"Max" must be greater than "Min"' : null })] })) : null] }), jsxRuntime.jsxs(material.DialogActions, { children: [jsxRuntime.jsx(material.Button, { variant: "contained", type: "submit", disabled: submitDisabled, children: "Submit" }), jsxRuntime.jsx(material.Button, { variant: "outlined", type: "button", onClick: handleClose, children: "Cancel" })] })] }), errorMessage ? (jsxRuntime.jsx(material.DialogContent, { children: jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage }) })) : null] }));
|
|
2928
|
+
}
|
|
2929
|
+
|
|
2785
2930
|
class BackendDriver {
|
|
2786
2931
|
clientStore;
|
|
2787
2932
|
constructor(clientStore) {
|
|
@@ -2854,7 +2999,7 @@ class LocalDriver extends BackendDriver {
|
|
|
2854
2999
|
async getSequence(region) {
|
|
2855
3000
|
const session = util.getSession(this.clientStore);
|
|
2856
3001
|
const { assemblyManager } = session;
|
|
2857
|
-
const assembly = await assemblyManager.waitForAssembly(region.assemblyName);
|
|
3002
|
+
const assembly = (await assemblyManager.waitForAssembly(region.assemblyName));
|
|
2858
3003
|
if (!assembly) {
|
|
2859
3004
|
throw new Error(`Assembly not found: "${region.assemblyName}"`);
|
|
2860
3005
|
}
|
|
@@ -2885,7 +3030,7 @@ class LocalDriver extends BackendDriver {
|
|
|
2885
3030
|
async getRegions(assemblyName) {
|
|
2886
3031
|
const session = util.getSession(this.clientStore);
|
|
2887
3032
|
const { assemblyManager } = session;
|
|
2888
|
-
const assembly = await assemblyManager.waitForAssembly(assemblyName);
|
|
3033
|
+
const assembly = (await assemblyManager.waitForAssembly(assemblyName));
|
|
2889
3034
|
if (!assembly) {
|
|
2890
3035
|
throw new Error(`Assembly not found: "${assemblyName}"`);
|
|
2891
3036
|
}
|
|
@@ -2901,7 +3046,7 @@ class LocalDriver extends BackendDriver {
|
|
|
2901
3046
|
async getRefNameAliases(assemblyName) {
|
|
2902
3047
|
const session = util.getSession(this.clientStore);
|
|
2903
3048
|
const { assemblyManager } = session;
|
|
2904
|
-
const assembly = await assemblyManager.waitForAssembly(assemblyName);
|
|
3049
|
+
const assembly = (await assemblyManager.waitForAssembly(assemblyName));
|
|
2905
3050
|
if (!assembly) {
|
|
2906
3051
|
throw new Error(`Assembly not found: "${assemblyName}"`);
|
|
2907
3052
|
}
|
|
@@ -3560,7 +3705,7 @@ class CollaborationServerDriver extends BackendDriver {
|
|
|
3560
3705
|
const localSessionId = shared.makeUserSessionId(token);
|
|
3561
3706
|
const changeManager = new ChangeManager(this.clientStore);
|
|
3562
3707
|
// Save server last change sequence into session storage
|
|
3563
|
-
internetAccount.setLastChangeSequenceNumber(
|
|
3708
|
+
internetAccount.setLastChangeSequenceNumber(message.changeSequence);
|
|
3564
3709
|
if (message.userSessionId === localSessionId) {
|
|
3565
3710
|
return; // we did this change, no need to apply it again
|
|
3566
3711
|
}
|
|
@@ -3608,14 +3753,10 @@ class CollaborationServerDriver extends BackendDriver {
|
|
|
3608
3753
|
const seq = await inFlightPromise;
|
|
3609
3754
|
return { seq, refSeq };
|
|
3610
3755
|
}
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
let apolloRefSeq = apolloAssembly.refSeqs.get(refSeq);
|
|
3616
|
-
if (!apolloRefSeq) {
|
|
3617
|
-
apolloRefSeq = apolloAssembly.addRefSeq(refSeq, refName);
|
|
3618
|
-
}
|
|
3756
|
+
const apolloAssembly = this.clientStore.assemblies.get(assemblyName) ??
|
|
3757
|
+
this.clientStore.addAssembly(assemblyName);
|
|
3758
|
+
const apolloRefSeq = apolloAssembly.refSeqs.get(refSeq) ??
|
|
3759
|
+
apolloAssembly.addRefSeq(refSeq, refName);
|
|
3619
3760
|
const clientStoreSequence = apolloRefSeq.getSequence(start, end);
|
|
3620
3761
|
if (clientStoreSequence.length === end - start) {
|
|
3621
3762
|
return { seq: clientStoreSequence, refSeq };
|
|
@@ -3972,7 +4113,7 @@ function AddFeature({ changeManager, handleClose, region, session, }) {
|
|
|
3972
4113
|
setType(NewFeature[value]);
|
|
3973
4114
|
}
|
|
3974
4115
|
};
|
|
3975
|
-
let submitDisabled =
|
|
4116
|
+
let submitDisabled = error || !(start && end && type);
|
|
3976
4117
|
if ((type === NewFeature.CUSTOM && !customType) ||
|
|
3977
4118
|
(!strand && type === NewFeature.GENE_AND_SUBFEATURES) ||
|
|
3978
4119
|
(!strand && type === NewFeature.TRANSCRIPT_AND_SUBFEATURES)) {
|
|
@@ -3993,7 +4134,7 @@ function AddFeature({ changeManager, handleClose, region, session, }) {
|
|
|
3993
4134
|
function DeleteAssembly({ changeManager, handleClose, session, }) {
|
|
3994
4135
|
const { internetAccounts } = mobxStateTree.getRoot(session);
|
|
3995
4136
|
const [errorMessage, setErrorMessage] = React.useState('');
|
|
3996
|
-
const [confirmDelete,
|
|
4137
|
+
const [confirmDelete, setConfirmDelete] = React.useState(false);
|
|
3997
4138
|
const [submitted, setSubmitted] = React.useState(false);
|
|
3998
4139
|
const apolloInternetAccounts = internetAccounts.filter((ia) => ia.type === 'ApolloInternetAccount');
|
|
3999
4140
|
if (apolloInternetAccounts.length === 0) {
|
|
@@ -4034,7 +4175,7 @@ function DeleteAssembly({ changeManager, handleClose, session, }) {
|
|
|
4034
4175
|
event.preventDefault();
|
|
4035
4176
|
}
|
|
4036
4177
|
return (jsxRuntime.jsxs(Dialog, { open: true, title: "Delete Assembly", handleClose: handleClose, maxWidth: false, "data-testid": "delete-assembly", children: [jsxRuntime.jsxs("form", { onSubmit: onSubmit, children: [jsxRuntime.jsxs(material.DialogContent, { style: { display: 'flex', flexDirection: 'column' }, children: [apolloInternetAccounts.length > 1 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(material.DialogContentText, { children: "Select account" }), jsxRuntime.jsx(material.Select, { value: selectedInternetAccount.internetAccountId, onChange: handleChangeInternetAccount, disabled: submitted && !errorMessage, children: internetAccounts.map((option) => (jsxRuntime.jsx(material.MenuItem, { value: option.internetAccountId, children: option.name }, option.id))) })] })) : null, jsxRuntime.jsx(material.DialogContentText, { children: "Select assembly" }), jsxRuntime.jsx(material.Select, { labelId: "label", value: selectedAssembly?.name ?? '', onChange: handleChangeAssembly, disabled: assemblies.length === 0, children: assemblies.map((option) => (jsxRuntime.jsx(material.MenuItem, { value: option.name, children: option.displayName }, option.name))) }), jsxRuntime.jsx(material.DialogContentText, { children: jsxRuntime.jsx("strong", { style: { color: 'red' }, children: "NOTE: All assembly data will be deleted and this operation cannot be undone!" }) }), jsxRuntime.jsx(material.FormGroup, { children: jsxRuntime.jsx(material.FormControlLabel, { control: jsxRuntime.jsx(material.Checkbox, { checked: confirmDelete, onChange: () => {
|
|
4037
|
-
|
|
4178
|
+
setConfirmDelete(!confirmDelete);
|
|
4038
4179
|
} }), label: "I understand that all assembly data will be deleted" }) })] }), jsxRuntime.jsxs(material.DialogActions, { children: [jsxRuntime.jsx(material.Button, { disabled: !selectedAssembly || !confirmDelete, variant: "contained", type: "submit", children: "Delete" }), jsxRuntime.jsx(material.Button, { variant: "outlined", type: "submit", onClick: handleClose, children: "Cancel" })] })] }), errorMessage ? (jsxRuntime.jsx(material.DialogContent, { children: jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage }) })) : null] }));
|
|
4039
4180
|
}
|
|
4040
4181
|
|
|
@@ -4043,7 +4184,7 @@ function Export(props) {
|
|
|
4043
4184
|
return (jsxRuntime.jsx(material.SvgIcon, { viewBox: "0 0 24 24", ...props, children: jsxRuntime.jsx("path", { d: "M23,12L19,8V11H10V13H19V16M1,18V6C1,4.89 1.9,4 3,4H15A2,2 0 0,1 17,6V9H15V6H3V18H15V15H17V18A2,2 0 0,1 15,20H3A2,2 0 0,1 1,18Z" }) }));
|
|
4044
4185
|
}
|
|
4045
4186
|
function DownloadGFF3({ handleClose, session, assembly: assemblyName, }) {
|
|
4046
|
-
const [includeFASTA,
|
|
4187
|
+
const [includeFASTA, setIncludeFASTA] = React.useState(false);
|
|
4047
4188
|
const [errorMessage, setErrorMessage] = React.useState('');
|
|
4048
4189
|
const { getInternetAccount } = session.apolloDataStore;
|
|
4049
4190
|
const { assemblyManager } = session;
|
|
@@ -4092,7 +4233,7 @@ function DownloadGFF3({ handleClose, session, assembly: assemblyName, }) {
|
|
|
4092
4233
|
window.open(exportUri, '_blank');
|
|
4093
4234
|
}
|
|
4094
4235
|
return (jsxRuntime.jsxs(Dialog, { open: true, title: "Export annotations", handleClose: handleClose, maxWidth: false, "data-testid": "download-gff3", children: [jsxRuntime.jsxs("form", { onSubmit: onSubmit, children: [jsxRuntime.jsxs(material.DialogContent, { style: { display: 'flex', flexDirection: 'column' }, children: [jsxRuntime.jsxs(material.DialogContentText, { children: ["Exporting annotations for ", assemblyName] }), jsxRuntime.jsx(material.FormGroup, { children: jsxRuntime.jsx(material.FormControlLabel, { "data-testid": "include-fasta-checkbox", control: jsxRuntime.jsx(material.Checkbox, { checked: includeFASTA, onChange: () => {
|
|
4095
|
-
|
|
4236
|
+
setIncludeFASTA(!includeFASTA);
|
|
4096
4237
|
}, disabled: !internetAccountConfigId }), label: "Include fasta sequence in GFF output" }) })] }), jsxRuntime.jsxs(material.DialogActions, { children: [jsxRuntime.jsx(material.Button, { variant: "contained", type: "submit", children: "Download" }), jsxRuntime.jsx(material.Button, { variant: "outlined", type: "submit", onClick: handleClose, children: "Cancel" })] })] }), errorMessage ? (jsxRuntime.jsx(material.DialogContent, { children: jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage }) })) : null] }));
|
|
4097
4238
|
}
|
|
4098
4239
|
function getAssemblyGFF3Stream(assemblyName) {
|
|
@@ -4131,6 +4272,49 @@ async function* getFeaturesForAssembly(assemblyName) {
|
|
|
4131
4272
|
db.close();
|
|
4132
4273
|
}
|
|
4133
4274
|
|
|
4275
|
+
function ImportAnnotations({ assemblyName, handleClose, session, }) {
|
|
4276
|
+
const { changeManager } = session.apolloDataStore;
|
|
4277
|
+
const [file, setFile] = React.useState();
|
|
4278
|
+
const [errorMessage, setErrorMessage] = React.useState('');
|
|
4279
|
+
const [loading, setLoading] = React.useState(false);
|
|
4280
|
+
function handleChangeFile(e) {
|
|
4281
|
+
if (e.target.files) {
|
|
4282
|
+
setFile(e.target.files[0]);
|
|
4283
|
+
}
|
|
4284
|
+
}
|
|
4285
|
+
async function onSubmit(event) {
|
|
4286
|
+
event.preventDefault();
|
|
4287
|
+
if (!file) {
|
|
4288
|
+
return;
|
|
4289
|
+
}
|
|
4290
|
+
setLoading(true);
|
|
4291
|
+
setErrorMessage('');
|
|
4292
|
+
try {
|
|
4293
|
+
const stream = file
|
|
4294
|
+
.stream()
|
|
4295
|
+
.pipeThrough(new TransformStream(new gff.GFFTransformer({ parseSequences: false })));
|
|
4296
|
+
for await (const value of stream) {
|
|
4297
|
+
const annotationFeature = shared.gff3ToAnnotationFeature(value);
|
|
4298
|
+
const change = new shared.AddFeatureChange({
|
|
4299
|
+
changedIds: [annotationFeature._id],
|
|
4300
|
+
typeName: 'AddFeatureChange',
|
|
4301
|
+
assembly: assemblyName,
|
|
4302
|
+
addedFeature: annotationFeature,
|
|
4303
|
+
});
|
|
4304
|
+
await changeManager.submit(change);
|
|
4305
|
+
}
|
|
4306
|
+
handleClose();
|
|
4307
|
+
}
|
|
4308
|
+
catch (error) {
|
|
4309
|
+
setErrorMessage(String(error));
|
|
4310
|
+
}
|
|
4311
|
+
finally {
|
|
4312
|
+
setLoading(false);
|
|
4313
|
+
}
|
|
4314
|
+
}
|
|
4315
|
+
return (jsxRuntime.jsxs(Dialog, { open: true, title: "Import annotations", handleClose: handleClose, children: [loading ? jsxRuntime.jsx(material.LinearProgress, {}) : null, jsxRuntime.jsxs("form", { onSubmit: onSubmit, children: [jsxRuntime.jsxs(material.DialogContent, { children: [jsxRuntime.jsx(material.DialogContentText, { children: "Import annotations from a GFF3 file into the current Apollo track. Features are parsed and stored locally in the browser, so importing very large GFF3 files is not recommended." }), jsxRuntime.jsx("input", { type: "file", accept: ".gff,.gff3", onChange: handleChangeFile, disabled: loading }), errorMessage ? (jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage })) : null] }), jsxRuntime.jsxs(material.DialogActions, { children: [jsxRuntime.jsx(material.Button, { variant: "contained", type: "submit", disabled: !file || loading, children: "Import" }), jsxRuntime.jsx(material.Button, { variant: "outlined", onClick: handleClose, children: "Cancel" })] })] })] }));
|
|
4316
|
+
}
|
|
4317
|
+
|
|
4134
4318
|
function ImportFeatures({ changeManager, handleClose, session, }) {
|
|
4135
4319
|
const { apolloDataStore } = session;
|
|
4136
4320
|
const [file, setFile] = React.useState();
|
|
@@ -4534,7 +4718,7 @@ function ManageUsers({ changeManager, handleClose, session, }) {
|
|
|
4534
4718
|
});
|
|
4535
4719
|
return newRow;
|
|
4536
4720
|
}
|
|
4537
|
-
return (jsxRuntime.jsxs(Dialog, { open: true, fullScreen: true, title: "Manage users", handleClose: handleClose, "data-testid": "manage-users", children: [jsxRuntime.jsxs(material.DialogContent, { children: [apolloInternetAccounts.length > 1 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(material.DialogContentText, { children: "Select account" }), jsxRuntime.jsx(material.Select, { value: selectedInternetAccount.internetAccountId, onChange: handleChangeInternetAccount, disabled: !errorMessage, children: internetAccounts.map((option) => (jsxRuntime.jsx(material.MenuItem, { value: option.internetAccountId, children: option.name }, option.id))) })] })) : null, jsxRuntime.jsx("div", { style: { height: '100%', width: '100%' }, children: jsxRuntime.jsx(xDataGrid.DataGrid, { pagination: true, rows: users, columns: gridColumns, getRowId: (row) => row._id,
|
|
4721
|
+
return (jsxRuntime.jsxs(Dialog, { open: true, fullScreen: true, title: "Manage users", handleClose: handleClose, "data-testid": "manage-users", children: [jsxRuntime.jsxs(material.DialogContent, { children: [apolloInternetAccounts.length > 1 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(material.DialogContentText, { children: "Select account" }), jsxRuntime.jsx(material.Select, { value: selectedInternetAccount.internetAccountId, onChange: handleChangeInternetAccount, disabled: !errorMessage, children: internetAccounts.map((option) => (jsxRuntime.jsx(material.MenuItem, { value: option.internetAccountId, children: option.name }, option.id))) })] })) : null, jsxRuntime.jsx("div", { style: { height: '100%', width: '100%' }, children: jsxRuntime.jsx(xDataGrid.DataGrid, { pagination: true, rows: users, columns: gridColumns, getRowId: (row) => row._id, showToolbar: true, getRowHeight: () => 'auto', isCellEditable: (params) => !isCurrentUser(params.id), processRowUpdate: processRowUpdate, onProcessRowUpdateError: (error) => {
|
|
4538
4722
|
setErrorMessage(String(error));
|
|
4539
4723
|
} }) })] }), jsxRuntime.jsx(material.DialogActions, { children: jsxRuntime.jsx(material.Button, { variant: "outlined", type: "submit", onClick: handleClose, children: "Close" }) }), errorMessage ? (jsxRuntime.jsx(material.DialogContent, { children: jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage }) })) : null] }));
|
|
4540
4724
|
}
|
|
@@ -4844,12 +5028,13 @@ const AddRefSeqAliases = mobxReact.observer(function AddRefSeqAliases({ changeMa
|
|
|
4844
5028
|
const [enableSubmit, setEnableSubmit] = React.useState(false);
|
|
4845
5029
|
const [selectedAssembly, setSelectedAssembly] = React.useState();
|
|
4846
5030
|
const [selectedRows, setSelectedRows] = React.useState([]);
|
|
4847
|
-
const [refNameAliasMap, setRefNameAliasMap] = React.useState(new Map());
|
|
5031
|
+
const [refNameAliasMap, setRefNameAliasMap] = React.useState(() => new Map());
|
|
4848
5032
|
const { apolloDataStore } = session;
|
|
4849
5033
|
const { collaborationServerDriver } = apolloDataStore;
|
|
4850
5034
|
const assemblies = collaborationServerDriver.getAssemblies();
|
|
4851
5035
|
React.useEffect(() => {
|
|
4852
5036
|
if (assemblies.length > 0) {
|
|
5037
|
+
// eslint-disable-next-line @eslint-react/set-state-in-effect
|
|
4853
5038
|
setSelectedAssembly(assemblies[0]);
|
|
4854
5039
|
collaborationServerDriver
|
|
4855
5040
|
.getRefNameAliases(assemblies[0].name)
|
|
@@ -4861,7 +5046,7 @@ const AddRefSeqAliases = mobxReact.observer(function AddRefSeqAliases({ changeMa
|
|
|
4861
5046
|
setErrorMessage('Error fetching refName aliases for assembly');
|
|
4862
5047
|
});
|
|
4863
5048
|
}
|
|
4864
|
-
// eslint-disable-next-line react
|
|
5049
|
+
// eslint-disable-next-line @eslint-react/exhaustive-deps
|
|
4865
5050
|
}, []);
|
|
4866
5051
|
const initializeRefNameAliasMap = (refNameAliasesList) => {
|
|
4867
5052
|
const initialMap = new Map();
|
|
@@ -5266,17 +5451,23 @@ const useStyles$d = tssReact.makeStyles()((theme) => ({
|
|
|
5266
5451
|
}));
|
|
5267
5452
|
function GoogleButton(props) {
|
|
5268
5453
|
const { classes } = useStyles$d();
|
|
5269
|
-
const {
|
|
5270
|
-
return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(Google, {
|
|
5454
|
+
const { message } = props;
|
|
5455
|
+
return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(Google, {}), ...props, children: message }));
|
|
5271
5456
|
}
|
|
5272
5457
|
function MicrosoftButton(props) {
|
|
5273
5458
|
const { classes } = useStyles$d();
|
|
5274
|
-
const {
|
|
5275
|
-
return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(Microsoft, {
|
|
5459
|
+
const { message } = props;
|
|
5460
|
+
return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(Microsoft, {}), ...props, children: message }));
|
|
5276
5461
|
}
|
|
5277
5462
|
function GuestButton(props) {
|
|
5278
5463
|
const { classes } = useStyles$d();
|
|
5279
|
-
|
|
5464
|
+
const { message } = props;
|
|
5465
|
+
return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(AccountCircleIcon, { fontSize: "small" }), ...props, children: message }));
|
|
5466
|
+
}
|
|
5467
|
+
function GenericButton(props) {
|
|
5468
|
+
const { classes } = useStyles$d();
|
|
5469
|
+
const { message } = props;
|
|
5470
|
+
return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(BusinessIcon, { fontSize: "small" }), ...props, children: message }));
|
|
5280
5471
|
}
|
|
5281
5472
|
|
|
5282
5473
|
const useStyles$c = tssReact.makeStyles()((theme) => ({
|
|
@@ -5288,7 +5479,7 @@ const useStyles$c = tssReact.makeStyles()((theme) => ({
|
|
|
5288
5479
|
const AuthTypeSelector = ({ baseURL, handleClose, name, }) => {
|
|
5289
5480
|
const { classes } = useStyles$c();
|
|
5290
5481
|
const [errorMessage, setErrorMessage] = React.useState('');
|
|
5291
|
-
const [loginTypes, setLoginTypes] = React.useState(
|
|
5482
|
+
const [loginTypes, setLoginTypes] = React.useState(null);
|
|
5292
5483
|
React.useEffect(() => {
|
|
5293
5484
|
const controller = new AbortController();
|
|
5294
5485
|
const { signal } = controller;
|
|
@@ -5302,6 +5493,9 @@ const AuthTypeSelector = ({ baseURL, handleClose, name, }) => {
|
|
|
5302
5493
|
}
|
|
5303
5494
|
const data = (await response.json());
|
|
5304
5495
|
setLoginTypes(data);
|
|
5496
|
+
if (data.length === 0) {
|
|
5497
|
+
setErrorMessage('No login types configured');
|
|
5498
|
+
}
|
|
5305
5499
|
}
|
|
5306
5500
|
getAuthTypes().catch((error) => {
|
|
5307
5501
|
if (!aborting.isAbortException(error)) {
|
|
@@ -5313,26 +5507,35 @@ const AuthTypeSelector = ({ baseURL, handleClose, name, }) => {
|
|
|
5313
5507
|
};
|
|
5314
5508
|
}, [baseURL]);
|
|
5315
5509
|
function handleClick(authType) {
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
}
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5510
|
+
handleClose(authType);
|
|
5511
|
+
}
|
|
5512
|
+
if (loginTypes === null) {
|
|
5513
|
+
return 'Loading…';
|
|
5514
|
+
}
|
|
5515
|
+
const firstLoginType = loginTypes.at(0);
|
|
5516
|
+
if (firstLoginType && loginTypes.length === 1 && !firstLoginType.needsPopup) {
|
|
5517
|
+
handleClick(firstLoginType);
|
|
5518
|
+
}
|
|
5519
|
+
return (jsxRuntime.jsxs(Dialog, { open: true, title: `Log in to ${name}`, handleClose: handleClose, maxWidth: false, "data-testid": "login-apollo", children: [jsxRuntime.jsx(material.DialogContent, { style: { display: 'flex', flexDirection: 'column', paddingTop: 8 }, children: loginTypes.map((loginType) => {
|
|
5520
|
+
if (loginType.name === 'google') {
|
|
5521
|
+
return (jsxRuntime.jsx(GoogleButton, { message: loginType.message, onClick: () => {
|
|
5522
|
+
handleClick(loginType);
|
|
5523
|
+
} }, loginType.name));
|
|
5524
|
+
}
|
|
5525
|
+
if (loginType.name === 'microsoft') {
|
|
5526
|
+
return (jsxRuntime.jsx(MicrosoftButton, { message: loginType.message, onClick: () => {
|
|
5527
|
+
handleClick(loginType);
|
|
5528
|
+
} }, loginType.name));
|
|
5529
|
+
}
|
|
5530
|
+
if (loginType.name === 'guest') {
|
|
5531
|
+
return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(material.Divider, { className: classes.divider }), jsxRuntime.jsx(GuestButton, { message: loginType.message, onClick: () => {
|
|
5532
|
+
handleClick(loginType);
|
|
5533
|
+
} })] }, loginType.name));
|
|
5534
|
+
}
|
|
5535
|
+
return (jsxRuntime.jsx(GenericButton, { message: loginType.message, onClick: () => {
|
|
5536
|
+
handleClick(loginType);
|
|
5537
|
+
} }, loginType.name));
|
|
5538
|
+
}) }), jsxRuntime.jsx(material.DialogActions, { children: jsxRuntime.jsx(material.Button, { variant: "outlined", type: "submit", onClick: () => {
|
|
5336
5539
|
handleClose();
|
|
5337
5540
|
}, children: "Cancel" }) }), errorMessage ? (jsxRuntime.jsx(material.DialogContent, { children: jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage }) })) : null] }));
|
|
5338
5541
|
};
|
|
@@ -5528,13 +5731,13 @@ const stateModelFactory$3 = (configSchema) => {
|
|
|
5528
5731
|
},
|
|
5529
5732
|
]);
|
|
5530
5733
|
});
|
|
5531
|
-
if (authType
|
|
5734
|
+
if (authType.needsPopup) {
|
|
5532
5735
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
5533
|
-
self.openAuthWindow(authType, resolve, reject);
|
|
5736
|
+
self.openAuthWindow(authType.name, resolve, reject);
|
|
5534
5737
|
return;
|
|
5535
5738
|
}
|
|
5536
5739
|
const url = new URL('auth/login', baseURL);
|
|
5537
|
-
const searchParams = new URLSearchParams({ type: authType });
|
|
5740
|
+
const searchParams = new URLSearchParams({ type: authType.name });
|
|
5538
5741
|
url.search = searchParams.toString();
|
|
5539
5742
|
const uri = url.toString();
|
|
5540
5743
|
const response = await fetch(uri, { signal: self.controller.signal });
|
|
@@ -5655,7 +5858,7 @@ const stateModelFactory$3 = (configSchema) => {
|
|
|
5655
5858
|
socket.on('COMMON', (message) => {
|
|
5656
5859
|
if ('checkResult' in message) {
|
|
5657
5860
|
if (message.deleted) {
|
|
5658
|
-
deleteCheckResult(message.checkResult._id
|
|
5861
|
+
deleteCheckResult(message.checkResult._id);
|
|
5659
5862
|
}
|
|
5660
5863
|
else {
|
|
5661
5864
|
addCheckResult(message.checkResult);
|
|
@@ -6275,7 +6478,9 @@ const DefaultAttributeEditor = mobxReact.observer(function DefaultAttributeEdito
|
|
|
6275
6478
|
return newValues;
|
|
6276
6479
|
});
|
|
6277
6480
|
}
|
|
6278
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [newValues.map((value, idx) => (
|
|
6481
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [newValues.map((value, idx) => (
|
|
6482
|
+
// eslint-disable-next-line @eslint-react/no-array-index-key
|
|
6483
|
+
jsxRuntime.jsxs("div", { style: { display: 'flex' }, children: [jsxRuntime.jsx(StringTextField, { value: value, onChangeCommitted: (editedValue) => {
|
|
6279
6484
|
updateValue(idx, editedValue);
|
|
6280
6485
|
}, variant: "outlined", fullWidth: true }), jsxRuntime.jsx(material.IconButton, { "aria-label": "delete", size: "medium", edge: "end", onClick: () => {
|
|
6281
6486
|
deleteValue(idx);
|
|
@@ -6287,7 +6492,9 @@ const DefaultAttributeEditor = mobxReact.observer(function DefaultAttributeEdito
|
|
|
6287
6492
|
});
|
|
6288
6493
|
|
|
6289
6494
|
function DefaultAttributeViewer({ values }) {
|
|
6290
|
-
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: values?.map((value, idx) => (jsxRuntime.jsx(material.Typography
|
|
6495
|
+
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: values?.map((value, idx) => (jsxRuntime.jsx(material.Typography
|
|
6496
|
+
// eslint-disable-next-line @eslint-react/no-array-index-key
|
|
6497
|
+
, { variant: "body2", color: "textSecondary", children: value }, `${idx}.${value}`))) }));
|
|
6291
6498
|
}
|
|
6292
6499
|
|
|
6293
6500
|
const useStyles$a = tssReact.makeStyles()((theme) => ({
|
|
@@ -6593,6 +6800,7 @@ const ApolloFeatureDetailsWidget = mobxReact.observer(function ApolloFeatureDeta
|
|
|
6593
6800
|
const { classes } = useStyles$8();
|
|
6594
6801
|
const [panelState, setPanelState] = React.useState(['attributes']);
|
|
6595
6802
|
React.useEffect(() => {
|
|
6803
|
+
// eslint-disable-next-line @eslint-react/set-state-in-effect
|
|
6596
6804
|
setPanelState(['attributes']);
|
|
6597
6805
|
}, [feature]);
|
|
6598
6806
|
if (!(feature && currentAssembly)) {
|
|
@@ -6894,7 +7102,7 @@ const TranscriptSequence = mobxReact.observer(function TranscriptSequence({ asse
|
|
|
6894
7102
|
else {
|
|
6895
7103
|
setSequenceOptions(defaultSequenceOptions);
|
|
6896
7104
|
}
|
|
6897
|
-
// eslint-disable-next-line react
|
|
7105
|
+
// eslint-disable-next-line @eslint-react/exhaustive-deps
|
|
6898
7106
|
}, [feature]);
|
|
6899
7107
|
if (!(currentAssembly && refData)) {
|
|
6900
7108
|
return null;
|
|
@@ -6957,19 +7165,6 @@ const TranscriptSequence = mobxReact.observer(function TranscriptSequence({ asse
|
|
|
6957
7165
|
.join(';'), "(strand=", feature.strand === 1 ? '+' : '-', ";length=", getSequenceLength(sequenceSegments), ")", jsxRuntime.jsx("br", {}), wrapSequence(sequenceSegments, SEQUENCE_WRAP_LENGTH)] })] }));
|
|
6958
7166
|
});
|
|
6959
7167
|
|
|
6960
|
-
const StyledTextField = styled(NumberTextField)(() => ({
|
|
6961
|
-
'&.MuiFormControl-root': {
|
|
6962
|
-
marginTop: 0,
|
|
6963
|
-
marginBottom: 0,
|
|
6964
|
-
width: '100%',
|
|
6965
|
-
},
|
|
6966
|
-
'& .MuiInputBase-input': {
|
|
6967
|
-
fontSize: 12,
|
|
6968
|
-
height: 20,
|
|
6969
|
-
padding: 1,
|
|
6970
|
-
paddingLeft: 10,
|
|
6971
|
-
},
|
|
6972
|
-
}));
|
|
6973
7168
|
const SequenceContainer = styled('div')({
|
|
6974
7169
|
display: 'flex',
|
|
6975
7170
|
justifyContent: 'center',
|
|
@@ -6991,6 +7186,195 @@ const StyledAccordionSummary$1 = styled(material.AccordionSummary)(() => ({
|
|
|
6991
7186
|
maxHeight: 30,
|
|
6992
7187
|
},
|
|
6993
7188
|
}));
|
|
7189
|
+
function Translation({ changeInProgress, cdsLocations, refData, strand,
|
|
7190
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
7191
|
+
updateCDSLocation, cdsMin, cdsMax, feature, session, }) {
|
|
7192
|
+
const seqRef = React.useRef(null);
|
|
7193
|
+
const { notify } = session;
|
|
7194
|
+
const cdsSequences = [];
|
|
7195
|
+
const [firstLocation] = cdsLocations;
|
|
7196
|
+
for (const loc of firstLocation) {
|
|
7197
|
+
const seq = refData.getSequence(loc.min, loc.max);
|
|
7198
|
+
cdsSequences.push(strand === -1 ? util.revcom(seq) : seq);
|
|
7199
|
+
}
|
|
7200
|
+
const cdsSequence = cdsSequences.join('');
|
|
7201
|
+
const proteinSequence = [];
|
|
7202
|
+
for (let codonGenomicPos = 0; codonGenomicPos < cdsSequence.length; codonGenomicPos += 3) {
|
|
7203
|
+
const codonSeq = cdsSequence
|
|
7204
|
+
.slice(codonGenomicPos, codonGenomicPos + 3)
|
|
7205
|
+
.toUpperCase();
|
|
7206
|
+
const protein = util.defaultCodonTable[codonSeq] || '&';
|
|
7207
|
+
proteinSequence.push(protein);
|
|
7208
|
+
}
|
|
7209
|
+
const onCopyClick = () => {
|
|
7210
|
+
const seqDiv = seqRef.current;
|
|
7211
|
+
if (!seqDiv) {
|
|
7212
|
+
return;
|
|
7213
|
+
}
|
|
7214
|
+
void copyToClipboard(seqDiv);
|
|
7215
|
+
};
|
|
7216
|
+
// Codon position is the index of the start codon in the CDS genomic sequence
|
|
7217
|
+
// Calculate the genomic location of the start codon based on the codon position in the CDS
|
|
7218
|
+
const getCodonGenomicLocation = (codonGenomicPosition) => {
|
|
7219
|
+
const [firstLocation] = cdsLocations;
|
|
7220
|
+
let cdsLen = 0;
|
|
7221
|
+
const sortedCDSLocations = firstLocation.toSorted(({ min: a }, { min: b }) => a - b);
|
|
7222
|
+
// Suppose CDS locations are [{min: 0, max: 10}, {min: 20, max: 30}, {min: 40, max: 50}]
|
|
7223
|
+
// and codonGenomicPosition is 25
|
|
7224
|
+
// ((10 - 0) + (30 - 20) + (50 - 40)) > 25
|
|
7225
|
+
// So, start codon is in (40, 50)
|
|
7226
|
+
// 40 + (25-20) = 45 is the genomic location of the start codon
|
|
7227
|
+
if (strand === 1) {
|
|
7228
|
+
for (const loc of sortedCDSLocations) {
|
|
7229
|
+
const locLength = loc.max - loc.min;
|
|
7230
|
+
if (cdsLen + locLength > codonGenomicPosition) {
|
|
7231
|
+
return loc.min + (codonGenomicPosition - cdsLen);
|
|
7232
|
+
}
|
|
7233
|
+
cdsLen += locLength;
|
|
7234
|
+
}
|
|
7235
|
+
}
|
|
7236
|
+
else if (strand === -1) {
|
|
7237
|
+
for (let i = sortedCDSLocations.length - 1; i >= 0; i--) {
|
|
7238
|
+
const loc = sortedCDSLocations[i];
|
|
7239
|
+
const locLength = loc.max - loc.min;
|
|
7240
|
+
if (cdsLen + locLength > codonGenomicPosition) {
|
|
7241
|
+
return loc.max - (codonGenomicPosition - cdsLen);
|
|
7242
|
+
}
|
|
7243
|
+
cdsLen += locLength;
|
|
7244
|
+
}
|
|
7245
|
+
}
|
|
7246
|
+
if (strand === 1) {
|
|
7247
|
+
return cdsMin;
|
|
7248
|
+
}
|
|
7249
|
+
return cdsMax;
|
|
7250
|
+
};
|
|
7251
|
+
const trimTranslationSequence = () => {
|
|
7252
|
+
if ((proteinSequence.at(0) === 'M' && proteinSequence.at(-1) === '*') ||
|
|
7253
|
+
proteinSequence.length === 0) {
|
|
7254
|
+
return;
|
|
7255
|
+
}
|
|
7256
|
+
// Trim any sequence before first start codon and after stop codon
|
|
7257
|
+
const startCodonIndex = proteinSequence.indexOf('M');
|
|
7258
|
+
if (startCodonIndex === -1) {
|
|
7259
|
+
notify('Start codon not found', 'error');
|
|
7260
|
+
return;
|
|
7261
|
+
}
|
|
7262
|
+
const stopCodonIndex = proteinSequence.indexOf('*', startCodonIndex);
|
|
7263
|
+
if (stopCodonIndex === -1) {
|
|
7264
|
+
notify('Stop codon not found', 'error');
|
|
7265
|
+
return;
|
|
7266
|
+
}
|
|
7267
|
+
const startCodonPos = startCodonIndex * 3;
|
|
7268
|
+
const stopCodonPos = stopCodonIndex * 3;
|
|
7269
|
+
const startCodonGenomicLoc = getCodonGenomicLocation(startCodonPos);
|
|
7270
|
+
let stopCodonGenomicLoc = getCodonGenomicLocation(stopCodonPos);
|
|
7271
|
+
if (strand === 1) {
|
|
7272
|
+
if (startCodonGenomicLoc > stopCodonGenomicLoc) {
|
|
7273
|
+
notify('Start codon genomic location should be less than stop codon genomic location', 'error');
|
|
7274
|
+
return;
|
|
7275
|
+
}
|
|
7276
|
+
let promise;
|
|
7277
|
+
stopCodonGenomicLoc += 3; // move to end of stop codon
|
|
7278
|
+
if (startCodonGenomicLoc !== cdsMin) {
|
|
7279
|
+
promise = new Promise((resolve) => {
|
|
7280
|
+
updateCDSLocation(cdsMin, startCodonGenomicLoc, feature, true, () => {
|
|
7281
|
+
resolve(true);
|
|
7282
|
+
});
|
|
7283
|
+
});
|
|
7284
|
+
}
|
|
7285
|
+
if (stopCodonGenomicLoc !== cdsMax) {
|
|
7286
|
+
if (promise) {
|
|
7287
|
+
void promise.then(() => {
|
|
7288
|
+
updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
|
|
7289
|
+
});
|
|
7290
|
+
}
|
|
7291
|
+
else {
|
|
7292
|
+
updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
|
|
7293
|
+
}
|
|
7294
|
+
}
|
|
7295
|
+
}
|
|
7296
|
+
if (strand === -1) {
|
|
7297
|
+
// reverse strand
|
|
7298
|
+
if (startCodonGenomicLoc < stopCodonGenomicLoc) {
|
|
7299
|
+
notify('Start codon genomic location should be less than stop codon genomic location', 'error');
|
|
7300
|
+
return;
|
|
7301
|
+
}
|
|
7302
|
+
let promise;
|
|
7303
|
+
stopCodonGenomicLoc -= 3; // move to end of stop codon
|
|
7304
|
+
if (startCodonGenomicLoc !== cdsMax) {
|
|
7305
|
+
promise = new Promise((resolve) => {
|
|
7306
|
+
updateCDSLocation(cdsMax, startCodonGenomicLoc, feature, false, () => {
|
|
7307
|
+
resolve(true);
|
|
7308
|
+
});
|
|
7309
|
+
});
|
|
7310
|
+
}
|
|
7311
|
+
if (stopCodonGenomicLoc !== cdsMin) {
|
|
7312
|
+
if (promise) {
|
|
7313
|
+
void promise.then(() => {
|
|
7314
|
+
updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
|
|
7315
|
+
});
|
|
7316
|
+
}
|
|
7317
|
+
else {
|
|
7318
|
+
updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
|
|
7319
|
+
}
|
|
7320
|
+
}
|
|
7321
|
+
}
|
|
7322
|
+
notify('Translation sequence trimmed to start and stop codons', 'success');
|
|
7323
|
+
};
|
|
7324
|
+
return (jsxRuntime.jsx("div", { children: jsxRuntime.jsxs(material.Accordion, { children: [jsxRuntime.jsx(StyledAccordionSummary$1, { expandIcon: jsxRuntime.jsx(ExpandMoreIcon, { style: { color: 'white' } }), "aria-controls": "panel1-content", id: "panel1-header", children: jsxRuntime.jsx(material.Typography, { component: "span", fontWeight: 'bold', children: "Translation" }) }), jsxRuntime.jsxs(material.AccordionDetails, { children: [jsxRuntime.jsx(SequenceContainer, { children: jsxRuntime.jsx(material.Typography, { component: 'span', ref: seqRef, style: { maxHeight: 120, overflowY: 'scroll' }, children: proteinSequence.map((protein, idx) => {
|
|
7325
|
+
const codonGenomicPos = idx * 3;
|
|
7326
|
+
if (protein === 'M') {
|
|
7327
|
+
return (jsxRuntime.jsx(material.Typography, { component: 'span', style: {
|
|
7328
|
+
backgroundColor: changeInProgress
|
|
7329
|
+
? 'lightgray'
|
|
7330
|
+
: 'yellow',
|
|
7331
|
+
cursor: 'pointer',
|
|
7332
|
+
border: '1px solid black',
|
|
7333
|
+
}, onClick: () => {
|
|
7334
|
+
if (changeInProgress) {
|
|
7335
|
+
return;
|
|
7336
|
+
}
|
|
7337
|
+
// NOTE: codonGenomicPos is important here for calculating the genomic location
|
|
7338
|
+
// of the start codon. We are using the codonGenomicPos as the key in the typography
|
|
7339
|
+
// elements to maintain the genomic postion of the codon start
|
|
7340
|
+
const startCodonGenomicLocation = getCodonGenomicLocation(codonGenomicPos);
|
|
7341
|
+
if (startCodonGenomicLocation !== cdsMin &&
|
|
7342
|
+
strand === 1) {
|
|
7343
|
+
updateCDSLocation(cdsMin, startCodonGenomicLocation, feature, true);
|
|
7344
|
+
}
|
|
7345
|
+
if (startCodonGenomicLocation !== cdsMax &&
|
|
7346
|
+
strand === -1) {
|
|
7347
|
+
updateCDSLocation(cdsMax, startCodonGenomicLocation, feature, false);
|
|
7348
|
+
}
|
|
7349
|
+
}, children: protein }, codonGenomicPos));
|
|
7350
|
+
}
|
|
7351
|
+
if (protein === '*') {
|
|
7352
|
+
return (jsxRuntime.jsx(material.Typography, { style: { backgroundColor: 'red', color: 'white' }, component: 'span', children: protein }, codonGenomicPos));
|
|
7353
|
+
}
|
|
7354
|
+
// Pass the codonGenomicPos as the key to maintain the genomic position of the codon
|
|
7355
|
+
return (jsxRuntime.jsx(material.Typography, { component: 'span', children: protein }, codonGenomicPos));
|
|
7356
|
+
}) }) }), jsxRuntime.jsxs("div", { style: {
|
|
7357
|
+
marginTop: 10,
|
|
7358
|
+
display: 'flex',
|
|
7359
|
+
flexDirection: 'row',
|
|
7360
|
+
alignItems: 'center',
|
|
7361
|
+
gap: 10,
|
|
7362
|
+
}, children: [jsxRuntime.jsx(material.Tooltip, { title: "Copy", children: jsxRuntime.jsx("button", { onClick: onCopyClick, style: { border: 'none', background: 'none', padding: 0 }, disabled: changeInProgress, children: jsxRuntime.jsx(ContentCopyIcon, { style: { fontSize: 15 } }) }) }), jsxRuntime.jsx(material.Tooltip, { title: "Trim", children: jsxRuntime.jsx("button", { onClick: trimTranslationSequence, style: { border: 'none', background: 'none', padding: 0 }, disabled: changeInProgress, children: jsxRuntime.jsx(ContentCutIcon, { style: { fontSize: 15 } }) }) })] })] })] }) }));
|
|
7363
|
+
}
|
|
7364
|
+
|
|
7365
|
+
const StyledTextField = styled(NumberTextField)(() => ({
|
|
7366
|
+
'&.MuiFormControl-root': {
|
|
7367
|
+
marginTop: 0,
|
|
7368
|
+
marginBottom: 0,
|
|
7369
|
+
width: '100%',
|
|
7370
|
+
},
|
|
7371
|
+
'& .MuiInputBase-input': {
|
|
7372
|
+
fontSize: 12,
|
|
7373
|
+
height: 20,
|
|
7374
|
+
padding: 1,
|
|
7375
|
+
paddingLeft: 10,
|
|
7376
|
+
},
|
|
7377
|
+
}));
|
|
6994
7378
|
const Strand = (props) => {
|
|
6995
7379
|
const { strand } = props;
|
|
6996
7380
|
return (jsxRuntime.jsx("div", { children: strand === 1 ? (jsxRuntime.jsx(AddIcon, {})) : strand === -1 ? (jsxRuntime.jsx(RemoveIcon, {})) : (jsxRuntime.jsx(material.Typography, { component: 'span', children: "N/A" })) }));
|
|
@@ -7009,7 +7393,6 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
|
|
|
7009
7393
|
const currentAssembly = session.apolloDataStore.assemblies.get(assembly);
|
|
7010
7394
|
const refData = currentAssembly?.getByRefName(refName);
|
|
7011
7395
|
const { changeManager } = session.apolloDataStore;
|
|
7012
|
-
const seqRef = React.useRef(null);
|
|
7013
7396
|
const { changeInProgress } = session;
|
|
7014
7397
|
if (!refData) {
|
|
7015
7398
|
return null;
|
|
@@ -7436,184 +7819,6 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
|
|
|
7436
7819
|
},
|
|
7437
7820
|
];
|
|
7438
7821
|
};
|
|
7439
|
-
const getTranslationSequence = () => {
|
|
7440
|
-
let wholeSequence = '';
|
|
7441
|
-
const [firstLocation] = cdsLocations;
|
|
7442
|
-
const sortedCDSLocations = firstLocation.toSorted(({ min: a }, { min: b }) => a - b);
|
|
7443
|
-
for (const loc of sortedCDSLocations) {
|
|
7444
|
-
wholeSequence += refData.getSequence(loc.min, loc.max);
|
|
7445
|
-
}
|
|
7446
|
-
if (strand === -1) {
|
|
7447
|
-
// Original: ACGCAT
|
|
7448
|
-
// Complement: TGCGTA
|
|
7449
|
-
// Reverse complement: ATGCGT
|
|
7450
|
-
wholeSequence = util.revcom(wholeSequence);
|
|
7451
|
-
}
|
|
7452
|
-
const elements = [];
|
|
7453
|
-
for (let codonGenomicPos = 0; codonGenomicPos < wholeSequence.length; codonGenomicPos += 3) {
|
|
7454
|
-
const codonSeq = wholeSequence
|
|
7455
|
-
.slice(codonGenomicPos, codonGenomicPos + 3)
|
|
7456
|
-
.toUpperCase();
|
|
7457
|
-
const protein = util.defaultCodonTable[codonSeq] || '&';
|
|
7458
|
-
// highlight start codon and stop codons
|
|
7459
|
-
if (codonSeq === 'ATG') {
|
|
7460
|
-
elements.push(jsxRuntime.jsx(material.Typography, { component: 'span', style: {
|
|
7461
|
-
backgroundColor: changeInProgress ? 'lightgray' : 'yellow',
|
|
7462
|
-
cursor: 'pointer',
|
|
7463
|
-
border: '1px solid black',
|
|
7464
|
-
}, onClick: () => {
|
|
7465
|
-
if (changeInProgress) {
|
|
7466
|
-
return;
|
|
7467
|
-
}
|
|
7468
|
-
// NOTE: codonGenomicPos is important here for calculating the genomic location
|
|
7469
|
-
// of the start codon. We are using the codonGenomicPos as the key in the typography
|
|
7470
|
-
// elements to maintain the genomic postion of the codon start
|
|
7471
|
-
const startCodonGenomicLocation = getCodonGenomicLocation(codonGenomicPos);
|
|
7472
|
-
if (startCodonGenomicLocation !== cdsMin && strand === 1) {
|
|
7473
|
-
updateCDSLocation(cdsMin, startCodonGenomicLocation, feature, true);
|
|
7474
|
-
}
|
|
7475
|
-
if (startCodonGenomicLocation !== cdsMax && strand === -1) {
|
|
7476
|
-
updateCDSLocation(cdsMax, startCodonGenomicLocation, feature, false);
|
|
7477
|
-
}
|
|
7478
|
-
}, children: protein }, codonGenomicPos));
|
|
7479
|
-
}
|
|
7480
|
-
else if (['TAA', 'TAG', 'TGA'].includes(codonSeq)) {
|
|
7481
|
-
elements.push(jsxRuntime.jsx(material.Typography, { style: { backgroundColor: 'red', color: 'white' }, component: 'span', children: protein }, codonGenomicPos));
|
|
7482
|
-
}
|
|
7483
|
-
else {
|
|
7484
|
-
elements.push(
|
|
7485
|
-
// Pass the codonGenomicPos as the key to maintain the genomic position of the codon
|
|
7486
|
-
jsxRuntime.jsx(material.Typography, { component: 'span', children: protein }, codonGenomicPos));
|
|
7487
|
-
}
|
|
7488
|
-
}
|
|
7489
|
-
return elements;
|
|
7490
|
-
};
|
|
7491
|
-
// Codon position is the index of the start codon in the CDS genomic sequence
|
|
7492
|
-
// Calculate the genomic location of the start codon based on the codon position in the CDS
|
|
7493
|
-
const getCodonGenomicLocation = (codonGenomicPosition) => {
|
|
7494
|
-
const [firstLocation] = cdsLocations;
|
|
7495
|
-
let cdsLen = 0;
|
|
7496
|
-
const sortedCDSLocations = firstLocation.toSorted(({ min: a }, { min: b }) => a - b);
|
|
7497
|
-
// Suppose CDS locations are [{min: 0, max: 10}, {min: 20, max: 30}, {min: 40, max: 50}]
|
|
7498
|
-
// and codonGenomicPosition is 25
|
|
7499
|
-
// ((10 - 0) + (30 - 20) + (50 - 40)) > 25
|
|
7500
|
-
// So, start codon is in (40, 50)
|
|
7501
|
-
// 40 + (25-20) = 45 is the genomic location of the start codon
|
|
7502
|
-
if (strand === 1) {
|
|
7503
|
-
for (const loc of sortedCDSLocations) {
|
|
7504
|
-
const locLength = loc.max - loc.min;
|
|
7505
|
-
if (cdsLen + locLength > codonGenomicPosition) {
|
|
7506
|
-
return loc.min + (codonGenomicPosition - cdsLen);
|
|
7507
|
-
}
|
|
7508
|
-
cdsLen += locLength;
|
|
7509
|
-
}
|
|
7510
|
-
}
|
|
7511
|
-
else if (strand === -1) {
|
|
7512
|
-
for (let i = sortedCDSLocations.length - 1; i >= 0; i--) {
|
|
7513
|
-
const loc = sortedCDSLocations[i];
|
|
7514
|
-
const locLength = loc.max - loc.min;
|
|
7515
|
-
if (cdsLen + locLength > codonGenomicPosition) {
|
|
7516
|
-
return loc.max - (codonGenomicPosition - cdsLen);
|
|
7517
|
-
}
|
|
7518
|
-
cdsLen += locLength;
|
|
7519
|
-
}
|
|
7520
|
-
}
|
|
7521
|
-
if (strand === 1) {
|
|
7522
|
-
return cdsMin;
|
|
7523
|
-
}
|
|
7524
|
-
return cdsMax;
|
|
7525
|
-
};
|
|
7526
|
-
const trimTranslationSequence = () => {
|
|
7527
|
-
const sequenceElements = getTranslationSequence();
|
|
7528
|
-
const translationSequence = sequenceElements
|
|
7529
|
-
.map((el) => el.props.children)
|
|
7530
|
-
.join('');
|
|
7531
|
-
if (translationSequence.startsWith('M') &&
|
|
7532
|
-
translationSequence.endsWith('*')) {
|
|
7533
|
-
return;
|
|
7534
|
-
}
|
|
7535
|
-
// NOTE: We are maintaining the genomic location of the codon start as the "key"
|
|
7536
|
-
// in typography elements. See getTranslationSequence function
|
|
7537
|
-
const translSeqCodonStartGenomicPosArr = [];
|
|
7538
|
-
for (const el of sequenceElements) {
|
|
7539
|
-
translSeqCodonStartGenomicPosArr.push({
|
|
7540
|
-
codonGenomicPos: el.key,
|
|
7541
|
-
sequenceLetter: el.props.children,
|
|
7542
|
-
});
|
|
7543
|
-
}
|
|
7544
|
-
if (translSeqCodonStartGenomicPosArr.length === 0) {
|
|
7545
|
-
return;
|
|
7546
|
-
}
|
|
7547
|
-
// Trim any sequence before first start codon and after stop codon
|
|
7548
|
-
const startCodonIndex = translationSequence.indexOf('M');
|
|
7549
|
-
const stopCodonIndex = translationSequence.indexOf('*');
|
|
7550
|
-
const startCodonPos = translSeqCodonStartGenomicPosArr[startCodonIndex].codonGenomicPos;
|
|
7551
|
-
const stopCodonPos = translSeqCodonStartGenomicPosArr[stopCodonIndex].codonGenomicPos;
|
|
7552
|
-
if (!startCodonPos || !stopCodonPos) {
|
|
7553
|
-
return;
|
|
7554
|
-
}
|
|
7555
|
-
const startCodonGenomicLoc = getCodonGenomicLocation(startCodonPos);
|
|
7556
|
-
let stopCodonGenomicLoc = getCodonGenomicLocation(stopCodonPos);
|
|
7557
|
-
if (strand === 1) {
|
|
7558
|
-
if (startCodonGenomicLoc > stopCodonGenomicLoc) {
|
|
7559
|
-
notify('Start codon genomic location should be less than stop codon genomic location', 'error');
|
|
7560
|
-
return;
|
|
7561
|
-
}
|
|
7562
|
-
let promise;
|
|
7563
|
-
stopCodonGenomicLoc += 3; // move to end of stop codon
|
|
7564
|
-
if (startCodonGenomicLoc !== cdsMin) {
|
|
7565
|
-
promise = new Promise((resolve) => {
|
|
7566
|
-
updateCDSLocation(cdsMin, startCodonGenomicLoc, feature, true, () => {
|
|
7567
|
-
resolve(true);
|
|
7568
|
-
});
|
|
7569
|
-
});
|
|
7570
|
-
}
|
|
7571
|
-
if (stopCodonGenomicLoc !== cdsMax) {
|
|
7572
|
-
if (promise) {
|
|
7573
|
-
void promise.then(() => {
|
|
7574
|
-
updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
|
|
7575
|
-
});
|
|
7576
|
-
}
|
|
7577
|
-
else {
|
|
7578
|
-
updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
|
|
7579
|
-
}
|
|
7580
|
-
}
|
|
7581
|
-
}
|
|
7582
|
-
if (strand === -1) {
|
|
7583
|
-
// reverse strand
|
|
7584
|
-
if (startCodonGenomicLoc < stopCodonGenomicLoc) {
|
|
7585
|
-
notify('Start codon genomic location should be less than stop codon genomic location', 'error');
|
|
7586
|
-
return;
|
|
7587
|
-
}
|
|
7588
|
-
let promise;
|
|
7589
|
-
stopCodonGenomicLoc -= 3; // move to end of stop codon
|
|
7590
|
-
if (startCodonGenomicLoc !== cdsMax) {
|
|
7591
|
-
promise = new Promise((resolve) => {
|
|
7592
|
-
updateCDSLocation(cdsMax, startCodonGenomicLoc, feature, false, () => {
|
|
7593
|
-
resolve(true);
|
|
7594
|
-
});
|
|
7595
|
-
});
|
|
7596
|
-
}
|
|
7597
|
-
if (stopCodonGenomicLoc !== cdsMin) {
|
|
7598
|
-
if (promise) {
|
|
7599
|
-
void promise.then(() => {
|
|
7600
|
-
updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
|
|
7601
|
-
});
|
|
7602
|
-
}
|
|
7603
|
-
else {
|
|
7604
|
-
updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
|
|
7605
|
-
}
|
|
7606
|
-
}
|
|
7607
|
-
}
|
|
7608
|
-
notify('Translation sequence trimmed to start and stop codons', 'success');
|
|
7609
|
-
};
|
|
7610
|
-
const onCopyClick = () => {
|
|
7611
|
-
const seqDiv = seqRef.current;
|
|
7612
|
-
if (!seqDiv) {
|
|
7613
|
-
return;
|
|
7614
|
-
}
|
|
7615
|
-
void copyToClipboard(seqDiv);
|
|
7616
|
-
};
|
|
7617
7822
|
return (jsxRuntime.jsxs("div", { children: [cdsPresent && (jsxRuntime.jsx("div", { children: jsxRuntime.jsxs(material.Grid, { container: true, justifyContent: "center", alignItems: "center", style: { textAlign: 'center' }, children: [jsxRuntime.jsx(material.Grid, { size: 1 }), strand === 1 ? (jsxRuntime.jsx(material.Grid, { size: 4, children: jsxRuntime.jsx(StyledTextField, { margin: "dense", variant: "outlined", value: cdsMin + 1, onChangeCommitted: (newLocation) => {
|
|
7618
7823
|
return updateCDSLocation(cdsMin, newLocation - 1, feature, true);
|
|
7619
7824
|
}, style: { border: '1px solid black', borderRadius: 5 }, disabled: changeInProgress }) })) : (jsxRuntime.jsx(material.Grid, { size: 4, children: jsxRuntime.jsx(StyledTextField, { margin: "dense", variant: "outlined", value: cdsMax, onChangeCommitted: (newLocation) => {
|
|
@@ -7623,8 +7828,12 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
|
|
|
7623
7828
|
}, style: { border: '1px solid black', borderRadius: 5 }, disabled: changeInProgress }) })) : (jsxRuntime.jsx(material.Grid, { size: 4, children: jsxRuntime.jsx(StyledTextField, { margin: "dense", variant: "outlined", value: cdsMin + 1, onChangeCommitted: (newLocation) => {
|
|
7624
7829
|
return updateCDSLocation(cdsMin, newLocation - 1, feature, true);
|
|
7625
7830
|
}, style: { border: '1px solid black', borderRadius: 5 }, disabled: changeInProgress }) })), jsxRuntime.jsx(material.Grid, { size: 1 })] }) })), jsxRuntime.jsxs("div", { style: { marginTop: 5, marginBottom: 10 }, children: [jsxRuntime.jsx("div", { style: { textAlign: 'center' }, children: jsxRuntime.jsx(material.Typography, { children: "Exons" }) }), transcriptExonParts.map((loc, index) => {
|
|
7626
|
-
return (
|
|
7627
|
-
|
|
7831
|
+
return (
|
|
7832
|
+
// eslint-disable-next-line @eslint-react/no-array-index-key
|
|
7833
|
+
jsxRuntime.jsx("div", { children: loc.type === 'exon' && (jsxRuntime.jsxs(material.Grid, { container: true, justifyContent: "center", alignItems: "center", style: { textAlign: 'center' }, children: [jsxRuntime.jsx(material.Grid, { size: 1, children: index !== 0 &&
|
|
7834
|
+
getFivePrimeSpliceSite(loc, index).map((site, idx) => (jsxRuntime.jsx(material.Typography
|
|
7835
|
+
// eslint-disable-next-line @eslint-react/no-array-index-key
|
|
7836
|
+
, { component: 'span', color: site.color, children: site.spliceSite }, idx))) }), strand === 1 ? (jsxRuntime.jsx(material.Grid, { size: 4, style: { padding: 0 }, children: jsxRuntime.jsx(StyledTextField, { margin: "dense", variant: "outlined", value: loc.min + 1, onChangeCommitted: (newLocation) => {
|
|
7628
7837
|
return handleExonLocationChange(loc.min, newLocation - 1, feature, true);
|
|
7629
7838
|
}, disabled: changeInProgress }) })) : (jsxRuntime.jsx(material.Grid, { size: 4, style: { padding: 0 }, children: jsxRuntime.jsx(StyledTextField, { margin: "dense", variant: "outlined", value: loc.max, onChangeCommitted: (newLocation) => {
|
|
7630
7839
|
return handleExonLocationChange(loc.max, newLocation, feature, false);
|
|
@@ -7633,14 +7842,10 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
|
|
|
7633
7842
|
}, disabled: changeInProgress }) })) : (jsxRuntime.jsx(material.Grid, { size: 4, style: { padding: 0 }, children: jsxRuntime.jsx(StyledTextField, { margin: "dense", variant: "outlined", value: loc.min + 1, onChangeCommitted: (newLocation) => {
|
|
7634
7843
|
return handleExonLocationChange(loc.min, newLocation - 1, feature, true);
|
|
7635
7844
|
}, disabled: changeInProgress }) })), jsxRuntime.jsx(material.Grid, { size: 1, children: index !== transcriptExonParts.length - 1 &&
|
|
7636
|
-
getThreePrimeSpliceSite(loc, index).map((site, idx) => (jsxRuntime.jsx(material.Typography
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
flexDirection: 'row',
|
|
7641
|
-
alignItems: 'center',
|
|
7642
|
-
gap: 10,
|
|
7643
|
-
}, children: [jsxRuntime.jsx(material.Tooltip, { title: "Copy", children: jsxRuntime.jsx("button", { onClick: onCopyClick, style: { border: 'none', background: 'none', padding: 0 }, disabled: changeInProgress, children: jsxRuntime.jsx(ContentCopyIcon, { style: { fontSize: 15 } }) }) }), jsxRuntime.jsx(material.Tooltip, { title: "Trim", children: jsxRuntime.jsx("button", { onClick: trimTranslationSequence, style: { border: 'none', background: 'none', padding: 0 }, disabled: changeInProgress, children: jsxRuntime.jsx(ContentCutIcon, { style: { fontSize: 15 } }) }) })] })] })] }) }))] }));
|
|
7845
|
+
getThreePrimeSpliceSite(loc, index).map((site, idx) => (jsxRuntime.jsx(material.Typography
|
|
7846
|
+
// eslint-disable-next-line @eslint-react/no-array-index-key
|
|
7847
|
+
, { component: 'span', color: site.color, children: site.spliceSite }, idx))) })] })) }, index));
|
|
7848
|
+
})] }), cdsPresent && (jsxRuntime.jsx(Translation, { changeInProgress: changeInProgress, cdsLocations: cdsLocations, refData: refData, strand: strand, updateCDSLocation: updateCDSLocation, cdsMin: cdsMin, cdsMax: cdsMax, feature: feature, session: session }))] }));
|
|
7644
7849
|
});
|
|
7645
7850
|
|
|
7646
7851
|
const HeaderTableCell = styled(material.TableCell)(() => ({
|
|
@@ -7677,7 +7882,7 @@ const ApolloTranscriptDetailsWidget = mobxReact.observer(function ApolloTranscri
|
|
|
7677
7882
|
const { assembly, feature, refName } = model;
|
|
7678
7883
|
React.useEffect(() => {
|
|
7679
7884
|
setPanelState(DEFAULT_PANELS);
|
|
7680
|
-
// eslint-disable-next-line react
|
|
7885
|
+
// eslint-disable-next-line @eslint-react/exhaustive-deps
|
|
7681
7886
|
}, [feature]);
|
|
7682
7887
|
const session = util.getSession(model);
|
|
7683
7888
|
const { pluginManager } = util.getEnv(session);
|
|
@@ -7736,8 +7941,8 @@ function handleFeatureTypeChange(changeManager, feature, oldType, newType) {
|
|
|
7736
7941
|
typeName: 'TypeChange',
|
|
7737
7942
|
changedIds: [featureId],
|
|
7738
7943
|
featureId,
|
|
7739
|
-
oldType
|
|
7740
|
-
newType
|
|
7944
|
+
oldType,
|
|
7945
|
+
newType,
|
|
7741
7946
|
assembly: feature.assemblyId,
|
|
7742
7947
|
});
|
|
7743
7948
|
return changeManager.submit(change);
|
|
@@ -7833,7 +8038,7 @@ const NumberCell = mobxReact.observer(function NumberCell({ initialValue, notify
|
|
|
7833
8038
|
if (initialValue !== value) {
|
|
7834
8039
|
setValue(initialValue);
|
|
7835
8040
|
}
|
|
7836
|
-
// eslint-disable-next-line react
|
|
8041
|
+
// eslint-disable-next-line @eslint-react/exhaustive-deps
|
|
7837
8042
|
}, [initialValue]);
|
|
7838
8043
|
React.useEffect(() => {
|
|
7839
8044
|
if (blur) {
|
|
@@ -8674,8 +8879,9 @@ function draw$3(display, ctx, gene, row, rowInFeature, block) {
|
|
|
8674
8879
|
const [top, left, width] = getFeatureBox(display, gene, row, block);
|
|
8675
8880
|
const height = getRowCount$2(display, gene) * apolloRowHeight;
|
|
8676
8881
|
if (width > 2) {
|
|
8677
|
-
|
|
8678
|
-
|
|
8882
|
+
const selectedColor = configuration.readConfObject(session.getPluginConfiguration(), 'geneBackgroundColor', {
|
|
8883
|
+
featureType: gene.type,
|
|
8884
|
+
}) ?? material.alpha(theme.palette.background.paper, 0.6);
|
|
8679
8885
|
ctx.fillStyle = selectedColor;
|
|
8680
8886
|
ctx.fillRect(left, top, width, height);
|
|
8681
8887
|
}
|
|
@@ -8948,7 +9154,7 @@ function getLayout(display, feature) {
|
|
|
8948
9154
|
}
|
|
8949
9155
|
return layout;
|
|
8950
9156
|
}
|
|
8951
|
-
function getContextMenuItems$1(display,
|
|
9157
|
+
function getContextMenuItems$1(display, transcript) {
|
|
8952
9158
|
const { changeManager, regions, selectedFeature, session } = display;
|
|
8953
9159
|
const [region] = regions;
|
|
8954
9160
|
const currentAssemblyId = display.getAssemblyId(region.assemblyName);
|
|
@@ -8958,7 +9164,7 @@ function getContextMenuItems$1(display, feature) {
|
|
|
8958
9164
|
label: 'Open transcript editor',
|
|
8959
9165
|
onClick: () => {
|
|
8960
9166
|
const apolloTranscriptWidget = session.addWidget('ApolloTranscriptDetails', 'apolloTranscriptDetails', {
|
|
8961
|
-
feature,
|
|
9167
|
+
feature: transcript,
|
|
8962
9168
|
assembly: currentAssemblyId,
|
|
8963
9169
|
changeManager,
|
|
8964
9170
|
refName: region.refName,
|
|
@@ -8978,7 +9184,7 @@ function getContextMenuItems$1(display, feature) {
|
|
|
8978
9184
|
doneCallback();
|
|
8979
9185
|
},
|
|
8980
9186
|
changeManager,
|
|
8981
|
-
sourceFeature:
|
|
9187
|
+
sourceFeature: transcript,
|
|
8982
9188
|
sourceAssemblyId: currentAssemblyId,
|
|
8983
9189
|
selectedFeature,
|
|
8984
9190
|
setSelectedFeature: (feature) => {
|
|
@@ -8998,7 +9204,7 @@ function getContextMenuItems$1(display, feature) {
|
|
|
8998
9204
|
doneCallback();
|
|
8999
9205
|
},
|
|
9000
9206
|
changeManager,
|
|
9001
|
-
sourceFeature:
|
|
9207
|
+
sourceFeature: transcript,
|
|
9002
9208
|
sourceAssemblyId: currentAssemblyId,
|
|
9003
9209
|
selectedFeature,
|
|
9004
9210
|
setSelectedFeature: (feature) => {
|
|
@@ -9008,6 +9214,31 @@ function getContextMenuItems$1(display, feature) {
|
|
|
9008
9214
|
]);
|
|
9009
9215
|
},
|
|
9010
9216
|
});
|
|
9217
|
+
const { children } = transcript;
|
|
9218
|
+
if (!children) {
|
|
9219
|
+
return [];
|
|
9220
|
+
}
|
|
9221
|
+
const cdsChildren = [...children.values()].filter((child) => isCDSFeature(child, session));
|
|
9222
|
+
if (cdsChildren.length === 0) {
|
|
9223
|
+
menuItems.push({
|
|
9224
|
+
label: 'Add coding sequence',
|
|
9225
|
+
onClick: () => {
|
|
9226
|
+
session.queueDialog((doneCallback) => [
|
|
9227
|
+
AddCodingSequence,
|
|
9228
|
+
{
|
|
9229
|
+
session,
|
|
9230
|
+
handleClose: () => {
|
|
9231
|
+
doneCallback();
|
|
9232
|
+
},
|
|
9233
|
+
changeManager,
|
|
9234
|
+
sourceFeature: transcript,
|
|
9235
|
+
sourceAssemblyId: currentAssemblyId,
|
|
9236
|
+
refName: region.refName,
|
|
9237
|
+
},
|
|
9238
|
+
]);
|
|
9239
|
+
},
|
|
9240
|
+
});
|
|
9241
|
+
}
|
|
9011
9242
|
return menuItems;
|
|
9012
9243
|
}
|
|
9013
9244
|
// False positive here, none of these functions use "this"
|
|
@@ -9185,7 +9416,7 @@ function colorCode(letter, theme) {
|
|
|
9185
9416
|
letterUpper === 'C' ||
|
|
9186
9417
|
letterUpper === 'G' ||
|
|
9187
9418
|
letterUpper === 'T') {
|
|
9188
|
-
return theme.palette.bases[letterUpper].main
|
|
9419
|
+
return theme.palette.bases[letterUpper].main;
|
|
9189
9420
|
}
|
|
9190
9421
|
return 'lightgray';
|
|
9191
9422
|
}
|
|
@@ -9282,8 +9513,7 @@ function baseModelFactory$2(_pluginManager, configSchema) {
|
|
|
9282
9513
|
return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
|
|
9283
9514
|
},
|
|
9284
9515
|
get changeManager() {
|
|
9285
|
-
return self.session.apolloDataStore
|
|
9286
|
-
.changeManager;
|
|
9516
|
+
return self.session.apolloDataStore.changeManager;
|
|
9287
9517
|
},
|
|
9288
9518
|
getAssemblyId(assemblyName) {
|
|
9289
9519
|
const { assemblyManager } = self.session;
|
|
@@ -9294,12 +9524,10 @@ function baseModelFactory$2(_pluginManager, configSchema) {
|
|
|
9294
9524
|
return assembly.name;
|
|
9295
9525
|
},
|
|
9296
9526
|
get selectedFeature() {
|
|
9297
|
-
return self.session
|
|
9298
|
-
.apolloSelectedFeature;
|
|
9527
|
+
return self.session.apolloSelectedFeature;
|
|
9299
9528
|
},
|
|
9300
9529
|
get hoveredFeature() {
|
|
9301
|
-
return self.session
|
|
9302
|
-
.apolloHoveredFeature;
|
|
9530
|
+
return self.session.apolloHoveredFeature;
|
|
9303
9531
|
},
|
|
9304
9532
|
}))
|
|
9305
9533
|
.actions((self) => ({
|
|
@@ -9344,8 +9572,11 @@ function baseModelFactory$2(_pluginManager, configSchema) {
|
|
|
9344
9572
|
const { filteredFeatureTypes, trackMenuItems: superTrackMenuItems } = self;
|
|
9345
9573
|
return {
|
|
9346
9574
|
trackMenuItems() {
|
|
9347
|
-
const { graphical, table, showCheckResults } = self;
|
|
9348
|
-
|
|
9575
|
+
const { graphical, table, showCheckResults, session } = self;
|
|
9576
|
+
const [region] = self.regions;
|
|
9577
|
+
const { assemblyName } = region;
|
|
9578
|
+
const backendDriver = session.apolloDataStore.getBackendDriver(assemblyName);
|
|
9579
|
+
const items = [
|
|
9349
9580
|
...superTrackMenuItems(),
|
|
9350
9581
|
{
|
|
9351
9582
|
type: 'subMenu',
|
|
@@ -9397,7 +9628,7 @@ function baseModelFactory$2(_pluginManager, configSchema) {
|
|
|
9397
9628
|
{
|
|
9398
9629
|
label: 'Filter features by type',
|
|
9399
9630
|
onClick: () => {
|
|
9400
|
-
const session = self
|
|
9631
|
+
const { session } = self;
|
|
9401
9632
|
self.session.queueDialog((doneCallback) => [
|
|
9402
9633
|
FilterFeatures,
|
|
9403
9634
|
{
|
|
@@ -9413,76 +9644,93 @@ function baseModelFactory$2(_pluginManager, configSchema) {
|
|
|
9413
9644
|
]);
|
|
9414
9645
|
},
|
|
9415
9646
|
},
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9647
|
+
];
|
|
9648
|
+
if (backendDriver instanceof LocalDriver) {
|
|
9649
|
+
items.push({
|
|
9650
|
+
label: 'Import annotations',
|
|
9651
|
+
icon: InputIcon,
|
|
9419
9652
|
onClick: () => {
|
|
9420
|
-
const [region] = self.regions;
|
|
9421
|
-
const { assemblyName } = region;
|
|
9422
|
-
const assembly = self.getAssemblyId(assemblyName);
|
|
9423
|
-
if (!assembly) {
|
|
9424
|
-
return;
|
|
9425
|
-
}
|
|
9426
|
-
const session = self.session;
|
|
9427
9653
|
session.queueDialog((doneCallback) => [
|
|
9428
|
-
|
|
9654
|
+
ImportAnnotations,
|
|
9429
9655
|
{
|
|
9430
9656
|
session,
|
|
9657
|
+
assemblyName,
|
|
9431
9658
|
handleClose: () => {
|
|
9432
9659
|
doneCallback();
|
|
9433
9660
|
},
|
|
9434
|
-
assembly,
|
|
9435
9661
|
},
|
|
9436
9662
|
]);
|
|
9437
9663
|
},
|
|
9664
|
+
});
|
|
9665
|
+
}
|
|
9666
|
+
items.push({
|
|
9667
|
+
label: 'Export annotations',
|
|
9668
|
+
icon: Export,
|
|
9669
|
+
onClick: () => {
|
|
9670
|
+
const [region] = self.regions;
|
|
9671
|
+
const { assemblyName } = region;
|
|
9672
|
+
const assembly = self.getAssemblyId(assemblyName);
|
|
9673
|
+
if (!assembly) {
|
|
9674
|
+
return;
|
|
9675
|
+
}
|
|
9676
|
+
const { session } = self;
|
|
9677
|
+
session.queueDialog((doneCallback) => [
|
|
9678
|
+
DownloadGFF3,
|
|
9679
|
+
{
|
|
9680
|
+
session,
|
|
9681
|
+
handleClose: () => {
|
|
9682
|
+
doneCallback();
|
|
9683
|
+
},
|
|
9684
|
+
assembly,
|
|
9685
|
+
},
|
|
9686
|
+
]);
|
|
9438
9687
|
},
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9456
|
-
},
|
|
9457
|
-
assembly,
|
|
9688
|
+
}, {
|
|
9689
|
+
label: 'View Change Log',
|
|
9690
|
+
icon: TrackChangesIcon,
|
|
9691
|
+
onClick: () => {
|
|
9692
|
+
const [region] = self.regions;
|
|
9693
|
+
const { assemblyName } = region;
|
|
9694
|
+
const assembly = self.getAssemblyId(assemblyName);
|
|
9695
|
+
if (!assembly) {
|
|
9696
|
+
return;
|
|
9697
|
+
}
|
|
9698
|
+
const { session } = self;
|
|
9699
|
+
session.queueDialog((doneCallback) => [
|
|
9700
|
+
ViewChangeLog,
|
|
9701
|
+
{
|
|
9702
|
+
session,
|
|
9703
|
+
handleClose: () => {
|
|
9704
|
+
doneCallback();
|
|
9458
9705
|
},
|
|
9459
|
-
|
|
9460
|
-
|
|
9706
|
+
assembly,
|
|
9707
|
+
},
|
|
9708
|
+
]);
|
|
9461
9709
|
},
|
|
9462
|
-
|
|
9463
|
-
|
|
9464
|
-
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
|
|
9468
|
-
|
|
9469
|
-
|
|
9470
|
-
|
|
9471
|
-
|
|
9472
|
-
|
|
9473
|
-
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
|
|
9478
|
-
|
|
9479
|
-
},
|
|
9480
|
-
assembly,
|
|
9710
|
+
}, {
|
|
9711
|
+
label: 'View Check Results',
|
|
9712
|
+
icon: FactCheckIcon,
|
|
9713
|
+
onClick: () => {
|
|
9714
|
+
const [region] = self.regions;
|
|
9715
|
+
const { assemblyName } = region;
|
|
9716
|
+
const assembly = self.getAssemblyId(assemblyName);
|
|
9717
|
+
if (!assembly) {
|
|
9718
|
+
return;
|
|
9719
|
+
}
|
|
9720
|
+
const { session } = self;
|
|
9721
|
+
session.queueDialog((doneCallback) => [
|
|
9722
|
+
ViewCheckResults,
|
|
9723
|
+
{
|
|
9724
|
+
session,
|
|
9725
|
+
handleClose: () => {
|
|
9726
|
+
doneCallback();
|
|
9481
9727
|
},
|
|
9482
|
-
|
|
9483
|
-
|
|
9728
|
+
assembly,
|
|
9729
|
+
},
|
|
9730
|
+
]);
|
|
9484
9731
|
},
|
|
9485
|
-
|
|
9732
|
+
});
|
|
9733
|
+
return items;
|
|
9486
9734
|
},
|
|
9487
9735
|
};
|
|
9488
9736
|
})
|
|
@@ -10548,8 +10796,7 @@ function baseModelFactory$1(_pluginManager, configSchema) {
|
|
|
10548
10796
|
return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
|
|
10549
10797
|
},
|
|
10550
10798
|
get changeManager() {
|
|
10551
|
-
return self.session.apolloDataStore
|
|
10552
|
-
.changeManager;
|
|
10799
|
+
return self.session.apolloDataStore.changeManager;
|
|
10553
10800
|
},
|
|
10554
10801
|
getAssemblyId(assemblyName) {
|
|
10555
10802
|
const { assemblyManager } = self.session;
|
|
@@ -10560,12 +10807,10 @@ function baseModelFactory$1(_pluginManager, configSchema) {
|
|
|
10560
10807
|
return assembly.name;
|
|
10561
10808
|
},
|
|
10562
10809
|
get selectedFeature() {
|
|
10563
|
-
return self.session
|
|
10564
|
-
.apolloSelectedFeature;
|
|
10810
|
+
return self.session.apolloSelectedFeature;
|
|
10565
10811
|
},
|
|
10566
10812
|
get hoveredFeature() {
|
|
10567
|
-
return self.session
|
|
10568
|
-
.apolloHoveredFeature;
|
|
10813
|
+
return self.session.apolloHoveredFeature;
|
|
10569
10814
|
},
|
|
10570
10815
|
get height() {
|
|
10571
10816
|
const { sequenceRowHeight } = self;
|
|
@@ -10777,11 +11022,9 @@ const FilterTranscripts = mobxReact.observer(function FilterTranscripts({ source
|
|
|
10777
11022
|
onUpdate(newForms);
|
|
10778
11023
|
setExcludedTranscripts(newForms);
|
|
10779
11024
|
};
|
|
10780
|
-
return (jsxRuntime.jsx(Dialog, { open: true, maxWidth: false, "data-testid": "filter-transcripts-dialog", title: "Filter transcripts by ID", handleClose: handleClose, children: jsxRuntime.jsxs(material.DialogContent, { children: [jsxRuntime.jsx(material.DialogContentText, { children: "Select the alternate transcripts you want to display in the apollo track" }), jsxRuntime.jsx(material.Grid, { container: true, spacing: 2, children: jsxRuntime.jsx(material.Grid, { size: 8, children: jsxRuntime.jsx(material.FormGroup, { children: allTranscripts.map((item) => (
|
|
10781
|
-
// eslint-disable-next-line react/jsx-key
|
|
10782
|
-
jsxRuntime.jsx(material.FormControlLabel, { control: jsxRuntime.jsx(material.Checkbox, { checked: !excludedTranscripts.includes(item), onChange: () => {
|
|
11025
|
+
return (jsxRuntime.jsx(Dialog, { open: true, maxWidth: false, "data-testid": "filter-transcripts-dialog", title: "Filter transcripts by ID", handleClose: handleClose, children: jsxRuntime.jsxs(material.DialogContent, { children: [jsxRuntime.jsx(material.DialogContentText, { children: "Select the alternate transcripts you want to display in the apollo track" }), jsxRuntime.jsx(material.Grid, { container: true, spacing: 2, children: jsxRuntime.jsx(material.Grid, { size: 8, children: jsxRuntime.jsx(material.FormGroup, { children: allTranscripts.map((item) => (jsxRuntime.jsx(material.FormControlLabel, { control: jsxRuntime.jsx(material.Checkbox, { checked: !excludedTranscripts.includes(item), onChange: () => {
|
|
10783
11026
|
handleChange(item);
|
|
10784
|
-
}, slotProps: { input: { 'aria-label': 'controlled' } } }), label: item }))) }) }) })] }) }));
|
|
11027
|
+
}, slotProps: { input: { 'aria-label': 'controlled' } } }), label: item }, item))) }) }) })] }) }));
|
|
10785
11028
|
});
|
|
10786
11029
|
|
|
10787
11030
|
let forwardFillLight = null;
|
|
@@ -11627,8 +11870,7 @@ function baseModelFactory(_pluginManager, configSchema) {
|
|
|
11627
11870
|
return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
|
|
11628
11871
|
},
|
|
11629
11872
|
get changeManager() {
|
|
11630
|
-
return self.session.apolloDataStore
|
|
11631
|
-
.changeManager;
|
|
11873
|
+
return self.session.apolloDataStore.changeManager;
|
|
11632
11874
|
},
|
|
11633
11875
|
getAssemblyId(assemblyName) {
|
|
11634
11876
|
const { assemblyManager } = self.session;
|
|
@@ -11639,12 +11881,10 @@ function baseModelFactory(_pluginManager, configSchema) {
|
|
|
11639
11881
|
return assembly.name;
|
|
11640
11882
|
},
|
|
11641
11883
|
get selectedFeature() {
|
|
11642
|
-
return self.session
|
|
11643
|
-
.apolloSelectedFeature;
|
|
11884
|
+
return self.session.apolloSelectedFeature;
|
|
11644
11885
|
},
|
|
11645
11886
|
get hoveredFeature() {
|
|
11646
|
-
return self.session
|
|
11647
|
-
.apolloHoveredFeature;
|
|
11887
|
+
return self.session.apolloHoveredFeature;
|
|
11648
11888
|
},
|
|
11649
11889
|
}))
|
|
11650
11890
|
.actions((self) => ({
|
|
@@ -11772,7 +12012,7 @@ function baseModelFactory(_pluginManager, configSchema) {
|
|
|
11772
12012
|
{
|
|
11773
12013
|
label: 'Filter features by type',
|
|
11774
12014
|
onClick: () => {
|
|
11775
|
-
const session = self
|
|
12015
|
+
const { session } = self;
|
|
11776
12016
|
self.session.queueDialog((doneCallback) => [
|
|
11777
12017
|
FilterFeatures,
|
|
11778
12018
|
{
|
|
@@ -12210,17 +12450,12 @@ function mouseEventsModelIntermediateFactory(pluginManager, configSchema) {
|
|
|
12210
12450
|
if (!featureTypeOntology) {
|
|
12211
12451
|
throw new Error('featureTypeOntology is undefined');
|
|
12212
12452
|
}
|
|
12213
|
-
|
|
12214
|
-
|
|
12215
|
-
foundFeature = layoutRow.find((f) => f.feature.type == 'exon' &&
|
|
12453
|
+
const foundFeature = self.geneTrackRowNums.includes(row)
|
|
12454
|
+
? layoutRow.find((f) => f.feature.type == 'exon' &&
|
|
12216
12455
|
bp >= f.feature.min &&
|
|
12217
|
-
bp <= f.feature.max)
|
|
12218
|
-
|
|
12219
|
-
|
|
12220
|
-
}
|
|
12221
|
-
}
|
|
12222
|
-
else {
|
|
12223
|
-
foundFeature = layoutRow.find((f) => {
|
|
12456
|
+
bp <= f.feature.max) ??
|
|
12457
|
+
layoutRow.find((f) => bp >= f.feature.min && bp <= f.feature.max)
|
|
12458
|
+
: layoutRow.find((f) => {
|
|
12224
12459
|
const { feature } = f;
|
|
12225
12460
|
const featureID = feature.attributes.get('gff_id')?.toString();
|
|
12226
12461
|
const isTranscript = featureTypeOntology.isTypeOf(feature.type, 'transcript');
|
|
@@ -12242,7 +12477,6 @@ function mouseEventsModelIntermediateFactory(pluginManager, configSchema) {
|
|
|
12242
12477
|
}
|
|
12243
12478
|
return false;
|
|
12244
12479
|
});
|
|
12245
|
-
}
|
|
12246
12480
|
if (!foundFeature) {
|
|
12247
12481
|
return mousePosition;
|
|
12248
12482
|
}
|
|
@@ -12565,27 +12799,33 @@ function CreateApolloAnnotation({ annotationFeature, assembly, handleClose, refS
|
|
|
12565
12799
|
const [selectedDestinationFeature, setSelectedDestinationFeature] = React.useState();
|
|
12566
12800
|
const apolloAssembly = apolloSessionModel.apolloDataStore.assemblies.get(assembly.name);
|
|
12567
12801
|
const refSeq = apolloAssembly?.refSeqs.get(refSeqId);
|
|
12568
|
-
const features = refSeq?.getFeatures(region.start, region.end);
|
|
12569
|
-
const getDestinationFeatures = () => {
|
|
12570
|
-
const filteredFeatures = [];
|
|
12571
|
-
for (const f of features ?? []) {
|
|
12572
|
-
if (f.min > region.end || f.max < region.start) {
|
|
12573
|
-
continue;
|
|
12574
|
-
}
|
|
12575
|
-
// Destination feature should be of type gene
|
|
12576
|
-
if (featureTypeOntology?.isTypeOf(f.type, 'gene')) {
|
|
12577
|
-
const featureSnapshot = mobxStateTree.getSnapshot(f);
|
|
12578
|
-
filteredFeatures.push(featureSnapshot);
|
|
12579
|
-
}
|
|
12580
|
-
}
|
|
12581
|
-
return filteredFeatures;
|
|
12582
|
-
};
|
|
12802
|
+
const features = React.useMemo(() => refSeq?.getFeatures(region.start, region.end), [refSeq, region.start, region.end]);
|
|
12583
12803
|
React.useEffect(() => {
|
|
12804
|
+
const getDestinationFeatures = () => {
|
|
12805
|
+
const filteredFeatures = [];
|
|
12806
|
+
for (const f of features ?? []) {
|
|
12807
|
+
if (f.min > region.end || f.max < region.start) {
|
|
12808
|
+
continue;
|
|
12809
|
+
}
|
|
12810
|
+
// Destination feature should be of type gene
|
|
12811
|
+
if (featureTypeOntology?.isTypeOf(f.type, 'gene')) {
|
|
12812
|
+
const featureSnapshot = mobxStateTree.getSnapshot(f);
|
|
12813
|
+
filteredFeatures.push(featureSnapshot);
|
|
12814
|
+
}
|
|
12815
|
+
}
|
|
12816
|
+
return filteredFeatures;
|
|
12817
|
+
};
|
|
12584
12818
|
setErrorMessage('');
|
|
12585
|
-
const
|
|
12586
|
-
setDestinationFeatures(
|
|
12587
|
-
setSelectedDestinationFeature(
|
|
12588
|
-
}, [
|
|
12819
|
+
const destinationFeatures = getDestinationFeatures();
|
|
12820
|
+
setDestinationFeatures(destinationFeatures);
|
|
12821
|
+
setSelectedDestinationFeature(destinationFeatures[0]);
|
|
12822
|
+
}, [
|
|
12823
|
+
checkedChildrens,
|
|
12824
|
+
featureTypeOntology,
|
|
12825
|
+
features,
|
|
12826
|
+
parentFeatureChecked,
|
|
12827
|
+
region,
|
|
12828
|
+
]);
|
|
12589
12829
|
const handleParentFeatureCheck = (event) => {
|
|
12590
12830
|
const isChecked = event.target.checked;
|
|
12591
12831
|
setParentFeatureChecked(isChecked);
|
|
@@ -12865,8 +13105,21 @@ function annotationFromPileup(pluggableElement) {
|
|
|
12865
13105
|
getAnnotationFeature(jbrowseFeature, refSeqId) {
|
|
12866
13106
|
const start = jbrowseFeature.get('start');
|
|
12867
13107
|
const end = jbrowseFeature.get('end');
|
|
12868
|
-
const strand = jbrowseFeature.get('strand');
|
|
12869
13108
|
const name = jbrowseFeature.get('name');
|
|
13109
|
+
let strand;
|
|
13110
|
+
const tags = jbrowseFeature.get('tags');
|
|
13111
|
+
const { ts, TS, XS } = tags;
|
|
13112
|
+
for (const tag of [ts, TS, XS]) {
|
|
13113
|
+
if (!strand && tag) {
|
|
13114
|
+
if ([1, '1', '+'].includes(tag)) {
|
|
13115
|
+
strand = 1;
|
|
13116
|
+
}
|
|
13117
|
+
else if ([-1, '-1', '-'].includes(tag)) {
|
|
13118
|
+
strand = -1;
|
|
13119
|
+
}
|
|
13120
|
+
}
|
|
13121
|
+
}
|
|
13122
|
+
strand ??= jbrowseFeature.get('strand');
|
|
12870
13123
|
const cigarData = jbrowseFeature.get('CIGAR');
|
|
12871
13124
|
const ops = parseCigar(cigarData);
|
|
12872
13125
|
let position = start;
|
|
@@ -12882,9 +13135,7 @@ function annotationFromPileup(pluggableElement) {
|
|
|
12882
13135
|
case 'M':
|
|
12883
13136
|
case '=':
|
|
12884
13137
|
case 'X': {
|
|
12885
|
-
|
|
12886
|
-
currentExonStart = position;
|
|
12887
|
-
}
|
|
13138
|
+
currentExonStart ??= position;
|
|
12888
13139
|
position += len;
|
|
12889
13140
|
break;
|
|
12890
13141
|
}
|
|
@@ -13298,7 +13549,7 @@ const LinearApolloDisplay = mobxReact.observer(function LinearApolloDisplay(prop
|
|
|
13298
13549
|
: undefined, menuItems: contextMenuItems })] }))] }));
|
|
13299
13550
|
});
|
|
13300
13551
|
|
|
13301
|
-
const TrackLines = mobxReact.observer(function TrackLines({ model, hrStyle = { margin: 0, top: 0, color: 'black' }, idx
|
|
13552
|
+
const TrackLines = mobxReact.observer(function TrackLines({ model, hrStyle = { margin: 0, top: 0, color: 'black' }, idx, }) {
|
|
13302
13553
|
const { apolloRowHeight, highestRow, showFeatureLabels } = model;
|
|
13303
13554
|
const featureLabelSpacer = showFeatureLabels ? 2 : 1;
|
|
13304
13555
|
return (jsxRuntime.jsx("div", { style: {
|
|
@@ -13370,8 +13621,7 @@ const LinearApolloSixFrameDisplay = mobxReact.observer(function LinearApolloSixF
|
|
|
13370
13621
|
regionNumber: idx,
|
|
13371
13622
|
})?.offsetPx ?? 0) - lgv.offsetPx;
|
|
13372
13623
|
const [feature] = checkResult.featureIds;
|
|
13373
|
-
if (!feature ||
|
|
13374
|
-
!feature.parent ||
|
|
13624
|
+
if (!feature?.parent ||
|
|
13375
13625
|
!looksLikeGene(feature.parent, session)) {
|
|
13376
13626
|
return null;
|
|
13377
13627
|
}
|
|
@@ -13623,18 +13873,15 @@ function addTopLevelMenus(rootModel) {
|
|
|
13623
13873
|
}
|
|
13624
13874
|
}
|
|
13625
13875
|
|
|
13626
|
-
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
13627
13876
|
/* eslint-disable @typescript-eslint/no-confusing-void-expression */
|
|
13628
13877
|
const ApolloJobModel = mobxStateTree.types
|
|
13629
13878
|
.model('JobsManager', {})
|
|
13630
13879
|
.views((self) => ({
|
|
13631
13880
|
get jobStatusWidget() {
|
|
13632
13881
|
const { widgets } = util.getSession(self);
|
|
13633
|
-
|
|
13634
|
-
if (!jobStatusWidget) {
|
|
13882
|
+
const jobStatusWidget = widgets.get('JobsList') ??
|
|
13635
13883
|
// @ts-expect-error: addWidget function not detected on the session
|
|
13636
|
-
|
|
13637
|
-
}
|
|
13884
|
+
util.getSession(self).addWidget('JobsListWidget', 'JobsList');
|
|
13638
13885
|
return jobStatusWidget;
|
|
13639
13886
|
},
|
|
13640
13887
|
}))
|
|
@@ -13913,19 +14160,15 @@ function clientDataStoreFactory(AnnotationFeatureExtended) {
|
|
|
13913
14160
|
continue;
|
|
13914
14161
|
}
|
|
13915
14162
|
const { assemblyName, refName } = region;
|
|
13916
|
-
|
|
13917
|
-
|
|
13918
|
-
assembly = self.assemblies.put({ _id: assemblyName, refSeqs: {} });
|
|
13919
|
-
}
|
|
14163
|
+
const assembly = self.assemblies.get(assemblyName) ??
|
|
14164
|
+
self.assemblies.put({ _id: assemblyName, refSeqs: {} });
|
|
13920
14165
|
const [firstFeature] = features;
|
|
13921
|
-
|
|
13922
|
-
|
|
13923
|
-
ref = assembly.refSeqs.put({
|
|
14166
|
+
const ref = assembly.refSeqs.get(firstFeature.refSeq) ??
|
|
14167
|
+
assembly.refSeqs.put({
|
|
13924
14168
|
_id: firstFeature.refSeq,
|
|
13925
14169
|
name: refName,
|
|
13926
14170
|
features: {},
|
|
13927
14171
|
});
|
|
13928
|
-
}
|
|
13929
14172
|
for (const feature of features) {
|
|
13930
14173
|
if (!ref.features.has(feature._id)) {
|
|
13931
14174
|
ref.features.put(feature);
|
|
@@ -13942,24 +14185,153 @@ function clientDataStoreFactory(AnnotationFeatureExtended) {
|
|
|
13942
14185
|
}
|
|
13943
14186
|
const { refSeq, seq } = yield backendDriver.getSequence(region);
|
|
13944
14187
|
const { assemblyName, end, refName, start } = region;
|
|
13945
|
-
|
|
13946
|
-
|
|
13947
|
-
|
|
13948
|
-
|
|
13949
|
-
let ref = assembly.refSeqs.get(refSeq);
|
|
13950
|
-
if (!ref) {
|
|
13951
|
-
ref = assembly.refSeqs.put({
|
|
13952
|
-
_id: refSeq,
|
|
13953
|
-
name: refName,
|
|
13954
|
-
sequence: [],
|
|
13955
|
-
});
|
|
13956
|
-
}
|
|
14188
|
+
const assembly = self.assemblies.get(assemblyName) ??
|
|
14189
|
+
self.assemblies.put({ _id: assemblyName, refSeqs: {} });
|
|
14190
|
+
const ref = assembly.refSeqs.get(refSeq) ??
|
|
14191
|
+
assembly.refSeqs.put({ _id: refSeq, name: refName, sequence: [] });
|
|
13957
14192
|
ref.addSequence({ start, stop: end, sequence: seq });
|
|
13958
14193
|
}
|
|
13959
14194
|
}),
|
|
13960
14195
|
}));
|
|
13961
14196
|
}
|
|
13962
14197
|
|
|
14198
|
+
function reassignIds(feature) {
|
|
14199
|
+
const newChildren = {};
|
|
14200
|
+
if (feature.children) {
|
|
14201
|
+
for (const child of Object.values(feature.children)) {
|
|
14202
|
+
const newChild = reassignIds(child);
|
|
14203
|
+
newChildren[newChild._id] = newChild;
|
|
14204
|
+
}
|
|
14205
|
+
}
|
|
14206
|
+
return {
|
|
14207
|
+
...feature,
|
|
14208
|
+
_id: new ObjectID().toHexString(),
|
|
14209
|
+
children: feature.children ? newChildren : undefined,
|
|
14210
|
+
};
|
|
14211
|
+
}
|
|
14212
|
+
function DuplicateFeatureDialog({ assemblyName, changeManager, existingFeature, featureSnapshot, handleClose, }) {
|
|
14213
|
+
const [choice, setChoice] = React.useState('keepExisting');
|
|
14214
|
+
const [errorMessage, setErrorMessage] = React.useState('');
|
|
14215
|
+
const [submitted, setSubmitted] = React.useState(false);
|
|
14216
|
+
const handleSubmit = async () => {
|
|
14217
|
+
setErrorMessage('');
|
|
14218
|
+
setSubmitted(true);
|
|
14219
|
+
try {
|
|
14220
|
+
if (choice === 'useNew') {
|
|
14221
|
+
const deleteChange = new shared.DeleteFeatureChange({
|
|
14222
|
+
typeName: 'DeleteFeatureChange',
|
|
14223
|
+
assembly: assemblyName,
|
|
14224
|
+
changedIds: [existingFeature._id],
|
|
14225
|
+
deletedFeature: existingFeature,
|
|
14226
|
+
});
|
|
14227
|
+
await changeManager.submit(deleteChange);
|
|
14228
|
+
const addChange = new shared.AddFeatureChange({
|
|
14229
|
+
typeName: 'AddFeatureChange',
|
|
14230
|
+
assembly: assemblyName,
|
|
14231
|
+
changedIds: [featureSnapshot._id],
|
|
14232
|
+
addedFeature: featureSnapshot,
|
|
14233
|
+
});
|
|
14234
|
+
await changeManager.submit(addChange);
|
|
14235
|
+
}
|
|
14236
|
+
else if (choice === 'keepBoth') {
|
|
14237
|
+
const newSnapshot = reassignIds(featureSnapshot);
|
|
14238
|
+
const addChange = new shared.AddFeatureChange({
|
|
14239
|
+
typeName: 'AddFeatureChange',
|
|
14240
|
+
assembly: assemblyName,
|
|
14241
|
+
changedIds: [newSnapshot._id],
|
|
14242
|
+
addedFeature: newSnapshot,
|
|
14243
|
+
});
|
|
14244
|
+
await changeManager.submit(addChange);
|
|
14245
|
+
}
|
|
14246
|
+
handleClose();
|
|
14247
|
+
}
|
|
14248
|
+
catch (error) {
|
|
14249
|
+
setErrorMessage(String(error));
|
|
14250
|
+
setSubmitted(false);
|
|
14251
|
+
}
|
|
14252
|
+
};
|
|
14253
|
+
return (jsxRuntime.jsxs(Dialog, { open: true, title: "Duplicate Feature Detected", handleClose: handleClose, maxWidth: "sm", fullWidth: true, children: [jsxRuntime.jsxs(material.DialogContent, { children: [jsxRuntime.jsxs(material.DialogContentText, { children: ["A feature with ID \u201C", featureSnapshot._id, "\u201D already exists. How would you like to resolve this conflict?"] }), jsxRuntime.jsxs(material.FormControl, { component: "fieldset", sx: { mt: 2 }, children: [jsxRuntime.jsx(material.FormLabel, { component: "legend", children: "Resolution" }), jsxRuntime.jsxs(material.RadioGroup, { value: choice, onChange: (e) => {
|
|
14254
|
+
setChoice(e.target.value);
|
|
14255
|
+
}, children: [jsxRuntime.jsx(material.FormControlLabel, { value: "keepExisting", control: jsxRuntime.jsx(material.Radio, {}), label: "Keep the existing feature" }), jsxRuntime.jsx(material.FormControlLabel, { value: "useNew", control: jsxRuntime.jsx(material.Radio, {}), label: "Replace with the new feature" }), jsxRuntime.jsx(material.FormControlLabel, { value: "keepBoth", control: jsxRuntime.jsx(material.Radio, {}), label: "Keep both (assign new IDs to the incoming feature)" })] })] }), errorMessage ? (jsxRuntime.jsx(material.DialogContentText, { color: "error", sx: { mt: 1 }, children: errorMessage })) : null] }), jsxRuntime.jsxs(material.DialogActions, { children: [jsxRuntime.jsx(material.Button, { variant: "contained", onClick: handleSubmit, disabled: submitted, children: "Confirm" }), jsxRuntime.jsx(material.Button, { onClick: handleClose, disabled: submitted, children: "Cancel" })] })] }));
|
|
14256
|
+
}
|
|
14257
|
+
|
|
14258
|
+
function fromUrlSafeBase64(urlSafeBase64) {
|
|
14259
|
+
let base64 = urlSafeBase64.replaceAll('-', '+').replaceAll('_', '/');
|
|
14260
|
+
const pad = base64.length % 4;
|
|
14261
|
+
if (pad) {
|
|
14262
|
+
base64 += '='.repeat(4 - pad);
|
|
14263
|
+
}
|
|
14264
|
+
return base64;
|
|
14265
|
+
}
|
|
14266
|
+
// Base64 decode + decompress
|
|
14267
|
+
async function decompress(encoded) {
|
|
14268
|
+
const binaryString = atob(encoded);
|
|
14269
|
+
// eslint-disable-next-line unicorn/prefer-code-point
|
|
14270
|
+
const bytes = Uint8Array.from(binaryString, (char) => char.charCodeAt(0));
|
|
14271
|
+
const stream = new Blob([bytes]).stream();
|
|
14272
|
+
const decompressionStream = new DecompressionStream('gzip');
|
|
14273
|
+
const decompressedStream = stream.pipeThrough(decompressionStream);
|
|
14274
|
+
const response = new Response(decompressedStream);
|
|
14275
|
+
const decompressed = await response.arrayBuffer();
|
|
14276
|
+
return JSON.parse(new TextDecoder().decode(decompressed));
|
|
14277
|
+
}
|
|
14278
|
+
async function handleApolloFeaturesUrlParam(encodedFeatures, apolloDataStore, assemblyManager, session) {
|
|
14279
|
+
const base64 = fromUrlSafeBase64(encodedFeatures);
|
|
14280
|
+
const decompressed = await decompress(base64);
|
|
14281
|
+
const featuresData = decompressed;
|
|
14282
|
+
for (const [assemblyName, featureSnapshots] of Object.entries(featuresData)) {
|
|
14283
|
+
const backendDriver = apolloDataStore.getBackendDriver(assemblyName);
|
|
14284
|
+
if (!(backendDriver instanceof LocalDriver)) {
|
|
14285
|
+
continue;
|
|
14286
|
+
}
|
|
14287
|
+
const assembly = (await assemblyManager.waitForAssembly(assemblyName));
|
|
14288
|
+
if (!assembly) {
|
|
14289
|
+
throw new Error(`Assembly not found: "${assemblyName}"`);
|
|
14290
|
+
}
|
|
14291
|
+
const { regions } = assembly;
|
|
14292
|
+
if (!regions) {
|
|
14293
|
+
throw new Error(`Assembly not found: "${assemblyName}"`);
|
|
14294
|
+
}
|
|
14295
|
+
const refNames = regions.map((r) => r.refName);
|
|
14296
|
+
const db = await openDb(assemblyName, refNames);
|
|
14297
|
+
for (const featureSnapshot of featureSnapshots) {
|
|
14298
|
+
const storeName = `features-${featureSnapshot.refSeq}`;
|
|
14299
|
+
const existing = (await db.get(storeName, featureSnapshot._id));
|
|
14300
|
+
if (existing === undefined) {
|
|
14301
|
+
const change = new shared.AddFeatureChange({
|
|
14302
|
+
typeName: 'AddFeatureChange',
|
|
14303
|
+
assembly: assemblyName,
|
|
14304
|
+
changedIds: [featureSnapshot._id],
|
|
14305
|
+
addedFeature: featureSnapshot,
|
|
14306
|
+
});
|
|
14307
|
+
await apolloDataStore.changeManager.submit(change);
|
|
14308
|
+
}
|
|
14309
|
+
else {
|
|
14310
|
+
// get rid of undefined values in JSON
|
|
14311
|
+
// eslint-disable-next-line unicorn/prefer-structured-clone
|
|
14312
|
+
const existingFeature = JSON.parse(JSON.stringify(existing));
|
|
14313
|
+
if (!equal(featureSnapshot, existingFeature)) {
|
|
14314
|
+
await new Promise((resolve) => {
|
|
14315
|
+
session.queueDialog((doneCallback) => [
|
|
14316
|
+
DuplicateFeatureDialog,
|
|
14317
|
+
{
|
|
14318
|
+
featureSnapshot,
|
|
14319
|
+
existingFeature,
|
|
14320
|
+
assemblyName,
|
|
14321
|
+
changeManager: apolloDataStore.changeManager,
|
|
14322
|
+
handleClose: () => {
|
|
14323
|
+
doneCallback();
|
|
14324
|
+
resolve();
|
|
14325
|
+
},
|
|
14326
|
+
},
|
|
14327
|
+
]);
|
|
14328
|
+
});
|
|
14329
|
+
}
|
|
14330
|
+
}
|
|
14331
|
+
}
|
|
14332
|
+
}
|
|
14333
|
+
}
|
|
14334
|
+
|
|
13963
14335
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
13964
14336
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
13965
14337
|
function extendSession(pluginManager, sessionModel) {
|
|
@@ -14045,8 +14417,7 @@ function extendSession(pluginManager, sessionModel) {
|
|
|
14045
14417
|
for (const block of dynamicBlocks.contentBlocks) {
|
|
14046
14418
|
const { assemblyName, end, refName, start } = block;
|
|
14047
14419
|
const assembly = self.apolloDataStore.assemblies.get(assemblyName);
|
|
14048
|
-
if (assembly
|
|
14049
|
-
assembly.backendDriverType === 'CollaborationServerDriver') {
|
|
14420
|
+
if (assembly?.backendDriverType === 'CollaborationServerDriver') {
|
|
14050
14421
|
locations.push({ assemblyName, refName, start, end });
|
|
14051
14422
|
}
|
|
14052
14423
|
}
|
|
@@ -14105,8 +14476,8 @@ function extendSession(pluginManager, sessionModel) {
|
|
|
14105
14476
|
for (const block of dynamicBlocks.contentBlocks) {
|
|
14106
14477
|
const { assemblyName, end, refName, start } = block;
|
|
14107
14478
|
const assembly = self.apolloDataStore.assemblies.get(assemblyName);
|
|
14108
|
-
if (assembly
|
|
14109
|
-
|
|
14479
|
+
if (assembly?.backendDriverType ===
|
|
14480
|
+
'CollaborationServerDriver') {
|
|
14110
14481
|
locations.push({ assemblyName, refName, start, end });
|
|
14111
14482
|
}
|
|
14112
14483
|
}
|
|
@@ -14226,6 +14597,37 @@ function extendSession(pluginManager, sessionModel) {
|
|
|
14226
14597
|
beforeDestroy() {
|
|
14227
14598
|
self.abortController.abort(new DOMException('Clean up Apollo session', 'AbortError'));
|
|
14228
14599
|
},
|
|
14600
|
+
}))
|
|
14601
|
+
.actions((self) => ({
|
|
14602
|
+
async afterCreate() {
|
|
14603
|
+
if (util.isElectron) {
|
|
14604
|
+
return;
|
|
14605
|
+
}
|
|
14606
|
+
const url = new URL(globalThis.location.href);
|
|
14607
|
+
const apolloFeatures = url.searchParams.get('apolloFeatures');
|
|
14608
|
+
if (!apolloFeatures) {
|
|
14609
|
+
return;
|
|
14610
|
+
}
|
|
14611
|
+
const { assemblyManager } = self;
|
|
14612
|
+
await handleApolloFeaturesUrlParam(apolloFeatures, self.apolloDataStore, assemblyManager, self);
|
|
14613
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
14614
|
+
const updatedURL = new URL(globalThis.location.href);
|
|
14615
|
+
updatedURL.searchParams.delete('apolloFeatures');
|
|
14616
|
+
globalThis.history.replaceState(null, '', updatedURL.toString());
|
|
14617
|
+
},
|
|
14618
|
+
afterAttach() {
|
|
14619
|
+
mobxStateTree.addDisposer(self, mobx.autorun((reaction) => {
|
|
14620
|
+
const { focusedViewId, activeWidgets } = self;
|
|
14621
|
+
if (!(focusedViewId && activeWidgets)) {
|
|
14622
|
+
return;
|
|
14623
|
+
}
|
|
14624
|
+
const trackSelector = activeWidgets.get('hierarchicalTrackSelector');
|
|
14625
|
+
// @ts-expect-error Don't have type for track selector
|
|
14626
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
14627
|
+
trackSelector?.setView(focusedViewId);
|
|
14628
|
+
reaction.dispose();
|
|
14629
|
+
}));
|
|
14630
|
+
},
|
|
14229
14631
|
}))
|
|
14230
14632
|
.views((self) => {
|
|
14231
14633
|
const superTrackActions = self
|
|
@@ -14357,7 +14759,6 @@ function extendSession(pluginManager, sessionModel) {
|
|
|
14357
14759
|
}
|
|
14358
14760
|
|
|
14359
14761
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
14360
|
-
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
14361
14762
|
/* eslint-disable @typescript-eslint/no-misused-promises */
|
|
14362
14763
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
14363
14764
|
function isApolloMessageData(data) {
|
|
@@ -14545,10 +14946,10 @@ class ApolloPlugin extends Plugin {
|
|
|
14545
14946
|
});
|
|
14546
14947
|
if (!inWebWorker) {
|
|
14547
14948
|
pluginManager.addToExtensionPoint('Core-extendWorker', (handle) => {
|
|
14548
|
-
if (!('on' in handle && handle.on)) {
|
|
14949
|
+
if (!('on' in handle.client && handle.client.on)) {
|
|
14549
14950
|
return handle;
|
|
14550
14951
|
}
|
|
14551
|
-
handle.on('apollo', async (event) => {
|
|
14952
|
+
handle.client.on('apollo', async (event) => {
|
|
14552
14953
|
if (!isApolloMessageData(event)) {
|
|
14553
14954
|
return;
|
|
14554
14955
|
}
|
|
@@ -14566,7 +14967,7 @@ class ApolloPlugin extends Plugin {
|
|
|
14566
14967
|
break;
|
|
14567
14968
|
}
|
|
14568
14969
|
const { seq: sequence } = await backendDriver.getSequence(region);
|
|
14569
|
-
handle.
|
|
14970
|
+
handle.worker.postMessage({
|
|
14570
14971
|
apollo,
|
|
14571
14972
|
messageId,
|
|
14572
14973
|
sequence,
|
|
@@ -14584,7 +14985,7 @@ class ApolloPlugin extends Plugin {
|
|
|
14584
14985
|
break;
|
|
14585
14986
|
}
|
|
14586
14987
|
const regions = await backendDriver.getRegions(assembly);
|
|
14587
|
-
handle.
|
|
14988
|
+
handle.worker.postMessage({
|
|
14588
14989
|
apollo,
|
|
14589
14990
|
messageId,
|
|
14590
14991
|
regions,
|
|
@@ -14602,7 +15003,7 @@ class ApolloPlugin extends Plugin {
|
|
|
14602
15003
|
break;
|
|
14603
15004
|
}
|
|
14604
15005
|
const refNameAliases = await backendDriver.getRefNameAliases(assembly);
|
|
14605
|
-
handle.
|
|
15006
|
+
handle.worker.postMessage({
|
|
14606
15007
|
apollo,
|
|
14607
15008
|
messageId,
|
|
14608
15009
|
refNameAliases,
|