@teselagen/ove 0.7.6-beta.3 → 0.7.6

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.6-beta.3",
3
+ "version": "0.7.6",
4
4
  "main": "./src/index.js",
5
5
  "exports": {
6
6
  ".": {
@@ -12,10 +12,12 @@
12
12
  "dependencies": {
13
13
  "@teselagen/sequence-utils": "0.3.27",
14
14
  "@teselagen/range-utils": "0.3.7",
15
- "@teselagen/ui": "0.7.10-beta.3",
15
+ "@teselagen/ui": "0.7.10",
16
16
  "@teselagen/file-utils": "0.3.16",
17
17
  "@teselagen/bounce-loader": "0.3.11",
18
18
  "@teselagen/bio-parsers": "0.4.22",
19
+ "@blueprintjs/core": "3.54.0",
20
+ "@blueprintjs/datetime": "^3.24.1",
19
21
  "@blueprintjs/icons": "3.33.0",
20
22
  "@blueprintjs/select": "3.18.11",
21
23
  "@dnd-kit/core": "^6.1.0",
@@ -69,6 +71,7 @@
69
71
  "react-dom": "^18.3.1",
70
72
  "react-draggable": "4.4.5",
71
73
  "react-dropzone": "^11.4.2",
74
+ "react-markdown": "9.0.1",
72
75
  "react-measure": "^2.5.2",
73
76
  "react-redux": "^8.0.5",
74
77
  "react-rnd": "^10.2.4",
@@ -80,6 +83,7 @@
80
83
  "redux-act": "^1.8.0",
81
84
  "redux-form": "^8.3.10",
82
85
  "redux-thunk": "2.4.1",
86
+ "remark-gfm": "^4.0.0",
83
87
  "reselect": "^4.1.7",
84
88
  "shortid": "^2.2.16",
85
89
  "tg-use-local-storage-state": "^16.0.3",
@@ -4,7 +4,6 @@ import {
4
4
  Droppable,
5
5
  Draggable as DndDraggable
6
6
  } from "@hello-pangea/dnd";
7
- import ClipboardJS from "clipboard";
8
7
  import React from "react";
9
8
  import { connect } from "react-redux";
10
9
  import {
@@ -104,6 +103,7 @@ export class AlignmentView extends React.Component {
104
103
  this.handleAlignmentCopy
105
104
  );
106
105
  }
106
+
107
107
  getMaxLength = () => {
108
108
  const { alignmentTracks } = this.props;
109
109
  const { sequenceData = { sequence: "" }, alignmentData } =
@@ -175,6 +175,7 @@ export class AlignmentView extends React.Component {
175
175
  this.onShortcutCopy &&
176
176
  document.removeEventListener("keydown", this.handleAlignmentCopy);
177
177
  }
178
+
178
179
  handleAlignmentCopy = event => {
179
180
  if (
180
181
  event.key === "c" &&
@@ -196,6 +197,7 @@ export class AlignmentView extends React.Component {
196
197
  event.preventDefault();
197
198
  }
198
199
  };
200
+
199
201
  getAllAlignmentsFastaText = () => {
200
202
  const selectionLayer =
201
203
  this.props.store.getState().VectorEditor.__allEditorsOptions.alignments[
@@ -214,6 +216,7 @@ export class AlignmentView extends React.Component {
214
216
  });
215
217
  return seqDataOfAllTracksToCopy.join("");
216
218
  };
219
+
217
220
  state = {
218
221
  alignmentName: this.props.alignmentName,
219
222
  isTrackDragging: false,
@@ -222,6 +225,7 @@ export class AlignmentView extends React.Component {
222
225
  width: 0,
223
226
  nameDivWidth: 140
224
227
  };
228
+
225
229
  easyStore = store({
226
230
  selectionLayer: { start: -1, end: -1 },
227
231
  caretPosition: -1,
@@ -280,6 +284,7 @@ export class AlignmentView extends React.Component {
280
284
  }, 5000);
281
285
  }
282
286
  }
287
+
283
288
  componentDidMount() {
284
289
  const updateAlignmentSelection = newRangeOrCaret => {
285
290
  this.updateSelectionOrCaret(false, newRangeOrCaret, {
@@ -1194,6 +1199,44 @@ export class AlignmentView extends React.Component {
1194
1199
 
1195
1200
  const alignmentData = track.alignmentData;
1196
1201
  const { name } = alignmentData;
1202
+
1203
+ const copySpecificAlignmentFasta = async () => {
1204
+ const { selectionLayer } =
1205
+ this.props.store.getState().VectorEditor
1206
+ .__allEditorsOptions.alignments[
1207
+ this.props.id
1208
+ ] || {};
1209
+ const seqDataToCopy = getSequenceDataBetweenRange(
1210
+ alignmentData,
1211
+ selectionLayer
1212
+ ).sequence;
1213
+ const seqDataToCopyAsFasta = `>${name}\r\n${seqDataToCopy}\r\n`;
1214
+ await navigator.clipboard.writeText(
1215
+ seqDataToCopyAsFasta
1216
+ );
1217
+ };
1218
+
1219
+ const copySpecificAlignment = async () => {
1220
+ const { selectionLayer } =
1221
+ this.props.store.getState().VectorEditor
1222
+ .__allEditorsOptions.alignments[
1223
+ this.props.id
1224
+ ] || {};
1225
+ const seqDataToCopy = getSequenceDataBetweenRange(
1226
+ alignmentData,
1227
+ selectionLayer
1228
+ ).sequence;
1229
+ await navigator.clipboard.writeText(
1230
+ seqDataToCopy
1231
+ );
1232
+ };
1233
+
1234
+ const getAllAlignmentsFastaText = async () => {
1235
+ await navigator.clipboard.writeText(
1236
+ this.getAllAlignmentsFastaText()
1237
+ );
1238
+ };
1239
+
1197
1240
  showContextMenu(
1198
1241
  [
1199
1242
  ...(additionalSelectionLayerRightClickedOptions
@@ -1207,24 +1250,8 @@ export class AlignmentView extends React.Component {
1207
1250
  className:
1208
1251
  "copyAllAlignmentsFastaClipboardHelper",
1209
1252
  hotkey: "cmd+c",
1210
- willUnmount: () => {
1211
- this
1212
- .copyAllAlignmentsFastaClipboardHelper &&
1213
- this.copyAllAlignmentsFastaClipboardHelper.destroy();
1214
- },
1215
- didMount: () => {
1216
- this.copyAllAlignmentsFastaClipboardHelper =
1217
- new ClipboardJS(
1218
- `.copyAllAlignmentsFastaClipboardHelper`,
1219
- {
1220
- action: "copyAllAlignmentsFasta",
1221
- text: () => {
1222
- return this.getAllAlignmentsFastaText();
1223
- }
1224
- }
1225
- );
1226
- },
1227
1253
  onClick: () => {
1254
+ getAllAlignmentsFastaText();
1228
1255
  window.toastr.success("Selection Copied");
1229
1256
  }
1230
1257
  },
@@ -1232,36 +1259,8 @@ export class AlignmentView extends React.Component {
1232
1259
  text: `Copy Selection of ${name} as Fasta`,
1233
1260
  className:
1234
1261
  "copySpecificAlignmentFastaClipboardHelper",
1235
- willUnmount: () => {
1236
- this
1237
- .copySpecificAlignmentFastaClipboardHelper &&
1238
- this.copySpecificAlignmentFastaClipboardHelper.destroy();
1239
- },
1240
- didMount: () => {
1241
- this.copySpecificAlignmentFastaClipboardHelper =
1242
- new ClipboardJS(
1243
- `.copySpecificAlignmentFastaClipboardHelper`,
1244
- {
1245
- action: "copySpecificAlignmentFasta",
1246
- text: () => {
1247
- const { selectionLayer } =
1248
- this.props.store.getState()
1249
- .VectorEditor
1250
- .__allEditorsOptions.alignments[
1251
- this.props.id
1252
- ] || {};
1253
- const seqDataToCopy =
1254
- getSequenceDataBetweenRange(
1255
- alignmentData,
1256
- selectionLayer
1257
- ).sequence;
1258
- const seqDataToCopyAsFasta = `>${name}\r\n${seqDataToCopy}\r\n`;
1259
- return seqDataToCopyAsFasta;
1260
- }
1261
- }
1262
- );
1263
- },
1264
1262
  onClick: () => {
1263
+ copySpecificAlignmentFasta();
1265
1264
  window.toastr.success(
1266
1265
  "Selection Copied As Fasta"
1267
1266
  );
@@ -1271,35 +1270,8 @@ export class AlignmentView extends React.Component {
1271
1270
  text: `Copy Selection of ${name}`,
1272
1271
  className:
1273
1272
  "copySpecificAlignmentAsPlainClipboardHelper",
1274
- willUnmount: () => {
1275
- this
1276
- .copySpecificAlignmentAsPlainClipboardHelper &&
1277
- this.copySpecificAlignmentAsPlainClipboardHelper.destroy();
1278
- },
1279
- didMount: () => {
1280
- this.copySpecificAlignmentAsPlainClipboardHelper =
1281
- new ClipboardJS(
1282
- `.copySpecificAlignmentAsPlainClipboardHelper`,
1283
- {
1284
- action: "copySpecificAlignmentFasta",
1285
- text: () => {
1286
- const { selectionLayer } =
1287
- this.props.store.getState()
1288
- .VectorEditor
1289
- .__allEditorsOptions.alignments[
1290
- this.props.id
1291
- ] || {};
1292
- const seqDataToCopy =
1293
- getSequenceDataBetweenRange(
1294
- alignmentData,
1295
- selectionLayer
1296
- ).sequence;
1297
- return seqDataToCopy;
1298
- }
1299
- }
1300
- );
1301
- },
1302
1273
  onClick: () => {
1274
+ copySpecificAlignment();
1303
1275
  window.toastr.success("Selection Copied");
1304
1276
  }
1305
1277
  }
@@ -1313,7 +1285,7 @@ export class AlignmentView extends React.Component {
1313
1285
  sequenceLength={sequenceLength}
1314
1286
  charWidth={this.getCharWidthInLinearView()}
1315
1287
  row={{ start: 0, end: sequenceLength - 1 }}
1316
- ></PerformantSelectionLayer>
1288
+ />
1317
1289
  <PerformantCaret
1318
1290
  leftMargin={this.state.nameDivWidth}
1319
1291
  className="veAlignmentSelectionLayer"
@@ -1679,7 +1651,7 @@ export class AlignmentView extends React.Component {
1679
1651
  sequenceLength={sequenceLength}
1680
1652
  charWidth={this.getMinCharWidth(true)}
1681
1653
  row={{ start: 0, end: sequenceLength - 1 }}
1682
- ></PerformantSelectionLayer>
1654
+ />
1683
1655
  <PerformantCaret
1684
1656
  style={{
1685
1657
  opacity: 0.2
@@ -4,7 +4,6 @@ import {
4
4
  getAminoAcidStringFromSequenceString
5
5
  } from "@teselagen/sequence-utils";
6
6
  import { getSequenceWithinRange } from "@teselagen/range-utils";
7
- import ClipboardJS from "clipboard";
8
7
  import { compose } from "redux";
9
8
  import {
10
9
  getReverseComplementSequenceAndAnnotations,
@@ -93,6 +92,7 @@ function VectorInteractionHOC(Component /* options */) {
93
92
  return <ConnectedMenu {...props} {...p} />;
94
93
  };
95
94
  }
95
+
96
96
  componentWillUnmount() {
97
97
  this.combokeys && this.combokeys.detach();
98
98
  }
@@ -183,6 +183,7 @@ function VectorInteractionHOC(Component /* options */) {
183
183
  omitIcons: true
184
184
  });
185
185
  }
186
+
186
187
  updateSelectionOrCaret = (shiftHeld, newRangeOrCaret) => {
187
188
  const {
188
189
  selectionLayer,
@@ -257,7 +258,8 @@ function VectorInteractionHOC(Component /* options */) {
257
258
  e.preventDefault();
258
259
  };
259
260
 
260
- handleCutOrCopy = isCut => e => {
261
+ handleCutOrCopy = isCut => async e => {
262
+ e.preventDefault();
261
263
  const {
262
264
  onCopy = noop,
263
265
  sequenceData,
@@ -303,7 +305,6 @@ function VectorInteractionHOC(Component /* options */) {
303
305
  }`
304
306
  );
305
307
 
306
- const clipboardData = e.clipboardData;
307
308
  const textToCopy =
308
309
  (this.sequenceDataToCopy || {}).textToCopy !== undefined
309
310
  ? this.sequenceDataToCopy.textToCopy
@@ -312,9 +313,16 @@ function VectorInteractionHOC(Component /* options */) {
312
313
  : seqData.sequence;
313
314
 
314
315
  seqData.textToCopy = textToCopy;
315
- clipboardData.setData("text/plain", textToCopy);
316
- clipboardData.setData("application/json", JSON.stringify(seqData));
317
- e.preventDefault();
316
+ await navigator.clipboard.writeText(textToCopy);
317
+ // application/json is not supported by clipboard api on browser
318
+ // await navigator.clipboard.write([
319
+ // new ClipboardItem({
320
+ // "application/json": new Blob([JSON.stringify(seqData)], {
321
+ // type: "application/json"
322
+ // }),
323
+ // "text/plain": new Blob([textToCopy], { type: "text/plain" })
324
+ // })
325
+ // ]);
318
326
 
319
327
  if (isCut && !(readOnly || disableBpEditing) && !disableBpEditing) {
320
328
  this.handleDnaDelete(false);
@@ -326,10 +334,8 @@ function VectorInteractionHOC(Component /* options */) {
326
334
  }),
327
335
  this.props
328
336
  );
329
- document.body.removeEventListener("cut", this.handleCut);
330
337
  } else {
331
338
  onCopy(e, seqData, this.props);
332
- document.body.removeEventListener("copy", this.handleCopy);
333
339
  }
334
340
  window.toastr.success(
335
341
  `Selection ${
@@ -340,6 +346,7 @@ function VectorInteractionHOC(Component /* options */) {
340
346
  );
341
347
  this.sequenceDataToCopy = undefined;
342
348
  };
349
+
343
350
  handleCut = this.handleCutOrCopy(true);
344
351
 
345
352
  handleCopy = this.handleCutOrCopy();
@@ -358,6 +365,7 @@ function VectorInteractionHOC(Component /* options */) {
358
365
  }
359
366
  };
360
367
  };
368
+
361
369
  createDisableBpEditingMsg = () => {
362
370
  window.toastr.warning(
363
371
  typeof this.props.disableBpEditing === "string"
@@ -366,6 +374,7 @@ function VectorInteractionHOC(Component /* options */) {
366
374
  this.getDuplicateAction()
367
375
  );
368
376
  };
377
+
369
378
  createReadOnlyMsg = () => {
370
379
  window.toastr.warning(
371
380
  this.props.readOnly === "string"
@@ -486,6 +495,7 @@ function VectorInteractionHOC(Component /* options */) {
486
495
  //we only call caretPositionUpdate if we're actually changing something
487
496
  this.props.caretPositionUpdate(position);
488
497
  };
498
+
489
499
  selectionLayerUpdate = newSelection => {
490
500
  const { selectionLayer = { start: -1, end: -1 }, ignoreGapsOnHighlight } =
491
501
  this.props;
@@ -582,6 +592,7 @@ function VectorInteractionHOC(Component /* options */) {
582
592
  annotationDeselectAll(undefined);
583
593
  annotationSelect(annotation);
584
594
  };
595
+
585
596
  insertHelper = {
586
597
  onClick: (e, ctxInfo) => {
587
598
  this.handleDnaInsert({
@@ -593,75 +604,55 @@ function VectorInteractionHOC(Component /* options */) {
593
604
  }
594
605
  };
595
606
 
596
- // eslint-disable-next-line no-unused-vars
597
607
  getCopyOptions = annotation => {
598
608
  const { sequenceData, readOnly, disableBpEditing, selectionLayer } =
599
609
  this.props;
600
610
  const { isProtein } = sequenceData;
601
- const makeTextCopyable = (transformFunc, className, action = "copy") => {
602
- return new ClipboardJS(`.${className}`, {
603
- action: () => action,
604
- text: () => {
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 } =
612
- store.getState().VectorEditor[editorName];
611
+ const makeTextCopyable = transformFunc => {
612
+ return async () => {
613
+ const { editorName, store } = this.props;
614
+ const { sequenceData, copyOptions, selectionLayer } =
615
+ store.getState().VectorEditor[editorName];
613
616
 
614
- const selectedSeqData = getSequenceDataBetweenRange(
615
- sequenceData,
616
- getSelFromWrappedAddon(
617
- selectionLayer,
618
- sequenceData.sequence.length
619
- ),
620
- {
621
- excludePartial: {
622
- features: !copyOptions.partialFeatures,
623
- parts: !copyOptions.partialParts
624
- },
625
- exclude: {
626
- features: !copyOptions.features,
627
- parts: !copyOptions.parts
628
- }
617
+ const selectedSeqData = getSequenceDataBetweenRange(
618
+ sequenceData,
619
+ getSelFromWrappedAddon(
620
+ selectionLayer,
621
+ sequenceData.sequence.length
622
+ ),
623
+ {
624
+ excludePartial: {
625
+ features: !copyOptions.partialFeatures,
626
+ parts: !copyOptions.partialParts
627
+ },
628
+ exclude: {
629
+ features: !copyOptions.features,
630
+ parts: !copyOptions.parts
629
631
  }
630
- );
631
- const sequenceDataToCopy = transformFunc(
632
- selectedSeqData,
633
- sequenceData
634
- );
635
- this.sequenceDataToCopy = sequenceDataToCopy;
636
-
637
- if (window.Cypress) {
638
- window.Cypress.textToCopy = sequenceDataToCopy.textToCopy;
639
- window.Cypress.seqDataToCopy = sequenceDataToCopy;
640
632
  }
641
- return sequenceDataToCopy.textToCopy || sequenceDataToCopy.sequence;
633
+ );
634
+ const sequenceDataToCopy = transformFunc(
635
+ selectedSeqData,
636
+ sequenceData
637
+ );
638
+ this.sequenceDataToCopy = sequenceDataToCopy;
639
+
640
+ if (window.Cypress) {
641
+ window.Cypress.textToCopy = sequenceDataToCopy.textToCopy;
642
+ window.Cypress.seqDataToCopy = sequenceDataToCopy;
642
643
  }
643
- });
644
+ };
644
645
  };
645
646
  const aaCopy = {
646
647
  text: "Copy AA Sequence",
647
648
  className: "openVeCopyAA",
648
- willUnmount: () => {
649
- this.openVeCopyAA && this.openVeCopyAA.destroy();
650
- },
651
- didMount: ({ className }) => {
652
- this.openVeCopyAA = makeTextCopyable(selectedSeqData => {
653
- const textToCopy = isProtein
654
- ? selectedSeqData.proteinSequence.toUpperCase()
655
- : getAminoAcidStringFromSequenceString(selectedSeqData.sequence);
656
- return {
657
- ...selectedSeqData,
658
- textToCopy
659
- };
660
- }, className);
661
- }
649
+ onClick: makeTextCopyable(selectedSeqData => ({
650
+ ...selectedSeqData,
651
+ textToCopy: isProtein
652
+ ? selectedSeqData.proteinSequence.toUpperCase()
653
+ : getAminoAcidStringFromSequenceString(selectedSeqData.sequence)
654
+ }))
662
655
  };
663
- // TODO: maybe stop using Clipboard.js and unify clipboard handling with
664
- // a more versatile approach
665
656
  return [
666
657
  ...(readOnly || disableBpEditing
667
658
  ? []
@@ -673,20 +664,7 @@ function VectorInteractionHOC(Component /* options */) {
673
664
  {
674
665
  text: "Cut",
675
666
  className: "openVeCut",
676
- willUnmount: () => {
677
- this.openVeCut && this.openVeCut.destroy();
678
- },
679
- didMount: ({ className }) => {
680
- // TODO: Maybe use a cut action instead
681
- this.openVeCut = makeTextCopyable(
682
- s => ({
683
- ...s,
684
- textToCopy: isProtein ? s.proteinSequence : s.sequence
685
- }),
686
- className,
687
- "cut"
688
- );
689
- }
667
+ onClick: this.handleCut
690
668
  }
691
669
  ]),
692
670
  {
@@ -699,80 +677,45 @@ function VectorInteractionHOC(Component /* options */) {
699
677
  {
700
678
  text: isProtein ? "Copy DNA Bps" : "Copy",
701
679
  className: "openVeCopy2",
702
- willUnmount: () => {
703
- this.openVeCopy2 && this.openVeCopy2.destroy();
704
- },
705
- didMount: ({ className }) => {
706
- this.openVeCopy2 = makeTextCopyable(
707
- s => ({ ...s, textToCopy: s.sequence }),
708
- className
709
- );
710
- }
680
+ onClick: makeTextCopyable(s => ({
681
+ ...s,
682
+ textToCopy: s.sequence
683
+ }))
711
684
  },
712
685
 
713
686
  {
714
687
  text: "Copy Genbank For Selection",
715
688
  className: "openVeCopyGenbankForSelection",
716
- willUnmount: () => {
717
- this.openVeCopyGenbankForSelection &&
718
- this.openVeCopyGenbankForSelection.destroy();
719
- },
720
- didMount: ({ className }) => {
721
- this.openVeCopyGenbankForSelection = makeTextCopyable(
722
- getGenbankFromSelection,
723
- className
724
- );
725
- }
689
+ onClick: makeTextCopyable(getGenbankFromSelection)
726
690
  },
727
691
  {
728
692
  text: isProtein
729
693
  ? "Copy Reverse Complement DNA Bps"
730
694
  : "Copy Reverse Complement",
731
695
  className: "openVeCopyReverse",
732
- willUnmount: () => {
733
- this.openVeCopyReverse && this.openVeCopyReverse.destroy();
734
- },
735
- didMount: ({ className }) => {
736
- this.openVeCopyReverse = makeTextCopyable(
737
- getReverseComplementSequenceAndAnnotations,
738
- className
739
- );
740
- }
696
+ onClick: makeTextCopyable(
697
+ getReverseComplementSequenceAndAnnotations
698
+ )
741
699
  },
742
700
  ...(isProtein ? [] : [aaCopy]),
743
701
  {
744
702
  text: "Copy Reverse Complement AA Sequence",
745
703
  className: "openVeCopyAAReverse",
746
- willUnmount: () => {
747
- this.openVeCopyAAReverse && this.openVeCopyAAReverse.destroy();
748
- },
749
- didMount: ({ className }) => {
750
- this.openVeCopyAAReverse = makeTextCopyable(selectedSeqData => {
751
- const revSeqData =
752
- getReverseComplementSequenceAndAnnotations(selectedSeqData);
753
- const textToCopy = isProtein
704
+ onClick: makeTextCopyable(selectedSeqData => {
705
+ const revSeqData =
706
+ getReverseComplementSequenceAndAnnotations(selectedSeqData);
707
+ return {
708
+ ...revSeqData,
709
+ textToCopy: isProtein
754
710
  ? revSeqData.proteinSequence.toUpperCase()
755
- : getAminoAcidStringFromSequenceString(revSeqData.sequence);
756
- return {
757
- ...revSeqData,
758
- textToCopy
759
- };
760
- }, className);
761
- }
711
+ : getAminoAcidStringFromSequenceString(revSeqData.sequence)
712
+ };
713
+ })
762
714
  },
763
715
  {
764
716
  text: isProtein ? "Copy Complement DNA Bps" : "Copy Complement",
765
717
  className: "openVeCopyComplement",
766
- willUnmount: () => {
767
- this.openVeCopyComplement &&
768
- this.openVeCopyComplement.destroy();
769
- },
770
- didMount: ({ className }) => {
771
- this.openVeCopyComplement = makeTextCopyable(
772
- getComplementSequenceAndAnnotations,
773
- className
774
- );
775
- }
718
+ onClick: makeTextCopyable(getComplementSequenceAndAnnotations)
776
719
  },
777
720
  copyOptionsMenu
778
721
  ]