add full screen to monaco editors

This commit is contained in:
Sidney Alcantara
2021-11-02 12:36:12 +11:00
parent 7cc74dba9b
commit 2ce4fa55a6
5 changed files with 161 additions and 68 deletions

View File

@@ -1,3 +1,4 @@
import { useState } from "react";
import {
DiffEditor as MonacoDiffEditor,
DiffEditorProps,
@@ -5,16 +6,18 @@ import {
} from "@monaco-editor/react";
import { useTheme, Box, BoxProps } from "@mui/material";
import TrapFocus from "@mui/material/Unstable_TrapFocus";
import CircularProgressOptical from "@src/components/CircularProgressOptical";
import ResizeBottomRightIcon from "@src/assets/icons/ResizeBottomRight";
import useMonacoCustomizations, {
IUseMonacoCustomizationsProps,
} from "./useMonacoCustomizations";
import FullScreenButton from "./FullScreenButton";
export interface IDiffEditorProps
extends Partial<DiffEditorProps>,
IUseMonacoCustomizationsProps {
Omit<IUseMonacoCustomizationsProps, "fullScreen"> {
onChange?: EditorProps["onChange"];
containerProps?: Partial<BoxProps>;
}
@@ -34,6 +37,8 @@ export default function DiffEditor({
}: IDiffEditorProps) {
const theme = useTheme();
const [fullScreen, setFullScreen] = useState(false);
const { boxSx } = useMonacoCustomizations({
minHeight,
disabled,
@@ -41,6 +46,7 @@ export default function DiffEditor({
extraLibs,
diagnosticsOptions,
onUnmount,
fullScreen,
});
// Needs manual patch since `onMount` prop is not available in `DiffEditor`
@@ -55,39 +61,50 @@ export default function DiffEditor({
};
return (
<Box sx={{ ...boxSx, ...containerProps?.sx }}>
<MonacoDiffEditor
language="javascript"
loading={<CircularProgressOptical size={20} sx={{ m: 2 }} />}
className="editor"
{...props}
onMount={handleEditorMount}
options={
{
readOnly: disabled,
fontFamily: theme.typography.fontFamilyMono,
rulers: [80],
minimap: { enabled: false },
lineNumbersMinChars: 4,
lineDecorationsWidth: "18",
automaticLayout: true,
fixedOverflowWidgets: true,
tabSize: 2,
...props.options,
} as any
}
/>
<TrapFocus open={fullScreen}>
<Box
sx={{ ...boxSx, ...containerProps?.sx }}
style={fullScreen ? { height: "100%" } : {}}
>
<MonacoDiffEditor
language="javascript"
loading={<CircularProgressOptical size={20} sx={{ m: 2 }} />}
className="editor"
{...props}
onMount={handleEditorMount}
options={
{
readOnly: disabled,
fontFamily: theme.typography.fontFamilyMono,
rulers: [80],
minimap: { enabled: false },
lineNumbersMinChars: 4,
lineDecorationsWidth: "18",
automaticLayout: true,
fixedOverflowWidgets: true,
tabSize: 2,
...props.options,
} as any
}
/>
<ResizeBottomRightIcon
aria-label="Resize code editor"
color="action"
sx={{
position: "absolute",
bottom: 1,
right: 1,
zIndex: 1,
}}
/>
</Box>
<FullScreenButton
onClick={() => setFullScreen((f) => !f)}
active={fullScreen}
style={{ right: 32 }}
/>
<ResizeBottomRightIcon
aria-label="Resize code editor"
color="action"
sx={{
position: "absolute",
bottom: 1,
right: 1,
zIndex: 1,
}}
/>
</Box>
</TrapFocus>
);
}

View File

@@ -0,0 +1,32 @@
import { Button, ButtonProps } from "@mui/material";
import FullscreenIcon from "@mui/icons-material/Fullscreen";
import FullscreenExitIcon from "@mui/icons-material/FullscreenExit";
export interface IFullScreenButtonProps extends ButtonProps {
active: boolean;
}
export default function FullScreenButton({
active,
...props
}: IFullScreenButtonProps) {
return (
<Button
aria-label={`${active ? "Exit" : "Enter"} full screen`}
variant={active ? "contained" : "outlined"}
color={active ? "secondary" : undefined}
{...props}
style={{
position: "absolute",
bottom: 4,
right: 16,
zIndex: 2,
minWidth: 32,
padding: 0,
...props.style,
}}
>
{active ? <FullscreenExitIcon /> : <FullscreenIcon />}
</Button>
);
}

View File

@@ -3,16 +3,18 @@ import Editor, { EditorProps } from "@monaco-editor/react";
import type { editor } from "monaco-editor/esm/vs/editor/editor.api";
import { useTheme, Box, BoxProps } from "@mui/material";
import TrapFocus from "@mui/material/Unstable_TrapFocus";
import CircularProgressOptical from "@src/components/CircularProgressOptical";
import ResizeBottomRightIcon from "@src/assets/icons/ResizeBottomRight";
import useMonacoCustomizations, {
IUseMonacoCustomizationsProps,
} from "./useMonacoCustomizations";
import FullScreenButton from "./FullScreenButton";
export interface ICodeEditorProps
extends Partial<EditorProps>,
IUseMonacoCustomizationsProps {
Omit<IUseMonacoCustomizationsProps, "fullScreen"> {
value: string;
containerProps?: Partial<BoxProps>;
@@ -44,6 +46,7 @@ export default function CodeEditor({
// Store editor value to prevent code editor values not being saved when
// Side Drawer is in the middle of a refresh
const [initialEditorValue] = useState(value ?? "");
const [fullScreen, setFullScreen] = useState(false);
const { boxSx } = useMonacoCustomizations({
minHeight,
@@ -52,6 +55,7 @@ export default function CodeEditor({
extraLibs,
diagnosticsOptions,
onUnmount,
fullScreen,
});
const onValidate_: EditorProps["onValidate"] = (markers) => {
@@ -60,38 +64,50 @@ export default function CodeEditor({
};
return (
<Box sx={{ ...boxSx, ...containerProps?.sx }}>
<Editor
defaultLanguage="javascript"
value={initialEditorValue}
loading={<CircularProgressOptical size={20} sx={{ m: 2 }} />}
className="editor"
{...props}
onValidate={onValidate_}
options={{
readOnly: disabled,
fontFamily: theme.typography.fontFamilyMono,
rulers: [80],
minimap: { enabled: false },
lineNumbersMinChars: 4,
lineDecorationsWidth: 0,
automaticLayout: true,
fixedOverflowWidgets: true,
tabSize: 2,
...props.options,
}}
/>
<TrapFocus open={fullScreen}>
<Box
sx={{ ...boxSx, ...containerProps?.sx }}
style={fullScreen ? { height: "100%" } : {}}
>
<Editor
defaultLanguage="javascript"
value={initialEditorValue}
loading={<CircularProgressOptical size={20} sx={{ m: 2 }} />}
className="editor"
{...props}
onValidate={onValidate_}
options={{
readOnly: disabled,
fontFamily: theme.typography.fontFamilyMono,
rulers: [80],
minimap: { enabled: false },
lineNumbersMinChars: 4,
lineDecorationsWidth: 0,
automaticLayout: true,
fixedOverflowWidgets: true,
tabSize: 2,
...props.options,
}}
/>
<ResizeBottomRightIcon
aria-label="Resize code editor"
color="action"
sx={{
position: "absolute",
bottom: 1,
right: 1,
zIndex: 1,
}}
/>
</Box>
<FullScreenButton
onClick={() => setFullScreen((f) => !f)}
active={fullScreen}
/>
{!fullScreen && (
<ResizeBottomRightIcon
aria-label="Resize code editor"
color="action"
sx={{
position: "absolute",
bottom: 1,
right: 1,
zIndex: 1,
}}
/>
)}
</Box>
</TrapFocus>
);
}

View File

@@ -26,6 +26,9 @@ export interface IUseMonacoCustomizationsProps {
extraLibs?: string[];
diagnosticsOptions?: languages.typescript.DiagnosticsOptions;
onUnmount?: () => void;
// Internal only
fullScreen?: boolean;
}
export default function useMonacoCustomizations({
@@ -36,6 +39,8 @@ export default function useMonacoCustomizations({
extraLibs,
diagnosticsOptions,
onUnmount,
fullScreen,
}: IUseMonacoCustomizationsProps) {
const theme = useTheme();
const { tableState } = useProjectContext();
@@ -157,7 +162,7 @@ export default function useMonacoCustomizations({
}
}, [tableState?.columns, monaco, diagnosticsOptions, extraLibs]);
const boxSx: SxProps<Theme> = {
let boxSx: SxProps<Theme> = {
minWidth: 400,
minHeight,
height: minHeight,
@@ -214,5 +219,22 @@ export default function useMonacoCustomizations({
},
};
if (fullScreen)
boxSx = {
...boxSx,
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: theme.zIndex.tooltip + 1,
m: "0 !important",
resize: "none",
backgroundColor: theme.palette.background.paper,
borderRadius: 0,
"&::after": { display: "none" },
};
return { boxSx };
}

View File

@@ -45,6 +45,9 @@ import SnackbarProgress, {
} from "@src/components/SnackbarProgress";
import CircularProgressOptical from "@src/components/CircularProgressOptical";
import CodeEditor from "@src/components/CodeEditor";
import DiffEditor from "@src/components/CodeEditor/DiffEditor";
const typographyVariants = [
"h1",
"h2",
@@ -928,6 +931,9 @@ export default function TestView() {
</Stack>
<LinearProgress />
<CodeEditor value={`x\n`} />
<DiffEditor original={`x\n`} modified="y" />
</Stack>
</Container>
</Navigation>