@teselagen/ove 0.7.1 → 0.7.2

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.
Files changed (34) hide show
  1. package/DigestTool/DigestTool.d.ts +1 -1
  2. package/Menlo.ttf +0 -0
  3. package/Monaco.ttf +0 -0
  4. package/helperComponents/PropertiesDialog/index.d.ts +2 -7
  5. package/html2canvas.esm--JN4fLQL.mjs +7891 -0
  6. package/html2canvas.esm-B7d7VJmQ.js +7891 -0
  7. package/html2canvas.esm-GLpiTZLt.cjs +7891 -0
  8. package/html2canvas.esm-GLpiTZLt.js +7891 -0
  9. package/html2canvas.esm-nFNn58sx.js +7891 -0
  10. package/html2canvas.esm-nFNn58sx.mjs +7891 -0
  11. package/index.cjs.js +6534 -536
  12. package/index.es.js +6523 -525
  13. package/index.umd.js +6523 -516
  14. package/package.json +2 -2
  15. package/src/AutoAnnotate.js +1 -0
  16. package/src/CircularView/Labels/index.js +1 -15
  17. package/src/CircularView/index.js +3 -4
  18. package/src/CreateAnnotationsPage.js +1 -1
  19. package/src/DigestTool/DigestTool.js +78 -75
  20. package/src/GlobalDialogUtils.js +1 -0
  21. package/src/Reflex/reflex-styles.css +10 -10
  22. package/src/RowItem/CutsiteSelectionLayers.js +1 -1
  23. package/src/RowItem/SelectionLayer/index.js +2 -2
  24. package/src/RowItem/Sequence.js +0 -1
  25. package/src/RowItem/Translations/Translation.js +1 -0
  26. package/src/RowItem/index.js +0 -1
  27. package/src/helperComponents/PropertiesDialog/CutsiteProperties.js +126 -135
  28. package/src/helperComponents/PropertiesDialog/SingleEnzymeCutsiteInfo.js +59 -51
  29. package/src/helperComponents/PropertiesDialog/index.js +115 -114
  30. package/src/helperComponents/RemoveDuplicates/index.js +144 -160
  31. package/src/utils/useFormValue.js +7 -0
  32. package/src/withEditorInteractions/index.js +8 -7
  33. package/style.css +39 -15
  34. 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
- <React.Fragment>
21
+ <>
22
22
  {additionalHeaderEls}
23
23
  <Comp {...rest} />
24
24
  {additionalFooterEls}
