@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.
Files changed (207) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/package.json +37 -0
  3. package/src/components/AppAlerts.jsx +19 -0
  4. package/src/components/CloningHistory.jsx +40 -0
  5. package/src/components/DataModelDisplayer.jsx +30 -0
  6. package/src/components/DescriptionEditor.jsx +68 -0
  7. package/src/components/DownloadCloningStrategyDialog.jsx +84 -0
  8. package/src/components/DownloadSequenceFileDialog.jsx +90 -0
  9. package/src/components/DragAndDropCloningHistoryWrapper.jsx +39 -0
  10. package/src/components/DraggableDialogPaper.jsx +16 -0
  11. package/src/components/EditSequenceNameDialog.jsx +90 -0
  12. package/src/components/ExternalServicesStatusCheck.jsx +92 -0
  13. package/src/components/HistoryLoadedDialog.jsx +49 -0
  14. package/src/components/LoadCloningHistoryWrapper.jsx +166 -0
  15. package/src/components/MainSequenceCheckBox.jsx +83 -0
  16. package/src/components/MainSequenceEditor.jsx +165 -0
  17. package/src/components/NetworkNode.jsx +159 -0
  18. package/src/components/NetworkTree.css +127 -0
  19. package/src/components/ObjectTable.jsx +24 -0
  20. package/src/components/OpenCloning.jsx +102 -0
  21. package/src/components/OverhangsDisplay.jsx +25 -0
  22. package/src/components/SequenceEditor.jsx +120 -0
  23. package/src/components/SequenceTab.jsx +14 -0
  24. package/src/components/TemplateSequence.jsx +38 -0
  25. package/src/components/annotation/PlannotateAnnotationReport.jsx +33 -0
  26. package/src/components/annotation/useUpdateAnnotationInMainSequence.js +39 -0
  27. package/src/components/assembler/AssemblePartWidget.jsx +252 -0
  28. package/src/components/assembler/Assembler.jsx +273 -0
  29. package/src/components/assembler/AssemblerPart.jsx +99 -0
  30. package/src/components/assembler/StopIcon.jsx +34 -0
  31. package/src/components/assembler/assembler_data2.json +50 -0
  32. package/src/components/assembler/assembly_component.module.css +81 -0
  33. package/src/components/assembler/moclo.json +110 -0
  34. package/src/components/assembler/sbol_visual_glyphs/LICENSE.html +21 -0
  35. package/src/components/assembler/sbol_visual_glyphs/assembly-scar.svg +63 -0
  36. package/src/components/assembler/sbol_visual_glyphs/cds-stop.svg +85 -0
  37. package/src/components/assembler/sbol_visual_glyphs/cds.svg +60 -0
  38. package/src/components/assembler/sbol_visual_glyphs/chromosomal-locus.svg +78 -0
  39. package/src/components/assembler/sbol_visual_glyphs/engineered-region.svg +56 -0
  40. package/src/components/assembler/sbol_visual_glyphs/five-prime-sticky-restriction-site.svg +56 -0
  41. package/src/components/assembler/sbol_visual_glyphs/origin-of-replication.svg +57 -0
  42. package/src/components/assembler/sbol_visual_glyphs/primer-binding-site.svg +59 -0
  43. package/src/components/assembler/sbol_visual_glyphs/promoter.svg +60 -0
  44. package/src/components/assembler/sbol_visual_glyphs/ribosome-entry-site.svg +56 -0
  45. package/src/components/assembler/sbol_visual_glyphs/specific-recombination-site.svg +59 -0
  46. package/src/components/assembler/sbol_visual_glyphs/terminator.svg +60 -0
  47. package/src/components/assembler/sbol_visual_glyphs/three-prime-sticky-restriction-site.svg +56 -0
  48. package/src/components/assembler/sbol_visual_glyphs.js +36 -0
  49. package/src/components/assembler/useAssembler.js +71 -0
  50. package/src/components/dummy/DummyInterface.js +41 -0
  51. package/src/components/dummy/GetSequenceFileAndDatabaseIdComponent.jsx +59 -0
  52. package/src/components/eLabFTW/ELabFTWCategorySelect.cy.jsx +86 -0
  53. package/src/components/eLabFTW/ELabFTWCategorySelect.jsx +43 -0
  54. package/src/components/eLabFTW/ELabFTWFileSelect.cy.jsx +43 -0
  55. package/src/components/eLabFTW/ELabFTWFileSelect.jsx +29 -0
  56. package/src/components/eLabFTW/ELabFTWResourceSelect.cy.jsx +107 -0
  57. package/src/components/eLabFTW/ELabFTWResourceSelect.jsx +23 -0
  58. package/src/components/eLabFTW/GetPrimerComponent.cy.jsx +261 -0
  59. package/src/components/eLabFTW/GetPrimerComponent.jsx +55 -0
  60. package/src/components/eLabFTW/GetSequenceFileAndDatabaseIdComponent.cy.jsx +184 -0
  61. package/src/components/eLabFTW/GetSequenceFileAndDatabaseIdComponent.jsx +62 -0
  62. package/src/components/eLabFTW/LoadHistoryComponent.cy.jsx +235 -0
  63. package/src/components/eLabFTW/LoadHistoryComponent.jsx +51 -0
  64. package/src/components/eLabFTW/PrimersNotInDatabaseComponent.cy.jsx +159 -0
  65. package/src/components/eLabFTW/PrimersNotInDatabaseComponent.jsx +54 -0
  66. package/src/components/eLabFTW/SubmitToDatabaseComponent.cy.jsx +185 -0
  67. package/src/components/eLabFTW/SubmitToDatabaseComponent.jsx +51 -0
  68. package/src/components/eLabFTW/common.js +26 -0
  69. package/src/components/eLabFTW/eLabFTWInterface.js +294 -0
  70. package/src/components/eLabFTW/eLabFTWInterface.test.js +839 -0
  71. package/src/components/eLabFTW/envValues.js +7 -0
  72. package/src/components/eLabFTW/utils.js +39 -0
  73. package/src/components/form/CustomFormHelperText.jsx +10 -0
  74. package/src/components/form/EnzymeMultiSelect.cy.jsx +61 -0
  75. package/src/components/form/EnzymeMultiSelect.jsx +34 -0
  76. package/src/components/form/GetRequestMultiSelect.jsx +107 -0
  77. package/src/components/form/LabelWithTooltip.jsx +16 -0
  78. package/src/components/form/PostRequestSelect.cy.jsx +70 -0
  79. package/src/components/form/PostRequestSelect.jsx +86 -0
  80. package/src/components/form/RequestStatusWrapper.jsx +17 -0
  81. package/src/components/form/RetryAlert.jsx +20 -0
  82. package/src/components/form/ServerErrorMessage.jsx +10 -0
  83. package/src/components/form/SubmitButtonBackendAPI.jsx +15 -0
  84. package/src/components/form/SubmitToDatabaseDialog.jsx +133 -0
  85. package/src/components/form/TextFieldValidate.jsx +67 -0
  86. package/src/components/form/ValidatedTextField.jsx +33 -0
  87. package/src/components/form/intermediates_disclaimer.svg +181 -0
  88. package/src/components/navigation/ButtonWithMenu.jsx +43 -0
  89. package/src/components/navigation/CustomTab.jsx +14 -0
  90. package/src/components/navigation/FeedbackDialog.jsx +34 -0
  91. package/src/components/navigation/GithubCornerRight.jsx +29 -0
  92. package/src/components/navigation/MainAppBar.css +26 -0
  93. package/src/components/navigation/MainAppBar.jsx +205 -0
  94. package/src/components/navigation/SelectExampleDialog.jsx +69 -0
  95. package/src/components/navigation/SelectTemplateDialog.jsx +107 -0
  96. package/src/components/navigation/TabPanel.jsx +28 -0
  97. package/src/components/navigation/VersionDialog.jsx +33 -0
  98. package/src/components/primers/CreatePrimerFromSequenceForm.jsx +42 -0
  99. package/src/components/primers/DownloadPrimersButton.jsx +104 -0
  100. package/src/components/primers/PrimerForm.css +14 -0
  101. package/src/components/primers/PrimerForm.jsx +107 -0
  102. package/src/components/primers/PrimerList.css +46 -0
  103. package/src/components/primers/PrimerList.cy.jsx +95 -0
  104. package/src/components/primers/PrimerList.jsx +126 -0
  105. package/src/components/primers/PrimerTableRow.cy.jsx +57 -0
  106. package/src/components/primers/PrimerTableRow.jsx +84 -0
  107. package/src/components/primers/SelectPrimerForm.jsx +66 -0
  108. package/src/components/primers/import_primers/ImportPrimersButton.jsx +101 -0
  109. package/src/components/primers/import_primers/ImportPrimersTable.jsx +51 -0
  110. package/src/components/primers/import_primers/PrimerDatabaseImportForm.jsx +107 -0
  111. package/src/components/primers/import_primers/styles.css +60 -0
  112. package/src/components/primers/primer_design/SequenceTabComponents/CollapsableLabel.jsx +31 -0
  113. package/src/components/primers/primer_design/SequenceTabComponents/GatewayRoiSelect.jsx +164 -0
  114. package/src/components/primers/primer_design/SequenceTabComponents/OrientationPicker.jsx +37 -0
  115. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignContext.jsx +369 -0
  116. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignEBIC.jsx +24 -0
  117. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignForm.jsx +29 -0
  118. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignGatewayBP.jsx +36 -0
  119. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignGibsonAssembly.jsx +26 -0
  120. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignHomologousRecombination.jsx +32 -0
  121. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignRestriction.jsx +25 -0
  122. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignSimplePair.jsx +25 -0
  123. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesignStepper.jsx +53 -0
  124. package/src/components/primers/primer_design/SequenceTabComponents/PrimerDesigner.jsx +80 -0
  125. package/src/components/primers/primer_design/SequenceTabComponents/PrimerResultForm.jsx +49 -0
  126. package/src/components/primers/primer_design/SequenceTabComponents/PrimerSettingsForm.jsx +68 -0
  127. package/src/components/primers/primer_design/SequenceTabComponents/PrimerSpacerForm.jsx +84 -0
  128. package/src/components/primers/primer_design/SequenceTabComponents/RestrictionSpacerForm.jsx +85 -0
  129. package/src/components/primers/primer_design/SequenceTabComponents/StepNavigation.jsx +48 -0
  130. package/src/components/primers/primer_design/SequenceTabComponents/TabPanelEBICSettings.jsx +216 -0
  131. package/src/components/primers/primer_design/SequenceTabComponents/TabPanelResults.jsx +42 -0
  132. package/src/components/primers/primer_design/SequenceTabComponents/TabPanelSelectRoi.jsx +61 -0
  133. package/src/components/primers/primer_design/SequenceTabComponents/TabPannelSettings.jsx +59 -0
  134. package/src/components/primers/primer_design/SequenceTabComponents/primerDesignMinimalValues.json +5 -0
  135. package/src/components/primers/primer_design/SequenceTabComponents/useEBICPrimerDesignSettings.js +31 -0
  136. package/src/components/primers/primer_design/SequenceTabComponents/useEnzymePrimerDesignSettings.js +57 -0
  137. package/src/components/primers/primer_design/SequenceTabComponents/useGatewayPrimerDesignSettings.js +18 -0
  138. package/src/components/primers/primer_design/SequenceTabComponents/usePrimerDesignSettings.js +31 -0
  139. package/src/components/primers/primer_design/SourceComponents/PrimerDesignGatewayBP.jsx +88 -0
  140. package/src/components/primers/primer_design/SourceComponents/PrimerDesignGibsonAssembly.jsx +65 -0
  141. package/src/components/primers/primer_design/SourceComponents/PrimerDesignHomologousRecombination.jsx +84 -0
  142. package/src/components/primers/primer_design/SourceComponents/PrimerDesignSourceForm.jsx +74 -0
  143. package/src/components/primers/primer_design/common/NoAttPSitesError.jsx +31 -0
  144. package/src/components/primers/primer_details/PCRTable.cy.jsx +51 -0
  145. package/src/components/primers/primer_details/PCRTable.jsx +35 -0
  146. package/src/components/primers/primer_details/Primer3Figure.jsx +25 -0
  147. package/src/components/primers/primer_details/PrimerDetailsTds.jsx +39 -0
  148. package/src/components/primers/primer_details/PrimerInfoIcon.cy.jsx +137 -0
  149. package/src/components/primers/primer_details/PrimerInfoIcon.jsx +132 -0
  150. package/src/components/primers/primer_details/TableSection.jsx +17 -0
  151. package/src/components/primers/primer_details/primerDetailsFormatting.js +3 -0
  152. package/src/components/primers/primer_details/useMultiplePrimerDetails.js +29 -0
  153. package/src/components/primers/primer_details/usePCRDetails.js +47 -0
  154. package/src/components/primers/primer_details/usePrimerDetailsEndpoints.js +49 -0
  155. package/src/components/primers/primer_details/useSinglePrimerSequenceDetails.js +25 -0
  156. package/src/components/primers/primersToTabularFile.js +49 -0
  157. package/src/components/primers/primersToTabularFile.test.js +108 -0
  158. package/src/components/settings/SettingsTab.cy.jsx +267 -0
  159. package/src/components/settings/SettingsTab.jsx +170 -0
  160. package/src/components/sources/AssemblyPlanDisplayer.cy.jsx +22 -0
  161. package/src/components/sources/AssemblyPlanDisplayer.jsx +27 -0
  162. package/src/components/sources/CollectionSource.jsx +97 -0
  163. package/src/components/sources/FinishedSource.jsx +397 -0
  164. package/src/components/sources/KnownSourceErrors.jsx +50 -0
  165. package/src/components/sources/MultipleInputsSelector.jsx +63 -0
  166. package/src/components/sources/MultipleOutputsSelector.jsx +63 -0
  167. package/src/components/sources/NewSourceBox.jsx +37 -0
  168. package/src/components/sources/PCRUnitForm.jsx +102 -0
  169. package/src/components/sources/SingleInputSelector.jsx +36 -0
  170. package/src/components/sources/Source.jsx +125 -0
  171. package/src/components/sources/SourceAnnotation.jsx +44 -0
  172. package/src/components/sources/SourceAssembly.jsx +201 -0
  173. package/src/components/sources/SourceBox.css +18 -0
  174. package/src/components/sources/SourceBox.jsx +60 -0
  175. package/src/components/sources/SourceCopySequence.jsx +38 -0
  176. package/src/components/sources/SourceDatabase.jsx +28 -0
  177. package/src/components/sources/SourceFile.jsx +188 -0
  178. package/src/components/sources/SourceGenomeRegion.cy.jsx +131 -0
  179. package/src/components/sources/SourceGenomeRegion.jsx +486 -0
  180. package/src/components/sources/SourceHomologousRecombination.jsx +125 -0
  181. package/src/components/sources/SourceKnownGenomeRegion.jsx +60 -0
  182. package/src/components/sources/SourceManuallyTyped.jsx +116 -0
  183. package/src/components/sources/SourcePCRorHybridization.jsx +165 -0
  184. package/src/components/sources/SourcePolymeraseExtension.jsx +44 -0
  185. package/src/components/sources/SourceRepositoryId.jsx +409 -0
  186. package/src/components/sources/SourceRestriction.jsx +41 -0
  187. package/src/components/sources/SourceReverseComplement.jsx +33 -0
  188. package/src/components/sources/SourceTypeSelector.jsx +94 -0
  189. package/src/components/sources/SubSequenceDisplayer.jsx +70 -0
  190. package/src/components/sources/VerifyDeleteDialog.jsx +23 -0
  191. package/src/components/sources/repositoryMetadata.js +14 -0
  192. package/src/components/verification/LoadFromDatabaseButton.jsx +90 -0
  193. package/src/components/verification/SequencingFileRow.jsx +34 -0
  194. package/src/components/verification/VerificationFileDialog.cy.jsx +176 -0
  195. package/src/components/verification/VerificationFileDialog.jsx +248 -0
  196. package/src/config/defaultMainEditorProps.js +44 -0
  197. package/src/hooks/useAlerts.js +16 -0
  198. package/src/hooks/useBackendAPI.js +51 -0
  199. package/src/hooks/useBackendRoute.js +22 -0
  200. package/src/hooks/useDatabase.js +18 -0
  201. package/src/hooks/useDragAndDropFile.js +31 -0
  202. package/src/hooks/useGatewaySites.js +40 -0
  203. package/src/hooks/useHttpClient.js +12 -0
  204. package/src/hooks/useLoadDatabaseFile.js +108 -0
  205. package/src/hooks/useStoreEditor.js +101 -0
  206. package/src/hooks/useValidateState.js +43 -0
  207. package/vitest.config.js +18 -0
