@teselagen/ove 0.8.2 → 0.8.4
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/AlignmentView/LabileSitesLayer.d.ts +13 -0
- package/AlignmentView/PairwiseAlignmentView.d.ts +1 -9
- package/BarPlot/index.d.ts +33 -0
- package/LinearView/SequenceName.d.ts +2 -1
- package/PropertySidePanel/calculateAminoAcidFrequency.d.ts +46 -0
- package/PropertySidePanel/index.d.ts +6 -0
- package/RowItem/Caret/index.d.ts +2 -1
- package/StatusBar/index.d.ts +2 -1
- package/aaprops.svg +2287 -0
- package/constants/dnaToColor.d.ts +122 -4
- package/index.cjs.js +4214 -7859
- package/index.es.js +2166 -5811
- package/index.umd.js +3745 -7390
- package/ove.css +100 -19
- package/package.json +2 -2
- package/src/AlignmentView/AlignmentVisibilityTool.js +141 -37
- package/src/AlignmentView/LabileSitesLayer.js +33 -0
- package/src/AlignmentView/Minimap.js +5 -3
- package/src/AlignmentView/PairwiseAlignmentView.js +55 -61
- package/src/AlignmentView/index.js +476 -257
- package/src/AlignmentView/style.css +27 -0
- package/src/BarPlot/index.js +156 -0
- package/src/CircularView/Caret.js +8 -2
- package/src/CircularView/SelectionLayer.js +4 -2
- package/src/CircularView/index.js +5 -1
- package/src/Editor/darkmode.css +10 -0
- package/src/Editor/index.js +3 -0
- package/src/Editor/userDefinedHandlersAndOpts.js +2 -1
- package/src/FindBar/index.js +2 -3
- package/src/LinearView/SequenceName.js +8 -2
- package/src/LinearView/index.js +21 -0
- package/src/PropertySidePanel/calculateAminoAcidFrequency.js +77 -0
- package/src/PropertySidePanel/index.js +236 -0
- package/src/PropertySidePanel/style.css +39 -0
- package/src/RowItem/Caret/index.js +8 -2
- package/src/RowItem/Labels.js +1 -1
- package/src/RowItem/SelectionLayer/index.js +5 -1
- package/src/RowItem/Sequence.js +99 -5
- package/src/RowItem/Translations/Translation.js +3 -2
- package/src/RowItem/Translations/index.js +2 -0
- package/src/RowItem/index.js +74 -8
- package/src/RowItem/style.css +3 -4
- package/src/StatusBar/index.js +11 -4
- package/src/constants/dnaToColor.js +151 -0
- package/src/helperComponents/PinchHelper/PinchHelper.js +5 -1
- package/src/helperComponents/SelectDialog.js +5 -2
- package/src/style.css +2 -2
- package/src/utils/editorUtils.js +5 -3
- package/src/utils/getAlignedAminoAcidSequenceProps.js +379 -0
- package/src/withEditorInteractions/createSequenceInputPopup.js +19 -5
- package/src/withEditorInteractions/index.js +9 -3
- package/utils/editorUtils.d.ts +2 -1
- package/utils/getAlignedAminoAcidSequenceProps.d.ts +49 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
function calculatePairwiseIdentity(seq1, seq2, excludeGaps = true) {
|
|
2
|
+
if (seq1.length !== seq2.length) {
|
|
3
|
+
throw new Error("Sequences must be aligned (same length)");
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
let identicalPositions = 0;
|
|
7
|
+
let validPositions = 0;
|
|
8
|
+
|
|
9
|
+
for (let i = 0; i < seq1.length; i++) {
|
|
10
|
+
const aa1 = seq1[i].toUpperCase();
|
|
11
|
+
const aa2 = seq2[i].toUpperCase();
|
|
12
|
+
|
|
13
|
+
// Skip positions with gaps if excludeGaps is true
|
|
14
|
+
if (excludeGaps && aa1 === "-" && aa2 === "-") {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
validPositions++;
|
|
19
|
+
|
|
20
|
+
if (aa1 === aa2) {
|
|
21
|
+
identicalPositions++;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return { validPositions, identicalPositions };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function calculateIdentityMatrix(alignedSequences) {
|
|
28
|
+
const n = Object.keys(alignedSequences).length;
|
|
29
|
+
const identityMatrix = Array(n)
|
|
30
|
+
.fill(n)
|
|
31
|
+
.map(() => Array(n).fill(0));
|
|
32
|
+
const sequenceNames = Object.keys(alignedSequences);
|
|
33
|
+
const _identicalPositions = [];
|
|
34
|
+
|
|
35
|
+
// Calculate pairwise identities
|
|
36
|
+
for (let i = 0; i < n; i++) {
|
|
37
|
+
for (let j = i; j < n; j++) {
|
|
38
|
+
if (i === j) {
|
|
39
|
+
identityMatrix[i][j] = 100.0; // Self-identity
|
|
40
|
+
} else {
|
|
41
|
+
const seq1 = alignedSequences[sequenceNames[i]];
|
|
42
|
+
const seq2 = alignedSequences[sequenceNames[j]];
|
|
43
|
+
const { validPositions, identicalPositions } =
|
|
44
|
+
calculatePairwiseIdentity(seq1, seq2, true);
|
|
45
|
+
|
|
46
|
+
const identityPercentage =
|
|
47
|
+
validPositions > 0 ? (identicalPositions / validPositions) * 100 : 0;
|
|
48
|
+
|
|
49
|
+
_identicalPositions.push({
|
|
50
|
+
identicalPositions,
|
|
51
|
+
seqs: [sequenceNames[i], sequenceNames[j]]
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
identityMatrix[i][j] = identityPercentage;
|
|
55
|
+
identityMatrix[j][i] = identityPercentage;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
matrix: identityMatrix,
|
|
62
|
+
sequenceNames: sequenceNames,
|
|
63
|
+
identicalPositions: _identicalPositions
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getPropertyAnalysis(alignedSequences) {
|
|
68
|
+
const sequences = Object.values(alignedSequences).slice(1);
|
|
69
|
+
|
|
70
|
+
function getResidueProperties(residue, map) {
|
|
71
|
+
return Object.keys(map).filter(key => map[key].includes(residue));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 4. Intersection helper
|
|
75
|
+
function intersectHelper(arrays) {
|
|
76
|
+
if (arrays.length === 0) return [];
|
|
77
|
+
return arrays.reduce((a, b) => a.filter(x => b.includes(x)));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const seqLength = sequences[0].length;
|
|
81
|
+
|
|
82
|
+
return Array.from({ length: seqLength }, (_, pos) => {
|
|
83
|
+
const residues = sequences.map(seq => seq[pos]).filter(r => r !== "-");
|
|
84
|
+
|
|
85
|
+
const sizeProps = residues.map(r =>
|
|
86
|
+
getResidueProperties(r, residueSizeMap)
|
|
87
|
+
);
|
|
88
|
+
const polarityProps = residues.map(r =>
|
|
89
|
+
getResidueProperties(r, polarityMap)
|
|
90
|
+
);
|
|
91
|
+
const specificGroupProps = residues.map(r =>
|
|
92
|
+
getResidueProperties(r, specificGroupMap)
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const shared = {
|
|
96
|
+
size: intersectHelper(sizeProps),
|
|
97
|
+
polarity: intersectHelper(polarityProps),
|
|
98
|
+
specificGroup: intersectHelper(specificGroupProps)
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const group =
|
|
102
|
+
shared.specificGroup.length > 0
|
|
103
|
+
? shared.specificGroup[0]
|
|
104
|
+
: `${shared.size.length ? shared.size[0] : ""} ${shared.polarity.length ? shared.polarity[0] : ""}`;
|
|
105
|
+
|
|
106
|
+
function mostFrequent(arr) {
|
|
107
|
+
const freq = {};
|
|
108
|
+
arr.forEach(val => {
|
|
109
|
+
if (val !== "none") freq[val] = (freq[val] || 0) + 1;
|
|
110
|
+
});
|
|
111
|
+
const max = Math.max(...Object.values(freq), 0);
|
|
112
|
+
const mostFrequentProps = Object.entries(freq)
|
|
113
|
+
.filter(([, count]) => count === max)
|
|
114
|
+
.map(([prop]) => prop);
|
|
115
|
+
return {
|
|
116
|
+
props: mostFrequentProps,
|
|
117
|
+
count: max
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let mostFreqGroup;
|
|
122
|
+
if (group.trim() === "") {
|
|
123
|
+
const mostFreqResidueGroups = {
|
|
124
|
+
size: mostFrequent(sizeProps.flat()),
|
|
125
|
+
polarity: mostFrequent(polarityProps.flat()),
|
|
126
|
+
specificGroup: mostFrequent(specificGroupProps.flat())
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const sortedGroups = Object.entries(mostFreqResidueGroups).sort(
|
|
130
|
+
(a, b) => b[1].count - a[1].count
|
|
131
|
+
);
|
|
132
|
+
const topCount = sortedGroups[0][1].count;
|
|
133
|
+
const topOrTiedGroups = sortedGroups.filter(
|
|
134
|
+
([, val]) => val.count === topCount
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const specificGroupEntry = topOrTiedGroups.find(
|
|
138
|
+
([key]) => key === "specificGroup"
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
if (specificGroupEntry) {
|
|
142
|
+
mostFreqGroup = specificGroupEntry[1].props.join(" ");
|
|
143
|
+
} else {
|
|
144
|
+
const allProps = [
|
|
145
|
+
...new Set(topOrTiedGroups.flatMap(([, val]) => val.props))
|
|
146
|
+
];
|
|
147
|
+
mostFreqGroup = allProps.join(" ");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const aaGroup = group.trim() || mostFreqGroup.trim();
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
position: pos,
|
|
155
|
+
residues,
|
|
156
|
+
group: aaGroup,
|
|
157
|
+
color: combineGroupColors(aaGroup.split(" "))
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function getIdentityAndFrequencies(alignedSequences) {
|
|
163
|
+
const sequences = Object.values(alignedSequences).slice(1); // Exclude consensus sequence
|
|
164
|
+
const alignmentLength = sequences[0].length;
|
|
165
|
+
|
|
166
|
+
let totalScore = 0;
|
|
167
|
+
let totalPositions = 0;
|
|
168
|
+
const identityPercentages = [];
|
|
169
|
+
const propertyFrequencies = [];
|
|
170
|
+
|
|
171
|
+
for (let pos = 0; pos < alignmentLength; pos++) {
|
|
172
|
+
const column = sequences.map(seq => seq[pos]);
|
|
173
|
+
const nonGapResidues = column;
|
|
174
|
+
const propertyCounts = {};
|
|
175
|
+
|
|
176
|
+
if (nonGapResidues.length === 0) identityPercentages.push(0);
|
|
177
|
+
if (nonGapResidues.length < 2) continue; // Skip if <2 sequences have residues
|
|
178
|
+
|
|
179
|
+
// Calculate conservation score for this position
|
|
180
|
+
let totalProperties = 0;
|
|
181
|
+
const residueCounts = {};
|
|
182
|
+
nonGapResidues.forEach(aa => {
|
|
183
|
+
residueCounts[aa] = (residueCounts[aa] || 0) + 1;
|
|
184
|
+
|
|
185
|
+
const props = residuePropertyMap[aa] || [];
|
|
186
|
+
|
|
187
|
+
props.forEach(prop => {
|
|
188
|
+
propertyCounts[prop] = (propertyCounts[prop] || 0) + 1;
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
totalProperties++;
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const propertyPercentages = {};
|
|
195
|
+
Object.entries(propertyCounts).forEach(([prop, count]) => {
|
|
196
|
+
propertyPercentages[prop] = (count / totalProperties) * 100;
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const maxCount = Math.max(...Object.values(residueCounts));
|
|
200
|
+
const positionScore = maxCount / nonGapResidues.length;
|
|
201
|
+
identityPercentages.push((maxCount / nonGapResidues.length) * 100);
|
|
202
|
+
propertyFrequencies.push(propertyPercentages);
|
|
203
|
+
|
|
204
|
+
totalScore += positionScore;
|
|
205
|
+
totalPositions++;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const overallIdentity =
|
|
209
|
+
totalPositions > 0 ? (totalScore / totalPositions) * 100 : 0;
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
overallIdentity,
|
|
213
|
+
frequencies: identityPercentages
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function getLabileSites(alignedSequences, threshold = 0.5) {
|
|
218
|
+
const sequences = Object.values(alignedSequences);
|
|
219
|
+
const alignmentLength = sequences[0].length;
|
|
220
|
+
|
|
221
|
+
const labileSites = [];
|
|
222
|
+
const conservationScores = [];
|
|
223
|
+
|
|
224
|
+
for (let pos = 0; pos < alignmentLength; pos++) {
|
|
225
|
+
const column = sequences.map(seq => seq[pos]);
|
|
226
|
+
const nonGapResidues = column.filter(aa => aa !== "-");
|
|
227
|
+
|
|
228
|
+
if (nonGapResidues.length < 2) {
|
|
229
|
+
conservationScores.push(null); // Skip gap-only columns
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Count residue frequencies
|
|
234
|
+
const counts = {};
|
|
235
|
+
nonGapResidues.forEach(aa => {
|
|
236
|
+
counts[aa] = (counts[aa] || 0) + 1;
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// Calculate conservation score (0 = completely variable, 1 = completely conserved)
|
|
240
|
+
const maxCount = Math.max(...Object.values(counts));
|
|
241
|
+
const conservationScore = maxCount / nonGapResidues.length;
|
|
242
|
+
|
|
243
|
+
conservationScores.push(conservationScore);
|
|
244
|
+
|
|
245
|
+
// Identify labile sites (low conservation)
|
|
246
|
+
if (conservationScore <= threshold) {
|
|
247
|
+
labileSites.push({
|
|
248
|
+
position: pos + 1, // 1-based indexing
|
|
249
|
+
conservationScore: conservationScore,
|
|
250
|
+
residueVariation: Object.keys(counts),
|
|
251
|
+
frequencies: counts
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
sites: labileSites,
|
|
258
|
+
conservationScores: conservationScores,
|
|
259
|
+
totalLabileSites: labileSites.length,
|
|
260
|
+
percentageLabile: (labileSites.length / alignmentLength) * 100
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export const getAlignedAminoAcidSequenceProps = tracks => {
|
|
265
|
+
let sequences = {};
|
|
266
|
+
|
|
267
|
+
tracks.forEach(at => {
|
|
268
|
+
sequences = {
|
|
269
|
+
...sequences,
|
|
270
|
+
[at.alignmentData.name]: at.alignmentData.sequence
|
|
271
|
+
};
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const identity = calculateIdentityMatrix(sequences);
|
|
275
|
+
const { overallIdentity, frequencies } = getIdentityAndFrequencies(sequences);
|
|
276
|
+
const labileSites = getLabileSites(sequences, 0.5);
|
|
277
|
+
const propertyAnalysis = getPropertyAnalysis(sequences);
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
...identity,
|
|
281
|
+
overallIdentity,
|
|
282
|
+
frequencies,
|
|
283
|
+
labileSites,
|
|
284
|
+
propertyAnalysis
|
|
285
|
+
};
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const residueSizeMap = {
|
|
289
|
+
tiny: ["A", "C", "G", "S"],
|
|
290
|
+
small: ["A", "C", "D", "G", "N", "P", "S", "T", "V"],
|
|
291
|
+
large: ["E", "F", "H", "I", "K", "L", "M", "Q", "R", "W", "Y"]
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const polarityMap = {
|
|
295
|
+
hydrophobic: ["A", "C", "F", "I", "L", "M", "V", "W", "Y", "H", "K", "R"],
|
|
296
|
+
polar: ["D", "E", "H", "K", "N", "Q", "R", "S", "T", "Y"],
|
|
297
|
+
charged: ["D", "E", "H", "K", "R"]
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const specificGroupMap = {
|
|
301
|
+
aliphatic: ["I", "L", "V"],
|
|
302
|
+
aromatic: ["F", "W", "Y", "H"],
|
|
303
|
+
positive: ["H", "K", "R"],
|
|
304
|
+
negative: ["D", "E"],
|
|
305
|
+
amidic: ["N", "Q"],
|
|
306
|
+
"sulphur-containing": ["C", "M"],
|
|
307
|
+
hydroxylic: ["S", "T"]
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
export const residuePropertyMap = {
|
|
311
|
+
A: ["Small", "Tiny", "Hydrophobic"],
|
|
312
|
+
C: ["Small", "Tiny", "Hydrophobic", "Sulphur-Containing"],
|
|
313
|
+
D: ["Small", "Polar", "Charged", "Negative"],
|
|
314
|
+
E: ["Large", "Polar", "Charged", "Negative"],
|
|
315
|
+
F: ["Hydrophobic", "Aromatic"],
|
|
316
|
+
G: ["Small", "Tiny"],
|
|
317
|
+
H: ["Hydrophobic", "Polar", "Charged", "Aromatic", "Positive"],
|
|
318
|
+
I: ["Hydrophobic", "Aliphatic"],
|
|
319
|
+
K: ["Hydrophobic", "Polar", "Charged", "Positive"],
|
|
320
|
+
L: ["Hydrophobic", "Aliphatic"],
|
|
321
|
+
M: ["Hydrophobic", "Sulphur-Containing"],
|
|
322
|
+
N: ["Polar", "Small", "Amidic"],
|
|
323
|
+
P: ["Small"],
|
|
324
|
+
Q: ["Polar", "Amidic"],
|
|
325
|
+
R: ["Polar", "Charged", "Positive"],
|
|
326
|
+
S: ["Polar", "Small", "Tiny", "Hydroxylic"],
|
|
327
|
+
T: ["Polar", "Small", "Hydroxylic"],
|
|
328
|
+
V: ["Hydrophobic", "Small", "Aliphatic"],
|
|
329
|
+
W: ["Hydrophobic", "Polar", "Aromatic"],
|
|
330
|
+
Y: ["Hydrophobic", "Polar", "Aromatic"]
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const propertiesColorMap = {
|
|
334
|
+
aliphatic: "#AE83A3",
|
|
335
|
+
aromatic: "#EC8BA0",
|
|
336
|
+
amidic: "#83C6C2",
|
|
337
|
+
hydroxylic: "#65A3AC",
|
|
338
|
+
"sulphur-containing": "#F8CD7F",
|
|
339
|
+
positive: "#A1838F",
|
|
340
|
+
negative: "#DC855C",
|
|
341
|
+
large: "#C1B87E",
|
|
342
|
+
small: "#B1DEF0",
|
|
343
|
+
tiny: "#74BDA8",
|
|
344
|
+
hydrophobic: "#F4B3A2",
|
|
345
|
+
polar: "#C1DCAE",
|
|
346
|
+
charged: "#D7AD7A",
|
|
347
|
+
none: "#888"
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
function combineGroupColors(colorKeys, colorMap = propertiesColorMap) {
|
|
351
|
+
if (!colorKeys.length || colorKeys[0] === "none") return "#000";
|
|
352
|
+
|
|
353
|
+
const toRGB = hex => {
|
|
354
|
+
if (!hex) return [0, 0, 0];
|
|
355
|
+
const h = hex.replace("#", "");
|
|
356
|
+
return [
|
|
357
|
+
parseInt(h.substring(0, 2), 16),
|
|
358
|
+
parseInt(h.substring(2, 4), 16),
|
|
359
|
+
parseInt(h.substring(4, 6), 16)
|
|
360
|
+
];
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const rgbs = colorKeys
|
|
364
|
+
.map(key => toRGB(colorMap[key]))
|
|
365
|
+
.filter(rgb => rgb.every(Number.isFinite));
|
|
366
|
+
if (rgbs.length === 0) return "#888";
|
|
367
|
+
|
|
368
|
+
const avg = [0, 1, 2].map(i =>
|
|
369
|
+
Math.round(rgbs.reduce((sum, rgb) => sum + rgb[i], 0) / rgbs.length)
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
return (
|
|
373
|
+
"#" +
|
|
374
|
+
avg
|
|
375
|
+
.map(val => val.toString(16).padStart(2, "0"))
|
|
376
|
+
.join("")
|
|
377
|
+
.toUpperCase()
|
|
378
|
+
);
|
|
379
|
+
}
|
|
@@ -81,7 +81,8 @@ class SequenceInputNoHotkeys extends React.Component {
|
|
|
81
81
|
isProtein,
|
|
82
82
|
caretPosition,
|
|
83
83
|
sequenceData,
|
|
84
|
-
maxInsertSize
|
|
84
|
+
maxInsertSize,
|
|
85
|
+
showAminoAcidUnitAsCodon
|
|
85
86
|
} = this.props;
|
|
86
87
|
const { charsToInsert, hasTempError } = this.state;
|
|
87
88
|
|
|
@@ -97,7 +98,12 @@ class SequenceInputNoHotkeys extends React.Component {
|
|
|
97
98
|
<span>
|
|
98
99
|
Press <span style={{ fontWeight: "bolder" }}>ENTER</span> to replace{" "}
|
|
99
100
|
{divideBy3(getRangeLength(selectionLayer, sequenceLength), isProtein)}{" "}
|
|
100
|
-
{isProtein
|
|
101
|
+
{isProtein
|
|
102
|
+
? showAminoAcidUnitAsCodon
|
|
103
|
+
? "codons"
|
|
104
|
+
: "AAs"
|
|
105
|
+
: "base pairs"}{" "}
|
|
106
|
+
between{" "}
|
|
101
107
|
{isProtein
|
|
102
108
|
? convertDnaCaretPositionOrRangeToAA(betweenVals[0])
|
|
103
109
|
: betweenVals[0]}{" "}
|
|
@@ -111,8 +117,12 @@ class SequenceInputNoHotkeys extends React.Component {
|
|
|
111
117
|
message = (
|
|
112
118
|
<span>
|
|
113
119
|
Press <span style={{ fontWeight: "bolder" }}>ENTER</span> to insert{" "}
|
|
114
|
-
{charsToInsert.length}
|
|
115
|
-
{isProtein
|
|
120
|
+
{charsToInsert.length}{" "}
|
|
121
|
+
{isProtein
|
|
122
|
+
? `${showAminoAcidUnitAsCodon ? "codons" : "AAs"}`
|
|
123
|
+
: "base pairs"}{" "}
|
|
124
|
+
after{" "}
|
|
125
|
+
{isProtein ? `${showAminoAcidUnitAsCodon ? "codon" : "AA"}` : "base"}{" "}
|
|
116
126
|
{isProtein
|
|
117
127
|
? convertDnaCaretPositionOrRangeToAA(caretPosition)
|
|
118
128
|
: caretPosition}
|
|
@@ -156,7 +166,7 @@ class SequenceInputNoHotkeys extends React.Component {
|
|
|
156
166
|
}
|
|
157
167
|
if (maxInsertSize && sanitizedVal.length > maxInsertSize) {
|
|
158
168
|
return window.toastr.error(
|
|
159
|
-
`Sorry, your insert is greater than ${maxInsertSize}
|
|
169
|
+
`Sorry, your insert is greater than ${maxInsertSize}`
|
|
160
170
|
);
|
|
161
171
|
}
|
|
162
172
|
e.target.value = sanitizedVal;
|
|
@@ -221,6 +231,10 @@ export default function createSequenceInputPopup(props) {
|
|
|
221
231
|
// function closeInput() {
|
|
222
232
|
// sequenceInputBubble.remove();
|
|
223
233
|
// }
|
|
234
|
+
if (document.getElementById("sequenceInputBubble")) {
|
|
235
|
+
// remove the old one if it exists
|
|
236
|
+
document.getElementById("sequenceInputBubble").outerHTML = "";
|
|
237
|
+
}
|
|
224
238
|
div = document.createElement("div");
|
|
225
239
|
div.style.zIndex = "400000";
|
|
226
240
|
div.id = "sequenceInputBubble";
|
|
@@ -238,8 +238,12 @@ function VectorInteractionHOC(Component /* options */) {
|
|
|
238
238
|
sequence: clipboardData.getData("text/plain") || e.target.value
|
|
239
239
|
};
|
|
240
240
|
}
|
|
241
|
-
if (sequenceData.isProtein
|
|
242
|
-
seqDataToInsert.
|
|
241
|
+
if (sequenceData.isProtein) {
|
|
242
|
+
seqDataToInsert.isProtein = true;
|
|
243
|
+
|
|
244
|
+
if (!seqDataToInsert.proteinSequence) {
|
|
245
|
+
seqDataToInsert.proteinSequence = seqDataToInsert.sequence;
|
|
246
|
+
}
|
|
243
247
|
}
|
|
244
248
|
|
|
245
249
|
if (
|
|
@@ -399,7 +403,8 @@ function VectorInteractionHOC(Component /* options */) {
|
|
|
399
403
|
sequenceData = { sequence: "" },
|
|
400
404
|
readOnly,
|
|
401
405
|
disableBpEditing,
|
|
402
|
-
maxInsertSize
|
|
406
|
+
maxInsertSize,
|
|
407
|
+
showAminoAcidUnitAsCodon
|
|
403
408
|
// updateSequenceData,
|
|
404
409
|
// wrappedInsertSequenceDataAtPositionOrRange
|
|
405
410
|
// handleInsert
|
|
@@ -421,6 +426,7 @@ function VectorInteractionHOC(Component /* options */) {
|
|
|
421
426
|
sequenceLength,
|
|
422
427
|
caretPosition,
|
|
423
428
|
maxInsertSize,
|
|
429
|
+
showAminoAcidUnitAsCodon,
|
|
424
430
|
handleInsert: async seqDataToInsert => {
|
|
425
431
|
await insertAndSelectHelper({
|
|
426
432
|
props: this.props,
|
package/utils/editorUtils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function getSelectionMessage({ caretPosition, selectionLayer, customTitle, sequenceLength, sequenceData, showGCContent, GCDecimalDigits, isProtein }: {
|
|
1
|
+
export function getSelectionMessage({ caretPosition, selectionLayer, customTitle, sequenceLength, sequenceData, showGCContent, GCDecimalDigits, isProtein, showAminoAcidUnitAsCodon }: {
|
|
2
2
|
caretPosition?: number | undefined;
|
|
3
3
|
selectionLayer?: {
|
|
4
4
|
start: number;
|
|
@@ -10,6 +10,7 @@ export function getSelectionMessage({ caretPosition, selectionLayer, customTitle
|
|
|
10
10
|
showGCContent: any;
|
|
11
11
|
GCDecimalDigits: any;
|
|
12
12
|
isProtein: any;
|
|
13
|
+
showAminoAcidUnitAsCodon: any;
|
|
13
14
|
}): string;
|
|
14
15
|
export function preventDefaultStopPropagation(e: any): void;
|
|
15
16
|
export function getNodeToRefocus(caretEl: any): any;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export function getAlignedAminoAcidSequenceProps(tracks: any): {
|
|
2
|
+
overallIdentity: number;
|
|
3
|
+
frequencies: number[];
|
|
4
|
+
labileSites: {
|
|
5
|
+
sites: {
|
|
6
|
+
position: number;
|
|
7
|
+
conservationScore: number;
|
|
8
|
+
residueVariation: string[];
|
|
9
|
+
frequencies: {};
|
|
10
|
+
}[];
|
|
11
|
+
conservationScores: (number | null)[];
|
|
12
|
+
totalLabileSites: number;
|
|
13
|
+
percentageLabile: number;
|
|
14
|
+
};
|
|
15
|
+
propertyAnalysis: {
|
|
16
|
+
position: number;
|
|
17
|
+
residues: any[];
|
|
18
|
+
group: any;
|
|
19
|
+
color: string;
|
|
20
|
+
}[];
|
|
21
|
+
matrix: any[][];
|
|
22
|
+
sequenceNames: string[];
|
|
23
|
+
identicalPositions: {
|
|
24
|
+
identicalPositions: number;
|
|
25
|
+
seqs: string[];
|
|
26
|
+
}[];
|
|
27
|
+
};
|
|
28
|
+
export namespace residuePropertyMap {
|
|
29
|
+
let A: string[];
|
|
30
|
+
let C: string[];
|
|
31
|
+
let D: string[];
|
|
32
|
+
let E: string[];
|
|
33
|
+
let F: string[];
|
|
34
|
+
let G: string[];
|
|
35
|
+
let H: string[];
|
|
36
|
+
let I: string[];
|
|
37
|
+
let K: string[];
|
|
38
|
+
let L: string[];
|
|
39
|
+
let M: string[];
|
|
40
|
+
let N: string[];
|
|
41
|
+
let P: string[];
|
|
42
|
+
let Q: string[];
|
|
43
|
+
let R: string[];
|
|
44
|
+
let S: string[];
|
|
45
|
+
let T: string[];
|
|
46
|
+
let V: string[];
|
|
47
|
+
let W: string[];
|
|
48
|
+
let Y: string[];
|
|
49
|
+
}
|