@tanstack/react-router 0.0.1-beta.211 → 0.0.1-beta.213
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 -46
- 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 -49
- 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 -51
- 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 +55 -46
- 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
|
|
@@ -1417,16 +1425,16 @@ function RouterProvider({
|
|
|
1417
1425
|
const load = useStableCallback(async () => {
|
|
1418
1426
|
const promise = new Promise(async (resolve, reject) => {
|
|
1419
1427
|
const next = latestLocationRef.current;
|
|
1420
|
-
const prevLocation = state.resolvedLocation
|
|
1421
|
-
const pathDidChange =
|
|
1428
|
+
const prevLocation = state.resolvedLocation;
|
|
1429
|
+
const pathDidChange = prevLocation.href !== next.href;
|
|
1422
1430
|
let latestPromise;
|
|
1423
1431
|
|
|
1424
1432
|
// Cancel any pending matches
|
|
1425
1433
|
cancelMatches(state);
|
|
1426
1434
|
router.emit({
|
|
1427
1435
|
type: 'onBeforeLoad',
|
|
1428
|
-
|
|
1429
|
-
|
|
1436
|
+
fromLocation: prevLocation,
|
|
1437
|
+
toLocation: next,
|
|
1430
1438
|
pathChanged: pathDidChange
|
|
1431
1439
|
});
|
|
1432
1440
|
|
|
@@ -1434,10 +1442,13 @@ function RouterProvider({
|
|
|
1434
1442
|
let matches = matchRoutes(next.pathname, next.search, {
|
|
1435
1443
|
debug: true
|
|
1436
1444
|
});
|
|
1445
|
+
const previousMatches = state.matches;
|
|
1437
1446
|
|
|
1438
1447
|
// Ingest the new matches
|
|
1439
1448
|
setState(s => ({
|
|
1440
1449
|
...s,
|
|
1450
|
+
status: 'pending',
|
|
1451
|
+
location: next,
|
|
1441
1452
|
matches
|
|
1442
1453
|
}));
|
|
1443
1454
|
try {
|
|
@@ -1456,17 +1467,9 @@ function RouterProvider({
|
|
|
1456
1467
|
if (latestPromise = checkLatest(promise)) {
|
|
1457
1468
|
return latestPromise;
|
|
1458
1469
|
}
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
// (id) => !state.pendingMatches.includes(id),
|
|
1463
|
-
// )
|
|
1464
|
-
// const enteringMatchIds = state.pendingMatches.filter(
|
|
1465
|
-
// (id) => !previousMatches.includes(id),
|
|
1466
|
-
// )
|
|
1467
|
-
// const stayingMatchIds = previousMatches.filter((id) =>
|
|
1468
|
-
// state.pendingMatches.includes(id),
|
|
1469
|
-
// )
|
|
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))
|
|
1470
1473
|
|
|
1471
1474
|
// setState((s) => ({
|
|
1472
1475
|
// ...s,
|
|
@@ -1474,23 +1477,17 @@ function RouterProvider({
|
|
|
1474
1477
|
// resolvedLocation: s.location,
|
|
1475
1478
|
// }))
|
|
1476
1479
|
|
|
1477
|
-
//
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
// ).forEach(([matches, hook]) => {
|
|
1485
|
-
// matches.forEach((match) => {
|
|
1486
|
-
// const route = this.getRoute(match.routeId)
|
|
1487
|
-
// route.options[hook]?.(match)
|
|
1488
|
-
// })
|
|
1489
|
-
// })
|
|
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
|
+
});
|
|
1490
1487
|
router.emit({
|
|
1491
1488
|
type: 'onLoad',
|
|
1492
|
-
|
|
1493
|
-
|
|
1489
|
+
fromLocation: prevLocation,
|
|
1490
|
+
toLocation: next,
|
|
1494
1491
|
pathChanged: pathDidChange
|
|
1495
1492
|
});
|
|
1496
1493
|
resolve();
|
|
@@ -1623,20 +1620,17 @@ function RouterProvider({
|
|
|
1623
1620
|
disabled
|
|
1624
1621
|
};
|
|
1625
1622
|
});
|
|
1626
|
-
const latestLocationRef = React.useRef(state.location);
|
|
1627
1623
|
React.useLayoutEffect(() => {
|
|
1628
1624
|
const unsub = history.subscribe(() => {
|
|
1629
1625
|
latestLocationRef.current = parseLocation(latestLocationRef.current);
|
|
1630
|
-
setState(s => ({
|
|
1631
|
-
...s,
|
|
1632
|
-
status: 'pending'
|
|
1633
|
-
}));
|
|
1634
1626
|
if (state.location !== latestLocationRef.current) {
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1627
|
+
startReactTransition(() => {
|
|
1628
|
+
try {
|
|
1629
|
+
load();
|
|
1630
|
+
} catch (err) {
|
|
1631
|
+
console.error(err);
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1640
1634
|
}
|
|
1641
1635
|
});
|
|
1642
1636
|
const nextLocation = buildLocation({
|
|
@@ -1704,7 +1698,9 @@ function RouterProvider({
|
|
|
1704
1698
|
options,
|
|
1705
1699
|
history,
|
|
1706
1700
|
load,
|
|
1707
|
-
buildLocation
|
|
1701
|
+
buildLocation,
|
|
1702
|
+
subscribe: router.subscribe,
|
|
1703
|
+
resetNextScrollRef
|
|
1708
1704
|
};
|
|
1709
1705
|
return /*#__PURE__*/React.createElement(routerContext.Provider, {
|
|
1710
1706
|
value: routerContextValue
|
|
@@ -2090,6 +2086,7 @@ function useLinkProps(options) {
|
|
|
2090
2086
|
preloadDelay,
|
|
2091
2087
|
replace,
|
|
2092
2088
|
startTransition,
|
|
2089
|
+
resetScroll,
|
|
2093
2090
|
// element props
|
|
2094
2091
|
style,
|
|
2095
2092
|
className,
|
|
@@ -2169,6 +2166,151 @@ const Link = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
2169
2166
|
}));
|
|
2170
2167
|
});
|
|
2171
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
|
+
|
|
2172
2314
|
function useBlocker(message, condition = true) {
|
|
2173
2315
|
const {
|
|
2174
2316
|
history
|
|
@@ -2219,7 +2361,7 @@ function Navigate(props) {
|
|
|
2219
2361
|
const match = useMatch({
|
|
2220
2362
|
strict: false
|
|
2221
2363
|
});
|
|
2222
|
-
useLayoutEffect(() => {
|
|
2364
|
+
useLayoutEffect$1(() => {
|
|
2223
2365
|
navigate({
|
|
2224
2366
|
from: props.to ? match.pathname : undefined,
|
|
2225
2367
|
...props
|
|
@@ -2228,5 +2370,5 @@ function Navigate(props) {
|
|
|
2228
2370
|
return null;
|
|
2229
2371
|
}
|
|
2230
2372
|
|
|
2231
|
-
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 };
|
|
2232
2374
|
//# sourceMappingURL=index.js.map
|