@apollo-annotation/jbrowse-plugin-apollo 1.0.1 → 1.1.0
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 +826 -433
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +821 -428
- 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 +859 -467
- 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 +358 -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 +28 -23
- 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.0
|
|
73
|
+
var version = "1.1.0";
|
|
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,187 @@ 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
|
+
const stopCodonIndex = proteinSequence.lastIndexOf('*');
|
|
7259
|
+
const startCodonPos = startCodonIndex * 3;
|
|
7260
|
+
const stopCodonPos = stopCodonIndex * 3;
|
|
7261
|
+
const startCodonGenomicLoc = getCodonGenomicLocation(startCodonPos);
|
|
7262
|
+
let stopCodonGenomicLoc = getCodonGenomicLocation(stopCodonPos);
|
|
7263
|
+
if (strand === 1) {
|
|
7264
|
+
if (startCodonGenomicLoc > stopCodonGenomicLoc) {
|
|
7265
|
+
notify('Start codon genomic location should be less than stop codon genomic location', 'error');
|
|
7266
|
+
return;
|
|
7267
|
+
}
|
|
7268
|
+
let promise;
|
|
7269
|
+
stopCodonGenomicLoc += 3; // move to end of stop codon
|
|
7270
|
+
if (startCodonGenomicLoc !== cdsMin) {
|
|
7271
|
+
promise = new Promise((resolve) => {
|
|
7272
|
+
updateCDSLocation(cdsMin, startCodonGenomicLoc, feature, true, () => {
|
|
7273
|
+
resolve(true);
|
|
7274
|
+
});
|
|
7275
|
+
});
|
|
7276
|
+
}
|
|
7277
|
+
if (stopCodonGenomicLoc !== cdsMax) {
|
|
7278
|
+
if (promise) {
|
|
7279
|
+
void promise.then(() => {
|
|
7280
|
+
updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
|
|
7281
|
+
});
|
|
7282
|
+
}
|
|
7283
|
+
else {
|
|
7284
|
+
updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
|
|
7285
|
+
}
|
|
7286
|
+
}
|
|
7287
|
+
}
|
|
7288
|
+
if (strand === -1) {
|
|
7289
|
+
// reverse strand
|
|
7290
|
+
if (startCodonGenomicLoc < stopCodonGenomicLoc) {
|
|
7291
|
+
notify('Start codon genomic location should be less than stop codon genomic location', 'error');
|
|
7292
|
+
return;
|
|
7293
|
+
}
|
|
7294
|
+
let promise;
|
|
7295
|
+
stopCodonGenomicLoc -= 3; // move to end of stop codon
|
|
7296
|
+
if (startCodonGenomicLoc !== cdsMax) {
|
|
7297
|
+
promise = new Promise((resolve) => {
|
|
7298
|
+
updateCDSLocation(cdsMax, startCodonGenomicLoc, feature, false, () => {
|
|
7299
|
+
resolve(true);
|
|
7300
|
+
});
|
|
7301
|
+
});
|
|
7302
|
+
}
|
|
7303
|
+
if (stopCodonGenomicLoc !== cdsMin) {
|
|
7304
|
+
if (promise) {
|
|
7305
|
+
void promise.then(() => {
|
|
7306
|
+
updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
|
|
7307
|
+
});
|
|
7308
|
+
}
|
|
7309
|
+
else {
|
|
7310
|
+
updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
|
|
7311
|
+
}
|
|
7312
|
+
}
|
|
7313
|
+
}
|
|
7314
|
+
notify('Translation sequence trimmed to start and stop codons', 'success');
|
|
7315
|
+
};
|
|
7316
|
+
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) => {
|
|
7317
|
+
const codonGenomicPos = idx * 3;
|
|
7318
|
+
if (protein === 'M') {
|
|
7319
|
+
return (jsxRuntime.jsx(material.Typography, { component: 'span', style: {
|
|
7320
|
+
backgroundColor: changeInProgress
|
|
7321
|
+
? 'lightgray'
|
|
7322
|
+
: 'yellow',
|
|
7323
|
+
cursor: 'pointer',
|
|
7324
|
+
border: '1px solid black',
|
|
7325
|
+
}, onClick: () => {
|
|
7326
|
+
if (changeInProgress) {
|
|
7327
|
+
return;
|
|
7328
|
+
}
|
|
7329
|
+
// NOTE: codonGenomicPos is important here for calculating the genomic location
|
|
7330
|
+
// of the start codon. We are using the codonGenomicPos as the key in the typography
|
|
7331
|
+
// elements to maintain the genomic postion of the codon start
|
|
7332
|
+
const startCodonGenomicLocation = getCodonGenomicLocation(codonGenomicPos);
|
|
7333
|
+
if (startCodonGenomicLocation !== cdsMin &&
|
|
7334
|
+
strand === 1) {
|
|
7335
|
+
updateCDSLocation(cdsMin, startCodonGenomicLocation, feature, true);
|
|
7336
|
+
}
|
|
7337
|
+
if (startCodonGenomicLocation !== cdsMax &&
|
|
7338
|
+
strand === -1) {
|
|
7339
|
+
updateCDSLocation(cdsMax, startCodonGenomicLocation, feature, false);
|
|
7340
|
+
}
|
|
7341
|
+
}, children: protein }, codonGenomicPos));
|
|
7342
|
+
}
|
|
7343
|
+
if (protein === '*') {
|
|
7344
|
+
return (jsxRuntime.jsx(material.Typography, { style: { backgroundColor: 'red', color: 'white' }, component: 'span', children: protein }, codonGenomicPos));
|
|
7345
|
+
}
|
|
7346
|
+
// Pass the codonGenomicPos as the key to maintain the genomic position of the codon
|
|
7347
|
+
return (jsxRuntime.jsx(material.Typography, { component: 'span', children: protein }, codonGenomicPos));
|
|
7348
|
+
}) }) }), jsxRuntime.jsxs("div", { style: {
|
|
7349
|
+
marginTop: 10,
|
|
7350
|
+
display: 'flex',
|
|
7351
|
+
flexDirection: 'row',
|
|
7352
|
+
alignItems: 'center',
|
|
7353
|
+
gap: 10,
|
|
7354
|
+
}, 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 } }) }) })] })] })] }) }));
|
|
7355
|
+
}
|
|
7356
|
+
|
|
7357
|
+
const StyledTextField = styled(NumberTextField)(() => ({
|
|
7358
|
+
'&.MuiFormControl-root': {
|
|
7359
|
+
marginTop: 0,
|
|
7360
|
+
marginBottom: 0,
|
|
7361
|
+
width: '100%',
|
|
7362
|
+
},
|
|
7363
|
+
'& .MuiInputBase-input': {
|
|
7364
|
+
fontSize: 12,
|
|
7365
|
+
height: 20,
|
|
7366
|
+
padding: 1,
|
|
7367
|
+
paddingLeft: 10,
|
|
7368
|
+
},
|
|
7369
|
+
}));
|
|
6994
7370
|
const Strand = (props) => {
|
|
6995
7371
|
const { strand } = props;
|
|
6996
7372
|
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 +7385,6 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
|
|
|
7009
7385
|
const currentAssembly = session.apolloDataStore.assemblies.get(assembly);
|
|
7010
7386
|
const refData = currentAssembly?.getByRefName(refName);
|
|
7011
7387
|
const { changeManager } = session.apolloDataStore;
|
|
7012
|
-
const seqRef = React.useRef(null);
|
|
7013
7388
|
const { changeInProgress } = session;
|
|
7014
7389
|
if (!refData) {
|
|
7015
7390
|
return null;
|
|
@@ -7436,184 +7811,6 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
|
|
|
7436
7811
|
},
|
|
7437
7812
|
];
|
|
7438
7813
|
};
|
|
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
7814
|
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
7815
|
return updateCDSLocation(cdsMin, newLocation - 1, feature, true);
|
|
7619
7816
|
}, 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 +7820,12 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
|
|
|
7623
7820
|
}, 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
7821
|
return updateCDSLocation(cdsMin, newLocation - 1, feature, true);
|
|
7625
7822
|
}, 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
|
-
|
|
7823
|
+
return (
|
|
7824
|
+
// eslint-disable-next-line @eslint-react/no-array-index-key
|
|
7825
|
+
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 &&
|
|
7826
|
+
getFivePrimeSpliceSite(loc, index).map((site, idx) => (jsxRuntime.jsx(material.Typography
|
|
7827
|
+
// eslint-disable-next-line @eslint-react/no-array-index-key
|
|
7828
|
+
, { 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
7829
|
return handleExonLocationChange(loc.min, newLocation - 1, feature, true);
|
|
7629
7830
|
}, 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
7831
|
return handleExonLocationChange(loc.max, newLocation, feature, false);
|
|
@@ -7633,14 +7834,10 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
|
|
|
7633
7834
|
}, 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
7835
|
return handleExonLocationChange(loc.min, newLocation - 1, feature, true);
|
|
7635
7836
|
}, 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 } }) }) })] })] })] }) }))] }));
|
|
7837
|
+
getThreePrimeSpliceSite(loc, index).map((site, idx) => (jsxRuntime.jsx(material.Typography
|
|
7838
|
+
// eslint-disable-next-line @eslint-react/no-array-index-key
|
|
7839
|
+
, { component: 'span', color: site.color, children: site.spliceSite }, idx))) })] })) }, index));
|
|
7840
|
+
})] }), cdsPresent && (jsxRuntime.jsx(Translation, { changeInProgress: changeInProgress, cdsLocations: cdsLocations, refData: refData, strand: strand, updateCDSLocation: updateCDSLocation, cdsMin: cdsMin, cdsMax: cdsMax, feature: feature, session: session }))] }));
|
|
7644
7841
|
});
|
|
7645
7842
|
|
|
7646
7843
|
const HeaderTableCell = styled(material.TableCell)(() => ({
|
|
@@ -7677,7 +7874,7 @@ const ApolloTranscriptDetailsWidget = mobxReact.observer(function ApolloTranscri
|
|
|
7677
7874
|
const { assembly, feature, refName } = model;
|
|
7678
7875
|
React.useEffect(() => {
|
|
7679
7876
|
setPanelState(DEFAULT_PANELS);
|
|
7680
|
-
// eslint-disable-next-line react
|
|
7877
|
+
// eslint-disable-next-line @eslint-react/exhaustive-deps
|
|
7681
7878
|
}, [feature]);
|
|
7682
7879
|
const session = util.getSession(model);
|
|
7683
7880
|
const { pluginManager } = util.getEnv(session);
|
|
@@ -7736,8 +7933,8 @@ function handleFeatureTypeChange(changeManager, feature, oldType, newType) {
|
|
|
7736
7933
|
typeName: 'TypeChange',
|
|
7737
7934
|
changedIds: [featureId],
|
|
7738
7935
|
featureId,
|
|
7739
|
-
oldType
|
|
7740
|
-
newType
|
|
7936
|
+
oldType,
|
|
7937
|
+
newType,
|
|
7741
7938
|
assembly: feature.assemblyId,
|
|
7742
7939
|
});
|
|
7743
7940
|
return changeManager.submit(change);
|
|
@@ -7833,7 +8030,7 @@ const NumberCell = mobxReact.observer(function NumberCell({ initialValue, notify
|
|
|
7833
8030
|
if (initialValue !== value) {
|
|
7834
8031
|
setValue(initialValue);
|
|
7835
8032
|
}
|
|
7836
|
-
// eslint-disable-next-line react
|
|
8033
|
+
// eslint-disable-next-line @eslint-react/exhaustive-deps
|
|
7837
8034
|
}, [initialValue]);
|
|
7838
8035
|
React.useEffect(() => {
|
|
7839
8036
|
if (blur) {
|
|
@@ -8674,8 +8871,9 @@ function draw$3(display, ctx, gene, row, rowInFeature, block) {
|
|
|
8674
8871
|
const [top, left, width] = getFeatureBox(display, gene, row, block);
|
|
8675
8872
|
const height = getRowCount$2(display, gene) * apolloRowHeight;
|
|
8676
8873
|
if (width > 2) {
|
|
8677
|
-
|
|
8678
|
-
|
|
8874
|
+
const selectedColor = configuration.readConfObject(session.getPluginConfiguration(), 'geneBackgroundColor', {
|
|
8875
|
+
featureType: gene.type,
|
|
8876
|
+
}) ?? material.alpha(theme.palette.background.paper, 0.6);
|
|
8679
8877
|
ctx.fillStyle = selectedColor;
|
|
8680
8878
|
ctx.fillRect(left, top, width, height);
|
|
8681
8879
|
}
|
|
@@ -8948,7 +9146,7 @@ function getLayout(display, feature) {
|
|
|
8948
9146
|
}
|
|
8949
9147
|
return layout;
|
|
8950
9148
|
}
|
|
8951
|
-
function getContextMenuItems$1(display,
|
|
9149
|
+
function getContextMenuItems$1(display, transcript) {
|
|
8952
9150
|
const { changeManager, regions, selectedFeature, session } = display;
|
|
8953
9151
|
const [region] = regions;
|
|
8954
9152
|
const currentAssemblyId = display.getAssemblyId(region.assemblyName);
|
|
@@ -8958,7 +9156,7 @@ function getContextMenuItems$1(display, feature) {
|
|
|
8958
9156
|
label: 'Open transcript editor',
|
|
8959
9157
|
onClick: () => {
|
|
8960
9158
|
const apolloTranscriptWidget = session.addWidget('ApolloTranscriptDetails', 'apolloTranscriptDetails', {
|
|
8961
|
-
feature,
|
|
9159
|
+
feature: transcript,
|
|
8962
9160
|
assembly: currentAssemblyId,
|
|
8963
9161
|
changeManager,
|
|
8964
9162
|
refName: region.refName,
|
|
@@ -8978,7 +9176,7 @@ function getContextMenuItems$1(display, feature) {
|
|
|
8978
9176
|
doneCallback();
|
|
8979
9177
|
},
|
|
8980
9178
|
changeManager,
|
|
8981
|
-
sourceFeature:
|
|
9179
|
+
sourceFeature: transcript,
|
|
8982
9180
|
sourceAssemblyId: currentAssemblyId,
|
|
8983
9181
|
selectedFeature,
|
|
8984
9182
|
setSelectedFeature: (feature) => {
|
|
@@ -8998,7 +9196,7 @@ function getContextMenuItems$1(display, feature) {
|
|
|
8998
9196
|
doneCallback();
|
|
8999
9197
|
},
|
|
9000
9198
|
changeManager,
|
|
9001
|
-
sourceFeature:
|
|
9199
|
+
sourceFeature: transcript,
|
|
9002
9200
|
sourceAssemblyId: currentAssemblyId,
|
|
9003
9201
|
selectedFeature,
|
|
9004
9202
|
setSelectedFeature: (feature) => {
|
|
@@ -9008,6 +9206,31 @@ function getContextMenuItems$1(display, feature) {
|
|
|
9008
9206
|
]);
|
|
9009
9207
|
},
|
|
9010
9208
|
});
|
|
9209
|
+
const { children } = transcript;
|
|
9210
|
+
if (!children) {
|
|
9211
|
+
return [];
|
|
9212
|
+
}
|
|
9213
|
+
const cdsChildren = [...children.values()].filter((child) => isCDSFeature(child, session));
|
|
9214
|
+
if (cdsChildren.length === 0) {
|
|
9215
|
+
menuItems.push({
|
|
9216
|
+
label: 'Add coding sequence',
|
|
9217
|
+
onClick: () => {
|
|
9218
|
+
session.queueDialog((doneCallback) => [
|
|
9219
|
+
AddCodingSequence,
|
|
9220
|
+
{
|
|
9221
|
+
session,
|
|
9222
|
+
handleClose: () => {
|
|
9223
|
+
doneCallback();
|
|
9224
|
+
},
|
|
9225
|
+
changeManager,
|
|
9226
|
+
sourceFeature: transcript,
|
|
9227
|
+
sourceAssemblyId: currentAssemblyId,
|
|
9228
|
+
refName: region.refName,
|
|
9229
|
+
},
|
|
9230
|
+
]);
|
|
9231
|
+
},
|
|
9232
|
+
});
|
|
9233
|
+
}
|
|
9011
9234
|
return menuItems;
|
|
9012
9235
|
}
|
|
9013
9236
|
// False positive here, none of these functions use "this"
|
|
@@ -9185,7 +9408,7 @@ function colorCode(letter, theme) {
|
|
|
9185
9408
|
letterUpper === 'C' ||
|
|
9186
9409
|
letterUpper === 'G' ||
|
|
9187
9410
|
letterUpper === 'T') {
|
|
9188
|
-
return theme.palette.bases[letterUpper].main
|
|
9411
|
+
return theme.palette.bases[letterUpper].main;
|
|
9189
9412
|
}
|
|
9190
9413
|
return 'lightgray';
|
|
9191
9414
|
}
|
|
@@ -9282,8 +9505,7 @@ function baseModelFactory$2(_pluginManager, configSchema) {
|
|
|
9282
9505
|
return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
|
|
9283
9506
|
},
|
|
9284
9507
|
get changeManager() {
|
|
9285
|
-
return self.session.apolloDataStore
|
|
9286
|
-
.changeManager;
|
|
9508
|
+
return self.session.apolloDataStore.changeManager;
|
|
9287
9509
|
},
|
|
9288
9510
|
getAssemblyId(assemblyName) {
|
|
9289
9511
|
const { assemblyManager } = self.session;
|
|
@@ -9294,12 +9516,10 @@ function baseModelFactory$2(_pluginManager, configSchema) {
|
|
|
9294
9516
|
return assembly.name;
|
|
9295
9517
|
},
|
|
9296
9518
|
get selectedFeature() {
|
|
9297
|
-
return self.session
|
|
9298
|
-
.apolloSelectedFeature;
|
|
9519
|
+
return self.session.apolloSelectedFeature;
|
|
9299
9520
|
},
|
|
9300
9521
|
get hoveredFeature() {
|
|
9301
|
-
return self.session
|
|
9302
|
-
.apolloHoveredFeature;
|
|
9522
|
+
return self.session.apolloHoveredFeature;
|
|
9303
9523
|
},
|
|
9304
9524
|
}))
|
|
9305
9525
|
.actions((self) => ({
|
|
@@ -9344,8 +9564,11 @@ function baseModelFactory$2(_pluginManager, configSchema) {
|
|
|
9344
9564
|
const { filteredFeatureTypes, trackMenuItems: superTrackMenuItems } = self;
|
|
9345
9565
|
return {
|
|
9346
9566
|
trackMenuItems() {
|
|
9347
|
-
const { graphical, table, showCheckResults } = self;
|
|
9348
|
-
|
|
9567
|
+
const { graphical, table, showCheckResults, session } = self;
|
|
9568
|
+
const [region] = self.regions;
|
|
9569
|
+
const { assemblyName } = region;
|
|
9570
|
+
const backendDriver = session.apolloDataStore.getBackendDriver(assemblyName);
|
|
9571
|
+
const items = [
|
|
9349
9572
|
...superTrackMenuItems(),
|
|
9350
9573
|
{
|
|
9351
9574
|
type: 'subMenu',
|
|
@@ -9397,7 +9620,7 @@ function baseModelFactory$2(_pluginManager, configSchema) {
|
|
|
9397
9620
|
{
|
|
9398
9621
|
label: 'Filter features by type',
|
|
9399
9622
|
onClick: () => {
|
|
9400
|
-
const session = self
|
|
9623
|
+
const { session } = self;
|
|
9401
9624
|
self.session.queueDialog((doneCallback) => [
|
|
9402
9625
|
FilterFeatures,
|
|
9403
9626
|
{
|
|
@@ -9413,76 +9636,93 @@ function baseModelFactory$2(_pluginManager, configSchema) {
|
|
|
9413
9636
|
]);
|
|
9414
9637
|
},
|
|
9415
9638
|
},
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9639
|
+
];
|
|
9640
|
+
if (backendDriver instanceof LocalDriver) {
|
|
9641
|
+
items.push({
|
|
9642
|
+
label: 'Import annotations',
|
|
9643
|
+
icon: InputIcon,
|
|
9419
9644
|
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
9645
|
session.queueDialog((doneCallback) => [
|
|
9428
|
-
|
|
9646
|
+
ImportAnnotations,
|
|
9429
9647
|
{
|
|
9430
9648
|
session,
|
|
9649
|
+
assemblyName,
|
|
9431
9650
|
handleClose: () => {
|
|
9432
9651
|
doneCallback();
|
|
9433
9652
|
},
|
|
9434
|
-
assembly,
|
|
9435
9653
|
},
|
|
9436
9654
|
]);
|
|
9437
9655
|
},
|
|
9656
|
+
});
|
|
9657
|
+
}
|
|
9658
|
+
items.push({
|
|
9659
|
+
label: 'Export annotations',
|
|
9660
|
+
icon: Export,
|
|
9661
|
+
onClick: () => {
|
|
9662
|
+
const [region] = self.regions;
|
|
9663
|
+
const { assemblyName } = region;
|
|
9664
|
+
const assembly = self.getAssemblyId(assemblyName);
|
|
9665
|
+
if (!assembly) {
|
|
9666
|
+
return;
|
|
9667
|
+
}
|
|
9668
|
+
const { session } = self;
|
|
9669
|
+
session.queueDialog((doneCallback) => [
|
|
9670
|
+
DownloadGFF3,
|
|
9671
|
+
{
|
|
9672
|
+
session,
|
|
9673
|
+
handleClose: () => {
|
|
9674
|
+
doneCallback();
|
|
9675
|
+
},
|
|
9676
|
+
assembly,
|
|
9677
|
+
},
|
|
9678
|
+
]);
|
|
9438
9679
|
},
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9456
|
-
},
|
|
9457
|
-
assembly,
|
|
9680
|
+
}, {
|
|
9681
|
+
label: 'View Change Log',
|
|
9682
|
+
icon: TrackChangesIcon,
|
|
9683
|
+
onClick: () => {
|
|
9684
|
+
const [region] = self.regions;
|
|
9685
|
+
const { assemblyName } = region;
|
|
9686
|
+
const assembly = self.getAssemblyId(assemblyName);
|
|
9687
|
+
if (!assembly) {
|
|
9688
|
+
return;
|
|
9689
|
+
}
|
|
9690
|
+
const { session } = self;
|
|
9691
|
+
session.queueDialog((doneCallback) => [
|
|
9692
|
+
ViewChangeLog,
|
|
9693
|
+
{
|
|
9694
|
+
session,
|
|
9695
|
+
handleClose: () => {
|
|
9696
|
+
doneCallback();
|
|
9458
9697
|
},
|
|
9459
|
-
|
|
9460
|
-
|
|
9698
|
+
assembly,
|
|
9699
|
+
},
|
|
9700
|
+
]);
|
|
9461
9701
|
},
|
|
9462
|
-
|
|
9463
|
-
|
|
9464
|
-
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
|
|
9468
|
-
|
|
9469
|
-
|
|
9470
|
-
|
|
9471
|
-
|
|
9472
|
-
|
|
9473
|
-
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
|
|
9478
|
-
|
|
9479
|
-
},
|
|
9480
|
-
assembly,
|
|
9702
|
+
}, {
|
|
9703
|
+
label: 'View Check Results',
|
|
9704
|
+
icon: FactCheckIcon,
|
|
9705
|
+
onClick: () => {
|
|
9706
|
+
const [region] = self.regions;
|
|
9707
|
+
const { assemblyName } = region;
|
|
9708
|
+
const assembly = self.getAssemblyId(assemblyName);
|
|
9709
|
+
if (!assembly) {
|
|
9710
|
+
return;
|
|
9711
|
+
}
|
|
9712
|
+
const { session } = self;
|
|
9713
|
+
session.queueDialog((doneCallback) => [
|
|
9714
|
+
ViewCheckResults,
|
|
9715
|
+
{
|
|
9716
|
+
session,
|
|
9717
|
+
handleClose: () => {
|
|
9718
|
+
doneCallback();
|
|
9481
9719
|
},
|
|
9482
|
-
|
|
9483
|
-
|
|
9720
|
+
assembly,
|
|
9721
|
+
},
|
|
9722
|
+
]);
|
|
9484
9723
|
},
|
|
9485
|
-
|
|
9724
|
+
});
|
|
9725
|
+
return items;
|
|
9486
9726
|
},
|
|
9487
9727
|
};
|
|
9488
9728
|
})
|
|
@@ -10548,8 +10788,7 @@ function baseModelFactory$1(_pluginManager, configSchema) {
|
|
|
10548
10788
|
return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
|
|
10549
10789
|
},
|
|
10550
10790
|
get changeManager() {
|
|
10551
|
-
return self.session.apolloDataStore
|
|
10552
|
-
.changeManager;
|
|
10791
|
+
return self.session.apolloDataStore.changeManager;
|
|
10553
10792
|
},
|
|
10554
10793
|
getAssemblyId(assemblyName) {
|
|
10555
10794
|
const { assemblyManager } = self.session;
|
|
@@ -10560,12 +10799,10 @@ function baseModelFactory$1(_pluginManager, configSchema) {
|
|
|
10560
10799
|
return assembly.name;
|
|
10561
10800
|
},
|
|
10562
10801
|
get selectedFeature() {
|
|
10563
|
-
return self.session
|
|
10564
|
-
.apolloSelectedFeature;
|
|
10802
|
+
return self.session.apolloSelectedFeature;
|
|
10565
10803
|
},
|
|
10566
10804
|
get hoveredFeature() {
|
|
10567
|
-
return self.session
|
|
10568
|
-
.apolloHoveredFeature;
|
|
10805
|
+
return self.session.apolloHoveredFeature;
|
|
10569
10806
|
},
|
|
10570
10807
|
get height() {
|
|
10571
10808
|
const { sequenceRowHeight } = self;
|
|
@@ -10777,11 +11014,9 @@ const FilterTranscripts = mobxReact.observer(function FilterTranscripts({ source
|
|
|
10777
11014
|
onUpdate(newForms);
|
|
10778
11015
|
setExcludedTranscripts(newForms);
|
|
10779
11016
|
};
|
|
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: () => {
|
|
11017
|
+
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
11018
|
handleChange(item);
|
|
10784
|
-
}, slotProps: { input: { 'aria-label': 'controlled' } } }), label: item }))) }) }) })] }) }));
|
|
11019
|
+
}, slotProps: { input: { 'aria-label': 'controlled' } } }), label: item }, item))) }) }) })] }) }));
|
|
10785
11020
|
});
|
|
10786
11021
|
|
|
10787
11022
|
let forwardFillLight = null;
|
|
@@ -11627,8 +11862,7 @@ function baseModelFactory(_pluginManager, configSchema) {
|
|
|
11627
11862
|
return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
|
|
11628
11863
|
},
|
|
11629
11864
|
get changeManager() {
|
|
11630
|
-
return self.session.apolloDataStore
|
|
11631
|
-
.changeManager;
|
|
11865
|
+
return self.session.apolloDataStore.changeManager;
|
|
11632
11866
|
},
|
|
11633
11867
|
getAssemblyId(assemblyName) {
|
|
11634
11868
|
const { assemblyManager } = self.session;
|
|
@@ -11639,12 +11873,10 @@ function baseModelFactory(_pluginManager, configSchema) {
|
|
|
11639
11873
|
return assembly.name;
|
|
11640
11874
|
},
|
|
11641
11875
|
get selectedFeature() {
|
|
11642
|
-
return self.session
|
|
11643
|
-
.apolloSelectedFeature;
|
|
11876
|
+
return self.session.apolloSelectedFeature;
|
|
11644
11877
|
},
|
|
11645
11878
|
get hoveredFeature() {
|
|
11646
|
-
return self.session
|
|
11647
|
-
.apolloHoveredFeature;
|
|
11879
|
+
return self.session.apolloHoveredFeature;
|
|
11648
11880
|
},
|
|
11649
11881
|
}))
|
|
11650
11882
|
.actions((self) => ({
|
|
@@ -11772,7 +12004,7 @@ function baseModelFactory(_pluginManager, configSchema) {
|
|
|
11772
12004
|
{
|
|
11773
12005
|
label: 'Filter features by type',
|
|
11774
12006
|
onClick: () => {
|
|
11775
|
-
const session = self
|
|
12007
|
+
const { session } = self;
|
|
11776
12008
|
self.session.queueDialog((doneCallback) => [
|
|
11777
12009
|
FilterFeatures,
|
|
11778
12010
|
{
|
|
@@ -12210,17 +12442,12 @@ function mouseEventsModelIntermediateFactory(pluginManager, configSchema) {
|
|
|
12210
12442
|
if (!featureTypeOntology) {
|
|
12211
12443
|
throw new Error('featureTypeOntology is undefined');
|
|
12212
12444
|
}
|
|
12213
|
-
|
|
12214
|
-
|
|
12215
|
-
foundFeature = layoutRow.find((f) => f.feature.type == 'exon' &&
|
|
12445
|
+
const foundFeature = self.geneTrackRowNums.includes(row)
|
|
12446
|
+
? layoutRow.find((f) => f.feature.type == 'exon' &&
|
|
12216
12447
|
bp >= f.feature.min &&
|
|
12217
|
-
bp <= f.feature.max)
|
|
12218
|
-
|
|
12219
|
-
|
|
12220
|
-
}
|
|
12221
|
-
}
|
|
12222
|
-
else {
|
|
12223
|
-
foundFeature = layoutRow.find((f) => {
|
|
12448
|
+
bp <= f.feature.max) ??
|
|
12449
|
+
layoutRow.find((f) => bp >= f.feature.min && bp <= f.feature.max)
|
|
12450
|
+
: layoutRow.find((f) => {
|
|
12224
12451
|
const { feature } = f;
|
|
12225
12452
|
const featureID = feature.attributes.get('gff_id')?.toString();
|
|
12226
12453
|
const isTranscript = featureTypeOntology.isTypeOf(feature.type, 'transcript');
|
|
@@ -12242,7 +12469,6 @@ function mouseEventsModelIntermediateFactory(pluginManager, configSchema) {
|
|
|
12242
12469
|
}
|
|
12243
12470
|
return false;
|
|
12244
12471
|
});
|
|
12245
|
-
}
|
|
12246
12472
|
if (!foundFeature) {
|
|
12247
12473
|
return mousePosition;
|
|
12248
12474
|
}
|
|
@@ -12566,26 +12792,32 @@ function CreateApolloAnnotation({ annotationFeature, assembly, handleClose, refS
|
|
|
12566
12792
|
const apolloAssembly = apolloSessionModel.apolloDataStore.assemblies.get(assembly.name);
|
|
12567
12793
|
const refSeq = apolloAssembly?.refSeqs.get(refSeqId);
|
|
12568
12794
|
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
|
-
};
|
|
12583
12795
|
React.useEffect(() => {
|
|
12796
|
+
const getDestinationFeatures = () => {
|
|
12797
|
+
const filteredFeatures = [];
|
|
12798
|
+
for (const f of features ?? []) {
|
|
12799
|
+
if (f.min > region.end || f.max < region.start) {
|
|
12800
|
+
continue;
|
|
12801
|
+
}
|
|
12802
|
+
// Destination feature should be of type gene
|
|
12803
|
+
if (featureTypeOntology?.isTypeOf(f.type, 'gene')) {
|
|
12804
|
+
const featureSnapshot = mobxStateTree.getSnapshot(f);
|
|
12805
|
+
filteredFeatures.push(featureSnapshot);
|
|
12806
|
+
}
|
|
12807
|
+
}
|
|
12808
|
+
return filteredFeatures;
|
|
12809
|
+
};
|
|
12584
12810
|
setErrorMessage('');
|
|
12585
|
-
const
|
|
12586
|
-
setDestinationFeatures(
|
|
12587
|
-
setSelectedDestinationFeature(
|
|
12588
|
-
}, [
|
|
12811
|
+
const destinationFeatures = getDestinationFeatures();
|
|
12812
|
+
setDestinationFeatures(destinationFeatures);
|
|
12813
|
+
setSelectedDestinationFeature(destinationFeatures[0]);
|
|
12814
|
+
}, [
|
|
12815
|
+
checkedChildrens,
|
|
12816
|
+
featureTypeOntology,
|
|
12817
|
+
features,
|
|
12818
|
+
parentFeatureChecked,
|
|
12819
|
+
region,
|
|
12820
|
+
]);
|
|
12589
12821
|
const handleParentFeatureCheck = (event) => {
|
|
12590
12822
|
const isChecked = event.target.checked;
|
|
12591
12823
|
setParentFeatureChecked(isChecked);
|
|
@@ -12865,8 +13097,21 @@ function annotationFromPileup(pluggableElement) {
|
|
|
12865
13097
|
getAnnotationFeature(jbrowseFeature, refSeqId) {
|
|
12866
13098
|
const start = jbrowseFeature.get('start');
|
|
12867
13099
|
const end = jbrowseFeature.get('end');
|
|
12868
|
-
const strand = jbrowseFeature.get('strand');
|
|
12869
13100
|
const name = jbrowseFeature.get('name');
|
|
13101
|
+
let strand;
|
|
13102
|
+
const tags = jbrowseFeature.get('tags');
|
|
13103
|
+
const { ts, TS, XS } = tags;
|
|
13104
|
+
for (const tag of [ts, TS, XS]) {
|
|
13105
|
+
if (!strand && tag) {
|
|
13106
|
+
if ([1, '1', '+'].includes(tag)) {
|
|
13107
|
+
strand = 1;
|
|
13108
|
+
}
|
|
13109
|
+
else if ([-1, '-1', '-'].includes(tag)) {
|
|
13110
|
+
strand = -1;
|
|
13111
|
+
}
|
|
13112
|
+
}
|
|
13113
|
+
}
|
|
13114
|
+
strand ??= jbrowseFeature.get('strand');
|
|
12870
13115
|
const cigarData = jbrowseFeature.get('CIGAR');
|
|
12871
13116
|
const ops = parseCigar(cigarData);
|
|
12872
13117
|
let position = start;
|
|
@@ -12882,9 +13127,7 @@ function annotationFromPileup(pluggableElement) {
|
|
|
12882
13127
|
case 'M':
|
|
12883
13128
|
case '=':
|
|
12884
13129
|
case 'X': {
|
|
12885
|
-
|
|
12886
|
-
currentExonStart = position;
|
|
12887
|
-
}
|
|
13130
|
+
currentExonStart ??= position;
|
|
12888
13131
|
position += len;
|
|
12889
13132
|
break;
|
|
12890
13133
|
}
|
|
@@ -13298,7 +13541,7 @@ const LinearApolloDisplay = mobxReact.observer(function LinearApolloDisplay(prop
|
|
|
13298
13541
|
: undefined, menuItems: contextMenuItems })] }))] }));
|
|
13299
13542
|
});
|
|
13300
13543
|
|
|
13301
|
-
const TrackLines = mobxReact.observer(function TrackLines({ model, hrStyle = { margin: 0, top: 0, color: 'black' }, idx
|
|
13544
|
+
const TrackLines = mobxReact.observer(function TrackLines({ model, hrStyle = { margin: 0, top: 0, color: 'black' }, idx, }) {
|
|
13302
13545
|
const { apolloRowHeight, highestRow, showFeatureLabels } = model;
|
|
13303
13546
|
const featureLabelSpacer = showFeatureLabels ? 2 : 1;
|
|
13304
13547
|
return (jsxRuntime.jsx("div", { style: {
|
|
@@ -13370,8 +13613,7 @@ const LinearApolloSixFrameDisplay = mobxReact.observer(function LinearApolloSixF
|
|
|
13370
13613
|
regionNumber: idx,
|
|
13371
13614
|
})?.offsetPx ?? 0) - lgv.offsetPx;
|
|
13372
13615
|
const [feature] = checkResult.featureIds;
|
|
13373
|
-
if (!feature ||
|
|
13374
|
-
!feature.parent ||
|
|
13616
|
+
if (!feature?.parent ||
|
|
13375
13617
|
!looksLikeGene(feature.parent, session)) {
|
|
13376
13618
|
return null;
|
|
13377
13619
|
}
|
|
@@ -13623,18 +13865,15 @@ function addTopLevelMenus(rootModel) {
|
|
|
13623
13865
|
}
|
|
13624
13866
|
}
|
|
13625
13867
|
|
|
13626
|
-
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
13627
13868
|
/* eslint-disable @typescript-eslint/no-confusing-void-expression */
|
|
13628
13869
|
const ApolloJobModel = mobxStateTree.types
|
|
13629
13870
|
.model('JobsManager', {})
|
|
13630
13871
|
.views((self) => ({
|
|
13631
13872
|
get jobStatusWidget() {
|
|
13632
13873
|
const { widgets } = util.getSession(self);
|
|
13633
|
-
|
|
13634
|
-
if (!jobStatusWidget) {
|
|
13874
|
+
const jobStatusWidget = widgets.get('JobsList') ??
|
|
13635
13875
|
// @ts-expect-error: addWidget function not detected on the session
|
|
13636
|
-
|
|
13637
|
-
}
|
|
13876
|
+
util.getSession(self).addWidget('JobsListWidget', 'JobsList');
|
|
13638
13877
|
return jobStatusWidget;
|
|
13639
13878
|
},
|
|
13640
13879
|
}))
|
|
@@ -13913,19 +14152,15 @@ function clientDataStoreFactory(AnnotationFeatureExtended) {
|
|
|
13913
14152
|
continue;
|
|
13914
14153
|
}
|
|
13915
14154
|
const { assemblyName, refName } = region;
|
|
13916
|
-
|
|
13917
|
-
|
|
13918
|
-
assembly = self.assemblies.put({ _id: assemblyName, refSeqs: {} });
|
|
13919
|
-
}
|
|
14155
|
+
const assembly = self.assemblies.get(assemblyName) ??
|
|
14156
|
+
self.assemblies.put({ _id: assemblyName, refSeqs: {} });
|
|
13920
14157
|
const [firstFeature] = features;
|
|
13921
|
-
|
|
13922
|
-
|
|
13923
|
-
ref = assembly.refSeqs.put({
|
|
14158
|
+
const ref = assembly.refSeqs.get(firstFeature.refSeq) ??
|
|
14159
|
+
assembly.refSeqs.put({
|
|
13924
14160
|
_id: firstFeature.refSeq,
|
|
13925
14161
|
name: refName,
|
|
13926
14162
|
features: {},
|
|
13927
14163
|
});
|
|
13928
|
-
}
|
|
13929
14164
|
for (const feature of features) {
|
|
13930
14165
|
if (!ref.features.has(feature._id)) {
|
|
13931
14166
|
ref.features.put(feature);
|
|
@@ -13942,24 +14177,153 @@ function clientDataStoreFactory(AnnotationFeatureExtended) {
|
|
|
13942
14177
|
}
|
|
13943
14178
|
const { refSeq, seq } = yield backendDriver.getSequence(region);
|
|
13944
14179
|
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
|
-
}
|
|
14180
|
+
const assembly = self.assemblies.get(assemblyName) ??
|
|
14181
|
+
self.assemblies.put({ _id: assemblyName, refSeqs: {} });
|
|
14182
|
+
const ref = assembly.refSeqs.get(refSeq) ??
|
|
14183
|
+
assembly.refSeqs.put({ _id: refSeq, name: refName, sequence: [] });
|
|
13957
14184
|
ref.addSequence({ start, stop: end, sequence: seq });
|
|
13958
14185
|
}
|
|
13959
14186
|
}),
|
|
13960
14187
|
}));
|
|
13961
14188
|
}
|
|
13962
14189
|
|
|
14190
|
+
function reassignIds(feature) {
|
|
14191
|
+
const newChildren = {};
|
|
14192
|
+
if (feature.children) {
|
|
14193
|
+
for (const child of Object.values(feature.children)) {
|
|
14194
|
+
const newChild = reassignIds(child);
|
|
14195
|
+
newChildren[newChild._id] = newChild;
|
|
14196
|
+
}
|
|
14197
|
+
}
|
|
14198
|
+
return {
|
|
14199
|
+
...feature,
|
|
14200
|
+
_id: new ObjectID().toHexString(),
|
|
14201
|
+
children: feature.children ? newChildren : undefined,
|
|
14202
|
+
};
|
|
14203
|
+
}
|
|
14204
|
+
function DuplicateFeatureDialog({ assemblyName, changeManager, existingFeature, featureSnapshot, handleClose, }) {
|
|
14205
|
+
const [choice, setChoice] = React.useState('keepExisting');
|
|
14206
|
+
const [errorMessage, setErrorMessage] = React.useState('');
|
|
14207
|
+
const [submitted, setSubmitted] = React.useState(false);
|
|
14208
|
+
const handleSubmit = async () => {
|
|
14209
|
+
setErrorMessage('');
|
|
14210
|
+
setSubmitted(true);
|
|
14211
|
+
try {
|
|
14212
|
+
if (choice === 'useNew') {
|
|
14213
|
+
const deleteChange = new shared.DeleteFeatureChange({
|
|
14214
|
+
typeName: 'DeleteFeatureChange',
|
|
14215
|
+
assembly: assemblyName,
|
|
14216
|
+
changedIds: [existingFeature._id],
|
|
14217
|
+
deletedFeature: existingFeature,
|
|
14218
|
+
});
|
|
14219
|
+
await changeManager.submit(deleteChange);
|
|
14220
|
+
const addChange = new shared.AddFeatureChange({
|
|
14221
|
+
typeName: 'AddFeatureChange',
|
|
14222
|
+
assembly: assemblyName,
|
|
14223
|
+
changedIds: [featureSnapshot._id],
|
|
14224
|
+
addedFeature: featureSnapshot,
|
|
14225
|
+
});
|
|
14226
|
+
await changeManager.submit(addChange);
|
|
14227
|
+
}
|
|
14228
|
+
else if (choice === 'keepBoth') {
|
|
14229
|
+
const newSnapshot = reassignIds(featureSnapshot);
|
|
14230
|
+
const addChange = new shared.AddFeatureChange({
|
|
14231
|
+
typeName: 'AddFeatureChange',
|
|
14232
|
+
assembly: assemblyName,
|
|
14233
|
+
changedIds: [newSnapshot._id],
|
|
14234
|
+
addedFeature: newSnapshot,
|
|
14235
|
+
});
|
|
14236
|
+
await changeManager.submit(addChange);
|
|
14237
|
+
}
|
|
14238
|
+
handleClose();
|
|
14239
|
+
}
|
|
14240
|
+
catch (error) {
|
|
14241
|
+
setErrorMessage(String(error));
|
|
14242
|
+
setSubmitted(false);
|
|
14243
|
+
}
|
|
14244
|
+
};
|
|
14245
|
+
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) => {
|
|
14246
|
+
setChoice(e.target.value);
|
|
14247
|
+
}, 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" })] })] }));
|
|
14248
|
+
}
|
|
14249
|
+
|
|
14250
|
+
function fromUrlSafeBase64(urlSafeBase64) {
|
|
14251
|
+
let base64 = urlSafeBase64.replaceAll('-', '+').replaceAll('_', '/');
|
|
14252
|
+
const pad = base64.length % 4;
|
|
14253
|
+
if (pad) {
|
|
14254
|
+
base64 += '='.repeat(4 - pad);
|
|
14255
|
+
}
|
|
14256
|
+
return base64;
|
|
14257
|
+
}
|
|
14258
|
+
// Base64 decode + decompress
|
|
14259
|
+
async function decompress(encoded) {
|
|
14260
|
+
const binaryString = atob(encoded);
|
|
14261
|
+
// eslint-disable-next-line unicorn/prefer-code-point
|
|
14262
|
+
const bytes = Uint8Array.from(binaryString, (char) => char.charCodeAt(0));
|
|
14263
|
+
const stream = new Blob([bytes]).stream();
|
|
14264
|
+
const decompressionStream = new DecompressionStream('gzip');
|
|
14265
|
+
const decompressedStream = stream.pipeThrough(decompressionStream);
|
|
14266
|
+
const response = new Response(decompressedStream);
|
|
14267
|
+
const decompressed = await response.arrayBuffer();
|
|
14268
|
+
return JSON.parse(new TextDecoder().decode(decompressed));
|
|
14269
|
+
}
|
|
14270
|
+
async function handleApolloFeaturesUrlParam(encodedFeatures, apolloDataStore, assemblyManager, session) {
|
|
14271
|
+
const base64 = fromUrlSafeBase64(encodedFeatures);
|
|
14272
|
+
const decompressed = await decompress(base64);
|
|
14273
|
+
const featuresData = decompressed;
|
|
14274
|
+
for (const [assemblyName, featureSnapshots] of Object.entries(featuresData)) {
|
|
14275
|
+
const backendDriver = apolloDataStore.getBackendDriver(assemblyName);
|
|
14276
|
+
if (!(backendDriver instanceof LocalDriver)) {
|
|
14277
|
+
continue;
|
|
14278
|
+
}
|
|
14279
|
+
const assembly = (await assemblyManager.waitForAssembly(assemblyName));
|
|
14280
|
+
if (!assembly) {
|
|
14281
|
+
throw new Error(`Assembly not found: "${assemblyName}"`);
|
|
14282
|
+
}
|
|
14283
|
+
const { regions } = assembly;
|
|
14284
|
+
if (!regions) {
|
|
14285
|
+
throw new Error(`Assembly not found: "${assemblyName}"`);
|
|
14286
|
+
}
|
|
14287
|
+
const refNames = regions.map((r) => r.refName);
|
|
14288
|
+
const db = await openDb(assemblyName, refNames);
|
|
14289
|
+
for (const featureSnapshot of featureSnapshots) {
|
|
14290
|
+
const storeName = `features-${featureSnapshot.refSeq}`;
|
|
14291
|
+
const existing = (await db.get(storeName, featureSnapshot._id));
|
|
14292
|
+
if (existing === undefined) {
|
|
14293
|
+
const change = new shared.AddFeatureChange({
|
|
14294
|
+
typeName: 'AddFeatureChange',
|
|
14295
|
+
assembly: assemblyName,
|
|
14296
|
+
changedIds: [featureSnapshot._id],
|
|
14297
|
+
addedFeature: featureSnapshot,
|
|
14298
|
+
});
|
|
14299
|
+
await apolloDataStore.changeManager.submit(change);
|
|
14300
|
+
}
|
|
14301
|
+
else {
|
|
14302
|
+
// get rid of undefined values in JSON
|
|
14303
|
+
// eslint-disable-next-line unicorn/prefer-structured-clone
|
|
14304
|
+
const existingFeature = JSON.parse(JSON.stringify(existing));
|
|
14305
|
+
if (!equal(featureSnapshot, existingFeature)) {
|
|
14306
|
+
await new Promise((resolve) => {
|
|
14307
|
+
session.queueDialog((doneCallback) => [
|
|
14308
|
+
DuplicateFeatureDialog,
|
|
14309
|
+
{
|
|
14310
|
+
featureSnapshot,
|
|
14311
|
+
existingFeature,
|
|
14312
|
+
assemblyName,
|
|
14313
|
+
changeManager: apolloDataStore.changeManager,
|
|
14314
|
+
handleClose: () => {
|
|
14315
|
+
doneCallback();
|
|
14316
|
+
resolve();
|
|
14317
|
+
},
|
|
14318
|
+
},
|
|
14319
|
+
]);
|
|
14320
|
+
});
|
|
14321
|
+
}
|
|
14322
|
+
}
|
|
14323
|
+
}
|
|
14324
|
+
}
|
|
14325
|
+
}
|
|
14326
|
+
|
|
13963
14327
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
13964
14328
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
13965
14329
|
function extendSession(pluginManager, sessionModel) {
|
|
@@ -14045,8 +14409,7 @@ function extendSession(pluginManager, sessionModel) {
|
|
|
14045
14409
|
for (const block of dynamicBlocks.contentBlocks) {
|
|
14046
14410
|
const { assemblyName, end, refName, start } = block;
|
|
14047
14411
|
const assembly = self.apolloDataStore.assemblies.get(assemblyName);
|
|
14048
|
-
if (assembly
|
|
14049
|
-
assembly.backendDriverType === 'CollaborationServerDriver') {
|
|
14412
|
+
if (assembly?.backendDriverType === 'CollaborationServerDriver') {
|
|
14050
14413
|
locations.push({ assemblyName, refName, start, end });
|
|
14051
14414
|
}
|
|
14052
14415
|
}
|
|
@@ -14105,8 +14468,8 @@ function extendSession(pluginManager, sessionModel) {
|
|
|
14105
14468
|
for (const block of dynamicBlocks.contentBlocks) {
|
|
14106
14469
|
const { assemblyName, end, refName, start } = block;
|
|
14107
14470
|
const assembly = self.apolloDataStore.assemblies.get(assemblyName);
|
|
14108
|
-
if (assembly
|
|
14109
|
-
|
|
14471
|
+
if (assembly?.backendDriverType ===
|
|
14472
|
+
'CollaborationServerDriver') {
|
|
14110
14473
|
locations.push({ assemblyName, refName, start, end });
|
|
14111
14474
|
}
|
|
14112
14475
|
}
|
|
@@ -14226,6 +14589,37 @@ function extendSession(pluginManager, sessionModel) {
|
|
|
14226
14589
|
beforeDestroy() {
|
|
14227
14590
|
self.abortController.abort(new DOMException('Clean up Apollo session', 'AbortError'));
|
|
14228
14591
|
},
|
|
14592
|
+
}))
|
|
14593
|
+
.actions((self) => ({
|
|
14594
|
+
async afterCreate() {
|
|
14595
|
+
if (util.isElectron) {
|
|
14596
|
+
return;
|
|
14597
|
+
}
|
|
14598
|
+
const url = new URL(globalThis.location.href);
|
|
14599
|
+
const apolloFeatures = url.searchParams.get('apolloFeatures');
|
|
14600
|
+
if (!apolloFeatures) {
|
|
14601
|
+
return;
|
|
14602
|
+
}
|
|
14603
|
+
const { assemblyManager } = self;
|
|
14604
|
+
await handleApolloFeaturesUrlParam(apolloFeatures, self.apolloDataStore, assemblyManager, self);
|
|
14605
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
14606
|
+
const updatedURL = new URL(globalThis.location.href);
|
|
14607
|
+
updatedURL.searchParams.delete('apolloFeatures');
|
|
14608
|
+
globalThis.history.replaceState(null, '', updatedURL.toString());
|
|
14609
|
+
},
|
|
14610
|
+
afterAttach() {
|
|
14611
|
+
mobxStateTree.addDisposer(self, mobx.autorun((reaction) => {
|
|
14612
|
+
const { focusedViewId, activeWidgets } = self;
|
|
14613
|
+
if (!(focusedViewId && activeWidgets)) {
|
|
14614
|
+
return;
|
|
14615
|
+
}
|
|
14616
|
+
const trackSelector = activeWidgets.get('hierarchicalTrackSelector');
|
|
14617
|
+
// @ts-expect-error Don't have type for track selector
|
|
14618
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
14619
|
+
trackSelector?.setView(focusedViewId);
|
|
14620
|
+
reaction.dispose();
|
|
14621
|
+
}));
|
|
14622
|
+
},
|
|
14229
14623
|
}))
|
|
14230
14624
|
.views((self) => {
|
|
14231
14625
|
const superTrackActions = self
|
|
@@ -14357,7 +14751,6 @@ function extendSession(pluginManager, sessionModel) {
|
|
|
14357
14751
|
}
|
|
14358
14752
|
|
|
14359
14753
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
14360
|
-
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
14361
14754
|
/* eslint-disable @typescript-eslint/no-misused-promises */
|
|
14362
14755
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
14363
14756
|
function isApolloMessageData(data) {
|
|
@@ -14545,10 +14938,10 @@ class ApolloPlugin extends Plugin {
|
|
|
14545
14938
|
});
|
|
14546
14939
|
if (!inWebWorker) {
|
|
14547
14940
|
pluginManager.addToExtensionPoint('Core-extendWorker', (handle) => {
|
|
14548
|
-
if (!('on' in handle && handle.on)) {
|
|
14941
|
+
if (!('on' in handle.client && handle.client.on)) {
|
|
14549
14942
|
return handle;
|
|
14550
14943
|
}
|
|
14551
|
-
handle.on('apollo', async (event) => {
|
|
14944
|
+
handle.client.on('apollo', async (event) => {
|
|
14552
14945
|
if (!isApolloMessageData(event)) {
|
|
14553
14946
|
return;
|
|
14554
14947
|
}
|
|
@@ -14566,7 +14959,7 @@ class ApolloPlugin extends Plugin {
|
|
|
14566
14959
|
break;
|
|
14567
14960
|
}
|
|
14568
14961
|
const { seq: sequence } = await backendDriver.getSequence(region);
|
|
14569
|
-
handle.
|
|
14962
|
+
handle.worker.postMessage({
|
|
14570
14963
|
apollo,
|
|
14571
14964
|
messageId,
|
|
14572
14965
|
sequence,
|
|
@@ -14584,7 +14977,7 @@ class ApolloPlugin extends Plugin {
|
|
|
14584
14977
|
break;
|
|
14585
14978
|
}
|
|
14586
14979
|
const regions = await backendDriver.getRegions(assembly);
|
|
14587
|
-
handle.
|
|
14980
|
+
handle.worker.postMessage({
|
|
14588
14981
|
apollo,
|
|
14589
14982
|
messageId,
|
|
14590
14983
|
regions,
|
|
@@ -14602,7 +14995,7 @@ class ApolloPlugin extends Plugin {
|
|
|
14602
14995
|
break;
|
|
14603
14996
|
}
|
|
14604
14997
|
const refNameAliases = await backendDriver.getRefNameAliases(assembly);
|
|
14605
|
-
handle.
|
|
14998
|
+
handle.worker.postMessage({
|
|
14606
14999
|
apollo,
|
|
14607
15000
|
messageId,
|
|
14608
15001
|
refNameAliases,
|