@teselagen/ove 0.5.31-beta.3 → 0.5.32-beta.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.5.31-beta.3",
3
+ "version": "0.5.32-beta.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.5.23-beta.12",
15
+ "@teselagen/ui": "0.5.24-beta.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",
@@ -57,6 +57,8 @@
57
57
  "lodash": "4.17.21",
58
58
  "lodash-es": "^4.17.21",
59
59
  "math-expression-evaluator": "^1.3.7",
60
+ "mobx": "^6.10.2",
61
+ "mobx-react": "^9.0.1",
60
62
  "mock-fs": "5.2.0",
61
63
  "nanoid": "^4.0.0",
62
64
  "node-interval-tree": "^1.3.3",
@@ -66,16 +68,16 @@
66
68
  "popper.js": "^1.16.1",
67
69
  "prop-types": "^15.6.2",
68
70
  "qs": "^6.9.6",
69
- "react": "^18.3.1",
71
+ "react": "^18.2.0",
70
72
  "react-color": "^2.19.3",
71
- "react-dom": "^18.3.1",
73
+ "react-dom": "^18.2.0",
72
74
  "react-draggable": "4.4.5",
73
75
  "react-dropzone": "^11.4.2",
74
- "react-markdown": "8.0.7",
76
+ "react-markdown": "9.0.1",
75
77
  "react-measure": "^2.5.2",
76
78
  "react-redux": "^8.0.5",
77
79
  "react-rnd": "^10.2.4",
78
- "react-router-dom": "4",
80
+ "react-router-dom": "^4.3.1",
79
81
  "react-sizeme": "^2.6.12",
80
82
  "react-transition-group": "^2.4.0",
81
83
  "recompose": "npm:react-recompose@0.31.1",
@@ -152,46 +152,48 @@ export function CutsiteFilter(props) {
152
152
  filteredRestrictionEnzymes.length > 1;
153
153
  const [queryTracker, setQueryTracker] = useState("");
154
154
 
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();
155
+ const renderOptions = ({ label, value, canBeHidden }, props) => {
156
+ const { filteredRestrictionEnzymes, filteredRestrictionEnzymesUpdate } =
157
+ props;
171
158
 
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
- );
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
+ };
195
197
  const filteredRestrictionEnzymesUpdate = enzymes => {
196
198
  _filteredRestrictionEnzymesUpdate(enzymes);
197
199
  if (sequenceData?.id) {
@@ -283,7 +285,7 @@ export function CutsiteFilter(props) {
283
285
  data-tip="Manage Enzymes"
284
286
  onClick={onManageClick}
285
287
  icon="cog"
286
- />
288
+ ></Button>
287
289
  )}
288
290
  <div
289
291
  style={{
@@ -343,17 +345,19 @@ export function CutsiteFilter(props) {
343
345
  wrapperStyle={{ zIndex: 11 }}
344
346
  noResultsText={
345
347
  <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
- />
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>
357
361
  }
358
362
  onInputChange={queryTracker => {
359
363
  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, { useState } from "react";
5
+ import React 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,154 +18,153 @@ import {
18
18
  } from "@blueprintjs/core";
19
19
  import withEditorInteractions from "../withEditorInteractions";
20
20
  import { userDefinedHandlersAndOpts } from "../Editor/userDefinedHandlersAndOpts";
21
- import { pick } from "lodash-es";
21
+ import { noop, pick } from "lodash-es";
22
22
 
23
23
  const MAX_DIGEST_CUTSITES = 50;
24
24
  const MAX_PARTIAL_DIGEST_CUTSITES = 10;
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({});
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);
57
68
  }}
58
- style={{ marginLeft: 5 }}
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"
59
88
  >
60
- {" "}
61
- Save
62
- </Button>
89
+ Read the docs
90
+ </a>
63
91
  </div>
64
- )}
65
- <div style={{ display: "flex", justifyContent: "space-between" }}>
66
- <Checkbox
67
- onChange={() => {
68
- updateComputePartialDigest(!computePartialDigest);
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}
92
+ Choose your enzymes:
93
+ <CutsiteFilter
94
+ {...pick(this.props, userDefinedHandlersAndOpts)}
95
+ editorName={editorName}
84
96
  />
85
- <a
86
- target="_blank"
87
- rel="noopener noreferrer"
88
- href="http://docs.teselagen.com/en/articles/5489322-restriction-digest-in-the-vector-editor"
89
- >
90
- Read the docs
91
- </a>
92
- </div>
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"
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 });
105
114
  }}
