@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teselagen/ove",
3
- "version": "0.7.3-beta.7",
3
+ "version": "0.7.3",
4
4
  "main": "./src/index.js",
5
5
  "exports": {
6
6
  ".": {
@@ -12,7 +12,7 @@
12
12
  "dependencies": {
13
13
  "@teselagen/sequence-utils": "0.3.27",
14
14
  "@teselagen/range-utils": "0.3.7",
15
- "@teselagen/ui": "0.7.3-beta.5",
15
+ "@teselagen/ui": "0.7.7",
16
16
  "@teselagen/file-utils": "0.3.16",
17
17
  "@teselagen/bounce-loader": "0.3.11",
18
18
  "@teselagen/bio-parsers": "0.4.22",
@@ -48,7 +48,7 @@ export default compose(
48
48
  }))}
49
49
  withCheckboxes
50
50
  schema={annotationType === "feature" ? schemaFeatures : schemaOther}
51
- ></DataTable>
51
+ />
52
52
  </div>
53
53
  <DialogFooter
54
54
  hideModal={hideDialog}
@@ -1,13 +1,8 @@
1
- // import uniqid from "shortid";
2
- // import Ladder from "./Ladder";
3
- import { compose, withProps } from "recompose";
4
- // import selectionLayer from "../redux/selectionLayer";
5
- import React, { useState } from "react";
6
- import { DataTable } from "@teselagen/ui";
1
+ import React, { useMemo, useState } from "react";
2
+ import { DataTable, useStableReference } from "@teselagen/ui";
7
3
  import { getCutsiteType, getVirtualDigest } from "@teselagen/sequence-utils";
8
4
  import CutsiteFilter from "../CutsiteFilter";
9
5
  import Ladder from "./Ladder";
