@teselagen/ove 0.6.1-beta.1 → 0.7.1

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.6.1-beta.1",
3
+ "version": "0.7.1",
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.6.1-beta.1",
15
+ "@teselagen/ui": "0.7.1",
16
16
  "@teselagen/file-utils": "0.3.16",
17
17
  "@teselagen/bounce-loader": "0.3.11",
18
18
  "@teselagen/bio-parsers": "0.4.22",
@@ -58,7 +58,6 @@
58
58
  "lodash-es": "^4.17.21",
59
59
  "math-expression-evaluator": "^1.3.7",
60
60
  "mobx": "^6.10.2",
61
- "mobx-react": "^9.0.1",
62
61
  "mock-fs": "5.2.0",
63
62
  "nanoid": "^4.0.0",
64
63
  "node-interval-tree": "^1.3.3",
@@ -68,16 +67,16 @@
68
67
  "popper.js": "^1.16.1",
69
68
  "prop-types": "^15.6.2",
70
69
  "qs": "^6.9.6",
71
- "react": "^18.2.0",
70
+ "react": "^18.3.1",
72
71
  "react-color": "^2.19.3",
73
- "react-dom": "^18.2.0",
72
+ "react-dom": "^18.3.1",
74
73
  "react-draggable": "4.4.5",
75
74
  "react-dropzone": "^11.4.2",
76
- "react-markdown": "9.0.1",
75
+ "react-markdown": "8.0.7",
77
76
  "react-measure": "^2.5.2",
78
77
  "react-redux": "^8.0.5",
79
78
  "react-rnd": "^10.2.4",
80
- "react-router-dom": "^4.3.1",
79
+ "react-router-dom": "4",
81
80
  "react-sizeme": "^2.6.12",
82
81
  "react-transition-group": "^2.4.0",
83
82
  "recompose": "npm:react-recompose@0.31.1",
@@ -85,7 +84,7 @@
85
84
  "redux-act": "^1.8.0",
86
85
  "redux-form": "^8.3.10",
87
86
  "redux-thunk": "2.4.1",
88
- "remark-gfm": "^4.0.0",
87
+ "remark-gfm": "^3.0.1",
89
88
  "reselect": "^4.1.7",
90
89
  "shortid": "^2.2.16",
91
90
  "tg-use-local-storage-state": "^16.0.3",