25
- </React.Fragment>
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
- const { width, height: heightFromDim } = dimensions;
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
- 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];
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
- const name = nameOrOverride.name || nameOrOverride;
81
- const Comp = nameOrOverride.Comp || allTabs[name];
82
- if (isProtein) {
83
- if (
84
- name === "translations" ||
85
- name === "orfs" ||
86
- name === "primers" ||
87
- name === "cutsites"
88
- ) {
89
- return null;
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
- const title = (() => {
93
- if (nameOrOverride.Comp) return name; //just use the user supplied name because this is a custom panel
94
- if (name === "orfs") return "ORFs";
95
- if (name === "cutsites") return "Cut Sites";
96
- return startCase(name);
97
- })();
98
- return (
99
- <Tab
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
- position: "relative"
134
+ display: "flex",
135
+ width,
136
+ height: heightToUse || 300,
137
+ zIndex: 10,
138
+ padding: 10
139
+ // paddingBottom: '31px',
126
140
  }}
127
141
  >
128
- {closePanelButton}
129
- <div
130
- className="ve-propertiesPanel"
131
- style={{
132
- display: "flex",
133
- width,
134
- height: heightToUse || 300,
135
- zIndex: 10,
136
- padding: 10
137
- // paddingBottom: '31px',
138
- }}
139
- >
140
- {propertiesTabs.length ? (
141
- <Tabs
142
- style={{ width }}
143
- renderActiveTabPanelOnly
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
- tgFormValues
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
- class RemoveDuplicatesDialog extends React.Component {
21
- state = {
22
- dups: []
23
- };
24
- componentDidMount() {
25
- this.recomputeDups();
26
- }
31
+ const { selectedEntities } = useTableEntities(dataTableFormName);
27
32
 
28
- checkboxStyle = { marginTop: 0, marginBottom: 0 };
33
+ const ignoreName = useFormValue(dialogFormName, "ignoreName");
34
+ const ignoreStartAndEnd = useFormValue(dialogFormName, "ignoreStartAndEnd");
35
+ const ignoreStrand = useFormValue(dialogFormName, "ignoreStrand");
29
36
 
30
- delayedRecomputeDups = () => {
31
- setTimeout(() => {
32
- this.recomputeDups();
33
- });
34
- };
35
- recomputeDups = () => {
36
- const {
37
- // hideModal,
38
- type,
39
- sequenceData = { sequence: "" },
40
- // handleSubmit,
41
- sequenceLength,
42
- ignoreName,
43
- ignoreStrand,
44
- ignoreStartAndEnd
45
- // circular,
46
- // upsertFeature
47
- } = this.props;
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
- const annotations = sequenceData[type];
50
- const dups = [];
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
- const selectedIds = this.state.dups.map(d => d.id);
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
- const schema = {
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(this.props.isProtein),
82
+ sizeSchema(isProtein),
89
83
  { path: "strand", type: "string" }
90
84
  ]
91
- };
92
- // const sequenceLength = sequenceData.sequence.length;
93
- // const isCirc = (this.state || {}).circular;
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
- </div>
100
- })} */}
101
- <DataTable
102
- noPadding
103
- withCheckboxes
104
- noFullscreenButton
105
- // onRowSelect={this.onRowSelect}
106
- maxHeight={400}
107
- selectedIds={selectedIds}
108
- formName="duplicatesToRemove"
109
- noRouter
110
- noRowsFoundMessage="No duplicates found"
111
- compact
112
- noHeader
113
- noFooter
114
- withSearch={false}
115
- hideSelectedCount
116
- isInfinite
117
- schema={schema}
118
- entities={this.state.dups}
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
- <div
121
- style={{
122
- marginTop: 10,
123
- display: "flex",
124
- justifyContent: "space-between"
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
- <Popover
128
- target={<Button icon="settings" />}
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);
@@ -0,0 +1,7 @@
1
+ /* Copyright (C) 2018 TeselaGen Biotechnology, Inc. */
2
+ import { useSelector } from "react-redux";
3
+ import { get } from "lodash";
4
+
5
+ export const useFormValue = (formName, field) => {
6
+ return useSelector(state => get(state.form?.[formName]?.values, field));
7
+ };
@@ -602,8 +602,13 @@ function VectorInteractionHOC(Component /* options */) {
602
602
  return new Clipboard(`.${className}`, {
603
603
  action: () => action,
604
604
  text: () => {
605
- const { selectionLayer, editorName, store } = this.props;
606
- const { sequenceData, copyOptions } =
605
+ if (action === "copy") {
606
+ document.body.addEventListener("copy", this.handleCopy);
607
+ } else {
608
+ document.body.addEventListener("cut", this.handleCut);
609
+ }
610
+ const { editorName, store } = this.props;
611
+ const { sequenceData, copyOptions, selectionLayer } =
607
612
  store.getState().VectorEditor[editorName];
608
613
 
609
614
  const selectedSeqData = getSequenceDataBetweenRange(
@@ -628,11 +633,7 @@ function VectorInteractionHOC(Component /* options */) {
628
633
  sequenceData
629
634
  );
630
635
  this.sequenceDataToCopy = sequenceDataToCopy;
631
- if (action === "copy") {
632
- document.body.addEventListener("copy", this.handleCopy);
633
- } else {
634
- document.body.addEventListener("cut", this.handleCut);
635
- }
636
+
636
637
  if (window.Cypress) {
637
638
  window.Cypress.textToCopy = sequenceDataToCopy.textToCopy;
638
639
  window.Cypress.seqDataToCopy = sequenceDataToCopy;