79 lines
1.5 KiB
TypeScript
79 lines
1.5 KiB
TypeScript
|
import { Alert, Button, CircularProgress } from "@mui/material";
|
||
|
import { PropsWithChildren, useEffect, useRef, useState } from "react";
|
||
|
|
||
|
enum State {
|
||
|
Loading,
|
||
|
Ready,
|
||
|
Error,
|
||
|
}
|
||
|
|
||
|
export function AsyncWidget(p: {
|
||
|
loadKey: any;
|
||
|
load: () => Promise<void>;
|
||
|
errMsg: string;
|
||
|
build: () => React.ReactElement;
|
||
|
}): React.ReactElement {
|
||
|
const [state, setState] = useState(State.Loading);
|
||
|
|
||
|
const counter = useRef<any | null>(null);
|
||
|
|
||
|
const load = async () => {
|
||
|
try {
|
||
|
setState(State.Loading);
|
||
|
await p.load();
|
||
|
setState(State.Ready);
|
||
|
} catch (e) {
|
||
|
console.error(e);
|
||
|
setState(State.Error);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
useEffect(() => {
|
||
|
if (counter.current === p.loadKey) return;
|
||
|
counter.current = p.loadKey;
|
||
|
|
||
|
load();
|
||
|
});
|
||
|
|
||
|
if (state === State.Loading)
|
||
|
return (
|
||
|
<div
|
||
|
style={{
|
||
|
display: "flex",
|
||
|
justifyContent: "center",
|
||
|
alignItems: "center",
|
||
|
height: "100%",
|
||
|
flex: "1",
|
||
|
}}
|
||
|
>
|
||
|
<CircularProgress />
|
||
|
</div>
|
||
|
);
|
||
|
|
||
|
if (state === State.Error)
|
||
|
return (
|
||
|
<div
|
||
|
style={{
|
||
|
display: "flex",
|
||
|
justifyContent: "center",
|
||
|
alignItems: "center",
|
||
|
height: "100%",
|
||
|
flex: "1",
|
||
|
flexDirection: "column",
|
||
|
}}
|
||
|
>
|
||
|
<Alert
|
||
|
variant="outlined"
|
||
|
severity="error"
|
||
|
style={{ marginBottom: "15px" }}
|
||
|
>
|
||
|
{p.errMsg}
|
||
|
</Alert>
|
||
|
|
||
|
<Button onClick={load}>Réessayer</Button>
|
||
|
</div>
|
||
|
);
|
||
|
|
||
|
return p.build();
|
||
|
}
|