@@ -152,48 +152,46 @@ export function CutsiteFilter(props) {
152
152
  filteredRestrictionEnzymes.length > 1;
153
153
  const [queryTracker, setQueryTracker] = useState("");
154
154
 
155
- const renderOptions = ({ label, value, canBeHidden }, props) => {
156
- const { filteredRestrictionEnzymes, filteredRestrictionEnzymesUpdate } =
157
- props;
155
+ const renderOptions = (
156
+ { label, value, canBeHidden },
157
+ { filteredRestrictionEnzymes, filteredRestrictionEnzymesUpdate }
158
+ ) => (
159
+ <div
160
+ style={{
161
+ display: "flex",
162
+ justifyContent: "space-between",
163
+ width: "100%"
164
+ }}
165
+ >
166
+ {label}{" "}
167
+ {canBeHidden && (
168
+ <Icon
169
+ onClick={e => {
170
+ e.stopPropagation();
158
171
 
159
- return (
160
- <div
161
- style={{
162
- display: "flex",
163
- justifyContent: "space-between",
164
- width: "100%"
165
- }}
166
- >
167
- {label}{" "}
168
- {canBeHidden && (
169
- <Icon
170
- onClick={e => {
171
- e.stopPropagation();
172
-
173
- filteredRestrictionEnzymesUpdate(
174
- flatMap(filteredRestrictionEnzymes, e => {
175
- if (e.value === value) return [];
176
- return e;
177
- }).concat({
178
- label,
179
- className: "veHiddenEnzyme",
180
- value,
181
- // hiddenEnzyme: true,
182
- isHidden: true,
183
- canBeHidden
184
- })
185
- );
186
- }}
187
- htmlTitle="Hide this enzyme"
188
- className="veHideEnzymeBtn"
189
- style={{ paddingTop: 5 }}
190
- iconSize={14}
191
- icon="eye-off"
192
- ></Icon>
193
- )}
194
- </div>
195
- );
196
- };
172
+ filteredRestrictionEnzymesUpdate(
173
+ flatMap(filteredRestrictionEnzymes, e => {
174
+ if (e.value === value) return [];
175
+ return e;
176
+ }).concat({
177
+ label,
178
+ className: "veHiddenEnzyme",
179
+ value,
180
+ // hiddenEnzyme: true,
181
+ isHidden: true,
182
+ canBeHidden
183
+ })
184
+ );
185
+ }}
186
+ htmlTitle="Hide this enzyme"
187
+ className="veHideEnzymeBtn"
188
+ style={{ paddingTop: 5 }}
189
+ iconSize={14}
190
+ icon="eye-off"
191
+ />
192
+ )}
193
+ </div>
194
+ );
197
195
  const filteredRestrictionEnzymesUpdate = enzymes => {
198
196
  _filteredRestrictionEnzymesUpdate(enzymes);
199
197
  if (sequenceData?.id) {
@@ -285,7 +283,7 @@ export function CutsiteFilter(props) {
285
283
  data-tip="Manage Enzymes"
286
284
  onClick={onManageClick}
287
285
  icon="cog"
288
- ></Button>
286
+ />
289
287
  )}
290
288
  <div
291
289
  style={{
@@ -345,19 +343,17 @@ export function CutsiteFilter(props) {
345
343
  wrapperStyle={{ zIndex: 11 }}
346
344
  noResultsText={
347
345
  <NoResults
348
- {...{
349
- inputSequenceToTestAgainst: sequenceData.sequence,
350
- manageEnzymesLink,
351
- closeDropDown,
352
- onHiddenEnzymeAdd,
353
- queryString: queryTracker,
354
- additionalEnzymes,
355
- enzymeGroupsOverride,
356
- cutsitesByNameActive: filteredCutsites.cutsitesByName,
357
- cutsitesByName: allCutsites.cutsitesByName,
358
- editorName
359
- }}
360
- ></NoResults>
346
+ inputSequenceToTestAgainst={sequenceData.sequence}
347
+ manageEnzymesLink={manageEnzymesLink}
348
+ closeDropDown={closeDropDown}
349
+ onHiddenEnzymeAdd={onHiddenEnzymeAdd}
350
+ queryString={queryTracker}
351
+ additionalEnzymes={additionalEnzymes}
352
+ enzymeGroupsOverride={enzymeGroupsOverride}
353
+ cutsitesByNameActive={filteredCutsites.cutsitesByName}
354
+ cutsitesByName={allCutsites.cutsitesByName}
355
+ editorName={editorName}
356
+ />
361
357
  }
362
358
  onInputChange={queryTracker => {
363
359
  setQueryTracker(queryTracker);
@@ -2,7 +2,7 @@
2
2
  // import Ladder from "./Ladder";
3
3
  import { compose, withProps } from "recompose";
4
4
  // import selectionLayer from "../redux/selectionLayer";
5
- import React from "react";
5
+ import React, { useState } from "react";
6
6
  import { DataTable } from "@teselagen/ui";
7
7
  import { getCutsiteType, getVirtualDigest } from "@teselagen/sequence-utils";
8
8
  import CutsiteFilter from "../CutsiteFilter";
@@ -18,153 +18,154 @@ import {
18
18
  } from "@blueprintjs/core";
19
19
  import withEditorInteractions from "../withEditorInteractions";
20
20
  import { userDefinedHandlersAndOpts } from "../Editor/userDefinedHandlersAndOpts";
21
- import { noop, pick } from "lodash-es";
21
+ import { pick } from "lodash-es";
22
22
 
23
23
  const MAX_DIGEST_CUTSITES = 50;
24
24
  const MAX_PARTIAL_DIGEST_CUTSITES = 10;
25
- export class DigestTool extends React.Component {
26
- state = { selectedTab: "virtualDigest" };
27
- render() {
28
- const {
29
- editorName,
30
- // height = 100,
31
- dimensions = {},
32
- lanes,
33
- digestTool: { selectedFragment, computePartialDigest },
34
- onDigestSave,
35
- computePartialDigestDisabled,
36
- computeDigestDisabled,
37
- updateComputePartialDigest
38
- } = this.props;
39
- const { selectedTab } = this.state;
40
- return (
41
- <div
42
- style={{
43
- height:
44
- typeof dimensions.height === "string" ? 100 : dimensions.height,
45
- overflowY: "auto",
46
- padding: 10
47
- }}
48
- >
49
- {onDigestSave && (
50
- <div style={{ display: "flex", marginBottom: 10 }}>
51
- <InputGroup placeholder="My Digest" />
52
- <Button
53
- intent={Intent.PRIMARY}
54
- onClick={() => {
55
- onDigestSave({});
56
- }}
57
- style={{ marginLeft: 5 }}
58
- >
59
- {" "}
60
- Save
61
- </Button>
62
- </div>
63
- )}
64
- <div style={{ display: "flex", justifyContent: "space-between" }}>
65
- <Checkbox
66
- onChange={() => {
67
- updateComputePartialDigest(!computePartialDigest);
25
+
26
+ export const DigestTool = props => {
27
+ const [selectedTab, setSelectedTab] = useState("virtualDigest");
28
+ const {
29
+ editorName,
30
+ // height = 100,
31
+ dimensions = {},
32
+ lanes,
33
+ digestTool: { selectedFragment, computePartialDigest },
34
+ onDigestSave,
35
+ computePartialDigestDisabled,
36
+ computeDigestDisabled,
37
+ updateComputePartialDigest,
38
+ boxHeight,
39
+ digestLaneRightClicked,
40
+ ladders
41
+ } = props;
42
+ return (
43
+ <div
44
+ style={{
45
+ height: typeof dimensions.height === "string" ? 100 : dimensions.height,
46
+ overflowY: "auto",
47
+ padding: 10
48
+ }}
49
+ >
50
+ {onDigestSave && (
51
+ <div style={{ display: "flex", marginBottom: 10 }}>
52
+ <InputGroup placeholder="My Digest" />
53
+ <Button
54
+ intent={Intent.PRIMARY}
55
+ onClick={() => {
56
+ onDigestSave({});
68
57
  }}
69
- checked={computePartialDigest}
70
- label={
71
- <span>
72
- Show Partial Digests{" "}
73
- {computePartialDigestDisabled ? (
74
- <span style={{ fontSize: 10 }}>
75
- {" "}
76
- -- Disabled (only supports {MAX_PARTIAL_DIGEST_CUTSITES} or
77
- fewer cut sites){" "}
78
- </span>
79
- ) : null}
80
- </span>
81
- }
82
- disabled={computePartialDigestDisabled}
83
- ></Checkbox>
84
- <a
85
- target="_blank"
86
- rel="noopener noreferrer"
87
- href="http://docs.teselagen.com/en/articles/5489322-restriction-digest-in-the-vector-editor"
58
+ style={{ marginLeft: 5 }}
88
59
  >
89
- Read the docs
90
- </a>
60
+ {" "}
61
+ Save
62
+ </Button>
91
63
  </div>
92
- Choose your enzymes:
93
- <CutsiteFilter
94
- {...pick(this.props, userDefinedHandlersAndOpts)}
95
- editorName={editorName}
96
- />
97
- <br />
98
- {computeDigestDisabled && (
99
- <div
100
- style={{
101
- color: "red",
102
- marginBottom: "6px",
103
- fontSize: "15px"
104
- }}
105
- >
106
- {`>${MAX_DIGEST_CUTSITES} cut sites detected. Filter out additional
107
- restriction enzymes to visualize digest results`}
108
- </div>
109
- )}
110
- <Tabs
111
- selectedTabId={selectedTab}
112
- onChange={id => {
113
- this.setState({ selectedTab: id });
64
+ )}
65
+ <div style={{ display: "flex", justifyContent: "space-between" }}>
66
+ <Checkbox
67
+ onChange={() => {
68
+ updateComputePartialDigest(!computePartialDigest);
114
69
  }}
70
+ checked={computePartialDigest}
71
+ label={
72
+ <span>
73
+ Show Partial Digests{" "}
74
+ {computePartialDigestDisabled ? (
75
+ <span style={{ fontSize: 10 }}>
76
+ {" "}
77
+ -- Disabled (only supports {MAX_PARTIAL_DIGEST_CUTSITES} or
78
+ fewer cut sites){" "}
79
+ </span>
80
+ ) : null}
81
+ </span>
82
+ }
83
+ disabled={computePartialDigestDisabled}
84
+ />
85
+ <a
86
+ target="_blank"
87
+ rel="noopener noreferrer"
88
+ href="http://docs.teselagen.com/en/articles/5489322-restriction-digest-in-the-vector-editor"
115
89
  >
116
- <Tab
117
- title="Virtual Digest"
118
- id="virtualDigest"
119
- panel={<Ladder {...this.props} editorName={editorName} />}
120
- />
121
- <Tab
122
- title="Digest Info"
123
- id="table"
124
- panel={
125
- <DataTable
126
- noRouter
127
- isSimple
128
- maxHeight={400}
129
- // noFooter
130
- withSearch={false}
131
- onSingleRowSelect={({ onFragmentSelect }) => {
132
- onFragmentSelect();
133
- }}
134
- reduxFormSelectedEntityIdMap={{
135
- input: {
136
- value: {
137
- [selectedFragment]: true
138
- },
139
- onChange: noop
140
- }
141
- }}
142
- formName="digestInfoTable"
143
- entities={lanes[0].map(
144
- ({ id, cut1, cut2, start, end, size, ...rest }) => {
145
- return {
146
- ...rest,
147
- id,
148
- start,
149
- end,
150
- length: size,
151
- leftCutter: cut1.restrictionEnzyme.name,
152
- rightCutter: cut2.restrictionEnzyme.name,
153
- leftOverhang: getCutsiteType(cut1.restrictionEnzyme),
154
- rightOverhang: getCutsiteType(cut2.restrictionEnzyme)
155
- };
156
- }
157
- )}
158
- schema={schema}
159
- />
160
- }
161
- />
162
- </Tabs>
163
- <br />
90
+ Read the docs
91
+ </a>
164
92
  </div>
165
- );
166
- }
167
- }
93
+ Choose your enzymes:
94
+ <CutsiteFilter
95
+ {...pick(props, userDefinedHandlersAndOpts)}
96
+ editorName={editorName}
97
+ />
98
+ <br />
99
+ {computeDigestDisabled && (
100
+ <div
101
+ style={{
102
+ color: "red",
103
+ marginBottom: "6px",
104
+ fontSize: "15px"
105
+ }}
106
+ >
107
+ {`>${MAX_DIGEST_CUTSITES} cut sites detected. Filter out additional
108
+ restriction enzymes to visualize digest results`}
109
+ </div>
110
+ )}
111
+ <Tabs
112
+ selectedTabId={selectedTab}
113
+ onChange={id => {
114
+ setSelectedTab(id);
115
+ }}
116
+ >
117
+ <Tab
118
+ title="Virtual Digest"
119
+ id="virtualDigest"
120
+ panel={
121
+ <Ladder
122
+ boxHeight={boxHeight}
123
+ lanes={lanes}
124
+ digestLaneRightClicked={digestLaneRightClicked}
125
+ selectedFragment={selectedFragment}
126
+ ladders={ladders}
127
+ />
128
+ }
129
+ />
130
+ <Tab
131
+ title="Digest Info"
132
+ id="table"
133
+ panel={
134
+ <DataTable
135
+ noRouter
136
+ isSimple
137
+ maxHeight={400}
138
+ // noFooter
139
+ withSearch={false}
140
+ onSingleRowSelect={({ onFragmentSelect }) => {
141
+ onFragmentSelect();
142
+ }}
143
+ 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
+ )}
159
+ schema={schema}
160
+ />
161
+ }
162
+ />
163
+ </Tabs>
164
+ <br />
165
+ </div>
166
+ );
167
+ };
168
+
168
169
  const schema = {
169
170
  fields: [
170
171
  { width: 60, path: "start", displayName: "Start", type: "string" },
@@ -179,45 +180,46 @@ const schema = {
179
180
 
180
181
  export default compose(
181
182
  withEditorInteractions,
182
- withProps(props => {
183
- const {
183
+ withProps(
184
+ ({
184
185
  sequenceData,
185
186
  sequenceLength,
186
187
  selectionLayerUpdate,
187
188
  updateSelectedFragment,
188
189
  digestTool: { computePartialDigest }
189
- } = props;
190
- const isCircular = sequenceData.circular;
191
- const cutsites = sequenceData.cutsites;
192
- const computePartialDigestDisabled =
193
- cutsites.length > MAX_PARTIAL_DIGEST_CUTSITES;
194
- const computeDigestDisabled = cutsites.length > MAX_DIGEST_CUTSITES;
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;
195
196
 
196
- const { fragments, overlappingEnzymes } = getVirtualDigest({
197
- cutsites,
198
- sequenceLength,
199
- isCircular,
200
- computePartialDigest,
201
- computePartialDigestDisabled,
202
- computeDigestDisabled
203
- });
204
- return {
205
- computePartialDigestDisabled,
206
- computeDigestDisabled,
207
- lanes: [
208
- fragments.map(f => ({
209
- ...f,
210
- onFragmentSelect: () => {
211
- selectionLayerUpdate({
212
- start: f.start,
213
- end: f.end,
214
- name: f.name
215
- });
216
- updateSelectedFragment(f.Intentid);
217
- }
218
- }))
219
- ],
220
- overlappingEnzymes
221
- };
222
- })
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
+ )
223
225
  )(DigestTool);
@@ -123,7 +123,10 @@ function PCRTool(props) {
123
123
  inlineLabel
124
124
  tooltipError
125
125
  options={forwardPrimers}
126
- defaultValue={forwardPrimers[0] ? forwardPrimers[0].value : undefined}
126
+ defaultValue={
127
+ forwardPrimer ??
128
+ (forwardPrimers[0] ? forwardPrimers[0].value : undefined)
129
+ }
127
130
  name="forwardPrimer"
128
131
  label="Forward Primer:"
129
132
  />
@@ -132,7 +135,10 @@ function PCRTool(props) {
132
135
  inlineLabel
133
136
  tooltipError
134
137
  options={reversePrimers}
135
- defaultValue={reversePrimers[0] ? reversePrimers[0].value : undefined}
138
+ defaultValue={
139
+ reversePrimer ??
140
+ (reversePrimers[0] ? reversePrimers[0].value : undefined)
141
+ }
136
142
  name="reversePrimer"
137
143
  label="Reverse Primer:"
138
144
  />
@@ -190,7 +190,7 @@ class AddOrEditAnnotationDialog extends React.Component {
190
190
  sequenceData = { sequence: "" },
191
191
  handleSubmit,
192
192
  beforeAnnotationCreate,
193
- renderTypes,
193
+ RenderTypes,
194
194
  renderTags,
195
195
  RenderBases,
196
196
  allowMultipleFeatureDirections,
@@ -210,6 +210,7 @@ class AddOrEditAnnotationDialog extends React.Component {
210
210
  overlapsSelf,
211
211
  start,
212
212
  end,
213
+ type,
213
214
  readOnly,
214
215
  getAdditionalEditAnnotationComps,
215
216
  advancedOptions,
@@ -370,7 +371,9 @@ class AddOrEditAnnotationDialog extends React.Component {
370
371
  />
371
372
  ))}
372
373
 
373
- {renderTypes || null}
374
+ {RenderTypes && (
375
+ <RenderTypes readOnly={this.props.readOnly} type={type}></RenderTypes>
376
+ )}
374
377
  {renderTags || null}
375
378
 
376
379
  {/* {allowPrimerBasesToBeEdited && RenderBases ? null : !renderLocations || */}
@@ -387,7 +390,7 @@ class AddOrEditAnnotationDialog extends React.Component {
387
390
  format={this.formatStart}
388
391
  parse={this.parseStart}
389
392
  tooltipError
390
- defaultValue={1}
393
+ defaultValue={start ?? 1}
391
394
  min={1}
392
395
  max={sequenceLength || 1}
393
396
  name="start"
@@ -401,7 +404,7 @@ class AddOrEditAnnotationDialog extends React.Component {
401
404
  parse={this.parseEnd}
402
405
  inlineLabel
403
406
  tooltipError
404
- defaultValue={sequenceData.isProtein ? 3 : 1}
407
+ defaultValue={end ?? (sequenceData.isProtein ? 3 : 1)}
405
408
  min={1}
406
409
  max={sequenceLength || 1}
407
410
  name="end"
@@ -569,6 +572,7 @@ export default ({ formName, getProps, dialogProps }) => {
569
572
  tgFormValues(
570
573
  "start",
571
574
  "end",
575
+ "type",
572
576
  "overlapsSelf",
573
577
  "locations",
574
578
  "bases",