From 20168e5f156eb33b879cf3f3e0a479ee5da09500 Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Mon, 29 Jul 2024 16:47:33 +0000 Subject: [PATCH 1/2] Update toolbar, smarter hiding of labels --- gui/src/app/FileEditor/StanFileEditor.tsx | 32 +++-- gui/src/app/FileEditor/TextEditor.tsx | 137 ++++++++++++++-------- gui/src/localStyles.css | 47 ++++++-- 3 files changed, 140 insertions(+), 76 deletions(-) diff --git a/gui/src/app/FileEditor/StanFileEditor.tsx b/gui/src/app/FileEditor/StanFileEditor.tsx index 1ed6df8a..6f06b909 100644 --- a/gui/src/app/FileEditor/StanFileEditor.tsx +++ b/gui/src/app/FileEditor/StanFileEditor.tsx @@ -1,5 +1,4 @@ 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"; @@ -123,7 +122,6 @@ const StanFileEditor: FunctionComponent = ({ } }, [fileContent, handleCompile, didInitialCompile]); - const showLabelsOnButtons = useMediaQuery("(min-width:600px)"); const [syntaxWindowVisible, setSyntaxWindowVisible] = useState(false); const toolbarItems: ToolbarItem[] = useMemo(() => { @@ -134,7 +132,7 @@ const StanFileEditor: FunctionComponent = ({ ret.push({ type: "button", icon: , - label: showLabelsOnButtons ? "Syntax error" : "", + label: "Syntax error", color: "darkred", tooltip: "Syntax error in Stan file", onClick: () => { @@ -145,7 +143,7 @@ const StanFileEditor: FunctionComponent = ({ ret.push({ type: "button", icon: , - label: showLabelsOnButtons ? "Syntax warning" : "", + label: "Syntax warning", color: "blue", tooltip: "Syntax warning in Stan file", onClick: () => { @@ -155,17 +153,15 @@ const StanFileEditor: FunctionComponent = ({ } // auto format - if (!readOnly) { - if (editedFileContent) { - ret.push({ - type: "button", - icon: , - tooltip: "Auto format this stan file", - label: showLabelsOnButtons ? "auto format" : undefined, - onClick: requestFormat, - color: "darkblue", - }); - } + if (!readOnly && editedFileContent && validSyntax) { + ret.push({ + type: "button", + icon: , + tooltip: "Auto format this stan file", + label: "Auto format", + onClick: requestFormat, + color: "darkblue", + }); } if (editedFileContent && editedFileContent === fileContent) { if (compileStatus !== "compiling") { @@ -173,7 +169,7 @@ const StanFileEditor: FunctionComponent = ({ ret.push({ type: "button", tooltip: "Compile Stan model", - label: "compile", + label: "Compile", icon: , onClick: handleCompile, color: "darkblue", @@ -183,7 +179,8 @@ const StanFileEditor: FunctionComponent = ({ if (compileStatus !== "") { ret.push({ type: "text", - label: compileMessage, + label: + compileMessage.charAt(0).toUpperCase() + compileMessage.slice(1), color: compileStatus === "compiled" ? "green" @@ -200,7 +197,6 @@ const StanFileEditor: FunctionComponent = ({ fileContent, handleCompile, requestFormat, - showLabelsOnButtons, validSyntax, compileStatus, compileMessage, diff --git a/gui/src/app/FileEditor/TextEditor.tsx b/gui/src/app/FileEditor/TextEditor.tsx index 0156df67..c2843768 100644 --- a/gui/src/app/FileEditor/TextEditor.tsx +++ b/gui/src/app/FileEditor/TextEditor.tsx @@ -1,16 +1,16 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { SmallIconButton } from "@fi-sci/misc"; import { Editor, loader, useMonaco } from "@monaco-editor/react"; import { Save } from "@mui/icons-material"; +import Button from "@mui/material/Button"; import Link from "@mui/material/Link"; import monacoAddStanLang from "@SpComponents/stanLang"; import { CodeMarker } from "@SpStanc/Linting"; import { editor, MarkerSeverity } from "monaco-editor"; import { FunctionComponent, - PropsWithChildren, useCallback, useEffect, + useMemo, useState, } from "react"; @@ -36,7 +36,7 @@ export type ToolbarItem = tooltip?: string; label?: string; icon?: any; - onClick?: () => void; + onClick: () => void; color?: string; } | { @@ -131,32 +131,19 @@ const TextEditor: FunctionComponent = ({ [onSaveText, readOnly], ); + const edited = useMemo(() => { + return editedText !== text; + }, [editedText, text]); + return (
- -
- {label} -     - {!readOnly && text !== editedText && ( - } - title="Save file" - disabled={text === editedText} - label="save" - /> - )} -     - {editedText !== text && edited} -     - {readOnly && read only} -     - {toolbarItems && - toolbarItems.map((item, i) => ( - - ))} -
-
+ void; + edited: boolean; + readOnly: boolean; +}; + +const ToolBar: FunctionComponent = ({ + items, + label, + onSaveText, + edited, + readOnly, +}) => { + const toolBarItems = useMemo(() => { + const editorItems: ToolbarItem[] = []; + + if (!readOnly && edited) { + editorItems.push({ + type: "button", + icon: , + onClick: onSaveText, + tooltip: "Save file", + label: "Save", + }); + } + + if (edited) { + editorItems.push({ + type: "text", + label: "Edited", + color: "red", + }); + } + + if (readOnly) { + editorItems.push({ + type: "text", + label: "Read Only", + color: "gray", + }); + } + + return editorItems.concat(items); + }, [edited, items, onSaveText, readOnly]); + + return ( +
+
+ {label} + {toolBarItems && + toolBarItems.map((item, i) => ( + + ))} +
+
+ ); +}; + const ToolbarItemComponent: FunctionComponent<{ item: ToolbarItem }> = ({ item, }) => { @@ -193,32 +240,28 @@ const ToolbarItemComponent: FunctionComponent<{ item: ToolbarItem }> = ({ const { onClick, color, label, tooltip, icon } = item; if (icon) { return ( - - + ); } else { - if (!onClick) { - return ( - - {label}    - - ); - } return ( - + {label} @@ -228,7 +271,11 @@ const ToolbarItemComponent: FunctionComponent<{ item: ToolbarItem }> = ({ } } else if (item.type === "text") { return ( - + {item.label}    ); @@ -237,10 +284,6 @@ const ToolbarItemComponent: FunctionComponent<{ item: ToolbarItem }> = ({ } }; -const NotSelectable: FunctionComponent = ({ children }) => { - return
{children}
; -}; - const createHintTextContentWidget = (content: string | HTMLSpanElement) => { return { getDomNode: () => { diff --git a/gui/src/localStyles.css b/gui/src/localStyles.css index 1024392e..daf5216f 100644 --- a/gui/src/localStyles.css +++ b/gui/src/localStyles.css @@ -84,28 +84,53 @@ /* Text Editors ------ */ +:root { + --editor-menu-height: 30px; +} + .EditorMenuBar { background-color: lightgray; - height: 25px; + height: var(--editor-menu-height); + position: relative; + display: flex; + align-content: center; + align-items: center; + container-name: EditorMenuBar; + container-type: inline-size; + white-space: nowrap; + overflow-x: auto; + overflow-y: hidden; } .EditorWithToolbar { - height: calc(100% - 25px); + height: calc(100% - var(--editor-menu-height)); } -span.EditorTitle { - position: relative; - top: 1px; - padding-left: 5px; +span.EditorToolbarItem { + padding-left: 15px; } -.EditedText { - color: #a33; - font-size: 12px; +.ToolbarButtonText { + text-transform: none; + font-weight: normal; + letter-spacing: normal; + display: inline-block; + white-space: normal; } -.ReadOnlyText { - color: gray; +@container EditorMenuBar (max-width: 500px) { + .ToolbarButtonText { + display: none; + width: 0; + } + span.EditorToolbarItem { + padding: 0px; + } +} + +span.EditorTitle { + padding-left: 5px; + font-weight: 500; } .NotSelectable { From 61d7a3d3f4f2a32e196f2d067e592aa5b2e20fd1 Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Mon, 29 Jul 2024 18:09:39 +0000 Subject: [PATCH 2/2] Move toolbar to own file, tighter padding when collapsed --- gui/src/app/FileEditor/StanFileEditor.tsx | 3 +- gui/src/app/FileEditor/TextEditor.tsx | 131 +--------------------- gui/src/app/FileEditor/ToolBar.tsx | 125 +++++++++++++++++++++ gui/src/app/Scripting/ScriptEditor.tsx | 3 +- gui/src/localStyles.css | 3 + 5 files changed, 134 insertions(+), 131 deletions(-) create mode 100644 gui/src/app/FileEditor/ToolBar.tsx diff --git a/gui/src/app/FileEditor/StanFileEditor.tsx b/gui/src/app/FileEditor/StanFileEditor.tsx index 6f06b909..47e1a081 100644 --- a/gui/src/app/FileEditor/StanFileEditor.tsx +++ b/gui/src/app/FileEditor/StanFileEditor.tsx @@ -1,7 +1,8 @@ import { AutoFixHigh, Cancel, Settings } from "@mui/icons-material"; import { SplitDirection, Splitter } from "@SpComponents/Splitter"; import StanCompileResultWindow from "@SpComponents/StanCompileResultWindow"; -import TextEditor, { ToolbarItem } from "@SpComponents/TextEditor"; +import TextEditor from "@SpComponents/TextEditor"; +import { ToolbarItem } from "@SpComponents/ToolBar"; import compileStanProgram from "@SpStanc/compileStanProgram"; import { stancErrorsToCodeMarkers } from "@SpStanc/Linting"; import useStanc from "@SpStanc/useStanc"; diff --git a/gui/src/app/FileEditor/TextEditor.tsx b/gui/src/app/FileEditor/TextEditor.tsx index c2843768..609b7c08 100644 --- a/gui/src/app/FileEditor/TextEditor.tsx +++ b/gui/src/app/FileEditor/TextEditor.tsx @@ -1,9 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Editor, loader, useMonaco } from "@monaco-editor/react"; -import { Save } from "@mui/icons-material"; -import Button from "@mui/material/Button"; -import Link from "@mui/material/Link"; + import monacoAddStanLang from "@SpComponents/stanLang"; +import { ToolBar, ToolbarItem } from "@SpComponents/ToolBar"; import { CodeMarker } from "@SpStanc/Linting"; import { editor, MarkerSeverity } from "monaco-editor"; import { @@ -30,21 +29,6 @@ type Props = { contentOnEmpty?: string | HTMLSpanElement; }; -export type ToolbarItem = - | { - type: "button"; - tooltip?: string; - label?: string; - icon?: any; - onClick: () => void; - color?: string; - } - | { - type: "text"; - label: string; - color?: string; - }; - const TextEditor: FunctionComponent = ({ text, onSaveText, @@ -173,117 +157,6 @@ const toMonacoMarkerSeverity = ( } }; -type ToolbarProps = { - items: ToolbarItem[]; - label: string; - onSaveText: () => void; - edited: boolean; - readOnly: boolean; -}; - -const ToolBar: FunctionComponent = ({ - items, - label, - onSaveText, - edited, - readOnly, -}) => { - const toolBarItems = useMemo(() => { - const editorItems: ToolbarItem[] = []; - - if (!readOnly && edited) { - editorItems.push({ - type: "button", - icon: , - onClick: onSaveText, - tooltip: "Save file", - label: "Save", - }); - } - - if (edited) { - editorItems.push({ - type: "text", - label: "Edited", - color: "red", - }); - } - - if (readOnly) { - editorItems.push({ - type: "text", - label: "Read Only", - color: "gray", - }); - } - - return editorItems.concat(items); - }, [edited, items, onSaveText, readOnly]); - - return ( -
-
- {label} - {toolBarItems && - toolBarItems.map((item, i) => ( - - ))} -
-
- ); -}; - -const ToolbarItemComponent: FunctionComponent<{ item: ToolbarItem }> = ({ - item, -}) => { - if (item.type === "button") { - const { onClick, color, label, tooltip, icon } = item; - if (icon) { - return ( - - - - ); - } else { - return ( - - - {label} - -     - - ); - } - } else if (item.type === "text") { - return ( - - {item.label}    - - ); - } else { - return unknown toolbar item type; - } -}; - const createHintTextContentWidget = (content: string | HTMLSpanElement) => { return { getDomNode: () => { diff --git a/gui/src/app/FileEditor/ToolBar.tsx b/gui/src/app/FileEditor/ToolBar.tsx new file mode 100644 index 00000000..51ced36b --- /dev/null +++ b/gui/src/app/FileEditor/ToolBar.tsx @@ -0,0 +1,125 @@ +import { FunctionComponent, useMemo } from "react"; +import { Save } from "@mui/icons-material"; +import Link from "@mui/material/Link"; +import IconButton from "@mui/material/IconButton"; + +export type ToolbarItem = + | { + type: "button"; + tooltip?: string; + label?: string; + icon?: any; + onClick: () => void; + color?: string; + } + | { + type: "text"; + label: string; + color?: string; + }; + +type ToolbarProps = { + items: ToolbarItem[]; + label: string; + onSaveText: () => void; + edited: boolean; + readOnly: boolean; +}; + +export const ToolBar: FunctionComponent = ({ + items, + label, + onSaveText, + edited, + readOnly, +}) => { + const toolBarItems = useMemo(() => { + const editorItems: ToolbarItem[] = []; + + if (readOnly) { + editorItems.push({ + type: "text", + label: "Read Only", + color: "gray", + }); + } else if (edited) { + editorItems.push({ + type: "button", + icon: , + onClick: onSaveText, + tooltip: "Save file", + label: "Save", + }); + editorItems.push({ + type: "text", + label: "Edited", + color: "red", + }); + } + + return editorItems.concat(items); + }, [edited, items, onSaveText, readOnly]); + + return ( +
+
+ {label} + {toolBarItems && + toolBarItems.map((item, i) => ( + + ))} +
+
+ ); +}; + +const ToolbarItemComponent: FunctionComponent<{ item: ToolbarItem }> = ({ + item, +}) => { + if (item.type === "button") { + const { onClick, color, label, tooltip, icon } = item; + if (icon) { + return ( + + + {icon} + {label && {label}} + + + ); + } else { + return ( + + + {label} + +     + + ); + } + } else if (item.type === "text") { + return ( + + {item.label}    + + ); + } else { + return unknown toolbar item type; + } +}; diff --git a/gui/src/app/Scripting/ScriptEditor.tsx b/gui/src/app/Scripting/ScriptEditor.tsx index 97ec153f..b96c4960 100644 --- a/gui/src/app/Scripting/ScriptEditor.tsx +++ b/gui/src/app/Scripting/ScriptEditor.tsx @@ -1,6 +1,7 @@ import { Help, PlayArrow } from "@mui/icons-material"; import { SplitDirection, Splitter } from "@SpComponents/Splitter"; -import TextEditor, { ToolbarItem } from "@SpComponents/TextEditor"; +import TextEditor from "@SpComponents/TextEditor"; +import { ToolbarItem } from "@SpComponents/ToolBar"; import { FileNames } from "@SpCore/FileMapping"; import { ProjectContext } from "@SpCore/ProjectContextProvider"; import { ProjectKnownFiles } from "@SpCore/ProjectDataModel"; diff --git a/gui/src/localStyles.css b/gui/src/localStyles.css index daf5216f..1654f6cd 100644 --- a/gui/src/localStyles.css +++ b/gui/src/localStyles.css @@ -116,12 +116,15 @@ span.EditorToolbarItem { letter-spacing: normal; display: inline-block; white-space: normal; + padding-left: 5px; + font-size: 14px; } @container EditorMenuBar (max-width: 500px) { .ToolbarButtonText { display: none; width: 0; + padding: 0; } span.EditorToolbarItem { padding: 0px;