|
|
|
let currentLocation = '' |
|
|
|
/** |
|
* Usage: |
|
* @example |
|
* import { onDestroy, onMount } from 'svelte' |
|
* import { onNavigation } from '$utils/onNavigation' |
|
* |
|
* let offNavigation: () => void |
|
* |
|
* onMount(() => { |
|
* offNavigation = onNavigation(() => confirm('Are you sure you want to leave?')) |
|
* }) |
|
* onDestroy(() => { |
|
* offNavigation() |
|
* }) |
|
* @param fn Callback function. Probably a 'confirm' function |
|
* @returns Clean up function |
|
*/ |
|
export function onNavigation(fn: () => boolean): () => void { |
|
currentLocation = window.location.href |
|
|
|
const oldOnbeforeunload = window.onbeforeunload |
|
const oldOnpopstate = window.onpopstate |
|
|
|
const handler = ( |
|
oldFn: ((this: WindowEventHandlers, ev: PopStateEvent) => any) | null |
|
) => function (this: WindowEventHandlers, ev: PopStateEvent) { |
|
const newLocation = ev.target instanceof Window && ev.target.location.href |
|
const success = newLocation !== currentLocation && fn() |
|
if (!success) { |
|
// TODO does not work when forwarding history: Alt + Right arrow |
|
history.go(1) |
|
} else { |
|
oldFn && oldFn.call(this, ev) |
|
} |
|
} |
|
|
|
const onbeforeunload = () => { |
|
return 'Prevent beforeUnload' |
|
} |
|
const onpopstate = handler(oldOnpopstate) |
|
window.onbeforeunload = onbeforeunload |
|
window.onpopstate = onpopstate |
|
|
|
const oldOnclicks: { |
|
el: HTMLAnchorElement, |
|
clickFn: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null |
|
}[] = [] |
|
// TODO could do checks like this: https://github.com/PatrickG/sapper-navigation-enhancer/blob/master/src/index.ts#L42 |
|
document.querySelectorAll('a').forEach(el => { |
|
const clickFn = el.onclick |
|
el.onclick = function (e: MouseEvent) { |
|
const success = fn() |
|
if (!success) { |
|
e.preventDefault() |
|
} else { |
|
clickFn && clickFn.call(this, e) |
|
} |
|
} |
|
oldOnclicks.push({ el, clickFn }) |
|
}) |
|
return () => { |
|
window.onbeforeunload = oldOnbeforeunload |
|
window.onpopstate = oldOnpopstate |
|
oldOnclicks.forEach(({ el, clickFn }) => { |
|
el.onclick = clickFn |
|
}) |
|
} |
|
} |