This commit is contained in:
ryan
2026-07-13 15:08:40 +08:00
parent 08e8eea932
commit 1eff7878a1
441 changed files with 26491 additions and 18405 deletions
@@ -1,42 +1,103 @@
'use client';
import {useEffect} from 'react';
import {getHistoryTransition} from './editor-behavior';
import { useEffect } from 'react';
import { getHistoryTransition } from './editor-behavior';
const historyIndexKey = '__wafEditorIndex';
export function UnsavedChanges({dirty}: {dirty: boolean}) {
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 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);
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 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();
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; }
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); };
return () => {
history.pushState = originalPushState;
history.replaceState = originalReplaceState;
window.removeEventListener('beforeunload', handler);
document.removeEventListener('click', clickHandler, true);
window.removeEventListener('popstate', popstateHandler);
};
}, [dirty]);
return null;
}