diff --git a/package.json b/package.json
index fb55b85..31aa010 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
+ "@tanstack/react-virtual": "^3.0.0-beta.48",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
diff --git a/src/App.js b/src/App.js
index c52a2fe..e762ef8 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from "react";
+import React, { useState, useEffect, useRef, useMemo, useLayoutEffect } from "react";
import './App.css';
@@ -6,7 +6,7 @@ import { genbankToJson } from "bio-parsers";
import { useMeasure } from 'react-use'; // or just 'react-use-measure'
import {FaSearch,FaTimes} from 'react-icons/fa';
import {DebounceInput} from 'react-debounce-input';
-
+import { useVirtualizer, useWindowVirtualizer} from '@tanstack/react-virtual';
const Tooltip = ({ hoveredInfo }) => {
const [tooltipPosition, setTooltipPosition] = useState({ x: 0, y: 0 });
@@ -193,12 +193,8 @@ const codonToAminoAcid = (codon) => {
}
};
-const SingleRow = ({ parsedSequence, rowStart, rowEnd, setHoveredInfo, rowId, searchInput, renderProperly }) => {
- if (!renderProperly)
- {
- return
- }
+const SingleRow = ({ parsedSequence, rowStart, rowEnd, setHoveredInfo, rowId, searchInput }) => {
+
const isSelected = searchInput>=rowStart && searchInput<=rowEnd;
if(isSelected){
@@ -209,6 +205,7 @@ const SingleRow = ({ parsedSequence, rowStart, rowEnd, setHoveredInfo, rowId, se
const fullSequence = parsedSequence.sequence;
const rowSequence = fullSequence.slice(rowStart, rowEnd);
+
const relevantFeatures = parsedSequence.features.filter(
(feature) => (
feature.type !== "source" &&
@@ -393,7 +390,7 @@ const SingleRow = ({ parsedSequence, rowStart, rowEnd, setHoveredInfo, rowId, se
>
{codon.aminoAcid}
-
+
{codon.codonIndex+1}
@@ -547,7 +544,7 @@ function App() {
};
}, []);
const [ref, { width }] = useMeasure();
- //console.log("width", width);
+
const [hoveredInfo, setHoveredInfo] = useState(null);
const [genbankData, setGenbankData] = useState(null);
const [searchInput, setSearchInput] = useState(null);
@@ -614,41 +611,76 @@ function App() {
if (rowWidth < 40) {
rowWidth = 40;
}
+ //console.log("rowWidth", rowWidth);
+
+
+
+ let fullSequence, sequenceLength;
+ if(genbankData){
+ fullSequence = genbankData.parsedSequence.sequence;
+ sequenceLength = fullSequence.length;
+ }
+
+
+ const rowData = useMemo(() => {
+ if (!fullSequence) return [];
+ const rowData = [];
+
+
+
+ for (let i = 0; i < sequenceLength; i += rowWidth) {
+
+ rowData.push({
+ rowStart: i,
+ rowEnd: (i + rowWidth > sequenceLength ? sequenceLength : i + rowWidth)
+ });
+ }
+ return rowData;
+}
+, [fullSequence, rowWidth,sequenceLength]);
+
+
+ const parentRef = useRef(null);
+ const parentOffsetRef = useRef(0);
+
+ useLayoutEffect(() => {
+ parentOffsetRef.current = parentRef.current?.offsetTop ?? 0
+ }, [])
+
+
+
+
+ const rowVirtualizer = useWindowVirtualizer({
+ count: rowData.length,
+ estimateSize: () => 60,
+ scrollMargin: parentOffsetRef.current,
+ })
+
+
+
+ const virtualItems = rowVirtualizer.getVirtualItems();
- // useEffect
useEffect(() => {
if(!intSearchInput) return;
const row = Math.floor(intSearchInput / rowWidth);
- console.log("row", row);
+ rowVirtualizer.scrollToIndex(row, {align:"center",
+ smoothScroll:true});
- setTimeout(() => {
- const rowElement = document.getElementById(`row-${row}`);
- if (!rowElement) return;
- rowElement.scrollIntoView({ behavior: "smooth", block: "center" });
- }, 100);
- setTimeout(() => {
- const rowElement = document.getElementById(`row-${row}`);
- if (!rowElement) return;
- rowElement.scrollIntoView({ behavior: "smooth", block: "center" });
- }, 1000);
}, [intSearchInput]);
+
+ //console.log("virtualItems", virtualItems);
+
if (!genbankData ) {
return Loading...
;
}
- const rowData = [];
- const fullSequence = genbankData.parsedSequence.sequence;
- const sequenceLength = fullSequence.length;
- for (let i = 0; i < sequenceLength; i += rowWidth) {
- rowData.push({
- rowStart: i,
- rowEnd: (i + rowWidth > sequenceLength ? sequenceLength : i + rowWidth)
- });
- }
+
+
+
if(!width){
return (
@@ -676,10 +708,10 @@ function App() {
-
+
{genbankData && (
-
+
{genbankData.parsedSequence.name}
@@ -689,18 +721,47 @@ function App() {
-
- {rowData.map((row, index) => (
-
+
+
+
+ {virtualItems.map((virtualitem) => {
+ const row = rowData[virtualitem.index];
+ //return (
{genbankData.parsedSequence.sequence.slice(row.start,row.end)}
)
+ return (
+
+ =intSearchInput && row.rowEnd<=intSearchInput)}
+ renderProperly={true}
/>
- ))}
+
)
+ }
+ )
+}
+
+
diff --git a/yarn.lock b/yarn.lock
index 1c880a8..d9ca71f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1771,6 +1771,18 @@
"@svgr/plugin-svgo" "^5.5.0"
loader-utils "^2.0.0"
+"@tanstack/react-virtual@^3.0.0-beta.48":
+ version "3.0.0-beta.48"
+ resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.0.0-beta.48.tgz#c5055f78b84bf5cb37ee3ca612edf3b19ddf7e10"
+ integrity sha512-zNNEtCF5clBLhqvUZPfmGaOiWrqdgIc7IzrT/HiAKFkFTF4R30b/mfY4+wCJ9tW95Tvs1Ml4THUA3s/vIHMHQg==
+ dependencies:
+ "@tanstack/virtual-core" "3.0.0-beta.48"
+
+"@tanstack/virtual-core@3.0.0-beta.48":
+ version "3.0.0-beta.48"
+ resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.0.0-beta.48.tgz#bcc5737dc0b9bd3e3c61e75b319c1b5edf30c621"
+ integrity sha512-W57cC4J1rd3fvWfiio1EteZiokZL8/CvDK/wo+C7xkgYrFw4BXr2P3mvo0HFxCDb15KeqyuF2pTpGWN+D1OXIg==
+
"@testing-library/dom@^8.5.0":
version "8.20.0"
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.0.tgz#914aa862cef0f5e89b98cc48e3445c4c921010f6"