Skip to content

Commit

Permalink
add feature panel
Browse files Browse the repository at this point in the history
  • Loading branch information
theosanderson committed Nov 24, 2023
1 parent bb8db1e commit 42d9e22
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 14 deletions.
84 changes: 82 additions & 2 deletions src/components/GensploreView.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import React, {
useCallback,
} from "react";


import Offcanvas from './Offcanvas'; // Import the new offcanvas component

import Tooltip from "./Tooltip";
import { getReverseComplement, filterFeatures } from "../utils";
import SingleRow from "./SingleRow";
Expand All @@ -18,7 +21,7 @@ import { useWindowVirtualizer } from "@tanstack/react-virtual";
import { ToastContainer, toast } from "react-toastify";
import SearchPanel from "../SearchPanel";
import { GiDna1 } from "react-icons/gi";
import { MdSettings } from "react-icons/md";


function GensploreView({ genbankString, searchInput, setSearchInput }) {
const [searchPanelOpen, setSearchPanelOpen] = useState(false);
Expand Down Expand Up @@ -292,6 +295,37 @@ function GensploreView({ genbankString, searchInput, setSearchInput }) {
setLastSearch(sequenceSearchInput);
}, [sequenceSearchInput, curSeqHitIndex,includeRC]);

const [featureOffcanvasOpen, setFeatureOffcanvasOpen] = useState(false);
const [featureVisibility, setFeatureVisibility] = useState({});
const visibleFeatures = useMemo(() => {
if (!genbankData) return [];
const visibleFeatures = [];
genbankData.parsedSequence.features.forEach((feature, i) => {
if (featureVisibility[i]) {
visibleFeatures.push(feature);
}
});
return visibleFeatures;
}, [featureVisibility, genbankData]);



useEffect(() => {
if (!genbankData) return;
const newFeatureVisibility = {};
/*
feature.type !== "source" &&
feature.type !== "gene" &&
feature.type !== "mRNA" &&
*/
genbankData.parsedSequence.features.forEach((feature, i) => {
newFeatureVisibility[i] = feature.type !== "source" && feature.type !== "gene" && feature.type !== "mRNA"
}
);
setFeatureVisibility(newFeatureVisibility);
}, [genbankData]);



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

Expand Down Expand Up @@ -374,7 +408,8 @@ function GensploreView({ genbankString, searchInput, setSearchInput }) {
)}

<div className="fixed bottom-0 right-0 z-10 w-72 h-12 p-2 rounded shadow bg-white">
<SettingsPanel zoomLevel={zoomLevel} setZoomLevel={setZoomLevel} configModalOpen={configModalOpen} setConfigModalOpen={setConfigModalOpen} />
<SettingsPanel zoomLevel={zoomLevel} setZoomLevel={setZoomLevel} configModalOpen={configModalOpen} setConfigModalOpen={setConfigModalOpen}
setFeatureOffcanvasOpen={setFeatureOffcanvasOpen} />
</div>

<div className="w-full">
Expand Down Expand Up @@ -436,6 +471,7 @@ function GensploreView({ genbankString, searchInput, setSearchInput }) {
<SingleRow
key={virtualitem.index}
parsedSequence={genbankData.parsedSequence}
visibleFeatures={visibleFeatures}
rowStart={row.rowStart}
rowEnd={row.rowEnd}
rowWidth={rowWidth}
Expand Down Expand Up @@ -465,6 +501,50 @@ function GensploreView({ genbankString, searchInput, setSearchInput }) {
)}
</div>
</div>



<Offcanvas isOpen={featureOffcanvasOpen} onClose={() => setFeatureOffcanvasOpen(false)}>
<table className="min-w-full divide-y divide-gray-200">
<thead>
<tr>

<th className="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Feature
</th>
<th className="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Type
</th>


</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">

{genbankData?.parsedSequence.features.map((feature, index) => (
<tr key={index}>

<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<input
type="checkbox"
className="focus:ring-indigo-500 text-indigo-600 border-gray-300 rounded mr-2"
checked={!!featureVisibility[index]}
onChange={() => {
const newFeatureVisibility = { ...featureVisibility };
newFeatureVisibility[index] = !newFeatureVisibility[index];
setFeatureVisibility(newFeatureVisibility);
}
}
/>
{feature.name}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{feature.type}</td>


</tr>
))}
</tbody>
</table>
</Offcanvas>
</>
);
}
Expand Down
35 changes: 35 additions & 0 deletions src/components/Offcanvas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react';