@@ -0,0 +1,127 @@
1
+ /* Adapted from https://github.com/dumptyd/treeflex */
2
+
3
+ .tf-ancestor-tree {
4
+ position: relative;
5
+ }
6
+
7
+ .tf-ancestor-tree>div {
8
+ transform-origin: top left;
9
+ /* This is how you could potentially scale for screenshots */
10
+ /* transform: scale(0.5); */
11
+ }
12
+
13
+ .tf-ancestor-tree>div>ul {
14
+ transform: rotateX(180deg);
15
+ }
16
+
17
+ .node-text {
18
+ display: inline-block;
19
+ transform: rotateX(180deg);
20
+ }
21
+
22
+ .tf-tree {
23
+ font-size: 16px;
24
+ }
25
+
26
+ .tf-tree * {
27
+ box-sizing: border-box;
28
+ margin: 0;
29
+ padding: 0
30
+ }
31
+
32
+ .tf-tree ul {
33
+ display: inline-flex;
34
+ }
35
+
36
+ .tf-tree li {
37
+ align-items: center;
38
+ display: flex;
39
+ flex-direction: column;
40
+ flex-wrap: wrap;
41
+ padding: 0 1em;
42
+ position: relative
43
+ }
44
+
45
+ .tf-tree li ul {
46
+ margin-top: 2em
47
+ }
48
+
49
+ .tf-tree li li:before {
50
+ border-top: 2px solid #000;
51
+ content: "";
52
+ display: block;
53
+ height: .0625em;
54
+ left: -.03125em;
55
+ position: absolute;
56
+ top: -1.00em;
57
+ width: 100%;
58
+ z-index: -1;
59
+ }
60
+
61
+ .tf-tree li li:first-child:before {
62
+ left: calc(50% - .03125em);
63
+ max-width: calc(50% + .0625em)
64
+ }
65
+
66
+ .tf-tree li li:last-child:before {
67
+ left: auto;
68
+ max-width: calc(50% + .0625em);
69
+ right: calc(50% - .03125em)
70
+ }
71
+
72
+ .tf-tree li li:only-child:before {
73
+ display: none
74
+ }
75
+
76
+ .tf-tree li li:only-child>.tf-nc:before, .tf-tree li li:only-child>.tf-node-content:before {
77
+ height: 1.0625em;
78
+ top: -1.0625em
79
+ }
80
+
81
+ .tf-tree .tf-nc, .tf-tree .tf-node-content {
82
+ padding: .5em 1em;
83
+ position: relative
84
+ }
85
+
86
+ /* Sticking down */
87
+ .tf-tree .tf-nc:before, .tf-tree .tf-node-content:before {
88
+ top: -1.2em
89
+ }
90
+
91
+ .tf-tree .tf-nc:after, .tf-tree .tf-nc:before, .tf-tree .tf-node-content:after, .tf-tree .tf-node-content:before {
92
+ border-left: 2px solid #000;
93
+ content: "";
94
+ display: block;
95
+ height: 1.2em;
96
+ left: calc(50% - .03125em);
97
+ position: absolute;
98
+ width: 2px;
99
+ z-index: -1;
100
+ }
101
+
102
+ .tf-tree .tf-nc:after, .tf-tree .tf-node-content:after {
103
+ top: calc(100% + .12em)
104
+ }
105
+
106
+ /* .tf-tree>div>ul>li>.tf-nc:before removes the things sticking to the bottom */
107
+ .tf-tree .tf-nc:only-child:after, .tf-tree .tf-node-content:only-child:after, .tf-tree>div>ul>li>.tf-nc:before, .tf-tree>div>ul>li>.tf-node-content:before {
108
+ display: none
109
+ }
110
+
111
+ .tf-tree.tf-gap-sm li {
112
+ padding: 0 .6em
113
+ }
114
+
115
+ .tf-tree.tf-gap-sm li>.tf-nc:before, .tf-tree.tf-gap-sm li>.tf-node-content:before {
116
+ height: .6em;
117
+ top: -.6em
118
+ }
119
+
120
+ .tf-tree.tf-gap-sm li>.tf-nc:after, .tf-tree.tf-gap-sm li>.tf-node-content:after {
121
+ height: .6em
122
+ }
123
+
124
+ .tf-nc {
125
+ border-radius: 15px;
126
+ text-align: center;
127
+ }
@@ -0,0 +1,24 @@
1
+ // A react fucntioncal component that renders a table
2
+ // where each row contains a key-value pair of an object
3
+ // it uses mui
4
+
5
+ import { Table, TableCell, TableRow } from '@mui/material';
6
+ import React from 'react';
7
+
8
+ function ObjectTable({ object }) {
9
+ return (
10
+
11
+ <Table sx={{ my: 2 }}>
12
+
13
+ {Object.keys(object).map((key) => (
14
+ <TableRow key={key}>
15
+ <TableCell sx={{ py: 0.5 }}><strong>{key}</strong></TableCell>
16
+ <TableCell sx={{ py: 0.5 }}>{object[key]}</TableCell>
17
+ </TableRow>
18
+ ))}
19
+ </Table>
20
+
21
+ );
22
+ }
23
+
24
+ export default React.memo(ObjectTable);
@@ -0,0 +1,102 @@
1
+ import React, { useRef, useState } from 'react';
2
+ import { useDispatch, useSelector } from 'react-redux';
3
+ import Tabs from '@mui/material/Tabs';
4
+ import { isEqual } from 'lodash-es';
5
+ import DescriptionEditor from './DescriptionEditor';
6
+ import PrimerList from './primers/PrimerList';
7
+ import SettingsTab from './settings/SettingsTab';
8
+ import { cloningActions } from '@opencloning/store/cloning';
9
+ import TabPanel from './navigation/TabPanel';
10
+ import CustomTab from './navigation/CustomTab';
11
+ import DataModelDisplayer from './DataModelDisplayer';
12
+ import CloningHistory from './CloningHistory';
13
+ import SequenceTab from './SequenceTab';
14
+ import AppAlerts from './AppAlerts';
15
+ import Assembler from './assembler/Assembler';
16
+
17
+ const { setCurrentTab } = cloningActions;
18
+
19
+ function OpenCloning() {
20
+ const dispatch = useDispatch();
21
+ const currentTab = useSelector((state) => state.cloning.currentTab);
22
+ const tabPanelsRef = useRef(null);
23
+ const [smallDevice, setSmallDevice] = useState(window.innerWidth < 600);
24
+ const hasAppBar = useSelector((state) => state.cloning.config.showAppBar, isEqual);
25
+ const enableAssembler = useSelector((state) => state.cloning.config.enableAssembler);
26
+
27
+ React.useEffect(() => {
28
+ const handleResize = () => {
29
+ setSmallDevice(window.innerWidth < 600);
30
+ };
31
+ window.addEventListener('resize', handleResize);
32
+ return () => window.removeEventListener('resize', handleResize);
33
+ }, []);
34
+
35
+ const changeTab = (event, newValue) => {
36
+ dispatch(setCurrentTab(newValue));
37
+ if (tabPanelsRef.current) {
38
+ tabPanelsRef.current.scrollTo({
39
+ top: 0,
40
+ behavior: 'instant',
41
+ });
42
+ }
43
+ };
44
+
45
+ return (
46
+ <div className="app-container" style={{ height: hasAppBar ? 'calc(100vh - 114px - 10px)' : '100vh' }}>
47
+ <AppAlerts />
48
+ <Tabs
49
+ variant={smallDevice ? 'scrollable' : 'standard'}
50
+ scrollButtons={smallDevice ? 'auto' : false}
51
+ allowScrollButtonsMobile
52
+ centered={!smallDevice}
53
+ value={currentTab}
54
+ onChange={changeTab}
55
+ sx={{ pb: 3, pt: 1 }}
56
+ id="opencloning-app-tabs"
57
+ >
58
+ <CustomTab label="Cloning" index={0} />
59
+ <CustomTab label="Primers" index={1} />
60
+ <CustomTab label="Description" index={2} />
61
+ <CustomTab label="Sequence" index={3} />
62
+ <CustomTab label="Data model" index={4} />
63
+ <CustomTab label="Settings" index={5} />
64
+ {enableAssembler && <CustomTab label="Assembler" index={6} />}
65
+ </Tabs>
66
+ <div className="tab-panels-container" ref={tabPanelsRef}>
67
+ <TabPanel index={1} value={currentTab} className="primer-tab-pannel">
68
+ <div className="primer-list-container">
69
+ <PrimerList />
70
+ </div>
71
+ </TabPanel>
72
+ <TabPanel index={2} value={currentTab} className="description-tab-pannel">
73
+ <div className="description-editor">
74
+ <DescriptionEditor />
75
+ </div>
76
+ </TabPanel>
77
+ {/* For some reason, putting this here is required for primer.color to work */}
78
+ <TabPanel index={3} value={currentTab} className="main-editor-tab-pannel">
79
+ <div className="main-sequence-editor">
80
+ <SequenceTab />
81
+ </div>
82
+ </TabPanel>
83
+ <TabPanel index={0} value={currentTab} className="cloning-tab-pannel">
84
+ <div className="open-cloning">
85
+ <CloningHistory />
86
+ </div>
87
+ </TabPanel>
88
+ <TabPanel index={4} value={currentTab} className="data-model-tab-pannel">
89
+ <DataModelDisplayer />
90
+ </TabPanel>
91
+ <TabPanel index={5} value={currentTab} className="settings-tab-pannel">
92
+ <SettingsTab />
93
+ </TabPanel>
94
+ {enableAssembler && <TabPanel index={6} value={currentTab} className="assembler-tab-pannel">
95
+ <Assembler />
96
+ </TabPanel>}
97
+ </div>
98
+ </div>
99
+ );
100
+ }
101
+
102
+ export default OpenCloning;
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import { formatSequenceForOverhangDisplay } from '@opencloning/utils/sequenceDisplay';
3
+
4
+ function OverhangsDisplay({ sequence, sequenceData }) {
5
+ if (sequence === undefined
6
+ || (sequence.overhang_crick_3prime === 0 && sequence.overhang_watson_3prime === 0)
7
+ ) { return null; }
8
+ const { watson, crick, middle } = formatSequenceForOverhangDisplay(
9
+ sequenceData.sequence,
10
+ sequence.overhang_crick_3prime,
11
+ sequence.overhang_watson_3prime,
12
+ );
13
+
14
+ return (
15
+ <div className="overhang-representation">
16
+ {watson}
17
+ <br />
18
+ {middle}
19
+ <br />
20
+ {crick}
21
+ </div>
22
+ );
23
+ }
24
+
25
+ export default OverhangsDisplay;
@@ -0,0 +1,120 @@
1
+ import React from 'react';
2
+ import { SimpleCircularOrLinearView } from '@teselagen/ove';
3
+ import { useDispatch, useSelector } from 'react-redux';
4
+ import { isEqual } from 'lodash-es';
5
+ import OverhangsDisplay from './OverhangsDisplay';
6
+ import NewSourceBox from './sources/NewSourceBox';
7
+ import { cloningActions } from '@opencloning/store/cloning';
8
+ import getTransformCoords from '@opencloning/utils/transformCoords';
9
+ import { getPCRPrimers, isSequenceInputOfAnySource } from '@opencloning/store/cloning_utils';
10
+
11
+ const transformToRegion = (eventOutput) => {
12
+ if (eventOutput.selectionLayer) {
13
+ // When selecting a region
14
+ return { selectionLayer: eventOutput.selectionLayer, caretPosition: -1 };
15
+ }
16
+ // When clicking a feature
17
+ return { selectionLayer: { start: eventOutput.start, end: eventOutput.end }, caretPosition: -1 };
18
+ };
19
+
20
+ function AddSourceComponent({ sequenceId }) {
21
+ const isRootNode = useSelector((state) => !isSequenceInputOfAnySource(sequenceId, state.cloning.sources));
22
+ return isRootNode ? (
23
+ <div className="hang-from-node">
24
+ <div>
25
+ <NewSourceBox {...{ inputSequencesIds: [sequenceId] }} />
26
+ </div>
27
+ </div>
28
+ ) : null;
29
+ }
30
+
31
+ function SequenceEditor({ sequenceId }) {
32
+ const editorName = `editor_${sequenceId}`;
33
+ const sequence = useSelector((state) => state.cloning.sequences.find((e) => e.id === sequenceId), isEqual);
34
+ const pcrPrimers = useSelector(({ cloning }) => getPCRPrimers(cloning, sequenceId), isEqual);
35
+ const unmutableSeq = useSelector((state) => state.cloning.teselaJsonCache[sequenceId], isEqual);
36
+ const seq = { ...unmutableSeq };
37
+
38
+ // Make a copy
39
+ const seqCopy = React.useMemo(() => structuredClone(seq), [seq]);
40
+ // Filter out features of type "source"
41
+ seqCopy.features = seqCopy.features.filter((f) => f.type !== 'source');
42
+ // If the primer has been already added to the template, we don't add it again
43
+ const pcrPrimers2Include = pcrPrimers.filter((p) => !seqCopy.primers.some(
44
+ (p2) => p2.name === p.name && p2.start === p.start && p2.end === p.end,
45
+ ));
46
+ seqCopy.primers = [...seqCopy.primers, ...pcrPrimers2Include];
47
+ const parentSource = useSelector((state) => state.cloning.sources.find((source) => source.id === sequenceId), isEqual);
48
+ const stateSelectedRegion = useSelector((state) => state.cloning.selectedRegions.find((r) => r.id === sequenceId)?.selectedRegion, isEqual);
49
+ const parentSequenceData = useSelector((state) => parentSource.input.map(({sequence}) => state.cloning.teselaJsonCache[sequence]), isEqual);
50
+ const [rangeInParent, setRangeInParent] = React.useState(() => null);
51
+ React.useEffect(() => {
52
+ const callBack = getTransformCoords(parentSource, parentSequenceData, seqCopy.size);
53
+ // Here we have to set the state like this, since it's a function
54
+ // otherwise, react calls the function with the previous state
55
+ setRangeInParent(() => callBack);
56
+ }, [parentSource, parentSequenceData]);
57
+
58
+ const { setSelectedRegions } = cloningActions;
59
+ const dispatch = useDispatch();
60
+ const [selectedRegion, setSelectedRegion] = React.useState({ selectionLayer: { start: -1, end: -1 }, caretPosition: -1 });
61
+ const [timeOutId, setTimeOutId] = React.useState(null);
62
+
63
+ const { selectionLayer, caretPosition } = selectedRegion;
64
+
65
+ React.useEffect(() => {
66
+ if (stateSelectedRegion !== undefined) {
67
+ setSelectedRegion(stateSelectedRegion);
68
+ } else {
69
+ setSelectedRegion({ selectionLayer: { start: -1, end: -1 }, caretPosition: -1 });
70
+ }
71
+ }, [stateSelectedRegion]);
72
+
73
+ const updateSelectedRegion = (eventOutput, isCaret) => {
74
+ if (isCaret) {
75
+ // TODO: something here?
76
+ } else {
77
+ const newRegion = transformToRegion(eventOutput);
78
+ const newTimeOutId = setTimeout(() => {
79
+ const parentSequenceIds = parentSource.input.map(({sequence}) => sequence);
80
+ // We add the current sequence to the selectedRegions array
81
+ const selectedRegions = [{ id: sequenceId, selectedRegion: newRegion }];
82
+ // If possible, add the equivalent region in the parent sequence
83
+ parentSequenceIds.forEach((id) => {
84
+ const selectionLayerAssembly = rangeInParent(newRegion.selectionLayer, id);
85
+ if (selectionLayerAssembly !== null) {
86
+ selectedRegions.push({ id, selectedRegion: { selectionLayer: selectionLayerAssembly, caretPosition: -1 } });
87
+ }
88
+ });
89
+ dispatch(setSelectedRegions(selectedRegions));
90
+ }, 500);
91
+ setSelectedRegion(newRegion);
92
+ setTimeOutId((prev) => {
93
+ clearTimeout(prev);
94
+ return newTimeOutId;
95
+ });
96
+ }
97
+ };
98
+
99
+ return (
100
+ <div>
101
+ <SimpleCircularOrLinearView {...{
102
+ sequenceData: seqCopy,
103
+ editorName,
104
+ height: seq.circular ? null : 'auto',
105
+ withCaretEnabled: true,
106
+ withSelectionEnabled: true,
107
+ selectionLayer,
108
+ selectionLayerUpdate: (a) => updateSelectedRegion(a, false),
109
+ // TODO: this does not work
110
+ caretPosition,
111
+ caretPositionUpdate: (a) => updateSelectedRegion(a, true),
112
+ }}
113
+ />
114
+ <OverhangsDisplay {...{ sequenceData: seq, sequence }} />
115
+ <AddSourceComponent {...{ sequenceId }} />
116
+ </div>
117
+ );
118
+ }
119
+
120
+ export default React.memo(SequenceEditor);
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import MainSequenceEditor from './MainSequenceEditor';
3
+ import PrimerDesigner from './primers/primer_design/SequenceTabComponents/PrimerDesigner';
4
+
5
+ function SequenceTab() {
6
+ return (
7
+ <>
8
+ <PrimerDesigner />
9
+ <MainSequenceEditor />
10
+ </>
11
+ );
12
+ }
13
+
14
+ export default SequenceTab;
@@ -0,0 +1,38 @@
1
+ import { Tooltip } from '@mui/material';
2
+ import React from 'react';
3
+ import { useSelector } from 'react-redux';
4
+
5
+ function TemplateSequence({ sequenceId }) {
6
+ const circular = useSelector((state) => state.cloning.sequences.find((e) => e.id === sequenceId).circular);
7
+ let svgContent = null;
8
+ let tooltipText = null;
9
+
10
+ if (circular === true) {
11
+ tooltipText = 'Circular sequence expected';
12
+ svgContent = <circle cx="60" cy="60" r="40" stroke="lightgrey" strokeWidth="9" fill="none" />;
13
+ } else if (circular === false) {
14
+ svgContent = <line x1="10" y1="60" x2="140" y2="60" stroke="lightgrey" strokeWidth="9" />;
15
+ tooltipText = 'Linear sequence expected';
16
+ } else {
17
+ svgContent = (
18
+ <>
19
+ <circle cx="30" cy="40" r="25" stroke="lightgrey" strokeWidth="5" fill="none" />
20
+ <line x1="10" y1="110" x2="110" y2="20" stroke="grey" strokeWidth="2" />
21
+ <line x1="60" y1="90" x2="110" y2="90" stroke="lightgrey" strokeWidth="5" />
22
+ </>
23
+ );
24
+ tooltipText = 'Linear/circular sequence expected';
25
+ }
26
+ const toolTipElement = <div style={{ fontSize: 'medium' }}>{tooltipText}</div>;
27
+ return (
28
+ <div>
29
+ <Tooltip title={toolTipElement}>
30
+ <svg width="120" height="120" xmlns="http://www.w3.org/2000/svg">
31
+ {svgContent}
32
+ </svg>
33
+ </Tooltip>
34
+ </div>
35
+ );
36
+ }
37
+
38
+ export default React.memo(TemplateSequence);
@@ -0,0 +1,33 @@
1
+ import { Dialog, DialogContent, Table, TableBody, TableCell, TableHead, TableRow } from '@mui/material';
2
+ import React from 'react';
3
+
4
+ function PlannotateAnnotationReport({ dialogOpen, setDialogOpen, report }) {
5
+ return (
6
+ <Dialog fullWidth maxWidth="lg" open={dialogOpen} onClose={() => setDialogOpen(false)}>
7
+ <DialogContent>
8
+ <Table sx={{ textAlign: 'center' }}>
9
+ <TableHead>
10
+ <TableRow>
11
+ <TableCell sx={{ fontWeight: 'bold', textAlign: 'center' }}>Feature</TableCell>
12
+ <TableCell sx={{ fontWeight: 'bold', textAlign: 'center' }}>Percent identity</TableCell>
13
+ <TableCell sx={{ fontWeight: 'bold', textAlign: 'center' }}>Percent match length</TableCell>
14
+ <TableCell sx={{ fontWeight: 'bold', textAlign: 'center' }}>Description</TableCell>
15
+ </TableRow>
16
+ </TableHead>
17
+ <TableBody>
18
+ {report.map((row) => (
19
+ <TableRow key={`${row.Feature}-${row.Description}`}>
20
+ <TableCell>{row.Feature}</TableCell>
21
+ <TableCell sx={{ textAlign: 'center' }}>{Number(row.percent_identity).toFixed(0)}</TableCell>
22
+ <TableCell sx={{ textAlign: 'center' }}>{Number(row.percent_match_length).toFixed(0)}</TableCell>
23
+ <TableCell>{row.Description}</TableCell>
24
+ </TableRow>
25
+ ))}
26
+ </TableBody>
27
+ </Table>
28
+ </DialogContent>
29
+ </Dialog>
30
+ );
31
+ }
32
+
33
+ export default PlannotateAnnotationReport;
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import { useDispatch, useStore } from 'react-redux';
3
+ import useAlerts from '../../hooks/useAlerts';
4
+ import { jsonToGenbank } from '@teselagen/bio-parsers';
5
+ import { cloningActions } from '@opencloning/store/cloning';
6
+ import useStoreEditor from '../../hooks/useStoreEditor';
7
+
8
+ const { updateSequence } = cloningActions;
9
+
10
+ export default function useUpdateAnnotationInMainSequence() {
11
+ const store = useStore();
12
+ const { addAlert } = useAlerts();
13
+ const dispatch = useDispatch();
14
+ const { updateStoreEditor } = useStoreEditor();
15
+
16
+ const updateFunction = React.useCallback(() => {
17
+ const state = store.getState();
18
+ const mainSequenceId = state.cloning.mainSequenceId;
19
+ const currentSequenceData = state.cloning.teselaJsonCache[mainSequenceId];
20
+ const newSequenceData = state.VectorEditor.mainEditor.sequenceData;
21
+ const mainSequence = state.cloning.sequences.find((s) => s.id === mainSequenceId);
22
+ const newSequence = { ...mainSequence, file_content: jsonToGenbank(newSequenceData) };
23
+ if (currentSequenceData.sequence.toUpperCase() === newSequenceData.sequence.toUpperCase()) {
24
+ addAlert({
25
+ message: 'Annotation updated',
26
+ severity: 'success',
27
+ });
28
+ dispatch(updateSequence(newSequence));
29
+ // Clear the sequenceDataHistory
30
+ updateStoreEditor('mainEditor', mainSequenceId, { sequenceDataHistory: {} });
31
+ } else {
32
+ addAlert({
33
+ message: 'Sequences are different!',
34
+ severity: 'error',
35
+ });
36
+ }
37
+ }, [store, addAlert, dispatch, updateStoreEditor]);
38
+ return updateFunction;
39
+ };