@teselagen/ove 0.8.3 → 0.8.5
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 +4184 -2814
- package/index.es.js +2137 -767
- package/index.umd.js +3715 -2345
- package/ove.css +92 -6
- package/package.json +3 -3
- 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 +7 -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/PropertiesDialog/index.js +2 -2
- 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 +15 -5
- package/src/withEditorInteractions/index.js +3 -1
- 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;
|
|
@@ -403,7 +403,8 @@ function VectorInteractionHOC(Component /* options */) {
|
|
|
403
403
|
sequenceData = { sequence: "" },
|
|
404
404
|
readOnly,
|
|
405
405
|
disableBpEditing,
|
|
406
|
-
maxInsertSize
|
|
406
|
+
maxInsertSize,
|
|
407
|
+
showAminoAcidUnitAsCodon
|
|
407
408
|
// updateSequenceData,
|
|
408
409
|
// wrappedInsertSequenceDataAtPositionOrRange
|
|
409
410
|
// handleInsert
|
|
@@ -425,6 +426,7 @@ function VectorInteractionHOC(Component /* options */) {
|
|
|
425
426
|
sequenceLength,
|
|
426
427
|
caretPosition,
|
|
427
428
|
maxInsertSize,
|
|
429
|
+
showAminoAcidUnitAsCodon,
|
|
428
430
|
handleInsert: async seqDataToInsert => {
|
|
429
431
|
await insertAndSelectHelper({
|
|
430
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
|
+
}
|