From d5fe8b1804a7b01e193b119a45e1d89c172a90e8 Mon Sep 17 00:00:00 2001 From: Theo Sanderson Date: Wed, 15 Feb 2023 20:53:29 +0000 Subject: [PATCH] update --- package.json | 1 + src/App.js | 103 +++++++++++++++++++++++++++++++++++++++++++++------ yarn.lock | 12 ++++++ 3 files changed, 104 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 6d785b4..b1c2290 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "react-router-dom": "^6.8.1", "react-scripts": "5.0.1", "react-spinners": "^0.13.8", + "react-toastify": "^9.1.1", "react-tooltip": "^5.7.5", "react-use": "^17.4.0", "tailwindcss": "^3.2.6", diff --git a/src/App.js b/src/App.js index 5c9c305..8db5a14 100644 --- a/src/App.js +++ b/src/App.js @@ -13,10 +13,32 @@ import {AiOutlineZoomIn,AiOutlineZoomOut} from 'react-icons/ai'; import {GiDna1} from 'react-icons/gi'; import ColorHash from 'color-hash'; import { useNavigate, useLocation } from "react-router-dom" +import { ToastContainer, toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; import qs from "qs" +import { U } from "ve-sequence-utils/lib/DNAComplementMap"; var colorHash = new ColorHash({lightness: [0.75, 0.9, 0.7,0.8]}); + +const filterFeatures = (features, search) => { + return features.filter((feature) => { + // if feature name contains search string + if(feature.name.toLowerCase().includes(search.toLowerCase())){ + return true; + } + const product = feature.notes && feature.notes.product ? feature.notes.product[0] : ""; + if(product.toLowerCase().includes(search.toLowerCase())){ + return true; + } + const locus_tag = feature.notes && feature.notes.locus_tag ? feature.notes.locus_tag[0] : ""; + if(locus_tag.toLowerCase().includes(search.toLowerCase())){ + return true; + } + return false; + }); +} + const useQueryState = query => { const location = useLocation() const navigate = useNavigate() @@ -247,7 +269,7 @@ const getReverseComplement = (sequence) => { return sequence.split("").map((base) => baseToComplement[base]).reverse().join(""); } -const SingleRow = ({ parsedSequence, rowStart, rowEnd, setHoveredInfo, rowId, searchInput , zoomLevel, whereMouseWentDown, setWhereMouseWentDown, +const SingleRow = ({ parsedSequence, rowStart, rowEnd, setHoveredInfo, rowId, intSearchInput,annotSearchInput , zoomLevel, whereMouseWentDown, setWhereMouseWentDown, whereMouseWentUp, setWhereMouseWentUp, whereMouseCurrentlyIs,setWhereMouseCurrentlyIs}) => { @@ -257,7 +279,9 @@ whereMouseCurrentlyIs,setWhereMouseCurrentlyIs}) => { const sep = 10 * zoomFactor; - const isSelected = searchInput>=rowStart && searchInput<=rowEnd; + + + @@ -276,6 +300,11 @@ whereMouseCurrentlyIs,setWhereMouseCurrentlyIs}) => { (feature.start <= rowStart && feature.end >= rowEnd)) ) ); + + const searchFeatures = !annotSearchInput ? [] : filterFeatures(relevantFeatures,annotSearchInput) + + const isSelected = (intSearchInput>=rowStart && intSearchInput<=rowEnd) || searchFeatures.length>0; + if (rowStart==0){ //console.log(relevantFeatures); } @@ -521,17 +550,17 @@ const codonZoomThreshold = -2 // create a tick specifically at searchInput let searchTick = null; - if (searchInput != null && searchInput >= rowStart && searchInput <= rowEnd) { + if (intSearchInput != null && intSearchInput >= rowStart && intSearchInput <= rowEnd) { searchTick = ( - + {//rect behind tick } - - + - {searchInput+1} + {intSearchInput+1} ); @@ -634,23 +663,49 @@ const codonZoomThreshold = -2 ); }; -function SearchPanel({ searchPanelOpen,setSearchPanelOpen,searchInput,setSearchInput }) { +function SearchPanel({ searchPanelOpen,setSearchPanelOpen,searchInput,setSearchInput,searchType, setSearchType }) { const handleInputChange = (event) => { setSearchInput(event.target.value); + // if event.target.value has non-numeric characters, then set searchType to annot + if (event.target.value.match(/[^0-9]/)) { + setSearchType("annot"); + console.log("setting searchType to annot"); + } }; + + const searchOption = [ + { value: "nuc", label: "nucleotide" }, + { value: "annot", label: "annotation" }, + ]; return (
{searchPanelOpen ? ((<> + + + + { if(!intSearchInput) return; @@ -929,6 +986,24 @@ function GensploreView({genbankString, searchInput, setSearchInput}) { }, [intSearchInput, rowWidth]); + useEffect(() => { + if(!annotSearchInput) return; + const strippedAnnotInput = annotSearchInput.replace(/\s/g, ""); + if (strippedAnnotInput === "") return; + // search the features for one that matches + const matchingFeatures = filterFeatures(genbankData.parsedSequence.features, strippedAnnotInput); + if(matchingFeatures.length === 0){ + toast.error("No matching features found"); + return; + } + const firstMatchingFeature = matchingFeatures[0]; + const row = Math.floor(firstMatchingFeature.start / rowWidth); + rowVirtualizer.scrollToIndex(row+1, {align:"center"}); + setLastSearch(annotSearchInput); + }, [annotSearchInput]); + + + //console.log("virtualItems", virtualItems); @@ -953,6 +1028,7 @@ function GensploreView({genbankString, searchInput, setSearchInput}) { return (
+ {true && (
@@ -1036,7 +1114,8 @@ function GensploreView({genbankString, searchInput, setSearchInput}) { rowWidth={rowWidth} setHoveredInfo={setHoveredInfo} rowId={virtualitem.index} - searchInput={intSearchInput-1} + intSearchInput={intSearchInput-1} + annotSearchInput={annotSearchInput} renderProperly={true} zoomLevel={zoomLevel} diff --git a/yarn.lock b/yarn.lock index b505b49..aff83ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3154,6 +3154,11 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +clsx@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -7800,6 +7805,13 @@ react-spinners@^0.13.8: resolved "https://registry.yarnpkg.com/react-spinners/-/react-spinners-0.13.8.tgz#5262571be0f745d86bbd49a1e6b49f9f9cb19acc" integrity sha512-3e+k56lUkPj0vb5NDXPVFAOkPC//XyhKPJjvcGjyMNPWsBKpplfeyialP74G7H7+It7KzhtET+MvGqbKgAqpZA== +react-toastify@^9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-9.1.1.tgz#9280caea4a13dc1739c350d90660a630807bf10b" + integrity sha512-pkFCla1z3ve045qvjEmn2xOJOy4ZciwRXm1oMPULVkELi5aJdHCN/FHnuqXq8IwGDLB7PPk2/J6uP9D8ejuiRw== + dependencies: + clsx "^1.1.1" + react-tooltip@^5.7.5: version "5.7.5" resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-5.7.5.tgz#f3487e000a24b6ff84c8181064809a57b43c2e99"