@teselagen/ove 0.0.14 → 0.0.16

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 (362) hide show
  1. package/index.umd.js +164825 -135871
  2. package/package.json +78 -9
  3. package/src/AlignmentTool/index.js +16 -0
  4. package/src/AlignmentView/AlignmentVisibilityTool.js +105 -0
  5. package/src/AlignmentView/EditTrackNameDialog.js +34 -0
  6. package/src/AlignmentView/HorizontalPanelDragHandle.js +35 -0
  7. package/src/AlignmentView/Minimap.js +520 -0
  8. package/src/AlignmentView/Mismatches.js +134 -0
  9. package/src/AlignmentView/PairwiseAlignmentView.js +68 -0
  10. package/src/AlignmentView/PerformantSelectionLayer.js +32 -0
  11. package/src/AlignmentView/coerceInitialValue.js +7 -0
  12. package/src/AlignmentView/getGapMap.js +12 -0
  13. package/src/AlignmentView/getGaps.js +27 -0
  14. package/src/AlignmentView/getPairwiseOverviewLinearViewOptions.js +38 -0
  15. package/src/AlignmentView/getTrackFromEvent.js +25 -0
  16. package/src/AlignmentView/index.js +2058 -0
  17. package/src/AlignmentView/isTargetWithinEl.js +6 -0
  18. package/src/AlignmentView/style.css +100 -0
  19. package/src/AlignmentView/updateTrackHelper.js +58 -0
  20. package/src/AutoAnnotate.js +500 -0
  21. package/src/AutoAnnotateBpMatchingDialog.js +208 -0
  22. package/src/CircularView/Axis.js +40 -0
  23. package/src/CircularView/AxisNumbers.js +35 -0
  24. package/src/CircularView/Caret.js +63 -0
  25. package/src/CircularView/CircularDnaSequence.js +73 -0
  26. package/src/CircularView/CircularZoomMinimap.js +16 -0
  27. package/src/CircularView/Cutsite.js +18 -0
  28. package/src/CircularView/Cutsites.js +113 -0
  29. package/src/CircularView/DeletionLayer.js +28 -0
  30. package/src/CircularView/Feature.js +83 -0
  31. package/src/CircularView/Labels/index.js +536 -0
  32. package/src/CircularView/Labels/relaxLabelAngles.js +157 -0
  33. package/src/CircularView/Labels/relaxLabels_DEPRECATED.js +105 -0
  34. package/src/CircularView/Labels/style.css +55 -0
  35. package/src/CircularView/Orf.js +25 -0
  36. package/src/CircularView/Part.js +34 -0
  37. package/src/CircularView/PositionAnnotationOnCircle.js +26 -0
  38. package/src/CircularView/Primer.js +41 -0
  39. package/src/CircularView/RotateCircularViewSlider.js +82 -0
  40. package/src/CircularView/SelectionLayer.js +132 -0
  41. package/src/CircularView/VeTopRightContainer.js +12 -0
  42. package/src/CircularView/ZoomCircularViewSlider.js +62 -0
  43. package/src/CircularView/drawAnnotations.js +433 -0
  44. package/src/CircularView/drawDirectedPiePiece.js +142 -0
  45. package/src/CircularView/getAngleForPositionMidpoint.js +3 -0
  46. package/src/CircularView/getInternalLabel.js +40 -0
  47. package/src/CircularView/getRangeAnglesSpecial.js +12 -0
  48. package/src/CircularView/getYOffset.js +15 -0
  49. package/src/CircularView/index.d.ts +20 -0
  50. package/src/CircularView/index.js +930 -0
  51. package/src/CircularView/normalizeAngle.js +3 -0
  52. package/src/CircularView/normalizeAngleRange.js +9 -0
  53. package/src/CircularView/positionCutsites.js +6 -0
  54. package/src/CircularView/shouldFlipText.js +4 -0
  55. package/src/CircularView/style.css +47 -0
  56. package/src/CircularView/utils/polarToSpecialCartesian.js +7 -0
  57. package/src/CreateAnnotationsPage.js +96 -0
  58. package/src/CreateCustomEnzyme/index.js +337 -0
  59. package/src/CreateCustomEnzyme/style.css +100 -0
  60. package/src/CutsiteFilter/AdditionalCutsiteInfoDialog.js +599 -0
  61. package/src/CutsiteFilter/index.js +408 -0
  62. package/src/CutsiteFilter/style.css +23 -0
  63. package/src/CutsiteFilter/withRestrictionEnzymes.js +15 -0
  64. package/src/DigestTool/AddLaddersDialog.js +82 -0
  65. package/src/DigestTool/DigestTool.js +223 -0
  66. package/src/DigestTool/Ladder.css +20 -0
  67. package/src/DigestTool/Ladder.js +303 -0
  68. package/src/DigestTool/createFragmentLines.js +120 -0
  69. package/src/DigestTool/ladderDefaults.js +26 -0
  70. package/src/DigestTool/ruler.css +89 -0
  71. package/src/Editor/CommandHotkeyHandler.js +44 -0
  72. package/src/Editor/DropHandler.css +21 -0
  73. package/src/Editor/DropHandler.js +64 -0
  74. package/src/Editor/FillWindow.js +46 -0
  75. package/src/Editor/darkmode.css +98 -0
  76. package/src/Editor/index.js +1005 -0
  77. package/src/Editor/style.css +235 -0
  78. package/src/Editor/userDefinedHandlersAndOpts.js +56 -0
  79. package/src/EnzymeViewer/index.js +81 -0
  80. package/src/EnzymeViewer/style.css +6 -0
  81. package/src/FindBar/index.js +411 -0
  82. package/src/FindBar/style.css +46 -0
  83. package/src/GlobalDialog.js +66 -0
  84. package/src/GlobalDialogUtils.js +85 -0
  85. package/src/LinearView/SequenceName.js +15 -0
  86. package/src/LinearView/ZoomLinearView.js +47 -0
  87. package/src/LinearView/index.js +374 -0
  88. package/src/LinearView/style.css +12 -0
  89. package/src/ManageEnzymes/index.js +326 -0
  90. package/src/ManageEnzymes/style.css +100 -0
  91. package/src/MenuBar/defaultConfig.js +149 -0
  92. package/src/MenuBar/index.js +98 -0
  93. package/src/MenuBar/viewSubmenu.js +479 -0
  94. package/src/PCRTool/PCRTool.js +173 -0
  95. package/src/Reflex/Browser.js +107 -0
  96. package/src/Reflex/ReflexContainer.js +802 -0
  97. package/src/Reflex/ReflexElement.js +160 -0
  98. package/src/Reflex/ReflexEvents.js +77 -0
  99. package/src/Reflex/ReflexSplitter.js +205 -0
  100. package/src/Reflex/index.js +5 -0
  101. package/src/Reflex/reflex-styles.css +128 -0
  102. package/src/Reflex/reflex-styles.css.map +9 -0
  103. package/src/RowItem/AnnotationContainerHolder.js +20 -0
  104. package/src/RowItem/AnnotationPositioner.js +27 -0
  105. package/src/RowItem/Axis.js +149 -0
  106. package/src/RowItem/Caret/index.js +64 -0
  107. package/src/RowItem/Caret/style.css +8 -0
  108. package/src/RowItem/Chromatograms/Chromatogram.js +289 -0
  109. package/src/RowItem/CutsiteSelectionLayers.js +47 -0
  110. package/src/RowItem/Cutsites.js +271 -0
  111. package/src/RowItem/DeletionLayers/index.js +113 -0
  112. package/src/RowItem/DeletionLayers/style.css +5 -0
  113. package/src/RowItem/Labels.js +327 -0
  114. package/src/RowItem/Orf.js +109 -0
  115. package/src/RowItem/Orfs.js +35 -0
  116. package/src/RowItem/ReplacementLayers/style.css +5 -0
  117. package/src/RowItem/SelectionLayer/index.js +184 -0
  118. package/src/RowItem/SelectionLayer/style.css +21 -0
  119. package/src/RowItem/Sequence.js +269 -0
  120. package/src/RowItem/StackedAnnotations/PointedAnnotation.js +347 -0
  121. package/src/RowItem/StackedAnnotations/getStructuredBases.js +97 -0
  122. package/src/RowItem/StackedAnnotations/index.js +182 -0
  123. package/src/RowItem/StackedAnnotations/primerBases.js +218 -0
  124. package/src/RowItem/StackedAnnotations/style.css +14 -0
  125. package/src/RowItem/Translations/AASliver.js +190 -0
  126. package/src/RowItem/Translations/Translation.js +162 -0
  127. package/src/RowItem/Translations/index.js +54 -0
  128. package/src/RowItem/Translations/style.css +3 -0
  129. package/src/RowItem/constants.js +3 -0
  130. package/src/RowItem/getCutsiteLabelHeights.js +56 -0
  131. package/src/RowItem/getXStartAndWidthFromNonCircularRange.js +12 -0
  132. package/src/RowItem/getXStartAndWidthOfRangeWrtRow.js +27 -0
  133. package/src/RowItem/getXStartAndWidthOfRowAnnotation.js +19 -0
  134. package/src/RowItem/index.js +647 -0
  135. package/src/RowItem/partOverhangs.js +6 -0
  136. package/src/RowItem/style.css +103 -0
  137. package/src/RowItem/utils.js +32 -0
  138. package/src/RowView/estimateRowHeight.js +184 -0
  139. package/src/RowView/index.d.ts +10 -0
  140. package/src/RowView/index.js +554 -0
  141. package/src/RowView/style.css +12 -0
  142. package/src/SimpleCircularOrLinearView.js +379 -0
  143. package/src/SimpleOligoPreview.js +39 -0
  144. package/src/StatusBar/MeltingTemp.js +81 -0
  145. package/src/StatusBar/index.js +275 -0
  146. package/src/StatusBar/style.css +38 -0
  147. package/src/ToolBar/ToolbarItem.js +194 -0
  148. package/src/ToolBar/alignmentTool.js +503 -0
  149. package/src/ToolBar/array_move.js +10 -0
  150. package/src/ToolBar/cutsiteTool.js +88 -0
  151. package/src/ToolBar/downloadTool.js +38 -0
  152. package/src/ToolBar/editTool.js +26 -0
  153. package/src/ToolBar/featureTool.js +34 -0
  154. package/src/ToolBar/findTool.js +2 -0
  155. package/src/ToolBar/importTool.js +27 -0
  156. package/src/ToolBar/index.js +231 -0
  157. package/src/ToolBar/inlineFindTool.js +38 -0
  158. package/src/ToolBar/oligoTool.js +30 -0
  159. package/src/ToolBar/orfTool.js +141 -0
  160. package/src/ToolBar/partTool.js +47 -0
  161. package/src/ToolBar/printTool.js +31 -0
  162. package/src/ToolBar/redoTool.js +30 -0
  163. package/src/ToolBar/saveTool.js +48 -0
  164. package/src/ToolBar/style.css +138 -0
  165. package/src/ToolBar/undoTool.js +30 -0
  166. package/src/ToolBar/veToolbarIcons/find.png +0 -0
  167. package/src/ToolBar/veToolbarIcons/fullscreen.png +0 -0
  168. package/src/ToolBar/veToolbarIcons/linear.png +0 -0
  169. package/src/ToolBar/veToolbarIcons/pie.png +0 -0
  170. package/src/ToolBar/veToolbarIcons/print.png +0 -0
  171. package/src/ToolBar/veToolbarIcons/save.png +0 -0
  172. package/src/ToolBar/veToolbarIcons/show_cut_sites.png +0 -0
  173. package/src/ToolBar/veToolbarIcons/show_features.png +0 -0
  174. package/src/ToolBar/veToolbarIcons/show_orfs.png +0 -0
  175. package/src/ToolBar/veToolbarIcons/show_primers.png +0 -0
  176. package/src/ToolBar/veToolbarIcons/toggle_views.svg +1 -0
  177. package/src/ToolBar/versionHistoryTool.js +20 -0
  178. package/src/ToolBar/visibilityTool.js +39 -0
  179. package/src/VersionHistoryView/index.js +215 -0
  180. package/src/addAlignment.js +6 -0
  181. package/src/commands/getOveHotkeyDefs.js +12 -0
  182. package/src/commands/index.js +1585 -0
  183. package/src/constants/constants.js +2 -0
  184. package/src/constants/dnaToColor.js +17 -0
  185. package/src/constants/draggableClassnames.js +5 -0
  186. package/src/constants/findToolConstants.js +1 -0
  187. package/src/constants/orfFrameToColorMap.js +10 -0
  188. package/src/constants/rowviewContants.js +3 -0
  189. package/src/constants/specialCutsiteFilterOptions.js +22 -0
  190. package/src/constants.js +1 -0
  191. package/src/createVectorEditor/index.js +138 -0
  192. package/src/createVectorEditor/makeStore.js +34 -0
  193. package/src/fileUtils.js +103 -0
  194. package/src/helperComponents/AddOrEditAnnotationDialog/index.js +711 -0
  195. package/src/helperComponents/AddOrEditAnnotationDialog/style.css +11 -0
  196. package/src/helperComponents/AddOrEditFeatureDialog/index.js +58 -0
  197. package/src/helperComponents/AddOrEditPartDialog/index.js +101 -0
  198. package/src/helperComponents/AddOrEditPrimerDialog/EditCaretPosition.js +234 -0
  199. package/src/helperComponents/AddOrEditPrimerDialog/index.js +329 -0
  200. package/src/helperComponents/AddOrEditPrimerDialog/style.css +41 -0
  201. package/src/helperComponents/EnzymesDialog/index.js +904 -0
  202. package/src/helperComponents/EnzymesDialog/style.css +21 -0
  203. package/src/helperComponents/GoToDialog.js +21 -0
  204. package/src/helperComponents/MergeFeaturesDialog/index.js +253 -0
  205. package/src/helperComponents/MergeFeaturesDialog/style.css +3 -0
  206. package/src/helperComponents/MultipleSeqsDetectedOnImportDialog.js +74 -0
  207. package/src/helperComponents/PinchHelper/PinchHelper.js +24 -0
  208. package/src/helperComponents/PrintDialog/index.js +396 -0
  209. package/src/helperComponents/PrintDialog/style.css +4 -0
  210. package/src/helperComponents/PropertiesDialog/ColorPicker.js +30 -0
  211. package/src/helperComponents/PropertiesDialog/CutsiteProperties.js +185 -0
  212. package/src/helperComponents/PropertiesDialog/FeatureProperties.js +6 -0
  213. package/src/helperComponents/PropertiesDialog/GenbankView.js +74 -0
  214. package/src/helperComponents/PropertiesDialog/GeneralProperties.js +140 -0
  215. package/src/helperComponents/PropertiesDialog/GenericAnnotationProperties.js +406 -0
  216. package/src/helperComponents/PropertiesDialog/OrfProperties.js +117 -0
  217. package/src/helperComponents/PropertiesDialog/PartProperties.js +9 -0
  218. package/src/helperComponents/PropertiesDialog/PrimerProperties.js +19 -0
  219. package/src/helperComponents/PropertiesDialog/SingleEnzymeCutsiteInfo.js +131 -0
  220. package/src/helperComponents/PropertiesDialog/TranslationProperties.js +149 -0
  221. package/src/helperComponents/PropertiesDialog/index.js +166 -0
  222. package/src/helperComponents/PropertiesDialog/style.css +68 -0
  223. package/src/helperComponents/PropertiesDialog/typeField.js +24 -0
  224. package/src/helperComponents/PropertiesDialog/utils.js +37 -0
  225. package/src/helperComponents/RemoveDuplicates/index.js +194 -0
  226. package/src/helperComponents/RenameSequenceDialog.js +7 -0
  227. package/src/helperComponents/SelectDialog.js +150 -0
  228. package/src/helperComponents/UncontrolledSliderWithPlusMinusBtns.css +5 -0
  229. package/src/helperComponents/UncontrolledSliderWithPlusMinusBtns.js +134 -0
  230. package/src/helperComponents/VeWarning/index.js +22 -0
  231. package/src/helperComponents/VeWarning/style.css +10 -0
  232. package/src/helperComponents/createSimpleDialog.js +89 -0
  233. package/src/helperComponents/partTagSearch.js +72 -0
  234. package/src/helperComponents/simpleDialog.css +13 -0
  235. package/src/helperComponents/withHover.js +90 -0
  236. package/src/index.js +60 -0
  237. package/src/redux/alignments.js +373 -0
  238. package/src/redux/annotationLabelVisibility.js +53 -0
  239. package/src/redux/annotationVisibility.js +196 -0
  240. package/src/redux/annotationsToSupport.js +104 -0
  241. package/src/redux/caretPosition.js +27 -0
  242. package/src/redux/charWidth.js +22 -0
  243. package/src/redux/copyOptions.js +34 -0
  244. package/src/redux/createYourOwnEnzyme.js +39 -0
  245. package/src/redux/deletionLayers.js +36 -0
  246. package/src/redux/digestTool.js +34 -0
  247. package/src/redux/featureLengthsToHide.js +27 -0
  248. package/src/redux/findTool.js +79 -0
  249. package/src/redux/frameTranslations.js +52 -0
  250. package/src/redux/hoveredAnnotation.js +24 -0
  251. package/src/redux/index.js +196 -0
  252. package/src/redux/labelLineIntensity.js +25 -0
  253. package/src/redux/labelSize.js +23 -0
  254. package/src/redux/lastSavedId.js +20 -0
  255. package/src/redux/middleware.js +112 -0
  256. package/src/redux/minimumOrfSize.js +24 -0
  257. package/src/redux/modalActions.js +3 -0
  258. package/src/redux/panelsShown.js +273 -0
  259. package/src/redux/partLengthsToHide.js +23 -0
  260. package/src/redux/primerLengthsToHide.js +27 -0
  261. package/src/redux/propertiesTool.js +40 -0
  262. package/src/redux/readOnly.js +28 -0
  263. package/src/redux/replacementLayers.js +36 -0
  264. package/src/redux/restrictionEnzymes.js +52 -0
  265. package/src/redux/selectedAnnotations.js +89 -0
  266. package/src/redux/selectedPartTags.js +21 -0
  267. package/src/redux/selectionLayer.js +46 -0
  268. package/src/redux/sequenceData/circular.js +19 -0
  269. package/src/redux/sequenceData/description.js +21 -0
  270. package/src/redux/sequenceData/features.js +19 -0
  271. package/src/redux/sequenceData/index.js +81 -0
  272. package/src/redux/sequenceData/lineageLines.js +11 -0
  273. package/src/redux/sequenceData/materiallyAvailable.js +19 -0
  274. package/src/redux/sequenceData/name.js +19 -0
  275. package/src/redux/sequenceData/parts.js +19 -0
  276. package/src/redux/sequenceData/primers.js +19 -0
  277. package/src/redux/sequenceData/sequence.js +12 -0
  278. package/src/redux/sequenceData/sharedActionCreators.js +0 -0
  279. package/src/redux/sequenceData/translations.js +20 -0
  280. package/src/redux/sequenceData/upsertDeleteActionGenerator.js +31 -0
  281. package/src/redux/sequenceDataHistory.js +43 -0
  282. package/src/redux/showGCContent.js +23 -0
  283. package/src/redux/toolBar.js +25 -0
  284. package/src/redux/uppercaseSequenceMapFont.js +25 -0
  285. package/src/redux/useAdditionalOrfStartCodons.js +24 -0
  286. package/src/redux/utils/addDashesForMatchStartAndEndForTracks/index.js +71 -0
  287. package/src/redux/utils/addMetaToActionCreators.js +12 -0
  288. package/src/redux/utils/createMergedDefaultStateReducer.js +30 -0
  289. package/src/redux/utils/createMetaAction.js +12 -0
  290. package/src/redux/versionHistory.js +27 -0
  291. package/src/selectors/annotationLabelVisibility.js +2 -0
  292. package/src/selectors/annotationSearchSelector.js +24 -0
  293. package/src/selectors/cdsFeaturesSelector.js +9 -0
  294. package/src/selectors/circularSelector.js +4 -0
  295. package/src/selectors/cutsiteLabelColorSelector.js +6 -0
  296. package/src/selectors/cutsitesByRangeSelector.js +5 -0
  297. package/src/selectors/cutsitesSelector.js +61 -0
  298. package/src/selectors/editorSelector.js +2 -0
  299. package/src/selectors/featuresSelector.js +8 -0
  300. package/src/selectors/filteredCutsitesSelector.js +137 -0
  301. package/src/selectors/filteredFeaturesSelector.js +32 -0
  302. package/src/selectors/filteredPartsSelector.js +57 -0
  303. package/src/selectors/filteredPrimersSelector.js +27 -0
  304. package/src/selectors/filteredRestrictionEnzymesSelector.js +1 -0
  305. package/src/selectors/getAdditionalEnzymesSelector.js +46 -0
  306. package/src/selectors/index.js +41 -0
  307. package/src/selectors/isEnzymeFilterAndSelector.js +1 -0
  308. package/src/selectors/minimumOrfSizeSelector.js +2 -0
  309. package/src/selectors/orfsSelector.js +15 -0
  310. package/src/selectors/partsSelector.js +8 -0
  311. package/src/selectors/primersSelector.js +8 -0
  312. package/src/selectors/restrictionEnzymesSelector.js +34 -0
  313. package/src/selectors/searchLayersSelector.js +71 -0
  314. package/src/selectors/selectedAnnotationsSelector.js +1 -0
  315. package/src/selectors/selectedCutsitesSelector.js +21 -0
  316. package/src/selectors/sequenceDataSelector.js +2 -0
  317. package/src/selectors/sequenceLengthSelector.js +5 -0
  318. package/src/selectors/sequenceSelector.js +4 -0
  319. package/src/selectors/tagsToBoldSelector.js +2 -0
  320. package/src/selectors/translationSearchMatchesSelector.js +14 -0
  321. package/src/selectors/translationsRawSelector.js +8 -0
  322. package/src/selectors/translationsSelector.js +137 -0
  323. package/src/style.css +82 -0
  324. package/src/updateEditor.js +198 -0
  325. package/src/utils/PassThrough.js +3 -0
  326. package/src/utils/addWrappedAddons.js +20 -0
  327. package/src/utils/annotationTypes.js +37 -0
  328. package/src/utils/arrayUtils.js +19 -0
  329. package/src/utils/calculateTickMarkPositionsForGivenRange.js +47 -0
  330. package/src/utils/cleanSequenceData_DEPRECATED/arrayToObjWithIds.js +17 -0
  331. package/src/utils/combineReducersDontIgnoreKeys.js +12 -0
  332. package/src/utils/commandUtils.js +18 -0
  333. package/src/utils/editorUtils.js +223 -0
  334. package/src/utils/getAnnotationClassnames.js +12 -0
  335. package/src/utils/getAnnotationNameAndStartStopString.js +61 -0
  336. package/src/utils/getVisibleStartEnd.js +7 -0
  337. package/src/utils/massageTickSpacing.js +19 -0
  338. package/src/utils/onlyUpdateForKeysDeep.js +31 -0
  339. package/src/utils/prepareRowData.js +64 -0
  340. package/src/utils/proteinUtils.js +3 -0
  341. package/src/utils/pureNoFunc.js +18 -0
  342. package/src/utils/selectionLayer.js +25 -0
  343. package/src/utils/shouldRerender.js +27 -0
  344. package/src/utils/showFileDialog.js +26 -0
  345. package/src/utils/updateLabelsForInViewFeatures.js +55 -0
  346. package/src/utils/updateLabelsForInViewFeaturesCircView.js +41 -0
  347. package/src/utils/useAAColorType.js +8 -0
  348. package/src/utils/useAnnotationLimits.js +42 -0
  349. package/src/utils/useChromatogramPrefs.js +31 -0
  350. package/src/utils/useLadders.js +6 -0
  351. package/src/utils/useMeltingTemp.js +7 -0
  352. package/src/utils/useTmType.js +10 -0
  353. package/src/withEditorInteractions/Keyboard.js +86 -0
  354. package/src/withEditorInteractions/clickAndDragUtils.js +576 -0
  355. package/src/withEditorInteractions/createSequenceInputPopup.js +296 -0
  356. package/src/withEditorInteractions/createSequenceInputPopupStyle.css +85 -0
  357. package/src/withEditorInteractions/getBpsPerRow.js +19 -0
  358. package/src/withEditorInteractions/index.js +1252 -0
  359. package/src/withEditorInteractions/isElementInViewport.js +29 -0
  360. package/src/withEditorInteractions/moveCaret.js +58 -0
  361. package/src/withEditorProps/index.js +1010 -0
  362. package/index.mjs +0 -193228
