// Garde-fou navigation : popin si modifications non enregistrées.
const { useState, useEffect, useCallback, useRef, useContext, createContext } = React;
const UnsavedChangesContext = createContext(null);
function UnsavedLeaveDialog({ open, busy, onCancel, onSave, onDiscard }) {
if (!open) return null;
return (
e.stopPropagation()}
>
Modifications non enregistrées
Voulez-vous enregistrer vos modifications avant de quitter cette page ?
);
}
function UnsavedChangesProvider({ children, onNavigate }) {
const guardRef = useRef({ isDirty: false, save: null, discard: null });
const [pending, setPending] = useState(null);
const [busy, setBusy] = useState(false);
const [dirty, setDirty] = useState(false);
const registerGuard = useCallback((guard) => {
guardRef.current = guard || { isDirty: false, save: null, discard: null };
setDirty(!!guard?.isDirty);
}, []);
const requestNavigate = useCallback(
(next) => {
if (guardRef.current.isDirty) {
setPending(next);
return;
}
onNavigate(next);
},
[onNavigate],
);
const cancelLeave = useCallback(() => setPending(null), []);
const discardLeave = useCallback(() => {
const next = pending;
setPending(null);
guardRef.current.discard?.();
if (next != null) onNavigate(next);
}, [pending, onNavigate]);
const saveAndLeave = useCallback(async () => {
const next = pending;
if (!guardRef.current.save) {
discardLeave();
return;
}
setBusy(true);
try {
await guardRef.current.save();
setPending(null);
if (next != null) onNavigate(next);
} catch (e) {
alert(e?.message || "Enregistrement impossible.");
} finally {
setBusy(false);
}
}, [pending, onNavigate, discardLeave]);
useEffect(() => {
if (!dirty) return undefined;
function onBeforeUnload(e) {
e.preventDefault();
e.returnValue = "";
}
window.addEventListener("beforeunload", onBeforeUnload);
return () => window.removeEventListener("beforeunload", onBeforeUnload);
}, [dirty]);
const value = { registerGuard, requestNavigate };
return (
{children}
void saveAndLeave()}
onDiscard={discardLeave}
/>
);
}
function useUnsavedChangesGuard({ isDirty, onSave, onDiscard }) {
const ctx = useContext(UnsavedChangesContext);
useEffect(() => {
if (!ctx) return undefined;
ctx.registerGuard(
isDirty
? { isDirty: true, save: onSave || null, discard: onDiscard || null }
: null,
);
return () => ctx.registerGuard(null);
}, [ctx, isDirty, onSave, onDiscard]);
}
function useGuardedNavigate() {
const ctx = useContext(UnsavedChangesContext);
return ctx?.requestNavigate;
}
Object.assign(window, {
UnsavedChangesProvider,
useUnsavedChangesGuard,
useGuardedNavigate,
});