@tanstack/history 1.90.0 → 1.95.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +49 -28
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +6 -3
- package/dist/esm/index.d.ts +6 -3
- package/dist/esm/index.js +49 -28
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +62 -36
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const stateIndexKey = "__TSR_index";
|
|
3
4
|
const popStateEvent = "popstate";
|
|
4
5
|
const beforeUnloadEvent = "beforeunload";
|
|
5
6
|
function createHistory(opts) {
|
|
@@ -9,11 +10,9 @@ function createHistory(opts) {
|
|
|
9
10
|
location = opts.getLocation();
|
|
10
11
|
subscribers.forEach((subscriber) => subscriber({ location, action }));
|
|
11
12
|
};
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
(subscriber) => subscriber({ location, action: { type: "ROLLBACK" } })
|
|
16
|
-
);
|
|
13
|
+
const handleIndexChange = (action) => {
|
|
14
|
+
if (opts.notifyOnIndexChange ?? true) notify(action);
|
|
15
|
+
else location = opts.getLocation();
|
|
17
16
|
};
|
|
18
17
|
const tryNavigation = async ({
|
|
19
18
|
task,
|
|
@@ -37,7 +36,7 @@ function createHistory(opts) {
|
|
|
37
36
|
action: actionInfo.type
|
|
38
37
|
});
|
|
39
38
|
if (isBlocked) {
|
|
40
|
-
(_b = opts.onBlocked) == null ? void 0 : _b.call(opts
|
|
39
|
+
(_b = opts.onBlocked) == null ? void 0 : _b.call(opts);
|
|
41
40
|
return;
|
|
42
41
|
}
|
|
43
42
|
}
|
|
@@ -59,7 +58,8 @@ function createHistory(opts) {
|
|
|
59
58
|
};
|
|
60
59
|
},
|
|
61
60
|
push: (path, state, navigateOpts) => {
|
|
62
|
-
|
|
61
|
+
const currentIndex = location.state[stateIndexKey];
|
|
62
|
+
state = assignKeyAndIndex(currentIndex + 1, state);
|
|
63
63
|
tryNavigation({
|
|
64
64
|
task: () => {
|
|
65
65
|
opts.pushState(path, state);
|
|
@@ -72,7 +72,8 @@ function createHistory(opts) {
|
|
|
72
72
|
});
|
|
73
73
|
},
|
|
74
74
|
replace: (path, state, navigateOpts) => {
|
|
75
|
-
|
|
75
|
+
const currentIndex = location.state[stateIndexKey];
|
|
76
|
+
state = assignKeyAndIndex(currentIndex, state);
|
|
76
77
|
tryNavigation({
|
|
77
78
|
task: () => {
|
|
78
79
|
opts.replaceState(path, state);
|
|
@@ -88,7 +89,7 @@ function createHistory(opts) {
|
|
|
88
89
|
tryNavigation({
|
|
89
90
|
task: () => {
|
|
90
91
|
opts.go(index);
|
|
91
|
-
|
|
92
|
+
handleIndexChange({ type: "GO", index });
|
|
92
93
|
},
|
|
93
94
|
navigateOpts,
|
|
94
95
|
type: "GO"
|
|
@@ -98,7 +99,7 @@ function createHistory(opts) {
|
|
|
98
99
|
tryNavigation({
|
|
99
100
|
task: () => {
|
|
100
101
|
opts.back((navigateOpts == null ? void 0 : navigateOpts.ignoreBlocker) ?? false);
|
|
101
|
-
|
|
102
|
+
handleIndexChange({ type: "BACK" });
|
|
102
103
|
},
|
|
103
104
|
navigateOpts,
|
|
104
105
|
type: "BACK"
|
|
@@ -108,12 +109,13 @@ function createHistory(opts) {
|
|
|
108
109
|
tryNavigation({
|
|
109
110
|
task: () => {
|
|
110
111
|
opts.forward((navigateOpts == null ? void 0 : navigateOpts.ignoreBlocker) ?? false);
|
|
111
|
-
|
|
112
|
+
handleIndexChange({ type: "FORWARD" });
|
|
112
113
|
},
|
|
113
114
|
navigateOpts,
|
|
114
115
|
type: "FORWARD"
|
|
115
116
|
});
|
|
116
117
|
},
|
|
118
|
+
canGoBack: () => location.state[stateIndexKey] !== 0,
|
|
117
119
|
createHref: (str) => opts.createHref(str),
|
|
118
120
|
block: (blocker) => {
|
|
119
121
|
var _a;
|
|
@@ -138,13 +140,14 @@ function createHistory(opts) {
|
|
|
138
140
|
notify
|
|
139
141
|
};
|
|
140
142
|
}
|
|
141
|
-
function
|
|
143
|
+
function assignKeyAndIndex(index, state) {
|
|
142
144
|
if (!state) {
|
|
143
145
|
state = {};
|
|
144
146
|
}
|
|
145
147
|
return {
|
|
146
148
|
...state,
|
|
147
|
-
key: createRandomKey()
|
|
149
|
+
key: createRandomKey(),
|
|
150
|
+
[stateIndexKey]: index
|
|
148
151
|
};
|
|
149
152
|
}
|
|
150
153
|
function createBrowserHistory(opts) {
|
|
@@ -161,6 +164,7 @@ function createBrowserHistory(opts) {
|
|
|
161
164
|
));
|
|
162
165
|
let currentLocation = parseLocation();
|
|
163
166
|
let rollbackLocation;
|
|
167
|
+
let nextPopIsGo = false;
|
|
164
168
|
let ignoreNextPop = false;
|
|
165
169
|
let skipBlockerNextPop = false;
|
|
166
170
|
let ignoreNextBeforeUnload = false;
|
|
@@ -197,38 +201,50 @@ function createBrowserHistory(opts) {
|
|
|
197
201
|
scheduled = Promise.resolve().then(() => flush());
|
|
198
202
|
}
|
|
199
203
|
};
|
|
200
|
-
const onPushPop = () => {
|
|
204
|
+
const onPushPop = (type) => {
|
|
201
205
|
currentLocation = parseLocation();
|
|
202
|
-
history.notify({ type
|
|
206
|
+
history.notify({ type });
|
|
203
207
|
};
|
|
204
208
|
const onPushPopEvent = async () => {
|
|
205
209
|
if (ignoreNextPop) {
|
|
206
210
|
ignoreNextPop = false;
|
|
207
211
|
return;
|
|
208
212
|
}
|
|
213
|
+
const nextLocation = parseLocation();
|
|
214
|
+
const delta = nextLocation.state[stateIndexKey] - currentLocation.state[stateIndexKey];
|
|
215
|
+
const isForward = delta === 1;
|
|
216
|
+
const isBack = delta === -1;
|
|
217
|
+
const isGo = !isForward && !isBack || nextPopIsGo;
|
|
218
|
+
nextPopIsGo = false;
|
|
219
|
+
const action = isGo ? "GO" : isBack ? "BACK" : "FORWARD";
|
|
220
|
+
const notify = isGo ? {
|
|
221
|
+
type: "GO",
|
|
222
|
+
index: delta
|
|
223
|
+
} : {
|
|
224
|
+
type: isBack ? "BACK" : "FORWARD"
|
|
225
|
+
};
|
|
209
226
|
if (skipBlockerNextPop) {
|
|
210
227
|
skipBlockerNextPop = false;
|
|
211
228
|
} else {
|
|
212
229
|
const blockers2 = _getBlockers();
|
|
213
230
|
if (typeof document !== "undefined" && blockers2.length) {
|
|
214
231
|
for (const blocker of blockers2) {
|
|
215
|
-
const nextLocation = parseLocation();
|
|
216
232
|
const isBlocked = await blocker.blockerFn({
|
|
217
233
|
currentLocation,
|
|
218
234
|
nextLocation,
|
|
219
|
-
action
|
|
235
|
+
action
|
|
220
236
|
});
|
|
221
237
|
if (isBlocked) {
|
|
222
238
|
ignoreNextPop = true;
|
|
223
239
|
win.history.go(1);
|
|
224
|
-
history.notify(
|
|
240
|
+
history.notify(notify);
|
|
225
241
|
return;
|
|
226
242
|
}
|
|
227
243
|
}
|
|
228
244
|
}
|
|
229
245
|
}
|
|
230
246
|
currentLocation = parseLocation();
|
|
231
|
-
history.notify(
|
|
247
|
+
history.notify(notify);
|
|
232
248
|
};
|
|
233
249
|
const onBeforeUnload = (e) => {
|
|
234
250
|
if (ignoreNextBeforeUnload) {
|
|
@@ -271,7 +287,10 @@ function createBrowserHistory(opts) {
|
|
|
271
287
|
ignoreNextBeforeUnload = true;
|
|
272
288
|
win.history.forward();
|
|
273
289
|
},
|
|
274
|
-
go: (n) =>
|
|
290
|
+
go: (n) => {
|
|
291
|
+
nextPopIsGo = true;
|
|
292
|
+
win.history.go(n);
|
|
293
|
+
},
|
|
275
294
|
createHref: (href) => createHref(href),
|
|
276
295
|
flush,
|
|
277
296
|
destroy: () => {
|
|
@@ -282,25 +301,25 @@ function createBrowserHistory(opts) {
|
|
|
282
301
|
});
|
|
283
302
|
win.removeEventListener(popStateEvent, onPushPopEvent);
|
|
284
303
|
},
|
|
285
|
-
onBlocked: (
|
|
304
|
+
onBlocked: () => {
|
|
286
305
|
if (rollbackLocation && currentLocation !== rollbackLocation) {
|
|
287
306
|
currentLocation = rollbackLocation;
|
|
288
|
-
onUpdate();
|
|
289
307
|
}
|
|
290
308
|
},
|
|
291
309
|
getBlockers: _getBlockers,
|
|
292
|
-
setBlockers: _setBlockers
|
|
310
|
+
setBlockers: _setBlockers,
|
|
311
|
+
notifyOnIndexChange: false
|
|
293
312
|
});
|
|
294
313
|
win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true });
|
|
295
314
|
win.addEventListener(popStateEvent, onPushPopEvent);
|
|
296
315
|
win.history.pushState = function(...args) {
|
|
297
316
|
const res = originalPushState.apply(win.history, args);
|
|
298
|
-
if (!history._ignoreSubscribers) onPushPop();
|
|
317
|
+
if (!history._ignoreSubscribers) onPushPop("PUSH");
|
|
299
318
|
return res;
|
|
300
319
|
};
|
|
301
320
|
win.history.replaceState = function(...args) {
|
|
302
321
|
const res = originalReplaceState.apply(win.history, args);
|
|
303
|
-
if (!history._ignoreSubscribers) onPushPop();
|
|
322
|
+
if (!history._ignoreSubscribers) onPushPop("REPLACE");
|
|
304
323
|
return res;
|
|
305
324
|
};
|
|
306
325
|
return history;
|
|
@@ -320,8 +339,10 @@ function createMemoryHistory(opts = {
|
|
|
320
339
|
initialEntries: ["/"]
|
|
321
340
|
}) {
|
|
322
341
|
const entries = opts.initialEntries;
|
|
323
|
-
let index = opts.initialIndex
|
|
324
|
-
const states = entries.map(
|
|
342
|
+
let index = opts.initialIndex ? Math.min(Math.max(opts.initialIndex, 0), entries.length - 1) : entries.length - 1;
|
|
343
|
+
const states = entries.map(
|
|
344
|
+
(_entry, index2) => assignKeyAndIndex(index2, void 0)
|
|
345
|
+
);
|
|
325
346
|
const getLocation = () => parseHref(entries[index], states[index]);
|
|
326
347
|
return createHistory({
|
|
327
348
|
getLocation,
|
|
@@ -362,7 +383,7 @@ function parseHref(href, state) {
|
|
|
362
383
|
),
|
|
363
384
|
hash: hashIndex > -1 ? href.substring(hashIndex) : "",
|
|
364
385
|
search: searchIndex > -1 ? href.slice(searchIndex, hashIndex === -1 ? void 0 : hashIndex) : "",
|
|
365
|
-
state: state || {}
|
|
386
|
+
state: state || { [stateIndexKey]: 0 }
|
|
366
387
|
};
|
|
367
388
|
}
|
|
368
389
|
function createRandomKey() {
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../src/index.ts"],"sourcesContent":["// While the public API was clearly inspired by the \"history\" npm package,\n// This implementation attempts to be more lightweight by\n// making assumptions about the way TanStack Router works\n\nexport interface NavigateOptions {\n ignoreBlocker?: boolean\n}\n\ntype SubscriberHistoryAction =\n | {\n type: HistoryAction | 'ROLLBACK'\n }\n | {\n type: 'GO'\n index: number\n }\n\ntype SubscriberArgs = {\n location: HistoryLocation\n action: SubscriberHistoryAction\n}\n\nexport interface RouterHistory {\n location: HistoryLocation\n length: number\n subscribers: Set<(opts: SubscriberArgs) => void>\n subscribe: (cb: (opts: SubscriberArgs) => void) => () => void\n push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void\n replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void\n go: (index: number, navigateOpts?: NavigateOptions) => void\n back: (navigateOpts?: NavigateOptions) => void\n forward: (navigateOpts?: NavigateOptions) => void\n createHref: (href: string) => string\n block: (blocker: NavigationBlocker) => () => void\n flush: () => void\n destroy: () => void\n notify: (action: SubscriberHistoryAction) => void\n _ignoreSubscribers?: boolean\n}\n\nexport interface HistoryLocation extends ParsedPath {\n state: HistoryState\n}\n\nexport interface ParsedPath {\n href: string\n pathname: string\n search: string\n hash: string\n}\n\nexport interface HistoryState {\n key?: string\n}\n\ntype ShouldAllowNavigation = any\n\nexport type HistoryAction =\n | 'PUSH'\n | 'POP'\n | 'REPLACE'\n | 'FORWARD'\n | 'BACK'\n | 'GO'\n\nexport type BlockerFnArgs = {\n currentLocation: HistoryLocation\n nextLocation: HistoryLocation\n action: HistoryAction\n}\n\nexport type BlockerFn = (\n args: BlockerFnArgs,\n) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation\n\nexport type NavigationBlocker = {\n blockerFn: BlockerFn\n enableBeforeUnload?: (() => boolean) | boolean\n}\n\ntype TryNavigateArgs = {\n task: () => void\n type: 'PUSH' | 'REPLACE' | 'BACK' | 'FORWARD' | 'GO'\n navigateOpts?: NavigateOptions\n} & (\n | {\n type: 'PUSH' | 'REPLACE'\n path: string\n state: any\n }\n | {\n type: 'BACK' | 'FORWARD' | 'GO'\n }\n)\n\nconst popStateEvent = 'popstate'\nconst beforeUnloadEvent = 'beforeunload'\n\nexport function createHistory(opts: {\n getLocation: () => HistoryLocation\n getLength: () => number\n pushState: (path: string, state: any) => void\n replaceState: (path: string, state: any) => void\n go: (n: number) => void\n back: (ignoreBlocker: boolean) => void\n forward: (ignoreBlocker: boolean) => void\n createHref: (path: string) => string\n flush?: () => void\n destroy?: () => void\n onBlocked?: (onUpdate: () => void) => void\n getBlockers?: () => Array<NavigationBlocker>\n setBlockers?: (blockers: Array<NavigationBlocker>) => void\n}): RouterHistory {\n let location = opts.getLocation()\n const subscribers = new Set<(opts: SubscriberArgs) => void>()\n\n const notify = (action: SubscriberHistoryAction) => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) => subscriber({ location, action }))\n }\n\n const _notifyRollback = () => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) =>\n subscriber({ location, action: { type: 'ROLLBACK' } }),\n )\n }\n\n const tryNavigation = async ({\n task,\n navigateOpts,\n ...actionInfo\n }: TryNavigateArgs) => {\n const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false\n if (ignoreBlocker) {\n task()\n return\n }\n\n const blockers = opts.getBlockers?.() ?? []\n const isPushOrReplace =\n actionInfo.type === 'PUSH' || actionInfo.type === 'REPLACE'\n if (typeof document !== 'undefined' && blockers.length && isPushOrReplace) {\n for (const blocker of blockers) {\n const nextLocation = parseHref(actionInfo.path, actionInfo.state)\n const isBlocked = await blocker.blockerFn({\n currentLocation: location,\n nextLocation,\n action: actionInfo.type,\n })\n if (isBlocked) {\n opts.onBlocked?.(_notifyRollback)\n return\n }\n }\n }\n\n task()\n }\n\n return {\n get location() {\n return location\n },\n get length() {\n return opts.getLength()\n },\n subscribers,\n subscribe: (cb: (opts: SubscriberArgs) => void) => {\n subscribers.add(cb)\n\n return () => {\n subscribers.delete(cb)\n }\n },\n push: (path, state, navigateOpts) => {\n state = assignKey(state)\n tryNavigation({\n task: () => {\n opts.pushState(path, state)\n notify({ type: 'PUSH' })\n },\n navigateOpts,\n type: 'PUSH',\n path,\n state,\n })\n },\n replace: (path, state, navigateOpts) => {\n state = assignKey(state)\n tryNavigation({\n task: () => {\n opts.replaceState(path, state)\n notify({ type: 'REPLACE' })\n },\n navigateOpts,\n type: 'REPLACE',\n path,\n state,\n })\n },\n go: (index, navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.go(index)\n notify({ type: 'GO', index })\n },\n navigateOpts,\n type: 'GO',\n })\n },\n back: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.back(navigateOpts?.ignoreBlocker ?? false)\n notify({ type: 'BACK' })\n },\n navigateOpts,\n type: 'BACK',\n })\n },\n forward: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.forward(navigateOpts?.ignoreBlocker ?? false)\n notify({ type: 'FORWARD' })\n },\n navigateOpts,\n type: 'FORWARD',\n })\n },\n createHref: (str) => opts.createHref(str),\n block: (blocker) => {\n if (!opts.setBlockers) return () => {}\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers([...blockers, blocker])\n\n return () => {\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers?.(blockers.filter((b) => b !== blocker))\n }\n },\n flush: () => opts.flush?.(),\n destroy: () => opts.destroy?.(),\n notify,\n }\n}\n\nfunction assignKey(state: HistoryState | undefined) {\n if (!state) {\n state = {} as HistoryState\n }\n return {\n ...state,\n key: createRandomKey(),\n }\n}\n\n/**\n * Creates a history object that can be used to interact with the browser's\n * navigation. This is a lightweight API wrapping the browser's native methods.\n * It is designed to work with TanStack Router, but could be used as a standalone API as well.\n * IMPORTANT: This API implements history throttling via a microtask to prevent\n * excessive calls to the history API. In some browsers, calling history.pushState or\n * history.replaceState in quick succession can cause the browser to ignore subsequent\n * calls. This API smooths out those differences and ensures that your application\n * state will *eventually* match the browser state. In most cases, this is not a problem,\n * but if you need to ensure that the browser state is up to date, you can use the\n * `history.flush` method to immediately flush all pending state changes to the browser URL.\n * @param opts\n * @param opts.getHref A function that returns the current href (path + search + hash)\n * @param opts.createHref A function that takes a path and returns a href (path + search + hash)\n * @returns A history instance\n */\nexport function createBrowserHistory(opts?: {\n parseLocation?: () => HistoryLocation\n createHref?: (path: string) => string\n window?: any\n}): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n\n const originalPushState = win.history.pushState\n const originalReplaceState = win.history.replaceState\n\n let blockers: Array<NavigationBlocker> = []\n const _getBlockers = () => blockers\n const _setBlockers = (newBlockers: Array<NavigationBlocker>) =>\n (blockers = newBlockers)\n\n const createHref = opts?.createHref ?? ((path) => path)\n const parseLocation =\n opts?.parseLocation ??\n (() =>\n parseHref(\n `${win.location.pathname}${win.location.search}${win.location.hash}`,\n win.history.state,\n ))\n\n let currentLocation = parseLocation()\n let rollbackLocation: HistoryLocation | undefined\n\n let ignoreNextPop = false\n let skipBlockerNextPop = false\n let ignoreNextBeforeUnload = false\n\n const getLocation = () => currentLocation\n\n let next:\n | undefined\n | {\n // This is the latest location that we were attempting to push/replace\n href: string\n // This is the latest state that we were attempting to push/replace\n state: any\n // This is the latest type that we were attempting to push/replace\n isPush: boolean\n }\n\n // We need to track the current scheduled update to prevent\n // multiple updates from being scheduled at the same time.\n let scheduled: Promise<void> | undefined\n\n // This function flushes the next update to the browser history\n const flush = () => {\n if (!next) {\n return\n }\n\n // We need to ignore any updates to the subscribers while we update the browser history\n history._ignoreSubscribers = true\n\n // Update the browser history\n ;(next.isPush ? win.history.pushState : win.history.replaceState)(\n next.state,\n '',\n next.href,\n )\n\n // Stop ignoring subscriber updates\n history._ignoreSubscribers = false\n\n // Reset the nextIsPush flag and clear the scheduled update\n next = undefined\n scheduled = undefined\n rollbackLocation = undefined\n }\n\n // This function queues up a call to update the browser history\n const queueHistoryAction = (\n type: 'push' | 'replace',\n destHref: string,\n state: any,\n ) => {\n const href = createHref(destHref)\n\n if (!scheduled) {\n rollbackLocation = currentLocation\n }\n\n // Update the location in memory\n currentLocation = parseHref(destHref, state)\n\n // Keep track of the next location we need to flush to the URL\n next = {\n href,\n state,\n isPush: next?.isPush || type === 'push',\n }\n\n if (!scheduled) {\n // Schedule an update to the browser history\n scheduled = Promise.resolve().then(() => flush())\n }\n }\n\n const onPushPop = () => {\n currentLocation = parseLocation()\n history.notify({ type: 'POP' })\n }\n\n const onPushPopEvent = async () => {\n if (ignoreNextPop) {\n ignoreNextPop = false\n return\n }\n\n if (skipBlockerNextPop) {\n skipBlockerNextPop = false\n } else {\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const nextLocation = parseLocation()\n const isBlocked = await blocker.blockerFn({\n currentLocation,\n nextLocation,\n action: 'POP',\n })\n if (isBlocked) {\n ignoreNextPop = true\n win.history.go(1)\n history.notify({ type: 'POP' })\n return\n }\n }\n }\n }\n\n currentLocation = parseLocation()\n history.notify({ type: 'POP' })\n }\n\n const onBeforeUnload = (e: BeforeUnloadEvent) => {\n if (ignoreNextBeforeUnload) {\n ignoreNextBeforeUnload = false\n return\n }\n\n let shouldBlock = false\n\n // If one blocker has a non-disabled beforeUnload, we should block\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true\n if (shouldHaveBeforeUnload === true) {\n shouldBlock = true\n break\n }\n\n if (\n typeof shouldHaveBeforeUnload === 'function' &&\n shouldHaveBeforeUnload() === true\n ) {\n shouldBlock = true\n break\n }\n }\n }\n\n if (shouldBlock) {\n e.preventDefault()\n return (e.returnValue = '')\n }\n return\n }\n\n const history = createHistory({\n getLocation,\n getLength: () => win.history.length,\n pushState: (href, state) => queueHistoryAction('push', href, state),\n replaceState: (href, state) => queueHistoryAction('replace', href, state),\n back: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n return win.history.back()\n },\n forward: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n win.history.forward()\n },\n go: (n) => win.history.go(n),\n createHref: (href) => createHref(href),\n flush,\n destroy: () => {\n win.history.pushState = originalPushState\n win.history.replaceState = originalReplaceState\n win.removeEventListener(beforeUnloadEvent, onBeforeUnload, {\n capture: true,\n })\n win.removeEventListener(popStateEvent, onPushPopEvent)\n },\n onBlocked: (onUpdate) => {\n // If a navigation is blocked, we need to rollback the location\n // that we optimistically updated in memory.\n if (rollbackLocation && currentLocation !== rollbackLocation) {\n currentLocation = rollbackLocation\n // Notify subscribers\n onUpdate()\n }\n },\n getBlockers: _getBlockers,\n setBlockers: _setBlockers,\n })\n\n win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true })\n win.addEventListener(popStateEvent, onPushPopEvent)\n\n win.history.pushState = function (...args: Array<any>) {\n const res = originalPushState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop()\n return res\n }\n\n win.history.replaceState = function (...args: Array<any>) {\n const res = originalReplaceState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop()\n return res\n }\n\n return history\n}\n\nexport function createHashHistory(opts?: { window?: any }): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n return createBrowserHistory({\n window: win,\n parseLocation: () => {\n const hashHref = win.location.hash.split('#').slice(1).join('#') ?? '/'\n return parseHref(hashHref, win.history.state)\n },\n createHref: (href) =>\n `${win.location.pathname}${win.location.search}#${href}`,\n })\n}\n\nexport function createMemoryHistory(\n opts: {\n initialEntries: Array<string>\n initialIndex?: number\n } = {\n initialEntries: ['/'],\n },\n): RouterHistory {\n const entries = opts.initialEntries\n let index = opts.initialIndex ?? entries.length - 1\n const states = entries.map(() => ({}) as HistoryState)\n\n const getLocation = () => parseHref(entries[index]!, states[index])\n\n return createHistory({\n getLocation,\n getLength: () => entries.length,\n pushState: (path, state) => {\n // Removes all subsequent entries after the current index to start a new branch\n if (index < entries.length - 1) {\n entries.splice(index + 1)\n states.splice(index + 1)\n }\n states.push(state)\n entries.push(path)\n index = Math.max(entries.length - 1, 0)\n },\n replaceState: (path, state) => {\n states[index] = state\n entries[index] = path\n },\n back: () => {\n index = Math.max(index - 1, 0)\n },\n forward: () => {\n index = Math.min(index + 1, entries.length - 1)\n },\n go: (n) => {\n index = Math.min(Math.max(index + n, 0), entries.length - 1)\n },\n createHref: (path) => path,\n })\n}\n\nexport function parseHref(\n href: string,\n state: HistoryState | undefined,\n): HistoryLocation {\n const hashIndex = href.indexOf('#')\n const searchIndex = href.indexOf('?')\n\n return {\n href,\n pathname: href.substring(\n 0,\n hashIndex > 0\n ? searchIndex > 0\n ? Math.min(hashIndex, searchIndex)\n : hashIndex\n : searchIndex > 0\n ? searchIndex\n : href.length,\n ),\n hash: hashIndex > -1 ? href.substring(hashIndex) : '',\n search:\n searchIndex > -1\n ? href.slice(searchIndex, hashIndex === -1 ? undefined : hashIndex)\n : '',\n state: state || {},\n }\n}\n\n// Thanks co-pilot!\nfunction createRandomKey() {\n return (Math.random() + 1).toString(36).substring(7)\n}\n"],"names":["blockers","_a"],"mappings":";;AA+FA,MAAM,gBAAgB;AACtB,MAAM,oBAAoB;AAEnB,SAAS,cAAc,MAcZ;AACZ,MAAA,WAAW,KAAK,YAAY;AAC1B,QAAA,kCAAkB,IAAoC;AAEtD,QAAA,SAAS,CAAC,WAAoC;AAClD,eAAW,KAAK,YAAY;AAChB,gBAAA,QAAQ,CAAC,eAAe,WAAW,EAAE,UAAU,OAAA,CAAQ,CAAC;AAAA,EACtE;AAEA,QAAM,kBAAkB,MAAM;AAC5B,eAAW,KAAK,YAAY;AAChB,gBAAA;AAAA,MAAQ,CAAC,eACnB,WAAW,EAAE,UAAU,QAAQ,EAAE,MAAM,aAAc,CAAA;AAAA,IACvD;AAAA,EACF;AAEA,QAAM,gBAAgB,OAAO;AAAA,IAC3B;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EAAA,MACkB;;AACf,UAAA,iBAAgB,6CAAc,kBAAiB;AACrD,QAAI,eAAe;AACZ,WAAA;AACL;AAAA,IAAA;AAGF,UAAM,aAAW,UAAK,gBAAL,kCAAwB,CAAC;AAC1C,UAAM,kBACJ,WAAW,SAAS,UAAU,WAAW,SAAS;AACpD,QAAI,OAAO,aAAa,eAAe,SAAS,UAAU,iBAAiB;AACzE,iBAAW,WAAW,UAAU;AAC9B,cAAM,eAAe,UAAU,WAAW,MAAM,WAAW,KAAK;AAC1D,cAAA,YAAY,MAAM,QAAQ,UAAU;AAAA,UACxC,iBAAiB;AAAA,UACjB;AAAA,UACA,QAAQ,WAAW;AAAA,QAAA,CACpB;AACD,YAAI,WAAW;AACb,qBAAK,cAAL,8BAAiB;AACjB;AAAA,QAAA;AAAA,MACF;AAAA,IACF;AAGG,SAAA;AAAA,EACP;AAEO,SAAA;AAAA,IACL,IAAI,WAAW;AACN,aAAA;AAAA,IACT;AAAA,IACA,IAAI,SAAS;AACX,aAAO,KAAK,UAAU;AAAA,IACxB;AAAA,IACA;AAAA,IACA,WAAW,CAAC,OAAuC;AACjD,kBAAY,IAAI,EAAE;AAElB,aAAO,MAAM;AACX,oBAAY,OAAO,EAAE;AAAA,MACvB;AAAA,IACF;AAAA,IACA,MAAM,CAAC,MAAM,OAAO,iBAAiB;AACnC,cAAQ,UAAU,KAAK;AACT,oBAAA;AAAA,QACZ,MAAM,MAAM;AACL,eAAA,UAAU,MAAM,KAAK;AACnB,iBAAA,EAAE,MAAM,QAAQ;AAAA,QACzB;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA;AAAA,MAAA,CACD;AAAA,IACH;AAAA,IACA,SAAS,CAAC,MAAM,OAAO,iBAAiB;AACtC,cAAQ,UAAU,KAAK;AACT,oBAAA;AAAA,QACZ,MAAM,MAAM;AACL,eAAA,aAAa,MAAM,KAAK;AACtB,iBAAA,EAAE,MAAM,WAAW;AAAA,QAC5B;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA;AAAA,MAAA,CACD;AAAA,IACH;AAAA,IACA,IAAI,CAAC,OAAO,iBAAiB;AACb,oBAAA;AAAA,QACZ,MAAM,MAAM;AACV,eAAK,GAAG,KAAK;AACb,iBAAO,EAAE,MAAM,MAAM,MAAA,CAAO;AAAA,QAC9B;AAAA,QACA;AAAA,QACA,MAAM;AAAA,MAAA,CACP;AAAA,IACH;AAAA,IACA,MAAM,CAAC,iBAAiB;AACR,oBAAA;AAAA,QACZ,MAAM,MAAM;AACL,eAAA,MAAK,6CAAc,kBAAiB,KAAK;AACvC,iBAAA,EAAE,MAAM,QAAQ;AAAA,QACzB;AAAA,QACA;AAAA,QACA,MAAM;AAAA,MAAA,CACP;AAAA,IACH;AAAA,IACA,SAAS,CAAC,iBAAiB;AACX,oBAAA;AAAA,QACZ,MAAM,MAAM;AACL,eAAA,SAAQ,6CAAc,kBAAiB,KAAK;AAC1C,iBAAA,EAAE,MAAM,WAAW;AAAA,QAC5B;AAAA,QACA;AAAA,QACA,MAAM;AAAA,MAAA,CACP;AAAA,IACH;AAAA,IACA,YAAY,CAAC,QAAQ,KAAK,WAAW,GAAG;AAAA,IACxC,OAAO,CAAC,YAAY;;AAClB,UAAI,CAAC,KAAK,YAAa,QAAO,MAAM;AAAA,MAAC;AACrC,YAAM,aAAW,UAAK,gBAAL,kCAAwB,CAAC;AAC1C,WAAK,YAAY,CAAC,GAAG,UAAU,OAAO,CAAC;AAEvC,aAAO,MAAM;;AACX,cAAMA,cAAWC,MAAA,KAAK,gBAAL,gBAAAA,IAAA,eAAwB,CAAC;AAC1C,mBAAK,gBAAL,8BAAmBD,UAAS,OAAO,CAAC,MAAM,MAAM,OAAO;AAAA,MACzD;AAAA,IACF;AAAA,IACA,OAAO,MAAA;;AAAM,wBAAK,UAAL;AAAA;AAAA,IACb,SAAS,MAAA;;AAAM,wBAAK,YAAL;AAAA;AAAA,IACf;AAAA,EACF;AACF;AAEA,SAAS,UAAU,OAAiC;AAClD,MAAI,CAAC,OAAO;AACV,YAAQ,CAAC;AAAA,EAAA;AAEJ,SAAA;AAAA,IACL,GAAG;AAAA,IACH,KAAK,gBAAgB;AAAA,EACvB;AACF;AAkBO,SAAS,qBAAqB,MAInB;AAChB,QAAM,OACJ,6BAAM,YACL,OAAO,aAAa,cAAc,SAAU;AAEzC,QAAA,oBAAoB,IAAI,QAAQ;AAChC,QAAA,uBAAuB,IAAI,QAAQ;AAEzC,MAAI,WAAqC,CAAC;AAC1C,QAAM,eAAe,MAAM;AACrB,QAAA,eAAe,CAAC,gBACnB,WAAW;AAEd,QAAM,cAAa,6BAAM,gBAAe,CAAC,SAAS;AAC5C,QAAA,iBACJ,6BAAM,mBACL,MACC;AAAA,IACE,GAAG,IAAI,SAAS,QAAQ,GAAG,IAAI,SAAS,MAAM,GAAG,IAAI,SAAS,IAAI;AAAA,IAClE,IAAI,QAAQ;AAAA,EAAA;AAGlB,MAAI,kBAAkB,cAAc;AAChC,MAAA;AAEJ,MAAI,gBAAgB;AACpB,MAAI,qBAAqB;AACzB,MAAI,yBAAyB;AAE7B,QAAM,cAAc,MAAM;AAEtB,MAAA;AAaA,MAAA;AAGJ,QAAM,QAAQ,MAAM;AAClB,QAAI,CAAC,MAAM;AACT;AAAA,IAAA;AAIF,YAAQ,qBAAqB;AAG5B,KAAC,KAAK,SAAS,IAAI,QAAQ,YAAY,IAAI,QAAQ;AAAA,MAClD,KAAK;AAAA,MACL;AAAA,MACA,KAAK;AAAA,IACP;AAGA,YAAQ,qBAAqB;AAGtB,WAAA;AACK,gBAAA;AACO,uBAAA;AAAA,EACrB;AAGA,QAAM,qBAAqB,CACzB,MACA,UACA,UACG;AACG,UAAA,OAAO,WAAW,QAAQ;AAEhC,QAAI,CAAC,WAAW;AACK,yBAAA;AAAA,IAAA;AAIH,sBAAA,UAAU,UAAU,KAAK;AAGpC,WAAA;AAAA,MACL;AAAA,MACA;AAAA,MACA,SAAQ,6BAAM,WAAU,SAAS;AAAA,IACnC;AAEA,QAAI,CAAC,WAAW;AAEd,kBAAY,QAAQ,QAAQ,EAAE,KAAK,MAAM,OAAO;AAAA,IAAA;AAAA,EAEpD;AAEA,QAAM,YAAY,MAAM;AACtB,sBAAkB,cAAc;AAChC,YAAQ,OAAO,EAAE,MAAM,MAAA,CAAO;AAAA,EAChC;AAEA,QAAM,iBAAiB,YAAY;AACjC,QAAI,eAAe;AACD,sBAAA;AAChB;AAAA,IAAA;AAGF,QAAI,oBAAoB;AACD,2BAAA;AAAA,IAAA,OAChB;AACL,YAAMA,YAAW,aAAa;AAC9B,UAAI,OAAO,aAAa,eAAeA,UAAS,QAAQ;AACtD,mBAAW,WAAWA,WAAU;AAC9B,gBAAM,eAAe,cAAc;AAC7B,gBAAA,YAAY,MAAM,QAAQ,UAAU;AAAA,YACxC;AAAA,YACA;AAAA,YACA,QAAQ;AAAA,UAAA,CACT;AACD,cAAI,WAAW;AACG,4BAAA;AACZ,gBAAA,QAAQ,GAAG,CAAC;AAChB,oBAAQ,OAAO,EAAE,MAAM,MAAA,CAAO;AAC9B;AAAA,UAAA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGF,sBAAkB,cAAc;AAChC,YAAQ,OAAO,EAAE,MAAM,MAAA,CAAO;AAAA,EAChC;AAEM,QAAA,iBAAiB,CAAC,MAAyB;AAC/C,QAAI,wBAAwB;AACD,+BAAA;AACzB;AAAA,IAAA;AAGF,QAAI,cAAc;AAGlB,UAAMA,YAAW,aAAa;AAC9B,QAAI,OAAO,aAAa,eAAeA,UAAS,QAAQ;AACtD,iBAAW,WAAWA,WAAU;AACxB,cAAA,yBAAyB,QAAQ,sBAAsB;AAC7D,YAAI,2BAA2B,MAAM;AACrB,wBAAA;AACd;AAAA,QAAA;AAGF,YACE,OAAO,2BAA2B,cAClC,uBAAA,MAA6B,MAC7B;AACc,wBAAA;AACd;AAAA,QAAA;AAAA,MACF;AAAA,IACF;AAGF,QAAI,aAAa;AACf,QAAE,eAAe;AACjB,aAAQ,EAAE,cAAc;AAAA,IAAA;AAE1B;AAAA,EACF;AAEA,QAAM,UAAU,cAAc;AAAA,IAC5B;AAAA,IACA,WAAW,MAAM,IAAI,QAAQ;AAAA,IAC7B,WAAW,CAAC,MAAM,UAAU,mBAAmB,QAAQ,MAAM,KAAK;AAAA,IAClE,cAAc,CAAC,MAAM,UAAU,mBAAmB,WAAW,MAAM,KAAK;AAAA,IACxE,MAAM,CAAC,kBAAkB;AACvB,UAAI,cAAoC,sBAAA;AACf,+BAAA;AAClB,aAAA,IAAI,QAAQ,KAAK;AAAA,IAC1B;AAAA,IACA,SAAS,CAAC,kBAAkB;AAC1B,UAAI,cAAoC,sBAAA;AACf,+BAAA;AACzB,UAAI,QAAQ,QAAQ;AAAA,IACtB;AAAA,IACA,IAAI,CAAC,MAAM,IAAI,QAAQ,GAAG,CAAC;AAAA,IAC3B,YAAY,CAAC,SAAS,WAAW,IAAI;AAAA,IACrC;AAAA,IACA,SAAS,MAAM;AACb,UAAI,QAAQ,YAAY;AACxB,UAAI,QAAQ,eAAe;AACvB,UAAA,oBAAoB,mBAAmB,gBAAgB;AAAA,QACzD,SAAS;AAAA,MAAA,CACV;AACG,UAAA,oBAAoB,eAAe,cAAc;AAAA,IACvD;AAAA,IACA,WAAW,CAAC,aAAa;AAGnB,UAAA,oBAAoB,oBAAoB,kBAAkB;AAC1C,0BAAA;AAET,iBAAA;AAAA,MAAA;AAAA,IAEb;AAAA,IACA,aAAa;AAAA,IACb,aAAa;AAAA,EAAA,CACd;AAED,MAAI,iBAAiB,mBAAmB,gBAAgB,EAAE,SAAS,MAAM;AACrE,MAAA,iBAAiB,eAAe,cAAc;AAE9C,MAAA,QAAQ,YAAY,YAAa,MAAkB;AACrD,UAAM,MAAM,kBAAkB,MAAM,IAAI,SAAS,IAAW;AACxD,QAAA,CAAC,QAAQ,mBAA8B,WAAA;AACpC,WAAA;AAAA,EACT;AAEI,MAAA,QAAQ,eAAe,YAAa,MAAkB;AACxD,UAAM,MAAM,qBAAqB,MAAM,IAAI,SAAS,IAAW;AAC3D,QAAA,CAAC,QAAQ,mBAA8B,WAAA;AACpC,WAAA;AAAA,EACT;AAEO,SAAA;AACT;AAEO,SAAS,kBAAkB,MAAwC;AACxE,QAAM,OACJ,6BAAM,YACL,OAAO,aAAa,cAAc,SAAU;AAC/C,SAAO,qBAAqB;AAAA,IAC1B,QAAQ;AAAA,IACR,eAAe,MAAM;AACnB,YAAM,WAAW,IAAI,SAAS,KAAK,MAAM,GAAG,EAAE,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK;AACpE,aAAO,UAAU,UAAU,IAAI,QAAQ,KAAK;AAAA,IAC9C;AAAA,IACA,YAAY,CAAC,SACX,GAAG,IAAI,SAAS,QAAQ,GAAG,IAAI,SAAS,MAAM,IAAI,IAAI;AAAA,EAAA,CACzD;AACH;AAEO,SAAS,oBACd,OAGI;AAAA,EACF,gBAAgB,CAAC,GAAG;AACtB,GACe;AACf,QAAM,UAAU,KAAK;AACrB,MAAI,QAAQ,KAAK,gBAAgB,QAAQ,SAAS;AAClD,QAAM,SAAS,QAAQ,IAAI,OAAO,CAAmB,EAAA;AAE/C,QAAA,cAAc,MAAM,UAAU,QAAQ,KAAK,GAAI,OAAO,KAAK,CAAC;AAElE,SAAO,cAAc;AAAA,IACnB;AAAA,IACA,WAAW,MAAM,QAAQ;AAAA,IACzB,WAAW,CAAC,MAAM,UAAU;AAEtB,UAAA,QAAQ,QAAQ,SAAS,GAAG;AACtB,gBAAA,OAAO,QAAQ,CAAC;AACjB,eAAA,OAAO,QAAQ,CAAC;AAAA,MAAA;AAEzB,aAAO,KAAK,KAAK;AACjB,cAAQ,KAAK,IAAI;AACjB,cAAQ,KAAK,IAAI,QAAQ,SAAS,GAAG,CAAC;AAAA,IACxC;AAAA,IACA,cAAc,CAAC,MAAM,UAAU;AAC7B,aAAO,KAAK,IAAI;AAChB,cAAQ,KAAK,IAAI;AAAA,IACnB;AAAA,IACA,MAAM,MAAM;AACV,cAAQ,KAAK,IAAI,QAAQ,GAAG,CAAC;AAAA,IAC/B;AAAA,IACA,SAAS,MAAM;AACb,cAAQ,KAAK,IAAI,QAAQ,GAAG,QAAQ,SAAS,CAAC;AAAA,IAChD;AAAA,IACA,IAAI,CAAC,MAAM;AACD,cAAA,KAAK,IAAI,KAAK,IAAI,QAAQ,GAAG,CAAC,GAAG,QAAQ,SAAS,CAAC;AAAA,IAC7D;AAAA,IACA,YAAY,CAAC,SAAS;AAAA,EAAA,CACvB;AACH;AAEgB,SAAA,UACd,MACA,OACiB;AACX,QAAA,YAAY,KAAK,QAAQ,GAAG;AAC5B,QAAA,cAAc,KAAK,QAAQ,GAAG;AAE7B,SAAA;AAAA,IACL;AAAA,IACA,UAAU,KAAK;AAAA,MACb;AAAA,MACA,YAAY,IACR,cAAc,IACZ,KAAK,IAAI,WAAW,WAAW,IAC/B,YACF,cAAc,IACZ,cACA,KAAK;AAAA,IACb;AAAA,IACA,MAAM,YAAY,KAAK,KAAK,UAAU,SAAS,IAAI;AAAA,IACnD,QACE,cAAc,KACV,KAAK,MAAM,aAAa,cAAc,KAAK,SAAY,SAAS,IAChE;AAAA,IACN,OAAO,SAAS,CAAA;AAAA,EAClB;AACF;AAGA,SAAS,kBAAkB;AACjB,UAAA,KAAK,WAAW,GAAG,SAAS,EAAE,EAAE,UAAU,CAAC;AACrD;;;;;;"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/index.ts"],"sourcesContent":["// While the public API was clearly inspired by the \"history\" npm package,\n// This implementation attempts to be more lightweight by\n// making assumptions about the way TanStack Router works\n\nexport interface NavigateOptions {\n ignoreBlocker?: boolean\n}\n\ntype SubscriberHistoryAction =\n | {\n type: HistoryAction\n }\n | {\n type: 'GO'\n index: number\n }\n\ntype SubscriberArgs = {\n location: HistoryLocation\n action: SubscriberHistoryAction\n}\n\nexport interface RouterHistory {\n location: HistoryLocation\n length: number\n subscribers: Set<(opts: SubscriberArgs) => void>\n subscribe: (cb: (opts: SubscriberArgs) => void) => () => void\n push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void\n replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void\n go: (index: number, navigateOpts?: NavigateOptions) => void\n back: (navigateOpts?: NavigateOptions) => void\n forward: (navigateOpts?: NavigateOptions) => void\n canGoBack: () => boolean\n createHref: (href: string) => string\n block: (blocker: NavigationBlocker) => () => void\n flush: () => void\n destroy: () => void\n notify: (action: SubscriberHistoryAction) => void\n _ignoreSubscribers?: boolean\n}\n\nexport interface HistoryLocation extends ParsedPath {\n state: HistoryState\n}\n\nexport interface ParsedPath {\n href: string\n pathname: string\n search: string\n hash: string\n}\n\nexport interface HistoryState {\n key?: string\n __TSR_index: number\n}\n\ntype ShouldAllowNavigation = any\n\nexport type HistoryAction = 'PUSH' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO'\n\nexport type BlockerFnArgs = {\n currentLocation: HistoryLocation\n nextLocation: HistoryLocation\n action: HistoryAction\n}\n\nexport type BlockerFn = (\n args: BlockerFnArgs,\n) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation\n\nexport type NavigationBlocker = {\n blockerFn: BlockerFn\n enableBeforeUnload?: (() => boolean) | boolean\n}\n\ntype TryNavigateArgs = {\n task: () => void\n type: 'PUSH' | 'REPLACE' | 'BACK' | 'FORWARD' | 'GO'\n navigateOpts?: NavigateOptions\n} & (\n | {\n type: 'PUSH' | 'REPLACE'\n path: string\n state: any\n }\n | {\n type: 'BACK' | 'FORWARD' | 'GO'\n }\n)\n\nconst stateIndexKey = '__TSR_index'\nconst popStateEvent = 'popstate'\nconst beforeUnloadEvent = 'beforeunload'\n\nexport function createHistory(opts: {\n getLocation: () => HistoryLocation\n getLength: () => number\n pushState: (path: string, state: any) => void\n replaceState: (path: string, state: any) => void\n go: (n: number) => void\n back: (ignoreBlocker: boolean) => void\n forward: (ignoreBlocker: boolean) => void\n createHref: (path: string) => string\n flush?: () => void\n destroy?: () => void\n onBlocked?: () => void\n getBlockers?: () => Array<NavigationBlocker>\n setBlockers?: (blockers: Array<NavigationBlocker>) => void\n // Avoid notifying on forward/back/go, used for browser history as we already get notified by the popstate event\n notifyOnIndexChange?: boolean\n}): RouterHistory {\n let location = opts.getLocation()\n const subscribers = new Set<(opts: SubscriberArgs) => void>()\n\n const notify = (action: SubscriberHistoryAction) => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) => subscriber({ location, action }))\n }\n\n const handleIndexChange = (action: SubscriberHistoryAction) => {\n if (opts.notifyOnIndexChange ?? true) notify(action)\n else location = opts.getLocation()\n }\n\n const tryNavigation = async ({\n task,\n navigateOpts,\n ...actionInfo\n }: TryNavigateArgs) => {\n const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false\n if (ignoreBlocker) {\n task()\n return\n }\n\n const blockers = opts.getBlockers?.() ?? []\n const isPushOrReplace =\n actionInfo.type === 'PUSH' || actionInfo.type === 'REPLACE'\n if (typeof document !== 'undefined' && blockers.length && isPushOrReplace) {\n for (const blocker of blockers) {\n const nextLocation = parseHref(actionInfo.path, actionInfo.state)\n const isBlocked = await blocker.blockerFn({\n currentLocation: location,\n nextLocation,\n action: actionInfo.type,\n })\n if (isBlocked) {\n opts.onBlocked?.()\n return\n }\n }\n }\n\n task()\n }\n\n return {\n get location() {\n return location\n },\n get length() {\n return opts.getLength()\n },\n subscribers,\n subscribe: (cb: (opts: SubscriberArgs) => void) => {\n subscribers.add(cb)\n\n return () => {\n subscribers.delete(cb)\n }\n },\n push: (path, state, navigateOpts) => {\n const currentIndex = location.state[stateIndexKey]\n state = assignKeyAndIndex(currentIndex + 1, state)\n tryNavigation({\n task: () => {\n opts.pushState(path, state)\n notify({ type: 'PUSH' })\n },\n navigateOpts,\n type: 'PUSH',\n path,\n state,\n })\n },\n replace: (path, state, navigateOpts) => {\n const currentIndex = location.state[stateIndexKey]\n state = assignKeyAndIndex(currentIndex, state)\n tryNavigation({\n task: () => {\n opts.replaceState(path, state)\n notify({ type: 'REPLACE' })\n },\n navigateOpts,\n type: 'REPLACE',\n path,\n state,\n })\n },\n go: (index, navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.go(index)\n handleIndexChange({ type: 'GO', index })\n },\n navigateOpts,\n type: 'GO',\n })\n },\n back: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.back(navigateOpts?.ignoreBlocker ?? false)\n handleIndexChange({ type: 'BACK' })\n },\n navigateOpts,\n type: 'BACK',\n })\n },\n forward: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.forward(navigateOpts?.ignoreBlocker ?? false)\n handleIndexChange({ type: 'FORWARD' })\n },\n navigateOpts,\n type: 'FORWARD',\n })\n },\n canGoBack: () => location.state[stateIndexKey] !== 0,\n createHref: (str) => opts.createHref(str),\n block: (blocker) => {\n if (!opts.setBlockers) return () => {}\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers([...blockers, blocker])\n\n return () => {\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers?.(blockers.filter((b) => b !== blocker))\n }\n },\n flush: () => opts.flush?.(),\n destroy: () => opts.destroy?.(),\n notify,\n }\n}\n\nfunction assignKeyAndIndex(index: number, state: HistoryState | undefined) {\n if (!state) {\n state = {} as HistoryState\n }\n return {\n ...state,\n key: createRandomKey(),\n [stateIndexKey]: index,\n }\n}\n\n/**\n * Creates a history object that can be used to interact with the browser's\n * navigation. This is a lightweight API wrapping the browser's native methods.\n * It is designed to work with TanStack Router, but could be used as a standalone API as well.\n * IMPORTANT: This API implements history throttling via a microtask to prevent\n * excessive calls to the history API. In some browsers, calling history.pushState or\n * history.replaceState in quick succession can cause the browser to ignore subsequent\n * calls. This API smooths out those differences and ensures that your application\n * state will *eventually* match the browser state. In most cases, this is not a problem,\n * but if you need to ensure that the browser state is up to date, you can use the\n * `history.flush` method to immediately flush all pending state changes to the browser URL.\n * @param opts\n * @param opts.getHref A function that returns the current href (path + search + hash)\n * @param opts.createHref A function that takes a path and returns a href (path + search + hash)\n * @returns A history instance\n */\nexport function createBrowserHistory(opts?: {\n parseLocation?: () => HistoryLocation\n createHref?: (path: string) => string\n window?: any\n}): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n\n const originalPushState = win.history.pushState\n const originalReplaceState = win.history.replaceState\n\n let blockers: Array<NavigationBlocker> = []\n const _getBlockers = () => blockers\n const _setBlockers = (newBlockers: Array<NavigationBlocker>) =>\n (blockers = newBlockers)\n\n const createHref = opts?.createHref ?? ((path) => path)\n const parseLocation =\n opts?.parseLocation ??\n (() =>\n parseHref(\n `${win.location.pathname}${win.location.search}${win.location.hash}`,\n win.history.state,\n ))\n\n let currentLocation = parseLocation()\n let rollbackLocation: HistoryLocation | undefined\n\n let nextPopIsGo = false\n let ignoreNextPop = false\n let skipBlockerNextPop = false\n let ignoreNextBeforeUnload = false\n\n const getLocation = () => currentLocation\n\n let next:\n | undefined\n | {\n // This is the latest location that we were attempting to push/replace\n href: string\n // This is the latest state that we were attempting to push/replace\n state: any\n // This is the latest type that we were attempting to push/replace\n isPush: boolean\n }\n\n // We need to track the current scheduled update to prevent\n // multiple updates from being scheduled at the same time.\n let scheduled: Promise<void> | undefined\n\n // This function flushes the next update to the browser history\n const flush = () => {\n if (!next) {\n return\n }\n\n // We need to ignore any updates to the subscribers while we update the browser history\n history._ignoreSubscribers = true\n\n // Update the browser history\n ;(next.isPush ? win.history.pushState : win.history.replaceState)(\n next.state,\n '',\n next.href,\n )\n\n // Stop ignoring subscriber updates\n history._ignoreSubscribers = false\n\n // Reset the nextIsPush flag and clear the scheduled update\n next = undefined\n scheduled = undefined\n rollbackLocation = undefined\n }\n\n // This function queues up a call to update the browser history\n const queueHistoryAction = (\n type: 'push' | 'replace',\n destHref: string,\n state: any,\n ) => {\n const href = createHref(destHref)\n\n if (!scheduled) {\n rollbackLocation = currentLocation\n }\n\n // Update the location in memory\n currentLocation = parseHref(destHref, state)\n\n // Keep track of the next location we need to flush to the URL\n next = {\n href,\n state,\n isPush: next?.isPush || type === 'push',\n }\n\n if (!scheduled) {\n // Schedule an update to the browser history\n scheduled = Promise.resolve().then(() => flush())\n }\n }\n\n // NOTE: this function can probably be removed\n const onPushPop = (type: 'PUSH' | 'REPLACE') => {\n currentLocation = parseLocation()\n history.notify({ type })\n }\n\n const onPushPopEvent = async () => {\n if (ignoreNextPop) {\n ignoreNextPop = false\n return\n }\n\n const nextLocation = parseLocation()\n const delta =\n nextLocation.state[stateIndexKey] - currentLocation.state[stateIndexKey]\n const isForward = delta === 1\n const isBack = delta === -1\n const isGo = (!isForward && !isBack) || nextPopIsGo\n nextPopIsGo = false\n\n const action = isGo ? 'GO' : isBack ? 'BACK' : 'FORWARD'\n const notify: SubscriberHistoryAction = isGo\n ? {\n type: 'GO',\n index: delta,\n }\n : {\n type: isBack ? 'BACK' : 'FORWARD',\n }\n\n if (skipBlockerNextPop) {\n skipBlockerNextPop = false\n } else {\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const isBlocked = await blocker.blockerFn({\n currentLocation,\n nextLocation,\n action,\n })\n if (isBlocked) {\n ignoreNextPop = true\n win.history.go(1)\n history.notify(notify)\n return\n }\n }\n }\n }\n\n currentLocation = parseLocation()\n history.notify(notify)\n }\n\n const onBeforeUnload = (e: BeforeUnloadEvent) => {\n if (ignoreNextBeforeUnload) {\n ignoreNextBeforeUnload = false\n return\n }\n\n let shouldBlock = false\n\n // If one blocker has a non-disabled beforeUnload, we should block\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true\n if (shouldHaveBeforeUnload === true) {\n shouldBlock = true\n break\n }\n\n if (\n typeof shouldHaveBeforeUnload === 'function' &&\n shouldHaveBeforeUnload() === true\n ) {\n shouldBlock = true\n break\n }\n }\n }\n\n if (shouldBlock) {\n e.preventDefault()\n return (e.returnValue = '')\n }\n return\n }\n\n const history = createHistory({\n getLocation,\n getLength: () => win.history.length,\n pushState: (href, state) => queueHistoryAction('push', href, state),\n replaceState: (href, state) => queueHistoryAction('replace', href, state),\n back: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n return win.history.back()\n },\n forward: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n win.history.forward()\n },\n go: (n) => {\n nextPopIsGo = true\n win.history.go(n)\n },\n createHref: (href) => createHref(href),\n flush,\n destroy: () => {\n win.history.pushState = originalPushState\n win.history.replaceState = originalReplaceState\n win.removeEventListener(beforeUnloadEvent, onBeforeUnload, {\n capture: true,\n })\n win.removeEventListener(popStateEvent, onPushPopEvent)\n },\n onBlocked: () => {\n // If a navigation is blocked, we need to rollback the location\n // that we optimistically updated in memory.\n if (rollbackLocation && currentLocation !== rollbackLocation) {\n currentLocation = rollbackLocation\n }\n },\n getBlockers: _getBlockers,\n setBlockers: _setBlockers,\n notifyOnIndexChange: false,\n })\n\n win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true })\n win.addEventListener(popStateEvent, onPushPopEvent)\n\n win.history.pushState = function (...args: Array<any>) {\n const res = originalPushState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop('PUSH')\n return res\n }\n\n win.history.replaceState = function (...args: Array<any>) {\n const res = originalReplaceState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop('REPLACE')\n return res\n }\n\n return history\n}\n\nexport function createHashHistory(opts?: { window?: any }): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n return createBrowserHistory({\n window: win,\n parseLocation: () => {\n const hashHref = win.location.hash.split('#').slice(1).join('#') ?? '/'\n return parseHref(hashHref, win.history.state)\n },\n createHref: (href) =>\n `${win.location.pathname}${win.location.search}#${href}`,\n })\n}\n\nexport function createMemoryHistory(\n opts: {\n initialEntries: Array<string>\n initialIndex?: number\n } = {\n initialEntries: ['/'],\n },\n): RouterHistory {\n const entries = opts.initialEntries\n let index = opts.initialIndex\n ? Math.min(Math.max(opts.initialIndex, 0), entries.length - 1)\n : entries.length - 1\n const states = entries.map<HistoryState>((_entry, index) =>\n assignKeyAndIndex(index, undefined),\n )\n\n const getLocation = () => parseHref(entries[index]!, states[index])\n\n return createHistory({\n getLocation,\n getLength: () => entries.length,\n pushState: (path, state) => {\n // Removes all subsequent entries after the current index to start a new branch\n if (index < entries.length - 1) {\n entries.splice(index + 1)\n states.splice(index + 1)\n }\n states.push(state)\n entries.push(path)\n index = Math.max(entries.length - 1, 0)\n },\n replaceState: (path, state) => {\n states[index] = state\n entries[index] = path\n },\n back: () => {\n index = Math.max(index - 1, 0)\n },\n forward: () => {\n index = Math.min(index + 1, entries.length - 1)\n },\n go: (n) => {\n index = Math.min(Math.max(index + n, 0), entries.length - 1)\n },\n createHref: (path) => path,\n })\n}\n\nexport function parseHref(\n href: string,\n state: HistoryState | undefined,\n): HistoryLocation {\n const hashIndex = href.indexOf('#')\n const searchIndex = href.indexOf('?')\n\n return {\n href,\n pathname: href.substring(\n 0,\n hashIndex > 0\n ? searchIndex > 0\n ? Math.min(hashIndex, searchIndex)\n : hashIndex\n : searchIndex > 0\n ? searchIndex\n : href.length,\n ),\n hash: hashIndex > -1 ? href.substring(hashIndex) : '',\n search:\n searchIndex > -1\n ? href.slice(searchIndex, hashIndex === -1 ? undefined : hashIndex)\n : '',\n state: state || { [stateIndexKey]: 0 },\n }\n}\n\n// Thanks co-pilot!\nfunction createRandomKey() {\n return (Math.random() + 1).toString(36).substring(7)\n}\n"],"names":["blockers","_a","index"],"mappings":"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"}
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -2,7 +2,7 @@ export interface NavigateOptions {
|
|
|
2
2
|
ignoreBlocker?: boolean;
|
|
3
3
|
}
|
|
4
4
|
type SubscriberHistoryAction = {
|
|
5
|
-
type: HistoryAction
|
|
5
|
+
type: HistoryAction;
|
|
6
6
|
} | {
|
|
7
7
|
type: 'GO';
|
|
8
8
|
index: number;
|
|
@@ -21,6 +21,7 @@ export interface RouterHistory {
|
|
|
21
21
|
go: (index: number, navigateOpts?: NavigateOptions) => void;
|
|
22
22
|
back: (navigateOpts?: NavigateOptions) => void;
|
|
23
23
|
forward: (navigateOpts?: NavigateOptions) => void;
|
|
24
|
+
canGoBack: () => boolean;
|
|
24
25
|
createHref: (href: string) => string;
|
|
25
26
|
block: (blocker: NavigationBlocker) => () => void;
|
|
26
27
|
flush: () => void;
|
|
@@ -39,9 +40,10 @@ export interface ParsedPath {
|
|
|
39
40
|
}
|
|
40
41
|
export interface HistoryState {
|
|
41
42
|
key?: string;
|
|
43
|
+
__TSR_index: number;
|
|
42
44
|
}
|
|
43
45
|
type ShouldAllowNavigation = any;
|
|
44
|
-
export type HistoryAction = 'PUSH' | '
|
|
46
|
+
export type HistoryAction = 'PUSH' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO';
|
|
45
47
|
export type BlockerFnArgs = {
|
|
46
48
|
currentLocation: HistoryLocation;
|
|
47
49
|
nextLocation: HistoryLocation;
|
|
@@ -63,9 +65,10 @@ export declare function createHistory(opts: {
|
|
|
63
65
|
createHref: (path: string) => string;
|
|
64
66
|
flush?: () => void;
|
|
65
67
|
destroy?: () => void;
|
|
66
|
-
onBlocked?: (
|
|
68
|
+
onBlocked?: () => void;
|
|
67
69
|
getBlockers?: () => Array<NavigationBlocker>;
|
|
68
70
|
setBlockers?: (blockers: Array<NavigationBlocker>) => void;
|
|
71
|
+
notifyOnIndexChange?: boolean;
|
|
69
72
|
}): RouterHistory;
|
|
70
73
|
/**
|
|
71
74
|
* Creates a history object that can be used to interact with the browser's
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export interface NavigateOptions {
|
|
|
2
2
|
ignoreBlocker?: boolean;
|
|
3
3
|
}
|
|
4
4
|
type SubscriberHistoryAction = {
|
|
5
|
-
type: HistoryAction
|
|
5
|
+
type: HistoryAction;
|
|
6
6
|
} | {
|
|
7
7
|
type: 'GO';
|
|
8
8
|
index: number;
|
|
@@ -21,6 +21,7 @@ export interface RouterHistory {
|
|
|
21
21
|
go: (index: number, navigateOpts?: NavigateOptions) => void;
|
|
22
22
|
back: (navigateOpts?: NavigateOptions) => void;
|
|
23
23
|
forward: (navigateOpts?: NavigateOptions) => void;
|
|
24
|
+
canGoBack: () => boolean;
|
|
24
25
|
createHref: (href: string) => string;
|
|
25
26
|
block: (blocker: NavigationBlocker) => () => void;
|
|
26
27
|
flush: () => void;
|
|
@@ -39,9 +40,10 @@ export interface ParsedPath {
|
|
|
39
40
|
}
|
|
40
41
|
export interface HistoryState {
|
|
41
42
|
key?: string;
|
|
43
|
+
__TSR_index: number;
|
|
42
44
|
}
|
|
43
45
|
type ShouldAllowNavigation = any;
|
|
44
|
-
export type HistoryAction = 'PUSH' | '
|
|
46
|
+
export type HistoryAction = 'PUSH' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO';
|
|
45
47
|
export type BlockerFnArgs = {
|
|
46
48
|
currentLocation: HistoryLocation;
|
|
47
49
|
nextLocation: HistoryLocation;
|
|
@@ -63,9 +65,10 @@ export declare function createHistory(opts: {
|
|
|
63
65
|
createHref: (path: string) => string;
|
|
64
66
|
flush?: () => void;
|
|
65
67
|
destroy?: () => void;
|
|
66
|
-
onBlocked?: (
|
|
68
|
+
onBlocked?: () => void;
|
|
67
69
|
getBlockers?: () => Array<NavigationBlocker>;
|
|
68
70
|
setBlockers?: (blockers: Array<NavigationBlocker>) => void;
|
|
71
|
+
notifyOnIndexChange?: boolean;
|
|
69
72
|
}): RouterHistory;
|
|
70
73
|
/**
|
|
71
74
|
* Creates a history object that can be used to interact with the browser's
|
package/dist/esm/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const stateIndexKey = "__TSR_index";
|
|
1
2
|
const popStateEvent = "popstate";
|
|
2
3
|
const beforeUnloadEvent = "beforeunload";
|
|
3
4
|
function createHistory(opts) {
|
|
@@ -7,11 +8,9 @@ function createHistory(opts) {
|
|
|
7
8
|
location = opts.getLocation();
|
|
8
9
|
subscribers.forEach((subscriber) => subscriber({ location, action }));
|
|
9
10
|
};
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
(subscriber) => subscriber({ location, action: { type: "ROLLBACK" } })
|
|
14
|
-
);
|
|
11
|
+
const handleIndexChange = (action) => {
|
|
12
|
+
if (opts.notifyOnIndexChange ?? true) notify(action);
|
|
13
|
+
else location = opts.getLocation();
|
|
15
14
|
};
|
|
16
15
|
const tryNavigation = async ({
|
|
17
16
|
task,
|
|
@@ -35,7 +34,7 @@ function createHistory(opts) {
|
|
|
35
34
|
action: actionInfo.type
|
|
36
35
|
});
|
|
37
36
|
if (isBlocked) {
|
|
38
|
-
(_b = opts.onBlocked) == null ? void 0 : _b.call(opts
|
|
37
|
+
(_b = opts.onBlocked) == null ? void 0 : _b.call(opts);
|
|
39
38
|
return;
|
|
40
39
|
}
|
|
41
40
|
}
|
|
@@ -57,7 +56,8 @@ function createHistory(opts) {
|
|
|
57
56
|
};
|
|
58
57
|
},
|
|
59
58
|
push: (path, state, navigateOpts) => {
|
|
60
|
-
|
|
59
|
+
const currentIndex = location.state[stateIndexKey];
|
|
60
|
+
state = assignKeyAndIndex(currentIndex + 1, state);
|
|
61
61
|
tryNavigation({
|
|
62
62
|
task: () => {
|
|
63
63
|
opts.pushState(path, state);
|
|
@@ -70,7 +70,8 @@ function createHistory(opts) {
|
|
|
70
70
|
});
|
|
71
71
|
},
|
|
72
72
|
replace: (path, state, navigateOpts) => {
|
|
73
|
-
|
|
73
|
+
const currentIndex = location.state[stateIndexKey];
|
|
74
|
+
state = assignKeyAndIndex(currentIndex, state);
|
|
74
75
|
tryNavigation({
|
|
75
76
|
task: () => {
|
|
76
77
|
opts.replaceState(path, state);
|
|
@@ -86,7 +87,7 @@ function createHistory(opts) {
|
|
|
86
87
|
tryNavigation({
|
|
87
88
|
task: () => {
|
|
88
89
|
opts.go(index);
|
|
89
|
-
|
|
90
|
+
handleIndexChange({ type: "GO", index });
|
|
90
91
|
},
|
|
91
92
|
navigateOpts,
|
|
92
93
|
type: "GO"
|
|
@@ -96,7 +97,7 @@ function createHistory(opts) {
|
|
|
96
97
|
tryNavigation({
|
|
97
98
|
task: () => {
|
|
98
99
|
opts.back((navigateOpts == null ? void 0 : navigateOpts.ignoreBlocker) ?? false);
|
|
99
|
-
|
|
100
|
+
handleIndexChange({ type: "BACK" });
|
|
100
101
|
},
|
|
101
102
|
navigateOpts,
|
|
102
103
|
type: "BACK"
|
|
@@ -106,12 +107,13 @@ function createHistory(opts) {
|
|
|
106
107
|
tryNavigation({
|
|
107
108
|
task: () => {
|
|
108
109
|
opts.forward((navigateOpts == null ? void 0 : navigateOpts.ignoreBlocker) ?? false);
|
|
109
|
-
|
|
110
|
+
handleIndexChange({ type: "FORWARD" });
|
|
110
111
|
},
|
|
111
112
|
navigateOpts,
|
|
112
113
|
type: "FORWARD"
|
|
113
114
|
});
|
|
114
115
|
},
|
|
116
|
+
canGoBack: () => location.state[stateIndexKey] !== 0,
|
|
115
117
|
createHref: (str) => opts.createHref(str),
|
|
116
118
|
block: (blocker) => {
|
|
117
119
|
var _a;
|
|
@@ -136,13 +138,14 @@ function createHistory(opts) {
|
|
|
136
138
|
notify
|
|
137
139
|
};
|
|
138
140
|
}
|
|
139
|
-
function
|
|
141
|
+
function assignKeyAndIndex(index, state) {
|
|
140
142
|
if (!state) {
|
|
141
143
|
state = {};
|
|
142
144
|
}
|
|
143
145
|
return {
|
|
144
146
|
...state,
|
|
145
|
-
key: createRandomKey()
|
|
147
|
+
key: createRandomKey(),
|
|
148
|
+
[stateIndexKey]: index
|
|
146
149
|
};
|
|
147
150
|
}
|
|
148
151
|
function createBrowserHistory(opts) {
|
|
@@ -159,6 +162,7 @@ function createBrowserHistory(opts) {
|
|
|
159
162
|
));
|
|
160
163
|
let currentLocation = parseLocation();
|
|
161
164
|
let rollbackLocation;
|
|
165
|
+
let nextPopIsGo = false;
|
|
162
166
|
let ignoreNextPop = false;
|
|
163
167
|
let skipBlockerNextPop = false;
|
|
164
168
|
let ignoreNextBeforeUnload = false;
|
|
@@ -195,38 +199,50 @@ function createBrowserHistory(opts) {
|
|
|
195
199
|
scheduled = Promise.resolve().then(() => flush());
|
|
196
200
|
}
|
|
197
201
|
};
|
|
198
|
-
const onPushPop = () => {
|
|
202
|
+
const onPushPop = (type) => {
|
|
199
203
|
currentLocation = parseLocation();
|
|
200
|
-
history.notify({ type
|
|
204
|
+
history.notify({ type });
|
|
201
205
|
};
|
|
202
206
|
const onPushPopEvent = async () => {
|
|
203
207
|
if (ignoreNextPop) {
|
|
204
208
|
ignoreNextPop = false;
|
|
205
209
|
return;
|
|
206
210
|
}
|
|
211
|
+
const nextLocation = parseLocation();
|
|
212
|
+
const delta = nextLocation.state[stateIndexKey] - currentLocation.state[stateIndexKey];
|
|
213
|
+
const isForward = delta === 1;
|
|
214
|
+
const isBack = delta === -1;
|
|
215
|
+
const isGo = !isForward && !isBack || nextPopIsGo;
|
|
216
|
+
nextPopIsGo = false;
|
|
217
|
+
const action = isGo ? "GO" : isBack ? "BACK" : "FORWARD";
|
|
218
|
+
const notify = isGo ? {
|
|
219
|
+
type: "GO",
|
|
220
|
+
index: delta
|
|
221
|
+
} : {
|
|
222
|
+
type: isBack ? "BACK" : "FORWARD"
|
|
223
|
+
};
|
|
207
224
|
if (skipBlockerNextPop) {
|
|
208
225
|
skipBlockerNextPop = false;
|
|
209
226
|
} else {
|
|
210
227
|
const blockers2 = _getBlockers();
|
|
211
228
|
if (typeof document !== "undefined" && blockers2.length) {
|
|
212
229
|
for (const blocker of blockers2) {
|
|
213
|
-
const nextLocation = parseLocation();
|
|
214
230
|
const isBlocked = await blocker.blockerFn({
|
|
215
231
|
currentLocation,
|
|
216
232
|
nextLocation,
|
|
217
|
-
action
|
|
233
|
+
action
|
|
218
234
|
});
|
|
219
235
|
if (isBlocked) {
|
|
220
236
|
ignoreNextPop = true;
|
|
221
237
|
win.history.go(1);
|
|
222
|
-
history.notify(
|
|
238
|
+
history.notify(notify);
|
|
223
239
|
return;
|
|
224
240
|
}
|
|
225
241
|
}
|
|
226
242
|
}
|
|
227
243
|
}
|
|
228
244
|
currentLocation = parseLocation();
|
|
229
|
-
history.notify(
|
|
245
|
+
history.notify(notify);
|
|
230
246
|
};
|
|
231
247
|
const onBeforeUnload = (e) => {
|
|
232
248
|
if (ignoreNextBeforeUnload) {
|
|
@@ -269,7 +285,10 @@ function createBrowserHistory(opts) {
|
|
|
269
285
|
ignoreNextBeforeUnload = true;
|
|
270
286
|
win.history.forward();
|
|
271
287
|
},
|
|
272
|
-
go: (n) =>
|
|
288
|
+
go: (n) => {
|
|
289
|
+
nextPopIsGo = true;
|
|
290
|
+
win.history.go(n);
|
|
291
|
+
},
|
|
273
292
|
createHref: (href) => createHref(href),
|
|
274
293
|
flush,
|
|
275
294
|
destroy: () => {
|
|
@@ -280,25 +299,25 @@ function createBrowserHistory(opts) {
|
|
|
280
299
|
});
|
|
281
300
|
win.removeEventListener(popStateEvent, onPushPopEvent);
|
|
282
301
|
},
|
|
283
|
-
onBlocked: (
|
|
302
|
+
onBlocked: () => {
|
|
284
303
|
if (rollbackLocation && currentLocation !== rollbackLocation) {
|
|
285
304
|
currentLocation = rollbackLocation;
|
|
286
|
-
onUpdate();
|
|
287
305
|
}
|
|
288
306
|
},
|
|
289
307
|
getBlockers: _getBlockers,
|
|
290
|
-
setBlockers: _setBlockers
|
|
308
|
+
setBlockers: _setBlockers,
|
|
309
|
+
notifyOnIndexChange: false
|
|
291
310
|
});
|
|
292
311
|
win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true });
|
|
293
312
|
win.addEventListener(popStateEvent, onPushPopEvent);
|
|
294
313
|
win.history.pushState = function(...args) {
|
|
295
314
|
const res = originalPushState.apply(win.history, args);
|
|
296
|
-
if (!history._ignoreSubscribers) onPushPop();
|
|
315
|
+
if (!history._ignoreSubscribers) onPushPop("PUSH");
|
|
297
316
|
return res;
|
|
298
317
|
};
|
|
299
318
|
win.history.replaceState = function(...args) {
|
|
300
319
|
const res = originalReplaceState.apply(win.history, args);
|
|
301
|
-
if (!history._ignoreSubscribers) onPushPop();
|
|
320
|
+
if (!history._ignoreSubscribers) onPushPop("REPLACE");
|
|
302
321
|
return res;
|
|
303
322
|
};
|
|
304
323
|
return history;
|
|
@@ -318,8 +337,10 @@ function createMemoryHistory(opts = {
|
|
|
318
337
|
initialEntries: ["/"]
|
|
319
338
|
}) {
|
|
320
339
|
const entries = opts.initialEntries;
|
|
321
|
-
let index = opts.initialIndex
|
|
322
|
-
const states = entries.map(
|
|
340
|
+
let index = opts.initialIndex ? Math.min(Math.max(opts.initialIndex, 0), entries.length - 1) : entries.length - 1;
|
|
341
|
+
const states = entries.map(
|
|
342
|
+
(_entry, index2) => assignKeyAndIndex(index2, void 0)
|
|
343
|
+
);
|
|
323
344
|
const getLocation = () => parseHref(entries[index], states[index]);
|
|
324
345
|
return createHistory({
|
|
325
346
|
getLocation,
|
|
@@ -360,7 +381,7 @@ function parseHref(href, state) {
|
|
|
360
381
|
),
|
|
361
382
|
hash: hashIndex > -1 ? href.substring(hashIndex) : "",
|
|
362
383
|
search: searchIndex > -1 ? href.slice(searchIndex, hashIndex === -1 ? void 0 : hashIndex) : "",
|
|
363
|
-
state: state || {}
|
|
384
|
+
state: state || { [stateIndexKey]: 0 }
|
|
364
385
|
};
|
|
365
386
|
}
|
|
366
387
|
function createRandomKey() {
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/index.ts"],"sourcesContent":["// While the public API was clearly inspired by the \"history\" npm package,\n// This implementation attempts to be more lightweight by\n// making assumptions about the way TanStack Router works\n\nexport interface NavigateOptions {\n ignoreBlocker?: boolean\n}\n\ntype SubscriberHistoryAction =\n | {\n type: HistoryAction | 'ROLLBACK'\n }\n | {\n type: 'GO'\n index: number\n }\n\ntype SubscriberArgs = {\n location: HistoryLocation\n action: SubscriberHistoryAction\n}\n\nexport interface RouterHistory {\n location: HistoryLocation\n length: number\n subscribers: Set<(opts: SubscriberArgs) => void>\n subscribe: (cb: (opts: SubscriberArgs) => void) => () => void\n push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void\n replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void\n go: (index: number, navigateOpts?: NavigateOptions) => void\n back: (navigateOpts?: NavigateOptions) => void\n forward: (navigateOpts?: NavigateOptions) => void\n createHref: (href: string) => string\n block: (blocker: NavigationBlocker) => () => void\n flush: () => void\n destroy: () => void\n notify: (action: SubscriberHistoryAction) => void\n _ignoreSubscribers?: boolean\n}\n\nexport interface HistoryLocation extends ParsedPath {\n state: HistoryState\n}\n\nexport interface ParsedPath {\n href: string\n pathname: string\n search: string\n hash: string\n}\n\nexport interface HistoryState {\n key?: string\n}\n\ntype ShouldAllowNavigation = any\n\nexport type HistoryAction =\n | 'PUSH'\n | 'POP'\n | 'REPLACE'\n | 'FORWARD'\n | 'BACK'\n | 'GO'\n\nexport type BlockerFnArgs = {\n currentLocation: HistoryLocation\n nextLocation: HistoryLocation\n action: HistoryAction\n}\n\nexport type BlockerFn = (\n args: BlockerFnArgs,\n) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation\n\nexport type NavigationBlocker = {\n blockerFn: BlockerFn\n enableBeforeUnload?: (() => boolean) | boolean\n}\n\ntype TryNavigateArgs = {\n task: () => void\n type: 'PUSH' | 'REPLACE' | 'BACK' | 'FORWARD' | 'GO'\n navigateOpts?: NavigateOptions\n} & (\n | {\n type: 'PUSH' | 'REPLACE'\n path: string\n state: any\n }\n | {\n type: 'BACK' | 'FORWARD' | 'GO'\n }\n)\n\nconst popStateEvent = 'popstate'\nconst beforeUnloadEvent = 'beforeunload'\n\nexport function createHistory(opts: {\n getLocation: () => HistoryLocation\n getLength: () => number\n pushState: (path: string, state: any) => void\n replaceState: (path: string, state: any) => void\n go: (n: number) => void\n back: (ignoreBlocker: boolean) => void\n forward: (ignoreBlocker: boolean) => void\n createHref: (path: string) => string\n flush?: () => void\n destroy?: () => void\n onBlocked?: (onUpdate: () => void) => void\n getBlockers?: () => Array<NavigationBlocker>\n setBlockers?: (blockers: Array<NavigationBlocker>) => void\n}): RouterHistory {\n let location = opts.getLocation()\n const subscribers = new Set<(opts: SubscriberArgs) => void>()\n\n const notify = (action: SubscriberHistoryAction) => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) => subscriber({ location, action }))\n }\n\n const _notifyRollback = () => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) =>\n subscriber({ location, action: { type: 'ROLLBACK' } }),\n )\n }\n\n const tryNavigation = async ({\n task,\n navigateOpts,\n ...actionInfo\n }: TryNavigateArgs) => {\n const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false\n if (ignoreBlocker) {\n task()\n return\n }\n\n const blockers = opts.getBlockers?.() ?? []\n const isPushOrReplace =\n actionInfo.type === 'PUSH' || actionInfo.type === 'REPLACE'\n if (typeof document !== 'undefined' && blockers.length && isPushOrReplace) {\n for (const blocker of blockers) {\n const nextLocation = parseHref(actionInfo.path, actionInfo.state)\n const isBlocked = await blocker.blockerFn({\n currentLocation: location,\n nextLocation,\n action: actionInfo.type,\n })\n if (isBlocked) {\n opts.onBlocked?.(_notifyRollback)\n return\n }\n }\n }\n\n task()\n }\n\n return {\n get location() {\n return location\n },\n get length() {\n return opts.getLength()\n },\n subscribers,\n subscribe: (cb: (opts: SubscriberArgs) => void) => {\n subscribers.add(cb)\n\n return () => {\n subscribers.delete(cb)\n }\n },\n push: (path, state, navigateOpts) => {\n state = assignKey(state)\n tryNavigation({\n task: () => {\n opts.pushState(path, state)\n notify({ type: 'PUSH' })\n },\n navigateOpts,\n type: 'PUSH',\n path,\n state,\n })\n },\n replace: (path, state, navigateOpts) => {\n state = assignKey(state)\n tryNavigation({\n task: () => {\n opts.replaceState(path, state)\n notify({ type: 'REPLACE' })\n },\n navigateOpts,\n type: 'REPLACE',\n path,\n state,\n })\n },\n go: (index, navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.go(index)\n notify({ type: 'GO', index })\n },\n navigateOpts,\n type: 'GO',\n })\n },\n back: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.back(navigateOpts?.ignoreBlocker ?? false)\n notify({ type: 'BACK' })\n },\n navigateOpts,\n type: 'BACK',\n })\n },\n forward: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.forward(navigateOpts?.ignoreBlocker ?? false)\n notify({ type: 'FORWARD' })\n },\n navigateOpts,\n type: 'FORWARD',\n })\n },\n createHref: (str) => opts.createHref(str),\n block: (blocker) => {\n if (!opts.setBlockers) return () => {}\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers([...blockers, blocker])\n\n return () => {\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers?.(blockers.filter((b) => b !== blocker))\n }\n },\n flush: () => opts.flush?.(),\n destroy: () => opts.destroy?.(),\n notify,\n }\n}\n\nfunction assignKey(state: HistoryState | undefined) {\n if (!state) {\n state = {} as HistoryState\n }\n return {\n ...state,\n key: createRandomKey(),\n }\n}\n\n/**\n * Creates a history object that can be used to interact with the browser's\n * navigation. This is a lightweight API wrapping the browser's native methods.\n * It is designed to work with TanStack Router, but could be used as a standalone API as well.\n * IMPORTANT: This API implements history throttling via a microtask to prevent\n * excessive calls to the history API. In some browsers, calling history.pushState or\n * history.replaceState in quick succession can cause the browser to ignore subsequent\n * calls. This API smooths out those differences and ensures that your application\n * state will *eventually* match the browser state. In most cases, this is not a problem,\n * but if you need to ensure that the browser state is up to date, you can use the\n * `history.flush` method to immediately flush all pending state changes to the browser URL.\n * @param opts\n * @param opts.getHref A function that returns the current href (path + search + hash)\n * @param opts.createHref A function that takes a path and returns a href (path + search + hash)\n * @returns A history instance\n */\nexport function createBrowserHistory(opts?: {\n parseLocation?: () => HistoryLocation\n createHref?: (path: string) => string\n window?: any\n}): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n\n const originalPushState = win.history.pushState\n const originalReplaceState = win.history.replaceState\n\n let blockers: Array<NavigationBlocker> = []\n const _getBlockers = () => blockers\n const _setBlockers = (newBlockers: Array<NavigationBlocker>) =>\n (blockers = newBlockers)\n\n const createHref = opts?.createHref ?? ((path) => path)\n const parseLocation =\n opts?.parseLocation ??\n (() =>\n parseHref(\n `${win.location.pathname}${win.location.search}${win.location.hash}`,\n win.history.state,\n ))\n\n let currentLocation = parseLocation()\n let rollbackLocation: HistoryLocation | undefined\n\n let ignoreNextPop = false\n let skipBlockerNextPop = false\n let ignoreNextBeforeUnload = false\n\n const getLocation = () => currentLocation\n\n let next:\n | undefined\n | {\n // This is the latest location that we were attempting to push/replace\n href: string\n // This is the latest state that we were attempting to push/replace\n state: any\n // This is the latest type that we were attempting to push/replace\n isPush: boolean\n }\n\n // We need to track the current scheduled update to prevent\n // multiple updates from being scheduled at the same time.\n let scheduled: Promise<void> | undefined\n\n // This function flushes the next update to the browser history\n const flush = () => {\n if (!next) {\n return\n }\n\n // We need to ignore any updates to the subscribers while we update the browser history\n history._ignoreSubscribers = true\n\n // Update the browser history\n ;(next.isPush ? win.history.pushState : win.history.replaceState)(\n next.state,\n '',\n next.href,\n )\n\n // Stop ignoring subscriber updates\n history._ignoreSubscribers = false\n\n // Reset the nextIsPush flag and clear the scheduled update\n next = undefined\n scheduled = undefined\n rollbackLocation = undefined\n }\n\n // This function queues up a call to update the browser history\n const queueHistoryAction = (\n type: 'push' | 'replace',\n destHref: string,\n state: any,\n ) => {\n const href = createHref(destHref)\n\n if (!scheduled) {\n rollbackLocation = currentLocation\n }\n\n // Update the location in memory\n currentLocation = parseHref(destHref, state)\n\n // Keep track of the next location we need to flush to the URL\n next = {\n href,\n state,\n isPush: next?.isPush || type === 'push',\n }\n\n if (!scheduled) {\n // Schedule an update to the browser history\n scheduled = Promise.resolve().then(() => flush())\n }\n }\n\n const onPushPop = () => {\n currentLocation = parseLocation()\n history.notify({ type: 'POP' })\n }\n\n const onPushPopEvent = async () => {\n if (ignoreNextPop) {\n ignoreNextPop = false\n return\n }\n\n if (skipBlockerNextPop) {\n skipBlockerNextPop = false\n } else {\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const nextLocation = parseLocation()\n const isBlocked = await blocker.blockerFn({\n currentLocation,\n nextLocation,\n action: 'POP',\n })\n if (isBlocked) {\n ignoreNextPop = true\n win.history.go(1)\n history.notify({ type: 'POP' })\n return\n }\n }\n }\n }\n\n currentLocation = parseLocation()\n history.notify({ type: 'POP' })\n }\n\n const onBeforeUnload = (e: BeforeUnloadEvent) => {\n if (ignoreNextBeforeUnload) {\n ignoreNextBeforeUnload = false\n return\n }\n\n let shouldBlock = false\n\n // If one blocker has a non-disabled beforeUnload, we should block\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true\n if (shouldHaveBeforeUnload === true) {\n shouldBlock = true\n break\n }\n\n if (\n typeof shouldHaveBeforeUnload === 'function' &&\n shouldHaveBeforeUnload() === true\n ) {\n shouldBlock = true\n break\n }\n }\n }\n\n if (shouldBlock) {\n e.preventDefault()\n return (e.returnValue = '')\n }\n return\n }\n\n const history = createHistory({\n getLocation,\n getLength: () => win.history.length,\n pushState: (href, state) => queueHistoryAction('push', href, state),\n replaceState: (href, state) => queueHistoryAction('replace', href, state),\n back: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n return win.history.back()\n },\n forward: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n win.history.forward()\n },\n go: (n) => win.history.go(n),\n createHref: (href) => createHref(href),\n flush,\n destroy: () => {\n win.history.pushState = originalPushState\n win.history.replaceState = originalReplaceState\n win.removeEventListener(beforeUnloadEvent, onBeforeUnload, {\n capture: true,\n })\n win.removeEventListener(popStateEvent, onPushPopEvent)\n },\n onBlocked: (onUpdate) => {\n // If a navigation is blocked, we need to rollback the location\n // that we optimistically updated in memory.\n if (rollbackLocation && currentLocation !== rollbackLocation) {\n currentLocation = rollbackLocation\n // Notify subscribers\n onUpdate()\n }\n },\n getBlockers: _getBlockers,\n setBlockers: _setBlockers,\n })\n\n win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true })\n win.addEventListener(popStateEvent, onPushPopEvent)\n\n win.history.pushState = function (...args: Array<any>) {\n const res = originalPushState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop()\n return res\n }\n\n win.history.replaceState = function (...args: Array<any>) {\n const res = originalReplaceState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop()\n return res\n }\n\n return history\n}\n\nexport function createHashHistory(opts?: { window?: any }): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n return createBrowserHistory({\n window: win,\n parseLocation: () => {\n const hashHref = win.location.hash.split('#').slice(1).join('#') ?? '/'\n return parseHref(hashHref, win.history.state)\n },\n createHref: (href) =>\n `${win.location.pathname}${win.location.search}#${href}`,\n })\n}\n\nexport function createMemoryHistory(\n opts: {\n initialEntries: Array<string>\n initialIndex?: number\n } = {\n initialEntries: ['/'],\n },\n): RouterHistory {\n const entries = opts.initialEntries\n let index = opts.initialIndex ?? entries.length - 1\n const states = entries.map(() => ({}) as HistoryState)\n\n const getLocation = () => parseHref(entries[index]!, states[index])\n\n return createHistory({\n getLocation,\n getLength: () => entries.length,\n pushState: (path, state) => {\n // Removes all subsequent entries after the current index to start a new branch\n if (index < entries.length - 1) {\n entries.splice(index + 1)\n states.splice(index + 1)\n }\n states.push(state)\n entries.push(path)\n index = Math.max(entries.length - 1, 0)\n },\n replaceState: (path, state) => {\n states[index] = state\n entries[index] = path\n },\n back: () => {\n index = Math.max(index - 1, 0)\n },\n forward: () => {\n index = Math.min(index + 1, entries.length - 1)\n },\n go: (n) => {\n index = Math.min(Math.max(index + n, 0), entries.length - 1)\n },\n createHref: (path) => path,\n })\n}\n\nexport function parseHref(\n href: string,\n state: HistoryState | undefined,\n): HistoryLocation {\n const hashIndex = href.indexOf('#')\n const searchIndex = href.indexOf('?')\n\n return {\n href,\n pathname: href.substring(\n 0,\n hashIndex > 0\n ? searchIndex > 0\n ? Math.min(hashIndex, searchIndex)\n : hashIndex\n : searchIndex > 0\n ? searchIndex\n : href.length,\n ),\n hash: hashIndex > -1 ? href.substring(hashIndex) : '',\n search:\n searchIndex > -1\n ? href.slice(searchIndex, hashIndex === -1 ? undefined : hashIndex)\n : '',\n state: state || {},\n }\n}\n\n// Thanks co-pilot!\nfunction createRandomKey() {\n return (Math.random() + 1).toString(36).substring(7)\n}\n"],"names":["_a","blockers"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/index.ts"],"sourcesContent":["// While the public API was clearly inspired by the \"history\" npm package,\n// This implementation attempts to be more lightweight by\n// making assumptions about the way TanStack Router works\n\nexport interface NavigateOptions {\n ignoreBlocker?: boolean\n}\n\ntype SubscriberHistoryAction =\n | {\n type: HistoryAction\n }\n | {\n type: 'GO'\n index: number\n }\n\ntype SubscriberArgs = {\n location: HistoryLocation\n action: SubscriberHistoryAction\n}\n\nexport interface RouterHistory {\n location: HistoryLocation\n length: number\n subscribers: Set<(opts: SubscriberArgs) => void>\n subscribe: (cb: (opts: SubscriberArgs) => void) => () => void\n push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void\n replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void\n go: (index: number, navigateOpts?: NavigateOptions) => void\n back: (navigateOpts?: NavigateOptions) => void\n forward: (navigateOpts?: NavigateOptions) => void\n canGoBack: () => boolean\n createHref: (href: string) => string\n block: (blocker: NavigationBlocker) => () => void\n flush: () => void\n destroy: () => void\n notify: (action: SubscriberHistoryAction) => void\n _ignoreSubscribers?: boolean\n}\n\nexport interface HistoryLocation extends ParsedPath {\n state: HistoryState\n}\n\nexport interface ParsedPath {\n href: string\n pathname: string\n search: string\n hash: string\n}\n\nexport interface HistoryState {\n key?: string\n __TSR_index: number\n}\n\ntype ShouldAllowNavigation = any\n\nexport type HistoryAction = 'PUSH' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO'\n\nexport type BlockerFnArgs = {\n currentLocation: HistoryLocation\n nextLocation: HistoryLocation\n action: HistoryAction\n}\n\nexport type BlockerFn = (\n args: BlockerFnArgs,\n) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation\n\nexport type NavigationBlocker = {\n blockerFn: BlockerFn\n enableBeforeUnload?: (() => boolean) | boolean\n}\n\ntype TryNavigateArgs = {\n task: () => void\n type: 'PUSH' | 'REPLACE' | 'BACK' | 'FORWARD' | 'GO'\n navigateOpts?: NavigateOptions\n} & (\n | {\n type: 'PUSH' | 'REPLACE'\n path: string\n state: any\n }\n | {\n type: 'BACK' | 'FORWARD' | 'GO'\n }\n)\n\nconst stateIndexKey = '__TSR_index'\nconst popStateEvent = 'popstate'\nconst beforeUnloadEvent = 'beforeunload'\n\nexport function createHistory(opts: {\n getLocation: () => HistoryLocation\n getLength: () => number\n pushState: (path: string, state: any) => void\n replaceState: (path: string, state: any) => void\n go: (n: number) => void\n back: (ignoreBlocker: boolean) => void\n forward: (ignoreBlocker: boolean) => void\n createHref: (path: string) => string\n flush?: () => void\n destroy?: () => void\n onBlocked?: () => void\n getBlockers?: () => Array<NavigationBlocker>\n setBlockers?: (blockers: Array<NavigationBlocker>) => void\n // Avoid notifying on forward/back/go, used for browser history as we already get notified by the popstate event\n notifyOnIndexChange?: boolean\n}): RouterHistory {\n let location = opts.getLocation()\n const subscribers = new Set<(opts: SubscriberArgs) => void>()\n\n const notify = (action: SubscriberHistoryAction) => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) => subscriber({ location, action }))\n }\n\n const handleIndexChange = (action: SubscriberHistoryAction) => {\n if (opts.notifyOnIndexChange ?? true) notify(action)\n else location = opts.getLocation()\n }\n\n const tryNavigation = async ({\n task,\n navigateOpts,\n ...actionInfo\n }: TryNavigateArgs) => {\n const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false\n if (ignoreBlocker) {\n task()\n return\n }\n\n const blockers = opts.getBlockers?.() ?? []\n const isPushOrReplace =\n actionInfo.type === 'PUSH' || actionInfo.type === 'REPLACE'\n if (typeof document !== 'undefined' && blockers.length && isPushOrReplace) {\n for (const blocker of blockers) {\n const nextLocation = parseHref(actionInfo.path, actionInfo.state)\n const isBlocked = await blocker.blockerFn({\n currentLocation: location,\n nextLocation,\n action: actionInfo.type,\n })\n if (isBlocked) {\n opts.onBlocked?.()\n return\n }\n }\n }\n\n task()\n }\n\n return {\n get location() {\n return location\n },\n get length() {\n return opts.getLength()\n },\n subscribers,\n subscribe: (cb: (opts: SubscriberArgs) => void) => {\n subscribers.add(cb)\n\n return () => {\n subscribers.delete(cb)\n }\n },\n push: (path, state, navigateOpts) => {\n const currentIndex = location.state[stateIndexKey]\n state = assignKeyAndIndex(currentIndex + 1, state)\n tryNavigation({\n task: () => {\n opts.pushState(path, state)\n notify({ type: 'PUSH' })\n },\n navigateOpts,\n type: 'PUSH',\n path,\n state,\n })\n },\n replace: (path, state, navigateOpts) => {\n const currentIndex = location.state[stateIndexKey]\n state = assignKeyAndIndex(currentIndex, state)\n tryNavigation({\n task: () => {\n opts.replaceState(path, state)\n notify({ type: 'REPLACE' })\n },\n navigateOpts,\n type: 'REPLACE',\n path,\n state,\n })\n },\n go: (index, navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.go(index)\n handleIndexChange({ type: 'GO', index })\n },\n navigateOpts,\n type: 'GO',\n })\n },\n back: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.back(navigateOpts?.ignoreBlocker ?? false)\n handleIndexChange({ type: 'BACK' })\n },\n navigateOpts,\n type: 'BACK',\n })\n },\n forward: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.forward(navigateOpts?.ignoreBlocker ?? false)\n handleIndexChange({ type: 'FORWARD' })\n },\n navigateOpts,\n type: 'FORWARD',\n })\n },\n canGoBack: () => location.state[stateIndexKey] !== 0,\n createHref: (str) => opts.createHref(str),\n block: (blocker) => {\n if (!opts.setBlockers) return () => {}\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers([...blockers, blocker])\n\n return () => {\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers?.(blockers.filter((b) => b !== blocker))\n }\n },\n flush: () => opts.flush?.(),\n destroy: () => opts.destroy?.(),\n notify,\n }\n}\n\nfunction assignKeyAndIndex(index: number, state: HistoryState | undefined) {\n if (!state) {\n state = {} as HistoryState\n }\n return {\n ...state,\n key: createRandomKey(),\n [stateIndexKey]: index,\n }\n}\n\n/**\n * Creates a history object that can be used to interact with the browser's\n * navigation. This is a lightweight API wrapping the browser's native methods.\n * It is designed to work with TanStack Router, but could be used as a standalone API as well.\n * IMPORTANT: This API implements history throttling via a microtask to prevent\n * excessive calls to the history API. In some browsers, calling history.pushState or\n * history.replaceState in quick succession can cause the browser to ignore subsequent\n * calls. This API smooths out those differences and ensures that your application\n * state will *eventually* match the browser state. In most cases, this is not a problem,\n * but if you need to ensure that the browser state is up to date, you can use the\n * `history.flush` method to immediately flush all pending state changes to the browser URL.\n * @param opts\n * @param opts.getHref A function that returns the current href (path + search + hash)\n * @param opts.createHref A function that takes a path and returns a href (path + search + hash)\n * @returns A history instance\n */\nexport function createBrowserHistory(opts?: {\n parseLocation?: () => HistoryLocation\n createHref?: (path: string) => string\n window?: any\n}): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n\n const originalPushState = win.history.pushState\n const originalReplaceState = win.history.replaceState\n\n let blockers: Array<NavigationBlocker> = []\n const _getBlockers = () => blockers\n const _setBlockers = (newBlockers: Array<NavigationBlocker>) =>\n (blockers = newBlockers)\n\n const createHref = opts?.createHref ?? ((path) => path)\n const parseLocation =\n opts?.parseLocation ??\n (() =>\n parseHref(\n `${win.location.pathname}${win.location.search}${win.location.hash}`,\n win.history.state,\n ))\n\n let currentLocation = parseLocation()\n let rollbackLocation: HistoryLocation | undefined\n\n let nextPopIsGo = false\n let ignoreNextPop = false\n let skipBlockerNextPop = false\n let ignoreNextBeforeUnload = false\n\n const getLocation = () => currentLocation\n\n let next:\n | undefined\n | {\n // This is the latest location that we were attempting to push/replace\n href: string\n // This is the latest state that we were attempting to push/replace\n state: any\n // This is the latest type that we were attempting to push/replace\n isPush: boolean\n }\n\n // We need to track the current scheduled update to prevent\n // multiple updates from being scheduled at the same time.\n let scheduled: Promise<void> | undefined\n\n // This function flushes the next update to the browser history\n const flush = () => {\n if (!next) {\n return\n }\n\n // We need to ignore any updates to the subscribers while we update the browser history\n history._ignoreSubscribers = true\n\n // Update the browser history\n ;(next.isPush ? win.history.pushState : win.history.replaceState)(\n next.state,\n '',\n next.href,\n )\n\n // Stop ignoring subscriber updates\n history._ignoreSubscribers = false\n\n // Reset the nextIsPush flag and clear the scheduled update\n next = undefined\n scheduled = undefined\n rollbackLocation = undefined\n }\n\n // This function queues up a call to update the browser history\n const queueHistoryAction = (\n type: 'push' | 'replace',\n destHref: string,\n state: any,\n ) => {\n const href = createHref(destHref)\n\n if (!scheduled) {\n rollbackLocation = currentLocation\n }\n\n // Update the location in memory\n currentLocation = parseHref(destHref, state)\n\n // Keep track of the next location we need to flush to the URL\n next = {\n href,\n state,\n isPush: next?.isPush || type === 'push',\n }\n\n if (!scheduled) {\n // Schedule an update to the browser history\n scheduled = Promise.resolve().then(() => flush())\n }\n }\n\n // NOTE: this function can probably be removed\n const onPushPop = (type: 'PUSH' | 'REPLACE') => {\n currentLocation = parseLocation()\n history.notify({ type })\n }\n\n const onPushPopEvent = async () => {\n if (ignoreNextPop) {\n ignoreNextPop = false\n return\n }\n\n const nextLocation = parseLocation()\n const delta =\n nextLocation.state[stateIndexKey] - currentLocation.state[stateIndexKey]\n const isForward = delta === 1\n const isBack = delta === -1\n const isGo = (!isForward && !isBack) || nextPopIsGo\n nextPopIsGo = false\n\n const action = isGo ? 'GO' : isBack ? 'BACK' : 'FORWARD'\n const notify: SubscriberHistoryAction = isGo\n ? {\n type: 'GO',\n index: delta,\n }\n : {\n type: isBack ? 'BACK' : 'FORWARD',\n }\n\n if (skipBlockerNextPop) {\n skipBlockerNextPop = false\n } else {\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const isBlocked = await blocker.blockerFn({\n currentLocation,\n nextLocation,\n action,\n })\n if (isBlocked) {\n ignoreNextPop = true\n win.history.go(1)\n history.notify(notify)\n return\n }\n }\n }\n }\n\n currentLocation = parseLocation()\n history.notify(notify)\n }\n\n const onBeforeUnload = (e: BeforeUnloadEvent) => {\n if (ignoreNextBeforeUnload) {\n ignoreNextBeforeUnload = false\n return\n }\n\n let shouldBlock = false\n\n // If one blocker has a non-disabled beforeUnload, we should block\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true\n if (shouldHaveBeforeUnload === true) {\n shouldBlock = true\n break\n }\n\n if (\n typeof shouldHaveBeforeUnload === 'function' &&\n shouldHaveBeforeUnload() === true\n ) {\n shouldBlock = true\n break\n }\n }\n }\n\n if (shouldBlock) {\n e.preventDefault()\n return (e.returnValue = '')\n }\n return\n }\n\n const history = createHistory({\n getLocation,\n getLength: () => win.history.length,\n pushState: (href, state) => queueHistoryAction('push', href, state),\n replaceState: (href, state) => queueHistoryAction('replace', href, state),\n back: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n return win.history.back()\n },\n forward: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n win.history.forward()\n },\n go: (n) => {\n nextPopIsGo = true\n win.history.go(n)\n },\n createHref: (href) => createHref(href),\n flush,\n destroy: () => {\n win.history.pushState = originalPushState\n win.history.replaceState = originalReplaceState\n win.removeEventListener(beforeUnloadEvent, onBeforeUnload, {\n capture: true,\n })\n win.removeEventListener(popStateEvent, onPushPopEvent)\n },\n onBlocked: () => {\n // If a navigation is blocked, we need to rollback the location\n // that we optimistically updated in memory.\n if (rollbackLocation && currentLocation !== rollbackLocation) {\n currentLocation = rollbackLocation\n }\n },\n getBlockers: _getBlockers,\n setBlockers: _setBlockers,\n notifyOnIndexChange: false,\n })\n\n win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true })\n win.addEventListener(popStateEvent, onPushPopEvent)\n\n win.history.pushState = function (...args: Array<any>) {\n const res = originalPushState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop('PUSH')\n return res\n }\n\n win.history.replaceState = function (...args: Array<any>) {\n const res = originalReplaceState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop('REPLACE')\n return res\n }\n\n return history\n}\n\nexport function createHashHistory(opts?: { window?: any }): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n return createBrowserHistory({\n window: win,\n parseLocation: () => {\n const hashHref = win.location.hash.split('#').slice(1).join('#') ?? '/'\n return parseHref(hashHref, win.history.state)\n },\n createHref: (href) =>\n `${win.location.pathname}${win.location.search}#${href}`,\n })\n}\n\nexport function createMemoryHistory(\n opts: {\n initialEntries: Array<string>\n initialIndex?: number\n } = {\n initialEntries: ['/'],\n },\n): RouterHistory {\n const entries = opts.initialEntries\n let index = opts.initialIndex\n ? Math.min(Math.max(opts.initialIndex, 0), entries.length - 1)\n : entries.length - 1\n const states = entries.map<HistoryState>((_entry, index) =>\n assignKeyAndIndex(index, undefined),\n )\n\n const getLocation = () => parseHref(entries[index]!, states[index])\n\n return createHistory({\n getLocation,\n getLength: () => entries.length,\n pushState: (path, state) => {\n // Removes all subsequent entries after the current index to start a new branch\n if (index < entries.length - 1) {\n entries.splice(index + 1)\n states.splice(index + 1)\n }\n states.push(state)\n entries.push(path)\n index = Math.max(entries.length - 1, 0)\n },\n replaceState: (path, state) => {\n states[index] = state\n entries[index] = path\n },\n back: () => {\n index = Math.max(index - 1, 0)\n },\n forward: () => {\n index = Math.min(index + 1, entries.length - 1)\n },\n go: (n) => {\n index = Math.min(Math.max(index + n, 0), entries.length - 1)\n },\n createHref: (path) => path,\n })\n}\n\nexport function parseHref(\n href: string,\n state: HistoryState | undefined,\n): HistoryLocation {\n const hashIndex = href.indexOf('#')\n const searchIndex = href.indexOf('?')\n\n return {\n href,\n pathname: href.substring(\n 0,\n hashIndex > 0\n ? searchIndex > 0\n ? Math.min(hashIndex, searchIndex)\n : hashIndex\n : searchIndex > 0\n ? searchIndex\n : href.length,\n ),\n hash: hashIndex > -1 ? href.substring(hashIndex) : '',\n search:\n searchIndex > -1\n ? href.slice(searchIndex, hashIndex === -1 ? undefined : hashIndex)\n : '',\n state: state || { [stateIndexKey]: 0 },\n }\n}\n\n// Thanks co-pilot!\nfunction createRandomKey() {\n return (Math.random() + 1).toString(36).substring(7)\n}\n"],"names":["_a","blockers","index"],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -8,7 +8,7 @@ export interface NavigateOptions {
|
|
|
8
8
|
|
|
9
9
|
type SubscriberHistoryAction =
|
|
10
10
|
| {
|
|
11
|
-
type: HistoryAction
|
|
11
|
+
type: HistoryAction
|
|
12
12
|
}
|
|
13
13
|
| {
|
|
14
14
|
type: 'GO'
|
|
@@ -30,6 +30,7 @@ export interface RouterHistory {
|
|
|
30
30
|
go: (index: number, navigateOpts?: NavigateOptions) => void
|
|
31
31
|
back: (navigateOpts?: NavigateOptions) => void
|
|
32
32
|
forward: (navigateOpts?: NavigateOptions) => void
|
|
33
|
+
canGoBack: () => boolean
|
|
33
34
|
createHref: (href: string) => string
|
|
34
35
|
block: (blocker: NavigationBlocker) => () => void
|
|
35
36
|
flush: () => void
|
|
@@ -51,17 +52,12 @@ export interface ParsedPath {
|
|
|
51
52
|
|
|
52
53
|
export interface HistoryState {
|
|
53
54
|
key?: string
|
|
55
|
+
__TSR_index: number
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
type ShouldAllowNavigation = any
|
|
57
59
|
|
|
58
|
-
export type HistoryAction =
|
|
59
|
-
| 'PUSH'
|
|
60
|
-
| 'POP'
|
|
61
|
-
| 'REPLACE'
|
|
62
|
-
| 'FORWARD'
|
|
63
|
-
| 'BACK'
|
|
64
|
-
| 'GO'
|
|
60
|
+
export type HistoryAction = 'PUSH' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO'
|
|
65
61
|
|
|
66
62
|
export type BlockerFnArgs = {
|
|
67
63
|
currentLocation: HistoryLocation
|
|
@@ -93,6 +89,7 @@ type TryNavigateArgs = {
|
|
|
93
89
|
}
|
|
94
90
|
)
|
|
95
91
|
|
|
92
|
+
const stateIndexKey = '__TSR_index'
|
|
96
93
|
const popStateEvent = 'popstate'
|
|
97
94
|
const beforeUnloadEvent = 'beforeunload'
|
|
98
95
|
|
|
@@ -107,9 +104,11 @@ export function createHistory(opts: {
|
|
|
107
104
|
createHref: (path: string) => string
|
|
108
105
|
flush?: () => void
|
|
109
106
|
destroy?: () => void
|
|
110
|
-
onBlocked?: (
|
|
107
|
+
onBlocked?: () => void
|
|
111
108
|
getBlockers?: () => Array<NavigationBlocker>
|
|
112
109
|
setBlockers?: (blockers: Array<NavigationBlocker>) => void
|
|
110
|
+
// Avoid notifying on forward/back/go, used for browser history as we already get notified by the popstate event
|
|
111
|
+
notifyOnIndexChange?: boolean
|
|
113
112
|
}): RouterHistory {
|
|
114
113
|
let location = opts.getLocation()
|
|
115
114
|
const subscribers = new Set<(opts: SubscriberArgs) => void>()
|
|
@@ -119,11 +118,9 @@ export function createHistory(opts: {
|
|
|
119
118
|
subscribers.forEach((subscriber) => subscriber({ location, action }))
|
|
120
119
|
}
|
|
121
120
|
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
subscriber({ location, action: { type: 'ROLLBACK' } }),
|
|
126
|
-
)
|
|
121
|
+
const handleIndexChange = (action: SubscriberHistoryAction) => {
|
|
122
|
+
if (opts.notifyOnIndexChange ?? true) notify(action)
|
|
123
|
+
else location = opts.getLocation()
|
|
127
124
|
}
|
|
128
125
|
|
|
129
126
|
const tryNavigation = async ({
|
|
@@ -149,7 +146,7 @@ export function createHistory(opts: {
|
|
|
149
146
|
action: actionInfo.type,
|
|
150
147
|
})
|
|
151
148
|
if (isBlocked) {
|
|
152
|
-
opts.onBlocked?.(
|
|
149
|
+
opts.onBlocked?.()
|
|
153
150
|
return
|
|
154
151
|
}
|
|
155
152
|
}
|
|
@@ -174,7 +171,8 @@ export function createHistory(opts: {
|
|
|
174
171
|
}
|
|
175
172
|
},
|
|
176
173
|
push: (path, state, navigateOpts) => {
|
|
177
|
-
|
|
174
|
+
const currentIndex = location.state[stateIndexKey]
|
|
175
|
+
state = assignKeyAndIndex(currentIndex + 1, state)
|
|
178
176
|
tryNavigation({
|
|
179
177
|
task: () => {
|
|
180
178
|
opts.pushState(path, state)
|
|
@@ -187,7 +185,8 @@ export function createHistory(opts: {
|
|
|
187
185
|
})
|
|
188
186
|
},
|
|
189
187
|
replace: (path, state, navigateOpts) => {
|
|
190
|
-
|
|
188
|
+
const currentIndex = location.state[stateIndexKey]
|
|
189
|
+
state = assignKeyAndIndex(currentIndex, state)
|
|
191
190
|
tryNavigation({
|
|
192
191
|
task: () => {
|
|
193
192
|
opts.replaceState(path, state)
|
|
@@ -203,7 +202,7 @@ export function createHistory(opts: {
|
|
|
203
202
|
tryNavigation({
|
|
204
203
|
task: () => {
|
|
205
204
|
opts.go(index)
|
|
206
|
-
|
|
205
|
+
handleIndexChange({ type: 'GO', index })
|
|
207
206
|
},
|
|
208
207
|
navigateOpts,
|
|
209
208
|
type: 'GO',
|
|
@@ -213,7 +212,7 @@ export function createHistory(opts: {
|
|
|
213
212
|
tryNavigation({
|
|
214
213
|
task: () => {
|
|
215
214
|
opts.back(navigateOpts?.ignoreBlocker ?? false)
|
|
216
|
-
|
|
215
|
+
handleIndexChange({ type: 'BACK' })
|
|
217
216
|
},
|
|
218
217
|
navigateOpts,
|
|
219
218
|
type: 'BACK',
|
|
@@ -223,12 +222,13 @@ export function createHistory(opts: {
|
|
|
223
222
|
tryNavigation({
|
|
224
223
|
task: () => {
|
|
225
224
|
opts.forward(navigateOpts?.ignoreBlocker ?? false)
|
|
226
|
-
|
|
225
|
+
handleIndexChange({ type: 'FORWARD' })
|
|
227
226
|
},
|
|
228
227
|
navigateOpts,
|
|
229
228
|
type: 'FORWARD',
|
|
230
229
|
})
|
|
231
230
|
},
|
|
231
|
+
canGoBack: () => location.state[stateIndexKey] !== 0,
|
|
232
232
|
createHref: (str) => opts.createHref(str),
|
|
233
233
|
block: (blocker) => {
|
|
234
234
|
if (!opts.setBlockers) return () => {}
|
|
@@ -246,13 +246,14 @@ export function createHistory(opts: {
|
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
-
function
|
|
249
|
+
function assignKeyAndIndex(index: number, state: HistoryState | undefined) {
|
|
250
250
|
if (!state) {
|
|
251
251
|
state = {} as HistoryState
|
|
252
252
|
}
|
|
253
253
|
return {
|
|
254
254
|
...state,
|
|
255
255
|
key: createRandomKey(),
|
|
256
|
+
[stateIndexKey]: index,
|
|
256
257
|
}
|
|
257
258
|
}
|
|
258
259
|
|
|
@@ -301,6 +302,7 @@ export function createBrowserHistory(opts?: {
|
|
|
301
302
|
let currentLocation = parseLocation()
|
|
302
303
|
let rollbackLocation: HistoryLocation | undefined
|
|
303
304
|
|
|
305
|
+
let nextPopIsGo = false
|
|
304
306
|
let ignoreNextPop = false
|
|
305
307
|
let skipBlockerNextPop = false
|
|
306
308
|
let ignoreNextBeforeUnload = false
|
|
@@ -375,9 +377,10 @@ export function createBrowserHistory(opts?: {
|
|
|
375
377
|
}
|
|
376
378
|
}
|
|
377
379
|
|
|
378
|
-
|
|
380
|
+
// NOTE: this function can probably be removed
|
|
381
|
+
const onPushPop = (type: 'PUSH' | 'REPLACE') => {
|
|
379
382
|
currentLocation = parseLocation()
|
|
380
|
-
history.notify({ type
|
|
383
|
+
history.notify({ type })
|
|
381
384
|
}
|
|
382
385
|
|
|
383
386
|
const onPushPopEvent = async () => {
|
|
@@ -386,22 +389,39 @@ export function createBrowserHistory(opts?: {
|
|
|
386
389
|
return
|
|
387
390
|
}
|
|
388
391
|
|
|
392
|
+
const nextLocation = parseLocation()
|
|
393
|
+
const delta =
|
|
394
|
+
nextLocation.state[stateIndexKey] - currentLocation.state[stateIndexKey]
|
|
395
|
+
const isForward = delta === 1
|
|
396
|
+
const isBack = delta === -1
|
|
397
|
+
const isGo = (!isForward && !isBack) || nextPopIsGo
|
|
398
|
+
nextPopIsGo = false
|
|
399
|
+
|
|
400
|
+
const action = isGo ? 'GO' : isBack ? 'BACK' : 'FORWARD'
|
|
401
|
+
const notify: SubscriberHistoryAction = isGo
|
|
402
|
+
? {
|
|
403
|
+
type: 'GO',
|
|
404
|
+
index: delta,
|
|
405
|
+
}
|
|
406
|
+
: {
|
|
407
|
+
type: isBack ? 'BACK' : 'FORWARD',
|
|
408
|
+
}
|
|
409
|
+
|
|
389
410
|
if (skipBlockerNextPop) {
|
|
390
411
|
skipBlockerNextPop = false
|
|
391
412
|
} else {
|
|
392
413
|
const blockers = _getBlockers()
|
|
393
414
|
if (typeof document !== 'undefined' && blockers.length) {
|
|
394
415
|
for (const blocker of blockers) {
|
|
395
|
-
const nextLocation = parseLocation()
|
|
396
416
|
const isBlocked = await blocker.blockerFn({
|
|
397
417
|
currentLocation,
|
|
398
418
|
nextLocation,
|
|
399
|
-
action
|
|
419
|
+
action,
|
|
400
420
|
})
|
|
401
421
|
if (isBlocked) {
|
|
402
422
|
ignoreNextPop = true
|
|
403
423
|
win.history.go(1)
|
|
404
|
-
history.notify(
|
|
424
|
+
history.notify(notify)
|
|
405
425
|
return
|
|
406
426
|
}
|
|
407
427
|
}
|
|
@@ -409,7 +429,7 @@ export function createBrowserHistory(opts?: {
|
|
|
409
429
|
}
|
|
410
430
|
|
|
411
431
|
currentLocation = parseLocation()
|
|
412
|
-
history.notify(
|
|
432
|
+
history.notify(notify)
|
|
413
433
|
}
|
|
414
434
|
|
|
415
435
|
const onBeforeUnload = (e: BeforeUnloadEvent) => {
|
|
@@ -462,7 +482,10 @@ export function createBrowserHistory(opts?: {
|
|
|
462
482
|
ignoreNextBeforeUnload = true
|
|
463
483
|
win.history.forward()
|
|
464
484
|
},
|
|
465
|
-
go: (n) =>
|
|
485
|
+
go: (n) => {
|
|
486
|
+
nextPopIsGo = true
|
|
487
|
+
win.history.go(n)
|
|
488
|
+
},
|
|
466
489
|
createHref: (href) => createHref(href),
|
|
467
490
|
flush,
|
|
468
491
|
destroy: () => {
|
|
@@ -473,17 +496,16 @@ export function createBrowserHistory(opts?: {
|
|
|
473
496
|
})
|
|
474
497
|
win.removeEventListener(popStateEvent, onPushPopEvent)
|
|
475
498
|
},
|
|
476
|
-
onBlocked: (
|
|
499
|
+
onBlocked: () => {
|
|
477
500
|
// If a navigation is blocked, we need to rollback the location
|
|
478
501
|
// that we optimistically updated in memory.
|
|
479
502
|
if (rollbackLocation && currentLocation !== rollbackLocation) {
|
|
480
503
|
currentLocation = rollbackLocation
|
|
481
|
-
// Notify subscribers
|
|
482
|
-
onUpdate()
|
|
483
504
|
}
|
|
484
505
|
},
|
|
485
506
|
getBlockers: _getBlockers,
|
|
486
507
|
setBlockers: _setBlockers,
|
|
508
|
+
notifyOnIndexChange: false,
|
|
487
509
|
})
|
|
488
510
|
|
|
489
511
|
win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true })
|
|
@@ -491,13 +513,13 @@ export function createBrowserHistory(opts?: {
|
|
|
491
513
|
|
|
492
514
|
win.history.pushState = function (...args: Array<any>) {
|
|
493
515
|
const res = originalPushState.apply(win.history, args as any)
|
|
494
|
-
if (!history._ignoreSubscribers) onPushPop()
|
|
516
|
+
if (!history._ignoreSubscribers) onPushPop('PUSH')
|
|
495
517
|
return res
|
|
496
518
|
}
|
|
497
519
|
|
|
498
520
|
win.history.replaceState = function (...args: Array<any>) {
|
|
499
521
|
const res = originalReplaceState.apply(win.history, args as any)
|
|
500
|
-
if (!history._ignoreSubscribers) onPushPop()
|
|
522
|
+
if (!history._ignoreSubscribers) onPushPop('REPLACE')
|
|
501
523
|
return res
|
|
502
524
|
}
|
|
503
525
|
|
|
@@ -528,8 +550,12 @@ export function createMemoryHistory(
|
|
|
528
550
|
},
|
|
529
551
|
): RouterHistory {
|
|
530
552
|
const entries = opts.initialEntries
|
|
531
|
-
let index = opts.initialIndex
|
|
532
|
-
|
|
553
|
+
let index = opts.initialIndex
|
|
554
|
+
? Math.min(Math.max(opts.initialIndex, 0), entries.length - 1)
|
|
555
|
+
: entries.length - 1
|
|
556
|
+
const states = entries.map<HistoryState>((_entry, index) =>
|
|
557
|
+
assignKeyAndIndex(index, undefined),
|
|
558
|
+
)
|
|
533
559
|
|
|
534
560
|
const getLocation = () => parseHref(entries[index]!, states[index])
|
|
535
561
|
|
|
@@ -587,7 +613,7 @@ export function parseHref(
|
|
|
587
613
|
searchIndex > -1
|
|
588
614
|
? href.slice(searchIndex, hashIndex === -1 ? undefined : hashIndex)
|
|
589
615
|
: '',
|
|
590
|
-
state: state || {},
|
|
616
|
+
state: state || { [stateIndexKey]: 0 },
|
|
591
617
|
}
|
|
592
618
|
}
|
|
593
619
|
|