@tanstack/history
Advanced tools
Comparing version 1.87.6 to 1.90.0
export interface NavigateOptions { | ||
ignoreBlocker?: boolean; | ||
} | ||
type SubscriberHistoryAction = { | ||
type: HistoryAction | 'ROLLBACK'; | ||
} | { | ||
type: 'GO'; | ||
index: number; | ||
}; | ||
type SubscriberArgs = { | ||
location: HistoryLocation; | ||
action: SubscriberHistoryAction; | ||
}; | ||
export interface RouterHistory { | ||
location: HistoryLocation; | ||
length: number; | ||
subscribers: Set<(opts: { | ||
location: HistoryLocation; | ||
}) => void>; | ||
subscribe: (cb: (opts: { | ||
location: HistoryLocation; | ||
}) => void) => () => void; | ||
subscribers: Set<(opts: SubscriberArgs) => void>; | ||
subscribe: (cb: (opts: SubscriberArgs) => void) => () => void; | ||
push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void; | ||
@@ -19,6 +25,6 @@ replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void; | ||
createHref: (href: string) => string; | ||
block: (blocker: BlockerFn) => () => void; | ||
block: (blocker: NavigationBlocker) => () => void; | ||
flush: () => void; | ||
destroy: () => void; | ||
notify: () => void; | ||
notify: (action: SubscriberHistoryAction) => void; | ||
_ignoreSubscribers?: boolean; | ||
@@ -39,3 +45,13 @@ } | ||
type ShouldAllowNavigation = any; | ||
export type BlockerFn = () => Promise<ShouldAllowNavigation> | ShouldAllowNavigation; | ||
export type HistoryAction = 'PUSH' | 'POP' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO'; | ||
export type BlockerFnArgs = { | ||
currentLocation: HistoryLocation; | ||
nextLocation: HistoryLocation; | ||
action: HistoryAction; | ||
}; | ||
export type BlockerFn = (args: BlockerFnArgs) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation; | ||
export type NavigationBlocker = { | ||
blockerFn: BlockerFn; | ||
enableBeforeUnload?: (() => boolean) | boolean; | ||
}; | ||
export declare function createHistory(opts: { | ||
@@ -47,4 +63,4 @@ getLocation: () => HistoryLocation; | ||
go: (n: number) => void; | ||
back: () => void; | ||
forward: () => void; | ||
back: (ignoreBlocker: boolean) => void; | ||
forward: (ignoreBlocker: boolean) => void; | ||
createHref: (path: string) => string; | ||
@@ -54,2 +70,4 @@ flush?: () => void; | ||
onBlocked?: (onUpdate: () => void) => void; | ||
getBlockers?: () => Array<NavigationBlocker>; | ||
setBlockers?: (blockers: Array<NavigationBlocker>) => void; | ||
}): RouterHistory; | ||
@@ -56,0 +74,0 @@ /** |
@@ -1,29 +0,39 @@ | ||
const pushStateEvent = "pushstate"; | ||
const popStateEvent = "popstate"; | ||
const beforeUnloadEvent = "beforeunload"; | ||
const beforeUnloadListener = (event) => { | ||
event.preventDefault(); | ||
return event.returnValue = ""; | ||
}; | ||
const stopBlocking = () => { | ||
removeEventListener(beforeUnloadEvent, beforeUnloadListener, { | ||
capture: true | ||
}); | ||
}; | ||
function createHistory(opts) { | ||
let location = opts.getLocation(); | ||
const subscribers = /* @__PURE__ */ new Set(); | ||
let blockers = []; | ||
const notify = () => { | ||
const notify = (action) => { | ||
location = opts.getLocation(); | ||
subscribers.forEach((subscriber) => subscriber({ location })); | ||
subscribers.forEach((subscriber) => subscriber({ location, action })); | ||
}; | ||
const tryNavigation = async (task, navigateOpts) => { | ||
var _a; | ||
const _notifyRollback = () => { | ||
location = opts.getLocation(); | ||
subscribers.forEach( | ||
(subscriber) => subscriber({ location, action: { type: "ROLLBACK" } }) | ||
); | ||
}; | ||
const tryNavigation = async ({ | ||
task, | ||
navigateOpts, | ||
...actionInfo | ||
}) => { | ||
var _a, _b; | ||
const ignoreBlocker = (navigateOpts == null ? void 0 : navigateOpts.ignoreBlocker) ?? false; | ||
if (!ignoreBlocker && typeof document !== "undefined" && blockers.length) { | ||
if (ignoreBlocker) { | ||
task(); | ||
return; | ||
} | ||
const blockers = ((_a = opts.getBlockers) == null ? void 0 : _a.call(opts)) ?? []; | ||
const isPushOrReplace = actionInfo.type === "PUSH" || actionInfo.type === "REPLACE"; | ||
if (typeof document !== "undefined" && blockers.length && isPushOrReplace) { | ||
for (const blocker of blockers) { | ||
const allowed = await blocker(); | ||
if (!allowed) { | ||
(_a = opts.onBlocked) == null ? void 0 : _a.call(opts, notify); | ||
const nextLocation = parseHref(actionInfo.path, actionInfo.state); | ||
const isBlocked = await blocker.blockerFn({ | ||
currentLocation: location, | ||
nextLocation, | ||
action: actionInfo.type | ||
}); | ||
if (isBlocked) { | ||
(_b = opts.onBlocked) == null ? void 0 : _b.call(opts, _notifyRollback); | ||
return; | ||
@@ -51,45 +61,67 @@ } | ||
state = assignKey(state); | ||
tryNavigation(() => { | ||
opts.pushState(path, state); | ||
notify(); | ||
}, navigateOpts); | ||
tryNavigation({ | ||
task: () => { | ||
opts.pushState(path, state); | ||
notify({ type: "PUSH" }); | ||
}, | ||
navigateOpts, | ||
type: "PUSH", | ||
path, | ||
state | ||
}); | ||
}, | ||
replace: (path, state, navigateOpts) => { | ||
state = assignKey(state); | ||
tryNavigation(() => { | ||
opts.replaceState(path, state); | ||
notify(); | ||
}, navigateOpts); | ||
tryNavigation({ | ||
task: () => { | ||
opts.replaceState(path, state); | ||
notify({ type: "REPLACE" }); | ||
}, | ||
navigateOpts, | ||
type: "REPLACE", | ||
path, | ||
state | ||
}); | ||
}, | ||
go: (index, navigateOpts) => { | ||
tryNavigation(() => { | ||
opts.go(index); | ||
notify(); | ||
}, navigateOpts); | ||
tryNavigation({ | ||
task: () => { | ||
opts.go(index); | ||
notify({ type: "GO", index }); | ||
}, | ||
navigateOpts, | ||
type: "GO" | ||
}); | ||
}, | ||
back: (navigateOpts) => { | ||
tryNavigation(() => { | ||
opts.back(); | ||
notify(); | ||
}, navigateOpts); | ||
tryNavigation({ | ||
task: () => { | ||
opts.back((navigateOpts == null ? void 0 : navigateOpts.ignoreBlocker) ?? false); | ||
notify({ type: "BACK" }); | ||
}, | ||
navigateOpts, | ||
type: "BACK" | ||
}); | ||
}, | ||
forward: (navigateOpts) => { | ||
tryNavigation(() => { | ||
opts.forward(); | ||
notify(); | ||
}, navigateOpts); | ||
tryNavigation({ | ||
task: () => { | ||
opts.forward((navigateOpts == null ? void 0 : navigateOpts.ignoreBlocker) ?? false); | ||
notify({ type: "FORWARD" }); | ||
}, | ||
navigateOpts, | ||
type: "FORWARD" | ||
}); | ||
}, | ||
createHref: (str) => opts.createHref(str), | ||
block: (blocker) => { | ||
blockers.push(blocker); | ||
if (blockers.length === 1) { | ||
addEventListener(beforeUnloadEvent, beforeUnloadListener, { | ||
capture: true | ||
}); | ||
} | ||
var _a; | ||
if (!opts.setBlockers) return () => { | ||
}; | ||
const blockers = ((_a = opts.getBlockers) == null ? void 0 : _a.call(opts)) ?? []; | ||
opts.setBlockers([...blockers, blocker]); | ||
return () => { | ||
blockers = blockers.filter((b) => b !== blocker); | ||
if (!blockers.length) { | ||
stopBlocking(); | ||
} | ||
var _a2, _b; | ||
const blockers2 = ((_a2 = opts.getBlockers) == null ? void 0 : _a2.call(opts)) ?? []; | ||
(_b = opts.setBlockers) == null ? void 0 : _b.call(opts, blockers2.filter((b) => b !== blocker)); | ||
}; | ||
@@ -121,2 +153,5 @@ }, | ||
const originalReplaceState = win.history.replaceState; | ||
let blockers = []; | ||
const _getBlockers = () => blockers; | ||
const _setBlockers = (newBlockers) => blockers = newBlockers; | ||
const createHref = (opts == null ? void 0 : opts.createHref) ?? ((path) => path); | ||
@@ -129,2 +164,5 @@ const parseLocation = (opts == null ? void 0 : opts.parseLocation) ?? (() => parseHref( | ||
let rollbackLocation; | ||
let ignoreNextPop = false; | ||
let skipBlockerNextPop = false; | ||
let ignoreNextBeforeUnload = false; | ||
const getLocation = () => currentLocation; | ||
@@ -165,4 +203,59 @@ let next; | ||
currentLocation = parseLocation(); | ||
history.notify(); | ||
history.notify({ type: "POP" }); | ||
}; | ||
const onPushPopEvent = async () => { | ||
if (ignoreNextPop) { | ||
ignoreNextPop = false; | ||
return; | ||
} | ||
if (skipBlockerNextPop) { | ||
skipBlockerNextPop = false; | ||
} else { | ||
const blockers2 = _getBlockers(); | ||
if (typeof document !== "undefined" && blockers2.length) { | ||
for (const blocker of blockers2) { | ||
const nextLocation = parseLocation(); | ||
const isBlocked = await blocker.blockerFn({ | ||
currentLocation, | ||
nextLocation, | ||
action: "POP" | ||
}); | ||
if (isBlocked) { | ||
ignoreNextPop = true; | ||
win.history.go(1); | ||
history.notify({ type: "POP" }); | ||
return; | ||
} | ||
} | ||
} | ||
} | ||
currentLocation = parseLocation(); | ||
history.notify({ type: "POP" }); | ||
}; | ||
const onBeforeUnload = (e) => { | ||
if (ignoreNextBeforeUnload) { | ||
ignoreNextBeforeUnload = false; | ||
return; | ||
} | ||
let shouldBlock = false; | ||
const blockers2 = _getBlockers(); | ||
if (typeof document !== "undefined" && blockers2.length) { | ||
for (const blocker of blockers2) { | ||
const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true; | ||
if (shouldHaveBeforeUnload === true) { | ||
shouldBlock = true; | ||
break; | ||
} | ||
if (typeof shouldHaveBeforeUnload === "function" && shouldHaveBeforeUnload() === true) { | ||
shouldBlock = true; | ||
break; | ||
} | ||
} | ||
} | ||
if (shouldBlock) { | ||
e.preventDefault(); | ||
return e.returnValue = ""; | ||
} | ||
return; | ||
}; | ||
const history = createHistory({ | ||
@@ -173,4 +266,12 @@ getLocation, | ||
replaceState: (href, state) => queueHistoryAction("replace", href, state), | ||
back: () => win.history.back(), | ||
forward: () => win.history.forward(), | ||
back: (ignoreBlocker) => { | ||
if (ignoreBlocker) skipBlockerNextPop = true; | ||
ignoreNextBeforeUnload = true; | ||
return win.history.back(); | ||
}, | ||
forward: (ignoreBlocker) => { | ||
if (ignoreBlocker) skipBlockerNextPop = true; | ||
ignoreNextBeforeUnload = true; | ||
win.history.forward(); | ||
}, | ||
go: (n) => win.history.go(n), | ||
@@ -182,4 +283,6 @@ createHref: (href) => createHref(href), | ||
win.history.replaceState = originalReplaceState; | ||
win.removeEventListener(pushStateEvent, onPushPop); | ||
win.removeEventListener(popStateEvent, onPushPop); | ||
win.removeEventListener(beforeUnloadEvent, onBeforeUnload, { | ||
capture: true | ||
}); | ||
win.removeEventListener(popStateEvent, onPushPopEvent); | ||
}, | ||
@@ -191,6 +294,8 @@ onBlocked: (onUpdate) => { | ||
} | ||
} | ||
}, | ||
getBlockers: _getBlockers, | ||
setBlockers: _setBlockers | ||
}); | ||
win.addEventListener(pushStateEvent, onPushPop); | ||
win.addEventListener(popStateEvent, onPushPop); | ||
win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true }); | ||
win.addEventListener(popStateEvent, onPushPopEvent); | ||
win.history.pushState = function(...args) { | ||
@@ -197,0 +302,0 @@ const res = originalPushState.apply(win.history, args); |
{ | ||
"name": "@tanstack/history", | ||
"version": "1.87.6", | ||
"version": "1.90.0", | ||
"description": "Modern and scalable routing for React applications", | ||
@@ -5,0 +5,0 @@ "author": "Tanner Linsley", |
313
src/index.ts
@@ -8,7 +8,22 @@ // While the public API was clearly inspired by the "history" npm package, | ||
} | ||
type SubscriberHistoryAction = | ||
| { | ||
type: HistoryAction | 'ROLLBACK' | ||
} | ||
| { | ||
type: 'GO' | ||
index: number | ||
} | ||
type SubscriberArgs = { | ||
location: HistoryLocation | ||
action: SubscriberHistoryAction | ||
} | ||
export interface RouterHistory { | ||
location: HistoryLocation | ||
length: number | ||
subscribers: Set<(opts: { location: HistoryLocation }) => void> | ||
subscribe: (cb: (opts: { location: HistoryLocation }) => void) => () => void | ||
subscribers: Set<(opts: SubscriberArgs) => void> | ||
subscribe: (cb: (opts: SubscriberArgs) => void) => () => void | ||
push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void | ||
@@ -20,6 +35,6 @@ replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void | ||
createHref: (href: string) => string | ||
block: (blocker: BlockerFn) => () => void | ||
block: (blocker: NavigationBlocker) => () => void | ||
flush: () => void | ||
destroy: () => void | ||
notify: () => void | ||
notify: (action: SubscriberHistoryAction) => void | ||
_ignoreSubscribers?: boolean | ||
@@ -45,22 +60,43 @@ } | ||
export type BlockerFn = () => | ||
| Promise<ShouldAllowNavigation> | ||
| ShouldAllowNavigation | ||
export type HistoryAction = | ||
| 'PUSH' | ||
| 'POP' | ||
| 'REPLACE' | ||
| 'FORWARD' | ||
| 'BACK' | ||
| 'GO' | ||
const pushStateEvent = 'pushstate' | ||
const popStateEvent = 'popstate' | ||
const beforeUnloadEvent = 'beforeunload' | ||
const beforeUnloadListener = (event: Event) => { | ||
event.preventDefault() | ||
// @ts-expect-error | ||
return (event.returnValue = '') | ||
export type BlockerFnArgs = { | ||
currentLocation: HistoryLocation | ||
nextLocation: HistoryLocation | ||
action: HistoryAction | ||
} | ||
const stopBlocking = () => { | ||
removeEventListener(beforeUnloadEvent, beforeUnloadListener, { | ||
capture: true, | ||
}) | ||
export type BlockerFn = ( | ||
args: BlockerFnArgs, | ||
) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation | ||
export type NavigationBlocker = { | ||
blockerFn: BlockerFn | ||
enableBeforeUnload?: (() => boolean) | boolean | ||
} | ||
type TryNavigateArgs = { | ||
task: () => void | ||
type: 'PUSH' | 'REPLACE' | 'BACK' | 'FORWARD' | 'GO' | ||
navigateOpts?: NavigateOptions | ||
} & ( | ||
| { | ||
type: 'PUSH' | 'REPLACE' | ||
path: string | ||
state: any | ||
} | ||
| { | ||
type: 'BACK' | 'FORWARD' | 'GO' | ||
} | ||
) | ||
const popStateEvent = 'popstate' | ||
const beforeUnloadEvent = 'beforeunload' | ||
export function createHistory(opts: { | ||
@@ -72,4 +108,4 @@ getLocation: () => HistoryLocation | ||
go: (n: number) => void | ||
back: () => void | ||
forward: () => void | ||
back: (ignoreBlocker: boolean) => void | ||
forward: (ignoreBlocker: boolean) => void | ||
createHref: (path: string) => string | ||
@@ -79,22 +115,44 @@ flush?: () => void | ||
onBlocked?: (onUpdate: () => void) => void | ||
getBlockers?: () => Array<NavigationBlocker> | ||
setBlockers?: (blockers: Array<NavigationBlocker>) => void | ||
}): RouterHistory { | ||
let location = opts.getLocation() | ||
const subscribers = new Set<(opts: { location: HistoryLocation }) => void>() | ||
let blockers: Array<BlockerFn> = [] | ||
const subscribers = new Set<(opts: SubscriberArgs) => void>() | ||
const notify = () => { | ||
const notify = (action: SubscriberHistoryAction) => { | ||
location = opts.getLocation() | ||
subscribers.forEach((subscriber) => subscriber({ location })) | ||
subscribers.forEach((subscriber) => subscriber({ location, action })) | ||
} | ||
const tryNavigation = async ( | ||
task: () => void, | ||
navigateOpts?: NavigateOptions, | ||
) => { | ||
const _notifyRollback = () => { | ||
location = opts.getLocation() | ||
subscribers.forEach((subscriber) => | ||
subscriber({ location, action: { type: 'ROLLBACK' } }), | ||
) | ||
} | ||
const tryNavigation = async ({ | ||
task, | ||
navigateOpts, | ||
...actionInfo | ||
}: TryNavigateArgs) => { | ||
const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false | ||
if (!ignoreBlocker && typeof document !== 'undefined' && blockers.length) { | ||
if (ignoreBlocker) { | ||
task() | ||
return | ||
} | ||
const blockers = opts.getBlockers?.() ?? [] | ||
const isPushOrReplace = | ||
actionInfo.type === 'PUSH' || actionInfo.type === 'REPLACE' | ||
if (typeof document !== 'undefined' && blockers.length && isPushOrReplace) { | ||
for (const blocker of blockers) { | ||
const allowed = await blocker() | ||
if (!allowed) { | ||
opts.onBlocked?.(notify) | ||
const nextLocation = parseHref(actionInfo.path, actionInfo.state) | ||
const isBlocked = await blocker.blockerFn({ | ||
currentLocation: location, | ||
nextLocation, | ||
action: actionInfo.type, | ||
}) | ||
if (isBlocked) { | ||
opts.onBlocked?.(_notifyRollback) | ||
return | ||
@@ -116,3 +174,3 @@ } | ||
subscribers, | ||
subscribe: (cb: (opts: { location: HistoryLocation }) => void) => { | ||
subscribe: (cb: (opts: SubscriberArgs) => void) => { | ||
subscribers.add(cb) | ||
@@ -126,48 +184,65 @@ | ||
state = assignKey(state) | ||
tryNavigation(() => { | ||
opts.pushState(path, state) | ||
notify() | ||
}, navigateOpts) | ||
tryNavigation({ | ||
task: () => { | ||
opts.pushState(path, state) | ||
notify({ type: 'PUSH' }) | ||
}, | ||
navigateOpts, | ||
type: 'PUSH', | ||
path, | ||
state, | ||
}) | ||
}, | ||
replace: (path, state, navigateOpts) => { | ||
state = assignKey(state) | ||
tryNavigation(() => { | ||
opts.replaceState(path, state) | ||
notify() | ||
}, navigateOpts) | ||
tryNavigation({ | ||
task: () => { | ||
opts.replaceState(path, state) | ||
notify({ type: 'REPLACE' }) | ||
}, | ||
navigateOpts, | ||
type: 'REPLACE', | ||
path, | ||
state, | ||
}) | ||
}, | ||
go: (index, navigateOpts) => { | ||
tryNavigation(() => { | ||
opts.go(index) | ||
notify() | ||
}, navigateOpts) | ||
tryNavigation({ | ||
task: () => { | ||
opts.go(index) | ||
notify({ type: 'GO', index }) | ||
}, | ||
navigateOpts, | ||
type: 'GO', | ||
}) | ||
}, | ||
back: (navigateOpts) => { | ||
tryNavigation(() => { | ||
opts.back() | ||
notify() | ||
}, navigateOpts) | ||
tryNavigation({ | ||
task: () => { | ||
opts.back(navigateOpts?.ignoreBlocker ?? false) | ||
notify({ type: 'BACK' }) | ||
}, | ||
navigateOpts, | ||
type: 'BACK', | ||
}) | ||
}, | ||
forward: (navigateOpts) => { | ||
tryNavigation(() => { | ||
opts.forward() | ||
notify() | ||
}, navigateOpts) | ||
tryNavigation({ | ||
task: () => { | ||
opts.forward(navigateOpts?.ignoreBlocker ?? false) | ||
notify({ type: 'FORWARD' }) | ||
}, | ||
navigateOpts, | ||
type: 'FORWARD', | ||
}) | ||
}, | ||
createHref: (str) => opts.createHref(str), | ||
block: (blocker) => { | ||
blockers.push(blocker) | ||
if (!opts.setBlockers) return () => {} | ||
const blockers = opts.getBlockers?.() ?? [] | ||
opts.setBlockers([...blockers, blocker]) | ||
if (blockers.length === 1) { | ||
addEventListener(beforeUnloadEvent, beforeUnloadListener, { | ||
capture: true, | ||
}) | ||
} | ||
return () => { | ||
blockers = blockers.filter((b) => b !== blocker) | ||
if (!blockers.length) { | ||
stopBlocking() | ||
} | ||
const blockers = opts.getBlockers?.() ?? [] | ||
opts.setBlockers?.(blockers.filter((b) => b !== blocker)) | ||
} | ||
@@ -219,2 +294,7 @@ }, | ||
let blockers: Array<NavigationBlocker> = [] | ||
const _getBlockers = () => blockers | ||
const _setBlockers = (newBlockers: Array<NavigationBlocker>) => | ||
(blockers = newBlockers) | ||
const createHref = opts?.createHref ?? ((path) => path) | ||
@@ -232,2 +312,6 @@ const parseLocation = | ||
let ignoreNextPop = false | ||
let skipBlockerNextPop = false | ||
let ignoreNextBeforeUnload = false | ||
const getLocation = () => currentLocation | ||
@@ -305,5 +389,72 @@ | ||
currentLocation = parseLocation() | ||
history.notify() | ||
history.notify({ type: 'POP' }) | ||
} | ||
const onPushPopEvent = async () => { | ||
if (ignoreNextPop) { | ||
ignoreNextPop = false | ||
return | ||
} | ||
if (skipBlockerNextPop) { | ||
skipBlockerNextPop = false | ||
} else { | ||
const blockers = _getBlockers() | ||
if (typeof document !== 'undefined' && blockers.length) { | ||
for (const blocker of blockers) { | ||
const nextLocation = parseLocation() | ||
const isBlocked = await blocker.blockerFn({ | ||
currentLocation, | ||
nextLocation, | ||
action: 'POP', | ||
}) | ||
if (isBlocked) { | ||
ignoreNextPop = true | ||
win.history.go(1) | ||
history.notify({ type: 'POP' }) | ||
return | ||
} | ||
} | ||
} | ||
} | ||
currentLocation = parseLocation() | ||
history.notify({ type: 'POP' }) | ||
} | ||
const onBeforeUnload = (e: BeforeUnloadEvent) => { | ||
if (ignoreNextBeforeUnload) { | ||
ignoreNextBeforeUnload = false | ||
return | ||
} | ||
let shouldBlock = false | ||
// If one blocker has a non-disabled beforeUnload, we should block | ||
const blockers = _getBlockers() | ||
if (typeof document !== 'undefined' && blockers.length) { | ||
for (const blocker of blockers) { | ||
const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true | ||
if (shouldHaveBeforeUnload === true) { | ||
shouldBlock = true | ||
break | ||
} | ||
if ( | ||
typeof shouldHaveBeforeUnload === 'function' && | ||
shouldHaveBeforeUnload() === true | ||
) { | ||
shouldBlock = true | ||
break | ||
} | ||
} | ||
} | ||
if (shouldBlock) { | ||
e.preventDefault() | ||
return (e.returnValue = '') | ||
} | ||
return | ||
} | ||
const history = createHistory({ | ||
@@ -314,4 +465,12 @@ getLocation, | ||
replaceState: (href, state) => queueHistoryAction('replace', href, state), | ||
back: () => win.history.back(), | ||
forward: () => win.history.forward(), | ||
back: (ignoreBlocker) => { | ||
if (ignoreBlocker) skipBlockerNextPop = true | ||
ignoreNextBeforeUnload = true | ||
return win.history.back() | ||
}, | ||
forward: (ignoreBlocker) => { | ||
if (ignoreBlocker) skipBlockerNextPop = true | ||
ignoreNextBeforeUnload = true | ||
win.history.forward() | ||
}, | ||
go: (n) => win.history.go(n), | ||
@@ -323,4 +482,6 @@ createHref: (href) => createHref(href), | ||
win.history.replaceState = originalReplaceState | ||
win.removeEventListener(pushStateEvent, onPushPop) | ||
win.removeEventListener(popStateEvent, onPushPop) | ||
win.removeEventListener(beforeUnloadEvent, onBeforeUnload, { | ||
capture: true, | ||
}) | ||
win.removeEventListener(popStateEvent, onPushPopEvent) | ||
}, | ||
@@ -336,9 +497,11 @@ onBlocked: (onUpdate) => { | ||
}, | ||
getBlockers: _getBlockers, | ||
setBlockers: _setBlockers, | ||
}) | ||
win.addEventListener(pushStateEvent, onPushPop) | ||
win.addEventListener(popStateEvent, onPushPop) | ||
win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true }) | ||
win.addEventListener(popStateEvent, onPushPopEvent) | ||
win.history.pushState = function (...args: Array<any>) { | ||
const res = originalPushState.apply(win.history, args) | ||
const res = originalPushState.apply(win.history, args as any) | ||
if (!history._ignoreSubscribers) onPushPop() | ||
@@ -349,3 +512,3 @@ return res | ||
win.history.replaceState = function (...args: Array<any>) { | ||
const res = originalReplaceState.apply(win.history, args) | ||
const res = originalReplaceState.apply(win.history, args as any) | ||
if (!history._ignoreSubscribers) onPushPop() | ||
@@ -352,0 +515,0 @@ return res |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
98085
1384