Files
OpenFlare/frontend/app/(main)/waf/rules/editor/components/unsaved-changes.tsx
T
2026-07-13 15:10:28 +08:00

104 lines
3.2 KiB
TypeScript

'use client';
import { useEffect } from 'react';
import { getHistoryTransition } from './editor-behavior';
const historyIndexKey = '__wafEditorIndex';
export function UnsavedChanges({ dirty }: { dirty: boolean }) {
useEffect(() => {
if (!dirty) return;
const initialState =
history.state && typeof history.state === 'object' ? history.state : {};
let currentIndex = Number.isInteger(initialState[historyIndexKey])
? (initialState[historyIndexKey] as number)
: 0;
const currentUrl =
window.location.pathname + window.location.search + window.location.hash;
history.replaceState(
{ ...initialState, [historyIndexKey]: currentIndex },
'',
);
const originalPushState = history.pushState.bind(history);
const originalReplaceState = history.replaceState.bind(history);
history.pushState = (data, unused, url) => {
currentIndex++;
originalPushState(
{ ...data, [historyIndexKey]: currentIndex },
unused,
url,
);
};
history.replaceState = (data, unused, url) =>
originalReplaceState(
{ ...data, [historyIndexKey]: currentIndex },
unused,
url,
);
let restoring = false;
const handler = (event: BeforeUnloadEvent) => {
if (dirty) event.preventDefault();
};
const clickHandler = (event: MouseEvent) => {
if (
!dirty ||
event.defaultPrevented ||
event.button !== 0 ||
event.metaKey ||
event.ctrlKey ||
event.shiftKey ||
event.altKey
)
return;
const link = (event.target as Element | null)?.closest(
'a[href]',
) as HTMLAnchorElement | null;
if (
!link ||
link.target === '_blank' ||
new URL(link.href, window.location.href).origin !==
window.location.origin
)
return;
if (!window.confirm('存在未保存的更改,确定离开吗?'))
event.preventDefault();
};
const popstateHandler = (event: PopStateEvent) => {
const hasTargetIndex = Number.isInteger(event.state?.[historyIndexKey]);
const targetIndex = hasTargetIndex
? (event.state[historyIndexKey] as number)
: currentIndex;
if (restoring) {
restoring = false;
currentIndex = targetIndex;
return;
}
if (window.confirm('存在未保存的更改,确定离开吗?')) {
currentIndex = targetIndex;
return;
}
if (!hasTargetIndex) {
originalPushState(
{ ...initialState, [historyIndexKey]: currentIndex },
'',
currentUrl,
);
return;
}
restoring = true;
history.go(getHistoryTransition(currentIndex, targetIndex).restoreDelta);
};
window.addEventListener('beforeunload', handler);
document.addEventListener('click', clickHandler, true);
window.addEventListener('popstate', popstateHandler);
return () => {
history.pushState = originalPushState;
history.replaceState = originalReplaceState;
window.removeEventListener('beforeunload', handler);
document.removeEventListener('click', clickHandler, true);
window.removeEventListener('popstate', popstateHandler);
};
}, [dirty]);
return null;
}