diff --git a/optuna_dashboard/ts/components/Note.tsx b/optuna_dashboard/ts/components/Note.tsx index f563c7f4..86091ddc 100644 --- a/optuna_dashboard/ts/components/Note.tsx +++ b/optuna_dashboard/ts/components/Note.tsx @@ -4,6 +4,11 @@ import { Card, CardContent, CardHeader, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, IconButton, SxProps, TextField, @@ -23,6 +28,8 @@ import { CodeComponent, ReactMarkdownNames, } from "react-markdown/lib/ast-to-react" +import HtmlIcon from "@mui/icons-material/Html" +import ModeEditIcon from "@mui/icons-material/ModeEdit" import { Prism as SyntaxHighlighter } from "react-syntax-highlighter" import { darcula } from "react-syntax-highlighter/dist/esm/styles/prism" @@ -75,20 +82,72 @@ export const StudyNote: FC<{ return } +const useConfirmCloseDialog = ( + handleClose: () => void +): [() => void, () => React.ReactNode] => { + const theme = useTheme() + const [open, setOpen] = useState(false) + + const zIndex = theme.zIndex.snackbar - 1 + const openDialog = () => { + setOpen(true) + } + const renderDialog = () => { + return ( + + Unsaved changes + + + Do you want to save or discard your changes? + + + + + + + + ) + } + return [openDialog, renderDialog] +} + const MarkdownEditorModal: FC<{ studyId: number trialId?: number latestNote: Note - onClose: () => void -}> = ({ studyId, trialId, latestNote, onClose }) => { + setEditorUnmount: () => void +}> = ({ studyId, trialId, latestNote, setEditorUnmount }) => { const theme = useTheme() const action = actionCreator() + const [openConfirmCloseDialog, renderConfirmCloseDialog] = + useConfirmCloseDialog(() => { + setEditorUnmount() + window.onbeforeunload = null + }) + const [saving, setSaving] = useState(false) const [edited, setEdited] = useState(false) const [curNote, setCurNote] = useState({ version: 0, body: "" }) const textAreaRef = createRef() const notLatest = latestNote.version > curNote.version + const [previewMarkdown, setPreviewMarkdown] = useState("") + const [preview, setPreview] = useState(false) + useEffect(() => { setCurNote(latestNote) }, []) @@ -118,6 +177,7 @@ const MarkdownEditorModal: FC<{ .then(() => { setCurNote(newNote) window.onbeforeunload = null + setEditorUnmount() }) .finally(() => { setSaving(false) @@ -134,7 +194,7 @@ const MarkdownEditorModal: FC<{ } // See https://github.com/iamhosseindhv/notistack/issues/231#issuecomment-825924840 - const zIndex = theme.zIndex.snackbar - 1 + const zIndex = theme.zIndex.snackbar - 2 return ( + { + setPreview(!preview) + setPreviewMarkdown( + textAreaRef.current ? textAreaRef.current.value : "" + ) + }} + > + {preview ? ( + + ) : ( + + )} + + } + title="Markdown Editor" + /> + + + { const cur = textAreaRef.current ? textAreaRef.current.value : "" - setEdited(cur !== curNote.body) + if (edited !== (cur !== curNote.body)) { + setEdited(cur !== curNote.body) + } }} /> @@ -201,7 +297,17 @@ const MarkdownEditorModal: FC<{ )} - + {renderConfirmCloseDialog()} ) } @@ -227,18 +334,18 @@ const NoteBase: FC<{ cardSx?: SxProps }> = ({ studyId, trialId, latestNote, cardSx }) => { const theme = useTheme() - const [editorOpen, setEditorOpen] = useState(false) + const [editorMounted, setEditorMounted] = useState(false) const defaultBody = "*A markdown editor for taking a memo, related to the study. Click the 'Edit' button in the upper right corner to access the editor.*" return ( - + { - setEditorOpen(true) + setEditorMounted(true) }} > @@ -254,13 +361,13 @@ const NoteBase: FC<{ components={{ code: CodeBlock }} /> - {editorOpen && ( + {editorMounted && ( { - setEditorOpen(false) + setEditorUnmount={() => { + setEditorMounted(false) }} /> )}