mirror of
https://github.com/dstotijn/hetty.git
synced 2025-07-01 18:47:29 -04:00
Tidy up admin
structure
This commit is contained in:
21
admin/src/lib/components/CenteredPaper.tsx
Normal file
21
admin/src/lib/components/CenteredPaper.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { Paper } from "@mui/material";
|
||||
|
||||
function CenteredPaper({ children }: { children: React.ReactNode }): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<Paper
|
||||
elevation={0}
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
padding: 36,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Paper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CenteredPaper;
|
49
admin/src/lib/components/Editor.tsx
Normal file
49
admin/src/lib/components/Editor.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import MonacoEditor, { EditorProps } from "@monaco-editor/react";
|
||||
|
||||
const defaultMonacoOptions: EditorProps["options"] = {
|
||||
readOnly: true,
|
||||
wordWrap: "on",
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
};
|
||||
|
||||
type language = "html" | "typescript" | "json";
|
||||
|
||||
function languageForContentType(contentType?: string): language | undefined {
|
||||
switch (contentType?.toLowerCase()) {
|
||||
case "text/html":
|
||||
case "text/html; charset=utf-8":
|
||||
return "html";
|
||||
case "application/json":
|
||||
case "application/json; charset=utf-8":
|
||||
return "json";
|
||||
case "application/javascript":
|
||||
case "application/javascript; charset=utf-8":
|
||||
return "typescript";
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
interface Props {
|
||||
content: string;
|
||||
contentType?: string;
|
||||
monacoOptions?: EditorProps["options"];
|
||||
onChange?: EditorProps["onChange"];
|
||||
}
|
||||
|
||||
function Editor({ content, contentType, monacoOptions, onChange }: Props): JSX.Element {
|
||||
console.log(content);
|
||||
return (
|
||||
<MonacoEditor
|
||||
language={languageForContentType(contentType)}
|
||||
theme="vs-dark"
|
||||
options={{ ...defaultMonacoOptions, ...monacoOptions }}
|
||||
value={content}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default Editor;
|
25
admin/src/lib/components/HttpStatusIcon.tsx
Normal file
25
admin/src/lib/components/HttpStatusIcon.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
|
||||
import { SvgIconTypeMap } from "@mui/material";
|
||||
|
||||
interface Props {
|
||||
status: number;
|
||||
}
|
||||
|
||||
export default function HttpStatusIcon({ status }: Props): JSX.Element {
|
||||
let color: SvgIconTypeMap["props"]["color"] = "inherit";
|
||||
|
||||
switch (Math.floor(status / 100)) {
|
||||
case 2:
|
||||
case 3:
|
||||
color = "primary";
|
||||
break;
|
||||
case 4:
|
||||
color = "warning";
|
||||
break;
|
||||
case 5:
|
||||
color = "error";
|
||||
break;
|
||||
}
|
||||
|
||||
return <FiberManualRecordIcon sx={{ marginTop: "-.25rem", verticalAlign: "middle" }} color={color} />;
|
||||
}
|
36
admin/src/lib/components/ResponseStatus.tsx
Normal file
36
admin/src/lib/components/ResponseStatus.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { Typography } from "@mui/material";
|
||||
|
||||
import HttpStatusIcon from "./HttpStatusIcon";
|
||||
|
||||
import { HttpProtocol } from "lib/graphql/generated";
|
||||
|
||||
type ResponseStatusProps = {
|
||||
proto: HttpProtocol;
|
||||
statusCode: number;
|
||||
statusReason: string;
|
||||
};
|
||||
|
||||
function mapProto(proto: HttpProtocol): string {
|
||||
switch (proto) {
|
||||
case HttpProtocol.Http1:
|
||||
return "HTTP/1.1";
|
||||
case HttpProtocol.Http2:
|
||||
return "HTTP/2.0";
|
||||
default:
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
|
||||
export default function ResponseStatus({ proto, statusCode, statusReason }: ResponseStatusProps): JSX.Element {
|
||||
return (
|
||||
<Typography variant="h6" style={{ fontSize: "1rem", whiteSpace: "nowrap" }}>
|
||||
<HttpStatusIcon status={statusCode} />{" "}
|
||||
<Typography component="span" color="textSecondary">
|
||||
<Typography component="span" color="textSecondary" style={{ fontFamily: "'JetBrains Mono', monospace" }}>
|
||||
{mapProto(proto)}
|
||||
</Typography>
|
||||
</Typography>{" "}
|
||||
{statusCode} {statusReason}
|
||||
</Typography>
|
||||
);
|
||||
}
|
49
admin/src/lib/components/useContextMenu.tsx
Normal file
49
admin/src/lib/components/useContextMenu.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { Menu } from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
|
||||
interface ContextMenuProps {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function useContextMenu(): [
|
||||
(props: ContextMenuProps) => JSX.Element,
|
||||
(e: React.MouseEvent) => void,
|
||||
() => void
|
||||
] {
|
||||
const [contextMenu, setContextMenu] = useState<{
|
||||
mouseX: number;
|
||||
mouseY: number;
|
||||
} | null>(null);
|
||||
|
||||
const handleContextMenu = (event: React.MouseEvent) => {
|
||||
event.preventDefault();
|
||||
setContextMenu(
|
||||
contextMenu === null
|
||||
? {
|
||||
mouseX: event.clientX - 2,
|
||||
mouseY: event.clientY - 4,
|
||||
}
|
||||
: // repeated contextmenu when it is already open closes it with Chrome 84 on Ubuntu
|
||||
// Other native context menus might behave different.
|
||||
// With this behavior we prevent contextmenu from the backdrop to re-locale existing context menus.
|
||||
null
|
||||
);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setContextMenu(null);
|
||||
};
|
||||
|
||||
const menu = ({ children }: ContextMenuProps): JSX.Element => (
|
||||
<Menu
|
||||
open={contextMenu !== null}
|
||||
onClose={handleClose}
|
||||
anchorReference="anchorPosition"
|
||||
anchorPosition={contextMenu !== null ? { top: contextMenu.mouseY, left: contextMenu.mouseX } : undefined}
|
||||
>
|
||||
{children}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return [menu, handleContextMenu, handleClose];
|
||||
}
|
Reference in New Issue
Block a user