106
115
  >
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
-
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 />
164
+ </div>
165
+ );
166
+ }
167
+ }
169
168
  const schema = {
170
169
  fields: [
171
170
  { width: 60, path: "start", displayName: "Start", type: "string" },
@@ -180,46 +179,45 @@ const schema = {
180
179
 
181
180
  export default compose(
182
181
  withEditorInteractions,
183
- withProps(
184
- ({
182
+ withProps(props => {
183
+ const {
185
184
  sequenceData,
186
185
  sequenceLength,
187
186
  selectionLayerUpdate,
188
187
  updateSelectedFragment,
189
188
  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;
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;
196
195
 
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
- )
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
+ })
225
223
  )(DigestTool);
@@ -757,6 +757,11 @@ export class Editor extends React.Component {
757
757
  <Icon
758
758
  icon="small-cross"
759
759
  onClick={() => {
760
+ if (
761
+ this.props.onPanelTabClose &&
762
+ !this.props.onPanelTabClose(id)
763
+ )
764
+ return;
760
765
  closePanel(id);
761
766
  }}
762
767
  style={{ paddingLeft: 5 }}
@@ -123,10 +123,7 @@ function PCRTool(props) {
123
123
  inlineLabel
124
124
  tooltipError
125
125
  options={forwardPrimers}
126
- defaultValue={
127
- forwardPrimer ??
128
- (forwardPrimers[0] ? forwardPrimers[0].value : undefined)
129
- }
126
+ defaultValue={forwardPrimers[0] ? forwardPrimers[0].value : undefined}
130
127
  name="forwardPrimer"
131
128
  label="Forward Primer:"
132
129
  />
@@ -135,10 +132,7 @@ function PCRTool(props) {
135
132
  inlineLabel
136
133
  tooltipError
137
134
  options={reversePrimers}
138
- defaultValue={
139
- reversePrimer ??
140
- (reversePrimers[0] ? reversePrimers[0].value : undefined)
141
- }
135
+ defaultValue={reversePrimers[0] ? reversePrimers[0].value : undefined}
142
136
  name="reversePrimer"
143
137
  label="Reverse Primer:"
144
138
  />
@@ -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,7 +210,6 @@ class AddOrEditAnnotationDialog extends React.Component {
210
210
  overlapsSelf,
211
211
  start,
212
212
  end,
213
- type,
214
213
  readOnly,
215
214
  getAdditionalEditAnnotationComps,
216
215
  advancedOptions,
@@ -371,9 +370,7 @@ class AddOrEditAnnotationDialog extends React.Component {
371
370
  />
372
371
  ))}
373
372
 
374
- {RenderTypes && (
375
- <RenderTypes readOnly={this.props.readOnly} type={type}></RenderTypes>
376
- )}
373
+ {renderTypes || null}
377
374
  {renderTags || null}
378
375
 
379
376
  {/* {allowPrimerBasesToBeEdited && RenderBases ? null : !renderLocations || */}
@@ -390,7 +387,7 @@ class AddOrEditAnnotationDialog extends React.Component {
390
387
  format={this.formatStart}
391
388
  parse={this.parseStart}
392
389
  tooltipError
393
- defaultValue={start ?? 1}
390
+ defaultValue={1}
394
391
  min={1}
395
392
  max={sequenceLength || 1}
396
393
  name="start"
@@ -404,7 +401,7 @@ class AddOrEditAnnotationDialog extends React.Component {
404
401
  parse={this.parseEnd}
405
402
  inlineLabel
406
403
  tooltipError
407
- defaultValue={end ?? (sequenceData.isProtein ? 3 : 1)}
404
+ defaultValue={sequenceData.isProtein ? 3 : 1}
408
405
  min={1}
409
406
  max={sequenceLength || 1}
410
407
  name="end"
@@ -572,7 +569,6 @@ export default ({ formName, getProps, dialogProps }) => {
572
569
  tgFormValues(
573
570
  "start",
574
571
  "end",
575
- "type",
576
572
  "overlapsSelf",
577
573
  "locations",
578
574
  "bases",