@tanstack/react-router 0.0.1-beta.211 → 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.
@@ -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: undefined,
781
- location: 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(false);
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 [preState, setState] = React.useState(() => getInitialRouterState(parseLocation()));
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 || state.location;
1421
- const pathDidChange = !!(next && prevLocation.href !== next.href);
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
- from: prevLocation,
1429
- to: next ?? state.location,
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
- // TODO:
1461
- // const exitingMatchIds = previousMatches.filter(
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
- // TODO:
1478
- // ;(
1479
- // [
1480
- // [exitingMatchIds, 'onLeave'],
1481
- // [enteringMatchIds, 'onEnter'],
1482
- // [stayingMatchIds, 'onTransition'],
1483
- // ] as const
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
- from: prevLocation,
1493
- to: next,
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
- try {
1636
- load();
1637
- } catch (err) {
1638
- console.error(err);
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