@teselagen/ove 0.7.30-beta.2 → 0.7.30
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/CircularView/Labels/index.d.ts +1 -0
- package/CreateAnnotationsPage.d.ts +3 -4
- package/index.cjs.js +12306 -11064
- package/index.es.js +12306 -11064
- package/index.umd.js +13606 -12364
- package/ove.css +55 -55
- package/package.json +9 -13
- package/selectors/orfsSelector.d.ts +2 -2
- package/selectors/translationsSelector.d.ts +1 -1
- package/src/AlignmentView/index.js +5 -42
- package/src/AutoAnnotate.js +4 -4
- package/src/CircularView/Labels/index.js +84 -49
- package/src/CreateAnnotationsPage.js +2 -1
- package/src/GlobalDialogUtils.js +2 -2
- package/src/RowItem/Labels.js +5 -4
- package/src/ToolBar/alignmentTool.js +2 -2
- package/src/helperComponents/MergeFeaturesDialog/index.js +2 -2
- package/src/redux/alignments.js +2 -2
- package/src/redux/sequenceData/index.js +2 -2
- package/src/redux/sequenceData/upsertDeleteActionGenerator.js +2 -2
- package/src/selectors/cutsitesSelector.js +2 -2
- package/src/selectors/translationsSelector.js +3 -3
- package/src/utils/cleanSequenceData_DEPRECATED/arrayToObjWithIds.js +2 -2
- package/src/withEditorInteractions/index.js +136 -83
- package/src/withEditorProps/index.js +2 -2
- package/fileUtils.d.ts +0 -12
- package/html2canvas.esm--JN4fLQL.js +0 -7891
- package/html2canvas.esm-B7d7VJmQ.cjs +0 -7891
- package/src/fileUtils.js +0 -103
- package/style.css +0 -12107
package/ove.css
CHANGED
|
@@ -10724,6 +10724,61 @@ li.bp3-menu-divider:last-child {
|
|
|
10724
10724
|
.veLabelText {
|
|
10725
10725
|
user-select: none;
|
|
10726
10726
|
}
|
|
10727
|
+
.veLabels {
|
|
10728
|
+
font-weight: lighter;
|
|
10729
|
+
}
|
|
10730
|
+
|
|
10731
|
+
.labelTspan:hover {
|
|
10732
|
+
fill: red !important;
|
|
10733
|
+
}
|
|
10734
|
+
|
|
10735
|
+
.labelText {
|
|
10736
|
+
cursor: pointer;
|
|
10737
|
+
}
|
|
10738
|
+
|
|
10739
|
+
.veEditor .veAnnotationHovered:not(.topLevelLabelGroup) {
|
|
10740
|
+
font-weight: bold;
|
|
10741
|
+
}
|
|
10742
|
+
.veEditor
|
|
10743
|
+
.veAnnotationHovered:not(.topLevelLabelGroup):not(
|
|
10744
|
+
.veCircularViewInternalLabelText
|
|
10745
|
+
) {
|
|
10746
|
+
text-decoration: underline;
|
|
10747
|
+
}
|
|
10748
|
+
|
|
10749
|
+
.veEditor .veAnnotationHovered.veCutsiteLabel {
|
|
10750
|
+
font-weight: bold;
|
|
10751
|
+
text-decoration: underline;
|
|
10752
|
+
}
|
|
10753
|
+
|
|
10754
|
+
.bp3-dark .veLabels .veAnnotationHovered:not(.topLevelLabelGroup) {
|
|
10755
|
+
/* fill: white !important; */
|
|
10756
|
+
font-weight: normal;
|
|
10757
|
+
}
|
|
10758
|
+
.bp3-dark .veLabels .veAnnotationHovered rect {
|
|
10759
|
+
fill: rgb(64, 64, 92) !important;
|
|
10760
|
+
}
|
|
10761
|
+
|
|
10762
|
+
.partWithSelectedTag {
|
|
10763
|
+
font-size: 14px !important;
|
|
10764
|
+
font-weight: 900;
|
|
10765
|
+
text-decoration: underline;
|
|
10766
|
+
}
|
|
10767
|
+
.veCircularView .partWithSelectedTag.vePart .veLabelText {
|
|
10768
|
+
/* stroke: #ac68cc; */
|
|
10769
|
+
font-size: 15px !important;
|
|
10770
|
+
}
|
|
10771
|
+
.vePartLabel.partWithSelectedTag {
|
|
10772
|
+
font-size: large !important;
|
|
10773
|
+
}
|
|
10774
|
+
|
|
10775
|
+
.partWithSelectedTag path,
|
|
10776
|
+
path.partWithSelectedTag {
|
|
10777
|
+
stroke-width: 1.5 !important;
|
|
10778
|
+
}
|
|
10779
|
+
/* .veLabelLine {
|
|
10780
|
+
opacity: 0.1;
|
|
10781
|
+
} */
|
|
10727
10782
|
.veRowView {
|
|
10728
10783
|
overflow-x: visible;
|
|
10729
10784
|
}
|
|
@@ -11197,61 +11252,6 @@ li.bp3-menu-divider:last-child {
|
|
|
11197
11252
|
.simple-dialog .bp3-form-content {
|
|
11198
11253
|
height: 40px;
|
|
11199
11254
|
}
|
|
11200
|
-
.veLabels {
|
|
11201
|
-
font-weight: lighter;
|
|
11202
|
-
}
|
|
11203
|
-
|
|
11204
|
-
.labelTspan:hover {
|
|
11205
|
-
fill: red !important;
|
|
11206
|
-
}
|
|
11207
|
-
|
|
11208
|
-
.labelText {
|
|
11209
|
-
cursor: pointer;
|
|
11210
|
-
}
|
|
11211
|
-
|
|
11212
|
-
.veEditor .veAnnotationHovered:not(.topLevelLabelGroup) {
|
|
11213
|
-
font-weight: bold;
|
|
11214
|
-
}
|
|
11215
|
-
.veEditor
|
|
11216
|
-
.veAnnotationHovered:not(.topLevelLabelGroup):not(
|
|
11217
|
-
.veCircularViewInternalLabelText
|
|
11218
|
-
) {
|
|
11219
|
-
text-decoration: underline;
|
|
11220
|
-
}
|
|
11221
|
-
|
|
11222
|
-
.veEditor .veAnnotationHovered.veCutsiteLabel {
|
|
11223
|
-
font-weight: bold;
|
|
11224
|
-
text-decoration: underline;
|
|
11225
|
-
}
|
|
11226
|
-
|
|
11227
|
-
.bp3-dark .veLabels .veAnnotationHovered:not(.topLevelLabelGroup) {
|
|
11228
|
-
/* fill: white !important; */
|
|
11229
|
-
font-weight: normal;
|
|
11230
|
-
}
|
|
11231
|
-
.bp3-dark .veLabels .veAnnotationHovered rect {
|
|
11232
|
-
fill: rgb(64, 64, 92) !important;
|
|
11233
|
-
}
|
|
11234
|
-
|
|
11235
|
-
.partWithSelectedTag {
|
|
11236
|
-
font-size: 14px !important;
|
|
11237
|
-
font-weight: 900;
|
|
11238
|
-
text-decoration: underline;
|
|
11239
|
-
}
|
|
11240
|
-
.veCircularView .partWithSelectedTag.vePart .veLabelText {
|
|
11241
|
-
/* stroke: #ac68cc; */
|
|
11242
|
-
font-size: 15px !important;
|
|
11243
|
-
}
|
|
11244
|
-
.vePartLabel.partWithSelectedTag {
|
|
11245
|
-
font-size: large !important;
|
|
11246
|
-
}
|
|
11247
|
-
|
|
11248
|
-
.partWithSelectedTag path,
|
|
11249
|
-
path.partWithSelectedTag {
|
|
11250
|
-
stroke-width: 1.5 !important;
|
|
11251
|
-
}
|
|
11252
|
-
/* .veLabelLine {
|
|
11253
|
-
opacity: 0.1;
|
|
11254
|
-
} */
|
|
11255
11255
|
.veCircularViewMiddleOfVectorText {
|
|
11256
11256
|
display: flex;
|
|
11257
11257
|
height: 100%;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teselagen/ove",
|
|
3
|
-
"version": "0.7.30
|
|
3
|
+
"version": "0.7.30",
|
|
4
4
|
"main": "./src/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -14,19 +14,19 @@
|
|
|
14
14
|
"@blueprintjs/core": "3.54.0",
|
|
15
15
|
"@hello-pangea/dnd": "16.2.0",
|
|
16
16
|
"@risingstack/react-easy-state": "^6.3.0",
|
|
17
|
-
"@teselagen/bio-parsers": "0.4.
|
|
17
|
+
"@teselagen/bio-parsers": "0.4.28",
|
|
18
18
|
"@teselagen/file-utils": "0.3.20",
|
|
19
|
-
"@teselagen/range-utils": "0.3.
|
|
19
|
+
"@teselagen/range-utils": "0.3.13",
|
|
20
20
|
"@teselagen/react-list": "0.8.18",
|
|
21
|
-
"@teselagen/sequence-utils": "0.3.
|
|
22
|
-
"@teselagen/ui": "0.
|
|
21
|
+
"@teselagen/sequence-utils": "0.3.31",
|
|
22
|
+
"@teselagen/ui": "0.9.4",
|
|
23
23
|
"@use-gesture/react": "10.3.0",
|
|
24
24
|
"biomsa": "^0.2.4",
|
|
25
25
|
"classnames": "^2.3.2",
|
|
26
|
-
"
|
|
26
|
+
"clipboard": "^2.0.11",
|
|
27
|
+
"color": "^3.2.1",
|
|
27
28
|
"combokeys": "^3.0.1",
|
|
28
29
|
"copy-to-clipboard": "^3.3.1",
|
|
29
|
-
"cypress-real-events": "^1.13.0",
|
|
30
30
|
"deep-equal": "^1.1.1",
|
|
31
31
|
"dom-to-image": "^2.6.0",
|
|
32
32
|
"downloadjs": "^1.4.7",
|
|
@@ -57,15 +57,11 @@
|
|
|
57
57
|
"redux-thunk": "2.4.1",
|
|
58
58
|
"remark-gfm": "^4.0.0",
|
|
59
59
|
"reselect": "^4.1.7",
|
|
60
|
+
"shortid": "2.2.16",
|
|
60
61
|
"tg-use-local-storage-state": "^16.0.3",
|
|
61
62
|
"to-regex-range": "5.0.1",
|
|
62
63
|
"use-debounce": "^8.0.4",
|
|
63
|
-
"validate.io-nonnegative-integer-array": "^1.0.1"
|
|
64
|
-
"nanoid": "5.1.5"
|
|
65
|
-
},
|
|
66
|
-
"volta": {
|
|
67
|
-
"node": "18.18.0",
|
|
68
|
-
"yarn": "1.22.21"
|
|
64
|
+
"validate.io-nonnegative-integer-array": "^1.0.1"
|
|
69
65
|
},
|
|
70
66
|
"license": "MIT"
|
|
71
67
|
}
|
|
@@ -7,7 +7,7 @@ declare const _default: ((state: any) => {
|
|
|
7
7
|
forward: any;
|
|
8
8
|
annotationTypePlural: string;
|
|
9
9
|
isOrf: boolean;
|
|
10
|
-
id:
|
|
10
|
+
id: any;
|
|
11
11
|
}[]) & import('reselect').OutputSelectorFields<(args_0: any, args_1: any, args_2: any, args_3: any) => {
|
|
12
12
|
start: number;
|
|
13
13
|
end: number;
|
|
@@ -17,7 +17,7 @@ declare const _default: ((state: any) => {
|
|
|
17
17
|
forward: any;
|
|
18
18
|
annotationTypePlural: string;
|
|
19
19
|
isOrf: boolean;
|
|
20
|
-
id:
|
|
20
|
+
id: any;
|
|
21
21
|
}[], {
|
|
22
22
|
clearCache: () => void;
|
|
23
23
|
}> & {
|
|
@@ -7,7 +7,7 @@ declare const _default: ((state: any) => any) & import('reselect').OutputSelecto
|
|
|
7
7
|
forward: any;
|
|
8
8
|
annotationTypePlural: string;
|
|
9
9
|
isOrf: boolean;
|
|
10
|
-
id:
|
|
10
|
+
id: any;
|
|
11
11
|
}[], args_4: any, args_5: any, args_6: any[], args_7: any, args_8: any, args_9: any, args_10: any, args_11: any, args_12: any) => any, {
|
|
12
12
|
clearCache: () => void;
|
|
13
13
|
}> & {
|
|
@@ -15,9 +15,6 @@ import { connect } from "react-redux";
|
|
|
15
15
|
import {
|
|
16
16
|
Button,
|
|
17
17
|
Intent,
|
|
18
|
-
Popover,
|
|
19
|
-
Menu,
|
|
20
|
-
MenuItem,
|
|
21
18
|
Tooltip,
|
|
22
19
|
Icon,
|
|
23
20
|
Spinner,
|
|
@@ -108,8 +105,6 @@ export const AlignmentView = props => {
|
|
|
108
105
|
minimapLaneSpacing,
|
|
109
106
|
isInPairwiseOverviewView,
|
|
110
107
|
noVisibilityOptions,
|
|
111
|
-
updateAlignmentSortOrder,
|
|
112
|
-
alignmentSortOrder,
|
|
113
108
|
handleBackButtonClicked,
|
|
114
109
|
allowTrimming,
|
|
115
110
|
additionalSelectionLayerRightClickedOptions,
|
|
@@ -616,7 +611,6 @@ export const AlignmentView = props => {
|
|
|
616
611
|
} else {
|
|
617
612
|
i = _i;
|
|
618
613
|
}
|
|
619
|
-
|
|
620
614
|
const track = alignmentTracks?.[i];
|
|
621
615
|
if (!track) return null;
|
|
622
616
|
const {
|
|
@@ -636,7 +630,7 @@ export const AlignmentView = props => {
|
|
|
636
630
|
});
|
|
637
631
|
const linearViewWidth =
|
|
638
632
|
(alignmentData || sequenceData).sequence.length * charWidth;
|
|
639
|
-
const name = sequenceData.name || sequenceData.id;
|
|
633
|
+
const name = alignmentData.name || sequenceData.name || sequenceData.id;
|
|
640
634
|
|
|
641
635
|
const tickSpacing = massageTickSpacing(Math.ceil(120 / charWidth));
|
|
642
636
|
|
|
@@ -1261,7 +1255,7 @@ export const AlignmentView = props => {
|
|
|
1261
1255
|
await navigator.clipboard.writeText(seqDataToCopy);
|
|
1262
1256
|
};
|
|
1263
1257
|
|
|
1264
|
-
const
|
|
1258
|
+
const copyAllAlignmentsFastaText = async () => {
|
|
1265
1259
|
await navigator.clipboard.writeText(
|
|
1266
1260
|
getAllAlignmentsFastaText()
|
|
1267
1261
|
);
|
|
@@ -1281,7 +1275,7 @@ export const AlignmentView = props => {
|
|
|
1281
1275
|
"copyAllAlignmentsFastaClipboardHelper",
|
|
1282
1276
|
hotkey: "cmd+c",
|
|
1283
1277
|
onClick: () => {
|
|
1284
|
-
|
|
1278
|
+
copyAllAlignmentsFastaText();
|
|
1285
1279
|
window.toastr.success("Selection Copied");
|
|
1286
1280
|
}
|
|
1287
1281
|
},
|
|
@@ -1289,8 +1283,8 @@ export const AlignmentView = props => {
|
|
|
1289
1283
|
text: `Copy Selection of ${name} as Fasta`,
|
|
1290
1284
|
className:
|
|
1291
1285
|
"copySpecificAlignmentFastaClipboardHelper",
|
|
1292
|
-
onClick:
|
|
1293
|
-
copySpecificAlignmentFasta();
|
|
1286
|
+
onClick: e => {
|
|
1287
|
+
copySpecificAlignmentFasta(e);
|
|
1294
1288
|
window.toastr.success(
|
|
1295
1289
|
"Selection Copied As Fasta"
|
|
1296
1290
|
);
|
|
@@ -1581,37 +1575,6 @@ export const AlignmentView = props => {
|
|
|
1581
1575
|
{...alignmentVisibilityToolOptions}
|
|
1582
1576
|
/>
|
|
1583
1577
|
)}
|
|
1584
|
-
{updateAlignmentSortOrder && !isInPairwiseOverviewView && (
|
|
1585
|
-
<Popover
|
|
1586
|
-
minimal
|
|
1587
|
-
content={
|
|
1588
|
-
<Menu>
|
|
1589
|
-
<MenuItem
|
|
1590
|
-
active={true || alignmentSortOrder}
|
|
1591
|
-
onClick={() => {
|
|
1592
|
-
updateAlignmentSortOrder("Position");
|
|
1593
|
-
}}
|
|
1594
|
-
text="Position"
|
|
1595
|
-
/>
|
|
1596
|
-
<MenuItem
|
|
1597
|
-
active={false || alignmentSortOrder}
|
|
1598
|
-
onClick={() => {
|
|
1599
|
-
updateAlignmentSortOrder("Alphabetical");
|
|
1600
|
-
}}
|
|
1601
|
-
text="Alphabetical"
|
|
1602
|
-
/>
|
|
1603
|
-
</Menu>
|
|
1604
|
-
}
|
|
1605
|
-
target={
|
|
1606
|
-
<Button
|
|
1607
|
-
small
|
|
1608
|
-
text="Sort Order"
|
|
1609
|
-
rightIcon="caret-down"
|
|
1610
|
-
icon="sort"
|
|
1611
|
-
/>
|
|
1612
|
-
}
|
|
1613
|
-
/>
|
|
1614
|
-
)}
|
|
1615
1578
|
{additionalTopEl}
|
|
1616
1579
|
{saveMessage && (
|
|
1617
1580
|
<div
|
package/src/AutoAnnotate.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { unparse } from "papaparse";
|
|
5
5
|
import pluralize from "pluralize";
|
|
6
6
|
import { SubmissionError, reduxForm } from "redux-form";
|
|
7
|
-
import
|
|
7
|
+
import shortid from "shortid";
|
|
8
8
|
import CreateAnnotationsPage from "./CreateAnnotationsPage";
|
|
9
9
|
import { formName } from "./constants";
|
|
10
10
|
import { AutoAnnotateBpMatchingDialog } from "./AutoAnnotateBpMatchingDialog";
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
} from "@teselagen/ui";
|
|
34
34
|
import { startCase } from "lodash-es";
|
|
35
35
|
import withEditorProps from "./withEditorProps";
|
|
36
|
-
import
|
|
36
|
+
import { useEffect, useState } from "react";
|
|
37
37
|
import { Colors, Tab, Tabs } from "@blueprintjs/core";
|
|
38
38
|
import { typeField } from "./helperComponents/PropertiesDialog/typeField";
|
|
39
39
|
|
|
@@ -403,7 +403,7 @@ FRT GAAGTTCCTATTCTCTAGAAAGTATAGGAACTTC misc_recomb orchid pink 0 0`,
|
|
|
403
403
|
if (ann.matchType === "protein") {
|
|
404
404
|
ann.sequence = convertProteinSeqToDNAIupac(ann.sequence);
|
|
405
405
|
}
|
|
406
|
-
const id =
|
|
406
|
+
const id = shortid();
|
|
407
407
|
annotationsToCheckById[id] = {
|
|
408
408
|
...ann,
|
|
409
409
|
sequence: ann.isRegex
|
|
@@ -428,7 +428,7 @@ FRT GAAGTTCCTATTCTCTAGAAAGTATAGGAACTTC misc_recomb orchid pink 0 0`,
|
|
|
428
428
|
...annotationsToCheckById[a.id],
|
|
429
429
|
...a,
|
|
430
430
|
forward: a.strand !== -1,
|
|
431
|
-
id:
|
|
431
|
+
id: shortid()
|
|
432
432
|
};
|
|
433
433
|
toRet.color =
|
|
434
434
|
toRet.color || getFeatureToColorMap()[toRet.type];
|
|
@@ -8,11 +8,19 @@ import { avoidOverlapWith } from "../drawAnnotations";
|
|
|
8
8
|
|
|
9
9
|
const fontWidthToFontSize = 1.75;
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
let
|
|
11
|
+
export const getTextLengthWithCollapseSpace = (text, collapseWhiteSpace = true) => {
|
|
12
|
+
let displayText = text || "Unlabeled";
|
|
13
|
+
if (collapseWhiteSpace) {
|
|
14
|
+
displayText = displayText.replaceAll(
|
|
15
|
+
/\s+/g,
|
|
16
|
+
" "
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
let len = displayText.length;
|
|
13
20
|
// eslint-disable-next-line no-control-regex
|
|
14
21
|
const nonEnInputReg = /[^\x00-\xff]+/g;
|
|
15
|
-
const nonEnStrings =
|
|
22
|
+
const nonEnStrings =
|
|
23
|
+
displayText.match(nonEnInputReg) || [];
|
|
16
24
|
nonEnStrings.forEach(str => (len += str.length * 0.5));
|
|
17
25
|
return len;
|
|
18
26
|
};
|
|
@@ -58,7 +66,7 @@ function Labels({
|
|
|
58
66
|
_annotationCenterAngle + (rotationRadians || 0);
|
|
59
67
|
return {
|
|
60
68
|
...label,
|
|
61
|
-
width:
|
|
69
|
+
width: getTextLengthWithCollapseSpace(label.text) * fontWidth,
|
|
62
70
|
//three points define the label:
|
|
63
71
|
innerPoint: {
|
|
64
72
|
...polarToSpecialCartesian(
|
|
@@ -214,42 +222,26 @@ const DrawLabelGroup = withHover(function ({
|
|
|
214
222
|
}) {
|
|
215
223
|
let { text = "Unlabeled" } = label;
|
|
216
224
|
|
|
217
|
-
const textLength = getTextLength(text);
|
|
218
225
|
let groupLabelXStart;
|
|
219
226
|
//Add the number of unshown labels
|
|
220
227
|
if (label.labelAndSublabels && label.labelAndSublabels.length > 1) {
|
|
221
|
-
// if (label.x > 0) {
|
|
222
228
|
text = "+" + (label.labelAndSublabels.length - 1) + "," + text;
|
|
223
|
-
// } else {
|
|
224
|
-
// text += ', +' + (label.labelAndSublabels.length - 1)
|
|
225
|
-
// }
|
|
226
229
|
}
|
|
227
230
|
|
|
228
|
-
const labelLength =
|
|
229
|
-
const maxLabelLength = labelAndSublabels.reduce(function (
|
|
230
|
-
currentLength,
|
|
231
|
-
{ text = "Unlabeled" }
|
|
232
|
-
) {
|
|
233
|
-
const _textLength = getTextLength(text);
|
|
234
|
-
if (_textLength > currentLength) {
|
|
235
|
-
return _textLength;
|
|
236
|
-
}
|
|
237
|
-
return currentLength;
|
|
238
|
-
}, 0);
|
|
231
|
+
const labelLength = getTextLengthWithCollapseSpace(text) * fontWidth;
|
|
239
232
|
|
|
240
|
-
const maxLabelWidth = maxLabelLength * fontWidth;
|
|
241
233
|
const labelOnLeft = label.angle > Math.PI;
|
|
242
234
|
let labelXStart = label.x - (labelOnLeft ? labelLength : 0);
|
|
235
|
+
const maxDistance =
|
|
236
|
+
(outerRadius + 90) * Math.max(1, circularViewWidthVsHeightRatio);
|
|
243
237
|
if (condenseOverflowingXLabels) {
|
|
244
238
|
const distancePastBoundary =
|
|
245
239
|
Math.abs(label.x + (labelOnLeft ? -labelLength : labelLength)) -
|
|
246
|
-
|
|
247
|
-
|
|
240
|
+
maxDistance;
|
|
241
|
+
|
|
248
242
|
if (distancePastBoundary > 0) {
|
|
249
243
|
const numberOfCharsToChop =
|
|
250
244
|
Math.ceil(distancePastBoundary / fontWidth) + 2;
|
|
251
|
-
// if (numberOfCharsToChop > text.length) numberOfCharsToChop = text.length
|
|
252
|
-
//label overflows the boundaries!
|
|
253
245
|
text = text.slice(0, -numberOfCharsToChop) + "..";
|
|
254
246
|
groupLabelXStart =
|
|
255
247
|
labelXStart +
|
|
@@ -261,17 +253,58 @@ const DrawLabelGroup = withHover(function ({
|
|
|
261
253
|
const textYStart = label.y + dy / 2;
|
|
262
254
|
|
|
263
255
|
//if label xStart or label xEnd don't fit within the canvas, we need to shorten the label..
|
|
264
|
-
|
|
265
256
|
let content;
|
|
266
257
|
const labelClass = ` veLabelText veLabel veCircularViewLabelText clickable ${label.color} `;
|
|
267
258
|
|
|
268
259
|
if ((multipleLabels || groupLabelXStart !== undefined) && hovered) {
|
|
260
|
+
// Calculate the maximum label length for hovered status
|
|
261
|
+
const maxLabelLength = labelAndSublabels.reduce(function (
|
|
262
|
+
currentLength,
|
|
263
|
+
{ text = "Unlabeled" }
|
|
264
|
+
) {
|
|
265
|
+
const _textLength = getTextLengthWithCollapseSpace(text);
|
|
266
|
+
if (_textLength > currentLength) {
|
|
267
|
+
return _textLength;
|
|
268
|
+
}
|
|
269
|
+
return currentLength;
|
|
270
|
+
}, 0);
|
|
271
|
+
const maxLabelWidth = maxLabelLength * fontWidth;
|
|
272
|
+
|
|
273
|
+
labelXStart = label.x - (labelOnLeft ? maxLabelWidth : 0);
|
|
274
|
+
let distancePastBoundary = Math.abs(label.x + (labelOnLeft ? -maxLabelWidth : maxLabelWidth)) - maxDistance;
|
|
275
|
+
let lableRectWidth = maxLabelWidth - 14;
|
|
276
|
+
|
|
277
|
+
if (maxLabelWidth > maxDistance * 2) {
|
|
278
|
+
labelXStart = -maxDistance;
|
|
279
|
+
lableRectWidth = maxDistance * 2 - 24;
|
|
280
|
+
distancePastBoundary = maxLabelWidth - maxDistance * 2;
|
|
281
|
+
} else if (distancePastBoundary > 0) {
|
|
282
|
+
labelXStart += (labelOnLeft ? distancePastBoundary : -distancePastBoundary);
|
|
283
|
+
distancePastBoundary = 0;
|
|
284
|
+
}
|
|
285
|
+
|
|
269
286
|
//HOVERED: DRAW MULTIPLE LABELS IN A RECTANGLE
|
|
270
287
|
window.isLabelGroupOpen = true;
|
|
271
288
|
let hoveredLabel;
|
|
272
|
-
|
|
273
|
-
|
|
289
|
+
|
|
290
|
+
let truncatedLabelAndSublabels;
|
|
291
|
+
if (distancePastBoundary > 0) {
|
|
292
|
+
truncatedLabelAndSublabels = labelAndSublabels.map(lable => {
|
|
293
|
+
const labelWidth = getTextLengthWithCollapseSpace(lable.text) * fontWidth;
|
|
294
|
+
const truncatedText =
|
|
295
|
+
labelWidth >= lableRectWidth
|
|
296
|
+
? lable.text.slice(
|
|
297
|
+
0,
|
|
298
|
+
-Math.ceil((labelWidth - lableRectWidth) / fontWidth) - 2
|
|
299
|
+
) + ".."
|
|
300
|
+
: lable.text;
|
|
301
|
+
return {
|
|
302
|
+
...lable,
|
|
303
|
+
text: truncatedText
|
|
304
|
+
};
|
|
305
|
+
});
|
|
274
306
|
}
|
|
307
|
+
|
|
275
308
|
labelAndSublabels.some(function (label) {
|
|
276
309
|
if (label.id === hoveredId) {
|
|
277
310
|
hoveredLabel = label;
|
|
@@ -317,7 +350,7 @@ const DrawLabelGroup = withHover(function ({
|
|
|
317
350
|
// zIndex={10}
|
|
318
351
|
x={labelXStart - 4}
|
|
319
352
|
y={labelYStart - dy / 2}
|
|
320
|
-
width={
|
|
353
|
+
width={lableRectWidth + 24}
|
|
321
354
|
height={labelGroupHeight + 4}
|
|
322
355
|
fill="white"
|
|
323
356
|
stroke="black"
|
|
@@ -331,24 +364,26 @@ const DrawLabelGroup = withHover(function ({
|
|
|
331
364
|
fontStyle: label.fontStyle
|
|
332
365
|
}}
|
|
333
366
|
>
|
|
334
|
-
{labelAndSublabels.map(
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
367
|
+
{(truncatedLabelAndSublabels || labelAndSublabels).map(
|
|
368
|
+
function (label, index) {
|
|
369
|
+
return (
|
|
370
|
+
<DrawGroupInnerLabel
|
|
371
|
+
isSubLabel
|
|
372
|
+
noRedux={noRedux}
|
|
373
|
+
editorName={editorName}
|
|
374
|
+
logHover
|
|
375
|
+
key={"labelItem" + index}
|
|
376
|
+
className={
|
|
377
|
+
(label.className || "") +
|
|
378
|
+
labelClass +
|
|
379
|
+
" veDrawGroupInnerLabel"
|
|
380
|
+
}
|
|
381
|
+
id={label.id}
|
|
382
|
+
{...{ labelXStart, label, fontWidth, index, dy }}
|
|
383
|
+
/>
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
)}
|
|
352
387
|
</text>
|
|
353
388
|
</g>
|
|
354
389
|
</PutMyParentOnTop>
|
|
@@ -364,7 +399,7 @@ const DrawLabelGroup = withHover(function ({
|
|
|
364
399
|
data-title={label.title || label.text}
|
|
365
400
|
{...avoidOverlapWith}
|
|
366
401
|
x={labelXStart}
|
|
367
|
-
textLength={
|
|
402
|
+
textLength={getTextLengthWithCollapseSpace(text) * fontWidth}
|
|
368
403
|
lengthAdjust="spacing"
|
|
369
404
|
className={
|
|
370
405
|
labelClass + label.className + (hovered ? " veAnnotationHovered" : "")
|
|
@@ -438,7 +473,7 @@ const DrawGroupInnerLabel = withHover(
|
|
|
438
473
|
data-title={label.title}
|
|
439
474
|
{...avoidOverlapWith}
|
|
440
475
|
x={labelXStart}
|
|
441
|
-
textLength={
|
|
476
|
+
textLength={getTextLengthWithCollapseSpace(label.text) * fontWidth}
|
|
442
477
|
lengthAdjust="spacing"
|
|
443
478
|
onClick={label.onClick}
|
|
444
479
|
onDoubleClick={e => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { compose } from "redux";
|
|
2
2
|
import pluralize from "pluralize";
|
|
3
|
+
|
|
3
4
|
import { formName } from "./constants";
|
|
4
5
|
import { typeField } from "./helperComponents/PropertiesDialog/typeField";
|
|
5
6
|
import {
|
|
@@ -8,7 +9,7 @@ import {
|
|
|
8
9
|
withSelectTableRecords,
|
|
9
10
|
withSelectedEntities
|
|
10
11
|
} from "@teselagen/ui";
|
|
11
|
-
import
|
|
12
|
+
import { useEffect } from "react";
|
|
12
13
|
import { hideDialog } from "./GlobalDialogUtils";
|
|
13
14
|
import { startCase } from "lodash-es";
|
|
14
15
|
import { tidyUpAnnotation } from "@teselagen/sequence-utils";
|
package/src/GlobalDialogUtils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import shortid from "shortid";
|
|
2
2
|
|
|
3
3
|
import { cloneDeep, startCase } from "lodash-es";
|
|
4
4
|
import { convertRangeTo1Based } from "@teselagen/range-utils";
|
|
@@ -36,7 +36,7 @@ export function showDialog({
|
|
|
36
36
|
dialogHolder.CustomModalComponent = ModalComponent;
|
|
37
37
|
dialogHolder.props = props;
|
|
38
38
|
dialogHolder.overrideName = overrideName;
|
|
39
|
-
dialogHolder.setUniqKeyToForceRerender(
|
|
39
|
+
dialogHolder.setUniqKeyToForceRerender(shortid());
|
|
40
40
|
}
|
|
41
41
|
export function hideDialog() {
|
|
42
42
|
delete dialogHolder.dialogType;
|
package/src/RowItem/Labels.js
CHANGED
|
@@ -8,6 +8,7 @@ import { reduce, values, startCase, filter, clamp } from "lodash-es";
|
|
|
8
8
|
import { getRangeLength } from "@teselagen/range-utils";
|
|
9
9
|
import { doesLabelFitInAnnotation } from "./utils";
|
|
10
10
|
import getAnnotationNameAndStartStopString from "../utils/getAnnotationNameAndStartStopString";
|
|
11
|
+
import { getTextLengthWithCollapseSpace } from "../CircularView/Labels";
|
|
11
12
|
|
|
12
13
|
const BUFFER_WIDTH = 6; //labels shouldn't be less than 6px from eachother on the same line
|
|
13
14
|
|
|
@@ -98,11 +99,11 @@ function Labels(props) {
|
|
|
98
99
|
annotation = annotationRange;
|
|
99
100
|
}
|
|
100
101
|
const annotationLength =
|
|
101
|
-
|
|
102
|
+
getTextLengthWithCollapseSpace(
|
|
102
103
|
annotation.name ||
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
)
|
|
104
|
+
(annotation.restrictionEnzyme && annotation.restrictionEnzyme.name) ||
|
|
105
|
+
""
|
|
106
|
+
) * textWidth;
|
|
106
107
|
let { xStart, width } = getXStartAndWidthOfRowAnnotation(
|
|
107
108
|
annotationRange,
|
|
108
109
|
bpsPerRow,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import { reduxForm, FieldArray } from "redux-form";
|
|
11
11
|
import { anyToJson } from "@teselagen/bio-parsers";
|
|
12
12
|
import { flatMap } from "lodash-es";
|
|
13
|
-
import
|
|
13
|
+
import uniqid from "shortid";
|
|
14
14
|
import { cloneDeep } from "lodash-es";
|
|
15
15
|
import classNames from "classnames";
|
|
16
16
|
|
|
@@ -155,7 +155,7 @@ class AlignmentTool extends React.Component {
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
hideModal();
|
|
158
|
-
const alignmentId =
|
|
158
|
+
const alignmentId = uniqid();
|
|
159
159
|
// const alignmentIdMismatches = uniqid();
|
|
160
160
|
createNewAlignment({
|
|
161
161
|
id: alignmentId,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import
|
|
2
|
+
import uuid from "shortid";
|
|
3
3
|
|
|
4
4
|
import { reduxForm } from "redux-form";
|
|
5
5
|
|
|
@@ -58,7 +58,7 @@ class MergeFeaturesDialog extends React.Component {
|
|
|
58
58
|
upsertFeature(
|
|
59
59
|
{
|
|
60
60
|
...feat1,
|
|
61
|
-
id:
|
|
61
|
+
id: uuid(),
|
|
62
62
|
start: start - 1,
|
|
63
63
|
end: end - 1,
|
|
64
64
|
name
|
package/src/redux/alignments.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
condensePairwiseAlignmentDifferences
|
|
4
4
|
} from "@teselagen/sequence-utils";
|
|
5
5
|
import { convertBasePosTraceToPerBpTrace } from "@teselagen/bio-parsers";
|
|
6
|
-
import
|
|
6
|
+
import shortid from "shortid";
|
|
7
7
|
|
|
8
8
|
import addDashesForMatchStartAndEndForTracks from "./utils/addDashesForMatchStartAndEndForTracks";
|
|
9
9
|
|
|
@@ -149,7 +149,7 @@ export default (state = {}, { payload = {}, type }) => {
|
|
|
149
149
|
if (type === "UPSERT_ALIGNMENT_RUN") {
|
|
150
150
|
const { id } = payload;
|
|
151
151
|
const payloadToUse = {
|
|
152
|
-
stateTrackingId: state[id]?.stateTrackingId ?
|
|
152
|
+
stateTrackingId: state[id]?.stateTrackingId ? shortid() : "initialLoadId",
|
|
153
153
|
alignmentType: state[id]?.alignmentType,
|
|
154
154
|
...payload,
|
|
155
155
|
//assign default visibilities
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import deepEqual from "deep-equal";
|
|
2
2
|
import { tidyUpSequenceData } from "@teselagen/sequence-utils";
|
|
3
|
-
import
|
|
3
|
+
import uuid from "shortid";
|
|
4
4
|
|
|
5
5
|
import createAction from "../utils/createMetaAction";
|
|
6
6
|
import features from "./features";
|
|
@@ -75,7 +75,7 @@ export default function (state, action) {
|
|
|
75
75
|
return {
|
|
76
76
|
// ...cloneDeep(newState),
|
|
77
77
|
...newState,
|
|
78
|
-
stateTrackingId: newState.stateTrackingId ?
|
|
78
|
+
stateTrackingId: newState.stateTrackingId ? uuid() : "initialLoadId"
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
81
|
}
|