Skip to content

Commit

Permalink
collapsible side panel
Browse files Browse the repository at this point in the history
  • Loading branch information
magland committed Jun 26, 2024
1 parent 17211fc commit c5ee33e
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 8 deletions.
4 changes: 3 additions & 1 deletion gui/src/app/SPAnalysis/SPAnalysisContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ const SPAnalysisContextProvider: FunctionComponent<PropsWithChildren<SPAnalysisC
const savedState = localStorage.getItem('stan-playground-saved-state')
if (!savedState) return
const parsedData = deserializeAnalysisFromLocalStorage(savedState)
update({ type: 'loadLocalStorage', state: parsedData })
if (parsedData) {
update({ type: 'loadLocalStorage', state: parsedData })
}
}, [])
////////////////////////////////////////////////////////////////////////////////////////

Expand Down
41 changes: 36 additions & 5 deletions gui/src/app/pages/HomePage/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Splitter } from "@fi-sci/splitter";
import { FunctionComponent, useCallback, useContext, useEffect, useMemo, useState } from "react";
import { FunctionComponent, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
import DataFileEditor from "../../FileEditor/DataFileEditor";
import StanFileEditor from "../../FileEditor/StanFileEditor";
import RunPanel from "../../RunPanel/RunPanel";
Expand Down Expand Up @@ -30,23 +30,39 @@ const HomePage: FunctionComponent<Props> = ({ width, height }) => {
}

const HomePageChild: FunctionComponent<Props> = ({ width, height }) => {


const { data, update } = useContext(SPAnalysisContext)
const setSamplingOpts = useCallback((opts: SamplingOpts) => {
update({ type: 'setSamplingOpts', opts })
}, [update])

const [compiledMainJsUrl, setCompiledMainJsUrl] = useState<string>('')

const leftPanelWidth = Math.max(250, Math.min(340, width * 0.2))
const [leftPanelCollapsed, setLeftPanelCollapsed] = useState(determineShouldBeInitiallyCollapsed(width))
const expandedLeftPanelWidth = determineLeftPanelWidth(width) // what the width would be if expanded
const leftPanelWidth = leftPanelCollapsed ? 20 : expandedLeftPanelWidth // the actual width

// We automatically collapse the panel if user has resized the window to be
// too small but we only want to do this right when we cross the threshold,
// not every time we resize by a pixel. Similar for expanding the panel when
// we cross the threshold in the other direction.
const lastWidth = useRef(width)
useEffect(() => {
if (!determineShouldBeInitiallyCollapsed(lastWidth.current) && determineShouldBeInitiallyCollapsed(width)) {
lastWidth.current = width
setLeftPanelCollapsed(true)
}
else if (determineShouldBeInitiallyCollapsed(lastWidth.current) && !determineShouldBeInitiallyCollapsed(width)) {
lastWidth.current = width
setLeftPanelCollapsed(false)
}
}, [width])

const topBarHeight = 25

useEffect(() => {
document.title = "Stan Playground - " + data.meta.title;
}, [data.meta.title])


return (
<div style={{ position: 'absolute', width, height, overflow: 'hidden' }}>
<div className="top-bar" style={{ position: 'absolute', left: 0, top: 0, width, height: topBarHeight, overflow: 'hidden' }}>
Expand All @@ -58,6 +74,8 @@ const HomePageChild: FunctionComponent<Props> = ({ width, height }) => {
</div>
<div className="left-panel" style={{ position: 'absolute', left: 0, top: topBarHeight + 2, width: leftPanelWidth, height: height - topBarHeight - 2, overflow: 'auto' }}>
<LeftPanel
collapsed={leftPanelCollapsed}
onSetCollapsed={setLeftPanelCollapsed}
width={leftPanelWidth}
height={height - topBarHeight - 2}
hasUnsavedChanges={modelHasUnsavedChanges(data)}
Expand Down Expand Up @@ -100,6 +118,19 @@ const HomePageChild: FunctionComponent<Props> = ({ width, height }) => {
)
}

// the width of the left panel when it is expanded based on the overall width
const determineLeftPanelWidth = (width: number) => {
const minWidth = 250
const maxWidth = 500
return Math.min(maxWidth, Math.max(minWidth, width / 4))
}

// whether the left panel should be collapsed initially based on the overall
// width
const determineShouldBeInitiallyCollapsed = (width: number) => {
return width < 800
}

type RightViewProps = {
width: number
height: number
Expand Down
39 changes: 37 additions & 2 deletions gui/src/app/pages/HomePage/LeftPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import { Hyperlink } from "@fi-sci/misc"
import { Hyperlink, SmallIconButton } from "@fi-sci/misc"
import ModalWindow, { useModalWindow } from "@fi-sci/modal-window"
import { FunctionComponent, useCallback, useContext } from "react"
import examplesStanies, { Stanie } from "../../exampleStanies/exampleStanies"
import { SPAnalysisContext } from "../../SPAnalysis/SPAnalysisContextProvider"
import ExportWindow from "./ExportWindow"
import ImportWindow from "./ImportWindow"
import { ChevronLeft, ChevronRight } from "@mui/icons-material"

type LeftPanelProps = {
width: number
height: number
hasUnsavedChanges: boolean
collapsed: boolean
onSetCollapsed: (collapsed: boolean) => void
}

const LeftPanel: FunctionComponent<LeftPanelProps> = ({ width, height, hasUnsavedChanges }) => {
const LeftPanel: FunctionComponent<LeftPanelProps> = ({ width, height, hasUnsavedChanges, collapsed, onSetCollapsed }) => {
// note: this is close enough to pass in directly if we wish
const { update } = useContext(SPAnalysisContext)

Expand All @@ -22,9 +25,21 @@ const LeftPanel: FunctionComponent<LeftPanelProps> = ({ width, height, hasUnsave

const { visible: exportVisible, handleOpen: exportOpen, handleClose: exportClose } = useModalWindow()
const { visible: importVisible, handleOpen: importOpen, handleClose: importClose } = useModalWindow()

if (collapsed) {
return (
<div style={{position: 'absolute', width, height, backgroundColor: 'lightgray', overflowY: 'auto'}}>
<div style={{margin: 5}}>
<ExpandButton onClick={() => onSetCollapsed(false)} />
</div>
</div>
)
}

return (
<div style={{position: 'absolute', width, height, backgroundColor: 'lightgray', overflowY: 'auto'}}>
<div style={{margin: 5}}>
<CollapseButton onClick={() => onSetCollapsed(true)} />
<h3>Examples</h3>
{
examplesStanies.map((stanie, i) => (
Expand Down Expand Up @@ -85,4 +100,24 @@ const LeftPanel: FunctionComponent<LeftPanelProps> = ({ width, height, hasUnsave
)
}

const ExpandButton: FunctionComponent<{ onClick: () => void }> = ({ onClick }) => {
return (
<SmallIconButton
icon={<ChevronRight />}
onClick={onClick}
title="Expand"
/>
)
}

const CollapseButton: FunctionComponent<{ onClick: () => void }> = ({ onClick }) => {
return (
<SmallIconButton
icon={<ChevronLeft />}
onClick={onClick}
title="Collapse"
/>
)
}

export default LeftPanel

0 comments on commit c5ee33e

Please sign in to comment.