Skip to content

Commit

Permalink
Added plant, cell, and tissue efps with routing
Browse files Browse the repository at this point in the history
  • Loading branch information
Yukthiw committed Oct 31, 2024
1 parent 9e0b738 commit 7378d14
Show file tree
Hide file tree
Showing 22 changed files with 1,394 additions and 472 deletions.
64 changes: 2 additions & 62 deletions Eplant/Eplant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,70 +30,18 @@ export type EplantProps = Record<string, never>
*/
const Eplant = () => {
const [darkMode] = useDarkMode()
const [activeGeneId, setActiveGeneId] = useActiveGeneId()
const [activeViewId, setActiveViewId] = useState<string>('')
const [isCollapse, setIsCollapse] = useSidebarState()
const [activeGeneId, setActiveGeneId] = useActiveGeneId()
const [genes, setGenes] = useGeneticElements()
const theme = useTheme()
const [globalProgress, loaded] = usePageLoad()
const config = useConfig()
const navigate = useNavigate()
const location = useLocation()
const params = useParams()
const [speciesList] = useSpecies()

useEffect(() => {
if (loaded) {
updateColors(theme)
}
}, [theme, loaded])
// On app url change, make sure loaded gene and view aligns with URL
useEffect(() => {
const loadGene = async (geneid: string) => {
// TODO: This is super jank, should probably write some better utilities for loading genes
const species = speciesList.find(
(species) => species.name === 'Arabidopsis'
)
const gene = await species?.api.searchGene(geneid)
if (gene) {
setGenes([...genes, gene])
}
}
if (params.geneid) {
if (params.geneid !== activeGeneId) {
if (!genes.find((gene) => gene.id === params.geneid)) {
loadGene(params.geneid)
}
setActiveGeneId(params.geneid)
}
} else {
// Set active gene to first available if one is already loaded
if (genes.length > 0) {
setActiveGeneId(genes[0].id)
} else {
setActiveGeneId('')
}
}
setActiveViewId(location.pathname.split('/')[1])
}, [location.pathname])

// On active gene change update the gene path segment
useEffect(() => {
if (location.pathname !== import.meta.env.BASE_URL) {
// Only run this after initial redirect
const pathSegments = location.pathname.split('/')
const geneid = activeGeneId ? activeGeneId : ''
if (pathSegments.length == 3) {
pathSegments[pathSegments.length - 1] = geneid
} else if (pathSegments.length == 2) {
pathSegments.push(geneid)
}

const newPath = pathSegments.join('/') + location.search
if (newPath !== location.pathname + location.search) {
navigate(newPath)
}
}
}, [activeGeneId, location.pathname])

return (
<ThemeProvider theme={darkMode ? dark : light}>
Expand Down Expand Up @@ -124,14 +72,6 @@ const Eplant = () => {
{loaded ? (
<ViewContainer
gene={genes.find((gene) => gene.id === activeGeneId) ?? null}
view={
config.views.find(
(view) => view.id === (activeViewId ?? config.defaultView)
) ?? FallbackView
}
setView={(viewid) => {
setActiveViewId(viewid)
}}
sx={{
width: '100%',
height: '100%',
Expand Down
110 changes: 87 additions & 23 deletions Eplant/UI/Layout/ViewContainer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { useEffect, useId, useMemo, useState } from 'react'
import { Outlet, useLocation, useNavigate } from 'react-router-dom'
import { Outlet, useLocation, useNavigate, useParams } from 'react-router-dom'

import { useConfig } from '@eplant/config'
import GeneticElement from '@eplant/GeneticElement'
import { usePrinting } from '@eplant/state'
import {
useActiveGeneId,
useGeneticElements,
usePrinting,
useSpecies,
} from '@eplant/state'
import Modal from '@eplant/UI/Modal'
import downloadFile from '@eplant/util/downloadFile'
import ErrorBoundary from '@eplant/util/ErrorBoundary'
import { useViewData } from '@eplant/View/viewData'
import CellEFP from '@eplant/views/CellEFP'
import FallbackView from '@eplant/views/FallbackView'
import {
AppBar,
Box,
Expand Down Expand Up @@ -40,13 +47,9 @@ import ViewOptions from './ViewOptions'
* @returns
*/
export function ViewContainer<T, S, A>({
view,
setView,
gene,
...props
}: {
view: View<T, S, A>
setView: (viewid: string) => void
gene: GeneticElement | null
} & BoxProps) {
// const { activeData, error, dispatch, state } = useViewData(view, gene)
Expand All @@ -57,10 +60,16 @@ export function ViewContainer<T, S, A>({
const [printing, setPrinting] = usePrinting()

const [viewingCitations, setViewingCitations] = useState(false)
const navigate = useNavigate()
const location = useLocation()

const { userViews, views, genericViews } = useConfig()
const navigate = useNavigate()
const location = useLocation()
const params = useParams()
const [speciesList] = useSpecies()
const [genes, setGenes] = useGeneticElements()
const [activeGeneId, setActiveGeneId] = useActiveGeneId()
const [activeViewId, setActiveViewId] = useState<string>('')
const activeView = views.find((view) => view.id === activeViewId) ?? CellEFP

useEffect(() => {
if (printing) {
Expand All @@ -70,8 +79,61 @@ export function ViewContainer<T, S, A>({
}, 100)
}
}, [printing])
const topBar = useMemo(
() => (

// On app url change, make sure loaded gene and view aligns with URL
useEffect(() => {
const loadGene = async (geneid: string) => {
// TODO: This is super jank, should probably write some better utilities for loading genes
const species = speciesList.find(
(species) => species.name === 'Arabidopsis'
)
const gene = await species?.api.searchGene(geneid)
if (gene) {
setGenes([...genes, gene])
}
}
if (params.geneid) {
if (params.geneid !== activeGeneId) {
if (!genes.find((gene) => gene.id === params.geneid)) {
loadGene(params.geneid)
}
setActiveGeneId(params.geneid)
}
} else {
// Set active gene to first available if one is already loaded
if (genes.length > 0) {
setActiveGeneId(genes[0].id)
} else {
setActiveGeneId('')
}
}
setActiveViewId(location.pathname.split('/')[1])
}, [location.pathname])

// On active gene change update the gene path segment
useEffect(() => {
if (location.pathname !== import.meta.env.BASE_URL) {
// Only run this after initial redirect
const pathSegments = location.pathname
.split('/')
.filter((segment) => segment !== '')
if (pathSegments.length == 2 && activeGeneId) {
pathSegments[pathSegments.length - 1] = activeGeneId
} else if (pathSegments.length == 1 && activeGeneId) {
pathSegments.push(activeGeneId)
// Will never get here as of now, but if we decide to persist activeGene need
// to do this.
}

const newPath = '/' + pathSegments.join('/') + '/' + location.search
if (newPath !== location.pathname + '/' + location.search) {
navigate(newPath)
}
}
}, [activeGeneId])

const topBar = useMemo(() => {
return (
<AppBar
variant='elevation'
sx={(theme) => ({
Expand Down Expand Up @@ -102,9 +164,9 @@ export function ViewContainer<T, S, A>({
{/* View selector dropdown */}
<FormControl variant='standard'>
<Select
value={view.id}
value={activeView.id}
renderValue={() => {
if (view.id == 'get-started') {
if (activeView.id == 'get-started') {
return <span style={{ paddingLeft: 8 }}>View selector</span>
}
return (
Expand All @@ -115,9 +177,9 @@ export function ViewContainer<T, S, A>({
}}
>
<Box sx={{ paddingRight: 1.5, marginTop: 0.5 }}>
{view.icon && <view.icon />}
{activeView.icon && <activeView.icon />}
</Box>
{view.name}
{activeView.name}
</span>
)
}}
Expand All @@ -131,7 +193,7 @@ export function ViewContainer<T, S, A>({
pathSegments[1] = view.id
const newPath = pathSegments.join('/')
if (newPath !== location.pathname + location.search) {
setView(view.id)
setActiveViewId(view.id)
navigate(newPath)
}
}
Expand Down Expand Up @@ -196,7 +258,7 @@ export function ViewContainer<T, S, A>({
}}
key={view.name}
onClick={(e) => {
if (view) setView(view.id)
if (view) setActiveViewId(view.id)
}}
>
{view.name}
Expand Down Expand Up @@ -236,7 +298,7 @@ export function ViewContainer<T, S, A>({
color='secondary'
onClick={() => {
// downloadFile(
// `${view.id}${gene ? '-' + gene.id : ''}.json`,
// `${activeView.id}${gene ? '-' + gene.id : ''}.json`,
// JSON.stringify(activeData, null, 2)
// )
}}
Expand All @@ -245,17 +307,19 @@ export function ViewContainer<T, S, A>({
</Button>
</Toolbar>
</AppBar>
),
[view.id, gene?.id, loading]
)
)
}, [activeViewId, gene?.id, loading])

return (
<Box {...props} display='flex' flexDirection='column'>
<Modal open={viewingCitations} onClose={() => setViewingCitations(false)}>
<DialogTitle sx={{ minWidth: '512px' }}>
<Typography variant='h6'>Data sources for {view.name}</Typography>
<Typography variant='h6'>
Data sources for {activeView.name}
</Typography>
</DialogTitle>
<DialogContent>
{view.citation ? (
{activeView.citation ? (
// <view.citation state={state} activeData={activeData} gene={gene} />
<div></div>
) : (
Expand Down Expand Up @@ -301,7 +365,7 @@ export function ViewContainer<T, S, A>({
<LoadingPage
loadingAmount={loadAmount}
gene={gene}
view={view}
view={activeView}
error={null}
/>
) : (
Expand Down
4 changes: 2 additions & 2 deletions Eplant/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ const userViews = [
// GeneInfoView,
PublicationViewer,
// DebugView,
// PlantEFP,
PlantEFP,
CellEFP,
// ExperimentEFP,
ExperimentEFP,
ChromosomeViewerObject,
]

Expand Down
18 changes: 14 additions & 4 deletions Eplant/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import ErrorBoundary from './util/ErrorBoundary'
import { CellEFPView } from './views/CellEFP/CellEFP'
import { ChromosomeView } from './views/ChromosomeViewer/ChromosomeView'
import { ExperimentEFP } from './views/ExperimentEFP/ExperimentEFP'
import { PlantEFP } from './views/PlantEFP/PlantEFP'
import { PublicationsView } from './views/PublicationViewer/PublicationsView'
import { Config, defaultConfig } from './config'
import Eplant from './Eplant'
Expand All @@ -21,21 +23,29 @@ const router = createBrowserRouter([
element: <Eplant />,
children: [
{
element: <Navigate to={'/cell-efp'} replace={true}></Navigate>,
element: <Navigate to={'cell-efp/'} replace={true}></Navigate>,
index: true,
},
{
path: '/cell-efp/:geneid?',
path: 'cell-efp/:geneid?',
element: <CellEFPView></CellEFPView>,
},
{
path: '/publications/:geneid?',
path: 'publications/:geneid?',
element: <PublicationsView></PublicationsView>,
},
{
path: '/chromosome/:geneid?',
path: 'chromosome/:geneid?',
element: <ChromosomeView></ChromosomeView>,
},
{
path: 'plant-efp/:geneid?',
element: <PlantEFP></PlantEFP>,
},
{
path: 'tissue/:geneid?',
element: <ExperimentEFP></ExperimentEFP>,
},
],
errorElement: <ErrorBoundary></ErrorBoundary>,
},
Expand Down
50 changes: 50 additions & 0 deletions Eplant/util/stateUtils/ActionsPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Box, Button, Grid, Tooltip } from '@mui/material'

import { StateActions } from '.'

interface ActionsPanelProps<T> {
actions: StateActions<T>
prevState: T
setState: (newState: T) => void
}

export const ActionsPanel = <T,>({
actions,
prevState,
setState,
}: ActionsPanelProps<T>) => {
const actionButtons = Object.entries(actions)
.filter(([_, action]) => action.rendered)
.map(([key, action]) => {
if (action.rendered) {
return (
<Tooltip key={key} title={key}>
<Button
startIcon={action.icon}
onClick={() => setState(action.mutation(prevState))}
></Button>
</Tooltip>
)
}
return null // Should never get here but need this to make TS compiler happy
})

const rows = []
for (let i = 0; i < actionButtons.length; i += 5) {
rows.push(actionButtons.slice(i, i + 5))
}

return (
<Box>
{rows.map((row, index) => (
<Grid container spacing={2} key={index}>
{row.map((button) => (
<Grid item key={button?.key}>
{button}
</Grid>
))}
</Grid>
))}
</Box>
)
}
Loading

0 comments on commit 7378d14

Please sign in to comment.