@opencloning/ui 1.2.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/package.json +4 -3
- package/src/components/assembler/Assembler.cy.jsx +364 -0
- package/src/components/assembler/Assembler.jsx +298 -205
- package/src/components/assembler/AssemblerPart.cy.jsx +52 -0
- package/src/components/assembler/AssemblerPart.jsx +51 -79
- package/src/components/assembler/ExistingSyntaxDialog.cy.jsx +251 -0
- package/src/components/assembler/ExistingSyntaxDialog.jsx +104 -0
- package/src/components/assembler/PlasmidSyntaxTable.jsx +83 -0
- package/src/components/assembler/assembler_utils.js +134 -0
- package/src/components/assembler/assembler_utils.test.js +193 -0
- package/src/components/assembler/assembly_component.module.css +1 -1
- package/src/components/assembler/graph_utils.js +153 -0
- package/src/components/assembler/graph_utils.test.js +239 -0
- package/src/components/assembler/index.js +9 -0
- package/src/components/assembler/useAssembler.js +59 -22
- package/src/components/assembler/useCombinatorialAssembly.js +76 -0
- package/src/components/assembler/usePlasmidsLogic.js +82 -0
- package/src/components/eLabFTW/utils.js +0 -9
- package/src/components/index.js +2 -0
- package/src/components/navigation/SelectTemplateDialog.jsx +0 -1
- package/src/components/primers/DownloadPrimersButton.jsx +0 -1
- package/src/components/primers/PrimerList.jsx +4 -3
- package/src/components/primers/import_primers/ImportPrimersButton.jsx +0 -1
- package/src/version.js +1 -1
- package/vitest.config.js +2 -4
- package/src/components/DraggableDialogPaper.jsx +0 -16
- package/src/components/assembler/AssemblePartWidget.jsx +0 -252
- package/src/components/assembler/StopIcon.jsx +0 -34
- package/src/components/assembler/assembler_data2.json +0 -50
- package/src/components/assembler/moclo.json +0 -110
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { TextField, FormControl, InputLabel, Select, MenuItem, Box, Grid, Paper, Typography, Table, TableContainer, TableHead, TableBody, TableRow, TableCell, Button } from '@mui/material'
|
|
3
|
-
import { ContentCopy as ContentCopyIcon } from '@mui/icons-material'
|
|
4
|
-
import AssemblerPart from './AssemblerPart'
|
|
5
|
-
|
|
6
|
-
/* eslint-disable camelcase */
|
|
7
|
-
const defaultData = {
|
|
8
|
-
header: 'Header',
|
|
9
|
-
body: 'helper text / body text',
|
|
10
|
-
glyph: 'cds-stop',
|
|
11
|
-
left_overhang: 'CATG',
|
|
12
|
-
right_overhang: 'TATG',
|
|
13
|
-
left_inside: 'AAAATA',
|
|
14
|
-
right_inside: 'AATG',
|
|
15
|
-
left_codon_start: 2,
|
|
16
|
-
right_codon_start: 1,
|
|
17
|
-
color: 'greenyellow',
|
|
18
|
-
}
|
|
19
|
-
/* eslint-enable camelcase */
|
|
20
|
-
|
|
21
|
-
const glyphOptions = [
|
|
22
|
-
'assembly-scar',
|
|
23
|
-
'cds',
|
|
24
|
-
'cds-stop',
|
|
25
|
-
'chromosomal-locus',
|
|
26
|
-
'engineered-region',
|
|
27
|
-
'five-prime-sticky-restriction-site',
|
|
28
|
-
'origin-of-replication',
|
|
29
|
-
'primer-binding-site',
|
|
30
|
-
'promoter',
|
|
31
|
-
'ribosome-entry-site',
|
|
32
|
-
'specific-recombination-site',
|
|
33
|
-
'terminator',
|
|
34
|
-
'three-prime-sticky-restriction-site',
|
|
35
|
-
]
|
|
36
|
-
|
|
37
|
-
function AssemblePartWidget() {
|
|
38
|
-
const [formData, setFormData] = React.useState(defaultData)
|
|
39
|
-
|
|
40
|
-
const handleChange = (field) => (event) => {
|
|
41
|
-
const value = event.target.value
|
|
42
|
-
setFormData((prev) => ({
|
|
43
|
-
...prev,
|
|
44
|
-
[field]: field === 'left_codon_start' || field === 'right_codon_start'
|
|
45
|
-
? (value === '' ? '' : parseInt(value, 10) || 0)
|
|
46
|
-
: value,
|
|
47
|
-
}))
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const handleCopyRow = async () => {
|
|
51
|
-
const keys = Object.keys(formData)
|
|
52
|
-
const headers = keys.join('\t')
|
|
53
|
-
const values = keys.map((key) => String(formData[key])).join('\t')
|
|
54
|
-
const tsvData = `${headers}\n${values}`
|
|
55
|
-
|
|
56
|
-
try {
|
|
57
|
-
if (window.navigator && window.navigator.clipboard) {
|
|
58
|
-
await window.navigator.clipboard.writeText(tsvData)
|
|
59
|
-
}
|
|
60
|
-
} catch (err) {
|
|
61
|
-
// eslint-disable-next-line no-console
|
|
62
|
-
console.error('Failed to copy to clipboard:', err)
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return (
|
|
67
|
-
<Box sx={{
|
|
68
|
-
p: 1.5,
|
|
69
|
-
maxHeight: '100vh',
|
|
70
|
-
overflowY: 'auto',
|
|
71
|
-
overflowX: 'hidden'
|
|
72
|
-
}}>
|
|
73
|
-
<Grid container spacing={2}>
|
|
74
|
-
<Grid item xs={12} md={6}>
|
|
75
|
-
<Paper sx={{ p: 1.5 }}>
|
|
76
|
-
<Typography variant="h6" gutterBottom sx={{ mb: 1.5 }}>
|
|
77
|
-
Part Configuration
|
|
78
|
-
</Typography>
|
|
79
|
-
<Box component="form" sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
|
80
|
-
<TextField
|
|
81
|
-
size="small"
|
|
82
|
-
label="Header"
|
|
83
|
-
value={formData.header}
|
|
84
|
-
onChange={handleChange('header')}
|
|
85
|
-
fullWidth
|
|
86
|
-
/>
|
|
87
|
-
<TextField
|
|
88
|
-
size="small"
|
|
89
|
-
label="Body"
|
|
90
|
-
value={formData.body}
|
|
91
|
-
onChange={handleChange('body')}
|
|
92
|
-
fullWidth
|
|
93
|
-
multiline
|
|
94
|
-
rows={2}
|
|
95
|
-
/>
|
|
96
|
-
<FormControl fullWidth size="small">
|
|
97
|
-
<InputLabel id="glyph-select-label">Glyph</InputLabel>
|
|
98
|
-
<Select
|
|
99
|
-
labelId="glyph-select-label"
|
|
100
|
-
value={formData.glyph}
|
|
101
|
-
label="Glyph"
|
|
102
|
-
onChange={handleChange('glyph')}
|
|
103
|
-
>
|
|
104
|
-
{glyphOptions.map((option) => (
|
|
105
|
-
<MenuItem key={option} value={option}>
|
|
106
|
-
{option}
|
|
107
|
-
</MenuItem>
|
|
108
|
-
))}
|
|
109
|
-
</Select>
|
|
110
|
-
</FormControl>
|
|
111
|
-
<TextField
|
|
112
|
-
size="small"
|
|
113
|
-
label="Color"
|
|
114
|
-
value={formData.color}
|
|
115
|
-
onChange={handleChange('color')}
|
|
116
|
-
fullWidth
|
|
117
|
-
helperText="CSS color name or hex code"
|
|
118
|
-
/>
|
|
119
|
-
<Typography variant="subtitle2" sx={{ mt: 0.5, mb: 0.5 }}>
|
|
120
|
-
Left Side
|
|
121
|
-
</Typography>
|
|
122
|
-
<TextField
|
|
123
|
-
size="small"
|
|
124
|
-
label="Left Overhang"
|
|
125
|
-
value={formData.left_overhang}
|
|
126
|
-
onChange={handleChange('left_overhang')}
|
|
127
|
-
error={formData.left_overhang.length !== 4}
|
|
128
|
-
helperText={formData.left_overhang.length !== 4 ? 'Must be 4 bases' : ''}
|
|
129
|
-
fullWidth
|
|
130
|
-
/>
|
|
131
|
-
<TextField
|
|
132
|
-
size="small"
|
|
133
|
-
label="Left Inside"
|
|
134
|
-
value={formData.left_inside}
|
|
135
|
-
onChange={handleChange('left_inside')}
|
|
136
|
-
fullWidth
|
|
137
|
-
/>
|
|
138
|
-
<TextField
|
|
139
|
-
size="small"
|
|
140
|
-
label="Left Codon Start"
|
|
141
|
-
type="number"
|
|
142
|
-
value={formData.left_codon_start}
|
|
143
|
-
onChange={handleChange('left_codon_start')}
|
|
144
|
-
fullWidth
|
|
145
|
-
inputProps={{ min: 0 }}
|
|
146
|
-
helperText="If the left side is translated, where the codon starts"
|
|
147
|
-
/>
|
|
148
|
-
<Typography variant="subtitle2" sx={{ mt: 0.5, mb: 0.5 }}>
|
|
149
|
-
Right Side
|
|
150
|
-
</Typography>
|
|
151
|
-
<TextField
|
|
152
|
-
size="small"
|
|
153
|
-
label="Right Overhang"
|
|
154
|
-
value={formData.right_overhang}
|
|
155
|
-
onChange={handleChange('right_overhang')}
|
|
156
|
-
fullWidth
|
|
157
|
-
/>
|
|
158
|
-
<TextField
|
|
159
|
-
size="small"
|
|
160
|
-
label="Right Inside"
|
|
161
|
-
value={formData.right_inside}
|
|
162
|
-
onChange={handleChange('right_inside')}
|
|
163
|
-
fullWidth
|
|
164
|
-
/>
|
|
165
|
-
<TextField
|
|
166
|
-
size="small"
|
|
167
|
-
label="Right Codon Start"
|
|
168
|
-
type="number"
|
|
169
|
-
value={formData.right_codon_start}
|
|
170
|
-
onChange={handleChange('right_codon_start')}
|
|
171
|
-
fullWidth
|
|
172
|
-
inputProps={{ min: 0 }}
|
|
173
|
-
helperText="If the right side is translated, where the codon starts"
|
|
174
|
-
/>
|
|
175
|
-
</Box>
|
|
176
|
-
</Paper>
|
|
177
|
-
</Grid>
|
|
178
|
-
<Grid item xs={12} md={6}>
|
|
179
|
-
<Paper sx={{ p: 1.5 }}>
|
|
180
|
-
<Typography variant="h6" gutterBottom sx={{ mb: 1.5 }}>
|
|
181
|
-
Preview
|
|
182
|
-
</Typography>
|
|
183
|
-
<Box sx={{ mt: 1, display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
|
184
|
-
{(formData.header || formData.body) && (
|
|
185
|
-
<Box sx={{
|
|
186
|
-
textAlign: 'center',
|
|
187
|
-
mb: 1.5,
|
|
188
|
-
display: 'flex',
|
|
189
|
-
flexDirection: 'column',
|
|
190
|
-
gap: 0.5
|
|
191
|
-
}}>
|
|
192
|
-
{formData.header && (
|
|
193
|
-
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>
|
|
194
|
-
{formData.header}
|
|
195
|
-
</Typography>
|
|
196
|
-
)}
|
|
197
|
-
{formData.body && (
|
|
198
|
-
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
|
199
|
-
{formData.body}
|
|
200
|
-
</Typography>
|
|
201
|
-
)}
|
|
202
|
-
</Box>
|
|
203
|
-
)}
|
|
204
|
-
<AssemblerPart data={formData} />
|
|
205
|
-
</Box>
|
|
206
|
-
</Paper>
|
|
207
|
-
</Grid>
|
|
208
|
-
</Grid>
|
|
209
|
-
<Box sx={{ mt: 2 }}>
|
|
210
|
-
<Paper sx={{ p: 1.5 }}>
|
|
211
|
-
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 1.5 }}>
|
|
212
|
-
<Typography variant="h6">
|
|
213
|
-
JSON Data
|
|
214
|
-
</Typography>
|
|
215
|
-
<Button
|
|
216
|
-
size="small"
|
|
217
|
-
variant="outlined"
|
|
218
|
-
startIcon={<ContentCopyIcon />}
|
|
219
|
-
onClick={handleCopyRow}
|
|
220
|
-
>
|
|
221
|
-
Copy Row
|
|
222
|
-
</Button>
|
|
223
|
-
</Box>
|
|
224
|
-
<TableContainer>
|
|
225
|
-
<Table size="small" sx={{ '& .MuiTableCell-root': { py: 0.5, px: 1 } }}>
|
|
226
|
-
<TableHead>
|
|
227
|
-
<TableRow>
|
|
228
|
-
{Object.keys(formData).map((key) => (
|
|
229
|
-
<TableCell key={key} sx={{ fontWeight: 'bold' }}>
|
|
230
|
-
{key}
|
|
231
|
-
</TableCell>
|
|
232
|
-
))}
|
|
233
|
-
</TableRow>
|
|
234
|
-
</TableHead>
|
|
235
|
-
<TableBody>
|
|
236
|
-
<TableRow>
|
|
237
|
-
{Object.keys(formData).map((key) => (
|
|
238
|
-
<TableCell key={key}>
|
|
239
|
-
{String(formData[key])}
|
|
240
|
-
</TableCell>
|
|
241
|
-
))}
|
|
242
|
-
</TableRow>
|
|
243
|
-
</TableBody>
|
|
244
|
-
</Table>
|
|
245
|
-
</TableContainer>
|
|
246
|
-
</Paper>
|
|
247
|
-
</Box>
|
|
248
|
-
</Box>
|
|
249
|
-
)
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
export default AssemblePartWidget
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
|
|
3
|
-
function StopIcon({ color = 'currentColor', size = 24, ...props }) {
|
|
4
|
-
return (
|
|
5
|
-
<svg xmlns="http://www.w3.org/2000/svg"
|
|
6
|
-
viewBox="0 0 24 24"
|
|
7
|
-
width={size} height={size}
|
|
8
|
-
role="img"
|
|
9
|
-
aria-label="Stop sign icon"
|
|
10
|
-
{...props}
|
|
11
|
-
>
|
|
12
|
-
<mask id="stop-mask">
|
|
13
|
-
{/* Octagon area (visible part) */}
|
|
14
|
-
<polygon fill="white"
|
|
15
|
-
points="7.07,2 16.93,2 22,7.07 22,16.93 16.93,22 7.07,22 2,16.93 2,7.07" />
|
|
16
|
-
{/* Text area (cut out) */}
|
|
17
|
-
<text x="12" y="12.3"
|
|
18
|
-
fill="black"
|
|
19
|
-
fontFamily="Arial, Helvetica, sans-serif"
|
|
20
|
-
fontWeight="700"
|
|
21
|
-
fontSize="7.2"
|
|
22
|
-
textAnchor="middle"
|
|
23
|
-
dominantBaseline="middle">STOP</text>
|
|
24
|
-
</mask>
|
|
25
|
-
|
|
26
|
-
{/* Octagon using mask */}
|
|
27
|
-
<rect width="24" height="24" fill={color} mask="url(#stop-mask)" />
|
|
28
|
-
</svg>
|
|
29
|
-
)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export default StopIcon
|
|
33
|
-
|
|
34
|
-
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"name": "A",
|
|
4
|
-
"overhang": "GGAG"
|
|
5
|
-
},
|
|
6
|
-
{
|
|
7
|
-
"name": "B",
|
|
8
|
-
"overhang": "TACT"
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
"name": "T1",
|
|
12
|
-
"overhang": "CCAT"
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
"name": "T2",
|
|
16
|
-
"overhang": "GTCA"
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
"name": "T3",
|
|
20
|
-
"overhang": "TCCA"
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
"name": "C",
|
|
24
|
-
"overhang": "AATG"
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
"name": "D",
|
|
28
|
-
"overhang": "AGGT"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"name": "T4",
|
|
32
|
-
"overhang": "TTCG"
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
"name": "T5",
|
|
36
|
-
"overhang": "CGGC"
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
"name": "E",
|
|
40
|
-
"overhang": "GCTT"
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
"name": "F",
|
|
44
|
-
"overhang": "CGCT"
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
"name": "backbone",
|
|
48
|
-
"overhang": "GGAG"
|
|
49
|
-
}
|
|
50
|
-
]
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"header": "Assembly connector",
|
|
4
|
-
"body": "",
|
|
5
|
-
"glyph": "three-prime-sticky-restriction-site",
|
|
6
|
-
"left_overhang": "CCCT",
|
|
7
|
-
"right_overhang": "AACG",
|
|
8
|
-
"left_inside": "",
|
|
9
|
-
"right_inside": "",
|
|
10
|
-
"left_codon_start": null,
|
|
11
|
-
"right_codon_start": null,
|
|
12
|
-
"color": "#92C3DC"
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
"header": "Promoter",
|
|
16
|
-
"body": "",
|
|
17
|
-
"glyph": "promoter",
|
|
18
|
-
"left_overhang": "AACG",
|
|
19
|
-
"right_overhang": "TATG",
|
|
20
|
-
"left_inside": "",
|
|
21
|
-
"right_inside": "",
|
|
22
|
-
"left_codon_start": null,
|
|
23
|
-
"right_codon_start": null,
|
|
24
|
-
"color": "#B2D06E"
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
"header": "Coding sequence",
|
|
28
|
-
"body": "You can use the ATG from the fusion site as translation start. If possible omit STOP codon and make your protein in frame with the Ser.",
|
|
29
|
-
"glyph": "cds",
|
|
30
|
-
"left_overhang": "TATG",
|
|
31
|
-
"right_overhang": "ATCC",
|
|
32
|
-
"left_inside": "",
|
|
33
|
-
"right_inside": "",
|
|
34
|
-
"left_codon_start": 2,
|
|
35
|
-
"right_codon_start": 2,
|
|
36
|
-
"color": "#F1EC9B"
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
"header": "Terminator",
|
|
40
|
-
"body": "",
|
|
41
|
-
"glyph": "terminator",
|
|
42
|
-
"left_overhang": "ATCC",
|
|
43
|
-
"right_overhang": "GCTG",
|
|
44
|
-
"left_inside": "",
|
|
45
|
-
"right_inside": "",
|
|
46
|
-
"left_codon_start": null,
|
|
47
|
-
"right_codon_start": null,
|
|
48
|
-
"color": "#E2929E"
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"header": "Assembly connector",
|
|
52
|
-
"body": "",
|
|
53
|
-
"glyph": "three-prime-sticky-restriction-site",
|
|
54
|
-
"left_overhang": "GCTG",
|
|
55
|
-
"right_overhang": "TACA",
|
|
56
|
-
"left_inside": "",
|
|
57
|
-
"right_inside": "",
|
|
58
|
-
"left_codon_start": null,
|
|
59
|
-
"right_codon_start": null,
|
|
60
|
-
"color": "#7C6AA9"
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
"header": "Yeast marker",
|
|
64
|
-
"body": "",
|
|
65
|
-
"glyph": "engineered-region",
|
|
66
|
-
"left_overhang": "TACA",
|
|
67
|
-
"right_overhang": "GAGT",
|
|
68
|
-
"left_inside": "",
|
|
69
|
-
"right_inside": "",
|
|
70
|
-
"left_codon_start": null,
|
|
71
|
-
"right_codon_start": null,
|
|
72
|
-
"color": "#EFD198"
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
"header": "Yeast plasmid origin of replication",
|
|
76
|
-
"body": "",
|
|
77
|
-
"glyph": "origin-of-replication",
|
|
78
|
-
"left_overhang": "GAGT",
|
|
79
|
-
"right_overhang": "CCGA",
|
|
80
|
-
"left_inside": "",
|
|
81
|
-
"right_inside": "",
|
|
82
|
-
"left_codon_start": null,
|
|
83
|
-
"right_codon_start": null,
|
|
84
|
-
"color": "#85643E"
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
"header": "Bacterial marker and origin",
|
|
88
|
-
"body": "",
|
|
89
|
-
"glyph": "engineered-region",
|
|
90
|
-
"left_overhang": "CCGA",
|
|
91
|
-
"right_overhang": "CCCT",
|
|
92
|
-
"left_inside": "",
|
|
93
|
-
"right_inside": "",
|
|
94
|
-
"left_codon_start": null,
|
|
95
|
-
"right_codon_start": null,
|
|
96
|
-
"color": "#7F7F7F"
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
"header": "Dummy",
|
|
100
|
-
"body": "promoter text",
|
|
101
|
-
"glyph": "cds-stop",
|
|
102
|
-
"left_overhang": "CATG",
|
|
103
|
-
"right_overhang": "TATG",
|
|
104
|
-
"left_inside": "AAAATA",
|
|
105
|
-
"right_inside": "AATG",
|
|
106
|
-
"left_codon_start": 2,
|
|
107
|
-
"right_codon_start": 1,
|
|
108
|
-
"color": "greenyellow"
|
|
109
|
-
}
|
|
110
|
-
]
|