ra-core
Version:
Core components of react-admin, a frontend Framework for building admin applications on top of REST services, using ES6, React
80 lines • 4.03 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.useWarnWhenUnsavedChanges = void 0;
var react_1 = require("react");
var react_hook_form_1 = require("react-hook-form");
var react_router_dom_1 = require("react-router-dom");
var i18n_1 = require("../i18n");
/**
* Display a confirmation dialog if the form has unsaved changes.
* - If the user confirms, the navigation continues and the changes are lost.
* - If the user cancels, the navigation is cancelled and the changes are kept.
*/
var useWarnWhenUnsavedChanges = function (enable, formRootPathname, control) {
var translate = (0, i18n_1.useTranslate)();
var _a = (0, react_hook_form_1.useFormState)(control ? { control: control } : undefined), isSubmitSuccessful = _a.isSubmitSuccessful, dirtyFields = _a.dirtyFields;
var isDirty = Object.keys(dirtyFields).length > 0;
var _b = (0, react_1.useState)(false), shouldNotify = _b[0], setShouldNotify = _b[1];
var shouldNotBlock = !enable || !isDirty || isSubmitSuccessful;
var blocker = (0, react_router_dom_1.useBlocker)(function (_a) {
var currentLocation = _a.currentLocation, nextLocation = _a.nextLocation;
if (shouldNotBlock)
return false;
// Also check if the new location is inside the form
var initialLocation = formRootPathname || currentLocation.pathname;
var newLocationIsInsideCurrentLocation = nextLocation.pathname.startsWith(initialLocation);
var newLocationIsShowView = nextLocation.pathname.startsWith("".concat(initialLocation, "/show"));
var newLocationIsInsideForm = newLocationIsInsideCurrentLocation && !newLocationIsShowView;
if (newLocationIsInsideForm)
return false;
return true;
});
(0, react_1.useEffect)(function () {
if (blocker.state === 'blocked') {
// Corner case: the blocker might be triggered by a redirect in the onSuccess side effect,
// happening during the same tick the form is reset after a successful save.
// In that case, the blocker will block but shouldNotBlock will be true one tick after.
// If we are in that case, we can proceed immediately.
if (shouldNotBlock) {
blocker.proceed();
return;
}
setShouldNotify(true);
}
// This effect should only run when the blocker state changes, not when shouldNotBlock changes.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [blocker.state]);
(0, react_1.useEffect)(function () {
if (shouldNotify) {
var shouldProceed = window.confirm(translate('ra.message.unsaved_changes'));
if (shouldProceed) {
blocker.proceed && blocker.proceed();
}
else {
blocker.reset && blocker.reset();
}
}
setShouldNotify(false);
// Can't use blocker in the dependency array because it is not stable across rerenders
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [shouldNotify, translate]);
// This effect handles document navigation, e.g. closing the tab
(0, react_1.useEffect)(function () {
var beforeunload = function (e) {
// Invoking event.preventDefault() will trigger a warning dialog when the user closes or navigates the tab
// https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event#examples
e.preventDefault();
// Included for legacy support, e.g. Chrome/Edge < 119
e.returnValue = true;
};
if (shouldNotBlock) {
return;
}
window.addEventListener('beforeunload', beforeunload);
return function () {
window.removeEventListener('beforeunload', beforeunload);
};
}, [shouldNotBlock]);
};
exports.useWarnWhenUnsavedChanges = useWarnWhenUnsavedChanges;
//# sourceMappingURL=useWarnWhenUnsavedChanges.js.map
;