Skip to content

Commit

Permalink
Merge pull request #170 from flatironinstitute/editor-toolbar-cleanup
Browse files Browse the repository at this point in the history
Update toolbar, smarter hiding of labels
  • Loading branch information
WardBrian authored Jul 29, 2024
2 parents e412fbd + 61d7a3d commit 07586d1
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 128 deletions.
35 changes: 16 additions & 19 deletions gui/src/app/FileEditor/StanFileEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
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 TextEditor from "@SpComponents/TextEditor";
import { ToolbarItem } from "@SpComponents/ToolBar";
import compileStanProgram from "@SpStanc/compileStanProgram";
import { stancErrorsToCodeMarkers } from "@SpStanc/Linting";
import useStanc from "@SpStanc/useStanc";
Expand Down Expand Up @@ -123,7 +123,6 @@ const StanFileEditor: FunctionComponent<Props> = ({
}
}, [fileContent, handleCompile, didInitialCompile]);

const showLabelsOnButtons = useMediaQuery("(min-width:600px)");
const [syntaxWindowVisible, setSyntaxWindowVisible] = useState(false);

const toolbarItems: ToolbarItem[] = useMemo(() => {
Expand All @@ -134,7 +133,7 @@ const StanFileEditor: FunctionComponent<Props> = ({
ret.push({
type: "button",
icon: <Cancel />,
label: showLabelsOnButtons ? "Syntax error" : "",
label: "Syntax error",
color: "darkred",
tooltip: "Syntax error in Stan file",
onClick: () => {
Expand All @@ -145,7 +144,7 @@ const StanFileEditor: FunctionComponent<Props> = ({
ret.push({
type: "button",
icon: <Cancel />,
label: showLabelsOnButtons ? "Syntax warning" : "",
label: "Syntax warning",
color: "blue",
tooltip: "Syntax warning in Stan file",
onClick: () => {
Expand All @@ -155,25 +154,23 @@ const StanFileEditor: FunctionComponent<Props> = ({
}

// auto format
if (!readOnly) {
if (editedFileContent) {
ret.push({
type: "button",
icon: <AutoFixHigh />,
tooltip: "Auto format this stan file",
label: showLabelsOnButtons ? "auto format" : undefined,
onClick: requestFormat,
color: "darkblue",
});
}
if (!readOnly && editedFileContent && validSyntax) {
ret.push({
type: "button",
icon: <AutoFixHigh />,
tooltip: "Auto format this stan file",
label: "Auto format",
onClick: requestFormat,
color: "darkblue",
});
}
if (editedFileContent && editedFileContent === fileContent) {
if (compileStatus !== "compiling") {
if (validSyntax) {
ret.push({
type: "button",
tooltip: "Compile Stan model",
label: "compile",
label: "Compile",
icon: <Settings />,
onClick: handleCompile,
color: "darkblue",
Expand All @@ -183,7 +180,8 @@ const StanFileEditor: FunctionComponent<Props> = ({
if (compileStatus !== "") {
ret.push({
type: "text",
label: compileMessage,
label:
compileMessage.charAt(0).toUpperCase() + compileMessage.slice(1),
color:
compileStatus === "compiled"
? "green"
Expand All @@ -200,7 +198,6 @@ const StanFileEditor: FunctionComponent<Props> = ({
fileContent,
handleCompile,
requestFormat,
showLabelsOnButtons,
validSyntax,
compileStatus,
compileMessage,
Expand Down
112 changes: 14 additions & 98 deletions gui/src/app/FileEditor/TextEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
/* 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 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 {
FunctionComponent,
PropsWithChildren,
useCallback,
useEffect,
useMemo,
useState,
} from "react";

Expand All @@ -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<Props> = ({
text,
onSaveText,
Expand Down Expand Up @@ -131,32 +115,19 @@ const TextEditor: FunctionComponent<Props> = ({
[onSaveText, readOnly],
);

const edited = useMemo(() => {
return editedText !== text;
}, [editedText, text]);

return (
<div className="EditorWithToolbar" onKeyDown={handleKeyDown}>
<NotSelectable>
<div className="EditorMenuBar">
<span className="EditorTitle">{label}</span>
&nbsp;&nbsp;&nbsp;
{!readOnly && text !== editedText && (
<SmallIconButton
onClick={onSaveText}
icon={<Save />}
title="Save file"
disabled={text === editedText}
label="save"
/>
)}
&nbsp;&nbsp;&nbsp;
{editedText !== text && <span className="EditedText">edited</span>}
&nbsp;&nbsp;&nbsp;
{readOnly && <span className="ReadOnlyText">read only</span>}
&nbsp;&nbsp;&nbsp;
{toolbarItems &&
toolbarItems.map((item, i) => (
<ToolbarItemComponent key={i} item={item} />
))}
</div>
</NotSelectable>
<ToolBar
items={toolbarItems || []}
label={label}
onSaveText={onSaveText}
edited={edited}
readOnly={!!readOnly}
/>
<Editor
defaultLanguage={language}
onChange={handleChange}
Expand Down Expand Up @@ -186,61 +157,6 @@ const toMonacoMarkerSeverity = (
}
};

const ToolbarItemComponent: FunctionComponent<{ item: ToolbarItem }> = ({
item,
}) => {
if (item.type === "button") {
const { onClick, color, label, tooltip, icon } = item;
if (icon) {
return (
<span style={{ color }}>
<SmallIconButton
onClick={onClick}
icon={icon}
title={tooltip}
label={label}
disabled={!onClick}
/>
&nbsp;&nbsp;&nbsp;
</span>
);
} else {
if (!onClick) {
return (
<span style={{ color: color || "gray" }} title={label}>
{label}&nbsp;&nbsp;&nbsp;
</span>
);
}
return (
<span>
<Link
onClick={onClick}
color={color || "gray"}
component="button"
underline="none"
>
{label}
</Link>
&nbsp;&nbsp;&nbsp;
</span>
);
}
} else if (item.type === "text") {
return (
<span style={{ color: item.color || "gray" }} title={item.label}>
{item.label}&nbsp;&nbsp;&nbsp;
</span>
);
} else {
return <span>unknown toolbar item type</span>;
}
};

const NotSelectable: FunctionComponent<PropsWithChildren> = ({ children }) => {
return <div className="NotSelectable">{children}</div>;
};

const createHintTextContentWidget = (content: string | HTMLSpanElement) => {
return {
getDomNode: () => {
Expand Down
125 changes: 125 additions & 0 deletions gui/src/app/FileEditor/ToolBar.tsx
Original file line number Diff line number Diff line change
@@ -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<ToolbarProps> = ({
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: <Save />,
onClick: onSaveText,
tooltip: "Save file",
label: "Save",
});
editorItems.push({
type: "text",
label: "Edited",
color: "red",
});
}

return editorItems.concat(items);
}, [edited, items, onSaveText, readOnly]);

return (
<div className="NotSelectable">
<div className="EditorMenuBar">
<span className="EditorTitle">{label}</span>
{toolBarItems &&
toolBarItems.map((item, i) => (
<ToolbarItemComponent key={i} item={item} />
))}
</div>
</div>
);
};

const ToolbarItemComponent: FunctionComponent<{ item: ToolbarItem }> = ({
item,
}) => {
if (item.type === "button") {
const { onClick, color, label, tooltip, icon } = item;
if (icon) {
return (
<span className="EditorToolbarItem" style={{ color }}>
<IconButton
onClick={onClick}
disabled={!onClick}
color="inherit"
size="small"
title={tooltip}
>
{icon}
{label && <span className="ToolbarButtonText">{label}</span>}
</IconButton>
</span>
);
} else {
return (
<span className="EditorToolbarItem">
<Link
onClick={onClick}
color={color || "gray"}
component="button"
underline="none"
title={tooltip}
>
{label}
</Link>
&nbsp;&nbsp;&nbsp;
</span>
);
}
} else if (item.type === "text") {
return (
<span
className="EditorToolbarItem"
style={{ color: item.color || "gray" }}
title={item.label}
>
{item.label}&nbsp;&nbsp;&nbsp;
</span>
);
} else {
return <span>unknown toolbar item type</span>;
}
};
3 changes: 2 additions & 1 deletion gui/src/app/Scripting/ScriptEditor.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand Down
Loading

0 comments on commit 07586d1

Please sign in to comment.