const Offcanvas = ({ isOpen, onClose, children }) => {
return (
<div className={`${isOpen ? 'fixed' : 'hidden'} inset-0 overflow-hidden`}
style={
{
zIndex: 20
}
}>
<div className="absolute inset-0 overflow-hidden">
{/* Background overlay */}
<div className="absolute inset-0 bg-gray-500 bg-opacity-50 transition-opacity" onClick={onClose}></div>

{/* Offcanvas Panel from Bottom */}
<section className="absolute bottom-0 w-full"

>
{/* Offcanvas Panel */}
<div className="relative w-full mx-auto" >
<div className="flex flex-col h-96 bg-white shadow-xl overflow-y-scroll">

<div className="mt-6 relative flex-1 px-4 sm:px-6">
{/* Place your content here */}
{children}
</div>
</div>
</div>
</section>
</div>
</div>
);
};

export default Offcanvas;
8 changes: 6 additions & 2 deletions src/components/SettingsPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import Slider from 'rc-slider';
import "rc-slider/assets/index.css";
import { AiOutlineZoomIn, AiOutlineZoomOut } from 'react-icons/ai';
import { MdSettings } from 'react-icons/md';
import { PiTagChevronFill } from "react-icons/pi";

const SettingsPanel = ({ zoomLevel, setZoomLevel, configModalOpen, setConfigModalOpen }) => {
const SettingsPanel = ({ zoomLevel, setZoomLevel, configModalOpen, setConfigModalOpen, setFeatureOffcanvasOpen }) => {
return (
<>
<button className="inline-block mr-4 text-gray-400" onClick={() => setConfigModalOpen(true)}>
<button className="inline-block mr-4 text-gray-400 hover:text-gray-600" onClick={() => setFeatureOffcanvasOpen(true)}>
<PiTagChevronFill className="inline-block" />
</button>
<button className="inline-block mr-4 text-gray-400 hover:text-gray-600" onClick={() => setConfigModalOpen(true)}>
<MdSettings className="inline-block" />
</button>
<button
Expand Down
11 changes: 4 additions & 7 deletions src/components/SingleRow.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,17 @@ const SingleRow = ({
setWhereMouseCurrentlyIs,
sequenceHits,
curSeqHitIndex,
enableRC
enableRC,
visibleFeatures
}) => {
const zoomFactor = 2 ** zoomLevel;
const sep = 10 * zoomFactor;

const fullSequence = parsedSequence.sequence;
const rowSequence = fullSequence.slice(rowStart, rowEnd);

const relevantFeatures = parsedSequence.features.filter(
(feature) =>
feature.type !== "source" &&
feature.type !== "gene" &&
feature.type !== "mRNA" &&
((feature.start >= rowStart && feature.start <= rowEnd) ||
const relevantFeatures = visibleFeatures.filter(
(feature) => ((feature.start >= rowStart && feature.start <= rowEnd) ||
(feature.end >= rowStart && feature.end <= rowEnd) ||
(feature.start <= rowStart && feature.end >= rowEnd))
);
Expand Down
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7718,9 +7718,9 @@ react-error-overlay@^6.0.11:
integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==

react-icons@^4.7.1:
version "4.7.1"
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.7.1.tgz#0f4b25a5694e6972677cb189d2a72eabea7a8345"
integrity sha512-yHd3oKGMgm7zxo3EA7H2n7vxSoiGmHk5t6Ou4bXsfcgWyhfDKMpyKfhHR6Bjnn63c+YXBLBPUql9H4wPJM6sXw==
version "4.12.0"
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.12.0.tgz#54806159a966961bfd5cdb26e492f4dafd6a8d78"
integrity sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==

react-is@^16.12.0, react-is@^16.13.1:
version "16.13.1"
Expand Down

1 comment on commit 42d9e22

@vercel
Copy link

@vercel vercel bot commented on 42d9e22 Nov 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.