Skip to content

Commit

Permalink
sequence search
Browse files Browse the repository at this point in the history
  • Loading branch information
theosanderson committed Jul 28, 2023
1 parent 278f141 commit a49157f
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 4 deletions.
100 changes: 96 additions & 4 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { useWindowVirtualizer } from "@tanstack/react-virtual";
import Slider, { Range } from "rc-slider";
import { AiOutlineZoomIn, AiOutlineZoomOut } from "react-icons/ai";
import { GiDna1 } from "react-icons/gi";

import {BsArrowRightCircleFill, BsArrowLeftCircleFill} from "react-icons/bs";
import { ToastContainer, toast } from "react-toastify";
import { useDebounce, useQueryState } from "./hooks";
import "react-toastify/dist/ReactToastify.css";
Expand All @@ -33,23 +33,37 @@ function SearchPanel({
setSearchInput,
searchType,
setSearchType,
curSeqHitIndex,
sequenceHits,
setCurSeqHitIndex
}) {
const handleInputChange = (event) => {
setCurSeqHitIndex(0);
setSearchInput(event.target.value);
// if event.target.value has only ACGT characters, then set searchType to sequence
// if event.target.value has non-numeric characters, then set searchType to annot
if (event.target.value.match(/[^0-9]/)) {
// if event.target.value has only numeric characters, then set searchType to nuc
if (/^[0-9]+$/.test(event.target.value)) {
setSearchType("nuc");
}
else if (/^[ACGTacgt]+$/.test(event.target.value)) {
setSearchType("sequence");
}
else if (/^[^0-9]+$/.test(event.target.value)) {
setSearchType("annot");
console.log("setting searchType to annot");
}

};

const searchOption = [
{ value: "nuc", label: "nucleotide" },
{ value: "annot", label: "annotation" },
{ value: "sequence", label: "sequence"}
];

return (
<div className="bg-white p-1 text-sm shadow rounded flex items-center">
<div className="bg-white p-1 text-sm shadow rounded ">
<div className="flex items-center">
{searchPanelOpen ? (
<>
<select
Expand Down Expand Up @@ -94,6 +108,38 @@ function SearchPanel({
<FaSearch className="mr-2" />
</button>
)}
</div>
{
searchType === "sequence" && (
<div className="my-2 text-gray-800 text-center px-3">
{sequenceHits.length > 0 && (
<>
<button>
<BsArrowLeftCircleFill onClick={() => setCurSeqHitIndex((x) => x==0? sequenceHits.length-1: x-1)}
className="mx-3 text-gray-400" />
</button>




Hit {curSeqHitIndex + 1} of {sequenceHits.length}


<button>
<BsArrowRightCircleFill onClick={() => setCurSeqHitIndex((x) => x==sequenceHits.length-1? 0: x+1)}
className="mx-3 text-gray-400" />
</button>
</>
)}
{sequenceHits.length === 0 && (
<>
No hits found
</>
)}
</div>
)

}
</div>
);
}
Expand Down Expand Up @@ -139,10 +185,13 @@ function GensploreView({ genbankString, searchInput, setSearchInput }) {

const [hoveredInfo, setHoveredInfo] = useState(null);
const [genbankData, setGenbankData] = useState(null);
const [sequenceHits, setSequenceHits] = useState([]);
const [curSeqHitIndex, setCurSeqHitIndex] = useState(0);

// safely convert searchInput to int
const intSearchInput = searchType === "nuc" ? parseInt(searchInput) : null;
const annotSearchInput = searchType === "annot" ? searchInput : null;
const sequenceSearchInput = searchType === "sequence" ? searchInput.toUpperCase() : null;

const [whereOnPage, setWhereOnPage] = useState(0);

Expand Down Expand Up @@ -351,6 +400,44 @@ function GensploreView({ genbankString, searchInput, setSearchInput }) {
setLastSearch(annotSearchInput);
}, [annotSearchInput]);


useEffect(() => {
if(!sequenceSearchInput) {
setSequenceHits([]);
return;
}
const strippedSequenceInput = sequenceSearchInput.replace(/\s/g, "");
if (strippedSequenceInput === ""){
setSequenceHits([]);
return;
}
console.log("strippedSequenceInput", strippedSequenceInput);
const matchingSequence = fullSequence.indexOf(strippedSequenceInput);

if(matchingSequence === -1) {
//toast.error("No matching sequence found");
setSequenceHits([]);

return;
}
// we want to find all locations that match and store them with setSequenceHits as [start,end]
const seqHits = [];
let start = 0;
while (true) {
const hit = fullSequence.indexOf(strippedSequenceInput, start);
if (hit === -1) break;
seqHits.push([hit, hit + strippedSequenceInput.length]);
start = hit + 1;
}
setSequenceHits(seqHits);

const row = Math.floor(seqHits[curSeqHitIndex][0] / rowWidth);
console.log("row", row);
rowVirtualizer.scrollToIndex(row + 1, { align: "center" });
setLastSearch(sequenceSearchInput);
}, [sequenceSearchInput, curSeqHitIndex]);


//console.log("virtualItems", virtualItems);

if (!genbankData) {
Expand All @@ -377,6 +464,9 @@ function GensploreView({ genbankString, searchInput, setSearchInput }) {
setSearchPanelOpen={setSearchPanelOpen}
searchType={searchType}
setSearchType={setSearchType}
curSeqHitIndex={curSeqHitIndex}
setCurSeqHitIndex={setCurSeqHitIndex}
sequenceHits={sequenceHits}
/>
</div>
)}
Expand Down Expand Up @@ -459,6 +549,8 @@ function GensploreView({ genbankString, searchInput, setSearchInput }) {
setWhereMouseWentUp={setWhereMouseWentUp}
whereMouseCurrentlyIs={whereMouseCurrentlyIs}
setWhereMouseCurrentlyIs={setWhereMouseCurrentlyIs}
sequenceHits={sequenceHits}
curSeqHitIndex={curSeqHitIndex}
/>
</div>
);
Expand Down
41 changes: 41 additions & 0 deletions src/components/SingleRow.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ const SingleRow = ({
setWhereMouseWentUp,
whereMouseCurrentlyIs,
setWhereMouseCurrentlyIs,
sequenceHits,
curSeqHitIndex
}) => {
const zoomFactor = 2 ** zoomLevel;
const sep = 10 * zoomFactor;
Expand Down Expand Up @@ -601,6 +603,41 @@ const SingleRow = ({
);
}

let sequenceHitRects = null;

if(sequenceHits.length > 0){
sequenceHitRects = [];
for(let i = 0; i < sequenceHits.length; i++){
const hit = sequenceHits[i];
let [start, end] = hit;
// if the hit is outside the current view, skip it
if(start > rowEnd || end < rowStart){
continue;
}
// if the hit is partially outside the current view, clip it
if(start < rowStart){
start = rowStart;
}
if(end > rowEnd){
end = rowEnd;
}
sequenceHitRects.push(
<rect
x={extraPadding + (start - rowStart - 0.5) * sep}
y={0}
width={(end - start) * sep}
height={height}
fill={i==curSeqHitIndex? "#ff8888":"#ffbbbb"}
fillOpacity={0.5}
/>
);
}
}





// Concatenate sequence characters and ticks with SVG
return (
<div
Expand Down Expand Up @@ -649,6 +686,9 @@ const SingleRow = ({
style={{ position: "absolute", top: 0, left: 0 }}
>
{selectionRect}
<g>
{sequenceHitRects}
</g>
<g fillOpacity={0.7}>
<g
transform={`translate(${extraPadding}, ${height - 40})`}
Expand All @@ -675,6 +715,7 @@ const SingleRow = ({

<g transform={`translate(${extraPadding}, ${height - 55})`}>{chars}</g>
<g transform={`translate(${extraPadding}, 5)`}>{featureBlocksSVG}</g>

</svg>
</div>
);
Expand Down

0 comments on commit a49157f

Please sign in to comment.