83 lines
1.7 KiB
TypeScript
83 lines
1.7 KiB
TypeScript
import { Alert, Box, Button, CircularProgress } from "@mui/material";
|
|
import React from "react";
|
|
|
|
const State = {
|
|
Loading: 0,
|
|
Ready: 1,
|
|
Error: 2,
|
|
} as const;
|
|
|
|
type State = keyof typeof State;
|
|
|
|
export function AsyncWidget(p: {
|
|
loadKey: unknown;
|
|
load: () => Promise<void>;
|
|
errMsg: string;
|
|
build: () => React.ReactElement;
|
|
ready?: boolean;
|
|
errAdditionalElement?: () => React.ReactElement;
|
|
}): React.ReactElement {
|
|
const [state, setState] = React.useState<number>(State.Loading);
|
|
|
|
const load = async () => {
|
|
try {
|
|
setState(State.Loading);
|
|
await p.load();
|
|
setState(State.Ready);
|
|
} catch (e) {
|
|
console.error(e);
|
|
setState(State.Error);
|
|
}
|
|
};
|
|
|
|
React.useEffect(() => {
|
|
load();
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [p.loadKey]);
|
|
|
|
if (state === State.Error)
|
|
return (
|
|
<Box
|
|
component="div"
|
|
sx={{
|
|
display: "flex",
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
height: "100%",
|
|
flex: "1",
|
|
flexDirection: "column",
|
|
}}
|
|
>
|
|
<Alert
|
|
variant="outlined"
|
|
severity="error"
|
|
style={{ margin: "0px 15px 15px 15px" }}
|
|
>
|
|
{p.errMsg}
|
|
</Alert>
|
|
|
|
<Button onClick={load}>Try again</Button>
|
|
|
|
{p.errAdditionalElement?.()}
|
|
</Box>
|
|
);
|
|
|
|
if (state === State.Loading || p.ready === false)
|
|
return (
|
|
<Box
|
|
component="div"
|
|
sx={{
|
|
display: "flex",
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
height: "100%",
|
|
flex: "1",
|
|
}}
|
|
>
|
|
<CircularProgress />
|
|
</Box>
|
|
);
|
|
|
|
return p.build();
|
|
}
|