@teselagen/ove 0.7.3-beta.7 → 0.7.3
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/DigestTool/DigestTool.d.ts +1 -1
- package/helperComponents/PropertiesDialog/index.d.ts +2 -7
- package/index.cjs.js +6501 -494
- package/index.es.js +6490 -483
- package/index.umd.js +6501 -494
- package/package.json +2 -2
- package/src/CreateAnnotationsPage.js +1 -1
- package/src/DigestTool/DigestTool.js +78 -75
- package/src/GlobalDialogUtils.js +1 -0
- package/src/helperComponents/PropertiesDialog/CutsiteProperties.js +126 -135
- package/src/helperComponents/PropertiesDialog/SingleEnzymeCutsiteInfo.js +59 -51
- package/src/helperComponents/PropertiesDialog/index.js +115 -114
- package/src/helperComponents/RemoveDuplicates/index.js +144 -160
- package/src/utils/useFormValue.js +7 -0
- package/src/withEditorInteractions/index.js +16 -11
- package/utils/useFormValue.d.ts +1 -0
|
@@ -18,11 +18,11 @@ import { pick } from "lodash-es";
|
|
|
18
18
|
const PropertiesContainer = Comp => props => {
|
|
19
19
|
const { additionalFooterEls, additionalHeaderEls, ...rest } = props;
|
|
20
20
|
return (
|
|
21
|
-
|
|
21
|
+
<>
|
|
22
22
|
{additionalHeaderEls}
|
|
23
23
|
<Comp {...rest} />
|
|
24
24
|
{additionalFooterEls}
|
|
25
|
-
|
|
25
|
+
</>
|
|
26
26
|
);
|
|
27
27
|
};
|
|
28
28
|
const allTabs = {
|
|
@@ -35,129 +35,130 @@ const allTabs = {
|
|
|
35
35
|
orfs: PropertiesContainer(OrfProperties),
|
|
36
36
|
genbank: PropertiesContainer(GenbankView)
|
|
37
37
|
};
|
|
38
|
-
export class PropertiesDialog extends React.Component {
|
|
39
|
-
render() {
|
|
40
|
-
const {
|
|
41
|
-
propertiesTool = {},
|
|
42
|
-
propertiesViewTabUpdate,
|
|
43
|
-
dimensions = {},
|
|
44
|
-
height,
|
|
45
|
-
editorName,
|
|
46
|
-
onSave,
|
|
47
|
-
showReadOnly,
|
|
48
|
-
showAvailability,
|
|
49
|
-
isProtein,
|
|
50
|
-
annotationsToSupport = {},
|
|
51
|
-
disableSetReadOnly,
|
|
52
|
-
propertiesList = [
|
|
53
|
-
"general",
|
|
54
|
-
"features",
|
|
55
|
-
"parts",
|
|
56
|
-
"primers",
|
|
57
|
-
"translations",
|
|
58
|
-
"cutsites",
|
|
59
|
-
"orfs",
|
|
60
|
-
"genbank"
|
|
61
|
-
],
|
|
62
|
-
closePanelButton
|
|
63
|
-
} = { ...this.props, ...this.props.PropertiesProps };
|
|
64
38
|
|
|
65
|
-
|
|
39
|
+
export const PropertiesDialog = props => {
|
|
40
|
+
const {
|
|
41
|
+
propertiesTool = {},
|
|
42
|
+
propertiesViewTabUpdate,
|
|
43
|
+
dimensions = {},
|
|
44
|
+
height,
|
|
45
|
+
editorName,
|
|
46
|
+
onSave,
|
|
47
|
+
showReadOnly,
|
|
48
|
+
showAvailability,
|
|
49
|
+
isProtein,
|
|
50
|
+
annotationsToSupport = {},
|
|
51
|
+
disableSetReadOnly,
|
|
52
|
+
propertiesList = [
|
|
53
|
+
"general",
|
|
54
|
+
"features",
|
|
55
|
+
"parts",
|
|
56
|
+
"primers",
|
|
57
|
+
"translations",
|
|
58
|
+
"cutsites",
|
|
59
|
+
"orfs",
|
|
60
|
+
"genbank"
|
|
61
|
+
],
|
|
62
|
+
closePanelButton
|
|
63
|
+
} = { ...props, ...props.PropertiesProps };
|
|
66
64
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
tabId
|
|
65
|
+
const { width, height: heightFromDim } = dimensions;
|
|
66
|
+
|
|
67
|
+
let { tabId, selectedAnnotationId } = propertiesTool;
|
|
68
|
+
if (
|
|
69
|
+
propertiesList
|
|
70
|
+
.map(nameOrOverride => nameOrOverride.name || nameOrOverride)
|
|
71
|
+
.indexOf(tabId) === -1
|
|
72
|
+
) {
|
|
73
|
+
tabId = propertiesList[0].name || propertiesList[0];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const propertiesTabs = flatMap(propertiesList, nameOrOverride => {
|
|
77
|
+
if (annotationsToSupport[nameOrOverride] === false) {
|
|
78
|
+
return [];
|
|
74
79
|
}
|
|
75
|
-
const propertiesTabs = flatMap(propertiesList, nameOrOverride => {
|
|
76
|
-
if (annotationsToSupport[nameOrOverride] === false) {
|
|
77
|
-
return [];
|
|
78
|
-
}
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
81
|
+
const name = nameOrOverride.name || nameOrOverride;
|
|
82
|
+
const Comp = nameOrOverride.Comp || allTabs[name];
|
|
83
|
+
if (isProtein) {
|
|
84
|
+
if (
|
|
85
|
+
name === "translations" ||
|
|
86
|
+
name === "orfs" ||
|
|
87
|
+
name === "primers" ||
|
|
88
|
+
name === "cutsites"
|
|
89
|
+
) {
|
|
90
|
+
return null;
|
|
91
91
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
key={name}
|
|
101
|
-
title={title}
|
|
102
|
-
id={name}
|
|
103
|
-
panel={
|
|
104
|
-
<Comp
|
|
105
|
-
{...{
|
|
106
|
-
...pick(this.props, userDefinedHandlersAndOpts),
|
|
107
|
-
editorName,
|
|
108
|
-
onSave,
|
|
109
|
-
isProtein,
|
|
110
|
-
showReadOnly,
|
|
111
|
-
showAvailability,
|
|
112
|
-
disableSetReadOnly,
|
|
113
|
-
selectedAnnotationId,
|
|
114
|
-
...(nameOrOverride.name && nameOrOverride)
|
|
115
|
-
}}
|
|
116
|
-
/>
|
|
117
|
-
}
|
|
118
|
-
/>
|
|
119
|
-
);
|
|
120
|
-
});
|
|
121
|
-
const heightToUse = Math.max(0, Number((heightFromDim || height) - 30));
|
|
92
|
+
}
|
|
93
|
+
const title = (() => {
|
|
94
|
+
if (nameOrOverride.Comp) return name; //just use the user supplied name because this is a custom panel
|
|
95
|
+
if (name === "orfs") return "ORFs";
|
|
96
|
+
if (name === "cutsites") return "Cut Sites";
|
|
97
|
+
return startCase(name);
|
|
98
|
+
})();
|
|
99
|
+
|
|
122
100
|
return (
|
|
101
|
+
<Tab
|
|
102
|
+
key={name}
|
|
103
|
+
title={title}
|
|
104
|
+
id={name}
|
|
105
|
+
panel={
|
|
106
|
+
<Comp
|
|
107
|
+
{...{
|
|
108
|
+
...pick(props, userDefinedHandlersAndOpts),
|
|
109
|
+
editorName,
|
|
110
|
+
onSave,
|
|
111
|
+
isProtein,
|
|
112
|
+
showReadOnly,
|
|
113
|
+
showAvailability,
|
|
114
|
+
disableSetReadOnly,
|
|
115
|
+
selectedAnnotationId,
|
|
116
|
+
...(nameOrOverride.name && nameOrOverride)
|
|
117
|
+
}}
|
|
118
|
+
/>
|
|
119
|
+
}
|
|
120
|
+
/>
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
const heightToUse = Math.max(0, Number((heightFromDim || height) - 30));
|
|
124
|
+
return (
|
|
125
|
+
<div
|
|
126
|
+
style={{
|
|
127
|
+
position: "relative"
|
|
128
|
+
}}
|
|
129
|
+
>
|
|
130
|
+
{closePanelButton}
|
|
123
131
|
<div
|
|
132
|
+
className="ve-propertiesPanel"
|
|
124
133
|
style={{
|
|
125
|
-
|
|
134
|
+
display: "flex",
|
|
135
|
+
width,
|
|
136
|
+
height: heightToUse || 300,
|
|
137
|
+
zIndex: 10,
|
|
138
|
+
padding: 10
|
|
139
|
+
// paddingBottom: '31px',
|
|
126
140
|
}}
|
|
127
141
|
>
|
|
128
|
-
{
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
{
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
selectedTabId={tabId}
|
|
145
|
-
onChange={propertiesViewTabUpdate}
|
|
146
|
-
>
|
|
147
|
-
<Tabs.Expander />
|
|
148
|
-
{propertiesTabs}
|
|
149
|
-
<Tabs.Expander />
|
|
150
|
-
</Tabs>
|
|
151
|
-
) : (
|
|
152
|
-
<div style={{ margin: 20, fontSize: 20 }}>
|
|
153
|
-
No Properties to display
|
|
154
|
-
</div>
|
|
155
|
-
)}
|
|
156
|
-
</div>
|
|
142
|
+
{propertiesTabs.length ? (
|
|
143
|
+
<Tabs
|
|
144
|
+
style={{ width }}
|
|
145
|
+
renderActiveTabPanelOnly
|
|
146
|
+
selectedTabId={tabId}
|
|
147
|
+
onChange={propertiesViewTabUpdate}
|
|
148
|
+
>
|
|
149
|
+
<Tabs.Expander />
|
|
150
|
+
{propertiesTabs}
|
|
151
|
+
<Tabs.Expander />
|
|
152
|
+
</Tabs>
|
|
153
|
+
) : (
|
|
154
|
+
<div style={{ margin: 20, fontSize: 20 }}>
|
|
155
|
+
No Properties to display
|
|
156
|
+
</div>
|
|
157
|
+
)}
|
|
157
158
|
</div>
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
159
|
+
</div>
|
|
160
|
+
);
|
|
161
|
+
};
|
|
161
162
|
|
|
162
163
|
export default compose(
|
|
163
164
|
connectToEditor(({ propertiesTool, annotationsToSupport }) => {
|
|
@@ -1,194 +1,178 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useCallback, useMemo, useState } from "react";
|
|
2
2
|
import { reduxForm } from "redux-form";
|
|
3
|
-
|
|
4
3
|
import {
|
|
5
4
|
wrapDialog,
|
|
6
5
|
DataTable,
|
|
7
|
-
withSelectedEntities,
|
|
8
6
|
SwitchField,
|
|
9
|
-
|
|
7
|
+
useTableEntities
|
|
10
8
|
} from "@teselagen/ui";
|
|
11
9
|
import { compose } from "redux";
|
|
12
10
|
import { Button, Classes, Popover } from "@blueprintjs/core";
|
|
13
11
|
import classNames from "classnames";
|
|
14
|
-
|
|
15
12
|
import withEditorProps from "../../withEditorProps";
|
|
16
13
|
import { forEach, camelCase, startCase } from "lodash-es";
|
|
17
14
|
import { sizeSchema } from "../PropertiesDialog/utils";
|
|
18
15
|
import { getRangeLength } from "@teselagen/range-utils";
|
|
16
|
+
import { useFormValue } from "../../utils/useFormValue";
|
|
17
|
+
|
|
18
|
+
const dialogFormName = "RemoveDuplicatesDialog";
|
|
19
|
+
const dataTableFormName = "duplicatesToRemove";
|
|
20
|
+
const checkboxStyle = { marginTop: 0, marginBottom: 0 };
|
|
21
|
+
|
|
22
|
+
const RemoveDuplicatesDialog = props => {
|
|
23
|
+
const {
|
|
24
|
+
type,
|
|
25
|
+
sequenceData = { sequence: "" },
|
|
26
|
+
sequenceLength,
|
|
27
|
+
isProtein,
|
|
28
|
+
hideModal
|
|
29
|
+
} = props;
|
|
19
30
|
|
|
20
|
-
|
|
21
|
-
state = {
|
|
22
|
-
dups: []
|
|
23
|
-
};
|
|
24
|
-
componentDidMount() {
|
|
25
|
-
this.recomputeDups();
|
|
26
|
-
}
|
|
31
|
+
const { selectedEntities } = useTableEntities(dataTableFormName);
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
const ignoreName = useFormValue(dialogFormName, "ignoreName");
|
|
34
|
+
const ignoreStartAndEnd = useFormValue(dialogFormName, "ignoreStartAndEnd");
|
|
35
|
+
const ignoreStrand = useFormValue(dialogFormName, "ignoreStrand");
|
|
29
36
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
const recomputeDups = useCallback(
|
|
38
|
+
values => {
|
|
39
|
+
const ignoreName = values?.ignoreName;
|
|
40
|
+
const ignoreStartAndEnd = values?.ignoreStartAndEnd;
|
|
41
|
+
const ignoreStrand = values?.ignoreStrand;
|
|
42
|
+
const annotations = sequenceData[type];
|
|
43
|
+
const newDups = [];
|
|
44
|
+
const seqsHashByStartEndStrandName = {};
|
|
45
|
+
forEach(annotations, a => {
|
|
46
|
+
const hash = `${ignoreStartAndEnd ? "" : a.start}&${
|
|
47
|
+
ignoreStartAndEnd ? "" : a.end
|
|
48
|
+
}&${ignoreStrand ? "" : a.strand}&${ignoreName ? "" : a.name}`;
|
|
49
|
+
if (seqsHashByStartEndStrandName[hash]) {
|
|
50
|
+
newDups.push({ ...a, size: getRangeLength(a, sequenceLength) });
|
|
51
|
+
} else {
|
|
52
|
+
seqsHashByStartEndStrandName[hash] = true;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return newDups;
|
|
56
|
+
},
|
|
57
|
+
[sequenceData, sequenceLength, type]
|
|
58
|
+
);
|
|
48
59
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const seqsHashByStartEndStrandName = {};
|
|
52
|
-
forEach(annotations, a => {
|
|
53
|
-
const hash = `${ignoreStartAndEnd ? "" : a.start}&${
|
|
54
|
-
ignoreStartAndEnd ? "" : a.end
|
|
55
|
-
}&${ignoreStrand ? "" : a.strand}&${ignoreName ? "" : a.name}`;
|
|
56
|
-
if (seqsHashByStartEndStrandName[hash]) {
|
|
57
|
-
dups.push({ ...a, size: getRangeLength(a, sequenceLength) });
|
|
58
|
-
} else {
|
|
59
|
-
seqsHashByStartEndStrandName[hash] = true;
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
this.setState({ dups });
|
|
63
|
-
};
|
|
64
|
-
render() {
|
|
65
|
-
const { duplicatesToRemoveSelectedEntities, hideModal, type } = this.props;
|
|
60
|
+
const [dups, setDups] = useState(recomputeDups);
|
|
61
|
+
const selectedIds = useMemo(() => dups.map(d => d.id), [dups]);
|
|
66
62
|
|
|
67
|
-
|
|
63
|
+
const fieldSubmit = useCallback(
|
|
64
|
+
(newVal, field) => {
|
|
65
|
+
const values = {
|
|
66
|
+
ignoreName,
|
|
67
|
+
ignoreStartAndEnd,
|
|
68
|
+
ignoreStrand,
|
|
69
|
+
[field]: newVal
|
|
70
|
+
};
|
|
71
|
+
const newDups = recomputeDups(values);
|
|
72
|
+
setDups(newDups);
|
|
73
|
+
},
|
|
74
|
+
[ignoreName, ignoreStartAndEnd, ignoreStrand, recomputeDups]
|
|
75
|
+
);
|
|
68
76
|
|
|
69
|
-
|
|
77
|
+
const schema = useMemo(
|
|
78
|
+
() => ({
|
|
70
79
|
fields: [
|
|
71
|
-
// ...(noColor
|
|
72
|
-
// ? []
|
|
73
|
-
// : [
|
|
74
|
-
// {
|
|
75
|
-
// path: "color",
|
|
76
|
-
// type: "string",
|
|
77
|
-
// render: color => {
|
|
78
|
-
// return (
|
|
79
|
-
// <ColorPickerPopover>
|
|
80
|
-
// <div style={{ height: 20, width: 20, background: color }} />
|
|
81
|
-
// </ColorPickerPopover>
|
|
82
|
-
// );
|
|
83
|
-
// }
|
|
84
|
-
// }
|
|
85
|
-
// ]),
|
|
86
80
|
{ path: "name", type: "string" },
|
|
87
81
|
// ...(noType ? [] : [{ path: "type", type: "string" }]),
|
|
88
|
-
sizeSchema(
|
|
82
|
+
sizeSchema(isProtein),
|
|
89
83
|
{ path: "strand", type: "string" }
|
|
90
84
|
]
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return (
|
|
95
|
-
<div className={classNames(Classes.DIALOG_BODY, "tg-min-width-dialog")}>
|
|
96
|
-
{/* {dups.map((d) => {
|
|
97
|
-
return <div>
|
|
85
|
+
}),
|
|
86
|
+
[isProtein]
|
|
87
|
+
);
|
|
98
88
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
89
|
+
return (
|
|
90
|
+
<div className={classNames(Classes.DIALOG_BODY, "tg-min-width-dialog")}>
|
|
91
|
+
<DataTable
|
|
92
|
+
noPadding
|
|
93
|
+
withCheckboxes
|
|
94
|
+
noFullscreenButton
|
|
95
|
+
maxHeight={400}
|
|
96
|
+
selectedIds={selectedIds}
|
|
97
|
+
formName={dataTableFormName}
|
|
98
|
+
noRouter
|
|
99
|
+
noRowsFoundMessage="No duplicates found"
|
|
100
|
+
compact
|
|
101
|
+
noHeader
|
|
102
|
+
noFooter
|
|
103
|
+
withSearch={false}
|
|
104
|
+
hideSelectedCount
|
|
105
|
+
isInfinite
|
|
106
|
+
schema={schema}
|
|
107
|
+
entities={dups}
|
|
108
|
+
/>
|
|
109
|
+
<div
|
|
110
|
+
style={{
|
|
111
|
+
marginTop: 10,
|
|
112
|
+
display: "flex",
|
|
113
|
+
justifyContent: "space-between"
|
|
114
|
+
}}
|
|
115
|
+
>
|
|
116
|
+
<Popover
|
|
117
|
+
target={<Button icon="settings" />}
|
|
118
|
+
content={
|
|
119
|
+
<div style={{ padding: 20, maxWidth: 250 }}>
|
|
120
|
+
<div>Ignore These Fields While Finding Duplicates:</div>
|
|
121
|
+
<br />
|
|
122
|
+
<SwitchField
|
|
123
|
+
containerStyle={{ marginBottom: 2 }}
|
|
124
|
+
//delay the call to recompute dups until redux has had time to update
|
|
125
|
+
onFieldSubmit={newVal => fieldSubmit(newVal, "ignoreName")}
|
|
126
|
+
style={checkboxStyle}
|
|
127
|
+
name="ignoreName"
|
|
128
|
+
label="Name"
|
|
129
|
+
/>
|
|
130
|
+
<SwitchField
|
|
131
|
+
containerStyle={{ marginBottom: 2 }}
|
|
132
|
+
//delay the call to recompute dups until redux has had time to update
|
|
133
|
+
onFieldSubmit={newVal => fieldSubmit(newVal, "ignoreStrand")}
|
|
134
|
+
style={checkboxStyle}
|
|
135
|
+
name="ignoreStrand"
|
|
136
|
+
label="Strand"
|
|
137
|
+
/>
|
|
138
|
+
<SwitchField
|
|
139
|
+
containerStyle={{ marginBottom: 2 }}
|
|
140
|
+
//delay the call to recompute dups until redux has had time to update
|
|
141
|
+
onFieldSubmit={newVal =>
|
|
142
|
+
fieldSubmit(newVal, "ignoreStartAndEnd")
|
|
143
|
+
}
|
|
144
|
+
style={checkboxStyle}
|
|
145
|
+
name="ignoreStartAndEnd"
|
|
146
|
+
label="Start and End"
|
|
147
|
+
/>
|
|
148
|
+
</div>
|
|
149
|
+
}
|
|
119
150
|
/>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
151
|
+
|
|
152
|
+
<Button
|
|
153
|
+
intent="primary"
|
|
154
|
+
onClick={() => {
|
|
155
|
+
props[camelCase(`delete_${type}`).slice(0, -1)](
|
|
156
|
+
Object.keys(selectedEntities || {})
|
|
157
|
+
);
|
|
158
|
+
window.toastr.success(
|
|
159
|
+
`Successfully Deleted ${
|
|
160
|
+
Object.keys(selectedEntities || {}).length
|
|
161
|
+
} ${startCase(type)}`
|
|
162
|
+
);
|
|
163
|
+
hideModal();
|
|
125
164
|
}}
|
|
165
|
+
disabled={!Object.keys(selectedEntities || {}).length}
|
|
126
166
|
>
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
content={
|
|
130
|
-
<div style={{ padding: 20, maxWidth: 250 }}>
|
|
131
|
-
<div>Ignore These Fields While Finding Duplicates:</div>
|
|
132
|
-
<br></br>
|
|
133
|
-
<SwitchField
|
|
134
|
-
containerStyle={{ marginBottom: 2 }}
|
|
135
|
-
//delay the call to recompute dups until redux has had time to update
|
|
136
|
-
onFieldSubmit={this.delayedRecomputeDups}
|
|
137
|
-
style={this.checkboxStyle}
|
|
138
|
-
name="ignoreName"
|
|
139
|
-
label="Name"
|
|
140
|
-
></SwitchField>
|
|
141
|
-
<SwitchField
|
|
142
|
-
containerStyle={{ marginBottom: 2 }}
|
|
143
|
-
//delay the call to recompute dups until redux has had time to update
|
|
144
|
-
onFieldSubmit={this.delayedRecomputeDups}
|
|
145
|
-
style={this.checkboxStyle}
|
|
146
|
-
name="ignoreStrand"
|
|
147
|
-
label="Strand"
|
|
148
|
-
></SwitchField>
|
|
149
|
-
<SwitchField
|
|
150
|
-
containerStyle={{ marginBottom: 2 }}
|
|
151
|
-
//delay the call to recompute dups until redux has had time to update
|
|
152
|
-
onFieldSubmit={this.delayedRecomputeDups}
|
|
153
|
-
style={this.checkboxStyle}
|
|
154
|
-
name="ignoreStartAndEnd"
|
|
155
|
-
label="Start and End"
|
|
156
|
-
></SwitchField>
|
|
157
|
-
</div>
|
|
158
|
-
}
|
|
159
|
-
></Popover>
|
|
160
|
-
|
|
161
|
-
<Button
|
|
162
|
-
intent="primary"
|
|
163
|
-
onClick={() => {
|
|
164
|
-
this.props[camelCase(`delete_${type}`).slice(0, -1)](
|
|
165
|
-
duplicatesToRemoveSelectedEntities.map(d => d.id)
|
|
166
|
-
);
|
|
167
|
-
window.toastr.success(
|
|
168
|
-
`Successfully Deleted ${
|
|
169
|
-
duplicatesToRemoveSelectedEntities.length
|
|
170
|
-
} ${startCase(type)}`
|
|
171
|
-
);
|
|
172
|
-
hideModal();
|
|
173
|
-
}}
|
|
174
|
-
disabled={!(duplicatesToRemoveSelectedEntities || []).length}
|
|
175
|
-
>
|
|
176
|
-
Remove {duplicatesToRemoveSelectedEntities.length} Duplicates
|
|
177
|
-
</Button>
|
|
178
|
-
</div>
|
|
167
|
+
Remove {Object.keys(selectedEntities || {}).length} Duplicates
|
|
168
|
+
</Button>
|
|
179
169
|
</div>
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
170
|
+
</div>
|
|
171
|
+
);
|
|
172
|
+
};
|
|
183
173
|
|
|
184
174
|
export default compose(
|
|
185
175
|
wrapDialog(),
|
|
186
176
|
withEditorProps,
|
|
187
|
-
|
|
188
|
-
withSelectedEntities("duplicatesToRemove"),
|
|
189
|
-
|
|
190
|
-
reduxForm({
|
|
191
|
-
form: "RemoveDuplicatesDialog"
|
|
192
|
-
}),
|
|
193
|
-
tgFormValues("ignoreName", "ignoreStrand", "ignoreStartAndEnd")
|
|
177
|
+
reduxForm({ form: dialogFormName })
|
|
194
178
|
)(RemoveDuplicatesDialog);
|
|
@@ -600,8 +600,8 @@ function VectorInteractionHOC(Component /* options */) {
|
|
|
600
600
|
const makeTextCopyable = (transformFunc, className, action = "copy") => {
|
|
601
601
|
document.querySelectorAll(`.${className}`).forEach(element => {
|
|
602
602
|
element.addEventListener("click", async () => {
|
|
603
|
-
const {
|
|
604
|
-
const { sequenceData, copyOptions } =
|
|
603
|
+
const { editorName, store } = this.props;
|
|
604
|
+
const { sequenceData, copyOptions, selectionLayer } =
|
|
605
605
|
store.getState().VectorEditor[editorName];
|
|
606
606
|
|
|
607
607
|
const selectedSeqData = getSequenceDataBetweenRange(
|
|
@@ -621,6 +621,7 @@ function VectorInteractionHOC(Component /* options */) {
|
|
|
621
621
|
}
|
|
622
622
|
}
|
|
623
623
|
);
|
|
624
|
+
|
|
624
625
|
const sequenceDataToCopy = transformFunc(
|
|
625
626
|
selectedSeqData,
|
|
626
627
|
sequenceData
|
|
@@ -632,20 +633,24 @@ function VectorInteractionHOC(Component /* options */) {
|
|
|
632
633
|
|
|
633
634
|
try {
|
|
634
635
|
await navigator.clipboard.writeText(textToCopy);
|
|
635
|
-
if (action === "cut") {
|
|
636
|
-
// Optionally clear the original text or perform any "cut" related logic
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
if (window.Cypress) {
|
|
640
|
-
window.Cypress.textToCopy = sequenceDataToCopy.textToCopy;
|
|
641
|
-
window.Cypress.seqDataToCopy = sequenceDataToCopy;
|
|
642
|
-
}
|
|
643
636
|
} catch (err) {
|
|
644
|
-
console.error(
|
|
637
|
+
console.error("Failed to copy text:", err);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
if (action === "copy") {
|
|
641
|
+
document.body.addEventListener("copy", this.handleCopy);
|
|
642
|
+
} else {
|
|
643
|
+
document.body.addEventListener("cut", this.handleCut);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (window.Cypress) {
|
|
647
|
+
window.Cypress.textToCopy = sequenceDataToCopy.textToCopy;
|
|
648
|
+
window.Cypress.seqDataToCopy = sequenceDataToCopy;
|
|
645
649
|
}
|
|
646
650
|
});
|
|
647
651
|
});
|
|
648
652
|
};
|
|
653
|
+
|
|
649
654
|
const aaCopy = {
|
|
650
655
|
text: "Copy AA Sequence",
|
|
651
656
|
className: "openVeCopyAA",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function useFormValue(formName: any, field: any): any;
|