@remix-run/router 1.3.0-pre.0 → 1.3.0-pre.2
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/CHANGELOG.md +12 -0
- package/dist/history.d.ts +4 -0
- package/dist/router.cjs.js +259 -33
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +49 -2
- package/dist/router.js +259 -34
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +259 -33
- package/dist/router.umd.js.map +1 -1
- package/dist/router.umd.min.js +2 -2
- package/dist/router.umd.min.js.map +1 -1
- package/history.ts +56 -12
- package/package.json +1 -1
- package/router.ts +271 -14
- package/utils.ts +1 -1
package/dist/router.d.ts
CHANGED
|
@@ -64,13 +64,13 @@ export interface Router {
|
|
|
64
64
|
* Navigate forward/backward in the history stack
|
|
65
65
|
* @param to Delta to move in the history stack
|
|
66
66
|
*/
|
|
67
|
-
navigate(to: number): void
|
|
67
|
+
navigate(to: number): Promise<void>;
|
|
68
68
|
/**
|
|
69
69
|
* Navigate to the given path
|
|
70
70
|
* @param to Path to navigate to
|
|
71
71
|
* @param opts Navigation options (method, submission, etc.)
|
|
72
72
|
*/
|
|
73
|
-
navigate(to: To, opts?: RouterNavigateOptions): void
|
|
73
|
+
navigate(to: To, opts?: RouterNavigateOptions): Promise<void>;
|
|
74
74
|
/**
|
|
75
75
|
* @internal
|
|
76
76
|
* PRIVATE - DO NOT USE
|
|
@@ -130,6 +130,23 @@ export interface Router {
|
|
|
130
130
|
* Cleanup listeners and abort any in-progress loads
|
|
131
131
|
*/
|
|
132
132
|
dispose(): void;
|
|
133
|
+
/**
|
|
134
|
+
* @internal
|
|
135
|
+
* PRIVATE - DO NOT USE
|
|
136
|
+
*
|
|
137
|
+
* Get a navigation blocker
|
|
138
|
+
* @param key The identifier for the blocker
|
|
139
|
+
* @param fn The blocker function implementation
|
|
140
|
+
*/
|
|
141
|
+
getBlocker(key: string, fn: BlockerFunction): Blocker;
|
|
142
|
+
/**
|
|
143
|
+
* @internal
|
|
144
|
+
* PRIVATE - DO NOT USE
|
|
145
|
+
*
|
|
146
|
+
* Delete a navigation blocker
|
|
147
|
+
* @param key The identifier for the blocker
|
|
148
|
+
*/
|
|
149
|
+
deleteBlocker(key: string): void;
|
|
133
150
|
/**
|
|
134
151
|
* @internal
|
|
135
152
|
* PRIVATE - DO NOT USE
|
|
@@ -202,6 +219,10 @@ export interface RouterState {
|
|
|
202
219
|
* Map of current fetchers
|
|
203
220
|
*/
|
|
204
221
|
fetchers: Map<string, Fetcher>;
|
|
222
|
+
/**
|
|
223
|
+
* Map of current blockers
|
|
224
|
+
*/
|
|
225
|
+
blockers: Map<string, Blocker>;
|
|
205
226
|
}
|
|
206
227
|
/**
|
|
207
228
|
* Data that can be passed into hydrate a Router from SSR
|
|
@@ -285,6 +306,7 @@ declare type LinkNavigateOptions = {
|
|
|
285
306
|
declare type SubmissionNavigateOptions = {
|
|
286
307
|
replace?: boolean;
|
|
287
308
|
state?: any;
|
|
309
|
+
preventScrollReset?: boolean;
|
|
288
310
|
formMethod?: FormMethod;
|
|
289
311
|
formEncType?: FormEncType;
|
|
290
312
|
formData: FormData;
|
|
@@ -361,8 +383,33 @@ declare type FetcherStates<TData = any> = {
|
|
|
361
383
|
};
|
|
362
384
|
};
|
|
363
385
|
export declare type Fetcher<TData = any> = FetcherStates<TData>[keyof FetcherStates<TData>];
|
|
386
|
+
interface BlockerBlocked {
|
|
387
|
+
state: "blocked";
|
|
388
|
+
reset(): void;
|
|
389
|
+
proceed(): void;
|
|
390
|
+
location: Location;
|
|
391
|
+
}
|
|
392
|
+
interface BlockerUnblocked {
|
|
393
|
+
state: "unblocked";
|
|
394
|
+
reset: undefined;
|
|
395
|
+
proceed: undefined;
|
|
396
|
+
location: undefined;
|
|
397
|
+
}
|
|
398
|
+
interface BlockerProceeding {
|
|
399
|
+
state: "proceeding";
|
|
400
|
+
reset: undefined;
|
|
401
|
+
proceed: undefined;
|
|
402
|
+
location: Location;
|
|
403
|
+
}
|
|
404
|
+
export declare type Blocker = BlockerUnblocked | BlockerBlocked | BlockerProceeding;
|
|
405
|
+
export declare type BlockerFunction = (args: {
|
|
406
|
+
currentLocation: Location;
|
|
407
|
+
nextLocation: Location;
|
|
408
|
+
historyAction: HistoryAction;
|
|
409
|
+
}) => boolean;
|
|
364
410
|
export declare const IDLE_NAVIGATION: NavigationStates["Idle"];
|
|
365
411
|
export declare const IDLE_FETCHER: FetcherStates["Idle"];
|
|
412
|
+
export declare const IDLE_BLOCKER: BlockerUnblocked;
|
|
366
413
|
/**
|
|
367
414
|
* Create a router and listen to history POP navigations
|
|
368
415
|
*/
|
package/dist/router.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @remix-run/router v1.3.0-pre.
|
|
2
|
+
* @remix-run/router v1.3.0-pre.2
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -140,7 +140,8 @@ function createMemoryHistory(options) {
|
|
|
140
140
|
if (v5Compat && listener) {
|
|
141
141
|
listener({
|
|
142
142
|
action,
|
|
143
|
-
location: nextLocation
|
|
143
|
+
location: nextLocation,
|
|
144
|
+
delta: 1
|
|
144
145
|
});
|
|
145
146
|
}
|
|
146
147
|
},
|
|
@@ -153,19 +154,23 @@ function createMemoryHistory(options) {
|
|
|
153
154
|
if (v5Compat && listener) {
|
|
154
155
|
listener({
|
|
155
156
|
action,
|
|
156
|
-
location: nextLocation
|
|
157
|
+
location: nextLocation,
|
|
158
|
+
delta: 0
|
|
157
159
|
});
|
|
158
160
|
}
|
|
159
161
|
},
|
|
160
162
|
|
|
161
163
|
go(delta) {
|
|
162
164
|
action = Action.Pop;
|
|
163
|
-
|
|
165
|
+
let nextIndex = clampIndex(index + delta);
|
|
166
|
+
let nextLocation = entries[nextIndex];
|
|
167
|
+
index = nextIndex;
|
|
164
168
|
|
|
165
169
|
if (listener) {
|
|
166
170
|
listener({
|
|
167
171
|
action,
|
|
168
|
-
location:
|
|
172
|
+
location: nextLocation,
|
|
173
|
+
delta
|
|
169
174
|
});
|
|
170
175
|
}
|
|
171
176
|
},
|
|
@@ -290,10 +295,11 @@ function createKey() {
|
|
|
290
295
|
*/
|
|
291
296
|
|
|
292
297
|
|
|
293
|
-
function getHistoryState(location) {
|
|
298
|
+
function getHistoryState(location, index) {
|
|
294
299
|
return {
|
|
295
300
|
usr: location.state,
|
|
296
|
-
key: location.key
|
|
301
|
+
key: location.key,
|
|
302
|
+
idx: index
|
|
297
303
|
};
|
|
298
304
|
}
|
|
299
305
|
/**
|
|
@@ -377,15 +383,45 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
|
|
|
377
383
|
let globalHistory = window.history;
|
|
378
384
|
let action = Action.Pop;
|
|
379
385
|
let listener = null;
|
|
386
|
+
let index = getIndex(); // Index should only be null when we initialize. If not, it's because the
|
|
387
|
+
// user called history.pushState or history.replaceState directly, in which
|
|
388
|
+
// case we should log a warning as it will result in bugs.
|
|
389
|
+
|
|
390
|
+
if (index == null) {
|
|
391
|
+
index = 0;
|
|
392
|
+
globalHistory.replaceState(_extends({}, globalHistory.state, {
|
|
393
|
+
idx: index
|
|
394
|
+
}), "");
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function getIndex() {
|
|
398
|
+
let state = globalHistory.state || {
|
|
399
|
+
idx: null
|
|
400
|
+
};
|
|
401
|
+
return state.idx;
|
|
402
|
+
}
|
|
380
403
|
|
|
381
404
|
function handlePop() {
|
|
382
|
-
|
|
405
|
+
let nextAction = Action.Pop;
|
|
406
|
+
let nextIndex = getIndex();
|
|
383
407
|
|
|
384
|
-
if (
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
408
|
+
if (nextIndex != null) {
|
|
409
|
+
let delta = nextIndex - index;
|
|
410
|
+
action = nextAction;
|
|
411
|
+
index = nextIndex;
|
|
412
|
+
|
|
413
|
+
if (listener) {
|
|
414
|
+
listener({
|
|
415
|
+
action,
|
|
416
|
+
location: history.location,
|
|
417
|
+
delta
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
} else {
|
|
421
|
+
warning$1(false, // TODO: Write up a doc that explains our blocking strategy in detail
|
|
422
|
+
// and link to it here so people can understand better what is going on
|
|
423
|
+
// and how to avoid it.
|
|
424
|
+
"You are trying to block a POP navigation to a location that was not " + "created by @remix-run/router. The block will fail silently in " + "production, but in general you should do all navigation with the " + "router (instead of using window.history.pushState directly) " + "to avoid this situation.");
|
|
389
425
|
}
|
|
390
426
|
}
|
|
391
427
|
|
|
@@ -393,7 +429,8 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
|
|
|
393
429
|
action = Action.Push;
|
|
394
430
|
let location = createLocation(history.location, to, state);
|
|
395
431
|
if (validateLocation) validateLocation(location, to);
|
|
396
|
-
|
|
432
|
+
index = getIndex() + 1;
|
|
433
|
+
let historyState = getHistoryState(location, index);
|
|
397
434
|
let url = history.createHref(location); // try...catch because iOS limits us to 100 pushState calls :/
|
|
398
435
|
|
|
399
436
|
try {
|
|
@@ -407,7 +444,8 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
|
|
|
407
444
|
if (v5Compat && listener) {
|
|
408
445
|
listener({
|
|
409
446
|
action,
|
|
410
|
-
location: history.location
|
|
447
|
+
location: history.location,
|
|
448
|
+
delta: 1
|
|
411
449
|
});
|
|
412
450
|
}
|
|
413
451
|
}
|
|
@@ -416,14 +454,16 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
|
|
|
416
454
|
action = Action.Replace;
|
|
417
455
|
let location = createLocation(history.location, to, state);
|
|
418
456
|
if (validateLocation) validateLocation(location, to);
|
|
419
|
-
|
|
457
|
+
index = getIndex();
|
|
458
|
+
let historyState = getHistoryState(location, index);
|
|
420
459
|
let url = history.createHref(location);
|
|
421
460
|
globalHistory.replaceState(historyState, "", url);
|
|
422
461
|
|
|
423
462
|
if (v5Compat && listener) {
|
|
424
463
|
listener({
|
|
425
464
|
action,
|
|
426
|
-
location: history.location
|
|
465
|
+
location: history.location,
|
|
466
|
+
delta: 0
|
|
427
467
|
});
|
|
428
468
|
}
|
|
429
469
|
}
|
|
@@ -949,7 +989,7 @@ function warning(cond, message) {
|
|
|
949
989
|
if (typeof console !== "undefined") console.warn(message);
|
|
950
990
|
|
|
951
991
|
try {
|
|
952
|
-
// Welcome to debugging
|
|
992
|
+
// Welcome to debugging @remix-run/router!
|
|
953
993
|
//
|
|
954
994
|
// This error is thrown as a convenience so you can more easily
|
|
955
995
|
// find the source for a warning that appears in the console by
|
|
@@ -1385,6 +1425,12 @@ const IDLE_FETCHER = {
|
|
|
1385
1425
|
formEncType: undefined,
|
|
1386
1426
|
formData: undefined
|
|
1387
1427
|
};
|
|
1428
|
+
const IDLE_BLOCKER = {
|
|
1429
|
+
state: "unblocked",
|
|
1430
|
+
proceed: undefined,
|
|
1431
|
+
reset: undefined,
|
|
1432
|
+
location: undefined
|
|
1433
|
+
};
|
|
1388
1434
|
const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
|
|
1389
1435
|
const isServer = !isBrowser; //#endregion
|
|
1390
1436
|
////////////////////////////////////////////////////////////////////////////////
|
|
@@ -1449,7 +1495,8 @@ function createRouter(init) {
|
|
|
1449
1495
|
loaderData: init.hydrationData && init.hydrationData.loaderData || {},
|
|
1450
1496
|
actionData: init.hydrationData && init.hydrationData.actionData || null,
|
|
1451
1497
|
errors: init.hydrationData && init.hydrationData.errors || initialErrors,
|
|
1452
|
-
fetchers: new Map()
|
|
1498
|
+
fetchers: new Map(),
|
|
1499
|
+
blockers: new Map()
|
|
1453
1500
|
}; // -- Stateful internal variables to manage navigations --
|
|
1454
1501
|
// Current navigation in progress (to be committed in completeNavigation)
|
|
1455
1502
|
|
|
@@ -1491,7 +1538,16 @@ function createRouter(init) {
|
|
|
1491
1538
|
// promise resolves we update loaderData. If a new navigation starts we
|
|
1492
1539
|
// cancel active deferreds for eliminated routes.
|
|
1493
1540
|
|
|
1494
|
-
let activeDeferreds = new Map(); //
|
|
1541
|
+
let activeDeferreds = new Map(); // We ony support a single active blocker at the moment since we don't have
|
|
1542
|
+
// any compelling use cases for multi-blocker yet
|
|
1543
|
+
|
|
1544
|
+
let activeBlocker = null; // Store blocker functions in a separate Map outside of router state since
|
|
1545
|
+
// we don't need to update UI state if they change
|
|
1546
|
+
|
|
1547
|
+
let blockerFunctions = new Map(); // Flag to ignore the next history update, so we can revert the URL change on
|
|
1548
|
+
// a POP navigation that was blocked by the user without touching router state
|
|
1549
|
+
|
|
1550
|
+
let ignoreNextHistoryUpdate = false; // Initialize the router, all side effects should be kicked off from here.
|
|
1495
1551
|
// Implemented as a Fluent API for ease of:
|
|
1496
1552
|
// let router = createRouter(init).initialize();
|
|
1497
1553
|
|
|
@@ -1501,8 +1557,54 @@ function createRouter(init) {
|
|
|
1501
1557
|
unlistenHistory = init.history.listen(_ref => {
|
|
1502
1558
|
let {
|
|
1503
1559
|
action: historyAction,
|
|
1504
|
-
location
|
|
1560
|
+
location,
|
|
1561
|
+
delta
|
|
1505
1562
|
} = _ref;
|
|
1563
|
+
|
|
1564
|
+
// Ignore this event if it was just us resetting the URL from a
|
|
1565
|
+
// blocked POP navigation
|
|
1566
|
+
if (ignoreNextHistoryUpdate) {
|
|
1567
|
+
ignoreNextHistoryUpdate = false;
|
|
1568
|
+
return;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
let blockerKey = shouldBlockNavigation({
|
|
1572
|
+
currentLocation: state.location,
|
|
1573
|
+
nextLocation: location,
|
|
1574
|
+
historyAction
|
|
1575
|
+
});
|
|
1576
|
+
|
|
1577
|
+
if (blockerKey) {
|
|
1578
|
+
// Restore the URL to match the current UI, but don't update router state
|
|
1579
|
+
ignoreNextHistoryUpdate = true;
|
|
1580
|
+
init.history.go(delta * -1); // Put the blocker into a blocked state
|
|
1581
|
+
|
|
1582
|
+
updateBlocker(blockerKey, {
|
|
1583
|
+
state: "blocked",
|
|
1584
|
+
location,
|
|
1585
|
+
|
|
1586
|
+
proceed() {
|
|
1587
|
+
updateBlocker(blockerKey, {
|
|
1588
|
+
state: "proceeding",
|
|
1589
|
+
proceed: undefined,
|
|
1590
|
+
reset: undefined,
|
|
1591
|
+
location
|
|
1592
|
+
}); // Re-do the same POP navigation we just blocked
|
|
1593
|
+
|
|
1594
|
+
init.history.go(delta);
|
|
1595
|
+
},
|
|
1596
|
+
|
|
1597
|
+
reset() {
|
|
1598
|
+
deleteBlocker(blockerKey);
|
|
1599
|
+
updateState({
|
|
1600
|
+
blockers: new Map(router.state.blockers)
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
});
|
|
1605
|
+
return;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1506
1608
|
return startNavigation(historyAction, location);
|
|
1507
1609
|
}); // Kick off initial data load if needed. Use Pop to avoid modifying history
|
|
1508
1610
|
|
|
@@ -1522,6 +1624,7 @@ function createRouter(init) {
|
|
|
1522
1624
|
subscribers.clear();
|
|
1523
1625
|
pendingNavigationController && pendingNavigationController.abort();
|
|
1524
1626
|
state.fetchers.forEach((_, key) => deleteFetcher(key));
|
|
1627
|
+
state.blockers.forEach((_, key) => deleteBlocker(key));
|
|
1525
1628
|
} // Subscribe to state updates for the router
|
|
1526
1629
|
|
|
1527
1630
|
|
|
@@ -1542,7 +1645,7 @@ function createRouter(init) {
|
|
|
1542
1645
|
|
|
1543
1646
|
|
|
1544
1647
|
function completeNavigation(location, newState) {
|
|
1545
|
-
var _location$state;
|
|
1648
|
+
var _location$state, _location$state2;
|
|
1546
1649
|
|
|
1547
1650
|
// Deduce if we're in a loading/actionReload state:
|
|
1548
1651
|
// - We have committed actionData in the store
|
|
@@ -1568,7 +1671,16 @@ function createRouter(init) {
|
|
|
1568
1671
|
} // Always preserve any existing loaderData from re-used routes
|
|
1569
1672
|
|
|
1570
1673
|
|
|
1571
|
-
let loaderData = newState.loaderData ? mergeLoaderData(state.loaderData, newState.loaderData, newState.matches || [], newState.errors) : state.loaderData;
|
|
1674
|
+
let loaderData = newState.loaderData ? mergeLoaderData(state.loaderData, newState.loaderData, newState.matches || [], newState.errors) : state.loaderData; // On a successful navigation we can assume we got through all blockers
|
|
1675
|
+
// so we can start fresh
|
|
1676
|
+
|
|
1677
|
+
for (let [key] of blockerFunctions) {
|
|
1678
|
+
deleteBlocker(key);
|
|
1679
|
+
} // Always respect the user flag. Otherwise don't reset on mutation
|
|
1680
|
+
// submission navigations unless they redirect
|
|
1681
|
+
|
|
1682
|
+
|
|
1683
|
+
let preventScrollReset = pendingPreventScrollReset === true || state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && ((_location$state2 = location.state) == null ? void 0 : _location$state2._isRedirect) !== true;
|
|
1572
1684
|
updateState(_extends({}, newState, {
|
|
1573
1685
|
actionData,
|
|
1574
1686
|
loaderData,
|
|
@@ -1577,9 +1689,9 @@ function createRouter(init) {
|
|
|
1577
1689
|
initialized: true,
|
|
1578
1690
|
navigation: IDLE_NAVIGATION,
|
|
1579
1691
|
revalidation: "idle",
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1692
|
+
restoreScrollPosition: getSavedScrollPosition(location, newState.matches || state.matches),
|
|
1693
|
+
preventScrollReset,
|
|
1694
|
+
blockers: new Map(state.blockers)
|
|
1583
1695
|
}));
|
|
1584
1696
|
|
|
1585
1697
|
if (isUninterruptedRevalidation) ; else if (pendingAction === Action.Pop) ; else if (pendingAction === Action.Push) {
|
|
@@ -1610,13 +1722,14 @@ function createRouter(init) {
|
|
|
1610
1722
|
submission,
|
|
1611
1723
|
error
|
|
1612
1724
|
} = normalizeNavigateOptions(to, opts);
|
|
1613
|
-
let
|
|
1725
|
+
let currentLocation = state.location;
|
|
1726
|
+
let nextLocation = createLocation(state.location, path, opts && opts.state); // When using navigate as a PUSH/REPLACE we aren't reading an already-encoded
|
|
1614
1727
|
// URL from window.location, so we need to encode it here so the behavior
|
|
1615
1728
|
// remains the same as POP and non-data-router usages. new URL() does all
|
|
1616
1729
|
// the same encoding we'd get from a history.pushState/window.location read
|
|
1617
1730
|
// without having to touch history
|
|
1618
1731
|
|
|
1619
|
-
|
|
1732
|
+
nextLocation = _extends({}, nextLocation, init.history.encodeLocation(nextLocation));
|
|
1620
1733
|
let userReplace = opts && opts.replace != null ? opts.replace : undefined;
|
|
1621
1734
|
let historyAction = Action.Push;
|
|
1622
1735
|
|
|
@@ -1631,7 +1744,41 @@ function createRouter(init) {
|
|
|
1631
1744
|
}
|
|
1632
1745
|
|
|
1633
1746
|
let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : undefined;
|
|
1634
|
-
|
|
1747
|
+
let blockerKey = shouldBlockNavigation({
|
|
1748
|
+
currentLocation,
|
|
1749
|
+
nextLocation,
|
|
1750
|
+
historyAction
|
|
1751
|
+
});
|
|
1752
|
+
|
|
1753
|
+
if (blockerKey) {
|
|
1754
|
+
// Put the blocker into a blocked state
|
|
1755
|
+
updateBlocker(blockerKey, {
|
|
1756
|
+
state: "blocked",
|
|
1757
|
+
location: nextLocation,
|
|
1758
|
+
|
|
1759
|
+
proceed() {
|
|
1760
|
+
updateBlocker(blockerKey, {
|
|
1761
|
+
state: "proceeding",
|
|
1762
|
+
proceed: undefined,
|
|
1763
|
+
reset: undefined,
|
|
1764
|
+
location: nextLocation
|
|
1765
|
+
}); // Send the same navigation through
|
|
1766
|
+
|
|
1767
|
+
navigate(to, opts);
|
|
1768
|
+
},
|
|
1769
|
+
|
|
1770
|
+
reset() {
|
|
1771
|
+
deleteBlocker(blockerKey);
|
|
1772
|
+
updateState({
|
|
1773
|
+
blockers: new Map(state.blockers)
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
});
|
|
1778
|
+
return;
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
return await startNavigation(historyAction, nextLocation, {
|
|
1635
1782
|
submission,
|
|
1636
1783
|
// Send through the formData serialization error if we have one so we can
|
|
1637
1784
|
// render at the right error boundary after we match routes
|
|
@@ -2403,7 +2550,9 @@ function createRouter(init) {
|
|
|
2403
2550
|
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2404
2551
|
submission: _extends({}, submission, {
|
|
2405
2552
|
formAction: redirect.location
|
|
2406
|
-
})
|
|
2553
|
+
}),
|
|
2554
|
+
// Preserve this flag across redirects
|
|
2555
|
+
preventScrollReset: pendingPreventScrollReset
|
|
2407
2556
|
});
|
|
2408
2557
|
} else {
|
|
2409
2558
|
// Otherwise, we kick off a new loading navigation, preserving the
|
|
@@ -2416,7 +2565,9 @@ function createRouter(init) {
|
|
|
2416
2565
|
formAction: submission ? submission.formAction : undefined,
|
|
2417
2566
|
formEncType: submission ? submission.formEncType : undefined,
|
|
2418
2567
|
formData: submission ? submission.formData : undefined
|
|
2419
|
-
}
|
|
2568
|
+
},
|
|
2569
|
+
// Preserve this flag across redirects
|
|
2570
|
+
preventScrollReset: pendingPreventScrollReset
|
|
2420
2571
|
});
|
|
2421
2572
|
}
|
|
2422
2573
|
}
|
|
@@ -2535,6 +2686,78 @@ function createRouter(init) {
|
|
|
2535
2686
|
return yeetedKeys.length > 0;
|
|
2536
2687
|
}
|
|
2537
2688
|
|
|
2689
|
+
function getBlocker(key, fn) {
|
|
2690
|
+
let blocker = state.blockers.get(key) || IDLE_BLOCKER;
|
|
2691
|
+
|
|
2692
|
+
if (blockerFunctions.get(key) !== fn) {
|
|
2693
|
+
blockerFunctions.set(key, fn);
|
|
2694
|
+
|
|
2695
|
+
if (activeBlocker == null) {
|
|
2696
|
+
// This is now the active blocker
|
|
2697
|
+
activeBlocker = key;
|
|
2698
|
+
} else if (key !== activeBlocker) {
|
|
2699
|
+
warning(false, "A router only supports one blocker at a time");
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
|
|
2703
|
+
return blocker;
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
function deleteBlocker(key) {
|
|
2707
|
+
state.blockers.delete(key);
|
|
2708
|
+
blockerFunctions.delete(key);
|
|
2709
|
+
|
|
2710
|
+
if (activeBlocker === key) {
|
|
2711
|
+
activeBlocker = null;
|
|
2712
|
+
}
|
|
2713
|
+
} // Utility function to update blockers, ensuring valid state transitions
|
|
2714
|
+
|
|
2715
|
+
|
|
2716
|
+
function updateBlocker(key, newBlocker) {
|
|
2717
|
+
let blocker = state.blockers.get(key) || IDLE_BLOCKER; // Poor mans state machine :)
|
|
2718
|
+
// https://mermaid.live/edit#pako:eNqVkc9OwzAMxl8l8nnjAYrEtDIOHEBIgwvKJTReGy3_lDpIqO27k6awMG0XcrLlnz87nwdonESogKXXBuE79rq75XZO3-yHds0RJVuv70YrPlUrCEe2HfrORS3rubqZfuhtpg5C9wk5tZ4VKcRUq88q9Z8RS0-48cE1iHJkL0ugbHuFLus9L6spZy8nX9MP2CNdomVaposqu3fGayT8T8-jJQwhepo_UtpgBQaDEUom04dZhAN1aJBDlUKJBxE1ceB2Smj0Mln-IBW5AFU2dwUiktt_2Qaq2dBfaKdEup85UV7Yd-dKjlnkabl2Pvr0DTkTreM
|
|
2719
|
+
|
|
2720
|
+
invariant(blocker.state === "unblocked" && newBlocker.state === "blocked" || blocker.state === "blocked" && newBlocker.state === "blocked" || blocker.state === "blocked" && newBlocker.state === "proceeding" || blocker.state === "blocked" && newBlocker.state === "unblocked" || blocker.state === "proceeding" && newBlocker.state === "unblocked", "Invalid blocker state transition: " + blocker.state + " -> " + newBlocker.state);
|
|
2721
|
+
state.blockers.set(key, newBlocker);
|
|
2722
|
+
updateState({
|
|
2723
|
+
blockers: new Map(state.blockers)
|
|
2724
|
+
});
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2727
|
+
function shouldBlockNavigation(_ref10) {
|
|
2728
|
+
let {
|
|
2729
|
+
currentLocation,
|
|
2730
|
+
nextLocation,
|
|
2731
|
+
historyAction
|
|
2732
|
+
} = _ref10;
|
|
2733
|
+
|
|
2734
|
+
if (activeBlocker == null) {
|
|
2735
|
+
return;
|
|
2736
|
+
} // We only allow a single blocker at the moment. This will need to be
|
|
2737
|
+
// updated if we enhance to support multiple blockers in the future
|
|
2738
|
+
|
|
2739
|
+
|
|
2740
|
+
let blockerFunction = blockerFunctions.get(activeBlocker);
|
|
2741
|
+
invariant(blockerFunction, "Could not find a function for the active blocker");
|
|
2742
|
+
let blocker = state.blockers.get(activeBlocker);
|
|
2743
|
+
|
|
2744
|
+
if (blocker && blocker.state === "proceeding") {
|
|
2745
|
+
// If the blocker is currently proceeding, we don't need to re-check
|
|
2746
|
+
// it and can let this navigation continue
|
|
2747
|
+
return;
|
|
2748
|
+
} // At this point, we know we're unblocked/blocked so we need to check the
|
|
2749
|
+
// user-provided blocker function
|
|
2750
|
+
|
|
2751
|
+
|
|
2752
|
+
if (blockerFunction({
|
|
2753
|
+
currentLocation,
|
|
2754
|
+
nextLocation,
|
|
2755
|
+
historyAction
|
|
2756
|
+
})) {
|
|
2757
|
+
return activeBlocker;
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2538
2761
|
function cancelActiveDeferreds(predicate) {
|
|
2539
2762
|
let cancelledRouteIds = [];
|
|
2540
2763
|
activeDeferreds.forEach((dfd, routeId) => {
|
|
@@ -2627,6 +2850,8 @@ function createRouter(init) {
|
|
|
2627
2850
|
getFetcher,
|
|
2628
2851
|
deleteFetcher,
|
|
2629
2852
|
dispose,
|
|
2853
|
+
getBlocker,
|
|
2854
|
+
deleteBlocker,
|
|
2630
2855
|
_internalFetchControllers: fetchControllers,
|
|
2631
2856
|
_internalActiveDeferreds: activeDeferreds
|
|
2632
2857
|
};
|
|
@@ -3147,8 +3372,8 @@ function getMatchesToLoad(history, state, matches, submission, location, isReval
|
|
|
3147
3372
|
cancelledDeferredRoutes.some(id => id === match.route.id) || shouldRevalidateLoader(history, state.location, state.matches[index], submission, location, match, isRevalidationRequired, actionResult))); // Pick fetcher.loads that need to be revalidated
|
|
3148
3373
|
|
|
3149
3374
|
let revalidatingFetchers = [];
|
|
3150
|
-
fetchLoadMatches && fetchLoadMatches.forEach((
|
|
3151
|
-
let [href, match, fetchMatches] =
|
|
3375
|
+
fetchLoadMatches && fetchLoadMatches.forEach((_ref11, key) => {
|
|
3376
|
+
let [href, match, fetchMatches] = _ref11;
|
|
3152
3377
|
|
|
3153
3378
|
// This fetcher was cancelled from a prior action submission - force reload
|
|
3154
3379
|
if (cancelledFetcherLoads.includes(key)) {
|
|
@@ -3748,5 +3973,5 @@ function getTargetMatch(matches, location) {
|
|
|
3748
3973
|
return pathMatches[pathMatches.length - 1];
|
|
3749
3974
|
} //#endregion
|
|
3750
3975
|
|
|
3751
|
-
export { AbortedDeferredError, Action, ErrorResponse, IDLE_FETCHER, IDLE_NAVIGATION, UNSAFE_DEFERRED_SYMBOL, DeferredData as UNSAFE_DeferredData, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getPathContributingMatches as UNSAFE_getPathContributingMatches, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, createRouter, createStaticHandler, defer, generatePath, getStaticContextFromError, getToPathname, invariant, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, parsePath, redirect, resolvePath, resolveTo, stripBasename, warning };
|
|
3976
|
+
export { AbortedDeferredError, Action, ErrorResponse, IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, UNSAFE_DEFERRED_SYMBOL, DeferredData as UNSAFE_DeferredData, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getPathContributingMatches as UNSAFE_getPathContributingMatches, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, createRouter, createStaticHandler, defer, generatePath, getStaticContextFromError, getToPathname, invariant, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, parsePath, redirect, resolvePath, resolveTo, stripBasename, warning };
|
|
3752
3977
|
//# sourceMappingURL=router.js.map
|