@tanstack/react-router 1.28.7 → 1.28.8

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.
@@ -5,15 +5,6 @@ import { pick, useLayoutEffect } from "./utils.js";
5
5
  import { useRouter } from "./useRouter.js";
6
6
  import { useRouterState } from "./useRouterState.js";
7
7
  import { getRouterContext } from "./routerContext.js";
8
- const useTransition = (
9
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
10
- React.useTransition || (() => [
11
- false,
12
- (cb) => {
13
- cb();
14
- }
15
- ])
16
- );
17
8
  function RouterProvider({ router, ...rest }) {
18
9
  router.update({
19
10
  ...router.options,
@@ -40,31 +31,20 @@ function Transitioner() {
40
31
  const routerState = useRouterState({
41
32
  select: (s) => pick(s, ["isLoading", "location", "resolvedLocation", "isTransitioning"])
42
33
  });
43
- const [isTransitioning, startReactTransition] = useTransition();
44
- router.startReactTransition = startReactTransition;
45
- React.useEffect(() => {
46
- if (isTransitioning) {
47
- router.__store.setState((s) => ({
48
- ...s,
49
- isTransitioning
50
- }));
51
- }
52
- }, [isTransitioning, router]);
34
+ const [isTransitioning, startReactTransition_] = React.useTransition();
35
+ const hasPendingMatches = useRouterState({
36
+ select: (s) => s.matches.some((d) => d.status === "pending")
37
+ });
38
+ const previousIsLoading = usePrevious(routerState.isLoading);
39
+ const isAnyPending = routerState.isLoading || isTransitioning || hasPendingMatches;
40
+ const previousIsAnyPending = usePrevious(isAnyPending);
41
+ router.startReactTransition = startReactTransition_;
53
42
  const tryLoad = () => {
54
- const apply = (cb) => {
55
- if (!routerState.isTransitioning) {
56
- startReactTransition(() => cb());
57
- } else {
58
- cb();
59
- }
60
- };
61
- apply(() => {
62
- try {
63
- router.load();
64
- } catch (err) {
65
- console.error(err);
66
- }
67
- });
43
+ try {
44
+ router.load();
45
+ } catch (err) {
46
+ console.error(err);
47
+ }
68
48
  };
69
49
  useLayoutEffect(() => {
70
50
  const unsub = router.history.subscribe(() => {
@@ -88,42 +68,51 @@ function Transitioner() {
88
68
  };
89
69
  }, [router, router.history]);
90
70
  useLayoutEffect(() => {
91
- if (React.useTransition ? routerState.isTransitioning && !isTransitioning : !routerState.isLoading && routerState.resolvedLocation !== routerState.location) {
71
+ if (window.__TSR_DEHYDRATED__ || mountLoadForRouter.current.router === router && mountLoadForRouter.current.mounted) {
72
+ return;
73
+ }
74
+ mountLoadForRouter.current = { router, mounted: true };
75
+ tryLoad();
76
+ }, [router]);
77
+ useLayoutEffect(() => {
78
+ if (previousIsLoading && !routerState.isLoading) {
79
+ const toLocation = router.state.location;
80
+ const fromLocation = router.state.resolvedLocation;
81
+ const pathChanged = fromLocation.href !== toLocation.href;
82
+ router.emit({
83
+ type: "onLoad",
84
+ fromLocation,
85
+ toLocation,
86
+ pathChanged
87
+ });
88
+ }
89
+ }, [previousIsLoading, router, routerState.isLoading]);
90
+ useLayoutEffect(() => {
91
+ if (previousIsAnyPending && !isAnyPending) {
92
+ const toLocation = router.state.location;
93
+ const fromLocation = router.state.resolvedLocation;
94
+ const pathChanged = fromLocation.href !== toLocation.href;
92
95
  router.emit({
93
96
  type: "onResolved",
94
- fromLocation: routerState.resolvedLocation,
95
- toLocation: routerState.location,
96
- pathChanged: routerState.location.href !== routerState.resolvedLocation.href
97
+ fromLocation,
98
+ toLocation,
99
+ pathChanged
97
100
  });
101
+ router.__store.setState((s) => ({
102
+ ...s,
103
+ status: "idle",
104
+ resolvedLocation: s.location
105
+ }));
98
106
  if (document.querySelector) {
99
- if (routerState.location.hash !== "") {
100
- const el = document.getElementById(routerState.location.hash);
107
+ if (router.state.location.hash !== "") {
108
+ const el = document.getElementById(router.state.location.hash);
101
109
  if (el) {
102
110
  el.scrollIntoView();
103
111
  }
104
112
  }
105
113
  }
106
- router.__store.setState((s) => ({
107
- ...s,
108
- isTransitioning: false,
109
- resolvedLocation: s.location
110
- }));
111
114
  }
112
- }, [
113
- routerState.isTransitioning,
114
- isTransitioning,
115
- routerState.isLoading,
116
- routerState.resolvedLocation,
117
- routerState.location,
118
- router
119
- ]);
120
- useLayoutEffect(() => {
121
- if (window.__TSR_DEHYDRATED__ || mountLoadForRouter.current.router === router && mountLoadForRouter.current.mounted) {
122
- return;
123
- }
124
- mountLoadForRouter.current = { router, mounted: true };
125
- tryLoad();
126
- }, [router]);
115
+ }, [isAnyPending, previousIsAnyPending, router]);
127
116
  return null;
128
117
  }
129
118
  function getRouteMatch(state, id) {
@@ -133,6 +122,13 @@ function getRouteMatch(state, id) {
133
122
  ...state.matches
134
123
  ].find((d) => d.id === id);
135
124
  }
125
+ function usePrevious(value) {
126
+ const ref = React.useRef(value);
127
+ React.useEffect(() => {
128
+ ref.current = value;
129
+ });
130
+ return ref.current;
131
+ }
136
132
  export {
137
133
  RouterProvider,
138
134
  getRouteMatch
@@ -1 +1 @@
1
- {"version":3,"file":"RouterProvider.js","sources":["../../src/RouterProvider.tsx"],"sourcesContent":["import * as React from 'react'\nimport { Matches } from './Matches'\nimport { pick, useLayoutEffect } from './utils'\nimport { useRouter } from './useRouter'\nimport { useRouterState } from './useRouterState'\nimport { getRouterContext } from './routerContext'\nimport type { NavigateOptions, ToOptions } from './link'\nimport type { ParsedLocation } from './location'\nimport type { AnyRoute } from './route'\nimport type { RoutePaths } from './routeInfo'\nimport type {\n RegisteredRouter,\n Router,\n RouterOptions,\n RouterState,\n} from './router'\n\nimport type { RouteMatch } from './Matches'\n\nconst useTransition =\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n React.useTransition ||\n (() => [\n false,\n (cb) => {\n cb()\n },\n ])\n\nexport interface CommitLocationOptions {\n replace?: boolean\n resetScroll?: boolean\n startTransition?: boolean\n}\n\nexport interface MatchLocation {\n to?: string | number | null\n fuzzy?: boolean\n caseSensitive?: boolean\n from?: string\n}\n\nexport type NavigateFn = <\n TTo extends string,\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n>(\n opts: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,\n) => Promise<void>\n\nexport type BuildLocationFn<TRouteTree extends AnyRoute> = <\n TTo extends string,\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n>(\n opts: ToOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {\n leaveParams?: boolean\n },\n) => ParsedLocation\n\nexport type InjectedHtmlEntry = string | (() => Promise<string> | string)\n\nexport function RouterProvider<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TDehydrated extends Record<string, any> = Record<string, any>,\n>({ router, ...rest }: RouterProps<TRouteTree, TDehydrated>) {\n // Allow the router to update options on the router instance\n router.update({\n ...router.options,\n ...rest,\n context: {\n ...router.options.context,\n ...rest.context,\n },\n } as any)\n\n const matches = router.options.InnerWrap ? (\n <router.options.InnerWrap>\n <Matches />\n </router.options.InnerWrap>\n ) : (\n <Matches />\n )\n\n const routerContext = getRouterContext()\n\n const provider = (\n <React.Suspense fallback={null}>\n <routerContext.Provider value={router}>\n {matches}\n <Transitioner />\n </routerContext.Provider>\n </React.Suspense>\n )\n\n if (router.options.Wrap) {\n return <router.options.Wrap>{provider}</router.options.Wrap>\n }\n\n return provider\n}\n\nfunction Transitioner() {\n const router = useRouter()\n const mountLoadForRouter = React.useRef({ router, mounted: false })\n const routerState = useRouterState({\n select: (s) =>\n pick(s, ['isLoading', 'location', 'resolvedLocation', 'isTransitioning']),\n })\n\n const [isTransitioning, startReactTransition] = useTransition()\n\n router.startReactTransition = startReactTransition\n\n React.useEffect(() => {\n if (isTransitioning) {\n router.__store.setState((s) => ({\n ...s,\n isTransitioning,\n }))\n }\n }, [isTransitioning, router])\n\n const tryLoad = () => {\n const apply = (cb: () => void) => {\n if (!routerState.isTransitioning) {\n startReactTransition(() => cb())\n } else {\n cb()\n }\n }\n\n apply(() => {\n try {\n router.load()\n } catch (err) {\n console.error(err)\n }\n })\n }\n\n useLayoutEffect(() => {\n const unsub = router.history.subscribe(() => {\n router.latestLocation = router.parseLocation(router.latestLocation)\n if (router.state.location !== router.latestLocation) {\n tryLoad()\n }\n })\n\n const nextLocation = router.buildLocation({\n to: router.latestLocation.pathname,\n search: true,\n params: true,\n hash: true,\n state: true,\n })\n\n if (routerState.location.href !== nextLocation.href) {\n router.commitLocation({ ...nextLocation, replace: true })\n }\n\n return () => {\n unsub()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [router, router.history])\n\n useLayoutEffect(() => {\n if (\n (React.useTransition as any)\n ? routerState.isTransitioning && !isTransitioning\n : !routerState.isLoading &&\n routerState.resolvedLocation !== routerState.location\n ) {\n router.emit({\n type: 'onResolved',\n fromLocation: routerState.resolvedLocation,\n toLocation: routerState.location,\n pathChanged:\n routerState.location.href !== routerState.resolvedLocation.href,\n })\n\n if ((document as any).querySelector) {\n if (routerState.location.hash !== '') {\n const el = document.getElementById(routerState.location.hash)\n if (el) {\n el.scrollIntoView()\n }\n }\n }\n\n router.__store.setState((s) => ({\n ...s,\n isTransitioning: false,\n resolvedLocation: s.location,\n }))\n }\n }, [\n routerState.isTransitioning,\n isTransitioning,\n routerState.isLoading,\n routerState.resolvedLocation,\n routerState.location,\n router,\n ])\n\n useLayoutEffect(() => {\n if (\n window.__TSR_DEHYDRATED__ ||\n (mountLoadForRouter.current.router === router &&\n mountLoadForRouter.current.mounted)\n ) {\n return\n }\n mountLoadForRouter.current = { router, mounted: true }\n tryLoad()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [router])\n\n return null\n}\n\nexport function getRouteMatch<TRouteTree extends AnyRoute>(\n state: RouterState<TRouteTree>,\n id: string,\n): undefined | RouteMatch<TRouteTree> {\n return [\n ...state.cachedMatches,\n ...(state.pendingMatches ?? []),\n ...state.matches,\n ].find((d) => d.id === id)\n}\n\nexport type RouterProps<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TDehydrated extends Record<string, any> = Record<string, any>,\n> = Omit<RouterOptions<TRouteTree, TDehydrated>, 'context'> & {\n router: Router<TRouteTree>\n context?: Partial<RouterOptions<TRouteTree, TDehydrated>['context']>\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RouterProvider.js","sources":["../../src/RouterProvider.tsx"],"sourcesContent":["import * as React from 'react'\nimport { flushSync } from 'react-dom'\nimport { Matches } from './Matches'\nimport { pick, useLayoutEffect } from './utils'\nimport { useRouter } from './useRouter'\nimport { useRouterState } from './useRouterState'\nimport { getRouterContext } from './routerContext'\nimport type { NavigateOptions, ToOptions } from './link'\nimport type { ParsedLocation } from './location'\nimport type { AnyRoute } from './route'\nimport type { RoutePaths } from './routeInfo'\nimport type {\n RegisteredRouter,\n Router,\n RouterOptions,\n RouterState,\n} from './router'\n\nimport type { RouteMatch } from './Matches'\n\nexport interface CommitLocationOptions {\n replace?: boolean\n resetScroll?: boolean\n viewTransition?: boolean\n /**\n * @deprecated All navigations use React transitions under the hood now\n **/\n startTransition?: boolean\n}\n\nexport interface MatchLocation {\n to?: string | number | null\n fuzzy?: boolean\n caseSensitive?: boolean\n from?: string\n}\n\nexport type NavigateFn = <\n TTo extends string,\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n>(\n opts: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,\n) => Promise<void>\n\nexport type BuildLocationFn<TRouteTree extends AnyRoute> = <\n TTo extends string,\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n>(\n opts: ToOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {\n leaveParams?: boolean\n },\n) => ParsedLocation\n\nexport type InjectedHtmlEntry = string | (() => Promise<string> | string)\n\nexport function RouterProvider<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TDehydrated extends Record<string, any> = Record<string, any>,\n>({ router, ...rest }: RouterProps<TRouteTree, TDehydrated>) {\n // Allow the router to update options on the router instance\n router.update({\n ...router.options,\n ...rest,\n context: {\n ...router.options.context,\n ...rest.context,\n },\n } as any)\n\n const matches = router.options.InnerWrap ? (\n <router.options.InnerWrap>\n <Matches />\n </router.options.InnerWrap>\n ) : (\n <Matches />\n )\n\n const routerContext = getRouterContext()\n\n const provider = (\n <React.Suspense fallback={null}>\n <routerContext.Provider value={router}>\n {matches}\n <Transitioner />\n </routerContext.Provider>\n </React.Suspense>\n )\n\n if (router.options.Wrap) {\n return <router.options.Wrap>{provider}</router.options.Wrap>\n }\n\n return provider\n}\n\nfunction Transitioner() {\n const router = useRouter()\n const mountLoadForRouter = React.useRef({ router, mounted: false })\n const routerState = useRouterState({\n select: (s) =>\n pick(s, ['isLoading', 'location', 'resolvedLocation', 'isTransitioning']),\n })\n\n const [isTransitioning, startReactTransition_] = React.useTransition()\n // Track pending state changes\n const hasPendingMatches = useRouterState({\n select: (s) => s.matches.some((d) => d.status === 'pending'),\n })\n\n const previousIsLoading = usePrevious(routerState.isLoading)\n\n const isAnyPending =\n routerState.isLoading || isTransitioning || hasPendingMatches\n const previousIsAnyPending = usePrevious(isAnyPending)\n\n router.startReactTransition = startReactTransition_\n\n const tryLoad = () => {\n try {\n router.load()\n } catch (err) {\n console.error(err)\n }\n }\n\n // Subscribe to location changes\n // and try to load the new location\n useLayoutEffect(() => {\n const unsub = router.history.subscribe(() => {\n router.latestLocation = router.parseLocation(router.latestLocation)\n if (router.state.location !== router.latestLocation) {\n tryLoad()\n }\n })\n\n const nextLocation = router.buildLocation({\n to: router.latestLocation.pathname,\n search: true,\n params: true,\n hash: true,\n state: true,\n })\n\n if (routerState.location.href !== nextLocation.href) {\n router.commitLocation({ ...nextLocation, replace: true })\n }\n\n return () => {\n unsub()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [router, router.history])\n\n // Try to load the initial location\n useLayoutEffect(() => {\n if (\n window.__TSR_DEHYDRATED__ ||\n (mountLoadForRouter.current.router === router &&\n mountLoadForRouter.current.mounted)\n ) {\n return\n }\n mountLoadForRouter.current = { router, mounted: true }\n tryLoad()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [router])\n\n useLayoutEffect(() => {\n // The router was loading and now it's not\n if (previousIsLoading && !routerState.isLoading) {\n const toLocation = router.state.location\n const fromLocation = router.state.resolvedLocation\n const pathChanged = fromLocation.href !== toLocation.href\n\n router.emit({\n type: 'onLoad',\n fromLocation,\n toLocation,\n pathChanged,\n })\n\n // if (router.viewTransitionPromise) {\n // console.log('resolving view transition promise')\n // }\n\n // router.viewTransitionPromise?.resolve(true)\n }\n }, [previousIsLoading, router, routerState.isLoading])\n\n useLayoutEffect(() => {\n // The router was pending and now it's not\n if (previousIsAnyPending && !isAnyPending) {\n const toLocation = router.state.location\n const fromLocation = router.state.resolvedLocation\n const pathChanged = fromLocation.href !== toLocation.href\n\n router.emit({\n type: 'onResolved',\n fromLocation,\n toLocation,\n pathChanged,\n })\n\n router.__store.setState((s) => ({\n ...s,\n status: 'idle',\n resolvedLocation: s.location,\n }))\n\n if ((document as any).querySelector) {\n if (router.state.location.hash !== '') {\n const el = document.getElementById(router.state.location.hash)\n if (el) {\n el.scrollIntoView()\n }\n }\n }\n }\n }, [isAnyPending, previousIsAnyPending, router])\n\n return null\n}\n\nexport function getRouteMatch<TRouteTree extends AnyRoute>(\n state: RouterState<TRouteTree>,\n id: string,\n): undefined | RouteMatch<TRouteTree> {\n return [\n ...state.cachedMatches,\n ...(state.pendingMatches ?? []),\n ...state.matches,\n ].find((d) => d.id === id)\n}\n\nexport type RouterProps<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TDehydrated extends Record<string, any> = Record<string, any>,\n> = Omit<RouterOptions<TRouteTree, TDehydrated>, 'context'> & {\n router: Router<TRouteTree>\n context?: Partial<RouterOptions<TRouteTree, TDehydrated>['context']>\n}\n\nfunction usePrevious<T>(value: T) {\n const ref = React.useRef<T>(value)\n React.useEffect(() => {\n ref.current = value\n })\n return ref.current\n}\n"],"names":[],"mappings":"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"}
@@ -28,7 +28,9 @@ export type RelativeToPathAutoComplete<TRouteTree extends AnyRoute, TFrom extend
28
28
  export type NavigateOptions<TRouteTree extends AnyRoute = RegisteredRouter['routeTree'], TFrom extends RoutePaths<TRouteTree> | string = string, TTo extends string = '', TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom, TMaskTo extends string = ''> = ToOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {
29
29
  replace?: boolean;
30
30
  resetScroll?: boolean;
31
+ /** @deprecated All navigations now use startTransition under the hood */
31
32
  startTransition?: boolean;
33
+ viewTransition?: boolean;
32
34
  };
33
35
  export type ToOptions<TRouteTree extends AnyRoute = RegisteredRouter['routeTree'], TFrom extends RoutePaths<TRouteTree> | string = string, TTo extends string = '', TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom, TMaskTo extends string = ''> = ToSubOptions<TRouteTree, TFrom, TTo> & {
34
36
  _fromLocation?: ParsedLocation;
@@ -93,6 +95,7 @@ export type ActiveLinkOptions<TRouteTree extends AnyRoute = RegisteredRouter['ro
93
95
  export type LinkProps<TRouteTree extends AnyRoute = RegisteredRouter['routeTree'], TFrom extends RoutePaths<TRouteTree> | string = string, TTo extends string = string, TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom, TMaskTo extends string = ''> = ActiveLinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {
94
96
  children?: React.ReactNode | ((state: {
95
97
  isActive: boolean;
98
+ isTransitioning: boolean;
96
99
  }) => React.ReactNode);
97
100
  };
98
101
  type LinkComponentProps<TComp> = React.PropsWithoutRef<TComp extends React.FC<infer TProps> | React.Component<infer TProps> ? TProps : TComp extends keyof JSX.IntrinsicElements ? Omit<React.HTMLProps<TComp>, 'children' | 'preload'> : never> & React.RefAttributes<TComp extends React.FC<{
package/dist/esm/link.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
+ import { flushSync } from "react-dom";
3
4
  import { useMatch } from "./Matches.js";
4
5
  import { useRouterState } from "./useRouterState.js";
5
6
  import { useRouter } from "./useRouter.js";
@@ -11,6 +12,7 @@ function useLinkProps(options) {
11
12
  strict: false,
12
13
  select: (s) => s.pathname
13
14
  });
15
+ const [isTransitioning, setIsTransitioning] = React.useState(false);
14
16
  const {
15
17
  // custom props
16
18
  activeProps = () => ({ className: "active" }),
@@ -27,6 +29,7 @@ function useLinkProps(options) {
27
29
  replace,
28
30
  startTransition,
29
31
  resetScroll,
32
+ viewTransition,
30
33
  // element props
31
34
  children,
32
35
  target,
@@ -86,15 +89,26 @@ function useLinkProps(options) {
86
89
  const handleClick = (e) => {
87
90
  if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === "_self") && e.button === 0) {
88
91
  e.preventDefault();
89
- router.commitLocation({ ...next, replace, resetScroll, startTransition });
92
+ flushSync(() => {
93
+ setIsTransitioning(true);
94
+ });
95
+ const unsub = router.subscribe("onResolved", () => {
96
+ unsub();
97
+ setIsTransitioning(false);
98
+ });
99
+ router.commitLocation({
100
+ ...next,
101
+ replace,
102
+ resetScroll,
103
+ startTransition,
104
+ viewTransition
105
+ });
90
106
  }
91
107
  };
92
108
  const doPreload = () => {
93
- React.startTransition(() => {
94
- router.preloadRoute(dest).catch((err) => {
95
- console.warn(err);
96
- console.warn(preloadWarning);
97
- });
109
+ router.preloadRoute(dest).catch((err) => {
110
+ console.warn(err);
111
+ console.warn(preloadWarning);
98
112
  });
99
113
  };
100
114
  const handleFocus = (e) => {
@@ -166,7 +180,8 @@ function useLinkProps(options) {
166
180
  role: "link",
167
181
  "aria-disabled": true
168
182
  },
169
- ...isActive && { "data-status": "active", "aria-current": "page" }
183
+ ...isActive && { "data-status": "active", "aria-current": "page" },
184
+ ...isTransitioning && { "data-transitioning": "transitioning" }
170
185
  };
171
186
  }
172
187
  function createLink(Comp) {
@@ -1 +1 @@
1
- {"version":3,"file":"link.js","sources":["../../src/link.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useMatch } from './Matches'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\nimport { deepEqual, exactPathTest, functionalUpdate } from './utils'\nimport type { ParsedLocation } from '.'\nimport type { HistoryState } from '@tanstack/history'\nimport type { Trim } from './fileRoute'\nimport type { AnyRoute, RootSearchSchema } from './route'\nimport type {\n RouteByPath,\n RoutePaths,\n RoutePathsAutoComplete,\n} from './routeInfo'\nimport type { RegisteredRouter } from './router'\nimport type {\n Expand,\n IsUnion,\n MakeDifferenceOptional,\n NoInfer,\n NonNullableUpdater,\n PickRequired,\n Updater,\n WithoutEmpty,\n} from './utils'\n\nexport type CleanPath<T extends string> = T extends `${infer L}//${infer R}`\n ? CleanPath<`${CleanPath<L>}/${CleanPath<R>}`>\n : T extends `${infer L}//`\n ? `${CleanPath<L>}/`\n : T extends `//${infer L}`\n ? `/${CleanPath<L>}`\n : T\n\nexport type Split<TValue, TIncludeTrailingSlash = true> = TValue extends unknown\n ? string extends TValue\n ? Array<string>\n : TValue extends string\n ? CleanPath<TValue> extends ''\n ? []\n : TIncludeTrailingSlash extends true\n ? CleanPath<TValue> extends `${infer T}/`\n ? [...Split<T>, '/']\n : CleanPath<TValue> extends `/${infer U}`\n ? Split<U>\n : CleanPath<TValue> extends `${infer T}/${infer U}`\n ? [...Split<T>, ...Split<U>]\n : [TValue]\n : CleanPath<TValue> extends `${infer T}/${infer U}`\n ? [...Split<T>, ...Split<U>]\n : TValue extends string\n ? [TValue]\n : never\n : never\n : never\n\nexport type ParsePathParams<T extends string> = keyof {\n [K in Trim<Split<T>[number], '_'> as K extends `$${infer L}`\n ? L extends ''\n ? '_splat'\n : L\n : never]: K\n}\n\nexport type Join<T, TDelimiter extends string = '/'> = T extends []\n ? ''\n : T extends [infer L extends string]\n ? L\n : T extends [\n infer L extends string,\n ...infer Tail extends [...Array<string>],\n ]\n ? CleanPath<`${L}${TDelimiter}${Join<Tail>}`>\n : never\n\nexport type Last<T extends Array<any>> = T extends [...infer _, infer L]\n ? L\n : never\n\nexport type RemoveTrailingSlashes<T> = T extends `${infer R}/`\n ? RemoveTrailingSlashes<R>\n : T\n\nexport type RemoveLeadingSlashes<T> = T extends `/${infer R}`\n ? RemoveLeadingSlashes<R>\n : T\n\nexport type ResolvePaths<TRouteTree extends AnyRoute, TSearchPath> =\n RouteByPath<TRouteTree, RemoveTrailingSlashes<TSearchPath>> extends never\n ? RoutePaths<TRouteTree>\n : RoutePaths<RouteByPath<TRouteTree, RemoveTrailingSlashes<TSearchPath>>>\n\nexport type SearchPaths<\n TRouteTree extends AnyRoute,\n TSearchPath extends string,\n TPaths = ResolvePaths<TRouteTree, TSearchPath>,\n> = TPaths extends `${RemoveTrailingSlashes<TSearchPath>}/${infer TRest}`\n ? TRest\n : never\n\nexport type SearchRelativePathAutoComplete<\n TRouteTree extends AnyRoute,\n TTo extends string,\n TSearchPath extends string,\n> = `${TTo}/${SearchPaths<TRouteTree, TSearchPath>}`\n\nexport type RelativeToParentPathAutoComplete<\n TRouteTree extends AnyRoute,\n TFrom extends string,\n TTo extends string,\n TResolvedPath extends string = RemoveTrailingSlashes<\n ResolveRelativePath<TFrom, TTo>\n >,\n> =\n | SearchRelativePathAutoComplete<TRouteTree, TTo, TResolvedPath>\n | (TResolvedPath extends '' ? never : `${TTo}/../`)\n\nexport type RelativeToCurrentPathAutoComplete<\n TRouteTree extends AnyRoute,\n TFrom extends string,\n TTo extends string,\n TRestTo extends string,\n TResolvedPath extends\n string = RemoveTrailingSlashes<`${RemoveTrailingSlashes<TFrom>}/${RemoveLeadingSlashes<TRestTo>}`>,\n> = SearchRelativePathAutoComplete<TRouteTree, TTo, TResolvedPath>\n\nexport type AbsolutePathAutoComplete<\n TRouteTree extends AnyRoute,\n TFrom extends string,\n> =\n | (string extends TFrom\n ? './'\n : TFrom extends `/`\n ? never\n : SearchPaths<TRouteTree, TFrom> extends ''\n ? never\n : './')\n | (string extends TFrom ? '../' : TFrom extends `/` ? never : '../')\n | RoutePaths<TRouteTree>\n | (TFrom extends '/' ? never : SearchPaths<TRouteTree, TFrom>)\n\nexport type RelativeToPathAutoComplete<\n TRouteTree extends AnyRoute,\n TFrom extends string,\n TTo extends string,\n> = TTo extends `..${string}`\n ? RelativeToParentPathAutoComplete<\n TRouteTree,\n TFrom,\n RemoveTrailingSlashes<TTo>\n >\n : TTo extends `./${infer TRestTTo}`\n ? RelativeToCurrentPathAutoComplete<\n TRouteTree,\n TFrom,\n RemoveTrailingSlashes<TTo>,\n TRestTTo\n >\n : AbsolutePathAutoComplete<TRouteTree, TFrom>\n\nexport type NavigateOptions<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n> = ToOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {\n // `replace` is a boolean that determines whether the navigation should replace the current history entry or push a new one.\n replace?: boolean\n resetScroll?: boolean\n // If set to `true`, the link's underlying navigate() call will be wrapped in a `React.startTransition` call. Defaults to `true`.\n startTransition?: boolean\n}\n\nexport type ToOptions<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n> = ToSubOptions<TRouteTree, TFrom, TTo> & {\n _fromLocation?: ParsedLocation\n mask?: ToMaskOptions<TRouteTree, TMaskFrom, TMaskTo>\n}\n\nexport type ToMaskOptions<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TMaskFrom extends RoutePaths<TRouteTree> | string = string,\n TMaskTo extends string = '',\n> = ToSubOptions<TRouteTree, TMaskFrom, TMaskTo> & {\n unmaskOnReload?: boolean\n}\n\nexport type ToSubOptions<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = '',\n> = {\n to?: ToPathOption<TRouteTree, TFrom, TTo> & {}\n hash?: true | Updater<string>\n state?: true | NonNullableUpdater<HistoryState>\n // The source route path. This is automatically set when using route-level APIs, but for type-safe relative routing on the router itself, this is required\n from?: RoutePathsAutoComplete<TRouteTree, TFrom> & {}\n // // When using relative route paths, this option forces resolution from the current path, instead of the route API's path or `from` path\n} & SearchParamOptions<TRouteTree, TFrom, TTo> &\n PathParamOptions<TRouteTree, TFrom, TTo>\n\ntype ParamsReducer<TFrom, TTo> = TTo | ((current: TFrom) => TTo)\n\ntype ParamVariant = 'PATH' | 'SEARCH'\n\ntype ExcludeRootSearchSchema<T, TExcluded = Exclude<T, RootSearchSchema>> = [\n TExcluded,\n] extends [never]\n ? {}\n : TExcluded\n\nexport type ResolveRoute<\n TRouteTree extends AnyRoute,\n TFrom,\n TTo,\n TPath = RemoveTrailingSlashes<\n string extends TFrom\n ? TTo\n : string extends TTo\n ? TFrom\n : ResolveRelativePath<TFrom, TTo>\n >,\n> = TPath extends string\n ? RouteByPath<TRouteTree, `${TPath}/`> extends never\n ? RouteByPath<TRouteTree, TPath>\n : RouteByPath<TRouteTree, `${TPath}/`>\n : never\n\ntype PostProcessParams<\n T,\n TParamVariant extends ParamVariant,\n> = TParamVariant extends 'SEARCH' ? ExcludeRootSearchSchema<T> : T\n\ntype ResolveFromParams<\n TRouteTree extends AnyRoute,\n TParamVariant extends ParamVariant,\n TFrom,\n TFromRouteType extends\n | 'allParams'\n | 'fullSearchSchema' = TParamVariant extends 'PATH'\n ? 'allParams'\n : 'fullSearchSchema',\n> = PostProcessParams<\n RouteByPath<TRouteTree, TFrom>['types'][TFromRouteType],\n TParamVariant\n>\n\ntype ResolveToParams<\n TRouteTree extends AnyRoute,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n TRoute extends AnyRoute = ResolveRoute<TRouteTree, TFrom, TTo>,\n> = PostProcessParams<\n TRoute['types'][TParamVariant extends 'PATH'\n ? 'allParams'\n : 'fullSearchSchemaInput'],\n TParamVariant\n>\n\ntype ResolveRelativeToParams<\n TRouteTree extends AnyRoute,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n TToParams = ResolveToParams<TRouteTree, TParamVariant, TFrom, TTo>,\n> = TParamVariant extends 'SEARCH'\n ? TToParams\n : string extends TFrom\n ? TToParams\n : MakeDifferenceOptional<\n ResolveFromParams<TRouteTree, TParamVariant, TFrom>,\n TToParams\n >\n\ntype MakeOptionalParams<\n TRouteTree extends AnyRoute,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n> = TParamVariant extends 'SEARCH'\n ? {\n search?:\n | true\n | (ParamsReducer<\n Expand<ResolveFromParams<TRouteTree, TParamVariant, TFrom>>,\n Expand<\n ResolveRelativeToParams<TRouteTree, TParamVariant, TFrom, TTo>\n >\n > & {})\n }\n : {\n params?:\n | true\n | (ParamsReducer<\n Expand<ResolveFromParams<TRouteTree, TParamVariant, TFrom>>,\n Expand<\n ResolveRelativeToParams<TRouteTree, TParamVariant, TFrom, TTo>\n >\n > & {})\n }\n\ntype MakeRequiredParamsReducer<\n TRouteTree extends AnyRoute,\n TParamVariant extends ParamVariant,\n TFrom,\n TToParams,\n TFromParams = ResolveFromParams<TRouteTree, TParamVariant, TFrom>,\n> =\n | ([TFromParams] extends [WithoutEmpty<PickRequired<TToParams>>]\n ? true\n : never)\n | ParamsReducer<Expand<TFromParams>, TToParams>\n\nexport type MakeRequiredParams<\n TRouteTree extends AnyRoute,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n> = TParamVariant extends 'SEARCH'\n ? {\n search: Expand<\n MakeRequiredParamsReducer<\n TRouteTree,\n TParamVariant,\n TFrom,\n Expand<ResolveRelativeToParams<TRouteTree, TParamVariant, TFrom, TTo>>\n >\n > & {}\n }\n : {\n params: Expand<\n MakeRequiredParamsReducer<\n TRouteTree,\n TParamVariant,\n TFrom,\n Expand<ResolveRelativeToParams<TRouteTree, TParamVariant, TFrom, TTo>>\n >\n > & {}\n }\n\nexport type IsRequiredParams<TParams> = keyof TParams extends infer K extends\n keyof TParams\n ? K extends any\n ? undefined extends TParams[K]\n ? never\n : true\n : never\n : never\n\nexport type IsRequired<\n TRouteTree extends AnyRoute,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n> = string extends TTo\n ? string extends TFrom\n ? never\n : IsRequiredParams<\n ResolveRelativeToParams<TRouteTree, TParamVariant, TFrom, TTo>\n >\n : IsRequiredParams<\n ResolveRelativeToParams<TRouteTree, TParamVariant, TFrom, TTo>\n >\n\nexport type ParamOptions<\n TRouteTree extends AnyRoute,\n TFrom,\n TTo extends string,\n TParamVariant extends ParamVariant,\n> =\n IsRequired<TRouteTree, TParamVariant, TFrom, TTo> extends never\n ? MakeOptionalParams<TRouteTree, TParamVariant, TFrom, TTo>\n : MakeRequiredParams<TRouteTree, TParamVariant, TFrom, TTo>\n\nexport type SearchParamOptions<\n TRouteTree extends AnyRoute,\n TFrom,\n TTo extends string,\n> = ParamOptions<TRouteTree, TFrom, TTo, 'SEARCH'>\n\nexport type PathParamOptions<\n TRouteTree extends AnyRoute,\n TFrom,\n TTo extends string,\n> = ParamOptions<TRouteTree, TFrom, TTo, 'PATH'>\n\nexport type ToPathOption<\n TRouteTree extends AnyRoute = AnyRoute,\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = string,\n> =\n | CheckPath<TRouteTree, TTo, never, TFrom, TTo>\n | RelativeToPathAutoComplete<\n TRouteTree,\n NoInfer<TFrom> extends string ? NoInfer<TFrom> : '',\n NoInfer<TTo> & string\n >\n\nexport interface ActiveOptions {\n exact?: boolean\n includeHash?: boolean\n includeSearch?: boolean\n}\n\nexport type LinkOptions<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n> = NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {\n // The standard anchor tag target attribute\n target?: HTMLAnchorElement['target']\n // Defaults to `{ exact: false, includeHash: false }`\n activeOptions?: ActiveOptions\n // If set, will preload the linked route on hover and cache it for this many milliseconds in hopes that the user will eventually navigate there.\n preload?: false | 'intent'\n // Delay intent preloading by this many milliseconds. If the intent exits before this delay, the preload will be cancelled.\n preloadDelay?: number\n // If true, will render the link without the href attribute\n disabled?: boolean\n}\n\nexport type CheckPath<TRouteTree extends AnyRoute, TPass, TFail, TFrom, TTo> =\n ResolveRoute<TRouteTree, TFrom, TTo> extends never ? TFail : TPass\n\nexport type ResolveRelativePath<TFrom, TTo = '.'> = TFrom extends string\n ? TTo extends string\n ? TTo extends '.'\n ? TFrom\n : TTo extends `./`\n ? Join<[TFrom, '/']>\n : TTo extends `./${infer TRest}`\n ? ResolveRelativePath<TFrom, TRest>\n : TTo extends `/${infer TRest}`\n ? TTo\n : Split<TTo> extends ['..', ...infer ToRest]\n ? Split<TFrom> extends [...infer FromRest, infer FromTail]\n ? ToRest extends ['/']\n ? Join<['/', ...FromRest, '/']>\n : ResolveRelativePath<Join<FromRest>, Join<ToRest>>\n : never\n : Split<TTo> extends ['.', ...infer ToRest]\n ? ToRest extends ['/']\n ? Join<[TFrom, '/']>\n : ResolveRelativePath<TFrom, Join<ToRest>>\n : CleanPath<Join<['/', ...Split<TFrom>, ...Split<TTo>]>>\n : never\n : never\n\n// type Test1 = ResolveRelativePath<'/', '/posts'>\n// // ^?\n// type Test4 = ResolveRelativePath<'/posts/1/comments', '../..'>\n// // ^?\n// type Test5 = ResolveRelativePath<'/posts/1/comments', '../../..'>\n// // ^?\n// type Test6 = ResolveRelativePath<'/posts/1/comments', './1'>\n// // ^?\n// type Test7 = ResolveRelativePath<'/posts/1/comments', './1/2'>\n// // ^?\n// type Test8 = ResolveRelativePath<'/posts/1/comments', '../edit'>\n// // ^?\n// type Test9 = ResolveRelativePath<'/posts/1/comments', '1'>\n// // ^?\n// type Test10 = ResolveRelativePath<'/posts/1/comments', './1'>\n// // ^?\n// type Test11 = ResolveRelativePath<'/posts/1/comments', './1/2'>\n// // ^?\n\ntype LinkCurrentTargetElement = {\n preloadTimeout?: null | ReturnType<typeof setTimeout>\n}\n\nconst preloadWarning = 'Error preloading route! ☝️'\n\nexport function useLinkProps<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n>(\n options: UseLinkPropsOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,\n): React.AnchorHTMLAttributes<HTMLAnchorElement> {\n const router = useRouter()\n const matchPathname = useMatch({\n strict: false,\n select: (s) => s.pathname,\n })\n\n const {\n // custom props\n activeProps = () => ({ className: 'active' }),\n inactiveProps = () => ({}),\n activeOptions,\n hash,\n search,\n params,\n to,\n state,\n mask,\n preload: userPreload,\n preloadDelay: userPreloadDelay,\n replace,\n startTransition,\n resetScroll,\n // element props\n children,\n target,\n disabled,\n style,\n className,\n onClick,\n onFocus,\n onMouseEnter,\n onMouseLeave,\n onTouchStart,\n ...rest\n } = options\n\n // If this link simply reloads the current route,\n // make sure it has a new key so it will trigger a data refresh\n\n // If this `to` is a valid external URL, return\n // null for LinkUtils\n\n const dest = {\n ...(options.to && { from: matchPathname }),\n ...options,\n }\n\n let type: 'internal' | 'external' = 'internal'\n\n try {\n new URL(`${to}`)\n type = 'external'\n } catch {}\n\n const next = router.buildLocation(dest as any)\n const preload = userPreload ?? router.options.defaultPreload\n const preloadDelay =\n userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0\n\n const isActive = useRouterState({\n select: (s) => {\n // Compare path/hash for matches\n const currentPathSplit = s.location.pathname.split('/')\n const nextPathSplit = next.pathname.split('/')\n const pathIsFuzzyEqual = nextPathSplit.every(\n (d, i) => d === currentPathSplit[i],\n )\n // Combine the matches based on user router.options\n const pathTest = activeOptions?.exact\n ? exactPathTest(s.location.pathname, next.pathname)\n : pathIsFuzzyEqual\n const hashTest = activeOptions?.includeHash\n ? s.location.hash === next.hash\n : true\n const searchTest =\n activeOptions?.includeSearch ?? true\n ? deepEqual(s.location.search, next.search, !activeOptions?.exact)\n : true\n\n // The final \"active\" test\n return pathTest && hashTest && searchTest\n },\n })\n\n if (type === 'external') {\n return {\n ...rest,\n type,\n href: to,\n ...(children && { children }),\n ...(target && { target }),\n ...(disabled && { disabled }),\n ...(style && { style }),\n ...(className && { className }),\n ...(onClick && { onClick }),\n ...(onFocus && { onFocus }),\n ...(onMouseEnter && { onMouseEnter }),\n ...(onMouseLeave && { onMouseLeave }),\n ...(onTouchStart && { onTouchStart }),\n }\n }\n\n // The click handler\n const handleClick = (e: MouseEvent) => {\n if (\n !disabled &&\n !isCtrlEvent(e) &&\n !e.defaultPrevented &&\n (!target || target === '_self') &&\n e.button === 0\n ) {\n e.preventDefault()\n\n // All is well? Navigate!\n router.commitLocation({ ...next, replace, resetScroll, startTransition })\n }\n }\n\n const doPreload = () => {\n React.startTransition(() => {\n router.preloadRoute(dest as any).catch((err) => {\n console.warn(err)\n console.warn(preloadWarning)\n })\n })\n }\n\n // The click handler\n const handleFocus = (e: MouseEvent) => {\n if (disabled) return\n if (preload) {\n doPreload()\n }\n }\n\n const handleTouchStart = handleFocus\n\n const handleEnter = (e: MouseEvent) => {\n if (disabled) return\n const eventTarget = (e.target || {}) as LinkCurrentTargetElement\n\n if (preload) {\n if (eventTarget.preloadTimeout) {\n return\n }\n\n eventTarget.preloadTimeout = setTimeout(() => {\n eventTarget.preloadTimeout = null\n doPreload()\n }, preloadDelay)\n }\n }\n\n const handleLeave = (e: MouseEvent) => {\n if (disabled) return\n const eventTarget = (e.target || {}) as LinkCurrentTargetElement\n\n if (eventTarget.preloadTimeout) {\n clearTimeout(eventTarget.preloadTimeout)\n eventTarget.preloadTimeout = null\n }\n }\n\n const composeHandlers =\n (handlers: Array<undefined | ((e: any) => void)>) =>\n (e: { persist?: () => void; defaultPrevented: boolean }) => {\n e.persist?.()\n handlers.filter(Boolean).forEach((handler) => {\n if (e.defaultPrevented) return\n handler!(e)\n })\n }\n\n // Get the active props\n const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> = isActive\n ? functionalUpdate(activeProps as any, {}) ?? {}\n : {}\n\n // Get the inactive props\n const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =\n isActive ? {} : functionalUpdate(inactiveProps, {})\n\n const resolvedClassName = [\n className,\n resolvedActiveProps.className,\n resolvedInactiveProps.className,\n ]\n .filter(Boolean)\n .join(' ')\n\n const resolvedStyle = {\n ...style,\n ...resolvedActiveProps.style,\n ...resolvedInactiveProps.style,\n }\n\n return {\n ...resolvedActiveProps,\n ...resolvedInactiveProps,\n ...rest,\n href: disabled\n ? undefined\n : next.maskedLocation\n ? router.history.createHref(next.maskedLocation.href)\n : router.history.createHref(next.href),\n onClick: composeHandlers([onClick, handleClick]),\n onFocus: composeHandlers([onFocus, handleFocus]),\n onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),\n onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),\n onTouchStart: composeHandlers([onTouchStart, handleTouchStart]),\n target,\n ...(Object.keys(resolvedStyle).length && { style: resolvedStyle }),\n ...(resolvedClassName && { className: resolvedClassName }),\n ...(disabled && {\n role: 'link',\n 'aria-disabled': true,\n }),\n ...(isActive && { 'data-status': 'active', 'aria-current': 'page' }),\n }\n}\n\nexport type UseLinkPropsOptions<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n> = ActiveLinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &\n React.AnchorHTMLAttributes<HTMLAnchorElement>\n\nexport type ActiveLinkOptions<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n> = LinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {\n // A function that returns additional props for the `active` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)\n activeProps?:\n | React.AnchorHTMLAttributes<HTMLAnchorElement>\n | (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)\n // A function that returns additional props for the `inactive` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)\n inactiveProps?:\n | React.AnchorHTMLAttributes<HTMLAnchorElement>\n | (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)\n}\n\nexport type LinkProps<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = string,\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n> = ActiveLinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {\n // If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns\n children?:\n | React.ReactNode\n | ((state: { isActive: boolean }) => React.ReactNode)\n}\n\ntype LinkComponentProps<TComp> = React.PropsWithoutRef<\n TComp extends React.FC<infer TProps> | React.Component<infer TProps>\n ? TProps\n : TComp extends keyof JSX.IntrinsicElements\n ? Omit<React.HTMLProps<TComp>, 'children' | 'preload'>\n : never\n> &\n React.RefAttributes<\n TComp extends\n | React.FC<{ ref: infer TRef }>\n | React.Component<{ ref: infer TRef }>\n ? TRef\n : TComp extends keyof JSX.IntrinsicElements\n ? React.ComponentRef<TComp>\n : never\n >\n\nexport type LinkComponent<TComp> = <\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n>(\n props: LinkProps<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &\n LinkComponentProps<TComp>,\n) => React.ReactElement\n\nexport function createLink<const TComp>(Comp: TComp): LinkComponent<TComp> {\n return React.forwardRef(function CreatedLink(props, ref) {\n return <Link {...(props as any)} _asChild={Comp} ref={ref} />\n }) as any\n}\n\nexport const Link: LinkComponent<'a'> = React.forwardRef((props: any, ref) => {\n const { _asChild, ...rest } = props\n const { type, ...linkProps } = useLinkProps(rest)\n\n const children =\n typeof rest.children === 'function'\n ? rest.children({\n isActive: (linkProps as any)['data-status'] === 'active',\n })\n : rest.children\n\n return React.createElement(\n _asChild ? _asChild : 'a',\n {\n ...linkProps,\n ref,\n },\n children,\n )\n}) as any\n\nfunction isCtrlEvent(e: MouseEvent) {\n return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"link.js","sources":["../../src/link.tsx"],"sourcesContent":["import * as React from 'react'\nimport { flushSync } from 'react-dom'\nimport { useMatch } from './Matches'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\nimport { deepEqual, exactPathTest, functionalUpdate } from './utils'\nimport type { ParsedLocation } from '.'\nimport type { HistoryState } from '@tanstack/history'\nimport type { Trim } from './fileRoute'\nimport type { AnyRoute, RootSearchSchema } from './route'\nimport type {\n RouteByPath,\n RoutePaths,\n RoutePathsAutoComplete,\n} from './routeInfo'\nimport type { RegisteredRouter } from './router'\nimport type {\n Expand,\n IsUnion,\n MakeDifferenceOptional,\n NoInfer,\n NonNullableUpdater,\n PickRequired,\n Updater,\n WithoutEmpty,\n} from './utils'\n\nexport type CleanPath<T extends string> = T extends `${infer L}//${infer R}`\n ? CleanPath<`${CleanPath<L>}/${CleanPath<R>}`>\n : T extends `${infer L}//`\n ? `${CleanPath<L>}/`\n : T extends `//${infer L}`\n ? `/${CleanPath<L>}`\n : T\n\nexport type Split<TValue, TIncludeTrailingSlash = true> = TValue extends unknown\n ? string extends TValue\n ? Array<string>\n : TValue extends string\n ? CleanPath<TValue> extends ''\n ? []\n : TIncludeTrailingSlash extends true\n ? CleanPath<TValue> extends `${infer T}/`\n ? [...Split<T>, '/']\n : CleanPath<TValue> extends `/${infer U}`\n ? Split<U>\n : CleanPath<TValue> extends `${infer T}/${infer U}`\n ? [...Split<T>, ...Split<U>]\n : [TValue]\n : CleanPath<TValue> extends `${infer T}/${infer U}`\n ? [...Split<T>, ...Split<U>]\n : TValue extends string\n ? [TValue]\n : never\n : never\n : never\n\nexport type ParsePathParams<T extends string> = keyof {\n [K in Trim<Split<T>[number], '_'> as K extends `$${infer L}`\n ? L extends ''\n ? '_splat'\n : L\n : never]: K\n}\n\nexport type Join<T, TDelimiter extends string = '/'> = T extends []\n ? ''\n : T extends [infer L extends string]\n ? L\n : T extends [\n infer L extends string,\n ...infer Tail extends [...Array<string>],\n ]\n ? CleanPath<`${L}${TDelimiter}${Join<Tail>}`>\n : never\n\nexport type Last<T extends Array<any>> = T extends [...infer _, infer L]\n ? L\n : never\n\nexport type RemoveTrailingSlashes<T> = T extends `${infer R}/`\n ? RemoveTrailingSlashes<R>\n : T\n\nexport type RemoveLeadingSlashes<T> = T extends `/${infer R}`\n ? RemoveLeadingSlashes<R>\n : T\n\nexport type ResolvePaths<TRouteTree extends AnyRoute, TSearchPath> =\n RouteByPath<TRouteTree, RemoveTrailingSlashes<TSearchPath>> extends never\n ? RoutePaths<TRouteTree>\n : RoutePaths<RouteByPath<TRouteTree, RemoveTrailingSlashes<TSearchPath>>>\n\nexport type SearchPaths<\n TRouteTree extends AnyRoute,\n TSearchPath extends string,\n TPaths = ResolvePaths<TRouteTree, TSearchPath>,\n> = TPaths extends `${RemoveTrailingSlashes<TSearchPath>}/${infer TRest}`\n ? TRest\n : never\n\nexport type SearchRelativePathAutoComplete<\n TRouteTree extends AnyRoute,\n TTo extends string,\n TSearchPath extends string,\n> = `${TTo}/${SearchPaths<TRouteTree, TSearchPath>}`\n\nexport type RelativeToParentPathAutoComplete<\n TRouteTree extends AnyRoute,\n TFrom extends string,\n TTo extends string,\n TResolvedPath extends string = RemoveTrailingSlashes<\n ResolveRelativePath<TFrom, TTo>\n >,\n> =\n | SearchRelativePathAutoComplete<TRouteTree, TTo, TResolvedPath>\n | (TResolvedPath extends '' ? never : `${TTo}/../`)\n\nexport type RelativeToCurrentPathAutoComplete<\n TRouteTree extends AnyRoute,\n TFrom extends string,\n TTo extends string,\n TRestTo extends string,\n TResolvedPath extends\n string = RemoveTrailingSlashes<`${RemoveTrailingSlashes<TFrom>}/${RemoveLeadingSlashes<TRestTo>}`>,\n> = SearchRelativePathAutoComplete<TRouteTree, TTo, TResolvedPath>\n\nexport type AbsolutePathAutoComplete<\n TRouteTree extends AnyRoute,\n TFrom extends string,\n> =\n | (string extends TFrom\n ? './'\n : TFrom extends `/`\n ? never\n : SearchPaths<TRouteTree, TFrom> extends ''\n ? never\n : './')\n | (string extends TFrom ? '../' : TFrom extends `/` ? never : '../')\n | RoutePaths<TRouteTree>\n | (TFrom extends '/' ? never : SearchPaths<TRouteTree, TFrom>)\n\nexport type RelativeToPathAutoComplete<\n TRouteTree extends AnyRoute,\n TFrom extends string,\n TTo extends string,\n> = TTo extends `..${string}`\n ? RelativeToParentPathAutoComplete<\n TRouteTree,\n TFrom,\n RemoveTrailingSlashes<TTo>\n >\n : TTo extends `./${infer TRestTTo}`\n ? RelativeToCurrentPathAutoComplete<\n TRouteTree,\n TFrom,\n RemoveTrailingSlashes<TTo>,\n TRestTTo\n >\n : AbsolutePathAutoComplete<TRouteTree, TFrom>\n\nexport type NavigateOptions<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n> = ToOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {\n // `replace` is a boolean that determines whether the navigation should replace the current history entry or push a new one.\n replace?: boolean\n resetScroll?: boolean\n /** @deprecated All navigations now use startTransition under the hood */\n startTransition?: boolean\n // if set to `true`, the router will wrap the resulting navigation in a document.startViewTransition() call.\n viewTransition?: boolean\n}\n\nexport type ToOptions<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n> = ToSubOptions<TRouteTree, TFrom, TTo> & {\n _fromLocation?: ParsedLocation\n mask?: ToMaskOptions<TRouteTree, TMaskFrom, TMaskTo>\n}\n\nexport type ToMaskOptions<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TMaskFrom extends RoutePaths<TRouteTree> | string = string,\n TMaskTo extends string = '',\n> = ToSubOptions<TRouteTree, TMaskFrom, TMaskTo> & {\n unmaskOnReload?: boolean\n}\n\nexport type ToSubOptions<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = '',\n> = {\n to?: ToPathOption<TRouteTree, TFrom, TTo> & {}\n hash?: true | Updater<string>\n state?: true | NonNullableUpdater<HistoryState>\n // The source route path. This is automatically set when using route-level APIs, but for type-safe relative routing on the router itself, this is required\n from?: RoutePathsAutoComplete<TRouteTree, TFrom> & {}\n // // When using relative route paths, this option forces resolution from the current path, instead of the route API's path or `from` path\n} & SearchParamOptions<TRouteTree, TFrom, TTo> &\n PathParamOptions<TRouteTree, TFrom, TTo>\n\ntype ParamsReducer<TFrom, TTo> = TTo | ((current: TFrom) => TTo)\n\ntype ParamVariant = 'PATH' | 'SEARCH'\n\ntype ExcludeRootSearchSchema<T, TExcluded = Exclude<T, RootSearchSchema>> = [\n TExcluded,\n] extends [never]\n ? {}\n : TExcluded\n\nexport type ResolveRoute<\n TRouteTree extends AnyRoute,\n TFrom,\n TTo,\n TPath = RemoveTrailingSlashes<\n string extends TFrom\n ? TTo\n : string extends TTo\n ? TFrom\n : ResolveRelativePath<TFrom, TTo>\n >,\n> = TPath extends string\n ? RouteByPath<TRouteTree, `${TPath}/`> extends never\n ? RouteByPath<TRouteTree, TPath>\n : RouteByPath<TRouteTree, `${TPath}/`>\n : never\n\ntype PostProcessParams<\n T,\n TParamVariant extends ParamVariant,\n> = TParamVariant extends 'SEARCH' ? ExcludeRootSearchSchema<T> : T\n\ntype ResolveFromParams<\n TRouteTree extends AnyRoute,\n TParamVariant extends ParamVariant,\n TFrom,\n TFromRouteType extends\n | 'allParams'\n | 'fullSearchSchema' = TParamVariant extends 'PATH'\n ? 'allParams'\n : 'fullSearchSchema',\n> = PostProcessParams<\n RouteByPath<TRouteTree, TFrom>['types'][TFromRouteType],\n TParamVariant\n>\n\ntype ResolveToParams<\n TRouteTree extends AnyRoute,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n TRoute extends AnyRoute = ResolveRoute<TRouteTree, TFrom, TTo>,\n> = PostProcessParams<\n TRoute['types'][TParamVariant extends 'PATH'\n ? 'allParams'\n : 'fullSearchSchemaInput'],\n TParamVariant\n>\n\ntype ResolveRelativeToParams<\n TRouteTree extends AnyRoute,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n TToParams = ResolveToParams<TRouteTree, TParamVariant, TFrom, TTo>,\n> = TParamVariant extends 'SEARCH'\n ? TToParams\n : string extends TFrom\n ? TToParams\n : MakeDifferenceOptional<\n ResolveFromParams<TRouteTree, TParamVariant, TFrom>,\n TToParams\n >\n\ntype MakeOptionalParams<\n TRouteTree extends AnyRoute,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n> = TParamVariant extends 'SEARCH'\n ? {\n search?:\n | true\n | (ParamsReducer<\n Expand<ResolveFromParams<TRouteTree, TParamVariant, TFrom>>,\n Expand<\n ResolveRelativeToParams<TRouteTree, TParamVariant, TFrom, TTo>\n >\n > & {})\n }\n : {\n params?:\n | true\n | (ParamsReducer<\n Expand<ResolveFromParams<TRouteTree, TParamVariant, TFrom>>,\n Expand<\n ResolveRelativeToParams<TRouteTree, TParamVariant, TFrom, TTo>\n >\n > & {})\n }\n\ntype MakeRequiredParamsReducer<\n TRouteTree extends AnyRoute,\n TParamVariant extends ParamVariant,\n TFrom,\n TToParams,\n TFromParams = ResolveFromParams<TRouteTree, TParamVariant, TFrom>,\n> =\n | ([TFromParams] extends [WithoutEmpty<PickRequired<TToParams>>]\n ? true\n : never)\n | ParamsReducer<Expand<TFromParams>, TToParams>\n\nexport type MakeRequiredParams<\n TRouteTree extends AnyRoute,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n> = TParamVariant extends 'SEARCH'\n ? {\n search: Expand<\n MakeRequiredParamsReducer<\n TRouteTree,\n TParamVariant,\n TFrom,\n Expand<ResolveRelativeToParams<TRouteTree, TParamVariant, TFrom, TTo>>\n >\n > & {}\n }\n : {\n params: Expand<\n MakeRequiredParamsReducer<\n TRouteTree,\n TParamVariant,\n TFrom,\n Expand<ResolveRelativeToParams<TRouteTree, TParamVariant, TFrom, TTo>>\n >\n > & {}\n }\n\nexport type IsRequiredParams<TParams> = keyof TParams extends infer K extends\n keyof TParams\n ? K extends any\n ? undefined extends TParams[K]\n ? never\n : true\n : never\n : never\n\nexport type IsRequired<\n TRouteTree extends AnyRoute,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n> = string extends TTo\n ? string extends TFrom\n ? never\n : IsRequiredParams<\n ResolveRelativeToParams<TRouteTree, TParamVariant, TFrom, TTo>\n >\n : IsRequiredParams<\n ResolveRelativeToParams<TRouteTree, TParamVariant, TFrom, TTo>\n >\n\nexport type ParamOptions<\n TRouteTree extends AnyRoute,\n TFrom,\n TTo extends string,\n TParamVariant extends ParamVariant,\n> =\n IsRequired<TRouteTree, TParamVariant, TFrom, TTo> extends never\n ? MakeOptionalParams<TRouteTree, TParamVariant, TFrom, TTo>\n : MakeRequiredParams<TRouteTree, TParamVariant, TFrom, TTo>\n\nexport type SearchParamOptions<\n TRouteTree extends AnyRoute,\n TFrom,\n TTo extends string,\n> = ParamOptions<TRouteTree, TFrom, TTo, 'SEARCH'>\n\nexport type PathParamOptions<\n TRouteTree extends AnyRoute,\n TFrom,\n TTo extends string,\n> = ParamOptions<TRouteTree, TFrom, TTo, 'PATH'>\n\nexport type ToPathOption<\n TRouteTree extends AnyRoute = AnyRoute,\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = string,\n> =\n | CheckPath<TRouteTree, TTo, never, TFrom, TTo>\n | RelativeToPathAutoComplete<\n TRouteTree,\n NoInfer<TFrom> extends string ? NoInfer<TFrom> : '',\n NoInfer<TTo> & string\n >\n\nexport interface ActiveOptions {\n exact?: boolean\n includeHash?: boolean\n includeSearch?: boolean\n}\n\nexport type LinkOptions<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n> = NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {\n // The standard anchor tag target attribute\n target?: HTMLAnchorElement['target']\n // Defaults to `{ exact: false, includeHash: false }`\n activeOptions?: ActiveOptions\n // If set, will preload the linked route on hover and cache it for this many milliseconds in hopes that the user will eventually navigate there.\n preload?: false | 'intent'\n // Delay intent preloading by this many milliseconds. If the intent exits before this delay, the preload will be cancelled.\n preloadDelay?: number\n // If true, will render the link without the href attribute\n disabled?: boolean\n}\n\nexport type CheckPath<TRouteTree extends AnyRoute, TPass, TFail, TFrom, TTo> =\n ResolveRoute<TRouteTree, TFrom, TTo> extends never ? TFail : TPass\n\nexport type ResolveRelativePath<TFrom, TTo = '.'> = TFrom extends string\n ? TTo extends string\n ? TTo extends '.'\n ? TFrom\n : TTo extends `./`\n ? Join<[TFrom, '/']>\n : TTo extends `./${infer TRest}`\n ? ResolveRelativePath<TFrom, TRest>\n : TTo extends `/${infer TRest}`\n ? TTo\n : Split<TTo> extends ['..', ...infer ToRest]\n ? Split<TFrom> extends [...infer FromRest, infer FromTail]\n ? ToRest extends ['/']\n ? Join<['/', ...FromRest, '/']>\n : ResolveRelativePath<Join<FromRest>, Join<ToRest>>\n : never\n : Split<TTo> extends ['.', ...infer ToRest]\n ? ToRest extends ['/']\n ? Join<[TFrom, '/']>\n : ResolveRelativePath<TFrom, Join<ToRest>>\n : CleanPath<Join<['/', ...Split<TFrom>, ...Split<TTo>]>>\n : never\n : never\n\n// type Test1 = ResolveRelativePath<'/', '/posts'>\n// // ^?\n// type Test4 = ResolveRelativePath<'/posts/1/comments', '../..'>\n// // ^?\n// type Test5 = ResolveRelativePath<'/posts/1/comments', '../../..'>\n// // ^?\n// type Test6 = ResolveRelativePath<'/posts/1/comments', './1'>\n// // ^?\n// type Test7 = ResolveRelativePath<'/posts/1/comments', './1/2'>\n// // ^?\n// type Test8 = ResolveRelativePath<'/posts/1/comments', '../edit'>\n// // ^?\n// type Test9 = ResolveRelativePath<'/posts/1/comments', '1'>\n// // ^?\n// type Test10 = ResolveRelativePath<'/posts/1/comments', './1'>\n// // ^?\n// type Test11 = ResolveRelativePath<'/posts/1/comments', './1/2'>\n// // ^?\n\ntype LinkCurrentTargetElement = {\n preloadTimeout?: null | ReturnType<typeof setTimeout>\n}\n\nconst preloadWarning = 'Error preloading route! ☝️'\n\nexport function useLinkProps<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n>(\n options: UseLinkPropsOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,\n): React.AnchorHTMLAttributes<HTMLAnchorElement> {\n const router = useRouter()\n const matchPathname = useMatch({\n strict: false,\n select: (s) => s.pathname,\n })\n const [isTransitioning, setIsTransitioning] = React.useState(false)\n\n const {\n // custom props\n activeProps = () => ({ className: 'active' }),\n inactiveProps = () => ({}),\n activeOptions,\n hash,\n search,\n params,\n to,\n state,\n mask,\n preload: userPreload,\n preloadDelay: userPreloadDelay,\n replace,\n startTransition,\n resetScroll,\n viewTransition,\n // element props\n children,\n target,\n disabled,\n style,\n className,\n onClick,\n onFocus,\n onMouseEnter,\n onMouseLeave,\n onTouchStart,\n ...rest\n } = options\n\n // If this link simply reloads the current route,\n // make sure it has a new key so it will trigger a data refresh\n\n // If this `to` is a valid external URL, return\n // null for LinkUtils\n\n const dest = {\n ...(options.to && { from: matchPathname }),\n ...options,\n }\n\n let type: 'internal' | 'external' = 'internal'\n\n try {\n new URL(`${to}`)\n type = 'external'\n } catch {}\n\n const next = router.buildLocation(dest as any)\n const preload = userPreload ?? router.options.defaultPreload\n const preloadDelay =\n userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0\n\n const isActive = useRouterState({\n select: (s) => {\n // Compare path/hash for matches\n const currentPathSplit = s.location.pathname.split('/')\n const nextPathSplit = next.pathname.split('/')\n const pathIsFuzzyEqual = nextPathSplit.every(\n (d, i) => d === currentPathSplit[i],\n )\n // Combine the matches based on user router.options\n const pathTest = activeOptions?.exact\n ? exactPathTest(s.location.pathname, next.pathname)\n : pathIsFuzzyEqual\n const hashTest = activeOptions?.includeHash\n ? s.location.hash === next.hash\n : true\n const searchTest =\n activeOptions?.includeSearch ?? true\n ? deepEqual(s.location.search, next.search, !activeOptions?.exact)\n : true\n\n // The final \"active\" test\n return pathTest && hashTest && searchTest\n },\n })\n\n if (type === 'external') {\n return {\n ...rest,\n type,\n href: to,\n ...(children && { children }),\n ...(target && { target }),\n ...(disabled && { disabled }),\n ...(style && { style }),\n ...(className && { className }),\n ...(onClick && { onClick }),\n ...(onFocus && { onFocus }),\n ...(onMouseEnter && { onMouseEnter }),\n ...(onMouseLeave && { onMouseLeave }),\n ...(onTouchStart && { onTouchStart }),\n }\n }\n\n // The click handler\n const handleClick = (e: MouseEvent) => {\n if (\n !disabled &&\n !isCtrlEvent(e) &&\n !e.defaultPrevented &&\n (!target || target === '_self') &&\n e.button === 0\n ) {\n e.preventDefault()\n\n flushSync(() => {\n setIsTransitioning(true)\n })\n\n const unsub = router.subscribe('onResolved', () => {\n unsub()\n setIsTransitioning(false)\n })\n\n // All is well? Navigate!\n router.commitLocation({\n ...next,\n replace,\n resetScroll,\n startTransition,\n viewTransition,\n })\n }\n }\n\n const doPreload = () => {\n router.preloadRoute(dest as any).catch((err) => {\n console.warn(err)\n console.warn(preloadWarning)\n })\n }\n\n // The click handler\n const handleFocus = (e: MouseEvent) => {\n if (disabled) return\n if (preload) {\n doPreload()\n }\n }\n\n const handleTouchStart = handleFocus\n\n const handleEnter = (e: MouseEvent) => {\n if (disabled) return\n const eventTarget = (e.target || {}) as LinkCurrentTargetElement\n\n if (preload) {\n if (eventTarget.preloadTimeout) {\n return\n }\n\n eventTarget.preloadTimeout = setTimeout(() => {\n eventTarget.preloadTimeout = null\n doPreload()\n }, preloadDelay)\n }\n }\n\n const handleLeave = (e: MouseEvent) => {\n if (disabled) return\n const eventTarget = (e.target || {}) as LinkCurrentTargetElement\n\n if (eventTarget.preloadTimeout) {\n clearTimeout(eventTarget.preloadTimeout)\n eventTarget.preloadTimeout = null\n }\n }\n\n const composeHandlers =\n (handlers: Array<undefined | ((e: any) => void)>) =>\n (e: { persist?: () => void; defaultPrevented: boolean }) => {\n e.persist?.()\n handlers.filter(Boolean).forEach((handler) => {\n if (e.defaultPrevented) return\n handler!(e)\n })\n }\n\n // Get the active props\n const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> = isActive\n ? functionalUpdate(activeProps as any, {}) ?? {}\n : {}\n\n // Get the inactive props\n const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =\n isActive ? {} : functionalUpdate(inactiveProps, {})\n\n const resolvedClassName = [\n className,\n resolvedActiveProps.className,\n resolvedInactiveProps.className,\n ]\n .filter(Boolean)\n .join(' ')\n\n const resolvedStyle = {\n ...style,\n ...resolvedActiveProps.style,\n ...resolvedInactiveProps.style,\n }\n\n return {\n ...resolvedActiveProps,\n ...resolvedInactiveProps,\n ...rest,\n href: disabled\n ? undefined\n : next.maskedLocation\n ? router.history.createHref(next.maskedLocation.href)\n : router.history.createHref(next.href),\n onClick: composeHandlers([onClick, handleClick]),\n onFocus: composeHandlers([onFocus, handleFocus]),\n onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),\n onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),\n onTouchStart: composeHandlers([onTouchStart, handleTouchStart]),\n target,\n ...(Object.keys(resolvedStyle).length && { style: resolvedStyle }),\n ...(resolvedClassName && { className: resolvedClassName }),\n ...(disabled && {\n role: 'link',\n 'aria-disabled': true,\n }),\n ...(isActive && { 'data-status': 'active', 'aria-current': 'page' }),\n ...(isTransitioning && { 'data-transitioning': 'transitioning' }),\n }\n}\n\nexport type UseLinkPropsOptions<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n> = ActiveLinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &\n React.AnchorHTMLAttributes<HTMLAnchorElement>\n\nexport type ActiveLinkOptions<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n> = LinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {\n // A function that returns additional props for the `active` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)\n activeProps?:\n | React.AnchorHTMLAttributes<HTMLAnchorElement>\n | (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)\n // A function that returns additional props for the `inactive` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)\n inactiveProps?:\n | React.AnchorHTMLAttributes<HTMLAnchorElement>\n | (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)\n}\n\nexport type LinkProps<\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = string,\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n> = ActiveLinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {\n // If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns\n children?:\n | React.ReactNode\n | ((state: {\n isActive: boolean\n isTransitioning: boolean\n }) => React.ReactNode)\n}\n\ntype LinkComponentProps<TComp> = React.PropsWithoutRef<\n TComp extends React.FC<infer TProps> | React.Component<infer TProps>\n ? TProps\n : TComp extends keyof JSX.IntrinsicElements\n ? Omit<React.HTMLProps<TComp>, 'children' | 'preload'>\n : never\n> &\n React.RefAttributes<\n TComp extends\n | React.FC<{ ref: infer TRef }>\n | React.Component<{ ref: infer TRef }>\n ? TRef\n : TComp extends keyof JSX.IntrinsicElements\n ? React.ComponentRef<TComp>\n : never\n >\n\nexport type LinkComponent<TComp> = <\n TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n>(\n props: LinkProps<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &\n LinkComponentProps<TComp>,\n) => React.ReactElement\n\nexport function createLink<const TComp>(Comp: TComp): LinkComponent<TComp> {\n return React.forwardRef(function CreatedLink(props, ref) {\n return <Link {...(props as any)} _asChild={Comp} ref={ref} />\n }) as any\n}\n\nexport const Link: LinkComponent<'a'> = React.forwardRef((props: any, ref) => {\n const { _asChild, ...rest } = props\n const { type, ...linkProps } = useLinkProps(rest)\n\n const children =\n typeof rest.children === 'function'\n ? rest.children({\n isActive: (linkProps as any)['data-status'] === 'active',\n })\n : rest.children\n\n return React.createElement(\n _asChild ? _asChild : 'a',\n {\n ...linkProps,\n ref,\n },\n children,\n )\n}) as any\n\nfunction isCtrlEvent(e: MouseEvent) {\n return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)\n}\n"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import type * as React from 'react';
3
3
  import type { HistoryState, RouterHistory } from '@tanstack/history';
4
4
  import type { AnyContext, AnyRoute, AnySearchSchema, ErrorRouteComponent, NotFoundRouteComponent, RouteMask } from './route.js';
5
5
  import type { FullSearchSchema, RouteById, RoutePaths, RoutesById, RoutesByPath } from './routeInfo.js';
6
- import type { NonNullableUpdater, PickAsRequired, Timeout, Updater } from './utils.js';
6
+ import type { ControlledPromise, NonNullableUpdater, PickAsRequired, Timeout, Updater } from './utils.js';
7
7
  import type { RouteComponent } from './route.js';
8
8
  import type { AnyRouteMatch, MatchRouteOptions, RouteMatch } from './Matches.js';
9
9
  import type { ParsedLocation } from './location.js';
@@ -51,6 +51,7 @@ export interface RouterOptions<TRouteTree extends AnyRoute, TDehydrated extends
51
51
  defaultStaleTime?: number;
52
52
  defaultPreloadStaleTime?: number;
53
53
  defaultPreloadGcTime?: number;
54
+ defaultViewTransition?: boolean;
54
55
  notFoundMode?: 'root' | 'fuzzy';
55
56
  defaultGcTime?: number;
56
57
  caseSensitive?: boolean;
@@ -154,11 +155,13 @@ export declare function createRouter<TRouteTree extends AnyRoute = AnyRoute, TDe
154
155
  export declare class Router<in out TRouteTree extends AnyRoute = AnyRoute, in out TDehydrated extends Record<string, any> = Record<string, any>, in out TSerializedError extends Record<string, any> = Record<string, any>> {
155
156
  tempLocationKey: string | undefined;
156
157
  resetNextScroll: boolean;
158
+ shouldViewTransition?: true;
157
159
  navigateTimeout: Timeout | null;
158
160
  latestLoadPromise: Promise<void>;
159
161
  subscribers: Set<RouterListener<RouterEvent>>;
160
162
  injectedHtml: Array<InjectedHtmlEntry>;
161
163
  dehydratedData?: TDehydrated;
164
+ viewTransitionPromise?: ControlledPromise<true>;
162
165
  __store: Store<RouterState<TRouteTree>>;
163
166
  options: PickAsRequired<Omit<RouterOptions<TRouteTree, TDehydrated, TSerializedError>, 'transformer'> & {
164
167
  transformer: RouterTransformer;
@@ -192,7 +195,7 @@ export declare class Router<in out TRouteTree extends AnyRoute = AnyRoute, in ou
192
195
  cancelMatch: (id: string) => void;
193
196
  cancelMatches: () => void;
194
197
  buildLocation: BuildLocationFn<TRouteTree>;
195
- commitLocation: ({ startTransition, ...next }: ParsedLocation & CommitLocationOptions) => Promise<void>;
198
+ commitLocation: ({ startTransition, viewTransition, ...next }: ParsedLocation & CommitLocationOptions) => Promise<void>;
196
199
  buildAndCommitLocation: ({ replace, resetScroll, startTransition, ...rest }?: BuildNextOptions & CommitLocationOptions) => Promise<void>;
197
200
  navigate: NavigateFn;
198
201
  loadMatches: ({ checkLatest, location, matches, preload, }: {
@@ -27,6 +27,7 @@ class Router {
27
27
  Math.random() * 1e7
28
28
  )}`;
29
29
  this.resetNextScroll = true;
30
+ this.shouldViewTransition = void 0;
30
31
  this.navigateTimeout = null;
31
32
  this.latestLoadPromise = Promise.resolve();
32
33
  this.subscribers = /* @__PURE__ */ new Set();
@@ -69,7 +70,6 @@ class Router {
69
70
  onUpdate: () => {
70
71
  this.__store.state = {
71
72
  ...this.state,
72
- status: this.state.isTransitioning || this.state.isLoading ? "pending" : "idle",
73
73
  cachedMatches: this.state.cachedMatches.filter(
74
74
  (d) => !["redirected"].includes(d.status)
75
75
  )
@@ -493,6 +493,7 @@ class Router {
493
493
  };
494
494
  this.commitLocation = async ({
495
495
  startTransition,
496
+ viewTransition,
496
497
  ...next
497
498
  }) => {
498
499
  if (this.navigateTimeout)
@@ -522,17 +523,13 @@ class Router {
522
523
  nextHistory.state.__tempKey = this.tempLocationKey;
523
524
  }
524
525
  }
525
- const apply = () => {
526
- this.history[next.replace ? "replace" : "push"](
527
- nextHistory.href,
528
- nextHistory.state
529
- );
530
- };
531
- if (startTransition ?? true) {
532
- this.startReactTransition(apply);
533
- } else {
534
- apply();
526
+ if (viewTransition) {
527
+ this.shouldViewTransition = true;
535
528
  }
529
+ this.history[next.replace ? "replace" : "push"](
530
+ nextHistory.href,
531
+ nextHistory.state
532
+ );
536
533
  }
537
534
  this.resetNextScroll = next.resetScroll ?? true;
538
535
  return this.latestLoadPromise;
@@ -895,7 +892,8 @@ class Router {
895
892
  });
896
893
  this.latestLoadPromise = promise;
897
894
  let latestPromise;
898
- (async () => {
895
+ this.startReactTransition(async () => {
896
+ var _a, _b;
899
897
  try {
900
898
  const next = this.latestLocation;
901
899
  const prevLocation = this.state.resolvedLocation;
@@ -914,6 +912,7 @@ class Router {
914
912
  pendingMatches = this.matchRoutes(next.pathname, next.search);
915
913
  this.__store.setState((s) => ({
916
914
  ...s,
915
+ status: "pending",
917
916
  isLoading: true,
918
917
  location: next,
919
918
  pendingMatches,
@@ -952,38 +951,37 @@ class Router {
952
951
  const stayingMatches = previousMatches.filter(
953
952
  (match) => pendingMatches.find((d) => d.id === match.id)
954
953
  );
955
- this.__store.batch(() => {
956
- this.__store.setState((s) => ({
957
- ...s,
958
- isLoading: false,
959
- matches: s.pendingMatches,
960
- pendingMatches: void 0,
961
- cachedMatches: [
962
- ...s.cachedMatches,
963
- ...exitingMatches.filter((d) => d.status !== "error")
964
- ],
965
- statusCode: (redirect == null ? void 0 : redirect.statusCode) || notFound ? 404 : s.matches.some((d) => d.status === "error") ? 500 : 200,
966
- redirect
967
- }));
968
- this.cleanCache();
969
- });
970
- [
971
- [exitingMatches, "onLeave"],
972
- [enteringMatches, "onEnter"],
973
- [stayingMatches, "onStay"]
974
- ].forEach(([matches, hook]) => {
975
- matches.forEach((match) => {
976
- var _a, _b;
977
- (_b = (_a = this.looseRoutesById[match.routeId].options)[hook]) == null ? void 0 : _b.call(_a, match);
954
+ const shouldViewTransition = this.shouldViewTransition ?? this.options.defaultViewTransition;
955
+ delete this.shouldViewTransition;
956
+ const apply = () => {
957
+ this.__store.batch(() => {
958
+ this.__store.setState((s) => ({
959
+ ...s,
960
+ isLoading: false,
961
+ matches: s.pendingMatches,
962
+ pendingMatches: void 0,
963
+ cachedMatches: [
964
+ ...s.cachedMatches,
965
+ ...exitingMatches.filter((d) => d.status !== "error")
966
+ ],
967
+ statusCode: (redirect == null ? void 0 : redirect.statusCode) || notFound ? 404 : s.matches.some((d) => d.status === "error") ? 500 : 200,
968
+ redirect
969
+ }));
970
+ this.cleanCache();
978
971
  });
979
- });
980
- this.emit({
981
- type: "onLoad",
982
- fromLocation: prevLocation,
983
- toLocation: next,
984
- pathChanged: pathDidChange
985
- });
986
- resolveLoad();
972
+ [
973
+ [exitingMatches, "onLeave"],
974
+ [enteringMatches, "onEnter"],
975
+ [stayingMatches, "onStay"]
976
+ ].forEach(([matches, hook]) => {
977
+ matches.forEach((match) => {
978
+ var _a2, _b2;
979
+ (_b2 = (_a2 = this.looseRoutesById[match.routeId].options)[hook]) == null ? void 0 : _b2.call(_a2, match);
980
+ });
981
+ });
982
+ resolveLoad();
983
+ };
984
+ ((_b = (_a = shouldViewTransition && typeof document !== "undefined" ? document : void 0) == null ? void 0 : _a.startViewTransition) == null ? void 0 : _b.call(_a, apply)) || apply();
987
985
  } catch (err) {
988
986
  if (latestPromise = this.checkLatest(promise)) {
989
987
  return latestPromise;
@@ -991,7 +989,7 @@ class Router {
991
989
  console.error("Load Error", err);
992
990
  rejectLoad(err);
993
991
  }
994
- })();
992
+ });
995
993
  return this.latestLoadPromise;
996
994
  };
997
995
  this.resolveRedirect = (err) => {