diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditor.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditor.tsx index 3361e3ab5a..d3f376c9ac 100644 --- a/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditor.tsx +++ b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditor.tsx @@ -21,13 +21,20 @@ import React, { useReducer, useState, } from 'react'; +import { useKeyboardEvent } from '@react-hookz/web'; import useDebounce from 'react-use/lib/useDebounce'; import { useApiHolder } from '@backstage/core-plugin-api'; import { JsonObject, JsonValue } from '@backstage/types'; import { yaml as yamlSupport } from '@codemirror/legacy-modes/mode/yaml'; import { showPanel } from '@codemirror/view'; import { StreamLanguage } from '@codemirror/language'; -import { IconButton, makeStyles, Divider } from '@material-ui/core'; +import { + IconButton, + makeStyles, + Divider, + Tooltip, + Paper, +} from '@material-ui/core'; import SaveIcon from '@material-ui/icons/Save'; import RefreshIcon from '@material-ui/icons/Refresh'; import CloseIcon from '@material-ui/icons/Close'; @@ -99,6 +106,11 @@ const useStyles = makeStyles(theme => ({ left: 0, right: 0, }, + editorFloatingButtons: { + position: 'absolute', + top: theme.spacing(1), + right: theme.spacing(3), + }, preview: { gridArea: 'preview', overflow: 'auto', @@ -108,11 +120,7 @@ const useStyles = makeStyles(theme => ({ }, })); -export const TemplateEditor = ({ - directory, - fieldExtensions = [], - onClose, -}: { +export const TemplateEditor = (props: { directory: TemplateDirectoryAccess; fieldExtensions?: FieldExtensionOptions[]; onClose?: () => void; @@ -122,12 +130,12 @@ export const TemplateEditor = ({ const [errorText, setErrorText] = useState(); return ( - +
- +
@@ -135,7 +143,7 @@ export const TemplateEditor = ({
@@ -148,23 +156,52 @@ export const TemplateEditor = ({ ); }; -function TemplateEditorBrowser() { +function TemplateEditorBrowser(props: { onClose?: () => void }) { const classes = useStyles(); const directoryEditor = useDirectoryEditor(); + const changedFiles = directoryEditor.files.filter(file => file.dirty); + + const handleClose = () => { + if (!props.onClose) { + return; + } + if (changedFiles.length > 0) { + // eslint-disable-next-line no-alert + const accepted = window.confirm( + 'Are you sure? Unsaved changes will be lost', + ); + if (!accepted) { + return; + } + } + props.onClose(); + }; return ( <>
- - - - - - + + directoryEditor.save()} + > + + + + + directoryEditor.reload()} + > + + +
- - - + + + + +
e.key === 's' && (e.ctrlKey || e.metaKey), + e => { + e.preventDefault(); + directoryEditor.save(); + }, + ); + return ( - ({ dom: errorPanel, top: true })), - ]} - value={directoryEditor.selectedFile?.content} - onChange={content => directoryEditor.selectedFile?.updateContent(content)} - /> + <> + ({ dom: errorPanel, top: true })), + ]} + value={directoryEditor.selectedFile?.content} + onChange={content => + directoryEditor.selectedFile?.updateContent(content) + } + /> + {directoryEditor.selectedFile?.dirty && ( +
+ + + directoryEditor.save()} + > + + + + + directoryEditor.reload()} + > + + + + +
+ )} + ); }