@teselagen/ove 0.8.40 → 0.8.42
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/findAlignmentDifferences.d.ts +53 -0
- package/index.cjs.js +260 -153
- package/index.es.js +260 -153
- package/index.umd.js +260 -153
- package/ove.css +80 -0
- package/package.json +2 -2
- package/src/AlignmentView/AlignmentVisibilityTool.js +9 -11
- package/src/AlignmentView/Minimap.js +21 -3
- package/src/AlignmentView/Mismatches.js +164 -139
- package/src/AlignmentView/findAlignmentDifferences.js +116 -0
- package/src/AlignmentView/findAlignmentDifferences.test.js +208 -0
- package/src/AlignmentView/index.js +18 -2
- package/src/AlignmentView/style.css +80 -0
- package/src/redux/alignments.js +58 -20
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {"mismatch"|"insertion"|"deletion"|"gap"} DifferenceType
|
|
3
|
+
*
|
|
4
|
+
* @typedef {Object} AlignmentDifference
|
|
5
|
+
* @property {number} position - 0-based column index in the aligned sequence
|
|
6
|
+
* @property {DifferenceType} type
|
|
7
|
+
* @property {string[]} bases - bases at this column for each track (template first)
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Group consecutive same-type differences into regions.
|
|
11
|
+
* Mismatches are never grouped — each is its own entry.
|
|
12
|
+
* Insertions, deletions, and gaps that are side-by-side are collapsed into
|
|
13
|
+
* one entry with a `start` and `end` (both inclusive, 0-based).
|
|
14
|
+
*
|
|
15
|
+
* @param {AlignmentDifference[]} differences
|
|
16
|
+
* @returns {Array<AlignmentDifference & { start: number, end: number }>}
|
|
17
|
+
*/
|
|
18
|
+
export function groupConsecutiveDifferences(differences: AlignmentDifference[]): Array<AlignmentDifference & {
|
|
19
|
+
start: number;
|
|
20
|
+
end: number;
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* Classify alignment columns into difference types relative to the template track.
|
|
24
|
+
*
|
|
25
|
+
* Template is alignedSeqs[0]. Non-template tracks are alignedSeqs[1+].
|
|
26
|
+
*
|
|
27
|
+
* Classification rules (per column):
|
|
28
|
+
* - "gap" : no non-template track is in its aligned region at this position
|
|
29
|
+
* - "insertion" : template has '-', at least one aligned non-template has a non-gap base
|
|
30
|
+
* - "deletion" : template has a non-gap base, at least one aligned non-template has '-'
|
|
31
|
+
* - "mismatch" : no gaps among aligned tracks, unique base set has more than one member
|
|
32
|
+
*
|
|
33
|
+
* Only tracks whose aligned region covers position i participate in classification.
|
|
34
|
+
* This correctly handles multi-read alignments (e.g. Sanger) where reads cover
|
|
35
|
+
* different sub-ranges of the full alignment.
|
|
36
|
+
*
|
|
37
|
+
* @param {string[]} alignedSeqs - Aligned sequence strings, all same length
|
|
38
|
+
* @returns {AlignmentDifference[]}
|
|
39
|
+
*/
|
|
40
|
+
export function findAlignmentDifferences(alignedSeqs: string[]): AlignmentDifference[];
|
|
41
|
+
export default findAlignmentDifferences;
|
|
42
|
+
export type DifferenceType = "mismatch" | "insertion" | "deletion" | "gap";
|
|
43
|
+
export type AlignmentDifference = {
|
|
44
|
+
/**
|
|
45
|
+
* - 0-based column index in the aligned sequence
|
|
46
|
+
*/
|
|
47
|
+
position: number;
|
|
48
|
+
type: DifferenceType;
|
|
49
|
+
/**
|
|
50
|
+
* - bases at this column for each track (template first)
|
|
51
|
+
*/
|
|
52
|
+
bases: string[];
|
|
53
|
+
};
|
package/index.cjs.js
CHANGED
|
@@ -90314,17 +90314,20 @@ function snapgeneToJson(_0) {
|
|
|
90314
90314
|
const b3 = new fxpExports.XMLParser({
|
|
90315
90315
|
ignoreAttributes: false,
|
|
90316
90316
|
attributeNamePrefix: "",
|
|
90317
|
-
isArray: /* @__PURE__ */ __name((name2) =>
|
|
90317
|
+
isArray: /* @__PURE__ */ __name((name2) => ["Feature", "Segment", "Q", "V"].includes(name2), "isArray")
|
|
90318
90318
|
}).parse(xml2);
|
|
90319
90319
|
const { Features: { Feature: Feature2 = [] } = {} } = b3;
|
|
90320
90320
|
data.features = [];
|
|
90321
90321
|
Feature2.forEach((feat) => {
|
|
90322
|
+
var _a2, _b2, _c, _d;
|
|
90322
90323
|
const { directionality, Segment = [], name: name2, type: type2 } = feat;
|
|
90324
|
+
let color2;
|
|
90323
90325
|
let maxStart = 0;
|
|
90324
90326
|
let maxEnd = 0;
|
|
90325
90327
|
const locations = Segment && Segment.map((seg) => {
|
|
90326
90328
|
if (!seg) throw new Error("invalid feature definition");
|
|
90327
90329
|
const { range: range2 } = seg;
|
|
90330
|
+
if (seg.color) color2 = seg.color;
|
|
90328
90331
|
let { start: start2, end: end2 } = getStartAndEndFromRangeString(range2);
|
|
90329
90332
|
start2 = isProtein2 ? start2 * 3 : start2;
|
|
90330
90333
|
end2 = isProtein2 ? end2 * 3 + 2 : end2;
|
|
@@ -90335,6 +90338,10 @@ function snapgeneToJson(_0) {
|
|
|
90335
90338
|
end: end2
|
|
90336
90339
|
};
|
|
90337
90340
|
});
|
|
90341
|
+
const colorQual = (_a2 = feat.Q) == null ? void 0 : _a2.find((q2) => q2.name === "color");
|
|
90342
|
+
if (colorQual) {
|
|
90343
|
+
color2 = ((_c = (_b2 = colorQual.V) == null ? void 0 : _b2[0]) == null ? void 0 : _c.text) || ((_d = colorQual.V) == null ? void 0 : _d[0]);
|
|
90344
|
+
}
|
|
90338
90345
|
data.features.push(__spreadProps(__spreadValues({
|
|
90339
90346
|
name: name2,
|
|
90340
90347
|
type: type2
|
|
@@ -90342,8 +90349,8 @@ function snapgeneToJson(_0) {
|
|
|
90342
90349
|
strand: directionality ? strand_dict[directionality][0] : 1,
|
|
90343
90350
|
arrowheadType: directionality ? strand_dict[directionality][1] : "NONE",
|
|
90344
90351
|
start: maxStart,
|
|
90345
|
-
end: maxEnd
|
|
90346
|
-
|
|
90352
|
+
end: maxEnd,
|
|
90353
|
+
color: color2
|
|
90347
90354
|
}));
|
|
90348
90355
|
});
|
|
90349
90356
|
} else if (ord(next_byte) === 6) {
|
|
@@ -95510,14 +95517,34 @@ function addHighlightedDifferences(alignmentTracks) {
|
|
|
95510
95517
|
track.alignmentData.sequence
|
|
95511
95518
|
);
|
|
95512
95519
|
const mismatches = matchHighlightRanges.filter(({ isMatch }) => !isMatch);
|
|
95520
|
+
const alignedSeq = track.alignmentData.sequence;
|
|
95521
|
+
const seqLen = alignedSeq.length;
|
|
95522
|
+
const startIndex = seqLen - alignedSeq.replace(/^-+/, "").length;
|
|
95523
|
+
const endIndex = alignedSeq.replace(/-+$/, "").length;
|
|
95524
|
+
const gapRanges = [
|
|
95525
|
+
startIndex > 0 && {
|
|
95526
|
+
start: 0,
|
|
95527
|
+
end: startIndex - 1,
|
|
95528
|
+
differenceType: "gap"
|
|
95529
|
+
},
|
|
95530
|
+
endIndex < seqLen && {
|
|
95531
|
+
start: endIndex,
|
|
95532
|
+
end: seqLen - 1,
|
|
95533
|
+
differenceType: "gap"
|
|
95534
|
+
}
|
|
95535
|
+
].filter(Boolean);
|
|
95513
95536
|
return __spreadProps(__spreadValues({}, track), {
|
|
95514
95537
|
sequenceData: sequenceData2,
|
|
95515
95538
|
matchHighlightRanges,
|
|
95516
|
-
additionalSelectionLayers:
|
|
95517
|
-
|
|
95539
|
+
additionalSelectionLayers: [
|
|
95540
|
+
...matchHighlightRanges.filter(({ isMatch }) => !isMatch).map((range2) => __spreadProps(__spreadValues(__spreadValues({}, range2), highlightRangeProps), {
|
|
95518
95541
|
className: "veAlignmentMismatch"
|
|
95519
|
-
})
|
|
95520
|
-
|
|
95542
|
+
})),
|
|
95543
|
+
...gapRanges.map((range2) => __spreadProps(__spreadValues(__spreadValues({}, range2), highlightRangeProps), {
|
|
95544
|
+
className: "veAlignmentMismatch"
|
|
95545
|
+
}))
|
|
95546
|
+
],
|
|
95547
|
+
gapRanges,
|
|
95521
95548
|
mismatches
|
|
95522
95549
|
});
|
|
95523
95550
|
});
|
|
@@ -95651,23 +95678,30 @@ function getRangeMatchesBetweenTemplateAndNonTemplate(tempSeq, nonTempSeq) {
|
|
|
95651
95678
|
const startIndex = seqLength - nonTempSeqWithoutLeadingDashes.length;
|
|
95652
95679
|
const endIndex = seqLength - (seqLength - nonTempSeqWithoutTrailingDashes.length);
|
|
95653
95680
|
for (let index2 = startIndex; index2 < endIndex; index2++) {
|
|
95654
|
-
const
|
|
95655
|
-
const
|
|
95656
|
-
|
|
95657
|
-
|
|
95658
|
-
|
|
95681
|
+
const tempBase = tempSeq[index2].toLowerCase();
|
|
95682
|
+
const nonTempBase = nonTempSeq[index2].toLowerCase();
|
|
95683
|
+
const isMatch = tempBase === nonTempBase;
|
|
95684
|
+
let differenceType = null;
|
|
95685
|
+
if (!isMatch) {
|
|
95686
|
+
if (tempBase === "-") {
|
|
95687
|
+
differenceType = "insertion";
|
|
95688
|
+
} else if (nonTempBase === "-") {
|
|
95689
|
+
differenceType = "deletion";
|
|
95659
95690
|
} else {
|
|
95660
|
-
|
|
95661
|
-
start: index2,
|
|
95662
|
-
end: index2,
|
|
95663
|
-
isMatch
|
|
95664
|
-
});
|
|
95691
|
+
differenceType = "mismatch";
|
|
95665
95692
|
}
|
|
95693
|
+
}
|
|
95694
|
+
const previousRange = ranges[ranges.length - 1];
|
|
95695
|
+
if (previousRange && previousRange.isMatch === isMatch && previousRange.differenceType === differenceType) {
|
|
95696
|
+
previousRange.end++;
|
|
95697
|
+
} else if (previousRange) {
|
|
95698
|
+
ranges.push({ start: index2, end: index2, isMatch, differenceType });
|
|
95666
95699
|
} else {
|
|
95667
95700
|
ranges.push({
|
|
95668
95701
|
start: startIndex,
|
|
95669
95702
|
end: startIndex,
|
|
95670
|
-
isMatch
|
|
95703
|
+
isMatch,
|
|
95704
|
+
differenceType
|
|
95671
95705
|
});
|
|
95672
95706
|
}
|
|
95673
95707
|
}
|
|
@@ -117245,7 +117279,7 @@ function showFileDialog({ multiple = false, onSelect }) {
|
|
|
117245
117279
|
input.click();
|
|
117246
117280
|
}
|
|
117247
117281
|
__name(showFileDialog, "showFileDialog");
|
|
117248
|
-
const version = "0.8.
|
|
117282
|
+
const version = "0.8.42";
|
|
117249
117283
|
const packageJson = {
|
|
117250
117284
|
version
|
|
117251
117285
|
};
|
|
@@ -125114,11 +125148,13 @@ const _Minimap = class _Minimap extends React.Component {
|
|
|
125114
125148
|
dimensions: { width = 200 },
|
|
125115
125149
|
laneHeight,
|
|
125116
125150
|
laneSpacing = 1,
|
|
125117
|
-
isTrackSelected = []
|
|
125151
|
+
isTrackSelected = [],
|
|
125152
|
+
activeFilterType = "all"
|
|
125118
125153
|
} = this.props;
|
|
125119
125154
|
const charWidth2 = this.getCharWidth();
|
|
125120
125155
|
const {
|
|
125121
125156
|
matchHighlightRanges: _matchHighlightRanges,
|
|
125157
|
+
gapRanges = [],
|
|
125122
125158
|
alignmentData: { trimmedRange } = {}
|
|
125123
125159
|
} = alignmentTracks[i];
|
|
125124
125160
|
const matchHighlightRanges = !trimmedRange ? _matchHighlightRanges : flatMap(_matchHighlightRanges, (r2) => {
|
|
@@ -125145,10 +125181,19 @@ const _Minimap = class _Minimap extends React.Component {
|
|
|
125145
125181
|
charWidth2
|
|
125146
125182
|
);
|
|
125147
125183
|
const toAdd = `M${xStart},${y2} L${xStart + width2},${y2} L${xStart + width2},${y2 + height} L${xStart},${y2 + height}`;
|
|
125148
|
-
if (!range2.isMatch) {
|
|
125184
|
+
if (!range2.isMatch && (activeFilterType === "all" || range2.differenceType === activeFilterType)) {
|
|
125149
125185
|
redPath += toAdd;
|
|
125150
125186
|
}
|
|
125151
125187
|
});
|
|
125188
|
+
if (activeFilterType === "gap") {
|
|
125189
|
+
gapRanges.forEach((range2) => {
|
|
125190
|
+
const { xStart, width: width2 } = getXStartAndWidthFromNonCircularRange(
|
|
125191
|
+
range2,
|
|
125192
|
+
charWidth2
|
|
125193
|
+
);
|
|
125194
|
+
redPath += `M${xStart},${y2} L${xStart + width2},${y2} L${xStart + width2},${y2 + height} L${xStart},${y2 + height}`;
|
|
125195
|
+
});
|
|
125196
|
+
}
|
|
125152
125197
|
return /* @__PURE__ */ React.createElement(
|
|
125153
125198
|
"div",
|
|
125154
125199
|
{
|
|
@@ -125178,7 +125223,8 @@ const _Minimap = class _Minimap extends React.Component {
|
|
|
125178
125223
|
"scrollAlignmentView",
|
|
125179
125224
|
"laneHeight",
|
|
125180
125225
|
"laneSpacing",
|
|
125181
|
-
"isTrackSelected"
|
|
125226
|
+
"isTrackSelected",
|
|
125227
|
+
"activeFilterType"
|
|
125182
125228
|
].some((key) => props[key] !== newProps[key]))
|
|
125183
125229
|
return true;
|
|
125184
125230
|
return false;
|
|
@@ -125456,6 +125502,66 @@ function getTrimmedRangesToDisplay({ trimmedRange, seqLen }) {
|
|
|
125456
125502
|
return splitRangeIntoTwoPartsIfItIsCircular(inverted, seqLen);
|
|
125457
125503
|
}
|
|
125458
125504
|
__name(getTrimmedRangesToDisplay, "getTrimmedRangesToDisplay");
|
|
125505
|
+
function groupConsecutiveDifferences(differences) {
|
|
125506
|
+
const grouped = [];
|
|
125507
|
+
for (const diff of differences) {
|
|
125508
|
+
if (diff.type === "mismatch") {
|
|
125509
|
+
grouped.push(__spreadProps(__spreadValues({}, diff), { start: diff.position, end: diff.position }));
|
|
125510
|
+
continue;
|
|
125511
|
+
}
|
|
125512
|
+
const last2 = grouped[grouped.length - 1];
|
|
125513
|
+
if (last2 && last2.type === diff.type && last2.end === diff.position - 1) {
|
|
125514
|
+
grouped[grouped.length - 1] = __spreadProps(__spreadValues({}, last2), { end: diff.position });
|
|
125515
|
+
} else {
|
|
125516
|
+
grouped.push(__spreadProps(__spreadValues({}, diff), { start: diff.position, end: diff.position }));
|
|
125517
|
+
}
|
|
125518
|
+
}
|
|
125519
|
+
return grouped;
|
|
125520
|
+
}
|
|
125521
|
+
__name(groupConsecutiveDifferences, "groupConsecutiveDifferences");
|
|
125522
|
+
function findAlignmentDifferences(alignedSeqs) {
|
|
125523
|
+
var _a2;
|
|
125524
|
+
if (alignedSeqs.length < 2 || !((_a2 = alignedSeqs[0]) == null ? void 0 : _a2.length)) return [];
|
|
125525
|
+
const template = alignedSeqs[0].toLowerCase();
|
|
125526
|
+
const nonTemplates = alignedSeqs.slice(1).map((s2) => s2.toLowerCase());
|
|
125527
|
+
const trackBounds = nonTemplates.map((seq) => {
|
|
125528
|
+
const withoutLeading = seq.replace(/^-+/, "");
|
|
125529
|
+
const withoutTrailing = seq.replace(/-+$/, "");
|
|
125530
|
+
const start2 = seq.length - withoutLeading.length;
|
|
125531
|
+
const end2 = seq.length - (seq.length - withoutTrailing.length);
|
|
125532
|
+
return { start: start2, end: end2 };
|
|
125533
|
+
});
|
|
125534
|
+
const differences = [];
|
|
125535
|
+
for (let i = 0; i < template.length; i++) {
|
|
125536
|
+
const templateBase = template[i];
|
|
125537
|
+
const allNonTemplateBases = nonTemplates.map((seq) => seq[i]);
|
|
125538
|
+
const bases = [templateBase, ...allNonTemplateBases];
|
|
125539
|
+
const alignedIndices = trackBounds.reduce((acc, { start: start2, end: end2 }, idx) => {
|
|
125540
|
+
if (i >= start2 && i < end2) acc.push(idx);
|
|
125541
|
+
return acc;
|
|
125542
|
+
}, []);
|
|
125543
|
+
if (alignedIndices.length === 0) {
|
|
125544
|
+
differences.push({ position: i, type: "gap", bases });
|
|
125545
|
+
continue;
|
|
125546
|
+
}
|
|
125547
|
+
const alignedBases = alignedIndices.map((idx) => allNonTemplateBases[idx]);
|
|
125548
|
+
const templateIsGap = templateBase === "-";
|
|
125549
|
+
const nonTemplateHasBase = alignedBases.some((b3) => b3 !== "-");
|
|
125550
|
+
const nonTemplateHasGap = alignedBases.some((b3) => b3 === "-");
|
|
125551
|
+
if (templateIsGap && nonTemplateHasBase) {
|
|
125552
|
+
differences.push({ position: i, type: "insertion", bases });
|
|
125553
|
+
} else if (!templateIsGap && nonTemplateHasGap) {
|
|
125554
|
+
differences.push({ position: i, type: "deletion", bases });
|
|
125555
|
+
} else if (!templateIsGap) {
|
|
125556
|
+
const uniqueBases = /* @__PURE__ */ new Set([templateBase, ...alignedBases]);
|
|
125557
|
+
if (uniqueBases.size > 1) {
|
|
125558
|
+
differences.push({ position: i, type: "mismatch", bases });
|
|
125559
|
+
}
|
|
125560
|
+
}
|
|
125561
|
+
}
|
|
125562
|
+
return differences;
|
|
125563
|
+
}
|
|
125564
|
+
__name(findAlignmentDifferences, "findAlignmentDifferences");
|
|
125459
125565
|
function scrollToAlignmentSelection() {
|
|
125460
125566
|
const el = document.querySelector(".veCaret");
|
|
125461
125567
|
if (el) {
|
|
@@ -125469,159 +125575,145 @@ function updateCaretPosition({ start: start2, end: end2 }) {
|
|
|
125469
125575
|
}
|
|
125470
125576
|
}
|
|
125471
125577
|
__name(updateCaretPosition, "updateCaretPosition");
|
|
125578
|
+
const FILTER_OPTIONS = [
|
|
125579
|
+
{ value: "all", label: "All" },
|
|
125580
|
+
{ value: "mismatch", label: "Mismatches" },
|
|
125581
|
+
{ value: "insertion", label: "Insertions" },
|
|
125582
|
+
{ value: "deletion", label: "Deletions" },
|
|
125583
|
+
{ value: "gap", label: "Gaps" }
|
|
125584
|
+
];
|
|
125472
125585
|
function FindMismatches(props) {
|
|
125473
|
-
|
|
125586
|
+
var _a2;
|
|
125587
|
+
const { alignmentJson, id: id2, onFilterChange } = props;
|
|
125474
125588
|
const alignedSeqs = React.useMemo(
|
|
125475
125589
|
() => alignmentJson.map((t2) => {
|
|
125476
|
-
var
|
|
125477
|
-
return ((
|
|
125590
|
+
var _a3;
|
|
125591
|
+
return ((_a3 = t2.alignmentData) == null ? void 0 : _a3.sequence) || "";
|
|
125478
125592
|
}),
|
|
125479
125593
|
[alignmentJson]
|
|
125480
125594
|
);
|
|
125481
|
-
const
|
|
125482
|
-
|
|
125483
|
-
|
|
125484
|
-
|
|
125485
|
-
|
|
125486
|
-
|
|
125487
|
-
|
|
125488
|
-
|
|
125489
|
-
|
|
125490
|
-
|
|
125491
|
-
}
|
|
125492
|
-
return
|
|
125493
|
-
}, [
|
|
125595
|
+
const [activeFilter, setActiveFilter] = React.useState("all");
|
|
125596
|
+
const allDifferences = React.useMemo(
|
|
125597
|
+
() => groupConsecutiveDifferences(findAlignmentDifferences(alignedSeqs)),
|
|
125598
|
+
[alignedSeqs]
|
|
125599
|
+
);
|
|
125600
|
+
const countsByType = React.useMemo(() => {
|
|
125601
|
+
const counts = { all: 0, mismatch: 0, insertion: 0, deletion: 0, gap: 0 };
|
|
125602
|
+
allDifferences.forEach((d2) => {
|
|
125603
|
+
counts[d2.type] = (counts[d2.type] || 0) + 1;
|
|
125604
|
+
counts.all++;
|
|
125605
|
+
});
|
|
125606
|
+
return counts;
|
|
125607
|
+
}, [allDifferences]);
|
|
125608
|
+
const differences = React.useMemo(() => {
|
|
125609
|
+
const filtered = activeFilter === "all" ? allDifferences : allDifferences.filter((d2) => d2.type === activeFilter);
|
|
125610
|
+
return [{ position: -1, start: -1, end: -1, bases: [""] }, ...filtered];
|
|
125611
|
+
}, [allDifferences, activeFilter]);
|
|
125494
125612
|
const currentCaretPosition = reactRedux.useSelector(
|
|
125495
125613
|
(state2) => {
|
|
125496
|
-
var
|
|
125497
|
-
return (
|
|
125614
|
+
var _a3;
|
|
125615
|
+
return (_a3 = state2.VectorEditor.__allEditorsOptions.alignments[id2]) == null ? void 0 : _a3.caretPosition;
|
|
125498
125616
|
}
|
|
125499
125617
|
);
|
|
125500
125618
|
const [currentIdx, setCurrentIdx] = React.useState(0);
|
|
125501
|
-
const
|
|
125502
|
-
const
|
|
125503
|
-
const
|
|
125504
|
-
|
|
125505
|
-
(
|
|
125506
|
-
|
|
125507
|
-
|
|
125508
|
-
|
|
125509
|
-
|
|
125510
|
-
}
|
|
125511
|
-
const firstMismatchPos = mismatches[1].position;
|
|
125512
|
-
const lastMismatchPos = mismatches[mismatches.length - 1].position;
|
|
125513
|
-
setDisablePrev(caret <= firstMismatchPos);
|
|
125514
|
-
setDisableNext(caret >= lastMismatchPos);
|
|
125515
|
-
},
|
|
125516
|
-
[mismatches]
|
|
125517
|
-
);
|
|
125619
|
+
const currentDiff = differences[currentIdx];
|
|
125620
|
+
const disablePrev = currentIdx <= 1;
|
|
125621
|
+
const disableNext = currentIdx >= differences.length - 1;
|
|
125622
|
+
React.useEffect(() => {
|
|
125623
|
+
setCurrentIdx(0);
|
|
125624
|
+
}, [activeFilter]);
|
|
125625
|
+
React.useEffect(() => {
|
|
125626
|
+
onFilterChange == null ? void 0 : onFilterChange({ activeFilter });
|
|
125627
|
+
}, [activeFilter, onFilterChange]);
|
|
125518
125628
|
React.useEffect(() => {
|
|
125519
125629
|
if (currentCaretPosition !== -1) {
|
|
125520
|
-
const
|
|
125521
|
-
(
|
|
125630
|
+
const diffIdx = differences.findIndex(
|
|
125631
|
+
(d2, i) => i > 0 && currentCaretPosition >= d2.start && currentCaretPosition <= d2.end + 1
|
|
125522
125632
|
);
|
|
125523
|
-
if (
|
|
125524
|
-
|
|
125525
|
-
setCurrentIdx(mismatchIdx);
|
|
125633
|
+
if (diffIdx !== -1 && diffIdx !== currentIdx) {
|
|
125634
|
+
setCurrentIdx(diffIdx);
|
|
125526
125635
|
}
|
|
125527
125636
|
}
|
|
125528
|
-
}, [currentCaretPosition,
|
|
125529
|
-
const updateView = /* @__PURE__ */ __name((
|
|
125530
|
-
const idx =
|
|
125531
|
-
const
|
|
125532
|
-
handleButtonsState(position2);
|
|
125637
|
+
}, [currentCaretPosition, differences, currentIdx]);
|
|
125638
|
+
const updateView = /* @__PURE__ */ __name((diff) => {
|
|
125639
|
+
const idx = differences.indexOf(diff);
|
|
125640
|
+
const { start: start2, end: end2 } = diff;
|
|
125533
125641
|
setCurrentIdx(idx);
|
|
125534
|
-
updateCaretPosition({ start:
|
|
125642
|
+
updateCaretPosition({ start: start2, end: end2 });
|
|
125535
125643
|
setTimeout(() => {
|
|
125536
125644
|
scrollToAlignmentSelection();
|
|
125537
125645
|
}, 0);
|
|
125538
125646
|
}, "updateView");
|
|
125539
|
-
const
|
|
125540
|
-
|
|
125541
|
-
|
|
125542
|
-
|
|
125543
|
-
|
|
125544
|
-
|
|
125545
|
-
|
|
125546
|
-
|
|
125547
|
-
|
|
125548
|
-
|
|
125549
|
-
|
|
125550
|
-
}, "
|
|
125551
|
-
const
|
|
125552
|
-
|
|
125553
|
-
|
|
125554
|
-
|
|
125555
|
-
|
|
125556
|
-
|
|
125557
|
-
|
|
125558
|
-
|
|
125559
|
-
|
|
125560
|
-
|
|
125561
|
-
|
|
125647
|
+
const prevDifference = /* @__PURE__ */ __name(() => {
|
|
125648
|
+
var _a3, _b2;
|
|
125649
|
+
const pivot = currentCaretPosition >= 0 ? currentCaretPosition : (_b2 = (_a3 = differences[currentIdx]) == null ? void 0 : _a3.start) != null ? _b2 : 0;
|
|
125650
|
+
const prev = [...differences].reverse().find((d2) => d2.start >= 0 && d2.start < pivot);
|
|
125651
|
+
if (prev) updateView(prev);
|
|
125652
|
+
}, "prevDifference");
|
|
125653
|
+
const nextDifference = /* @__PURE__ */ __name(() => {
|
|
125654
|
+
var _a3, _b2;
|
|
125655
|
+
const pivot = currentCaretPosition >= 0 ? currentCaretPosition : (_b2 = (_a3 = differences[currentIdx]) == null ? void 0 : _a3.start) != null ? _b2 : -1;
|
|
125656
|
+
const next = differences.find((d2) => d2.start > pivot && d2.start >= 0);
|
|
125657
|
+
if (next) updateView(next);
|
|
125658
|
+
}, "nextDifference");
|
|
125659
|
+
const noDifferences = differences.length <= 1;
|
|
125660
|
+
const activeOption = FILTER_OPTIONS.find((o2) => o2.value === activeFilter);
|
|
125661
|
+
const activeLabel = (_a2 = activeOption == null ? void 0 : activeOption.label) != null ? _a2 : "Differences";
|
|
125662
|
+
const filterMenu = /* @__PURE__ */ React.createElement(core.Menu, null, FILTER_OPTIONS.map(({ value, label }) => {
|
|
125663
|
+
var _a3;
|
|
125664
|
+
const count2 = (_a3 = countsByType[value]) != null ? _a3 : 0;
|
|
125665
|
+
const isActive2 = activeFilter === value;
|
|
125666
|
+
return /* @__PURE__ */ React.createElement(
|
|
125667
|
+
core.MenuItem,
|
|
125668
|
+
{
|
|
125669
|
+
key: value,
|
|
125670
|
+
active: isActive2,
|
|
125671
|
+
onClick: /* @__PURE__ */ __name(() => setActiveFilter(value), "onClick"),
|
|
125672
|
+
text: /* @__PURE__ */ React.createElement("span", { className: "veDiffMenuItem-inner" }, label, /* @__PURE__ */ React.createElement(core.Tag, { round: true, minimal: true, style: { marginLeft: 6 } }, count2))
|
|
125562
125673
|
}
|
|
125563
|
-
|
|
125564
|
-
}
|
|
125565
|
-
return /* @__PURE__ */ React.createElement(
|
|
125566
|
-
|
|
125674
|
+
);
|
|
125675
|
+
}));
|
|
125676
|
+
return /* @__PURE__ */ React.createElement("div", { className: "veDiffNavigator" }, /* @__PURE__ */ React.createElement(
|
|
125677
|
+
core.Popover,
|
|
125567
125678
|
{
|
|
125568
|
-
|
|
125569
|
-
|
|
125570
|
-
|
|
125571
|
-
|
|
125572
|
-
alignItems: "center",
|
|
125573
|
-
gap: 10
|
|
125574
|
-
}
|
|
125575
|
-
},
|
|
125576
|
-
mismatches.length === 1 ? /* @__PURE__ */ React.createElement("span", { style: { fontStyle: "italic", color: "grey" } }, "no mismatches") : /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column" } }, /* @__PURE__ */ React.createElement(
|
|
125577
|
-
"div",
|
|
125578
|
-
{
|
|
125579
|
-
style: {
|
|
125580
|
-
display: "flex",
|
|
125581
|
-
alignItems: "center"
|
|
125582
|
-
}
|
|
125583
|
-
},
|
|
125584
|
-
/* @__PURE__ */ React.createElement("strong", null, "Mismatches"),
|
|
125585
|
-
/* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 2 } }, /* @__PURE__ */ React.createElement(
|
|
125586
|
-
core.Button,
|
|
125587
|
-
{
|
|
125588
|
-
intent: "primary",
|
|
125589
|
-
icon: "arrow-left",
|
|
125590
|
-
"data-tip": "Previous Mismatch",
|
|
125591
|
-
onClick: prevMismatch,
|
|
125592
|
-
disabled: disablePrev,
|
|
125593
|
-
small: true,
|
|
125594
|
-
minimal: true
|
|
125595
|
-
}
|
|
125596
|
-
), /* @__PURE__ */ React.createElement(
|
|
125679
|
+
minimal: true,
|
|
125680
|
+
position: core.Position.BOTTOM_LEFT,
|
|
125681
|
+
content: filterMenu,
|
|
125682
|
+
target: /* @__PURE__ */ React.createElement(
|
|
125597
125683
|
core.Button,
|
|
125598
125684
|
{
|
|
125599
|
-
|
|
125600
|
-
|
|
125601
|
-
"data-tip": "Next Mismatch",
|
|
125602
|
-
onClick: nextMismatch,
|
|
125603
|
-
disabled: disableNext,
|
|
125685
|
+
minimal: true,
|
|
125686
|
+
"data-tip": "Filter Difference Type",
|
|
125604
125687
|
small: true,
|
|
125605
|
-
|
|
125606
|
-
|
|
125607
|
-
|
|
125608
|
-
|
|
125609
|
-
|
|
125610
|
-
|
|
125611
|
-
|
|
125612
|
-
|
|
125613
|
-
|
|
125614
|
-
|
|
125615
|
-
|
|
125616
|
-
|
|
125617
|
-
|
|
125618
|
-
|
|
125619
|
-
|
|
125620
|
-
|
|
125621
|
-
|
|
125622
|
-
|
|
125623
|
-
|
|
125624
|
-
|
|
125688
|
+
rightIcon: "caret-down",
|
|
125689
|
+
className: "veDiffFilter-trigger"
|
|
125690
|
+
},
|
|
125691
|
+
activeLabel
|
|
125692
|
+
)
|
|
125693
|
+
}
|
|
125694
|
+
), noDifferences ? /* @__PURE__ */ React.createElement("span", { className: "veDiffNav-empty" }, "no", " ", activeFilter === "all" ? "differences" : activeLabel.toLowerCase()) : /* @__PURE__ */ React.createElement("div", { className: "veDiffNav" }, /* @__PURE__ */ React.createElement(
|
|
125695
|
+
core.Button,
|
|
125696
|
+
{
|
|
125697
|
+
minimal: true,
|
|
125698
|
+
small: true,
|
|
125699
|
+
"data-tip": "Previous Difference",
|
|
125700
|
+
icon: "arrow-left",
|
|
125701
|
+
intent: core.Intent.PRIMARY,
|
|
125702
|
+
onClick: prevDifference,
|
|
125703
|
+
disabled: disablePrev
|
|
125704
|
+
}
|
|
125705
|
+
), /* @__PURE__ */ React.createElement("div", { className: "veDiffNav-center" }, /* @__PURE__ */ React.createElement("span", { className: "veDiffNav-fraction" }, currentIdx, /* @__PURE__ */ React.createElement("span", { className: "veDiffNav-sep" }, "/"), differences.length - 1), (currentDiff == null ? void 0 : currentDiff.start) > -1 && /* @__PURE__ */ React.createElement("span", { className: "veDiffNav-pos" }, ":", currentDiff.start + 1)), /* @__PURE__ */ React.createElement(
|
|
125706
|
+
core.Button,
|
|
125707
|
+
{
|
|
125708
|
+
minimal: true,
|
|
125709
|
+
small: true,
|
|
125710
|
+
"data-tip": "Next Difference",
|
|
125711
|
+
icon: "arrow-right",
|
|
125712
|
+
intent: core.Intent.PRIMARY,
|
|
125713
|
+
onClick: nextDifference,
|
|
125714
|
+
disabled: disableNext
|
|
125715
|
+
}
|
|
125716
|
+
)));
|
|
125625
125717
|
}
|
|
125626
125718
|
__name(FindMismatches, "FindMismatches");
|
|
125627
125719
|
function getGapMap(sequence2) {
|
|
@@ -126310,17 +126402,18 @@ const AlignmentVisibilityTool = pure(/* @__PURE__ */ __name(function AlignmentVi
|
|
|
126310
126402
|
minimal: true,
|
|
126311
126403
|
position: "bottom",
|
|
126312
126404
|
content: /* @__PURE__ */ React.createElement(VisibilityOptions$2, __spreadValues({}, props)),
|
|
126313
|
-
target: /* @__PURE__ */ React.createElement(
|
|
126405
|
+
target: /* @__PURE__ */ React.createElement(
|
|
126314
126406
|
core.Button,
|
|
126315
126407
|
{
|
|
126316
126408
|
className: "tg-alignment-visibility-toggle",
|
|
126317
126409
|
small: true,
|
|
126410
|
+
"data-tip": "Visibility Options",
|
|
126318
126411
|
rightIcon: "caret-down",
|
|
126319
126412
|
intent: core.Intent.PRIMARY,
|
|
126320
126413
|
minimal: true,
|
|
126321
126414
|
icon: "eye-open"
|
|
126322
126415
|
}
|
|
126323
|
-
)
|
|
126416
|
+
)
|
|
126324
126417
|
}
|
|
126325
126418
|
);
|
|
126326
126419
|
}, "AlignmentVisibilityTool"));
|
|
@@ -140108,6 +140201,10 @@ const AlignmentView = /* @__PURE__ */ __name((props) => {
|
|
|
140108
140201
|
const [tempTrimAfter, setTempTrimAfter] = React.useState({});
|
|
140109
140202
|
const [tempTrimmingCaret, setTempTrimmingCaret] = React.useState({});
|
|
140110
140203
|
const [searchMatchLayers, setSearchMatchLayers] = React.useState([]);
|
|
140204
|
+
const [activeFilterType, setActiveFilterType] = React.useState("all");
|
|
140205
|
+
const handleFilterChange = React.useCallback(({ activeFilter }) => {
|
|
140206
|
+
setActiveFilterType(activeFilter);
|
|
140207
|
+
}, []);
|
|
140111
140208
|
const bindOutsideChangeHelper = React.useRef({});
|
|
140112
140209
|
const alignmentHolder = React.useRef(null);
|
|
140113
140210
|
const alignmentHolderTop = React.useRef(null);
|
|
@@ -140833,7 +140930,9 @@ ${seqDataToCopy}\r
|
|
|
140833
140930
|
chromatogramData
|
|
140834
140931
|
}) : linearViewOptions)), {
|
|
140835
140932
|
additionalSelectionLayers: [
|
|
140836
|
-
...additionalSelectionLayers || []
|
|
140933
|
+
...i !== 0 ? (additionalSelectionLayers || []).filter(
|
|
140934
|
+
(layer) => activeFilterType === "all" ? layer.differenceType !== "gap" : layer.differenceType === activeFilterType
|
|
140935
|
+
) : additionalSelectionLayers || [],
|
|
140837
140936
|
...searchMatchLayers || []
|
|
140838
140937
|
],
|
|
140839
140938
|
dimensions: {
|
|
@@ -141492,7 +141591,14 @@ ${seqDataToCopy}\r
|
|
|
141492
141591
|
setSearchMatchLayers
|
|
141493
141592
|
}
|
|
141494
141593
|
),
|
|
141495
|
-
/* @__PURE__ */ React.createElement(
|
|
141594
|
+
/* @__PURE__ */ React.createElement(
|
|
141595
|
+
FindMismatches,
|
|
141596
|
+
{
|
|
141597
|
+
alignmentJson: alignmentTracks,
|
|
141598
|
+
id: id2,
|
|
141599
|
+
onFilterChange: handleFilterChange
|
|
141600
|
+
}
|
|
141601
|
+
),
|
|
141496
141602
|
additionalTopEl,
|
|
141497
141603
|
saveMessage && /* @__PURE__ */ React.createElement(
|
|
141498
141604
|
"div",
|
|
@@ -141593,6 +141699,7 @@ ${seqDataToCopy}\r
|
|
|
141593
141699
|
}
|
|
141594
141700
|
)),
|
|
141595
141701
|
alignmentTracks,
|
|
141702
|
+
activeFilterType,
|
|
141596
141703
|
dimensions: {
|
|
141597
141704
|
width: Math.max(width, 10) || 10
|
|
141598
141705
|
},
|