@@ -0,0 +1,1252 @@
1
+ import {
2
+ getSequenceDataBetweenRange,
3
+ tidyUpSequenceData,
4
+ getAminoAcidStringFromSequenceString
5
+ } from "@teselagen/sequence-utils";
6
+ import { getSequenceWithinRange } from "@teselagen/range-utils";
7
+ import Clipboard from "clipboard";
8
+ import { compose } from "redux";
9
+ import {
10
+ getReverseComplementSequenceAndAnnotations,
11
+ getComplementSequenceAndAnnotations
12
+ } from "@teselagen/sequence-utils";
13
+ import { some, map, noop } from "lodash";
14
+ import { Menu } from "@blueprintjs/core";
15
+ import { branch } from "recompose";
16
+
17
+ import { normalizePositionByRangeLength } from "@teselagen/range-utils";
18
+ import React from "react";
19
+
20
+ import Combokeys from "combokeys";
21
+ import {
22
+ showContextMenu,
23
+ showConfirmationDialog,
24
+ commandMenuEnhancer,
25
+ withStore
26
+ } from "@teselagen/ui";
27
+ import { jsonToGenbank } from "@teselagen/bio-parsers";
28
+ import withEditorProps from "../withEditorProps";
29
+ import getCommands from "../commands";
30
+ import moveCaret from "./moveCaret";
31
+ import createSequenceInputPopup from "./createSequenceInputPopup";
32
+ import Keyboard from "./Keyboard";
33
+ import {
34
+ handleCaretMoved,
35
+ editorClicked,
36
+ updateSelectionOrCaret
37
+ } from "./clickAndDragUtils";
38
+ import getBpsPerRow from "./getBpsPerRow";
39
+ import {
40
+ copyOptionsMenu,
41
+ createNewAnnotationMenu
42
+ } from "../MenuBar/defaultConfig";
43
+ import { fullSequenceTranslationMenu } from "../MenuBar/viewSubmenu";
44
+ import {
45
+ getAcceptedChars,
46
+ getNodeToRefocus,
47
+ getSelFromWrappedAddon
48
+ } from "../utils/editorUtils";
49
+
50
+ import {
51
+ showAddOrEditAnnotationDialog,
52
+ showDialog
53
+ } from "../GlobalDialogUtils";
54
+
55
+ const annotationClickHandlers = [
56
+ "orfClicked",
57
+ "primerClicked",
58
+ "lineageAnnotationClicked",
59
+ "assemblyPieceClicked",
60
+ "translationClicked",
61
+ "primaryProteinSequenceClicked",
62
+ "cutsiteClicked",
63
+ "translationDoubleClicked",
64
+ "deletionLayerClicked",
65
+ "replacementLayerClicked",
66
+ "featureClicked",
67
+ "warningClicked",
68
+ "partClicked",
69
+ "searchLayerClicked"
70
+ ];
71
+ //tnr: because this menu is being rendered outside the main render tree (by blueprint)
72
+ //we need to make sure it re-renders whenever the redux state changes (so things like tick-marks will toggle properly etc..)
73
+ const ConnectedMenu = withEditorProps(({ children }) => (
74
+ <Menu>{children.map(React.cloneElement)}</Menu>
75
+ ));
76
+
77
+ //withEditorInteractions is meant to give "interaction" props like "onDrag, onCopy, onKeydown" to the circular/row/linear views
78
+ function VectorInteractionHOC(Component /* options */) {
79
+ return class VectorInteractionWrapper extends React.Component {
80
+ constructor(props) {
81
+ super(props);
82
+ annotationClickHandlers.forEach((handler) => {
83
+ this[handler] = (...args) => {
84
+ const { clickOverrides = {} } = this.props;
85
+ let preventDefault;
86
+ const defaultHandler = this[handler + "_localOverride"]
87
+ ? this[handler + "_localOverride"]
88
+ : this.annotationClicked;
89
+ if (clickOverrides[handler]) {
90
+ preventDefault = clickOverrides[handler](...args);
91
+ }
92
+ !preventDefault && defaultHandler(...args);
93
+ };
94
+ });
95
+
96
+ this.ConnectedMenu = (p) => {
97
+ return <ConnectedMenu {...props} {...p} />;
98
+ };
99
+ }
100
+ componentWillUnmount() {
101
+ this.combokeys && this.combokeys.detach();
102
+ }
103
+
104
+ componentDidMount() {
105
+ if (!this.node) return;
106
+ this.combokeys = new Combokeys(this.node);
107
+
108
+ // bind a bunch of this.combokeys shortcuts we're interested in catching
109
+ // we're using the "combokeys" library which extends mousetrap (available thru npm: https://www.npmjs.com/package/br-mousetrap)
110
+ // documentation: https://craig.is/killing/mice
111
+ this.combokeys.bind(
112
+ "-.*ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),
113
+ (event) => {
114
+ this.handleDnaInsert(event);
115
+ }
116
+ );
117
+
118
+ // TODO: move these into commands
119
+ const moveCaretBindings = [
120
+ { keyCombo: ["left", "shift+left"], type: "moveCaretLeftOne" },
121
+ { keyCombo: ["right", "shift+right"], type: "moveCaretRightOne" },
122
+ { keyCombo: ["up", "shift+up"], type: "moveCaretUpARow" },
123
+ { keyCombo: ["down", "shift+down"], type: "moveCaretDownARow" },
124
+ {
125
+ keyCombo: ["alt+right", "alt+shift+right"],
126
+ type: "moveCaretToEndOfRow"
127
+ },
128
+ {
129
+ keyCombo: ["alt+left", "alt+shift+left"],
130
+ type: "moveCaretToStartOfRow"
131
+ },
132
+ {
133
+ keyCombo: ["alt+up", "alt+shift+up"],
134
+ type: "moveCaretToStartOfSequence"
135
+ },
136
+ {
137
+ keyCombo: ["alt+down", "alt+shift+down"],
138
+ type: "moveCaretToEndOfSequence"
139
+ }
140
+ ];
141
+
142
+ moveCaretBindings.forEach(({ keyCombo, type }) => {
143
+ this.combokeys.bind(keyCombo, (event) => {
144
+ const shiftHeld = event.shiftKey;
145
+ const bpsPerRow = getBpsPerRow(this.props);
146
+ const {
147
+ selectionLayer,
148
+ caretPosition,
149
+ sequenceLength,
150
+ sequenceData: { isProtein, circular } = {},
151
+ circular: circular2,
152
+ caretPositionUpdate,
153
+ selectionLayerUpdate
154
+ } = this.props;
155
+ const moveBy = moveCaret({
156
+ sequenceLength,
157
+ bpsPerRow,
158
+ caretPosition,
159
+ selectionLayer,
160
+ isProtein,
161
+ shiftHeld,
162
+ type
163
+ });
164
+ handleCaretMoved({
165
+ moveBy,
166
+ circular: circular || circular2,
167
+ sequenceLength,
168
+ bpsPerRow,
169
+ caretPosition,
170
+ selectionLayer,
171
+ shiftHeld,
172
+ type,
173
+ caretPositionUpdate,
174
+ selectionLayerUpdate
175
+ });
176
+ event.stopPropagation();
177
+ });
178
+ });
179
+
180
+ this.combokeys.bind(["backspace", "del"], (event) => {
181
+ // Handle shortcut
182
+ this.handleDnaDelete(event);
183
+ });
184
+
185
+ this.commandEnhancer = commandMenuEnhancer(getCommands(this), {
186
+ useTicks: true,
187
+ omitIcons: true
188
+ });
189
+ }
190
+ updateSelectionOrCaret = (shiftHeld, newRangeOrCaret) => {
191
+ const {
192
+ selectionLayer,
193
+ caretPosition,
194
+ sequenceData = { sequence: "" }
195
+ } = this.props;
196
+ const sequenceLength = sequenceData.sequence.length;
197
+ updateSelectionOrCaret({
198
+ doNotWrapOrigin: !sequenceData.circular,
199
+ shiftHeld,
200
+ sequenceLength,
201
+ newRangeOrCaret,
202
+ caretPosition,
203
+ selectionLayer,
204
+ selectionLayerUpdate: this.selectionLayerUpdate,
205
+ caretPositionUpdate: this.caretPositionUpdate
206
+ });
207
+ };
208
+
209
+ handlePaste = (e) => {
210
+ const {
211
+ caretPosition = -1,
212
+ selectionLayer = { start: -1, end: -1 },
213
+ readOnly,
214
+ onPaste
215
+ } = this.props;
216
+
217
+ if (readOnly) {
218
+ return window.toastr.warning("Sorry the sequence is Read-Only");
219
+ }
220
+ if (!(caretPosition > -1 || selectionLayer.start > -1)) {
221
+ return window.toastr.warning("Please place the cursor before pasting");
222
+ }
223
+
224
+ let seqDataToInsert;
225
+ if (onPaste) {
226
+ seqDataToInsert = onPaste(e, this.props);
227
+ } else {
228
+ const clipboardData = e.clipboardData;
229
+ let jsonData = clipboardData.getData("application/json");
230
+ if (jsonData) {
231
+ jsonData = JSON.parse(jsonData);
232
+ }
233
+ seqDataToInsert = jsonData || {
234
+ sequence: clipboardData.getData("text/plain") || e.target.value
235
+ };
236
+ }
237
+
238
+ seqDataToInsert = tidyUpSequenceData(seqDataToInsert, {
239
+ provideNewIdsForAnnotations: true,
240
+ annotationsAsObjects: true,
241
+ removeUnwantedChars: true,
242
+ noCdsTranslations: true
243
+ });
244
+ if (!seqDataToInsert.sequence.length)
245
+ return window.toastr.warning("Sorry no valid base pairs to paste");
246
+
247
+ insertAndSelectHelper({
248
+ seqDataToInsert,
249
+ props: this.props
250
+ });
251
+
252
+ window.toastr.success("Sequence Pasted Successfully");
253
+ e.preventDefault();
254
+ };
255
+
256
+ handleCutOrCopy = (isCut) => (e) => {
257
+ const {
258
+ onCopy = noop,
259
+ sequenceData,
260
+ selectionLayer,
261
+ copyOptions,
262
+ readOnly
263
+ } = this.props;
264
+ const onCut = this.props.onCut || this.props.onCopy || (noop);
265
+ const seqData = tidyUpSequenceData(
266
+ this.sequenceDataToCopy ||
267
+ getSequenceDataBetweenRange(
268
+ sequenceData,
269
+ getSelFromWrappedAddon(
270
+ selectionLayer,
271
+ sequenceData.sequence.length
272
+ ),
273
+ {
274
+ excludePartial: {
275
+ features: !copyOptions.partialFeatures,
276
+ parts: !copyOptions.partialParts
277
+ },
278
+ exclude: {
279
+ features: !copyOptions.features,
280
+ parts: !copyOptions.parts
281
+ }
282
+ }
283
+ ),
284
+ { annotationsAsObjects: true }
285
+ );
286
+
287
+ if (
288
+ !(this.sequenceDataToCopy || {}).textToCopy &&
289
+ !seqData.sequence.length
290
+ )
291
+ return window.toastr.warning(
292
+ `No Sequence Selected To ${isCut && !readOnly ? "Cut" : "Copy"}`
293
+ );
294
+
295
+ const clipboardData = e.clipboardData;
296
+ const textToCopy =
297
+ (this.sequenceDataToCopy || {}).textToCopy !== undefined
298
+ ? this.sequenceDataToCopy.textToCopy
299
+ : seqData.isProtein
300
+ ? seqData.proteinSequence
301
+ : seqData.sequence;
302
+
303
+ seqData.textToCopy = textToCopy;
304
+
305
+ clipboardData.setData("text/plain", textToCopy);
306
+ clipboardData.setData("application/json", JSON.stringify(seqData));
307
+ e.preventDefault();
308
+
309
+ if (isCut && !readOnly) {
310
+ this.handleDnaDelete(false);
311
+ onCut(
312
+ e,
313
+ tidyUpSequenceData(seqData, { annotationsAsObjects: true }),
314
+ this.props
315
+ );
316
+ document.body.removeEventListener("cut", this.handleCut);
317
+ } else {
318
+ onCopy(e, seqData, this.props);
319
+ document.body.removeEventListener("copy", this.handleCopy);
320
+ }
321
+ window.toastr.success(
322
+ `Selection ${isCut && !readOnly ? "Cut" : "Copied"}`
323
+ );
324
+ this.sequenceDataToCopy = undefined;
325
+ };
326
+ handleCut = this.handleCutOrCopy(true);
327
+
328
+ handleCopy = this.handleCutOrCopy();
329
+
330
+ handleDnaInsert = ({ useEventPositioning }) => {
331
+ const {
332
+ caretPosition = -1,
333
+ selectionLayer = { start: -1, end: -1 },
334
+ sequenceData = { sequence: "" },
335
+ readOnly
336
+ // updateSequenceData,
337
+ // wrappedInsertSequenceDataAtPositionOrRange
338
+ // handleInsert
339
+ } = this.props;
340
+ const sequenceLength = sequenceData.sequence.length;
341
+ const isReplace = selectionLayer.start > -1;
342
+ if (readOnly) {
343
+ window.toastr.warning("Sorry the sequence is Read-Only");
344
+ } else {
345
+ createSequenceInputPopup({
346
+ useEventPositioning,
347
+ isReplace,
348
+ replaceChars: sequenceData.isRna ? { t: "u" } : undefined,
349
+ acceptedChars: getAcceptedChars(sequenceData),
350
+ isProtein: sequenceData.isProtein,
351
+ selectionLayer,
352
+ sequenceLength,
353
+ caretPosition,
354
+ handleInsert: (seqDataToInsert) => {
355
+ insertAndSelectHelper({
356
+ props: this.props,
357
+ seqDataToInsert
358
+ });
359
+
360
+ window.toastr.success("Sequence Inserted Successfully");
361
+ }
362
+ });
363
+ }
364
+ };
365
+
366
+ handleDnaDelete = (showToast = true) => {
367
+ const {
368
+ caretPosition = -1,
369
+ selectionLayer = { start: -1, end: -1 },
370
+ sequenceData = { sequence: "" },
371
+ readOnly,
372
+ updateSequenceData,
373
+ wrappedInsertSequenceDataAtPositionOrRange,
374
+ caretPositionUpdate
375
+ // handleInsert
376
+ } = this.props;
377
+ const sequenceLength = sequenceData.sequence.length;
378
+ if (readOnly) {
379
+ return window.toastr.warning("Sorry the sequence is Read-Only");
380
+ }
381
+ if (sequenceLength > 0) {
382
+ let rangeToDelete = selectionLayer;
383
+ let isCaretAtEndOfSeq;
384
+ if (caretPosition > 0) {
385
+ if (caretPosition === sequenceLength) {
386
+ isCaretAtEndOfSeq = true;
387
+ }
388
+ rangeToDelete = {
389
+ start: normalizePositionByRangeLength(
390
+ caretPosition - (sequenceData.isProtein ? 3 : 1),
391
+ sequenceLength
392
+ ),
393
+ end: normalizePositionByRangeLength(
394
+ caretPosition - 1,
395
+ sequenceLength
396
+ )
397
+ };
398
+ } else {
399
+ if (rangeToDelete.end === sequenceLength - 1) {
400
+ isCaretAtEndOfSeq = true;
401
+ }
402
+ }
403
+ const [newSeqData] = wrappedInsertSequenceDataAtPositionOrRange(
404
+ {},
405
+ sequenceData,
406
+ rangeToDelete
407
+ );
408
+ updateSequenceData(newSeqData);
409
+ caretPositionUpdate(
410
+ isCaretAtEndOfSeq
411
+ ? newSeqData.sequence.length
412
+ : rangeToDelete.start > newSeqData.sequence.length
413
+ ? //we're deleting around the origin so set the cursor to the 0 position
414
+ 0
415
+ : normalizePositionByRangeLength(
416
+ rangeToDelete.start,
417
+ newSeqData.sequence.length
418
+ )
419
+ );
420
+ if (showToast) window.toastr.success("Sequence Deleted Successfully");
421
+ }
422
+ };
423
+
424
+ caretPositionUpdate = (position) => {
425
+ const { caretPosition = -1 } = this.props;
426
+ if (caretPosition === position) {
427
+ return;
428
+ }
429
+ //we only call caretPositionUpdate if we're actually changing something
430
+ this.props.caretPositionUpdate(position);
431
+ };
432
+ selectionLayerUpdate = (newSelection) => {
433
+ const { selectionLayer = { start: -1, end: -1 }, ignoreGapsOnHighlight } =
434
+ this.props;
435
+ if (!newSelection) return;
436
+ const { start, end, forceUpdate } = newSelection;
437
+ if (
438
+ selectionLayer.start === start &&
439
+ selectionLayer.end === end &&
440
+ selectionLayer.forceUpdate === forceUpdate
441
+ ) {
442
+ return;
443
+ }
444
+ //we only call selectionLayerUpdate if we're actually changing something
445
+ this.props.selectionLayerUpdate({
446
+ ...newSelection,
447
+ start,
448
+ end,
449
+ ignoreGaps: ignoreGapsOnHighlight
450
+ });
451
+ };
452
+
453
+ annotationClicked = ({ event, annotation }) => {
454
+ event.preventDefault && event.preventDefault();
455
+ event.stopPropagation && event.stopPropagation();
456
+ // if (hoveredAnnEasyStore.selectedAnn?.id === annotation?.id) {
457
+ // hoveredAnnEasyStore.selectedAnn = undefined;
458
+ // hoveredAnnEasyStore.hoveredAnn = undefined;
459
+ // } else {
460
+ // hoveredAnnEasyStore.selectedAnn = annotation;
461
+ // }
462
+
463
+ const {
464
+ annotationSelect,
465
+ selectionLayer,
466
+ annotationDeselectAll,
467
+ propertiesViewTabUpdate
468
+ } = this.props;
469
+ let forceUpdate;
470
+ if (
471
+ annotation.start > -1 &&
472
+ // selectionLayer.start === annotation.start &&
473
+ // selectionLayer.end === annotation.end &&
474
+ event.altKey
475
+ ) {
476
+ forceUpdate = selectionLayer.forceUpdate === "end" ? "start" : "end";
477
+ }
478
+ this.updateSelectionOrCaret(event.shiftKey || event.metaKey, {
479
+ ...annotation,
480
+ isFromRowView: !!event?.target?.closest(".veRowView"),
481
+ ...(forceUpdate && { forceUpdate })
482
+ });
483
+ !event.shiftKey && annotationDeselectAll(undefined);
484
+ annotation.id && annotationSelect(annotation);
485
+ annotation.annotationTypePlural &&
486
+ propertiesViewTabUpdate(annotation.annotationTypePlural, annotation);
487
+ };
488
+
489
+ cutsiteClicked_localOverride = ({ event, annotation }) => {
490
+ event.preventDefault();
491
+ event.stopPropagation();
492
+ const { annotationSelect, annotationDeselectAll } = this.props;
493
+ this.updateSelectionOrCaret(
494
+ event.shiftKey,
495
+ event.metaKey
496
+ ? annotation
497
+ : event.altKey
498
+ ? annotation.bottomSnipPosition
499
+ : annotation.topSnipPosition
500
+ );
501
+ annotationDeselectAll(undefined);
502
+ annotationSelect(annotation);
503
+ };
504
+
505
+ warningDoubleClicked = ({ event, annotation, doNotStopPropagation }) => {
506
+ if (!doNotStopPropagation) {
507
+ event.preventDefault();
508
+ event.stopPropagation();
509
+ }
510
+ const { annotationSelect, annotationDeselectAll } = this.props;
511
+ showConfirmationDialog({
512
+ cancelButtonText: null,
513
+ confirmButtonText: "OK",
514
+ canEscapeKeyCancel: true,
515
+ // intent: Intent.NONE,
516
+ // onCancel: undefined,
517
+ text: (
518
+ <div style={{ wordBreak: "break-word" }}>
519
+ <h3>{annotation.name}:</h3>
520
+ {annotation.message}
521
+ </div>
522
+ )
523
+ });
524
+ this.updateSelectionOrCaret(event.shiftKey, annotation);
525
+ annotationDeselectAll(undefined);
526
+ annotationSelect(annotation);
527
+ };
528
+ insertHelper = {
529
+ onClick: (e, ctxInfo) => {
530
+ this.handleDnaInsert({
531
+ useEventPositioning: {
532
+ e,
533
+ nodeToReFocus: getNodeToRefocus(ctxInfo.event.target)
534
+ }
535
+ });
536
+ }
537
+ };
538
+
539
+ // eslint-disable-next-line no-unused-vars
540
+ getCopyOptions = (annotation) => {
541
+ const { sequenceData, readOnly, selectionLayer } = this.props;
542
+ const { isProtein } = sequenceData;
543
+ const makeTextCopyable = (transformFunc, className, action = "copy") => {
544
+ return new Clipboard(`.${className}`, {
545
+ action: () => action,
546
+ text: () => {
547
+ const { selectionLayer, editorName, store } = this.props;
548
+ const { sequenceData, copyOptions } =
549
+ store.getState().VectorEditor[editorName];
550
+
551
+ const selectedSeqData = getSequenceDataBetweenRange(
552
+ sequenceData,
553
+ getSelFromWrappedAddon(
554
+ selectionLayer,
555
+ sequenceData.sequence.length
556
+ ),
557
+ {
558
+ excludePartial: {
559
+ features: !copyOptions.partialFeatures,
560
+ parts: !copyOptions.partialParts
561
+ },
562
+ exclude: {
563
+ features: !copyOptions.features,
564
+ parts: !copyOptions.parts
565
+ }
566
+ }
567
+ );
568
+ const sequenceDataToCopy = transformFunc(
569
+ selectedSeqData,
570
+ sequenceData
571
+ );
572
+ this.sequenceDataToCopy = sequenceDataToCopy;
573
+ if (action === "copy") {
574
+ document.body.addEventListener("copy", this.handleCopy);
575
+ } else {
576
+ document.body.addEventListener("cut", this.handleCut);
577
+ }
578
+ if (window.Cypress) {
579
+ window.Cypress.textToCopy = sequenceDataToCopy.textToCopy;
580
+ window.Cypress.seqDataToCopy = sequenceDataToCopy;
581
+ }
582
+ return sequenceDataToCopy.textToCopy || sequenceDataToCopy.sequence;
583
+ }
584
+ });
585
+ };
586
+ const aaCopy = {
587
+ text: "Copy AA Sequence",
588
+ className: "openVeCopyAA",
589
+ willUnmount: () => {
590
+ this.openVeCopyAA && this.openVeCopyAA.destroy();
591
+ },
592
+ didMount: ({ className }) => {
593
+ this.openVeCopyAA = makeTextCopyable((selectedSeqData) => {
594
+ const textToCopy = isProtein
595
+ ? selectedSeqData.proteinSequence.toUpperCase()
596
+ : getAminoAcidStringFromSequenceString(selectedSeqData.sequence);
597
+ return {
598
+ ...selectedSeqData,
599
+ textToCopy
600
+ };
601
+ }, className);
602
+ }
603
+ };
604
+ // TODO: maybe stop using Clipboard.js and unify clipboard handling with
605
+ // a more versatile approach
606
+ return [
607
+ ...(readOnly
608
+ ? []
609
+ : [
610
+ {
611
+ text: "Replace",
612
+ ...this.insertHelper
613
+ },
614
+ {
615
+ text: "Cut",
616
+ className: "openVeCut",
617
+ willUnmount: () => {
618
+ this.openVeCut && this.openVeCut.destroy();
619
+ },
620
+ didMount: ({ className }) => {
621
+ // TODO: Maybe use a cut action instead
622
+ this.openVeCut = makeTextCopyable(
623
+ (s) => ({
624
+ ...s,
625
+ textToCopy: isProtein ? s.proteinSequence : s.sequence
626
+ }),
627
+ className,
628
+ "cut"
629
+ );
630
+ }
631
+ }
632
+ ]),
633
+ {
634
+ text:
635
+ annotation.overlapsSelf || selectionLayer.overlapsSelf
636
+ ? "Copy Wrapped Range"
637
+ : "Copy",
638
+ submenu: [
639
+ ...(isProtein ? [aaCopy] : []),
640
+ {
641
+ text: isProtein ? "Copy DNA Bps" : "Copy",
642
+ className: "openVeCopy2",
643
+ willUnmount: () => {
644
+ this.openVeCopy2 && this.openVeCopy2.destroy();
645
+ },
646
+ didMount: ({ className }) => {
647
+ this.openVeCopy2 = makeTextCopyable(
648
+ (s) => ({ ...s, textToCopy: s.sequence }),
649
+ className
650
+ );
651
+ }
652
+ },
653
+
654
+ {
655
+ text: "Copy Genbank For Selection",
656
+ className: "openVeCopyGenbankForSelection",
657
+ willUnmount: () => {
658
+ this.openVeCopyGenbankForSelection &&
659
+ this.openVeCopyGenbankForSelection.destroy();
660
+ },
661
+ didMount: ({ className }) => {
662
+ this.openVeCopyGenbankForSelection = makeTextCopyable(
663
+ getGenbankFromSelection,
664
+ className
665
+ );
666
+ }
667
+ },
668
+ {
669
+ text: isProtein
670
+ ? "Copy Reverse Complement DNA Bps"
671
+ : "Copy Reverse Complement",
672
+ className: "openVeCopyReverse",
673
+ willUnmount: () => {
674
+ this.openVeCopyReverse && this.openVeCopyReverse.destroy();
675
+ },
676
+ didMount: ({ className }) => {
677
+ this.openVeCopyReverse = makeTextCopyable(
678
+ getReverseComplementSequenceAndAnnotations,
679
+ className
680
+ );
681
+ }
682
+ },
683
+ ...(isProtein ? [] : [aaCopy]),
684
+ {
685
+ text: "Copy Reverse Complement AA Sequence",
686
+ className: "openVeCopyAAReverse",
687
+ willUnmount: () => {
688
+ this.openVeCopyAAReverse && this.openVeCopyAAReverse.destroy();
689
+ },
690
+ didMount: ({ className }) => {
691
+ this.openVeCopyAAReverse = makeTextCopyable(
692
+ (selectedSeqData) => {
693
+ const revSeqData =
694
+ getReverseComplementSequenceAndAnnotations(
695
+ selectedSeqData
696
+ );
697
+ const textToCopy = isProtein
698
+ ? revSeqData.proteinSequence.toUpperCase()
699
+ : getAminoAcidStringFromSequenceString(
700
+ revSeqData.sequence
701
+ );
702
+ return {
703
+ ...revSeqData,
704
+ textToCopy
705
+ };
706
+ },
707
+ className
708
+ );
709
+ }
710
+ },
711
+ {
712
+ text: isProtein ? "Copy Complement DNA Bps" : "Copy Complement",
713
+ className: "openVeCopyComplement",
714
+ willUnmount: () => {
715
+ this.openVeCopyComplement &&
716
+ this.openVeCopyComplement.destroy();
717
+ },
718
+ didMount: ({ className }) => {
719
+ this.openVeCopyComplement = makeTextCopyable(
720
+ getComplementSequenceAndAnnotations,
721
+ className
722
+ );
723
+ }
724
+ },
725
+ copyOptionsMenu
726
+ ]
727
+ }
728
+ ];
729
+ };
730
+
731
+ getSelectionMenuOptions = (annotation) => {
732
+ const items = [
733
+ ...this.getCopyOptions(annotation),
734
+ createNewAnnotationMenu,
735
+ "--",
736
+ "selectInverse",
737
+ "--",
738
+ "reverseComplementSelection",
739
+ "complementSelection",
740
+ {
741
+ cmd: "changeCaseCmd",
742
+ text: "Change Case",
743
+ submenu: [
744
+ // "upperCaseSequence",
745
+ // "lowerCaseSequence",
746
+ "upperCaseSelection",
747
+ "lowerCaseSelection"
748
+ ]
749
+ }
750
+ ];
751
+ return items;
752
+ };
753
+ normalizeAction = ({ event, ...rest }, handler) => {
754
+ event.preventDefault();
755
+ event.stopPropagation();
756
+ return handler({ event, ...rest }, this.props);
757
+ };
758
+ enhanceRightClickAction = (action, key) => {
759
+ return (opts) => {
760
+ const lastFocusedEl = document.activeElement;
761
+ const { rightClickOverrides = {} } = this.props;
762
+ const items = action(opts);
763
+ const e = (items && items._event) || opts.event || opts;
764
+ e.preventDefault && e.preventDefault();
765
+ e.stopPropagation && e.stopPropagation();
766
+ //override hook here
767
+ const override = rightClickOverrides[key];
768
+ showContextMenu(
769
+ override ? override(items, opts, this.props) : items,
770
+ [this.commandEnhancer],
771
+ e,
772
+ () => {
773
+ if (
774
+ lastFocusedEl &&
775
+ document.activeElement &&
776
+ (document.activeElement.classList.contains(
777
+ "bp3-popover-enter-done"
778
+ ) ||
779
+ (document.activeElement.type === "textarea" && //this is the clipboard textarea created by clipboardjs
780
+ document.activeElement.offsetLeft === -9999))
781
+ ) {
782
+ lastFocusedEl.focus();
783
+ }
784
+ },
785
+ opts, // context here
786
+ this.ConnectedMenu
787
+ );
788
+ };
789
+ };
790
+
791
+ selectionLayerRightClicked = this.enhanceRightClickAction(
792
+ ({ annotation }) => {
793
+ return this.getSelectionMenuOptions({
794
+ //manually only pluck off the start and end so that if the selection layer was generated from say a feature, those properties won't be carried into the create part/feature/primer dialogs
795
+ isWrappedAddon: annotation.isWrappedAddon,
796
+ overlapsSelf: annotation.overlapsSelf,
797
+ start: annotation.start,
798
+ end: annotation.end
799
+ });
800
+ },
801
+ "selectionLayerRightClicked"
802
+ );
803
+ digestLaneRightClicked = this.enhanceRightClickAction(() => {
804
+ return ["newFeature", "newPart"];
805
+ }, "digestLaneRightClicked");
806
+ searchLayerRightClicked = this.enhanceRightClickAction(({ annotation }) => {
807
+ this.props.selectionLayerUpdate({
808
+ start: annotation.start,
809
+ end: annotation.end,
810
+ forward: !annotation.bottomStrand
811
+ });
812
+ return this.getSelectionMenuOptions({
813
+ //manually only pluck off the start and end so that if the selection layer was generated from say a feature, those properties won't be carried into the create part/feature/primer dialogs
814
+ start: annotation.start,
815
+ end: annotation.end,
816
+ forward: !annotation.bottomStrand
817
+ });
818
+ }, "searchLayerRightClicked");
819
+
820
+ backgroundRightClicked = this.enhanceRightClickAction(
821
+ ({ nearestCaretPos, shiftHeld, event }) => {
822
+ this.updateSelectionOrCaret(shiftHeld, nearestCaretPos);
823
+ const {
824
+ readOnly
825
+ // sequenceData: { circular }
826
+ } = this.props;
827
+ const menu = [
828
+ ...(readOnly
829
+ ? []
830
+ : [
831
+ {
832
+ text: "Insert",
833
+ ...this.insertHelper
834
+ }
835
+ ]),
836
+ "rotateToCaretPosition",
837
+ "createMenuHolder",
838
+ {
839
+ ...fullSequenceTranslationMenu,
840
+ text: "View Full Sequence Translations"
841
+ }
842
+ ];
843
+ menu._event = event;
844
+ return menu;
845
+ },
846
+ "backgroundRightClicked"
847
+ );
848
+
849
+ deletionLayerRightClicked = this.enhanceRightClickAction(
850
+ ({ annotation }) => {
851
+ const { editorName, dispatch } = this.props;
852
+ return [
853
+ {
854
+ text: "Remove Deletion",
855
+ // icon: "ion-plus-round",
856
+ onClick: function () {
857
+ dispatch({
858
+ type: "DELETION_LAYER_DELETE",
859
+ meta: { editorName },
860
+ payload: { ...annotation }
861
+ });
862
+ }
863
+ }
864
+ ];
865
+ },
866
+ "deletionLayerRightClicked"
867
+ );
868
+
869
+ partRightClicked = this.enhanceRightClickAction(({ annotation, event }) => {
870
+ this.props.selectionLayerUpdate({
871
+ isFromRowView: !!event?.target?.closest(".veRowView"),
872
+ start: annotation.start,
873
+ end: annotation.end,
874
+ isWrappedAddon: annotation.isWrappedAddon,
875
+ overlapsSelf: annotation.overlapsSelf
876
+ });
877
+ return [
878
+ "editPart",
879
+ "deletePart",
880
+ "--",
881
+ ...this.getSelectionMenuOptions(annotation),
882
+ "--",
883
+ "showRemoveDuplicatesDialogParts",
884
+ "viewPartProperties"
885
+ ];
886
+ }, "partRightClicked");
887
+ warningRightClicked = this.enhanceRightClickAction(
888
+ ({ annotation, event }) => {
889
+ this.props.selectionLayerUpdate({
890
+ isFromRowView: !!event?.target?.closest(".veRowView"),
891
+ start: annotation.start,
892
+ end: annotation.end
893
+ });
894
+ return [
895
+ {
896
+ text: "View Warning Details",
897
+ onClick: (event) => {
898
+ this.warningDoubleClicked({
899
+ event,
900
+ annotation,
901
+ doNotStopPropagation: true
902
+ });
903
+ }
904
+ },
905
+
906
+ "--",
907
+ ...this.getSelectionMenuOptions(annotation)
908
+ ];
909
+ },
910
+ "warningRightClicked"
911
+ );
912
+ featureRightClicked = this.enhanceRightClickAction(
913
+ ({ annotation, event }) => {
914
+ this.props.selectionLayerUpdate({
915
+ isFromRowView: !!event?.target?.closest(".veRowView"),
916
+ start: annotation.start,
917
+ end: annotation.end
918
+ });
919
+ event.persist();
920
+ const { readOnly, annotationsToSupport: { parts } = {} } = this.props;
921
+ return [
922
+ "editFeature",
923
+ "deleteFeature",
924
+ ...this.getSelectionMenuOptions(annotation),
925
+ ...(readOnly
926
+ ? []
927
+ : [
928
+ ...(parts && [
929
+ "--",
930
+ {
931
+ text: "Make a Part from Feature",
932
+ onClick: async () => {
933
+ const { sequenceData, upsertPart } = this.props;
934
+ if (
935
+ some(sequenceData.parts, (part) => {
936
+ if (
937
+ part.start === annotation.start &&
938
+ part.end === annotation.end
939
+ ) {
940
+ return true;
941
+ }
942
+ })
943
+ ) {
944
+ const doAction = await showConfirmationDialog({
945
+ text: "A part already exists that matches this feature's range. Do you want to make one anyways?",
946
+ confirmButtonText: "Create Part",
947
+ canEscapeKeyCancel: true //this is false by default
948
+ });
949
+ if (!doAction) return; //early return
950
+ }
951
+ upsertPart({
952
+ start: annotation.start,
953
+ end: annotation.end,
954
+ type: annotation.type,
955
+ forward: annotation.forward,
956
+ name: annotation.name
957
+ });
958
+ }
959
+ }
960
+ ]),
961
+ {
962
+ text: "Merge With Another Feature",
963
+ onClick: () => {
964
+ this.annotationClicked({
965
+ annotation,
966
+ event: { ...event, shiftHeld: true }
967
+ });
968
+ // annotationSelect(annotation)
969
+ showDialog({
970
+ dialogType: "MergeFeaturesDialog"
971
+ });
972
+ }
973
+ },
974
+ "showRemoveDuplicatesDialogFeatures",
975
+ "--"
976
+ ]),
977
+ "toggleCdsFeatureTranslations",
978
+ "--",
979
+ "viewFeatureProperties"
980
+ ];
981
+ },
982
+ "featureRightClicked"
983
+ );
984
+
985
+ cutsiteRightClicked = this.enhanceRightClickAction(
986
+ () => ["viewCutsiteProperties"],
987
+ "cutsiteRightClicked"
988
+ );
989
+ primerRightClicked = this.enhanceRightClickAction(
990
+ ({ annotation, event }) => {
991
+ this.props.selectionLayerUpdate({
992
+ isFromRowView: !!event?.target?.closest(".veRowView"),
993
+ start: annotation.start,
994
+ end: annotation.end
995
+ });
996
+ return [
997
+ "editPrimer",
998
+ "deletePrimer",
999
+ ...this.getSelectionMenuOptions(annotation),
1000
+ "showRemoveDuplicatesDialogPrimers",
1001
+ "viewPrimerProperties"
1002
+ ];
1003
+ },
1004
+ "primerRightClicked"
1005
+ );
1006
+ orfRightClicked = this.enhanceRightClickAction(({ annotation, event }) => {
1007
+ this.props.selectionLayerUpdate({
1008
+ isFromRowView: !!event?.target?.closest(".veRowView"),
1009
+ start: annotation.start,
1010
+ end: annotation.end
1011
+ });
1012
+ return [
1013
+ "toggleOrfTranslations",
1014
+ ...this.getSelectionMenuOptions(annotation),
1015
+ "viewOrfProperties"
1016
+ ];
1017
+ }, "orfRightClicked");
1018
+ translationRightClicked = this.enhanceRightClickAction(
1019
+ ({ event, annotation }) => {
1020
+ event.preventDefault();
1021
+ event.stopPropagation();
1022
+ const { selectionLayerUpdate, annotationVisibilityToggle } = this.props;
1023
+ this.props.selectionLayerUpdate({
1024
+ isFromRowView: !!event?.target?.closest(".veRowView"),
1025
+ start: annotation.start,
1026
+ end: annotation.end
1027
+ });
1028
+ if (annotation.isOrf) {
1029
+ return [
1030
+ {
1031
+ text: "Hide ORF Translations",
1032
+ onClick: () => {
1033
+ annotationVisibilityToggle("orfTranslations");
1034
+ }
1035
+ },
1036
+ "viewOrfProperties"
1037
+ ];
1038
+ }
1039
+ return [
1040
+ "deleteTranslation",
1041
+ {
1042
+ text: "Select Translation",
1043
+ onClick: function () {
1044
+ selectionLayerUpdate({
1045
+ start: annotation.start,
1046
+ end: annotation.end
1047
+ });
1048
+ }
1049
+ },
1050
+ ...this.getSelectionMenuOptions(annotation),
1051
+ "viewTranslationProperties"
1052
+ ];
1053
+ },
1054
+ "translationRightClicked"
1055
+ );
1056
+
1057
+ featureDoubleClicked = ({ annotation }) => {
1058
+ showAddOrEditAnnotationDialog({ type: "feature", annotation });
1059
+ };
1060
+ partDoubleClicked = ({ annotation }) => {
1061
+ showAddOrEditAnnotationDialog({ type: "part", annotation });
1062
+ };
1063
+ primerDoubleClicked = ({ annotation }) => {
1064
+ showAddOrEditAnnotationDialog({ type: "primer", annotation });
1065
+ };
1066
+ cutsiteDoubleClicked = ({ annotation }) => {
1067
+ showDialog({
1068
+ dialogType: "AdditionalCutsiteInfoDialog",
1069
+ props: {
1070
+ dialogProps: {
1071
+ title: annotation.name
1072
+ },
1073
+ cutsiteOrGroupKey: annotation.name
1074
+ }
1075
+ });
1076
+ };
1077
+
1078
+ render() {
1079
+ const {
1080
+ closePanelButton,
1081
+ selectionLayer = { start: -1, end: -1 },
1082
+ sequenceData = { sequence: "" },
1083
+ tabHeight //height of the little clickable tabs (passed because they are measured together with the editor panels and thus need to be subtracted)
1084
+ // fitHeight //used to allow the editor to expand to fill the height of its containing component
1085
+ } = this.props;
1086
+ //do this in two steps to determine propsToPass
1087
+
1088
+ let {
1089
+ // eslint-disable-next-line prefer-const
1090
+ children,
1091
+ // eslint-disable-next-line prefer-const
1092
+ vectorInteractionWrapperStyle = {},
1093
+ // eslint-disable-next-line prefer-const
1094
+ disableEditorClickAndDrag = false,
1095
+ ...propsToPass
1096
+ } = this.props;
1097
+ const { width, height } = this.props.dimensions || {};
1098
+ propsToPass.width = width;
1099
+ propsToPass.height = height - tabHeight;
1100
+ // if (fitHeight) {
1101
+ // }
1102
+ const selectedBps = getSequenceWithinRange(
1103
+ selectionLayer,
1104
+ sequenceData.sequence
1105
+ );
1106
+ if (!disableEditorClickAndDrag) {
1107
+ propsToPass = {
1108
+ ...propsToPass,
1109
+ selectionLayerRightClicked: this.selectionLayerRightClicked,
1110
+ digestLaneRightClicked: this.digestLaneRightClicked,
1111
+ searchLayerRightClicked: this.searchLayerRightClicked,
1112
+ backgroundRightClicked: this.backgroundRightClicked,
1113
+ featureRightClicked: this.featureRightClicked,
1114
+ partRightClicked: this.partRightClicked,
1115
+ primerRightClicked: this.primerRightClicked,
1116
+ featureDoubleClicked: this.featureDoubleClicked,
1117
+ partDoubleClicked: this.partDoubleClicked,
1118
+ primerDoubleClicked: this.primerDoubleClicked,
1119
+ warningDoubleClicked: this.warningDoubleClicked,
1120
+ warningRightClicked: this.warningRightClicked,
1121
+ orfRightClicked: this.orfRightClicked,
1122
+ deletionLayerRightClicked: this.deletionLayerRightClicked,
1123
+ cutsiteRightClicked: this.cutsiteRightClicked,
1124
+ cutsiteDoubleClicked: this.cutsiteDoubleClicked,
1125
+ translationRightClicked: this.translationRightClicked,
1126
+ ...annotationClickHandlers.reduce((acc, handler) => {
1127
+ acc[handler] = this[handler];
1128
+ return acc;
1129
+ }, {}),
1130
+ editorClicked: (p) => {
1131
+ editorClicked({
1132
+ ...p,
1133
+ updateSelectionOrCaret: this.updateSelectionOrCaret
1134
+ });
1135
+ }
1136
+ };
1137
+ }
1138
+ // propsToPass.triggerClipboardCommand = this.triggerClipboardCommand;
1139
+
1140
+ return (
1141
+ <div
1142
+ tabIndex={0} //this helps with focusing using Keyboard's parentElement.focus()
1143
+ ref={(c) => (this.node = c)}
1144
+ className="veVectorInteractionWrapper"
1145
+ style={{ position: "relative", ...vectorInteractionWrapperStyle }}
1146
+ onFocus={this.handleWrapperFocus}
1147
+ >
1148
+ {closePanelButton}
1149
+ <Keyboard
1150
+ value={selectedBps}
1151
+ onCopy={this.handleCopy}
1152
+ onPaste={this.handlePaste}
1153
+ onCut={this.handleCut}
1154
+ />
1155
+
1156
+ {/* we pass this dialog here */}
1157
+ <Component {...propsToPass} />
1158
+ </div>
1159
+ );
1160
+ }
1161
+ };
1162
+ }
1163
+
1164
+ const withEditorInteractions = compose(
1165
+ withStore,
1166
+ withEditorProps,
1167
+ branch(({ noInteractions }) => !noInteractions, VectorInteractionHOC)
1168
+ );
1169
+ export default withEditorInteractions;
1170
+
1171
+ function getGenbankFromSelection(selectedSeqData, sequenceData) {
1172
+ const spansEntireSeq =
1173
+ sequenceData.sequence.length === selectedSeqData.sequence.length;
1174
+ const feats = map(selectedSeqData.features);
1175
+ const just1Feat = feats.length === 1;
1176
+
1177
+ return {
1178
+ ...selectedSeqData,
1179
+ textToCopy: jsonToGenbank({
1180
+ ...selectedSeqData,
1181
+ name: spansEntireSeq
1182
+ ? selectedSeqData.name
1183
+ : just1Feat
1184
+ ? feats[0].name
1185
+ : selectedSeqData.name + "_partial",
1186
+ circular: spansEntireSeq ? selectedSeqData.circular : false
1187
+ })
1188
+ };
1189
+ }
1190
+
1191
+ const insertAndSelectHelper = ({ seqDataToInsert, props }) => {
1192
+ const {
1193
+ updateSequenceData,
1194
+ wrappedInsertSequenceDataAtPositionOrRange,
1195
+ sequenceData,
1196
+ selectionLayerUpdate,
1197
+ caretPosition,
1198
+ selectionLayer
1199
+ } = props;
1200
+
1201
+ // sequenceData,
1202
+ // caretPosition,
1203
+ // selectionLayer
1204
+
1205
+ // updateSequenceData(
1206
+ // wrappedInsertSequenceDataAtPositionOrRange(
1207
+ // seqDataToInsert,
1208
+ // sequenceData,
1209
+ // caretPosition > -1 ? caretPosition : selectionLayer
1210
+ // )
1211
+ // );
1212
+
1213
+ // const newSelectionLayerStart =
1214
+ // caretPosition > -1 ? caretPosition : (selectionLayer.start > selectionLayer.end ? 0 : selectionLayer.start);
1215
+ // selectionLayerUpdate({
1216
+ // start: newSelectionLayerStart,
1217
+ // end: newSelectionLayerStart + seqDataToInsert.sequence.length - 1
1218
+ // });
1219
+ const [newSeqData, { maintainOriginSplit }] =
1220
+ wrappedInsertSequenceDataAtPositionOrRange(
1221
+ seqDataToInsert,
1222
+ sequenceData,
1223
+ caretPosition > -1 ? caretPosition : selectionLayer
1224
+ );
1225
+ updateSequenceData(newSeqData);
1226
+ const seqDataInsertLength = seqDataToInsert.sequence
1227
+ ? seqDataToInsert.sequence.length
1228
+ : null;
1229
+ const selectionStartDistanceFromEnd =
1230
+ Math.min(sequenceData.size - selectionLayer.start, seqDataInsertLength) ||
1231
+ seqDataInsertLength;
1232
+
1233
+ const newSelectionLayerStart =
1234
+ caretPosition > -1
1235
+ ? caretPosition
1236
+ : selectionLayer.start > selectionLayer.end
1237
+ ? maintainOriginSplit
1238
+ ? newSeqData.size - selectionStartDistanceFromEnd
1239
+ : 0
1240
+ : selectionLayer.start;
1241
+ const newSelectionLayerEnd =
1242
+ newSelectionLayerStart +
1243
+ (seqDataToInsert.sequence
1244
+ ? seqDataToInsert.sequence.length - 1
1245
+ : seqDataToInsert.proteinSequence
1246
+ ? seqDataToInsert.proteinSequence.length * 3 - 1
1247
+ : 0);
1248
+ selectionLayerUpdate({
1249
+ start: newSelectionLayerStart,
1250
+ end: newSelectionLayerEnd % newSeqData.sequence.length
1251
+ });
1252
+ };