diff --git a/gensplore-component/src/components/ContextMenu.jsx b/gensplore-component/src/components/ContextMenu.jsx
new file mode 100644
index 0000000..b486f97
--- /dev/null
+++ b/gensplore-component/src/components/ContextMenu.jsx
@@ -0,0 +1,27 @@
+import React from 'react';
+
+const ContextMenu = ({ x, y, onClose, onCopy, onCopyRC }) => {
+ if (x === null || y === null) return null;
+
+ return (
+
+
+
+
+ );
+};
+
+export default ContextMenu;
diff --git a/gensplore-component/src/components/GensploreView.jsx b/gensplore-component/src/components/GensploreView.jsx
index 6f90636..6315ea9 100644
--- a/gensplore-component/src/components/GensploreView.jsx
+++ b/gensplore-component/src/components/GensploreView.jsx
@@ -8,7 +8,8 @@ import React, {
} from "react";
import "../App.css"
-import Offcanvas from './Offcanvas'; // Import the new offcanvas component
+import Offcanvas from './Offcanvas';
+import ContextMenu from './ContextMenu';
import Tooltip from "./Tooltip";
import { getReverseComplement, filterFeatures } from "../utils";
@@ -33,6 +34,7 @@ function GensploreView({ genbankString, searchInput, setSearchInput, showLogo })
const [ref, { width }] = useMeasure();
const [hoveredInfo, setHoveredInfo] = useState(null);
+ const [contextMenu, setContextMenu] = useState({ x: null, y: null });
const [genbankData, setGenbankData] = useState(null);
const [sequenceHits, setSequenceHits] = useState([]);
const [curSeqHitIndex, setCurSeqHitIndex] = useState(0);
@@ -361,7 +363,46 @@ if (hit1 === -1) {
);
}
+ // Handle context menu
+ const handleContextMenu = (e) => {
+ e.preventDefault();
+ if (whereMouseWentDown !== null && whereMouseWentUp !== null) {
+ setContextMenu({ x: e.clientX, y: e.clientY });
+ }
+ };
+
+ const handleCloseContextMenu = () => {
+ setContextMenu({ x: null, y: null });
+ };
+
+ const handleCopySelection = () => {
+ const selStart = Math.min(whereMouseWentDown, whereMouseWentUp);
+ const selEnd = Math.max(whereMouseWentDown, whereMouseWentUp);
+ const selectedText = genbankData.parsedSequence.sequence.substring(selStart, selEnd);
+ navigator.clipboard.writeText(selectedText);
+ toast.success('Copied to clipboard');
+ handleCloseContextMenu();
+ };
+
+ const handleCopyRC = () => {
+ const selStart = Math.min(whereMouseWentDown, whereMouseWentUp);
+ const selEnd = Math.max(whereMouseWentDown, whereMouseWentUp);
+ const selectedText = genbankData.parsedSequence.sequence.substring(selStart, selEnd);
+ const rc = getReverseComplement(selectedText);
+ navigator.clipboard.writeText(rc);
+ toast.success('Copied reverse complement to clipboard');
+ handleCloseContextMenu();
+ };
+
+ useEffect(() => {
+ document.addEventListener('click', handleCloseContextMenu);
+ return () => {
+ document.removeEventListener('click', handleCloseContextMenu);
+ };
+ }, []);
+
return (<>
+
+ {contextMenu.x !== null && (
+
+ )}
{
featureOffcanvasOpen &&