'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; }