10
- // import getCutsiteType from "./getCutsiteType";
11
6
  import {
12
7
  Tabs,
13
8
  Tab,
@@ -22,23 +17,91 @@ import { pick } from "lodash-es";
22
17
 
23
18
  const MAX_DIGEST_CUTSITES = 50;
24
19
  const MAX_PARTIAL_DIGEST_CUTSITES = 10;
20
+ const onSingleSelectRow = ({ onFragmentSelect }) => {
21
+ onFragmentSelect();
22
+ };
25
23
 
26
24
  export const DigestTool = props => {
27
25
  const [selectedTab, setSelectedTab] = useState("virtualDigest");
28
26
  const {
29
27
  editorName,
30
- // height = 100,
31
28
  dimensions = {},
32
- lanes,
33
29
  digestTool: { selectedFragment, computePartialDigest },
34
30
  onDigestSave,
35
- computePartialDigestDisabled,
36
- computeDigestDisabled,
37
31
  updateComputePartialDigest,
38
32
  boxHeight,
39
33
  digestLaneRightClicked,
40
- ladders
34
+ ladders,
35
+ sequenceData,
36
+ sequenceLength,
37
+ selectionLayerUpdate: _selectionLayerUpdate,
38
+ updateSelectedFragment
41
39
  } = props;
40
+
41
+ const isCircular = sequenceData.circular;
42
+ const cutsites = sequenceData.cutsites;
43
+ const computePartialDigestDisabled =
44
+ cutsites.length > MAX_PARTIAL_DIGEST_CUTSITES;
45
+ const computeDigestDisabled = cutsites.length > MAX_DIGEST_CUTSITES;
46
+ // The selection layer update function is memoized to prevent re-renders
47
+ // It changes triggered by the DataTables below
48
+ const selectionLayerUpdate = useStableReference(_selectionLayerUpdate);
49
+
50
+ // This useMemo might not be necessary once if we figure out
51
+ // why the DataTables below triggers a re-render outside of them.
52
+ const lanes = useMemo(() => {
53
+ const { fragments } = getVirtualDigest({
54
+ cutsites,
55
+ sequenceLength,
56
+ isCircular,
57
+ computePartialDigest,
58
+ computePartialDigestDisabled,
59
+ computeDigestDisabled
60
+ });
61
+ const _lanes = [
62
+ fragments.map(f => ({
63
+ ...f,
64
+ onFragmentSelect: () => {
65
+ selectionLayerUpdate.current({
66
+ start: f.start,
67
+ end: f.end,
68
+ name: f.name
69
+ });
70
+ updateSelectedFragment(f.Intentid);
71
+ }
72
+ }))
73
+ ];
74
+ return _lanes;
75
+ }, [
76
+ computeDigestDisabled,
77
+ computePartialDigest,
78
+ computePartialDigestDisabled,
79
+ cutsites,
80
+ isCircular,
81
+ selectionLayerUpdate,
82
+ sequenceLength,
83
+ updateSelectedFragment
84
+ ]);
85
+
86
+ // Same comment as above
87
+ const digestInfoLanes = useMemo(
88
+ () =>
89
+ lanes[0].map(({ id, cut1, cut2, start, end, size, ...rest }) => {
90
+ return {
91
+ ...rest,
92
+ id,
93
+ start,
94
+ end,
95
+ length: size,
96
+ leftCutter: cut1.restrictionEnzyme.name,
97
+ rightCutter: cut2.restrictionEnzyme.name,
98
+ leftOverhang: getCutsiteType(cut1.restrictionEnzyme),
99
+ rightOverhang: getCutsiteType(cut2.restrictionEnzyme)
100
+ };
101
+ }),
102
+ [lanes]
103
+ );
104
+
42
105
  return (
43
106
  <div
44
107
  style={{
@@ -137,25 +200,9 @@ export const DigestTool = props => {
137
200
  maxHeight={400}
138
201
  // noFooter
139
202
  withSearch={false}
140
- onSingleRowSelect={({ onFragmentSelect }) => {
141
- onFragmentSelect();
142
- }}
203
+ onSingleRowSelect={onSingleSelectRow}
143
204
  formName="digestInfoTable"
144
- entities={lanes[0].map(
145
- ({ id, cut1, cut2, start, end, size, ...rest }) => {
146
- return {
147
- ...rest,
148
- id,
149
- start,
150
- end,
151
- length: size,
152
- leftCutter: cut1.restrictionEnzyme.name,
153
- rightCutter: cut2.restrictionEnzyme.name,
154
- leftOverhang: getCutsiteType(cut1.restrictionEnzyme),
155
- rightOverhang: getCutsiteType(cut2.restrictionEnzyme)
156
- };
157
- }
158
- )}
205
+ entities={digestInfoLanes}
159
206
  schema={schema}
160
207
  />
161
208
  }
@@ -178,48 +225,4 @@ const schema = {
178
225
  ]
179
226
  };
180
227
 
181
- export default compose(
182
- withEditorInteractions,
183
- withProps(
184
- ({
185
- sequenceData,
186
- sequenceLength,
187
- selectionLayerUpdate,
188
- updateSelectedFragment,
189
- digestTool: { computePartialDigest }
190
- }) => {
191
- const isCircular = sequenceData.circular;
192
- const cutsites = sequenceData.cutsites;
193
- const computePartialDigestDisabled =
194
- cutsites.length > MAX_PARTIAL_DIGEST_CUTSITES;
195
- const computeDigestDisabled = cutsites.length > MAX_DIGEST_CUTSITES;
196
-
197
- const { fragments, overlappingEnzymes } = getVirtualDigest({
198
- cutsites,
199
- sequenceLength,
200
- isCircular,
201
- computePartialDigest,
202
- computePartialDigestDisabled,
203
- computeDigestDisabled
204
- });
205
- return {
206
- computePartialDigestDisabled,
207
- computeDigestDisabled,
208
- lanes: [
209
- fragments.map(f => ({
210
- ...f,
211
- onFragmentSelect: () => {
212
- selectionLayerUpdate({
213
- start: f.start,
214
- end: f.end,
215
- name: f.name
216
- });
217
- updateSelectedFragment(f.Intentid);
218
- }
219
- }))
220
- ],
221
- overlappingEnzymes
222
- };
223
- }
224
- )
225
- )(DigestTool);
228
+ export default withEditorInteractions(DigestTool);
@@ -43,6 +43,7 @@ export function hideDialog() {
43
43
  delete dialogHolder.CustomModalComponent;
44
44
  delete dialogHolder.props;
45
45
  delete dialogHolder.overrideName;
46
+ delete dialogHolder.editorName;
46
47
  dialogHolder.setUniqKeyToForceRerender();
47
48
  }
48
49
 
@@ -1,16 +1,12 @@
1
- import React from "react";
2
- import {
3
- DataTable,
4
- withSelectedEntities,
5
- createCommandMenu
6
- } from "@teselagen/ui";
7
- import { map, get } from "lodash-es";
1
+ import React, { useCallback, useMemo } from "react";
2
+ import { DataTable, createCommandMenu } from "@teselagen/ui";
3
+ import { get } from "lodash-es";
8
4
  import CutsiteFilter from "../../CutsiteFilter";
9
5
  import { Button, ButtonGroup } from "@blueprintjs/core";
10
6
  import { connectToEditor } from "../../withEditorProps";
11
7
  import { compose } from "recompose";
12
8
  import selectors from "../../selectors";
13
- import commands from "../../commands";
9
+ import _commands from "../../commands";
14
10
  import { userDefinedHandlersAndOpts } from "../../Editor/userDefinedHandlersAndOpts";
15
11
  import { pick } from "lodash-es";
16
12
  import SingleEnzymeCutsiteInfo from "./SingleEnzymeCutsiteInfo";
@@ -18,105 +14,107 @@ import { withRestrictionEnzymes } from "../../CutsiteFilter/withRestrictionEnzym
18
14
  import { cutsitesSubmenu } from "../../MenuBar/viewSubmenu";
19
15
  import { getVisFilter } from "./GenericAnnotationProperties";
20
16
 
21
- class CutsiteProperties extends React.Component {
22
- constructor(props) {
23
- super(props);
24
- this.commands = commands(this);
25
- }
17
+ const schema = {
18
+ fields: [
19
+ { path: "name", type: "string" },
20
+ { path: "numberOfCuts", type: "number" },
21
+ { path: "groups", type: "string" }
22
+ ]
23
+ };
26
24
 
27
- SubComponent = row => {
28
- return (
29
- <SingleEnzymeCutsiteInfo
30
- {...{
31
- allRestrictionEnzymes: this.props.allRestrictionEnzymes,
32
- allCutsites: this.props.allCutsites,
33
- filteredCutsites: this.props.filteredCutsites,
34
- editorName: this.props.editorName,
35
- dispatch: this.props.dispatch,
36
- selectedAnnotationId: this.props.selectedAnnotationId,
37
- cutsiteGroup: row.original.cutsiteGroup,
38
- enzyme: row.original.enzyme
39
- }}
40
- ></SingleEnzymeCutsiteInfo>
41
- );
42
- };
25
+ const defaultValues = { order: ["numberOfCuts"] };
43
26
 
44
- schema = {
45
- fields: [
46
- { path: "name", type: "string" },
47
- { path: "numberOfCuts", type: "number" },
48
- { path: "groups", type: "string" }
49
- ]
50
- };
27
+ const CutsiteProperties = props => {
28
+ const commands = _commands({ props });
29
+ const {
30
+ allRestrictionEnzymes,
31
+ allCutsites,
32
+ annotationVisibilityShow,
33
+ createNewDigest,
34
+ dispatch,
35
+ editorName,
36
+ filteredCutsites,
37
+ selectedAnnotationId
38
+ } = props;
51
39
 
52
- onChangeHook = () => {
53
- this.props.annotationVisibilityShow("cutsites");
54
- };
55
- render() {
56
- const {
40
+ const SubComponent = useCallback(
41
+ row => (
42
+ <SingleEnzymeCutsiteInfo
43
+ allRestrictionEnzymes={allRestrictionEnzymes}
44
+ allCutsites={allCutsites}
45
+ filteredCutsites={filteredCutsites}
46
+ editorName={editorName}
47
+ dispatch={dispatch}
48
+ selectedAnnotationId={selectedAnnotationId}
49
+ cutsiteGroup={row.original.cutsiteGroup}
50
+ enzyme={row.original.enzyme}
51
+ />
52
+ ),
53
+ [
54
+ allCutsites,
55
+ allRestrictionEnzymes,
56
+ dispatch,
57
57
  editorName,
58
- createNewDigest,
59
- filteredCutsites: allCutsites,
58
+ filteredCutsites,
60
59
  selectedAnnotationId
61
- } = this.props;
60
+ ]
61
+ );
62
62
 
63
- const { cutsitesByName, cutsitesById } = allCutsites;
64
- const cutsitesToUse = map(cutsitesByName, cutsiteGroup => {
65
- const name = cutsiteGroup[0].restrictionEnzyme.name;
66
- let groups = "";
67
- const exisitingEnzymeGroups = window.getExistingEnzymeGroups();
63
+ const onChangeHook = useCallback(() => {
64
+ annotationVisibilityShow("cutsites");
65
+ }, [annotationVisibilityShow]);
68
66
 
69
- Object.keys(exisitingEnzymeGroups).forEach(key => {
70
- if (exisitingEnzymeGroups[key].includes(name)) groups += key;
71
- groups += " ";
72
- });
67
+ const { cutsitesByName, cutsitesById } = filteredCutsites;
73
68
 
74
- return {
75
- cutsiteGroup,
76
- id: name,
77
- name,
78
- numberOfCuts: cutsiteGroup.length,
79
- enzyme: cutsiteGroup[0].restrictionEnzyme,
80
- groups
81
- // size: getRangeLength(cutsiteGroup, sequenceData.sequence.length)
82
- };
83
- });
84
- return (
85
- <>
86
- <div
87
- style={{
88
- marginBottom: 10,
89
- paddingTop: 3,
90
- display: "flex",
91
- // flexWrap: 'wrap',
92
- width: "100%",
93
- // justifyContent: "space-between",
94
- alignItems: "center"
95
- }}
96
- >
97
- {/* <CmdCheckbox prefix="Show " cmd={this.commands.toggleCutsites} />
69
+ const cutsitesToUse = useMemo(
70
+ () =>
71
+ Object.values(cutsitesByName || {}).map(cutsiteGroup => {
72
+ const name = cutsiteGroup[0].restrictionEnzyme.name;
73
+ let groups = "";
74
+ const exisitingEnzymeGroups = window.getExistingEnzymeGroups();
75
+
76
+ Object.keys(exisitingEnzymeGroups).forEach(key => {
77
+ if (exisitingEnzymeGroups[key].includes(name)) groups += key;
78
+ groups += " ";
79
+ });
80
+
81
+ return {
82
+ cutsiteGroup,
83
+ id: name,
84
+ name,
85
+ numberOfCuts: cutsiteGroup.length,
86
+ enzyme: cutsiteGroup[0].restrictionEnzyme,
87
+ groups
88
+ };
89
+ }),
90
+ [cutsitesByName]
91
+ );
92
+
93
+ const selectedIds = useMemo(
94
+ () => get(cutsitesById[selectedAnnotationId], "restrictionEnzyme.name"),
95
+ [cutsitesById, selectedAnnotationId]
96
+ );
97
+
98
+ return (
99
+ <>
100
+ <div
101
+ style={{
102
+ marginBottom: 10,
103
+ paddingTop: 3,
104
+ display: "flex",
105
+ // flexWrap: 'wrap',
106
+ width: "100%",
107
+ // justifyContent: "space-between",
108
+ alignItems: "center"
109
+ }}
110
+ >
111
+ {getVisFilter(
112
+ createCommandMenu(cutsitesSubmenu, commands, {
113
+ useTicks: true
114
+ })
115
+ )}
116
+ <ButtonGroup>
98
117
  <Button
99
- style={{ marginLeft: 10, cursor: "auto" }}
100
- disabled
101
- minimal
102
- icon="filter"
103
- /> */}
104
- {getVisFilter(
105
- createCommandMenu(cutsitesSubmenu, this.commands, {
106
- useTicks: true
107
- })
108
- )}
109
- <ButtonGroup>
110
- <Button
111
- intent="success"
112
- data-tip="Virtual Digest"
113
- icon="cut"
114
- style={{ marginLeft: 15, flexGrow: -1 }}
115
- onClick={() => {
116
- createNewDigest();
117
- }}
118
- ></Button>
119
- {/* <Button
120
118
  intent="success"
121
119
  data-tip="Virtual Digest"
122
120
  icon="cut"
@@ -124,43 +122,37 @@ class CutsiteProperties extends React.Component {
124
122
  onClick={() => {
125
123
  createNewDigest();
126
124
  }}
127
- >
128
- </Button> */}
129
- </ButtonGroup>
130
-
131
- <CutsiteFilter
132
- {...pick(this.props, userDefinedHandlersAndOpts)}
133
- style={{ marginLeft: "auto", marginRight: 3 }}
134
- editorName={editorName}
135
- manageEnzymesToLeft
136
- onChangeHook={this.onChangeHook}
137
125
  />
138
- </div>
139
- <DataTable
140
- selectedIds={get(
141
- cutsitesById[selectedAnnotationId],
142
- "restrictionEnzyme.name"
143
- )}
144
- compact
145
- noSelect
146
- noHeader
147
- noFooter
148
- withExpandAndCollapseAllButton
149
- noFullscreenButton
150
- noPadding
151
- defaults={{ order: ["numberOfCuts"] }}
152
- formName="cutsiteProperties"
153
- noRouter
154
- withSearch={false}
155
- SubComponent={this.SubComponent}
156
- isInfinite
157
- schema={this.schema}
158
- entities={cutsitesToUse}
126
+ </ButtonGroup>
127
+ <CutsiteFilter
128
+ {...pick(props, userDefinedHandlersAndOpts)}
129
+ style={{ marginLeft: "auto", marginRight: 3 }}
130
+ editorName={editorName}
131
+ manageEnzymesToLeft
132
+ onChangeHook={onChangeHook}
159
133
  />
160
- </>
161
- );
162
- }
163
- }
134
+ </div>
135
+ <DataTable
136
+ selectedIds={selectedIds}
137
+ compact
138
+ noSelect
139
+ noHeader
140
+ noFooter
141
+ withExpandAndCollapseAllButton
142
+ noFullscreenButton
143
+ noPadding
144
+ defaults={defaultValues}
145
+ formName="cutsiteProperties"
146
+ noRouter
147
+ withSearch={false}
148
+ SubComponent={SubComponent}
149
+ isInfinite
150
+ schema={schema}
151
+ entities={cutsitesToUse}
152
+ />
153
+ </>
154
+ );
155
+ };
164
156
 
165
157
  export default compose(
166
158
  connectToEditor((editorState, ownProps) => {
@@ -180,6 +172,5 @@ export default compose(
180
172
  cutsites: cutsites.cutsitesArray
181
173
  };
182
174
  }),
183
- withRestrictionEnzymes,
184
- withSelectedEntities("cutsiteProperties")
175
+ withRestrictionEnzymes
185
176
  )(CutsiteProperties);
@@ -1,11 +1,17 @@
1
- import React from "react";
1
+ import React, { useCallback, useMemo } from "react";
2
2
  import { DataTable } from "@teselagen/ui";
3
-
4
3
  import { CutsiteTag } from "../../CutsiteFilter/AdditionalCutsiteInfoDialog";
5
-
6
4
  import EnzymeViewer from "../../EnzymeViewer";
7
5
  import { getEnzymeAliases } from "../../utils/editorUtils";
8
6
 
7
+ const schema = {
8
+ fields: [
9
+ { path: "topSnipPosition", displayName: "Top Snip", type: "string" },
10
+ { path: "position", type: "string" },
11
+ { path: "strand", type: "string" }
12
+ ]
13
+ };
14
+
9
15
  export default function SingleEnzymeCutsiteInfo({
10
16
  cutsiteGroup,
11
17
  enzyme,
@@ -16,40 +22,52 @@ export default function SingleEnzymeCutsiteInfo({
16
22
  allCutsites,
17
23
  filteredCutsites: { cutsitesByName: cutsitesByNameActive }
18
24
  }) {
19
- const onRowSelect = ([record]) => {
20
- if (!record) return;
25
+ const onRowSelect = useCallback(
26
+ ([record]) => {
27
+ if (!record) return;
21
28
 
22
- dispatch({
23
- type: "CARET_POSITION_UPDATE",
24
- payload: record.topSnipPosition,
25
- meta: {
26
- editorName
27
- }
28
- });
29
- };
30
- const aliases = getEnzymeAliases(enzyme);
31
- const entities = cutsiteGroup
32
- .sort((a, b) => a.topSnipPosition - b.topSnipPosition)
33
- .map(
34
- ({
35
- restrictionEnzyme: { forwardRegex, reverseRegex } = {},
36
- forward,
37
- id,
38
- topSnipBeforeBottom,
39
- topSnipPosition,
40
- bottomSnipPosition
41
- }) => {
42
- return {
43
- id,
44
- topSnipPosition,
45
- position: topSnipBeforeBottom
46
- ? topSnipPosition + " - " + bottomSnipPosition
47
- : bottomSnipPosition + " - " + topSnipPosition,
48
- strand:
49
- forwardRegex === reverseRegex ? "Palindromic" : forward ? "1" : "-1"
50
- };
51
- }
52
- );
29
+ dispatch({
30
+ type: "CARET_POSITION_UPDATE",
31
+ payload: record.topSnipPosition,
32
+ meta: {
33
+ editorName
34
+ }
35
+ });
36
+ },
37
+ [dispatch, editorName]
38
+ );
39
+
40
+ const aliases = useMemo(() => getEnzymeAliases(enzyme), [enzyme]);
41
+ const entities = useMemo(
42
+ () =>
43
+ cutsiteGroup
44
+ .sort((a, b) => a.topSnipPosition - b.topSnipPosition)
45
+ .map(
46
+ ({
47
+ restrictionEnzyme: { forwardRegex, reverseRegex } = {},
48
+ forward,
49
+ id,
50
+ topSnipBeforeBottom,
51
+ topSnipPosition,
52
+ bottomSnipPosition
53
+ }) => {
54
+ return {
55
+ id,
56
+ topSnipPosition,
57
+ position: topSnipBeforeBottom
58
+ ? topSnipPosition + " - " + bottomSnipPosition
59
+ : bottomSnipPosition + " - " + topSnipPosition,
60
+ strand:
61
+ forwardRegex === reverseRegex
62
+ ? "Palindromic"
63
+ : forward
64
+ ? "1"
65
+ : "-1"
66
+ };
67
+ }
68
+ ),
69
+ [cutsiteGroup]
70
+ );
53
71
 
54
72
  return (
55
73
  <div>
@@ -61,14 +79,12 @@ export default function SingleEnzymeCutsiteInfo({
61
79
  >
62
80
  {enzyme && (
63
81
  <EnzymeViewer
64
- {...{
65
- sequence: enzyme.site,
66
- reverseSnipPosition: enzyme.bottomSnipOffset,
67
- forwardSnipPosition: enzyme.topSnipOffset
68
- }}
82
+ sequence={enzyme.site}
83
+ reverseSnipPosition={enzyme.bottomSnipOffset}
84
+ forwardSnipPosition={enzyme.topSnipOffset}
69
85
  />
70
86
  )}
71
- <br></br>
87
+ <br />
72
88
  {entities && !!entities.length && (
73
89
  <div>
74
90
  <DataTable
@@ -106,7 +122,7 @@ export default function SingleEnzymeCutsiteInfo({
106
122
  key={i}
107
123
  name={n}
108
124
  doNotShowCuts
109
- ></CutsiteTag>
125
+ />
110
126
  );
111
127
  })}
112
128
  </div>
@@ -117,14 +133,6 @@ export default function SingleEnzymeCutsiteInfo({
117
133
  );
118
134
  }
119
135
 
120
- const schema = {
121
- fields: [
122
- { path: "topSnipPosition", displayName: "Top Snip", type: "string" },
123
- { path: "position", type: "string" },
124
- { path: "strand", type: "string" }
125
- ]
126
- };
127
-
128
136
  // export default compose(
129
137
  // withEditorProps,
130
138
  // withRestrictionEnzymes