@tanstack/react-router 0.0.1-beta.210 → 0.0.1-beta.212
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/build/cjs/RouterProvider.js +42 -47
- package/build/cjs/RouterProvider.js.map +1 -1
- package/build/cjs/index.js +3 -0
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/link.js +1 -0
- package/build/cjs/link.js.map +1 -1
- package/build/cjs/router.js.map +1 -1
- package/build/cjs/scroll-restoration.js +186 -0
- package/build/cjs/scroll-restoration.js.map +1 -0
- package/build/esm/index.js +191 -50
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +300 -256
- package/build/types/RouterProvider.d.ts +2 -0
- package/build/types/index.d.ts +1 -0
- package/build/types/router.d.ts +11 -5
- package/build/types/scroll-restoration.d.ts +6 -0
- package/build/umd/index.development.js +197 -52
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +1 -1
- package/build/umd/index.production.js.map +1 -1
- package/package.json +2 -2
- package/src/RouterProvider.tsx +56 -47
- package/src/index.tsx +1 -1
- package/src/link.tsx +1 -0
- package/src/router.ts +11 -5
- package/src/scroll-restoration.tsx +192 -205
package/build/esm/index.js
CHANGED
|
@@ -275,7 +275,7 @@ function useRouteContext(opts) {
|
|
|
275
275
|
select: match => opts?.select ? opts.select(match.context) : match.context
|
|
276
276
|
});
|
|
277
277
|
}
|
|
278
|
-
const useLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
|
|
278
|
+
const useLayoutEffect$1 = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
|
|
279
279
|
|
|
280
280
|
function joinPaths(paths) {
|
|
281
281
|
return cleanPath(paths.filter(Boolean).join('/'));
|
|
@@ -777,8 +777,8 @@ class PathParamError extends Error {}
|
|
|
777
777
|
function getInitialRouterState(location) {
|
|
778
778
|
return {
|
|
779
779
|
status: 'idle',
|
|
780
|
-
resolvedLocation:
|
|
781
|
-
location
|
|
780
|
+
resolvedLocation: location,
|
|
781
|
+
location,
|
|
782
782
|
matches: [],
|
|
783
783
|
pendingMatches: [],
|
|
784
784
|
lastUpdated: Date.now()
|
|
@@ -798,7 +798,7 @@ function RouterProvider({
|
|
|
798
798
|
};
|
|
799
799
|
const history = React.useState(() => options.history ?? createBrowserHistory())[0];
|
|
800
800
|
const tempLocationKeyRef = React.useRef(`${Math.round(Math.random() * 10000000)}`);
|
|
801
|
-
const resetNextScrollRef = React.useRef(
|
|
801
|
+
const resetNextScrollRef = React.useRef(true);
|
|
802
802
|
const navigateTimeoutRef = React.useRef(null);
|
|
803
803
|
const latestLoadPromiseRef = React.useRef(Promise.resolve());
|
|
804
804
|
const checkLatest = promise => {
|
|
@@ -838,14 +838,22 @@ function RouterProvider({
|
|
|
838
838
|
}
|
|
839
839
|
return location;
|
|
840
840
|
});
|
|
841
|
-
const
|
|
841
|
+
const latestLocationRef = React.useRef(parseLocation());
|
|
842
|
+
const [preState, setState] = React.useState(() => getInitialRouterState(latestLocationRef.current));
|
|
842
843
|
const [isTransitioning, startReactTransition] = React.useTransition();
|
|
843
844
|
const state = React.useMemo(() => ({
|
|
844
845
|
...preState,
|
|
845
|
-
status: isTransitioning ? 'pending' : 'idle'
|
|
846
|
+
status: isTransitioning ? 'pending' : 'idle',
|
|
847
|
+
location: isTransitioning ? latestLocationRef.current : preState.location
|
|
846
848
|
}), [preState, isTransitioning]);
|
|
847
849
|
React.useLayoutEffect(() => {
|
|
848
850
|
if (!isTransitioning && state.resolvedLocation !== state.location) {
|
|
851
|
+
router.emit({
|
|
852
|
+
type: 'onResolved',
|
|
853
|
+
fromLocation: state.resolvedLocation,
|
|
854
|
+
toLocation: state.location,
|
|
855
|
+
pathChanged: state.location.href !== state.resolvedLocation?.href
|
|
856
|
+
});
|
|
849
857
|
setState(s => ({
|
|
850
858
|
...s,
|
|
851
859
|
resolvedLocation: s.location
|
|
@@ -1298,7 +1306,6 @@ function RouterProvider({
|
|
|
1298
1306
|
preload: !!preload,
|
|
1299
1307
|
context: parentContext,
|
|
1300
1308
|
location: state.location,
|
|
1301
|
-
// TODO: This might need to be latestLocationRef.current...?
|
|
1302
1309
|
navigate: opts => navigate({
|
|
1303
1310
|
...opts,
|
|
1304
1311
|
from: match.pathname
|
|
@@ -1418,16 +1425,16 @@ function RouterProvider({
|
|
|
1418
1425
|
const load = useStableCallback(async () => {
|
|
1419
1426
|
const promise = new Promise(async (resolve, reject) => {
|
|
1420
1427
|
const next = latestLocationRef.current;
|
|
1421
|
-
const prevLocation = state.resolvedLocation
|
|
1422
|
-
const pathDidChange =
|
|
1428
|
+
const prevLocation = state.resolvedLocation;
|
|
1429
|
+
const pathDidChange = prevLocation.href !== next.href;
|
|
1423
1430
|
let latestPromise;
|
|
1424
1431
|
|
|
1425
1432
|
// Cancel any pending matches
|
|
1426
1433
|
cancelMatches(state);
|
|
1427
1434
|
router.emit({
|
|
1428
1435
|
type: 'onBeforeLoad',
|
|
1429
|
-
|
|
1430
|
-
|
|
1436
|
+
fromLocation: prevLocation,
|
|
1437
|
+
toLocation: next,
|
|
1431
1438
|
pathChanged: pathDidChange
|
|
1432
1439
|
});
|
|
1433
1440
|
|
|
@@ -1435,10 +1442,13 @@ function RouterProvider({
|
|
|
1435
1442
|
let matches = matchRoutes(next.pathname, next.search, {
|
|
1436
1443
|
debug: true
|
|
1437
1444
|
});
|
|
1445
|
+
const previousMatches = state.matches;
|
|
1438
1446
|
|
|
1439
1447
|
// Ingest the new matches
|
|
1440
1448
|
setState(s => ({
|
|
1441
1449
|
...s,
|
|
1450
|
+
status: 'pending',
|
|
1451
|
+
location: next,
|
|
1442
1452
|
matches
|
|
1443
1453
|
}));
|
|
1444
1454
|
try {
|
|
@@ -1457,17 +1467,9 @@ function RouterProvider({
|
|
|
1457
1467
|
if (latestPromise = checkLatest(promise)) {
|
|
1458
1468
|
return latestPromise;
|
|
1459
1469
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
// (id) => !state.pendingMatches.includes(id),
|
|
1464
|
-
// )
|
|
1465
|
-
// const enteringMatchIds = state.pendingMatches.filter(
|
|
1466
|
-
// (id) => !previousMatches.includes(id),
|
|
1467
|
-
// )
|
|
1468
|
-
// const stayingMatchIds = previousMatches.filter((id) =>
|
|
1469
|
-
// state.pendingMatches.includes(id),
|
|
1470
|
-
// )
|
|
1470
|
+
const exitingMatchIds = previousMatches.filter(id => !state.pendingMatches.includes(id));
|
|
1471
|
+
const enteringMatchIds = state.pendingMatches.filter(id => !previousMatches.includes(id));
|
|
1472
|
+
const stayingMatchIds = previousMatches.filter(id => state.pendingMatches.includes(id))
|
|
1471
1473
|
|
|
1472
1474
|
// setState((s) => ({
|
|
1473
1475
|
// ...s,
|
|
@@ -1475,23 +1477,17 @@ function RouterProvider({
|
|
|
1475
1477
|
// resolvedLocation: s.location,
|
|
1476
1478
|
// }))
|
|
1477
1479
|
|
|
1478
|
-
//
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
// ).forEach(([matches, hook]) => {
|
|
1486
|
-
// matches.forEach((match) => {
|
|
1487
|
-
// const route = this.getRoute(match.routeId)
|
|
1488
|
-
// route.options[hook]?.(match)
|
|
1489
|
-
// })
|
|
1490
|
-
// })
|
|
1480
|
+
//
|
|
1481
|
+
;
|
|
1482
|
+
[[exitingMatchIds, 'onLeave'], [enteringMatchIds, 'onEnter'], [stayingMatchIds, 'onTransition']].forEach(([matches, hook]) => {
|
|
1483
|
+
matches.forEach(match => {
|
|
1484
|
+
looseRoutesById[match.routeId].options[hook]?.(match);
|
|
1485
|
+
});
|
|
1486
|
+
});
|
|
1491
1487
|
router.emit({
|
|
1492
1488
|
type: 'onLoad',
|
|
1493
|
-
|
|
1494
|
-
|
|
1489
|
+
fromLocation: prevLocation,
|
|
1490
|
+
toLocation: next,
|
|
1495
1491
|
pathChanged: pathDidChange
|
|
1496
1492
|
});
|
|
1497
1493
|
resolve();
|
|
@@ -1624,20 +1620,17 @@ function RouterProvider({
|
|
|
1624
1620
|
disabled
|
|
1625
1621
|
};
|
|
1626
1622
|
});
|
|
1627
|
-
const latestLocationRef = React.useRef(state.location);
|
|
1628
1623
|
React.useLayoutEffect(() => {
|
|
1629
1624
|
const unsub = history.subscribe(() => {
|
|
1630
1625
|
latestLocationRef.current = parseLocation(latestLocationRef.current);
|
|
1631
|
-
setState(s => ({
|
|
1632
|
-
...s,
|
|
1633
|
-
status: 'pending'
|
|
1634
|
-
}));
|
|
1635
1626
|
if (state.location !== latestLocationRef.current) {
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1627
|
+
startReactTransition(() => {
|
|
1628
|
+
try {
|
|
1629
|
+
load();
|
|
1630
|
+
} catch (err) {
|
|
1631
|
+
console.error(err);
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1641
1634
|
}
|
|
1642
1635
|
});
|
|
1643
1636
|
const nextLocation = buildLocation({
|
|
@@ -1705,7 +1698,9 @@ function RouterProvider({
|
|
|
1705
1698
|
options,
|
|
1706
1699
|
history,
|
|
1707
1700
|
load,
|
|
1708
|
-
buildLocation
|
|
1701
|
+
buildLocation,
|
|
1702
|
+
subscribe: router.subscribe,
|
|
1703
|
+
resetNextScrollRef
|
|
1709
1704
|
};
|
|
1710
1705
|
return /*#__PURE__*/React.createElement(routerContext.Provider, {
|
|
1711
1706
|
value: routerContextValue
|
|
@@ -2091,6 +2086,7 @@ function useLinkProps(options) {
|
|
|
2091
2086
|
preloadDelay,
|
|
2092
2087
|
replace,
|
|
2093
2088
|
startTransition,
|
|
2089
|
+
resetScroll,
|
|
2094
2090
|
// element props
|
|
2095
2091
|
style,
|
|
2096
2092
|
className,
|
|
@@ -2170,6 +2166,151 @@ const Link = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
2170
2166
|
}));
|
|
2171
2167
|
});
|
|
2172
2168
|
|
|
2169
|
+
const useLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
|
|
2170
|
+
const windowKey = 'window';
|
|
2171
|
+
const delimiter = '___';
|
|
2172
|
+
let weakScrolledElements = new WeakSet();
|
|
2173
|
+
let cache;
|
|
2174
|
+
const sessionsStorage = typeof window !== 'undefined' && window.sessionStorage;
|
|
2175
|
+
const defaultGetKey = location => location.state.key;
|
|
2176
|
+
function useScrollRestoration(options) {
|
|
2177
|
+
const {
|
|
2178
|
+
state,
|
|
2179
|
+
subscribe,
|
|
2180
|
+
resetNextScrollRef
|
|
2181
|
+
} = useRouter();
|
|
2182
|
+
useLayoutEffect(() => {
|
|
2183
|
+
const getKey = options?.getKey || defaultGetKey;
|
|
2184
|
+
if (sessionsStorage) {
|
|
2185
|
+
if (!cache) {
|
|
2186
|
+
cache = (() => {
|
|
2187
|
+
const storageKey = 'tsr-scroll-restoration-v2';
|
|
2188
|
+
const state = JSON.parse(window.sessionStorage.getItem(storageKey) || 'null') || {
|
|
2189
|
+
cached: {},
|
|
2190
|
+
next: {}
|
|
2191
|
+
};
|
|
2192
|
+
return {
|
|
2193
|
+
state,
|
|
2194
|
+
set: updater => {
|
|
2195
|
+
cache.state = functionalUpdate(updater, cache.state);
|
|
2196
|
+
window.sessionStorage.setItem(storageKey, JSON.stringify(cache.state));
|
|
2197
|
+
}
|
|
2198
|
+
};
|
|
2199
|
+
})();
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
const {
|
|
2203
|
+
history
|
|
2204
|
+
} = window;
|
|
2205
|
+
if (history.scrollRestoration) {
|
|
2206
|
+
history.scrollRestoration = 'manual';
|
|
2207
|
+
}
|
|
2208
|
+
const onScroll = event => {
|
|
2209
|
+
if (weakScrolledElements.has(event.target)) return;
|
|
2210
|
+
weakScrolledElements.add(event.target);
|
|
2211
|
+
const elementSelector = event.target === document || event.target === window ? windowKey : getCssSelector(event.target);
|
|
2212
|
+
if (!cache.state.next[elementSelector]) {
|
|
2213
|
+
cache.set(c => ({
|
|
2214
|
+
...c,
|
|
2215
|
+
next: {
|
|
2216
|
+
...c.next,
|
|
2217
|
+
[elementSelector]: {
|
|
2218
|
+
scrollX: NaN,
|
|
2219
|
+
scrollY: NaN
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
}));
|
|
2223
|
+
}
|
|
2224
|
+
};
|
|
2225
|
+
const getCssSelector = el => {
|
|
2226
|
+
let path = [],
|
|
2227
|
+
parent;
|
|
2228
|
+
while (parent = el.parentNode) {
|
|
2229
|
+
path.unshift(`${el.tagName}:nth-child(${[].indexOf.call(parent.children, el) + 1})`);
|
|
2230
|
+
el = parent;
|
|
2231
|
+
}
|
|
2232
|
+
return `${path.join(' > ')}`.toLowerCase();
|
|
2233
|
+
};
|
|
2234
|
+
if (typeof document !== 'undefined') {
|
|
2235
|
+
document.addEventListener('scroll', onScroll, true);
|
|
2236
|
+
}
|
|
2237
|
+
const unsubOnBeforeLoad = subscribe('onBeforeLoad', event => {
|
|
2238
|
+
if (event.pathChanged) {
|
|
2239
|
+
const restoreKey = getKey(event.fromLocation);
|
|
2240
|
+
for (const elementSelector in cache.state.next) {
|
|
2241
|
+
const entry = cache.state.next[elementSelector];
|
|
2242
|
+
if (elementSelector === windowKey) {
|
|
2243
|
+
entry.scrollX = window.scrollX || 0;
|
|
2244
|
+
entry.scrollY = window.scrollY || 0;
|
|
2245
|
+
} else if (elementSelector) {
|
|
2246
|
+
const element = document.querySelector(elementSelector);
|
|
2247
|
+
entry.scrollX = element?.scrollLeft || 0;
|
|
2248
|
+
entry.scrollY = element?.scrollTop || 0;
|
|
2249
|
+
}
|
|
2250
|
+
cache.set(c => {
|
|
2251
|
+
const next = {
|
|
2252
|
+
...c.next
|
|
2253
|
+
};
|
|
2254
|
+
delete next[elementSelector];
|
|
2255
|
+
return {
|
|
2256
|
+
...c,
|
|
2257
|
+
next,
|
|
2258
|
+
cached: {
|
|
2259
|
+
...c.cached,
|
|
2260
|
+
[[restoreKey, elementSelector].join(delimiter)]: entry
|
|
2261
|
+
}
|
|
2262
|
+
};
|
|
2263
|
+
});
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
});
|
|
2267
|
+
const unsubOnResolved = subscribe('onResolved', event => {
|
|
2268
|
+
if (event.pathChanged) {
|
|
2269
|
+
if (!resetNextScrollRef.current) {
|
|
2270
|
+
return;
|
|
2271
|
+
}
|
|
2272
|
+
resetNextScrollRef.current = true;
|
|
2273
|
+
const getKey = options?.getKey || defaultGetKey;
|
|
2274
|
+
const restoreKey = getKey(event.toLocation);
|
|
2275
|
+
let windowRestored = false;
|
|
2276
|
+
for (const cacheKey in cache.state.cached) {
|
|
2277
|
+
const entry = cache.state.cached[cacheKey];
|
|
2278
|
+
const [key, elementSelector] = cacheKey.split(delimiter);
|
|
2279
|
+
if (key === restoreKey) {
|
|
2280
|
+
if (elementSelector === windowKey) {
|
|
2281
|
+
windowRestored = true;
|
|
2282
|
+
window.scrollTo(entry.scrollX, entry.scrollY);
|
|
2283
|
+
} else if (elementSelector) {
|
|
2284
|
+
const element = document.querySelector(elementSelector);
|
|
2285
|
+
if (element) {
|
|
2286
|
+
element.scrollLeft = entry.scrollX;
|
|
2287
|
+
element.scrollTop = entry.scrollY;
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
if (!windowRestored) {
|
|
2293
|
+
window.scrollTo(0, 0);
|
|
2294
|
+
}
|
|
2295
|
+
cache.set(c => ({
|
|
2296
|
+
...c,
|
|
2297
|
+
next: {}
|
|
2298
|
+
}));
|
|
2299
|
+
weakScrolledElements = new WeakSet();
|
|
2300
|
+
}
|
|
2301
|
+
});
|
|
2302
|
+
return () => {
|
|
2303
|
+
document.removeEventListener('scroll', onScroll);
|
|
2304
|
+
unsubOnBeforeLoad();
|
|
2305
|
+
unsubOnResolved();
|
|
2306
|
+
};
|
|
2307
|
+
}, []);
|
|
2308
|
+
}
|
|
2309
|
+
function ScrollRestoration(props) {
|
|
2310
|
+
useScrollRestoration(props);
|
|
2311
|
+
return null;
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2173
2314
|
function useBlocker(message, condition = true) {
|
|
2174
2315
|
const {
|
|
2175
2316
|
history
|
|
@@ -2220,7 +2361,7 @@ function Navigate(props) {
|
|
|
2220
2361
|
const match = useMatch({
|
|
2221
2362
|
strict: false
|
|
2222
2363
|
});
|
|
2223
|
-
useLayoutEffect(() => {
|
|
2364
|
+
useLayoutEffect$1(() => {
|
|
2224
2365
|
navigate({
|
|
2225
2366
|
from: props.to ? match.pathname : undefined,
|
|
2226
2367
|
...props
|
|
@@ -2229,5 +2370,5 @@ function Navigate(props) {
|
|
|
2229
2370
|
return null;
|
|
2230
2371
|
}
|
|
2231
2372
|
|
|
2232
|
-
export { Block, CatchBoundary, CatchBoundaryImpl, ErrorComponent, FileRoute, Link, Match, MatchRoute, Matches, Navigate, Outlet, PathParamError, RootRoute, Route, Router, RouterProvider, SearchParamError, cleanPath, componentTypes, createRouteMask, decode, defaultParseSearch, defaultStringifySearch, encode, functionalUpdate, getInitialRouterState, getRouteMatch, interpolatePath, isPlainObject, isRedirect, isServer, joinPaths, last, lazyFn, lazyRouteComponent, matchByPath, matchPathname, matchesContext, parsePathname, parseSearchWith, partialDeepEqual, pick, redirect, replaceEqualDeep, resolvePath, rootRouteId, rootRouteWithContext, routerContext, shallow, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, typedNavigate, useBlocker, useLayoutEffect, useLinkProps, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useRouteContext, useRouter, useRouterState, useSearch, useStableCallback };
|
|
2373
|
+
export { Block, CatchBoundary, CatchBoundaryImpl, ErrorComponent, FileRoute, Link, Match, MatchRoute, Matches, Navigate, Outlet, PathParamError, RootRoute, Route, Router, RouterProvider, ScrollRestoration, SearchParamError, cleanPath, componentTypes, createRouteMask, decode, defaultParseSearch, defaultStringifySearch, encode, functionalUpdate, getInitialRouterState, getRouteMatch, interpolatePath, isPlainObject, isRedirect, isServer, joinPaths, last, lazyFn, lazyRouteComponent, matchByPath, matchPathname, matchesContext, parsePathname, parseSearchWith, partialDeepEqual, pick, redirect, replaceEqualDeep, resolvePath, rootRouteId, rootRouteWithContext, routerContext, shallow, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, typedNavigate, useBlocker, useLayoutEffect$1 as useLayoutEffect, useLinkProps, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useRouteContext, useRouter, useRouterState, useScrollRestoration, useSearch, useStableCallback };
|
|
2233
2374
|
//# sourceMappingURL=index.js.map
|