@opencloning/ui 1.0.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/CHANGELOG.md +18 -0
- package/package.json +37 -0
- package/src/components/AppAlerts.jsx +19 -0
- package/src/components/CloningHistory.jsx +40 -0
- package/src/components/DataModelDisplayer.jsx +30 -0
- package/src/components/DescriptionEditor.jsx +68 -0
- package/src/components/DownloadCloningStrategyDialog.jsx +84 -0
- package/src/components/DownloadSequenceFileDialog.jsx +90 -0
- package/src/components/DragAndDropCloningHistoryWrapper.jsx +39 -0
- package/src/components/DraggableDialogPaper.jsx +16 -0
- package/src/components/EditSequenceNameDialog.jsx +90 -0
- package/src/components/ExternalServicesStatusCheck.jsx +92 -0
- package/src/components/HistoryLoadedDialog.jsx +49 -0
- package/src/components/LoadCloningHistoryWrapper.jsx +166 -0
- package/src/components/MainSequenceCheckBox.jsx +83 -0
- package/src/components/MainSequenceEditor.jsx +165 -0
- package/src/components/NetworkNode.jsx +159 -0
- package/src/components/NetworkTree.css +127 -0
- package/src/components/ObjectTable.jsx +24 -0
- package/src/components/OpenCloning.jsx +102 -0
- package/src/components/OverhangsDisplay.jsx +25 -0
- package/src/components/SequenceEditor.jsx +120 -0
- package/src/components/SequenceTab.jsx +14 -0
- package/src/components/TemplateSequence.jsx +38 -0
- package/src/components/annotation/PlannotateAnnotationReport.jsx +33 -0
- package/src/components/annotation/useUpdateAnnotationInMainSequence.js +39 -0
- package/src/components/assembler/AssemblePartWidget.jsx +252 -0
- package/src/components/assembler/Assembler.jsx +273 -0
- package/src/components/assembler/AssemblerPart.jsx +99 -0
- package/src/components/assembler/StopIcon.jsx +34 -0
- package/src/components/assembler/assembler_data2.json +50 -0
- package/src/components/assembler/assembly_component.module.css +81 -0
- package/src/components/assembler/moclo.json +110 -0
- package/src/components/assembler/sbol_visual_glyphs/LICENSE.html +21 -0
- package/src/components/assembler/sbol_visual_glyphs/assembly-scar.svg +63 -0
- package/src/components/assembler/sbol_visual_glyphs/cds-stop.svg +85 -0
- package/src/components/assembler/sbol_visual_glyphs/cds.svg +60 -0
- package/src/components/assembler/sbol_visual_glyphs/chromosomal-locus.svg +78 -0
- package/src/components/assembler/sbol_visual_glyphs/engineered-region.svg +56 -0
- package/src/components/assembler/sbol_visual_glyphs/five-prime-sticky-restriction-site.svg +56 -0
- package/src/components/assembler/sbol_visual_glyphs/origin-of-replication.svg +57 -0
- package/src/components/assembler/sbol_visual_glyphs/primer-binding-site.svg +59 -0
- package/src/components/assembler/sbol_visual_glyphs/promoter.svg +60 -0
- package/src/components/assembler/sbol_visual_glyphs/ribosome-entry-site.svg +56 -0
- package/src/components/assembler/sbol_visual_glyphs/specific-recombination-site.svg +59 -0
- package/src/components/assembler/sbol_visual_glyphs/terminator.svg +60 -0
- package/src/components/assembler/sbol_visual_glyphs/three-prime-sticky-restriction-site.svg +56 -0
- package/src/components/assembler/sbol_visual_glyphs.js +36 -0
- package/src/components/assembler/useAssembler.js +71 -0
- package/src/components/dummy/DummyInterface.js +41 -0
- package/src/components/dummy/GetSequenceFileAndDatabaseIdComponent.jsx +59 -0
- package/src/components/eLabFTW/ELabFTWCategorySelect.cy.jsx +86 -0
- package/src/components/eLabFTW/ELabFTWCategorySelect.jsx +43 -0
- package/src/components/eLabFTW/ELabFTWFileSelect.cy.jsx +43 -0
- package/src/components/eLabFTW/ELabFTWFileSelect.jsx +29 -0
- package/src/components/eLabFTW/ELabFTWResourceSelect.cy.jsx +107 -0
- package/src/components/eLabFTW/ELabFTWResourceSelect.jsx +23 -0
- package/src/components/eLabFTW/GetPrimerComponent.cy.jsx +261 -0
- package/src/components/eLabFTW/GetPrimerComponent.jsx +55 -0
- package/src/components/eLabFTW/GetSequenceFileAndDatabaseIdComponent.cy.jsx +184 -0
- package/src/components/eLabFTW/GetSequenceFileAndDatabaseIdComponent.jsx +62 -0
- package/src/components/eLabFTW/LoadHistoryComponent.cy.jsx +235 -0
- package/src/components/eLabFTW/LoadHistoryComponent.jsx +51 -0
- package/src/components/eLabFTW/PrimersNotInDatabaseComponent.cy.jsx +159 -0
- package/src/components/eLabFTW/PrimersNotInDatabaseComponent.jsx +54 -0
- package/src/components/eLabFTW/SubmitToDatabaseComponent.cy.jsx +185 -0
- package/src/components/eLabFTW/SubmitToDatabaseComponent.jsx +51 -0
- package/src/components/eLabFTW/common.js +26 -0
- package/src/components/eLabFTW/eLabFTWInterface.js +294 -0
- package/src/components/eLabFTW/eLabFTWInterface.test.js +839 -0
- package/src/components/eLabFTW/envValues.js +7 -0
- package/src/components/eLabFTW/utils.js +39 -0
- package/src/components/form/CustomFormHelperText.jsx +10 -0
- package/src/components/form/EnzymeMultiSelect.cy.jsx +61 -0
- package/src/components/form/EnzymeMultiSelect.jsx +34 -0
- package/src/components/form/GetRequestMultiSelect.jsx +107 -0
- package/src/components/form/LabelWithTooltip.jsx +16 -0
- package/src/components/form/PostRequestSelect.cy.jsx +70 -0
- package/src/components/form/PostRequestSelect.jsx +86 -0
- package/src/components/form/RequestStatusWrapper.jsx +17 -0
- package/src/components/form/RetryAlert.jsx +20 -0
- package/src/components/form/ServerErrorMessage.jsx +10 -0
- package/src/components/form/SubmitButtonBackendAPI.jsx +15 -0
- package/src/components/form/SubmitToDatabaseDialog.jsx +133 -0
- package/src/components/form/TextFieldValidate.jsx +67 -0
- package/src/components/form/ValidatedTextField.jsx +33 -0
- package/src/components/form/intermediates_disclaimer.svg +181 -0
- package/src/components/navigation/ButtonWithMenu.jsx +43 -0
- package/src/components/navigation/CustomTab.jsx +14 -0
- package/src/components/navigation/FeedbackDialog.jsx +34 -0
- package/src/components/navigation/GithubCornerRight.jsx +29 -0
- package/src/components/navigation/MainAppBar.css +26 -0
- package/src/components/navigation/MainAppBar.jsx +205 -0
- package/src/components/navigation/SelectExampleDialog.jsx +69 -0
- package/src/components/navigation/SelectTemplateDialog.jsx +107 -0
- package/src/components/navigation/TabPanel.jsx +28 -0
- package/src/components/navigation/VersionDialog.jsx +33 -0
- package/src/components/primers/CreatePrimerFromSequenceForm.jsx +42 -0
- package/src/components/primers/DownloadPrimersButton.jsx +104 -0
- package/src/components/primers/PrimerForm.css +14 -0
- package/src/components/primers/PrimerForm.jsx +107 -0
- package/src/components/primers/PrimerList.css +46 -0
- package/src/components/primers/PrimerList.cy.jsx +95 -0
- package/src/components/primers/PrimerList.jsx +126 -0
- package/src/components/primers/PrimerTableRow.cy.jsx +57 -0
- package/src/components/primers/PrimerTableRow.jsx +84 -0
- package/src/components/primers/SelectPrimerForm.jsx +66 -0
- package/src/components/primers/import_primers/ImportPrimersButton.jsx +101 -0
- package/src/components/primers/import_primers/ImportPrimersTable.jsx +51 -0
- package/src/components/primers/import_primers/PrimerDatabaseImportForm.jsx +107 -0
- package/src/components/primers/import_primers/styles.css +60 -0
- package/src/components/primers/primer_design/SequenceTabComponents/CollapsableLabel.jsx +31 -0
- package/src/components/primers/primer_design/SequenceTabComponents/GatewayRoiSelect.jsx +164 -0
- package/src/components/primers/primer_design/SequenceTabComponents/OrientationPicker.jsx +37 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignContext.jsx +369 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignEBIC.jsx +24 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignForm.jsx +29 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignGatewayBP.jsx +36 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignGibsonAssembly.jsx +26 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignHomologousRecombination.jsx +32 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignRestriction.jsx +25 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignSimplePair.jsx +25 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignStepper.jsx +53 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesigner.jsx +80 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerResultForm.jsx +49 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerSettingsForm.jsx +68 -0
- package/src/components/primers/primer_design/SequenceTabComponents/PrimerSpacerForm.jsx +84 -0
- package/src/components/primers/primer_design/SequenceTabComponents/RestrictionSpacerForm.jsx +85 -0
- package/src/components/primers/primer_design/SequenceTabComponents/StepNavigation.jsx +48 -0
- package/src/components/primers/primer_design/SequenceTabComponents/TabPanelEBICSettings.jsx +216 -0
- package/src/components/primers/primer_design/SequenceTabComponents/TabPanelResults.jsx +42 -0
- package/src/components/primers/primer_design/SequenceTabComponents/TabPanelSelectRoi.jsx +61 -0
- package/src/components/primers/primer_design/SequenceTabComponents/TabPannelSettings.jsx +59 -0
- package/src/components/primers/primer_design/SequenceTabComponents/primerDesignMinimalValues.json +5 -0
- package/src/components/primers/primer_design/SequenceTabComponents/useEBICPrimerDesignSettings.js +31 -0
- package/src/components/primers/primer_design/SequenceTabComponents/useEnzymePrimerDesignSettings.js +57 -0
- package/src/components/primers/primer_design/SequenceTabComponents/useGatewayPrimerDesignSettings.js +18 -0
- package/src/components/primers/primer_design/SequenceTabComponents/usePrimerDesignSettings.js +31 -0
- package/src/components/primers/primer_design/SourceComponents/PrimerDesignGatewayBP.jsx +88 -0
- package/src/components/primers/primer_design/SourceComponents/PrimerDesignGibsonAssembly.jsx +65 -0
- package/src/components/primers/primer_design/SourceComponents/PrimerDesignHomologousRecombination.jsx +84 -0
- package/src/components/primers/primer_design/SourceComponents/PrimerDesignSourceForm.jsx +74 -0
- package/src/components/primers/primer_design/common/NoAttPSitesError.jsx +31 -0
- package/src/components/primers/primer_details/PCRTable.cy.jsx +51 -0
- package/src/components/primers/primer_details/PCRTable.jsx +35 -0
- package/src/components/primers/primer_details/Primer3Figure.jsx +25 -0
- package/src/components/primers/primer_details/PrimerDetailsTds.jsx +39 -0
- package/src/components/primers/primer_details/PrimerInfoIcon.cy.jsx +137 -0
- package/src/components/primers/primer_details/PrimerInfoIcon.jsx +132 -0
- package/src/components/primers/primer_details/TableSection.jsx +17 -0
- package/src/components/primers/primer_details/primerDetailsFormatting.js +3 -0
- package/src/components/primers/primer_details/useMultiplePrimerDetails.js +29 -0
- package/src/components/primers/primer_details/usePCRDetails.js +47 -0
- package/src/components/primers/primer_details/usePrimerDetailsEndpoints.js +49 -0
- package/src/components/primers/primer_details/useSinglePrimerSequenceDetails.js +25 -0
- package/src/components/primers/primersToTabularFile.js +49 -0
- package/src/components/primers/primersToTabularFile.test.js +108 -0
- package/src/components/settings/SettingsTab.cy.jsx +267 -0
- package/src/components/settings/SettingsTab.jsx +170 -0
- package/src/components/sources/AssemblyPlanDisplayer.cy.jsx +22 -0
- package/src/components/sources/AssemblyPlanDisplayer.jsx +27 -0
- package/src/components/sources/CollectionSource.jsx +97 -0
- package/src/components/sources/FinishedSource.jsx +397 -0
- package/src/components/sources/KnownSourceErrors.jsx +50 -0
- package/src/components/sources/MultipleInputsSelector.jsx +63 -0
- package/src/components/sources/MultipleOutputsSelector.jsx +63 -0
- package/src/components/sources/NewSourceBox.jsx +37 -0
- package/src/components/sources/PCRUnitForm.jsx +102 -0
- package/src/components/sources/SingleInputSelector.jsx +36 -0
- package/src/components/sources/Source.jsx +125 -0
- package/src/components/sources/SourceAnnotation.jsx +44 -0
- package/src/components/sources/SourceAssembly.jsx +201 -0
- package/src/components/sources/SourceBox.css +18 -0
- package/src/components/sources/SourceBox.jsx +60 -0
- package/src/components/sources/SourceCopySequence.jsx +38 -0
- package/src/components/sources/SourceDatabase.jsx +28 -0
- package/src/components/sources/SourceFile.jsx +188 -0
- package/src/components/sources/SourceGenomeRegion.cy.jsx +131 -0
- package/src/components/sources/SourceGenomeRegion.jsx +486 -0
- package/src/components/sources/SourceHomologousRecombination.jsx +125 -0
- package/src/components/sources/SourceKnownGenomeRegion.jsx +60 -0
- package/src/components/sources/SourceManuallyTyped.jsx +116 -0
- package/src/components/sources/SourcePCRorHybridization.jsx +165 -0
- package/src/components/sources/SourcePolymeraseExtension.jsx +44 -0
- package/src/components/sources/SourceRepositoryId.jsx +409 -0
- package/src/components/sources/SourceRestriction.jsx +41 -0
- package/src/components/sources/SourceReverseComplement.jsx +33 -0
- package/src/components/sources/SourceTypeSelector.jsx +94 -0
- package/src/components/sources/SubSequenceDisplayer.jsx +70 -0
- package/src/components/sources/VerifyDeleteDialog.jsx +23 -0
- package/src/components/sources/repositoryMetadata.js +14 -0
- package/src/components/verification/LoadFromDatabaseButton.jsx +90 -0
- package/src/components/verification/SequencingFileRow.jsx +34 -0
- package/src/components/verification/VerificationFileDialog.cy.jsx +176 -0
- package/src/components/verification/VerificationFileDialog.jsx +248 -0
- package/src/config/defaultMainEditorProps.js +44 -0
- package/src/hooks/useAlerts.js +16 -0
- package/src/hooks/useBackendAPI.js +51 -0
- package/src/hooks/useBackendRoute.js +22 -0
- package/src/hooks/useDatabase.js +18 -0
- package/src/hooks/useDragAndDropFile.js +31 -0
- package/src/hooks/useGatewaySites.js +40 -0
- package/src/hooks/useHttpClient.js +12 -0
- package/src/hooks/useLoadDatabaseFile.js +108 -0
- package/src/hooks/useStoreEditor.js +101 -0
- package/src/hooks/useValidateState.js +43 -0
- package/vitest.config.js +18 -0
package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignGibsonAssembly.jsx
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { PrimerDesignProvider } from './PrimerDesignContext';
|
|
3
|
+
import PrimerDesignForm from './PrimerDesignForm';
|
|
4
|
+
import usePrimerDesignSettings from './usePrimerDesignSettings';
|
|
5
|
+
import { getPcrTemplateSequenceId } from '@opencloning/store/cloning_utils';
|
|
6
|
+
|
|
7
|
+
export default function PrimerDesignGibsonAssembly({ pcrSources }) {
|
|
8
|
+
const templateSequencesIds = React.useMemo(() => pcrSources.map(getPcrTemplateSequenceId), [pcrSources]);
|
|
9
|
+
const steps = React.useMemo(() => [
|
|
10
|
+
...templateSequencesIds.map((id, index) => (
|
|
11
|
+
{ label: `Seq ${id}`, selectOrientation: true }
|
|
12
|
+
)),
|
|
13
|
+
], [pcrSources]);
|
|
14
|
+
|
|
15
|
+
const primerDesignSettings = usePrimerDesignSettings({ homology_length: 35, minimal_hybridization_length: 20, target_tm: 55 });
|
|
16
|
+
return (
|
|
17
|
+
<PrimerDesignProvider
|
|
18
|
+
designType="gibson_assembly"
|
|
19
|
+
sequenceIds={templateSequencesIds}
|
|
20
|
+
primerDesignSettings={primerDesignSettings}
|
|
21
|
+
steps={steps}
|
|
22
|
+
>
|
|
23
|
+
<PrimerDesignForm />
|
|
24
|
+
</PrimerDesignProvider>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { PrimerDesignProvider } from './PrimerDesignContext';
|
|
3
|
+
import PrimerDesignForm from './PrimerDesignForm';
|
|
4
|
+
import usePrimerDesignSettings from './usePrimerDesignSettings';
|
|
5
|
+
import { getPcrTemplateSequenceId } from '@opencloning/store/cloning_utils';
|
|
6
|
+
|
|
7
|
+
export default function PrimerDesignHomologousRecombination({ homologousRecombinationTargetId, pcrSource }) {
|
|
8
|
+
const templateSequenceId = getPcrTemplateSequenceId(pcrSource);
|
|
9
|
+
const sequenceIds = React.useMemo(() => [templateSequenceId, homologousRecombinationTargetId], [templateSequenceId, homologousRecombinationTargetId]);
|
|
10
|
+
const steps = React.useMemo(() => [
|
|
11
|
+
{ label: 'Amplified region',
|
|
12
|
+
description: `Select the fragment of sequence ${templateSequenceId} to be amplified in the editor and click "Choose region"`,
|
|
13
|
+
inputLabel: `Amplified region (sequence ${templateSequenceId})` },
|
|
14
|
+
{ label: 'Replaced region',
|
|
15
|
+
description: 'Select the single position (insertion) or region (replacement) where recombination will introduce the amplified fragment',
|
|
16
|
+
inputLabel: `Replaced region (sequence ${homologousRecombinationTargetId})`,
|
|
17
|
+
allowSinglePosition: true,
|
|
18
|
+
},
|
|
19
|
+
], [templateSequenceId, homologousRecombinationTargetId]);
|
|
20
|
+
|
|
21
|
+
const primerDesignSettings = usePrimerDesignSettings({ homology_length: 80, minimal_hybridization_length: 20, target_tm: 55 });
|
|
22
|
+
return (
|
|
23
|
+
<PrimerDesignProvider
|
|
24
|
+
designType="homologous_recombination"
|
|
25
|
+
sequenceIds={sequenceIds}
|
|
26
|
+
primerDesignSettings={primerDesignSettings}
|
|
27
|
+
steps={steps}
|
|
28
|
+
>
|
|
29
|
+
<PrimerDesignForm />
|
|
30
|
+
</PrimerDesignProvider>
|
|
31
|
+
);
|
|
32
|
+
}
|
package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignRestriction.jsx
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { PrimerDesignProvider } from './PrimerDesignContext';
|
|
3
|
+
import PrimerDesignForm from './PrimerDesignForm';
|
|
4
|
+
import useEnzymePrimerDesignSettings from './useEnzymePrimerDesignSettings';
|
|
5
|
+
import { getPcrTemplateSequenceId } from '@opencloning/store/cloning_utils';
|
|
6
|
+
|
|
7
|
+
function PrimerDesignRestriction({ pcrSource }) {
|
|
8
|
+
const templateSequenceId = getPcrTemplateSequenceId(pcrSource);
|
|
9
|
+
const sequenceIds = React.useMemo(() => [templateSequenceId], [templateSequenceId]);
|
|
10
|
+
|
|
11
|
+
const steps = React.useMemo(() => [
|
|
12
|
+
{ label: 'Amplified region' },
|
|
13
|
+
], []);
|
|
14
|
+
|
|
15
|
+
const primerDesignSettings = useEnzymePrimerDesignSettings({ homology_length: null, minimal_hybridization_length: 20, target_tm: 55 });
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<PrimerDesignProvider designType="restriction_ligation" sequenceIds={sequenceIds} primerDesignSettings={primerDesignSettings} steps={steps}>
|
|
19
|
+
<PrimerDesignForm />
|
|
20
|
+
</PrimerDesignProvider>
|
|
21
|
+
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default PrimerDesignRestriction;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { PrimerDesignProvider } from './PrimerDesignContext';
|
|
3
|
+
import PrimerDesignForm from './PrimerDesignForm';
|
|
4
|
+
import usePrimerDesignSettings from './usePrimerDesignSettings';
|
|
5
|
+
import { getPcrTemplateSequenceId } from '@opencloning/store/cloning_utils';
|
|
6
|
+
|
|
7
|
+
function PrimerDesignSimplePair({ pcrSource }) {
|
|
8
|
+
const templateSequenceId = getPcrTemplateSequenceId(pcrSource);
|
|
9
|
+
const sequenceIds = React.useMemo(() => [templateSequenceId], [templateSequenceId]);
|
|
10
|
+
|
|
11
|
+
const steps = React.useMemo(() => [
|
|
12
|
+
{ label: 'Amplified region' },
|
|
13
|
+
], []);
|
|
14
|
+
|
|
15
|
+
const primerDesignSettings = usePrimerDesignSettings({ homology_length: null, minimal_hybridization_length: 20, target_tm: 55 });
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<PrimerDesignProvider designType="simple_pair" sequenceIds={sequenceIds} primerDesignSettings={primerDesignSettings} steps={steps}>
|
|
19
|
+
<PrimerDesignForm />
|
|
20
|
+
</PrimerDesignProvider>
|
|
21
|
+
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default PrimerDesignSimplePair;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Step, StepButton, Stepper } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { usePrimerDesign } from './PrimerDesignContext';
|
|
4
|
+
|
|
5
|
+
function PrimerDesignStepper() {
|
|
6
|
+
const { steps, selectedTab, onTabChange, rois, primers, submissionPreventedMessage } = usePrimerDesign();
|
|
7
|
+
|
|
8
|
+
const allSteps = [
|
|
9
|
+
...steps,
|
|
10
|
+
{ label: 'Other settings' },
|
|
11
|
+
{ label: 'Results' },
|
|
12
|
+
];
|
|
13
|
+
const completedSteps = [
|
|
14
|
+
...rois.map((roi) => roi !== null),
|
|
15
|
+
// We check submissionPreventedMessage, because you could design primers and then come back
|
|
16
|
+
// and have bad settings
|
|
17
|
+
primers.length > 0 && submissionPreventedMessage === '',
|
|
18
|
+
false,
|
|
19
|
+
];
|
|
20
|
+
const disabledSteps = [
|
|
21
|
+
...rois.map(() => false),
|
|
22
|
+
rois.some((roi) => roi === null),
|
|
23
|
+
primers.length === 0,
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Stepper
|
|
28
|
+
nonLinear
|
|
29
|
+
activeStep={selectedTab}
|
|
30
|
+
sx={{
|
|
31
|
+
pt: 1,
|
|
32
|
+
pb: 1,
|
|
33
|
+
'& .Mui-active.MuiStepLabel-label': {
|
|
34
|
+
fontWeight: 'bold',
|
|
35
|
+
color: 'primary.main',
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
{allSteps.map(({ label }, index) => (
|
|
41
|
+
|
|
42
|
+
<Step key={label} completed={!disabledSteps[index] && completedSteps[index]}>
|
|
43
|
+
<StepButton disabled={disabledSteps[index]} onClick={() => onTabChange(null, index)}>
|
|
44
|
+
{label}
|
|
45
|
+
</StepButton>
|
|
46
|
+
</Step>
|
|
47
|
+
))}
|
|
48
|
+
|
|
49
|
+
</Stepper>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default PrimerDesignStepper;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
3
|
+
import { isEqual } from 'lodash-es';
|
|
4
|
+
import { Box, Button } from '@mui/material';
|
|
5
|
+
import { getPcrTemplateSequenceId, getPrimerDesignObject } from '@opencloning/store/cloning_utils';
|
|
6
|
+
import PrimerDesignHomologousRecombination from './PrimerDesignHomologousRecombination';
|
|
7
|
+
import useStoreEditor from '../../../../hooks/useStoreEditor';
|
|
8
|
+
import { cloningActions } from '@opencloning/store/cloning';
|
|
9
|
+
import PrimerDesignGibsonAssembly from './PrimerDesignGibsonAssembly';
|
|
10
|
+
import PrimerDesignSimplePair from './PrimerDesignSimplePair';
|
|
11
|
+
import PrimerDesignGatewayBP from './PrimerDesignGatewayBP';
|
|
12
|
+
import PrimerDesignEBIC from './PrimerDesignEBIC';
|
|
13
|
+
import PrimerDesignRestriction from './PrimerDesignRestriction';
|
|
14
|
+
|
|
15
|
+
function PrimerDesigner() {
|
|
16
|
+
const { updateStoreEditor } = useStoreEditor();
|
|
17
|
+
const dispatch = useDispatch();
|
|
18
|
+
const { setMainSequenceId } = cloningActions;
|
|
19
|
+
|
|
20
|
+
const { finalSource, otherInputIds, pcrSources, outputSequences } = useSelector((state) => getPrimerDesignObject(state.cloning), isEqual);
|
|
21
|
+
|
|
22
|
+
const mainSequenceId = useSelector((state) => state.cloning.mainSequenceId);
|
|
23
|
+
|
|
24
|
+
const templateSequencesIds = pcrSources.map((pcrSource) => getPcrTemplateSequenceId(pcrSource));
|
|
25
|
+
const openPrimerDesigner = () => {
|
|
26
|
+
updateStoreEditor('mainEditor', templateSequencesIds[0]);
|
|
27
|
+
dispatch(setMainSequenceId(templateSequencesIds[0]));
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Nothing to design
|
|
31
|
+
if (templateSequencesIds.length === 0) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// The network supports design of primers, but the current main sequence is not part of it
|
|
36
|
+
const showPrimerDesigner = [...templateSequencesIds, ...otherInputIds].includes(mainSequenceId);
|
|
37
|
+
|
|
38
|
+
let component = null;
|
|
39
|
+
// Check conditions for different types of primer design
|
|
40
|
+
if (finalSource === null && pcrSources.length === 1 && outputSequences[0].primer_design === 'restriction_ligation') {
|
|
41
|
+
component = <PrimerDesignRestriction pcrSource={pcrSources[0]} />;
|
|
42
|
+
}
|
|
43
|
+
if (finalSource === null && pcrSources.length === 1 && outputSequences[0].primer_design === 'simple_pair') {
|
|
44
|
+
component = <PrimerDesignSimplePair pcrSource={pcrSources[0]} />;
|
|
45
|
+
}
|
|
46
|
+
if (finalSource?.type === 'GibsonAssemblySource' || finalSource?.type === 'InFusionSource' || finalSource?.type === 'InVivoAssemblySource' || finalSource?.type === 'CreLoxRecombinationSource') {
|
|
47
|
+
component = <PrimerDesignGibsonAssembly pcrSources={pcrSources} />;
|
|
48
|
+
}
|
|
49
|
+
if (finalSource?.type === 'HomologousRecombinationSource' && otherInputIds.length === 1 && pcrSources.length === 1) {
|
|
50
|
+
component = (
|
|
51
|
+
<PrimerDesignHomologousRecombination
|
|
52
|
+
homologousRecombinationTargetId={otherInputIds[0]}
|
|
53
|
+
pcrSource={pcrSources[0]}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
if (finalSource?.type === 'GatewaySource' && otherInputIds.length === 1 && pcrSources.length === 1 && outputSequences[0].primer_design === 'gateway_bp') {
|
|
58
|
+
component = <PrimerDesignGatewayBP donorVectorId={otherInputIds[0]} pcrSource={pcrSources[0]} />;
|
|
59
|
+
}
|
|
60
|
+
if (finalSource?.type === 'RestrictionAndLigationSource' && outputSequences.every((outputSequence) => outputSequence.primer_design === 'ebic')) {
|
|
61
|
+
component = <PrimerDesignEBIC pcrSources={pcrSources} />;
|
|
62
|
+
}
|
|
63
|
+
return (
|
|
64
|
+
<>
|
|
65
|
+
{!showPrimerDesigner && (
|
|
66
|
+
<div>
|
|
67
|
+
<Button sx={{ mb: 4 }} variant="contained" color="success" onClick={openPrimerDesigner}>Open primer designer</Button>
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
<Box className="primer-design" sx={{ display: showPrimerDesigner ? 'auto' : 'none', width: '60%', minWidth: '600px', margin: 'auto', border: 1, borderRadius: 2, overflow: 'hidden', borderColor: 'primary.main', marginBottom: 5 }}>
|
|
71
|
+
<Box sx={{ margin: 'auto', display: 'flex', height: 'auto', borderBottom: 2, borderColor: 'primary.main', backgroundColor: 'primary.main' }}>
|
|
72
|
+
<Box component="h2" sx={{ margin: 'auto', py: 1, color: 'white' }}>Primer designer</Box>
|
|
73
|
+
</Box>
|
|
74
|
+
{component}
|
|
75
|
+
</Box>
|
|
76
|
+
</>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export default React.memo(PrimerDesigner);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FormControl, TextField } from '@mui/material';
|
|
3
|
+
import '../../PrimerForm.css';
|
|
4
|
+
import '../../PrimerList.css';
|
|
5
|
+
|
|
6
|
+
function PrimerResultForm({
|
|
7
|
+
primer: { name, sequence }, updatePrimerName, existingPrimerNames,
|
|
8
|
+
}) {
|
|
9
|
+
let nameError = '';
|
|
10
|
+
if (existingPrimerNames.includes(name)) {
|
|
11
|
+
nameError = 'Name exists';
|
|
12
|
+
}
|
|
13
|
+
if (name === '') {
|
|
14
|
+
nameError = 'Name is required';
|
|
15
|
+
}
|
|
16
|
+
return (
|
|
17
|
+
<div className="primer-design-form">
|
|
18
|
+
<FormControl sx={{ mx: 1, width: '30%', mb: 1 }}>
|
|
19
|
+
<TextField
|
|
20
|
+
label="Name"
|
|
21
|
+
value={name}
|
|
22
|
+
onChange={(e) => updatePrimerName(e.target.value)}
|
|
23
|
+
error={nameError !== ''}
|
|
24
|
+
FormHelperTextProps={{ component: 'div' }}
|
|
25
|
+
helperText={nameError}
|
|
26
|
+
/>
|
|
27
|
+
</FormControl>
|
|
28
|
+
<FormControl sx={{ mx: 1, width: '60%' }}>
|
|
29
|
+
<TextField
|
|
30
|
+
label="Sequence"
|
|
31
|
+
value={sequence}
|
|
32
|
+
inputProps={{
|
|
33
|
+
id: 'sequence',
|
|
34
|
+
style: { fontSize: '0.8rem' }, // Reduce font size
|
|
35
|
+
}}
|
|
36
|
+
disabled
|
|
37
|
+
helperText=" " // Add an empty helper text to maintain consistent height
|
|
38
|
+
sx={{
|
|
39
|
+
'& .MuiInputBase-root': {
|
|
40
|
+
height: '56px', // Maintain the default height of TextField
|
|
41
|
+
},
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
</FormControl>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default PrimerResultForm;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Box, FormControl, FormLabel, InputAdornment, TextField } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import primerDesignMinimalValues from './primerDesignMinimalValues.json';
|
|
5
|
+
|
|
6
|
+
function PrimerSettingsForm({
|
|
7
|
+
homology_length, minimal_hybridization_length, target_tm, updateSettings,
|
|
8
|
+
}) {
|
|
9
|
+
return (
|
|
10
|
+
<Box sx={{ pt: 1 }}>
|
|
11
|
+
<FormLabel>Primer settings</FormLabel>
|
|
12
|
+
<Box sx={{ pt: 1.5 }}>
|
|
13
|
+
{homology_length !== null && (
|
|
14
|
+
<Box>
|
|
15
|
+
<FormControl>
|
|
16
|
+
<TextField
|
|
17
|
+
label="Homology length"
|
|
18
|
+
value={homology_length}
|
|
19
|
+
onChange={(e) => { updateSettings({ homology_length: Number(e.target.value) }); }}
|
|
20
|
+
type="number"
|
|
21
|
+
InputProps={{
|
|
22
|
+
endAdornment: <InputAdornment position="end">bp</InputAdornment>,
|
|
23
|
+
sx: { width: '10em' },
|
|
24
|
+
}}
|
|
25
|
+
error={homology_length < primerDesignMinimalValues.homology_length}
|
|
26
|
+
helperText={homology_length < primerDesignMinimalValues.homology_length ? `Min. ${primerDesignMinimalValues.homology_length} bp` : ''}
|
|
27
|
+
/>
|
|
28
|
+
</FormControl>
|
|
29
|
+
</Box>
|
|
30
|
+
)}
|
|
31
|
+
<Box sx={{ pt: 2 }}>
|
|
32
|
+
<FormControl sx={{ mr: 2 }}>
|
|
33
|
+
<TextField
|
|
34
|
+
label="Target hybridization Tm"
|
|
35
|
+
value={target_tm}
|
|
36
|
+
onChange={(e) => { updateSettings({ target_tm: Number(e.target.value) }); }}
|
|
37
|
+
type="number"
|
|
38
|
+
InputProps={{
|
|
39
|
+
endAdornment: <InputAdornment position="end">°C</InputAdornment>,
|
|
40
|
+
sx: { width: '10em' },
|
|
41
|
+
}}
|
|
42
|
+
error={target_tm < primerDesignMinimalValues.target_tm}
|
|
43
|
+
helperText={target_tm < primerDesignMinimalValues.target_tm ? `Min. ${primerDesignMinimalValues.target_tm} °C` : ''}
|
|
44
|
+
/>
|
|
45
|
+
</FormControl>
|
|
46
|
+
|
|
47
|
+
<FormControl>
|
|
48
|
+
<TextField
|
|
49
|
+
sx={{ minWidth: 'max-content' }}
|
|
50
|
+
label="Min. hybridization length"
|
|
51
|
+
value={minimal_hybridization_length}
|
|
52
|
+
onChange={(e) => { updateSettings({ minimal_hybridization_length: Number(e.target.value) }); }}
|
|
53
|
+
type="number"
|
|
54
|
+
InputProps={{
|
|
55
|
+
endAdornment: <InputAdornment position="end">bp</InputAdornment>,
|
|
56
|
+
sx: { width: '10em' },
|
|
57
|
+
}}
|
|
58
|
+
error={minimal_hybridization_length < primerDesignMinimalValues.hybridization_length}
|
|
59
|
+
helperText={minimal_hybridization_length < primerDesignMinimalValues.hybridization_length ? `Min. ${primerDesignMinimalValues.hybridization_length} bp` : ''}
|
|
60
|
+
/>
|
|
61
|
+
</FormControl>
|
|
62
|
+
</Box>
|
|
63
|
+
</Box>
|
|
64
|
+
</Box>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export default PrimerSettingsForm;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FormControl, TextField, Box } from '@mui/material';
|
|
3
|
+
import { stringIsNotDNA } from '@opencloning/store/cloning_utils';
|
|
4
|
+
import CollapsableLabel from './CollapsableLabel';
|
|
5
|
+
import { usePrimerDesign } from './PrimerDesignContext';
|
|
6
|
+
|
|
7
|
+
function PrimerSpacerForm({ open = true }) {
|
|
8
|
+
const { spacers, setSpacers, circularAssembly, templateSequenceNames, templateSequenceIds } = usePrimerDesign();
|
|
9
|
+
const [localSpacers, setLocalSpacers] = React.useState(spacers);
|
|
10
|
+
const timeoutRef = React.useRef();
|
|
11
|
+
|
|
12
|
+
// Debounced upstream updates to avoid heavy re-rendering
|
|
13
|
+
const handleSpacerChange = (index, value) => {
|
|
14
|
+
setLocalSpacers((current) => current.map((spacer, i) => (i === index ? value : spacer)));
|
|
15
|
+
|
|
16
|
+
// Clear any existing timeout
|
|
17
|
+
clearTimeout(timeoutRef.current);
|
|
18
|
+
|
|
19
|
+
// Set new timeout and store its ID in the ref
|
|
20
|
+
timeoutRef.current = setTimeout(() => {
|
|
21
|
+
setSpacers((current) => current.map((spacer, i) => (i === index ? value : spacer)));
|
|
22
|
+
}, 500);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
if (!localSpacers.every((spacer, index) => spacer === spacers[index])) {
|
|
27
|
+
setLocalSpacers(spacers);
|
|
28
|
+
}
|
|
29
|
+
}, [spacers]);
|
|
30
|
+
|
|
31
|
+
const fragmentCount = templateSequenceIds.length;
|
|
32
|
+
|
|
33
|
+
const sequenceNamesWrapped = [...templateSequenceNames, templateSequenceNames[0]];
|
|
34
|
+
const templateSequenceIdsWrapped = [...templateSequenceIds, templateSequenceIds[0]];
|
|
35
|
+
|
|
36
|
+
const getSequenceName = (seqIndex) => {
|
|
37
|
+
const name = sequenceNamesWrapped[seqIndex];
|
|
38
|
+
const id = templateSequenceIdsWrapped[seqIndex];
|
|
39
|
+
return name && name !== 'name' ? `Seq. ${id} (${name})` : `Seq. ${id}`;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const getSpacerLabel = (index) => {
|
|
43
|
+
if (index === 0 && !circularAssembly) {
|
|
44
|
+
return `Before ${getSequenceName(index)}`;
|
|
45
|
+
} if (index === fragmentCount && !circularAssembly) {
|
|
46
|
+
return `After ${getSequenceName(fragmentCount - 1)}`;
|
|
47
|
+
}
|
|
48
|
+
if (circularAssembly) {
|
|
49
|
+
return `Between ${getSequenceName(index)} and ${getSequenceName(index + 1)}`;
|
|
50
|
+
}
|
|
51
|
+
return `Between ${getSequenceName(index - 1)} and ${getSequenceName(index)}`;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<CollapsableLabel label="Spacer sequences" className="primer-spacer-form" open={open}>
|
|
56
|
+
<Box sx={{ pt: 1, width: '80%', margin: 'auto' }}>
|
|
57
|
+
<Box>
|
|
58
|
+
{localSpacers.map((spacer, index) => {
|
|
59
|
+
const error = stringIsNotDNA(spacer) ? 'Invalid DNA sequence' : '';
|
|
60
|
+
return (
|
|
61
|
+
<FormControl key={index} fullWidth sx={{ mb: 2 }}>
|
|
62
|
+
<TextField
|
|
63
|
+
label={getSpacerLabel(index)}
|
|
64
|
+
value={spacer}
|
|
65
|
+
onChange={(e) => handleSpacerChange(index, e.target.value)}
|
|
66
|
+
variant="outlined"
|
|
67
|
+
size="small"
|
|
68
|
+
inputProps={{
|
|
69
|
+
id: 'sequence',
|
|
70
|
+
}}
|
|
71
|
+
// Error if not DNA
|
|
72
|
+
error={error !== ''}
|
|
73
|
+
helperText={error}
|
|
74
|
+
/>
|
|
75
|
+
</FormControl>
|
|
76
|
+
);
|
|
77
|
+
})}
|
|
78
|
+
</Box>
|
|
79
|
+
</Box>
|
|
80
|
+
</CollapsableLabel>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export default PrimerSpacerForm;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FormLabel, Box, FormControl, TextField, Tooltip, FormControlLabel, Checkbox } from '@mui/material';
|
|
3
|
+
import InfoIcon from '@mui/icons-material/Info';
|
|
4
|
+
import { useStore } from 'react-redux';
|
|
5
|
+
import { updateEditor } from '@teselagen/ove';
|
|
6
|
+
import EnzymeMultiSelect from '../../../form/EnzymeMultiSelect';
|
|
7
|
+
import { stringIsNotDNA } from '@opencloning/store/cloning_utils';
|
|
8
|
+
import { usePrimerDesign } from './PrimerDesignContext';
|
|
9
|
+
import { isEnzymePalyndromic } from '@opencloning/utils/enzyme_utils';
|
|
10
|
+
|
|
11
|
+
function RestrictionSpacerForm() {
|
|
12
|
+
const { primerDesignSettings } = usePrimerDesign();
|
|
13
|
+
const {
|
|
14
|
+
left_enzyme: leftEnzyme,
|
|
15
|
+
right_enzyme: rightEnzyme,
|
|
16
|
+
left_enzyme_inverted: leftEnzymeInverted,
|
|
17
|
+
right_enzyme_inverted: rightEnzymeInverted,
|
|
18
|
+
filler_bases: fillerBases,
|
|
19
|
+
updateEnzymeSettings,
|
|
20
|
+
} = primerDesignSettings;
|
|
21
|
+
|
|
22
|
+
const store = useStore();
|
|
23
|
+
|
|
24
|
+
// When enzymes change, update the displayed enzymes in the editor
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
const allEnzymes = [leftEnzyme, rightEnzyme].filter((enzyme) => enzyme !== null);
|
|
27
|
+
const filteredRestrictionEnzymes = allEnzymes.map((enzyme) => ({
|
|
28
|
+
canBeHidden: true,
|
|
29
|
+
value: enzyme,
|
|
30
|
+
}));
|
|
31
|
+
updateEditor(store, 'mainEditor', { annotationVisibility: { cutsites: leftEnzyme || rightEnzyme }, restrictionEnzymes: { filteredRestrictionEnzymes, isEnzymeFilterAnd: false } });
|
|
32
|
+
}, [leftEnzyme, rightEnzyme]);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<>
|
|
36
|
+
<FormLabel>Restriction enzyme sites</FormLabel>
|
|
37
|
+
<Box sx={{ display: 'flex', flexDirection: 'row', justifyContent: 'center' }}>
|
|
38
|
+
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
|
39
|
+
<FormControl sx={{ width: '10em', mt: 1.5, mr: 2 }}>
|
|
40
|
+
<EnzymeMultiSelect value={leftEnzyme} setEnzymes={(v) => updateEnzymeSettings({ left_enzyme: v })} label="Left enzyme" multiple={false} />
|
|
41
|
+
</FormControl>
|
|
42
|
+
{leftEnzyme && !isEnzymePalyndromic(leftEnzyme) && (
|
|
43
|
+
<FormControlLabel
|
|
44
|
+
control={<Checkbox checked={leftEnzymeInverted} onChange={(e) => updateEnzymeSettings({ left_enzyme_inverted: e.target.checked })} />}
|
|
45
|
+
label="Invert site"
|
|
46
|
+
/>
|
|
47
|
+
)}
|
|
48
|
+
</Box>
|
|
49
|
+
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
|
50
|
+
<FormControl sx={{ width: '10em', mt: 1.5, mr: 2 }}>
|
|
51
|
+
<EnzymeMultiSelect value={rightEnzyme} setEnzymes={(v) => updateEnzymeSettings({ right_enzyme: v })} label="Right enzyme" multiple={false} />
|
|
52
|
+
</FormControl>
|
|
53
|
+
{rightEnzyme && !isEnzymePalyndromic(rightEnzyme) && (
|
|
54
|
+
<FormControlLabel
|
|
55
|
+
control={<Checkbox checked={rightEnzymeInverted} onChange={(e) => updateEnzymeSettings({ right_enzyme_inverted: e.target.checked })} />}
|
|
56
|
+
label="Invert site"
|
|
57
|
+
/>
|
|
58
|
+
)}
|
|
59
|
+
</Box>
|
|
60
|
+
<FormControl sx={{ width: '10em', mt: 1.5 }}>
|
|
61
|
+
<TextField
|
|
62
|
+
label={(
|
|
63
|
+
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
|
64
|
+
5' filler bases
|
|
65
|
+
<Tooltip arrow placement="top" title="These bases are added to the 5' end of the primer to ensure proper restriction enzyme digestion.">
|
|
66
|
+
<InfoIcon sx={{ fontSize: '1.2em', ml: 0.5 }} />
|
|
67
|
+
</Tooltip>
|
|
68
|
+
</Box>
|
|
69
|
+
)}
|
|
70
|
+
value={fillerBases}
|
|
71
|
+
onChange={(e) => updateEnzymeSettings({ filler_bases: e.target.value })}
|
|
72
|
+
variant="outlined"
|
|
73
|
+
inputProps={{
|
|
74
|
+
id: 'sequence',
|
|
75
|
+
}}
|
|
76
|
+
error={stringIsNotDNA(fillerBases)}
|
|
77
|
+
helperText={stringIsNotDNA(fillerBases) ? 'Invalid DNA sequence' : ''}
|
|
78
|
+
/>
|
|
79
|
+
</FormControl>
|
|
80
|
+
</Box>
|
|
81
|
+
</>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export default RestrictionSpacerForm;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Box, Button, Tooltip } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { usePrimerDesign } from './PrimerDesignContext';
|
|
4
|
+
|
|
5
|
+
function ToolTipWrapper({ title, children, enabled = false }) {
|
|
6
|
+
if (enabled) {
|
|
7
|
+
return (
|
|
8
|
+
<Tooltip title={title} placement="top" arrow>
|
|
9
|
+
<span>
|
|
10
|
+
{children}
|
|
11
|
+
</span>
|
|
12
|
+
</Tooltip>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
return children;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function StepNavigation({ onStepCompletion, allowStepCompletion, stepCompletionText, isFirstStep = false, nextDisabled = false, nextToolTip = '', stepCompletionToolTip = '' }) {
|
|
19
|
+
const { handleBack, handleNext } = usePrimerDesign();
|
|
20
|
+
return (
|
|
21
|
+
<Box sx={{ display: 'flex', flexDirection: 'row', pt: 2 }}>
|
|
22
|
+
<Button
|
|
23
|
+
color="inherit"
|
|
24
|
+
disabled={isFirstStep}
|
|
25
|
+
onClick={handleBack}
|
|
26
|
+
sx={{ mr: 1 }}
|
|
27
|
+
>
|
|
28
|
+
Back
|
|
29
|
+
</Button>
|
|
30
|
+
<Box sx={{ flex: '1 1 auto' }} />
|
|
31
|
+
|
|
32
|
+
<ToolTipWrapper title={stepCompletionToolTip} enabled={!allowStepCompletion}>
|
|
33
|
+
<Button onClick={onStepCompletion} disabled={!allowStepCompletion} sx={{ mr: 1 }}>
|
|
34
|
+
{stepCompletionText}
|
|
35
|
+
</Button>
|
|
36
|
+
</ToolTipWrapper>
|
|
37
|
+
|
|
38
|
+
<ToolTipWrapper title={nextToolTip} enabled={nextDisabled}>
|
|
39
|
+
<Button color="inherit" disabled={nextDisabled} onClick={handleNext} sx={{ mr: 1 }}>
|
|
40
|
+
Next
|
|
41
|
+
</Button>
|
|
42
|
+
</ToolTipWrapper>
|
|
43
|
+
|
|
44
|
+
</Box>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default StepNavigation;
|