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 (<> +
setConfigModalOpen(false)} @@ -536,6 +577,15 @@ if (hit1 === -1) {
+ {contextMenu.x !== null && ( + + )} { featureOffcanvasOpen &&