Skip to content

Commit

Permalink
Keep tabs mounted (#544)
Browse files Browse the repository at this point in the history
* Keep tabs mounted

Keep tabs mounted so that their cached data does not get removed when switching between tabs. This was don by wrapping components in a div and adding a style which changes the display type to block or non depoending on if it is open.

* Created TabPanel function

* Fix bar plot issues

* Fixed cCRE tabs not opening

Fixed an issue where clicking a cCRE in the browser would not open a new tab, but overwrite a previous one.

* rm unused variable

---------

Co-authored-by: jpfisher72 <[email protected]>
  • Loading branch information
JaiiR320 and jpfisher72 authored Jan 13, 2025
1 parent 2024205 commit a87b282
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 129 deletions.
2 changes: 1 addition & 1 deletion screen2.0/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,4 @@
"typescript-eslint": "^8.18.0"
},
"packageManager": "[email protected]"
}
}
31 changes: 24 additions & 7 deletions screen2.0/src/app/_barPlot/BarPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ const VerticalBarPlot = <T,>({
onBarClicked,
TooltipContents
}: BarPlotProps<T>) => {
const [spaceForLabel, setSpaceForLabel] = useState(200) //this needs to be initialized with zero. Will break useEffect if changed
const [spaceForLabel, setSpaceForLabel] = useState(200)
const [labelSpaceDecided, setLabelSpaceDecided] = useState(false)
// Unique ID needed to not mix up getElementByID calls if multiple charts are in DOM
const [uniqueID] = useState(topAxisLabel + String(Math.random()))
const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = useTooltip<BarData<T>>({});
const requestRef = useRef<number | null>(null);
const tooltipDataRef = useRef<{ top: number; left: number; data: BarData<T> } | null>(null);
Expand All @@ -48,6 +50,8 @@ const VerticalBarPlot = <T,>({
const Portal = VisxPortal as unknown as React.FC<PortalProps>;
const TooltipWithBounds = VisxTooltipWithBounds as unknown as React.FC<TooltipWithBoundsProps>;

const outerSvgRef = useRef<SVGSVGElement>(null)

const handleMouseMove = useCallback((event: React.MouseEvent, barData: BarData<T>) => {
tooltipDataRef.current = {
top: event.pageY,
Expand Down Expand Up @@ -91,19 +95,21 @@ const VerticalBarPlot = <T,>({
range: [0, Math.max(width - spaceForCategory - spaceForLabel, 0)],
}), [data, spaceForLabel, width])


//use prop ref or fallback if ref not passed
const containerWidth = SVGref ? SVGref.current?.clientWidth : outerSvgRef.current?.clientWidth

//This feels really dumb but I couldn't figure out a better way to have the labels not overflow sometimes - JF 11/8/24
//Whenever xScale is adjusted, it checks to see if any of the labels overflow the container, and if so
//it sets the spaceForLabel to be the amount overflowed.
useEffect(() => {
const containerWidth = document.getElementById('outerSVG')?.clientWidth
if (!containerWidth) { return }

let maxOverflow = 0
let minUnderflow: number = null
// let maxOverflowingPoint: [BarData<T>, { textWidth: number, barWidth: number, totalWidth: number, overflow: number }]

data.forEach((d, i) => {
const textElement = document.getElementById(`label-${i}`) as unknown as SVGSVGElement;
const textElement = document.getElementById(`label-${i}-${uniqueID}`) as unknown as SVGSVGElement;

if (textElement) {
const textWidth = textElement.getBBox().width;
Expand Down Expand Up @@ -137,14 +143,25 @@ const VerticalBarPlot = <T,>({
setLabelSpaceDecided(true)
}

}, [data, xScale]);
}, [data, xScale, spaceForLabel, labelSpaceDecided, SVGref, containerWidth, topAxisLabel, uniqueID]);

return (
<div ref={parentRef} style={{position: "relative"}}>
{data.length === 0 ?
<p>No Data To Display</p>
:
<svg ref={SVGref} width={width} height={totalHeight} opacity={(labelSpaceDecided && ParentWidth > 0) ? 1 : 0.3} id={'outerSVG'}>
<svg
//define fallback ref if not passed through props
ref={(node) => {
if (SVGref) {
SVGref.current = node;
}
outerSvgRef.current = node;
}}
width={width}
height={totalHeight}
opacity={(labelSpaceDecided && ParentWidth > 0) ? 1 : 0.3}
>
<Group left={spaceForCategory} top={spaceForTopAxis} >
{/* Top Axis with Label */}
<AxisTop scale={xScale} top={0} label={topAxisLabel} labelProps={{ dy: -5, fontSize: 16, fontFamily: fontFamily }} numTicks={width < 600 ? 4 : undefined} />
Expand Down Expand Up @@ -185,7 +202,7 @@ const VerticalBarPlot = <T,>({
/>
{/* Value label next to the bar */}
<Text
id={`label-${i}`}
id={`label-${i}-${uniqueID}`}
x={barX + barWidth + gapBetweenTextAndBar} // Position label slightly after the end of the bar
y={(barY ?? 0) + barHeight / 2}
dy=".35em" // Vertically align to the middle of the bar
Expand Down
16 changes: 7 additions & 9 deletions screen2.0/src/app/applets/gene-expression/geneexpression.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -601,15 +601,13 @@ export function GeneExpression(props: {
</Grid>
:
dataExperiments ?
// <Grid size={12}>
<VerticalBarPlot
data={plotData}
topAxisLabel={(gene + " Gene Expression in " + assembly + ' - ') + (scale === "linearTPM" ? "Linear TPM" : "Log10(TPM + 1)")}
SVGref={plotRef}
onBarClicked={(x) => window.open("https://www.encodeproject.org/experiments/" + x.metadata.accession, "_blank", "noopener,noreferrer")}
TooltipContents={(bar) => <PlotTooltip {...bar} />}
/>
// </Grid>
<VerticalBarPlot
data={plotData}
topAxisLabel={(gene + " Gene Expression in " + assembly + ' - ') + (scale === "linearTPM" ? "Linear TPM" : "Log10(TPM + 1)")}
SVGref={plotRef}
onBarClicked={(x) => window.open("https://www.encodeproject.org/experiments/" + x.metadata.accession, "_blank", "noopener,noreferrer")}
TooltipContents={(bar) => <PlotTooltip {...bar} />}
/>
:
<Typography variant="h5">
Please Select a Gene
Expand Down
49 changes: 23 additions & 26 deletions screen2.0/src/app/search/_newgbview/browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,39 +75,36 @@ export const Browser = ({ cCREClick, state, dispatch, coordinates, gene, biosamp
const bedMouseOut = () => {
dispatch({ type: BrowserActionType.REMOVE_LAST_HIGHLIGHT })
}
const bedClick = (item: Rect) => {
const bedClick = useMemo(() => (item: Rect) => {
dispatch({ type: BrowserActionType.REMOVE_LAST_HIGHLIGHT })
const { left, right } = linearScale(state.domain, item.start, item.end)
const ccre = { start: left, end: right, color: item.color, name: item.name, score: item.score, chromosome: state.domain.chromosome }
cCREClick(ccre)
}
const initialLoad = useRef(true)
}, [state.domain, state.highlights, cCREClick])

useEffect(() => {
if (initialLoad.current) {
const bigbeds = state.tracks.filter(track => track.trackType === TrackType.BIGBED)
bigbeds.forEach(track => {
dispatch({
type: BrowserActionType.UPDATE_TRACK, id: track.id, track: {
...track,
onMouseOut: bedMouseOut,
onMouseOver: bedMouseOver,
onClick: bedClick,
tooltipContent: (item: Rect) => CCRETooltip({ biosample, assembly: coordinates.assembly, name: item.name })
}
})
const bigbeds = state.tracks.filter(track => track.trackType === TrackType.BIGBED)
bigbeds.forEach(track => {
dispatch({
type: BrowserActionType.UPDATE_TRACK, id: track.id, track: {
...track,
onMouseOut: bedMouseOut,
onMouseOver: bedMouseOver,
onClick: bedClick,
tooltipContent: (item: Rect) => CCRETooltip({ biosample, assembly: coordinates.assembly, name: item.name })
}
})
const transcripts = state.tracks.filter(track => track.trackType === TrackType.TRANSCRIPT)
transcripts.forEach(track => {
dispatch({
type: BrowserActionType.UPDATE_PROPS, id: track.id,
props: {
geneName: gene
}
})
})
const transcripts = state.tracks.filter(track => track.trackType === TrackType.TRANSCRIPT)
transcripts.forEach(track => {
dispatch({
type: BrowserActionType.UPDATE_PROPS, id: track.id,
props: {
geneName: gene
}
})
initialLoad.current = false
}
}, [])
})
}, [cCREClick])

const [loadBiosample, { loading: bloading, data: bdata }] = useLazyQuery(BIOSAMPLE_QUERY, {
fetchPolicy: "cache-and-network",
Expand Down
Loading

0 comments on commit a87b282

Please sign in to comment.