diff --git a/gui/package.json b/gui/package.json index 529056a1..a64124b1 100644 --- a/gui/package.json +++ b/gui/package.json @@ -20,11 +20,11 @@ "test-watch": "vitest" }, "dependencies": { + "@devbookhq/splitter": "^1.4.2", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@fi-sci/misc": "^0.0.8", "@fi-sci/modal-window": "^0.0.2", - "@fi-sci/splitter": "^0.0.3", "@monaco-editor/react": "^4.6.0", "@mui/icons-material": "^5.15.17", "@mui/material": "^5.15.17", @@ -37,6 +37,7 @@ "react-dropzone": "^14.2.3", "react-plotly.js": "^2.6.0", "react-router-dom": "^6.17.0", + "react-use-measure": "^2.1.1", "react-visibility-sensor": "^5.1.1", "tinystan": "^0.0.2" }, diff --git a/gui/src/app/App.tsx b/gui/src/app/App.tsx index 44030cda..e816aed4 100644 --- a/gui/src/app/App.tsx +++ b/gui/src/app/App.tsx @@ -1,26 +1,15 @@ -import { useReducer } from "react"; import { BrowserRouter } from "react-router-dom"; -import MainWindow from "./MainWindow"; -import { - CustomStatusBarElementsContext, - customStatusBarElementsReducer, -} from "./StatusBar"; +import ProjectContextProvider from "@SpCore/ProjectContextProvider"; +import HomePage from "@SpPages/HomePage"; function App() { - const [customStatusBarStrings, customStatusBarStringsDispatch] = useReducer( - customStatusBarElementsReducer, - {}, - ); return ( - - - +
+ + + +
); } diff --git a/gui/src/app/CompilationServerConnectionControl/CompilationServerConnectionControl.tsx b/gui/src/app/CompilationServerConnectionControl/CompilationServerConnectionControl.tsx index e0914bad..9c73ff23 100644 --- a/gui/src/app/CompilationServerConnectionControl/CompilationServerConnectionControl.tsx +++ b/gui/src/app/CompilationServerConnectionControl/CompilationServerConnectionControl.tsx @@ -1,6 +1,8 @@ -import { Hyperlink, SmallIconButton } from "@fi-sci/misc"; +import { SmallIconButton } from "@fi-sci/misc"; import { default as ModalWindow, useModalWindow } from "@fi-sci/modal-window"; import { Cancel, Check } from "@mui/icons-material"; +import Button from "@mui/material/Button"; +import Link from "@mui/material/Link"; import { FunctionComponent, useCallback, useEffect, useState } from "react"; type CompilationServerConnectionControlProps = { @@ -47,10 +49,15 @@ const CompilationServerConnectionControl: FunctionComponent< />   - + {isConnected ? "connected to " : "not connected to "} {serverLabel} - +    @@ -127,7 +134,9 @@ const ConfigureCompilationServerDialog: FunctionComponent< Not connected )}   - retry + + retry +
@@ -200,7 +209,7 @@ const ConfigureCompilationServerDialog: FunctionComponent<
)}
- + ); diff --git a/gui/src/app/FileEditor/DataFileEditor.tsx b/gui/src/app/FileEditor/DataFileEditor.tsx index 11535ffd..b83ca6d3 100644 --- a/gui/src/app/FileEditor/DataFileEditor.tsx +++ b/gui/src/app/FileEditor/DataFileEditor.tsx @@ -9,8 +9,6 @@ type Props = { setEditedFileContent: (text: string) => void; onDeleteFile?: () => void; readOnly: boolean; - width: number; - height: number; }; const DataFileEditor: FunctionComponent = ({ @@ -20,8 +18,6 @@ const DataFileEditor: FunctionComponent = ({ editedFileContent, setEditedFileContent, readOnly, - width, - height, }) => { const toolbarItems: ToolbarItem[] = useMemo(() => { const ret: ToolbarItem[] = []; @@ -30,8 +26,6 @@ const DataFileEditor: FunctionComponent = ({ return ( void; }; const StanCompileResultWindow: FunctionComponent = ({ - width, - height, stancErrors, onClose, }) => { let content: any; if (stancErrors.errors && stancErrors.errors.length > 0) { content = ( -
+

Errors

{stancErrors.errors.slice(1).map((error, i) => (
@@ -30,7 +26,7 @@ const StanCompileResultWindow: FunctionComponent = ({ ); } else if (stancErrors.warnings && stancErrors.warnings.length > 0) { content = ( -
+

Warnings

{stancErrors.warnings.map((warning, i) => (
@@ -48,10 +44,8 @@ const StanCompileResultWindow: FunctionComponent = ({ } return ( -
-
- } onClick={onClose} /> -
+
+ } onClick={onClose} /> {content}
); diff --git a/gui/src/app/FileEditor/StanFileEditor.tsx b/gui/src/app/FileEditor/StanFileEditor.tsx index 9cc0b523..0b72a8b2 100644 --- a/gui/src/app/FileEditor/StanFileEditor.tsx +++ b/gui/src/app/FileEditor/StanFileEditor.tsx @@ -1,5 +1,6 @@ -import { Splitter } from "@fi-sci/splitter"; import { AutoFixHigh, Cancel, Settings } from "@mui/icons-material"; +import useMediaQuery from "@mui/material/useMediaQuery"; +import { SplitDirection, Splitter } from "@SpComponents/Splitter"; import StanCompileResultWindow from "@SpComponents/StanCompileResultWindow"; import TextEditor, { ToolbarItem } from "@SpComponents/TextEditor"; import compileStanProgram from "@SpStanc/compileStanProgram"; @@ -21,8 +22,6 @@ type Props = { setEditedFileContent: (text: string) => void; onDeleteFile?: () => void; readOnly: boolean; - width: number; - height: number; setCompiledUrl: (s: string) => void; }; @@ -35,8 +34,6 @@ const StanFileEditor: FunctionComponent = ({ editedFileContent, setEditedFileContent, readOnly, - width, - height, setCompiledUrl, }) => { const { stancErrors, requestFormat } = useStanc( @@ -126,7 +123,7 @@ const StanFileEditor: FunctionComponent = ({ } }, [fileContent, handleCompile, didInitialCompile]); - const showLabelsOnButtons = width > 700; + const showLabelsOnButtons = useMediaQuery("(min-width:600px)"); const [syntaxWindowVisible, setSyntaxWindowVisible] = useState(false); const toolbarItems: ToolbarItem[] = useMemo(() => { @@ -141,7 +138,7 @@ const StanFileEditor: FunctionComponent = ({ color: "darkred", tooltip: "Syntax error in Stan file", onClick: () => { - setSyntaxWindowVisible(true); + setSyntaxWindowVisible((v) => !v); }, }); } else if (hasWarnings && !!editedFileContent) { @@ -152,14 +149,14 @@ const StanFileEditor: FunctionComponent = ({ color: "blue", tooltip: "Syntax warning in Stan file", onClick: () => { - setSyntaxWindowVisible(true); + setSyntaxWindowVisible((v) => !v); }, }); } // auto format if (!readOnly) { - if (editedFileContent !== undefined) { + if (editedFileContent) { ret.push({ type: "button", icon: , @@ -170,7 +167,7 @@ const StanFileEditor: FunctionComponent = ({ }); } } - if (editedFileContent === fileContent) { + if (editedFileContent && editedFileContent === fileContent) { if (compileStatus !== "compiling") { if (validSyntax) { ret.push({ @@ -213,19 +210,28 @@ const StanFileEditor: FunctionComponent = ({ const isCompiling = compileStatus === "compiling"; - const compileResultsHeight = Math.min(300, height / 3); + const messagePaneNeeded = syntaxWindowVisible || !editedFileContent; + + const window = messagePaneNeeded ? ( + editedFileContent ? ( + setSyntaxWindowVisible(false)} + /> + ) : ( +
+ Begin editing or select an example from the left panel +
+ ) + ) : ( + <> + ); + + const initialSizes = messagePaneNeeded ? [60, 40] : [100, 0]; return ( - + = ({ toolbarItems={toolbarItems} codeMarkers={stancErrorsToCodeMarkers(stancErrors)} /> - {editedFileContent ? ( - setSyntaxWindowVisible(false)} - /> - ) : ( -
- Begin editing or select an example from the left panel -
- )} + {window}
); }; diff --git a/gui/src/app/FileEditor/TextEditor.tsx b/gui/src/app/FileEditor/TextEditor.tsx index 87a8fdaf..3a929844 100644 --- a/gui/src/app/FileEditor/TextEditor.tsx +++ b/gui/src/app/FileEditor/TextEditor.tsx @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Hyperlink, SmallIconButton } from "@fi-sci/misc"; +import { SmallIconButton } from "@fi-sci/misc"; import { Editor } from "@monaco-editor/react"; -import { Refresh, Save } from "@mui/icons-material"; +import { Save } from "@mui/icons-material"; +import Link from "@mui/material/Link"; import { highlightJsData } from "@SpComponents/stanLang"; import { CodeMarker } from "@SpStanc/Linting"; import { editor } from "monaco-editor"; @@ -25,11 +26,8 @@ type Props = { language: string; readOnly?: boolean; wordWrap?: boolean; - onReload?: () => void; toolbarItems?: ToolbarItem[]; label: string; - width: number; - height: number; codeMarkers?: CodeMarker[]; }; @@ -56,12 +54,9 @@ const TextEditor: FunctionComponent = ({ onSetEditedText, readOnly, wordWrap, - onReload, toolbarItems, language, label, - width, - height, codeMarkers, }) => { const handleChange = useCallback( @@ -198,21 +193,10 @@ const TextEditor: FunctionComponent = ({ [handleSave, readOnly], ); - const toolbarHeight = 25; return ( -
+
-
+
{label}     {!readOnly && ( @@ -229,42 +213,23 @@ const TextEditor: FunctionComponent = ({     {readOnly && read only}     - {onReload && ( - - - - - - )} -     {toolbarItems && toolbarItems.map((item, i) => ( ))}
-
- -
+ />
); }; @@ -305,23 +270,28 @@ const ToolbarItemComponent: FunctionComponent<{ item: ToolbarItem }> = ({ } else { if (!onClick) { return ( - + {label}    ); } return ( - + {label} - +     ); } } else if (item.type === "text") { return ( - + {item.label}    ); @@ -330,15 +300,6 @@ const ToolbarItemComponent: FunctionComponent<{ item: ToolbarItem }> = ({ } }; -// TODO: Consider whether to change this -const LowerABit: FunctionComponent< - PropsWithChildren<{ numPixels: number }> -> = ({ children, numPixels }) => { - return ( - {children} - ); -}; - const NotSelectable: FunctionComponent = ({ children }) => { return
{children}
; }; diff --git a/gui/src/app/MainWindow.tsx b/gui/src/app/MainWindow.tsx deleted file mode 100644 index a47f4dbc..00000000 --- a/gui/src/app/MainWindow.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useWindowDimensions } from "@fi-sci/misc"; -import HomePage from "@SpPages/HomePage"; -import { FunctionComponent } from "react"; -import StatusBar, { statusBarHeight } from "./StatusBar"; - -type Props = { - // none -}; - -const MainWindow: FunctionComponent = () => { - const { width, height } = useWindowDimensions(); - const H = height - statusBarHeight; - return ( -
-
- -
- {statusBarHeight && ( -
- -
- )} -
- ); -}; - -export default MainWindow; diff --git a/gui/src/app/RunPanel/RunPanel.tsx b/gui/src/app/RunPanel/RunPanel.tsx index adf44c0d..c88eb3c3 100644 --- a/gui/src/app/RunPanel/RunPanel.tsx +++ b/gui/src/app/RunPanel/RunPanel.tsx @@ -10,10 +10,9 @@ import { SamplingOpts } from "@SpCore/ProjectDataModel"; import { Progress } from "@SpStanSampler/StanModelWorker"; import StanSampler from "@SpStanSampler/StanSampler"; import { StanRun } from "@SpStanSampler/useStanSampler"; +import Button from "@mui/material/Button"; type RunPanelProps = { - width: number; - height: number; sampler?: StanSampler; latestRun: StanRun; data: any | undefined; @@ -22,8 +21,6 @@ type RunPanelProps = { }; const RunPanel: FunctionComponent = ({ - width, - height, sampler, latestRun, data, @@ -49,20 +46,27 @@ const RunPanel: FunctionComponent = ({ return
Data not saved
; } return ( -
+
- +   {runStatus === "sampling" && ( - + )}
{runStatus === "loading" &&
Loading compiled Stan model...
} diff --git a/gui/src/app/SamplerOutputView/HistsView.tsx b/gui/src/app/SamplerOutputView/HistsView.tsx index 512401ae..3335e515 100644 --- a/gui/src/app/SamplerOutputView/HistsView.tsx +++ b/gui/src/app/SamplerOutputView/HistsView.tsx @@ -1,20 +1,16 @@ +import ResponsiveGrid from "@SpComponents/ResponsiveGrid"; import SequenceHistogramWidget from "@SpComponents/SequenceHistogramWidget"; import { FunctionComponent, useMemo } from "react"; type HistsViewProps = { - width: number; - height: number; draws: number[][]; paramNames: string[]; drawChainIds: number[]; }; const HistsView: FunctionComponent = ({ - width, - height, draws, paramNames, - drawChainIds, }) => { const paramNamesResorted = useMemo(() => { // put the names that don't end with __ first @@ -23,46 +19,16 @@ const HistsView: FunctionComponent = ({ return [...names, ...namesWithSuffix]; }, [paramNames]); return ( -
+ {paramNamesResorted.map((paramName) => ( - ))} -
- ); -}; - -type SequenceHistProps = { - width: number; - height: number; - variableName: string; - draws: number[][]; - columnIndex: number; - drawChainIds: number[]; -}; - -const SequenceHist: FunctionComponent = ({ - width, - height, - variableName, - draws, - columnIndex, -}) => { - return ( - + ); }; diff --git a/gui/src/app/SamplerOutputView/SamplerOutputView.tsx b/gui/src/app/SamplerOutputView/SamplerOutputView.tsx index 689d5df1..13e1390e 100644 --- a/gui/src/app/SamplerOutputView/SamplerOutputView.tsx +++ b/gui/src/app/SamplerOutputView/SamplerOutputView.tsx @@ -3,22 +3,19 @@ import { Download } from "@mui/icons-material"; import HistsView from "@SpComponents/HistsView"; import SummaryView from "@SpComponents/SummaryView"; import TabWidget from "@SpComponents/TabWidget"; -import TracePlotsView from "@SpComponents/TracePlotsView"; import { SamplingOpts } from "@SpCore/ProjectDataModel"; import { StanRun } from "@SpStanSampler/useStanSampler"; import { triggerDownload } from "@SpUtil/triggerDownload"; import JSZip from "jszip"; import { FunctionComponent, useCallback, useMemo, useState } from "react"; +import TracePlotsView from "./TracePlotsView"; +import Button from "@mui/material/Button"; type SamplerOutputViewProps = { - width: number; - height: number; latestRun: StanRun; }; const SamplerOutputView: FunctionComponent = ({ - width, - height, latestRun, }) => { const { draws, paramNames, computeTimeSec, samplingOpts } = latestRun; @@ -26,8 +23,6 @@ const SamplerOutputView: FunctionComponent = ({ if (!draws || !paramNames || !samplingOpts) return ; return ( = ({ }; type DrawsDisplayProps = { - width: number; - height: number; draws: number[][]; paramNames: string[]; computeTimeSec: number | undefined; samplingOpts: SamplingOpts; }; -const tabs = [ - { - id: "summary", - label: "Summary", - title: "Summary view", - closeable: false, - }, - { - id: "draws", - label: "Draws", - title: "Draws view", - closeable: false, - }, - { - id: "hists", - label: "Histograms", - title: "Histograms view", - closeable: false, - }, - { - id: "traceplots", - label: "Trace Plots", - title: "Trace Plots view", - closeable: false, - }, -]; - const DrawsDisplay: FunctionComponent = ({ - width, - height, draws, paramNames, computeTimeSec, samplingOpts, }) => { - const [currentTabId, setCurrentTabId] = useState("summary"); - const numChains = samplingOpts.num_chains; const drawChainIds = useMemo(() => { @@ -98,24 +60,14 @@ const DrawsDisplay: FunctionComponent = ({ }, [draws, numChains]); return ( - + = ({ samplingOpts={samplingOpts} /> = ({ }; type DrawsViewProps = { - width: number; - height: number; draws: number[][]; paramNames: string[]; drawChainIds: number[]; @@ -151,8 +97,6 @@ type DrawsViewProps = { }; const DrawsView: FunctionComponent = ({ - width, - height, draws, paramNames, drawChainIds, @@ -197,7 +141,7 @@ const DrawsView: FunctionComponent = ({ URL.revokeObjectURL(url); }, [draws, paramNames, drawChainIds, samplingOpts]); return ( -
+
} @@ -236,13 +180,13 @@ const DrawsView: FunctionComponent = ({ {abbreviatedToNumRows !== undefined && abbreviatedToNumRows < draws[0].length && (
- +
)}
diff --git a/gui/src/app/SamplerOutputView/SequenceHistogramWidget.tsx b/gui/src/app/SamplerOutputView/SequenceHistogramWidget.tsx index 9b755f47..cd4812a9 100644 --- a/gui/src/app/SamplerOutputView/SequenceHistogramWidget.tsx +++ b/gui/src/app/SamplerOutputView/SequenceHistogramWidget.tsx @@ -5,15 +5,11 @@ type Props = { histData: number[]; title: string; variableName: string; - width: number; - height: number; }; const SequenceHistogramWidget: FunctionComponent = ({ histData, title, - width, - height, variableName, }) => { const data = useMemo( @@ -28,12 +24,10 @@ const SequenceHistogramWidget: FunctionComponent = ({ [histData], ); return ( -
+
= ({ plotSequences, variableName, highlightDrawRange, - width, - height, }) => { const shapes = useMemo( () => @@ -53,8 +49,6 @@ const SequencePlotWidget: FunctionComponent = ({ ); const layout = useMemo( () => ({ - width: width, - height, title: "", yaxis: { title: variableName }, xaxis: { title: "draw" }, @@ -66,10 +60,10 @@ const SequencePlotWidget: FunctionComponent = ({ }, showlegend: false, }), - [width, height, variableName, shapes], + [variableName, shapes], ); return ( -
+
); diff --git a/gui/src/app/SamplerOutputView/SummaryView.tsx b/gui/src/app/SamplerOutputView/SummaryView.tsx index e6be74cc..e841e07a 100644 --- a/gui/src/app/SamplerOutputView/SummaryView.tsx +++ b/gui/src/app/SamplerOutputView/SummaryView.tsx @@ -10,8 +10,6 @@ import { import { FunctionComponent, useMemo } from "react"; type SummaryViewProps = { - width: number; - height: number; draws: number[][]; paramNames: string[]; drawChainIds: number[]; @@ -75,8 +73,6 @@ type TableRow = { }; const SummaryView: FunctionComponent = ({ - width, - height, draws, paramNames, drawChainIds, @@ -122,7 +118,7 @@ const SummaryView: FunctionComponent = ({ }, [draws, paramNames, drawChainIds, computeTimeSec]); return ( -
+
diff --git a/gui/src/app/SamplerOutputView/TracePlotsView.tsx b/gui/src/app/SamplerOutputView/TracePlotsView.tsx index 00e56888..4364b5ef 100644 --- a/gui/src/app/SamplerOutputView/TracePlotsView.tsx +++ b/gui/src/app/SamplerOutputView/TracePlotsView.tsx @@ -4,28 +4,21 @@ import { FunctionComponent, useMemo, useState } from "react"; import ReactVisibilitySensor from "react-visibility-sensor"; type TracePlotsViewProps = { - width: number; - height: number; draws: number[][]; paramNames: string[]; drawChainIds: number[]; }; const TracePlotsView: FunctionComponent = ({ - width, - height, draws, paramNames, drawChainIds, }) => { - const plotHeight = Math.max(150, Math.min(400, height / 2)); return ( -
+
{paramNames.map((paramName, i) => ( = ({ }; type SequencePlotProps = { - width: number; - height: number; variableName: string; draws: number[][]; columnIndex: number; @@ -46,8 +37,6 @@ type SequencePlotProps = { }; const SequencePlot: FunctionComponent = ({ - width, - height, variableName, draws, columnIndex, @@ -57,15 +46,12 @@ const SequencePlot: FunctionComponent = ({ return (
{expanded && ( = ({ }; const SequencePlotChild: FunctionComponent = ({ - width, - height, variableName, draws, columnIndex, @@ -101,14 +85,12 @@ const SequencePlotChild: FunctionComponent = ({ ); }, [draws, columnIndex, drawChainIds]); return ( -
+
{({ isVisible }: { isVisible: boolean }) => { if (!isVisible) return
...
; return ( @@ -120,14 +102,12 @@ const SequencePlotChild: FunctionComponent = ({ }; type ExpandComponentProps = { - width: number; expanded: boolean; setExpanded: (expanded: boolean) => void; label: string; }; const ExpandComponent: FunctionComponent = ({ - width, expanded, setExpanded, label, @@ -135,7 +115,6 @@ const ExpandComponent: FunctionComponent = ({ return (
setExpanded(!expanded)} > {expanded ? "▼" : "▶"}  diff --git a/gui/src/app/SamplingOptsPanel/SamplingOptsPanel.tsx b/gui/src/app/SamplingOptsPanel/SamplingOptsPanel.tsx index 8c348643..70f236e6 100644 --- a/gui/src/app/SamplingOptsPanel/SamplingOptsPanel.tsx +++ b/gui/src/app/SamplingOptsPanel/SamplingOptsPanel.tsx @@ -1,5 +1,5 @@ -import { Hyperlink } from "@fi-sci/misc"; -import { Grid } from "@mui/material"; +import Grid from "@mui/material/Grid"; +import Link from "@mui/material/Link"; import { defaultSamplingOpts, SamplingOpts } from "@SpCore/ProjectDataModel"; import { FunctionComponent, useCallback } from "react"; @@ -167,9 +167,14 @@ const SamplingOptsPanel: FunctionComponent = ({
- + reset - +
); diff --git a/gui/src/app/StatusBar.tsx b/gui/src/app/StatusBar.tsx deleted file mode 100644 index 5cf6f9f2..00000000 --- a/gui/src/app/StatusBar.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { - createContext, - FunctionComponent, - useCallback, - useContext, -} from "react"; - -type Props = { - width: number; - height: number; -}; - -export const connectedColor = "#050"; -export const notConnectedColor = "#888"; - -export const statusBarHeight = 18; -// export const statusBarHeight = 0 - -type CustomStatusBarElements = { - [key: string]: any; -}; -type CustomStatusBarAction = { - type: "set"; - key: string; - value: string; -}; -export const customStatusBarElementsReducer = ( - state: CustomStatusBarElements, - action: CustomStatusBarAction, -): CustomStatusBarElements => { - if (action.type === "set") { - if (state[action.key] === action.value) return state; // this is important to avoid unnecessary re-renders - return { - ...state, - [action.key]: action.value, - }; - } else return state; -}; -export const CustomStatusBarElementsContext = createContext<{ - customStatusBarElements: CustomStatusBarElements; - customStatusBarElementsDispatch: (action: CustomStatusBarAction) => void; -} | null>(null); - -export const useCustomStatusBarElements = () => { - const { customStatusBarElements, customStatusBarElementsDispatch } = - useContext(CustomStatusBarElementsContext) || {}; - const setCustomStatusBarElement = useCallback( - (key: string, value: any) => { - customStatusBarElementsDispatch && - customStatusBarElementsDispatch({ type: "set", key, value }); - }, - [customStatusBarElementsDispatch], - ); - return { - customStatusBarElements: customStatusBarElements || {}, - setCustomStatusBarElement, - }; -}; - -const StatusBar: FunctionComponent = () => { - const { customStatusBarElements } = useCustomStatusBarElements(); - return ( -
- {/* The following is flush right */} - - {Object.keys(customStatusBarElements || {}).map((key) => ( - - {(customStatusBarElements || {})[key]} - - ))} - -
- ); -}; - -export default StatusBar; diff --git a/gui/src/app/TabWidget/TabWidget.tsx b/gui/src/app/TabWidget/TabWidget.tsx deleted file mode 100644 index d832364c..00000000 --- a/gui/src/app/TabWidget/TabWidget.tsx +++ /dev/null @@ -1,118 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import TabWidgetTabBar from "@SpComponents/TabWidgetTabBar"; -import { - FunctionComponent, - PropsWithChildren, - useCallback, - useEffect, - useMemo, - useState, -} from "react"; - -type Props = { - tabs: { - id: string; - label: string; - title?: string; - closeable: boolean; - icon?: any; - }[]; - currentTabId: string | undefined; - setCurrentTabId: (id: string) => void; - onCloseTab?: (id: string) => void; - width: number; - height: number; -}; - -const tabBarHeight = 30; - -const TabWidget: FunctionComponent> = ({ - children, - tabs, - currentTabId, - setCurrentTabId, - onCloseTab, - width, - height, -}) => { - const currentTabIndex = useMemo(() => { - if (!currentTabId) return undefined; - const index = tabs.findIndex((t) => t.id === currentTabId); - if (index < 0) return undefined; - return index; - }, [currentTabId, tabs]); - const children2 = Array.isArray(children) - ? (children as React.ReactElement[]) - : ([children] as React.ReactElement[]); - if ((children2 || []).length !== tabs.length) { - throw Error( - `TabWidget: incorrect number of tabs ${(children2 || []).length} <> ${tabs.length}`, - ); - } - const hMargin = 8; - const vMargin = 8; - const W = (width || 300) - hMargin * 2; - const H = height - vMargin * 2; - const [hasBeenVisible, setHasBeenVisible] = useState([]); - useEffect(() => { - if (currentTabIndex === undefined) return; - if (!hasBeenVisible.includes(currentTabIndex)) { - setHasBeenVisible([...hasBeenVisible, currentTabIndex]); - } - }, [currentTabIndex, hasBeenVisible]); - const handleSetCurrentTabIndex = useCallback( - (index: number) => { - if (index < 0 || index >= tabs.length) return; - setCurrentTabId(tabs[index].id); - }, - [setCurrentTabId, tabs], - ); - return ( -
-
- -
- {children2.map((c, i) => { - const visible = i === currentTabIndex; - return ( -
- {(visible || hasBeenVisible.includes(i)) && ( - - )} -
- ); - })} -
- ); -}; - -export default TabWidget; diff --git a/gui/src/app/TabWidget/TabWidgetTabBar.tsx b/gui/src/app/TabWidget/TabWidgetTabBar.tsx deleted file mode 100644 index 77c85b39..00000000 --- a/gui/src/app/TabWidget/TabWidgetTabBar.tsx +++ /dev/null @@ -1,71 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { Tab, Tabs } from "@mui/material"; -import { FunctionComponent, useEffect } from "react"; - -type Props = { - tabs: { - id: string; - label: string; - title?: string; - closeable: boolean; - icon?: any; - }[]; - currentTabIndex: number | undefined; - onCurrentTabIndexChanged: (i: number) => void; - onCloseTab?: (id: string) => void; -}; - -const TabWidgetTabBar: FunctionComponent = ({ - tabs, - currentTabIndex, - onCurrentTabIndexChanged, - onCloseTab, -}) => { - useEffect(() => { - if (currentTabIndex === undefined) { - if (tabs.length > 0) { - onCurrentTabIndexChanged(0); - } - } - }, [currentTabIndex, onCurrentTabIndexChanged, tabs.length]); - return ( - { - onCurrentTabIndexChanged(value); - }} - > - {tabs.map((tab, i) => ( - - {tab.icon ? ( - {tab.icon} - ) : ( - - )} - {tab.label} -    - {tab.closeable && onCloseTab && ( - { - onCloseTab(tab.id); - }} - > - ✕ - - )} - - } - sx={{ minHeight: 0, height: 0, fontSize: 12 }} - /> - ))} - - ); -}; - -export default TabWidgetTabBar; diff --git a/gui/src/app/components/LazyPlotlyPlot.tsx b/gui/src/app/components/LazyPlotlyPlot.tsx index 7cb805f5..d4f1c6d8 100644 --- a/gui/src/app/components/LazyPlotlyPlot.tsx +++ b/gui/src/app/components/LazyPlotlyPlot.tsx @@ -1,17 +1,36 @@ +import CircularProgress from "@mui/material/CircularProgress"; import React, { FunctionComponent, Suspense } from "react"; import type { PlotParams } from "react-plotly.js"; import createPlotlyComponent from "react-plotly.js/factory"; +import useMeasure from "react-use-measure"; const Plot = React.lazy(async () => { const plotly = await import("plotly.js-cartesian-dist"); return { default: createPlotlyComponent(plotly) }; }); const LazyPlotlyPlot: FunctionComponent = ({ data, layout }) => { + // plotly has a reactive setting, but it is buggy + const [ref, { width }] = useMeasure(); + + const layoutWithWidth = { + ...layout, + width: width, + }; + return ( - Loading plotly
}> - - +
+ + +

Loading Plotly.js

+ + } + > + +
+
); }; diff --git a/gui/src/app/components/ResponsiveGrid.tsx b/gui/src/app/components/ResponsiveGrid.tsx new file mode 100644 index 00000000..b703f3cc --- /dev/null +++ b/gui/src/app/components/ResponsiveGrid.tsx @@ -0,0 +1,40 @@ +// by default, MUI grids are only responsive to the viewport width +// not their container. This is based on the solution in +// https://github.com/mui/material-ui/issues/25189#issuecomment-1321236185 + +import { styled } from "@mui/material/styles"; +import { FunctionComponent } from "react"; + +const Container = styled("div")({ + display: "flex", + flexWrap: "wrap", + gap: 4, + containerType: "inline-size", +}); + +const Item = styled("div")(({ theme }) => ({ + width: "95%", + margin: "0 auto", + [theme.breakpoints.up("md").replace("@media", "@container")]: { + width: "calc(50% - 4px)", + }, + [theme.breakpoints.up("lg").replace("@media", "@container")]: { + width: "calc(100% / 4 - 12px)", + }, +})); + +type Props = { + children: JSX.Element[]; +}; + +const ResponsiveGrid: FunctionComponent = ({ children }) => { + return ( + + {children.map((child, i) => ( + {child} + ))} + + ); +}; + +export default ResponsiveGrid; diff --git a/gui/src/app/components/Splitter.tsx b/gui/src/app/components/Splitter.tsx new file mode 100644 index 00000000..d167b3f3 --- /dev/null +++ b/gui/src/app/components/Splitter.tsx @@ -0,0 +1,62 @@ +// @devbookhq/splitter has a known issue, https://github.com/DevbookHQ/splitter/issues/11, +// where re-renders of internal components can cause the splitter to lose its state. +// This wrapper captures the splitter sizes and stores them in a state to avoid this issue. +import DevbookSplit, { + SplitDirection as DevbookSplitDirection, + GutterTheme as DevbookGutterTheme, +} from "@devbookhq/splitter"; +import { + FunctionComponent, + useState, + useCallback, + PropsWithChildren, + useEffect, +} from "react"; + +interface SplitterProps { + direction?: keyof typeof DevbookSplitDirection; + minWidths?: number[]; + minHeights?: number[]; + initialSizes?: number[]; + gutterTheme?: keyof typeof DevbookGutterTheme; + gutterClassName?: string; + draggerClassName?: string; + onResizeStarted?: (pairIdx: number) => void; + onResizeFinished?: (pairIdx: number, newSizes: number[]) => void; + classes?: string[]; +} + +export const SplitDirection = DevbookSplitDirection; +export const GutterTheme = DevbookGutterTheme; + +export const Splitter: FunctionComponent> = ({ + direction = "Horizontal", + gutterTheme = "Light", + children, + initialSizes, + ...props +}) => { + const [persistentSizes, setPersistentSizes] = useState( + initialSizes, + ); + + useEffect(() => { + setPersistentSizes(initialSizes); + }, [initialSizes]); + + const handleResizeFinished = useCallback((_: number, newSizes: number[]) => { + setPersistentSizes(newSizes); + }, []); + + return ( + + {children} + + ); +}; diff --git a/gui/src/app/components/TabWidget.tsx b/gui/src/app/components/TabWidget.tsx new file mode 100644 index 00000000..e2ed7717 --- /dev/null +++ b/gui/src/app/components/TabWidget.tsx @@ -0,0 +1,64 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { FunctionComponent, useState } from "react"; +import Tabs from "@mui/material/Tabs"; +import Tab from "@mui/material/Tab"; +import Box from "@mui/material/Box"; + +type TabWidgetProps = { + labels: string[]; + children: JSX.Element[]; +}; + +const TabWidget: FunctionComponent = ({ labels, children }) => { + if (labels.length !== children.length) { + throw new Error("Number of labels and children must match"); + } + + const [index, setIndex] = useState(0); + + const handleChange = (_: React.SyntheticEvent, newValue: number) => { + setIndex(newValue); + }; + + return ( + + + {labels.map((label, i) => ( + + ))} + + + {children.map((child, i) => ( + + {child} + + ))} + + + ); +}; + +interface TabPanelProps { + children?: React.ReactNode; + index: number; + value: number; +} + +const CustomTabPanel = (props: TabPanelProps) => { + const { children, value, index, ...other } = props; + + return ( + + ); +}; + +export default TabWidget; diff --git a/gui/src/app/pages/HomePage/HomePage.tsx b/gui/src/app/pages/HomePage/HomePage.tsx index ae6ca11f..84ae8086 100644 --- a/gui/src/app/pages/HomePage/HomePage.tsx +++ b/gui/src/app/pages/HomePage/HomePage.tsx @@ -1,19 +1,22 @@ -import { Splitter } from "@fi-sci/splitter"; +import Box from "@mui/material/Box"; +import Divider from "@mui/material/Divider"; +import Grid from "@mui/material/Grid"; +import styled from "@mui/material/styles/styled"; +import useMediaQuery from "@mui/material/useMediaQuery"; import DataFileEditor from "@SpComponents/DataFileEditor"; import RunPanel from "@SpComponents/RunPanel"; import SamplerOutputView from "@SpComponents/SamplerOutputView"; import SamplingOptsPanel from "@SpComponents/SamplingOptsPanel"; +import { GutterTheme, SplitDirection, Splitter } from "@SpComponents/Splitter"; import StanFileEditor from "@SpComponents/StanFileEditor"; -import ProjectContextProvider, { - ProjectContext, -} from "@SpCore/ProjectContextProvider"; +import { ProjectContext } from "@SpCore/ProjectContextProvider"; import { modelHasUnsavedChanges, modelHasUnsavedDataFileChanges, ProjectKnownFiles, SamplingOpts, } from "@SpCore/ProjectDataModel"; -import LeftPanel from "@SpPages/LeftPanel"; +import Sidebar, { drawerWidth } from "@SpPages/Sidebar"; import TopBar from "@SpPages/TopBar"; import useStanSampler from "@SpStanSampler/useStanSampler"; import { @@ -27,143 +30,71 @@ import { } from "react"; type Props = { - width: number; - height: number; + // }; -const HomePage: FunctionComponent = ({ width, height }) => { - // NOTE: We should probably move the ProjectContextProvider up to the App or MainWindow - // component; however this will wait on routing refactor since I don't want to add the `route` - // item in those contexts in this PR - return ( - - - - ); -}; - -const HomePageChild: FunctionComponent = ({ width, height }) => { +const HomePage: FunctionComponent = () => { const { data } = useContext(ProjectContext); const [compiledMainJsUrl, setCompiledMainJsUrl] = useState(""); - 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 + const smallScreen = useMediaQuery("(max-width:600px)"); + + const [leftPanelCollapsed, setLeftPanelCollapsed] = useState(smallScreen); // 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 lastShouldBeCollapsed = useRef( - determineShouldBeInitiallyCollapsed(width), - ); + const lastShouldBeCollapsed = useRef(smallScreen); useEffect(() => { - const shouldBeCollapsed = determineShouldBeInitiallyCollapsed(width); - if (shouldBeCollapsed !== lastShouldBeCollapsed.current) { - lastShouldBeCollapsed.current = shouldBeCollapsed; - setLeftPanelCollapsed(shouldBeCollapsed); + if (smallScreen !== lastShouldBeCollapsed.current) { + lastShouldBeCollapsed.current = smallScreen; + setLeftPanelCollapsed(smallScreen); } - }, [width]); - - const topBarHeight = 22; + }, [smallScreen]); useEffect(() => { document.title = "Stan Playground - Editing " + data.meta.title; }, [data.meta.title]); return ( -
-
- -
-
- -
-
+ + + + + + - - + + -
-
+ + ); }; -// the width of the left panel when it is expanded based on the overall width -const determineLeftPanelWidth = (width: number) => { - const minWidth = 150; - const maxWidth = 250; - 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 LeftViewProps = { - width: number; - height: number; setCompiledMainJsUrl: (url: string) => void; }; const LeftView: FunctionComponent = ({ - width, - height, setCompiledMainJsUrl, }) => { const { data, update } = useContext(ProjectContext); return ( = ({ setCompiledUrl={setCompiledMainJsUrl} /> @@ -210,14 +139,10 @@ const LeftView: FunctionComponent = ({ }; type RightViewProps = { - width: number; - height: number; compiledMainJsUrl?: string; }; const RightView: FunctionComponent = ({ - width, - height, compiledMainJsUrl, }) => { const { data, update } = useContext(ProjectContext); @@ -228,8 +153,6 @@ const RightView: FunctionComponent = ({ return undefined; } }, [data.dataFileContent]); - const samplingOptsPanelHeight = 160; - const samplingOptsPanelWidth = Math.min(180, width / 2); const setSamplingOpts = useCallback( (opts: SamplingOpts) => { @@ -241,53 +164,51 @@ const RightView: FunctionComponent = ({ const { sampler, latestRun } = useStanSampler(compiledMainJsUrl); const isSampling = latestRun.status === "sampling"; return ( -
-
- -
-
- -
-
- -
-
+ + + + + + + + + + + + + + ); }; +// adapted from https://mui.com/material-ui/react-drawer/#persistent-drawer +const MovingBox = styled(Box, { + shouldForwardProp: (prop) => prop !== "open", +})<{ + open?: boolean; +}>(({ theme, open }) => ({ + flexGrow: 1, + transition: theme.transitions.create("padding", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + paddingLeft: `${drawerWidth}px`, + ...(open && { + transition: theme.transitions.create("padding", { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + paddingLeft: 0, + }), +})); + export default HomePage; diff --git a/gui/src/app/pages/HomePage/LoadProjectWindow.tsx b/gui/src/app/pages/HomePage/LoadProjectWindow.tsx index 24668ce4..27706868 100644 --- a/gui/src/app/pages/HomePage/LoadProjectWindow.tsx +++ b/gui/src/app/pages/HomePage/LoadProjectWindow.tsx @@ -1,3 +1,4 @@ +import Button from "@mui/material/Button"; import { FieldsContentsMap, FileNames, @@ -130,15 +131,15 @@ const LoadProjectWindow: FunctionComponent = ({ )} {showReplaceProjectOptions && (
- +   - +
)}
diff --git a/gui/src/app/pages/HomePage/SaveProjectWindow.tsx b/gui/src/app/pages/HomePage/SaveProjectWindow.tsx index 70d6a2c1..d620d071 100644 --- a/gui/src/app/pages/HomePage/SaveProjectWindow.tsx +++ b/gui/src/app/pages/HomePage/SaveProjectWindow.tsx @@ -5,6 +5,7 @@ import { FileRegistry, mapModelToFileManifest } from "@SpCore/FileMapping"; import { ProjectContext } from "@SpCore/ProjectContextProvider"; import saveAsGitHubGist from "@SpCore/gists/saveAsGitHubGist"; import { triggerDownload } from "@SpUtil/triggerDownload"; +import Button from "@mui/material/Button"; type SaveProjectWindowProps = { onClose: () => void; @@ -45,7 +46,7 @@ const SaveProjectWindow: FunctionComponent = ({
 
{!exportingToGist && (
- +   - +
)} {exportingToGist && ( @@ -164,11 +165,11 @@ const GistExportView: FunctionComponent = ({
 
{!gistUrl && (
- +   - +
)} {gistUrl && ( @@ -193,7 +194,7 @@ const GistExportView: FunctionComponent = ({

- +
)}
diff --git a/gui/src/app/pages/HomePage/LeftPanel.tsx b/gui/src/app/pages/HomePage/Sidebar.tsx similarity index 51% rename from gui/src/app/pages/HomePage/LeftPanel.tsx rename to gui/src/app/pages/HomePage/Sidebar.tsx index dee557b9..6ff2eca0 100644 --- a/gui/src/app/pages/HomePage/LeftPanel.tsx +++ b/gui/src/app/pages/HomePage/Sidebar.tsx @@ -1,21 +1,22 @@ import { ProjectContext } from "@SpCore/ProjectContextProvider"; import LoadProjectWindow from "@SpPages/LoadProjectWindow"; import SaveProjectWindow from "@SpPages/SaveProjectWindow"; -import { SmallIconButton } from "@fi-sci/misc"; import ModalWindow, { useModalWindow } from "@fi-sci/modal-window"; -import { ChevronLeft, ChevronRight } from "@mui/icons-material"; +import Button from "@mui/material/Button"; +import Divider from "@mui/material/Divider"; +import Drawer from "@mui/material/Drawer"; +import List from "@mui/material/List"; +import ListItem from "@mui/material/ListItem"; +import Toolbar from "@mui/material/Toolbar"; import { FunctionComponent, useContext } from "react"; import { Link } from "react-router-dom"; -type LeftPanelProps = { - width: number; - height: number; +type Sidebar = { hasUnsavedChanges: boolean; collapsed: boolean; - onSetCollapsed: (collapsed: boolean) => void; }; -const examplesStanies = [ +const exampleLinks = [ { name: "Linear regression", link: "https://gist.github.com/WardBrian/93d12876923790f23d9c5cb481e8cd34", @@ -26,12 +27,11 @@ const examplesStanies = [ }, ]; -const LeftPanel: FunctionComponent = ({ - width, - height, +export const drawerWidth = 240; + +const Sidebar: FunctionComponent = ({ hasUnsavedChanges, collapsed, - onSetCollapsed, }) => { // note: this is close enough to pass in directly if we wish const { update } = useContext(ProjectContext); @@ -46,43 +46,57 @@ const LeftPanel: FunctionComponent = ({ handleClose: loadProjectClose, } = useModalWindow(); - if (collapsed) { - return ( -
-
- onSetCollapsed(false)} /> -
-
- ); - } - return ( -
-
- onSetCollapsed(true)} /> -

Examples

+ + {/* For spacing purposes */} + - {examplesStanies.map((stanie, i) => ( -
- - {stanie.name} - -
- ))} -
-
- -   - + + + + -
-
 
-
+ + + {/* This will probably be removed or replaced in the future. It's just for convenience during development. */} - -
-
+ + + -
- ); -}; - -const ExpandButton: FunctionComponent<{ onClick: () => void }> = ({ - onClick, -}) => { - return ( - } onClick={onClick} title="Expand" /> - ); -}; - -const CollapseButton: FunctionComponent<{ onClick: () => void }> = ({ - onClick, -}) => { - return ( - } - onClick={onClick} - title="Collapse" - /> + ); }; -export default LeftPanel; +export default Sidebar; diff --git a/gui/src/app/pages/HomePage/TopBar.tsx b/gui/src/app/pages/HomePage/TopBar.tsx index 5fdf236a..3d07d14f 100644 --- a/gui/src/app/pages/HomePage/TopBar.tsx +++ b/gui/src/app/pages/HomePage/TopBar.tsx @@ -1,20 +1,32 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import CompilationServerConnectionControl from "@SpStanc/CompilationServerConnectionControl"; import { SmallIconButton } from "@fi-sci/misc"; -import { QuestionMark } from "@mui/icons-material"; -import { Toolbar } from "@mui/material"; +import { Menu, QuestionMark } from "@mui/icons-material"; +import AppBar from "@mui/material/AppBar"; +import IconButton from "@mui/material/IconButton"; +import Toolbar from "@mui/material/Toolbar"; import { FunctionComponent } from "react"; type TopBarProps = { title: string; - width: number; - height: number; + onSetCollapsed: (fn: (collapsed: boolean) => boolean) => void; }; -const TopBar: FunctionComponent = ({ title }) => { +const TopBar: FunctionComponent = ({ title, onSetCollapsed }) => { return ( -
- + theme.zIndex.drawer + 1 }} + > + + onSetCollapsed((b) => !b)} + edge="start" + > + + Stan Playground - {title} @@ -31,7 +43,7 @@ const TopBar: FunctionComponent = ({ title }) => { /> -
+ ); }; diff --git a/gui/src/draws-table.css b/gui/src/draws-table.css index 1bc53263..b04a7de0 100644 --- a/gui/src/draws-table.css +++ b/gui/src/draws-table.css @@ -1,5 +1,6 @@ .draws-table { width: 100%; + height: 100%; border-collapse: collapse; font-size: 0.75em; font-family: sans-serif; @@ -10,7 +11,6 @@ .draws-table td { word-break: break-all; max-width: 250px; - overflow: hidden; } .draws-table thead tr { diff --git a/gui/src/index.css b/gui/src/index.css index 6d6fa939..efb79ff0 100644 --- a/gui/src/index.css +++ b/gui/src/index.css @@ -1,19 +1,5 @@ body { margin: 0; font-family: "Arial", sans-serif; -} - -.top-bar { - background: #555; - color: white; - padding-top: 2px; - padding-left: 10px; - font-size: 16px; - font-family: Verdana, Geneva, Tahoma, sans-serif; -} - -.left-panel { - background-image: repeating-linear-gradient(45deg, #f0f0f0 1px, #e0e0e0 6px); - margin: 0px; - font-size: 14px; + min-height: 100vh; } diff --git a/gui/src/localStyles.css b/gui/src/localStyles.css index db236903..708ce1d4 100644 --- a/gui/src/localStyles.css +++ b/gui/src/localStyles.css @@ -4,15 +4,6 @@ color: red; } -.Absolute { - position: absolute; -} - -.AbsoluteHidden { - position: absolute; - overflow: hidden; -} - /* Compilation server connection mgmt */ .connected { color: green; @@ -35,10 +26,15 @@ } /* Compilation Results (StanCompileResultWindow) */ +.ErrorsWindow { + height: 100%; + width: 100%; + overflow: auto; +} + .CompileErrorsPane { color: red; padding: 0px; - overflow: auto; } .ErrorWarningMessage { @@ -49,7 +45,6 @@ .CompileWarningsPane { color: blue; padding: 0px; - overflow: auto; } .CompilationDone { @@ -58,94 +53,19 @@ /* Main window */ .MainWindow { - position: absolute; + height: 100vh; + width: 100%; overflow: hidden; } -.MainWindowContent { - position: absolute; - top: 0px; - overflow: hidden; -} - -.MainWindowStatusBar { - position: absolute; - bottom: 0px; - background-color: #eee; - overflow: hidden; -} - -/* Home Page */ -.MainHomePage { - position: absolute; - overflow: hidden; -} - -/* NOTE: These should be combined with the existing single-element-class CSS for the elements; -I haven't done this because those currently live in separate other files... */ -.TopBarPosition { - position: absolute; - left: 0px; - top: 0px; - overflow: hidden; -} - -.LeftMenuPanelPosition { - position: absolute; - left: 0px; - overflow: auto; -} - -.MainAreaPosition { - position: absolute; - overflow: hidden; -} - -.RunPanelPosition { - top: 0px; -} - -/* Main window status bar */ - -div.StatusBar { - font-size: 12px; - padding-top: 3px; - padding-left: 5px; -} - -.StatusBar span { - position: absolute; - right: 5px; - color: gray; -} - -.StatusBarItem { - padding-left: 10px; -} - /* Main Window toolbar/TopBar */ -/* !important is not great style, but required to get the Toolbar -to render its content text properly--needs to trump some of the -built-in styling. -I'd feel worse about it except it's what we're already doing already -with a style tag. -Anyway, need to revisit. */ -.TopBar { - min-height: 20px !important; -} .TopBarSpacer { margin-left: auto; } -/* Left menu bar */ -.LeftMenu { - position: absolute; - background-color: lightgray; - overflow-y: auto; -} - -.LeftMenuContentWrapper { +/* Sidebar */ +.SidebarContentWrapper { margin: 5px; } @@ -165,16 +85,18 @@ Anyway, need to revisit. */ /* Text Editors ------ */ .EditorMenuBar { - position: absolute; - padding-left: 20px; - padding-top: 3px; background-color: lightgray; - overflow: hidden; + height: 25px; +} + +.EditorWithToolbar { + height: calc(100% - 25px); } span.EditorTitle { position: relative; top: 1px; + padding-left: 5px; } .EditedText { @@ -186,10 +108,6 @@ span.EditorTitle { color: gray; } -.EditorWrapper { - position: absolute; -} - .NotSelectable { user-select: none; } @@ -201,7 +119,6 @@ span.EditorTitle { /* Sampling section ---- */ /* Run-Sampling */ .RunPanel { - position: absolute; overflow-y: auto; } @@ -210,20 +127,16 @@ span.EditorTitle { } .SamplingProgress { - width: 45%; + width: 85%; } /* Summary View */ .SummaryViewWrapper { - position: absolute; - overflow-y: auto; } /* Draws */ .DrawsTable { - position: absolute; - overflow: auto; } .DrawAbbreviationToggle { @@ -231,27 +144,8 @@ span.EditorTitle { padding: 5px; } -/* Histograms */ - -.HistogramsWrapper { - position: absolute; - overflow-y: auto; - display: flex; - flex-wrap: wrap; -} - -.SequenceHistogram { - position: relative; -} - /* Trace Plots */ .TracePlotsView { - position: absolute; - overflow-y: auto; -} - -.SqeuencePlot { - position: relative; } .SequencePlotChild { @@ -263,9 +157,6 @@ span.EditorTitle { } .TracePlotExpandComponent { - height: 25px; - padding-top: 5px; - padding-left: 5px; cursor: pointer; background-color: lightgray; } @@ -284,36 +175,6 @@ span.EditorTitle { width: 4em; } -/* Tab Widget etc ------ */ - -.TabWidget { - position: absolute; - overflow: hidden; -} - -.TabWidget-bar { - position: absolute; - left: 0px; - top: 0px; -} - -.TabBarIcon { - margin-right: 4px; -} - -.TabBarLabel { - text-transform: none; - font-size: 14px; -} - -.TabBody { - overflow-y: hidden; - overflow-x: hidden; - position: absolute; - left: 0px; - background: white; -} - /* Save/Load Project ------ */ .GistExplainer { max-width: 800px; diff --git a/gui/src/scientific-table.css b/gui/src/scientific-table.css index 68cd2cf1..6233215c 100644 --- a/gui/src/scientific-table.css +++ b/gui/src/scientific-table.css @@ -10,7 +10,6 @@ /* Table */ .scientific-table { border-collapse: collapse; - width: 100%; } /* Table header */ diff --git a/gui/yarn.lock b/gui/yarn.lock index c4e8fc64..965a0178 100644 --- a/gui/yarn.lock +++ b/gui/yarn.lock @@ -224,6 +224,13 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@devbookhq/splitter@^1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@devbookhq/splitter/-/splitter-1.4.2.tgz#97fb5d015ca605847511f7420cb9d59d70b0eb89" + integrity sha512-DqJXsL7WNeDn/DyCeyoeeSpFHHoYBXscYlKNd3cJQ5d1xur73MPezHpyR2OID6Kh40TZ4KAb4hYjl5nL2+5M1g== + dependencies: + react-is "^17.0.2" + "@emotion/babel-plugin@^11.11.0": version "11.11.0" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c" @@ -491,13 +498,6 @@ "@mui/icons-material" "^5.14.19" "@mui/material" "^5.14.20" -"@fi-sci/splitter@^0.0.3": - version "0.0.3" - resolved "https://registry.yarnpkg.com/@fi-sci/splitter/-/splitter-0.0.3.tgz#b296c2091f229b33f7c0176bcc10decce84d8733" - integrity sha512-kr79gCEsdUA2DWTcUD7gyS0Wp3dgkqQHzMKmFnIfFc62HxC8HmjcWLO9UGE/Vs7iB02ySlM0/PMzYrH9WLIQXw== - dependencies: - react-draggable "^4.4.6" - "@floating-ui/core@^1.0.0": version "1.6.2" resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.2.tgz#d37f3e0ac1f1c756c7de45db13303a266226851a" @@ -1498,11 +1498,6 @@ check-error@^1.0.3: dependencies: get-func-name "^2.0.2" -clsx@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" - integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== - clsx@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" @@ -1631,6 +1626,11 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" +debounce@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" + integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== + debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.5" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" @@ -3234,14 +3234,6 @@ react-dom@^18.2.0: loose-envify "^1.1.0" scheduler "^0.23.2" -react-draggable@^4.4.6: - version "4.4.6" - resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.4.6.tgz#63343ee945770881ca1256a5b6fa5c9f5983fe1e" - integrity sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw== - dependencies: - clsx "^1.1.1" - prop-types "^15.8.1" - react-dropzone@^14.2.3: version "14.2.3" resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-14.2.3.tgz#0acab68308fda2d54d1273a1e626264e13d4e84b" @@ -3256,7 +3248,7 @@ react-is@^16.13.1, react-is@^16.7.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-is@^17.0.1: +react-is@^17.0.1, react-is@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== @@ -3303,6 +3295,13 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" +react-use-measure@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.1.1.tgz#5824537f4ee01c9469c45d5f7a8446177c6cc4ba" + integrity sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig== + dependencies: + debounce "^1.2.1" + react-visibility-sensor@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/react-visibility-sensor/-/react-visibility-sensor-5.1.1.tgz#5238380960d3a0b2be0b7faddff38541e337f5a9"