// 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, });