@tanstack/react-router 1.20.0 → 1.20.3-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/dist/cjs/Asset.cjs +41 -0
- package/dist/cjs/Asset.cjs.map +1 -0
- package/dist/cjs/Asset.d.cts +2 -0
- package/dist/cjs/CatchBoundary.cjs +16 -12
- package/dist/cjs/CatchBoundary.cjs.map +1 -1
- package/dist/cjs/CatchBoundary.d.cts +8 -32
- package/dist/cjs/ClientOnly.cjs +20 -0
- package/dist/cjs/ClientOnly.cjs.map +1 -0
- package/dist/cjs/ClientOnly.d.cts +29 -0
- package/dist/cjs/HeadContent.cjs +155 -0
- package/dist/cjs/HeadContent.cjs.map +1 -0
- package/dist/cjs/HeadContent.d.cts +7 -0
- package/dist/cjs/Match.cjs +252 -0
- package/dist/cjs/Match.cjs.map +1 -0
- package/dist/cjs/Match.d.cts +8 -0
- package/dist/cjs/Matches.cjs +39 -287
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/Matches.d.cts +23 -83
- package/dist/cjs/RouterProvider.cjs +17 -140
- package/dist/cjs/RouterProvider.cjs.map +1 -1
- package/dist/cjs/RouterProvider.d.cts +8 -27
- package/dist/cjs/SafeFragment.cjs +8 -0
- package/dist/cjs/SafeFragment.cjs.map +1 -0
- package/dist/cjs/SafeFragment.d.cts +1 -0
- package/dist/cjs/ScriptOnce.cjs +28 -0
- package/dist/cjs/ScriptOnce.cjs.map +1 -0
- package/dist/cjs/ScriptOnce.d.cts +5 -0
- package/dist/cjs/Scripts.cjs +51 -0
- package/dist/cjs/Scripts.cjs.map +1 -0
- package/dist/cjs/Scripts.d.cts +1 -0
- package/dist/cjs/ScrollRestoration.cjs +39 -0
- package/dist/cjs/ScrollRestoration.cjs.map +1 -0
- package/dist/cjs/ScrollRestoration.d.cts +14 -0
- package/dist/cjs/Transitioner.cjs +115 -0
- package/dist/cjs/Transitioner.cjs.map +1 -0
- package/dist/cjs/Transitioner.d.cts +1 -0
- package/dist/cjs/awaited.cjs +12 -65
- package/dist/cjs/awaited.cjs.map +1 -1
- package/dist/cjs/awaited.d.cts +4 -4
- package/dist/cjs/fileRoute.cjs +41 -15
- package/dist/cjs/fileRoute.cjs.map +1 -1
- package/dist/cjs/fileRoute.d.cts +33 -108
- package/dist/cjs/history.d.cts +1 -0
- package/dist/cjs/index.cjs +216 -73
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +52 -29
- package/dist/cjs/lazyRouteComponent.cjs +40 -29
- package/dist/cjs/lazyRouteComponent.cjs.map +1 -1
- package/dist/cjs/lazyRouteComponent.d.cts +1 -1
- package/dist/cjs/link.cjs +212 -106
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/link.d.cts +41 -86
- package/dist/cjs/matchContext.cjs +27 -0
- package/dist/cjs/matchContext.cjs.map +1 -0
- package/dist/cjs/matchContext.d.cts +3 -0
- package/dist/cjs/not-found.cjs +9 -15
- package/dist/cjs/not-found.cjs.map +1 -1
- package/dist/cjs/not-found.d.cts +5 -22
- package/dist/cjs/renderRouteNotFound.cjs +22 -0
- package/dist/cjs/renderRouteNotFound.cjs.map +1 -0
- package/dist/cjs/renderRouteNotFound.d.cts +2 -0
- package/dist/cjs/route.cjs +110 -79
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +64 -361
- package/dist/cjs/router.cjs +12 -1237
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +69 -237
- package/dist/cjs/routerContext.cjs +1 -1
- package/dist/cjs/routerContext.cjs.map +1 -1
- package/dist/cjs/routerContext.d.cts +7 -2
- package/dist/cjs/scroll-restoration.cjs +16 -177
- package/dist/cjs/scroll-restoration.cjs.map +1 -1
- package/dist/cjs/scroll-restoration.d.cts +1 -18
- package/dist/cjs/serializer.d.cts +6 -0
- package/dist/cjs/structuralSharing.d.cts +8 -0
- package/dist/cjs/typePrimitives.d.cts +16 -0
- package/dist/cjs/useBlocker.cjs +138 -9
- package/dist/cjs/useBlocker.cjs.map +1 -1
- package/dist/cjs/useBlocker.d.cts +64 -7
- package/dist/cjs/useCanGoBack.cjs +8 -0
- package/dist/cjs/useCanGoBack.cjs.map +1 -0
- package/dist/cjs/useCanGoBack.d.cts +1 -0
- package/dist/cjs/useLoaderData.cjs +15 -0
- package/dist/cjs/useLoaderData.cjs.map +1 -0
- package/dist/cjs/useLoaderData.d.cts +8 -0
- package/dist/cjs/useLoaderDeps.cjs +14 -0
- package/dist/cjs/useLoaderDeps.cjs.map +1 -0
- package/dist/cjs/useLoaderDeps.d.cts +8 -0
- package/dist/cjs/useLocation.cjs +10 -0
- package/dist/cjs/useLocation.cjs.map +1 -0
- package/dist/cjs/useLocation.d.cts +7 -0
- package/dist/cjs/useMatch.cjs +47 -0
- package/dist/cjs/useMatch.cjs.map +1 -0
- package/dist/cjs/useMatch.d.cts +10 -0
- package/dist/cjs/useNavigate.cjs +18 -19
- package/dist/cjs/useNavigate.cjs.map +1 -1
- package/dist/cjs/useNavigate.d.cts +4 -8
- package/dist/cjs/useParams.cjs +8 -8
- package/dist/cjs/useParams.cjs.map +1 -1
- package/dist/cjs/useParams.d.cts +9 -8
- package/dist/cjs/useRouteContext.cjs +3 -3
- package/dist/cjs/useRouteContext.cjs.map +1 -1
- package/dist/cjs/useRouteContext.d.cts +3 -7
- package/dist/cjs/useRouter.cjs.map +1 -1
- package/dist/cjs/useRouter.d.cts +3 -4
- package/dist/cjs/useRouterState.cjs +18 -1
- package/dist/cjs/useRouterState.cjs.map +1 -1
- package/dist/cjs/useRouterState.d.cts +8 -6
- package/dist/cjs/useSearch.cjs +7 -4
- package/dist/cjs/useSearch.cjs.map +1 -1
- package/dist/cjs/useSearch.d.cts +9 -7
- package/dist/cjs/utils.cjs +40 -122
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +46 -50
- package/dist/esm/Asset.d.ts +2 -0
- package/dist/esm/Asset.js +41 -0
- package/dist/esm/Asset.js.map +1 -0
- package/dist/esm/CatchBoundary.d.ts +8 -32
- package/dist/esm/CatchBoundary.js +16 -12
- package/dist/esm/CatchBoundary.js.map +1 -1
- package/dist/esm/ClientOnly.d.ts +29 -0
- package/dist/esm/ClientOnly.js +20 -0
- package/dist/esm/ClientOnly.js.map +1 -0
- package/dist/esm/HeadContent.d.ts +7 -0
- package/dist/esm/HeadContent.js +139 -0
- package/dist/esm/HeadContent.js.map +1 -0
- package/dist/esm/Match.d.ts +8 -0
- package/dist/esm/Match.js +235 -0
- package/dist/esm/Match.js.map +1 -0
- package/dist/esm/Matches.d.ts +23 -83
- package/dist/esm/Matches.js +36 -284
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/RouterProvider.d.ts +8 -27
- package/dist/esm/RouterProvider.js +20 -126
- package/dist/esm/RouterProvider.js.map +1 -1
- package/dist/esm/SafeFragment.d.ts +1 -0
- package/dist/esm/SafeFragment.js +8 -0
- package/dist/esm/SafeFragment.js.map +1 -0
- package/dist/esm/ScriptOnce.d.ts +5 -0
- package/dist/esm/ScriptOnce.js +28 -0
- package/dist/esm/ScriptOnce.js.map +1 -0
- package/dist/esm/Scripts.d.ts +1 -0
- package/dist/esm/Scripts.js +51 -0
- package/dist/esm/Scripts.js.map +1 -0
- package/dist/esm/ScrollRestoration.d.ts +14 -0
- package/dist/esm/ScrollRestoration.js +39 -0
- package/dist/esm/ScrollRestoration.js.map +1 -0
- package/dist/esm/Transitioner.d.ts +1 -0
- package/dist/esm/Transitioner.js +98 -0
- package/dist/esm/Transitioner.js.map +1 -0
- package/dist/esm/awaited.d.ts +4 -4
- package/dist/esm/awaited.js +12 -65
- package/dist/esm/awaited.js.map +1 -1
- package/dist/esm/fileRoute.d.ts +33 -108
- package/dist/esm/fileRoute.js +38 -12
- package/dist/esm/fileRoute.js.map +1 -1
- package/dist/esm/history.d.ts +1 -0
- package/dist/esm/index.d.ts +52 -29
- package/dist/esm/index.js +41 -29
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lazyRouteComponent.d.ts +1 -1
- package/dist/esm/lazyRouteComponent.js +40 -29
- package/dist/esm/lazyRouteComponent.js.map +1 -1
- package/dist/esm/link.d.ts +41 -86
- package/dist/esm/link.js +212 -106
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/matchContext.d.ts +3 -0
- package/dist/esm/matchContext.js +10 -0
- package/dist/esm/matchContext.js.map +1 -0
- package/dist/esm/not-found.d.ts +5 -22
- package/dist/esm/not-found.js +9 -15
- package/dist/esm/not-found.js.map +1 -1
- package/dist/esm/renderRouteNotFound.d.ts +2 -0
- package/dist/esm/renderRouteNotFound.js +22 -0
- package/dist/esm/renderRouteNotFound.js.map +1 -0
- package/dist/esm/route.d.ts +64 -361
- package/dist/esm/route.js +103 -72
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +69 -237
- package/dist/esm/router.js +13 -1238
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/routerContext.d.ts +7 -2
- package/dist/esm/routerContext.js +1 -1
- package/dist/esm/routerContext.js.map +1 -1
- package/dist/esm/scroll-restoration.d.ts +1 -18
- package/dist/esm/scroll-restoration.js +17 -161
- package/dist/esm/scroll-restoration.js.map +1 -1
- package/dist/esm/serializer.d.ts +6 -0
- package/dist/esm/structuralSharing.d.ts +8 -0
- package/dist/esm/typePrimitives.d.ts +16 -0
- package/dist/esm/useBlocker.d.ts +64 -7
- package/dist/esm/useBlocker.js +138 -9
- package/dist/esm/useBlocker.js.map +1 -1
- package/dist/esm/useCanGoBack.d.ts +1 -0
- package/dist/esm/useCanGoBack.js +8 -0
- package/dist/esm/useCanGoBack.js.map +1 -0
- package/dist/esm/useLoaderData.d.ts +8 -0
- package/dist/esm/useLoaderData.js +15 -0
- package/dist/esm/useLoaderData.js.map +1 -0
- package/dist/esm/useLoaderDeps.d.ts +8 -0
- package/dist/esm/useLoaderDeps.js +14 -0
- package/dist/esm/useLoaderDeps.js.map +1 -0
- package/dist/esm/useLocation.d.ts +7 -0
- package/dist/esm/useLocation.js +10 -0
- package/dist/esm/useLocation.js.map +1 -0
- package/dist/esm/useMatch.d.ts +10 -0
- package/dist/esm/useMatch.js +30 -0
- package/dist/esm/useMatch.js.map +1 -0
- package/dist/esm/useNavigate.d.ts +4 -8
- package/dist/esm/useNavigate.js +18 -19
- package/dist/esm/useNavigate.js.map +1 -1
- package/dist/esm/useParams.d.ts +9 -8
- package/dist/esm/useParams.js +8 -8
- package/dist/esm/useParams.js.map +1 -1
- package/dist/esm/useRouteContext.d.ts +3 -7
- package/dist/esm/useRouteContext.js +2 -2
- package/dist/esm/useRouteContext.js.map +1 -1
- package/dist/esm/useRouter.d.ts +3 -4
- package/dist/esm/useRouter.js.map +1 -1
- package/dist/esm/useRouterState.d.ts +8 -6
- package/dist/esm/useRouterState.js +18 -1
- package/dist/esm/useRouterState.js.map +1 -1
- package/dist/esm/useSearch.d.ts +9 -7
- package/dist/esm/useSearch.js +6 -3
- package/dist/esm/useSearch.js.map +1 -1
- package/dist/esm/utils.d.ts +46 -50
- package/dist/esm/utils.js +41 -123
- package/dist/esm/utils.js.map +1 -1
- package/package.json +30 -31
- package/src/Asset.tsx +40 -0
- package/src/CatchBoundary.tsx +35 -19
- package/src/ClientOnly.tsx +68 -0
- package/src/HeadContent.tsx +174 -0
- package/src/Match.tsx +330 -0
- package/src/Matches.tsx +149 -558
- package/src/RouterProvider.tsx +58 -212
- package/src/SafeFragment.tsx +5 -0
- package/src/ScriptOnce.tsx +32 -0
- package/src/Scripts.tsx +65 -0
- package/src/ScrollRestoration.tsx +69 -0
- package/src/Transitioner.tsx +130 -0
- package/src/awaited.tsx +16 -87
- package/src/fileRoute.ts +145 -248
- package/src/history.ts +2 -1
- package/src/index.tsx +368 -30
- package/src/lazyRouteComponent.tsx +68 -54
- package/src/link.tsx +397 -522
- package/src/matchContext.tsx +8 -0
- package/src/not-found.tsx +13 -34
- package/src/renderRouteNotFound.tsx +27 -0
- package/src/route.tsx +572 -0
- package/src/router.ts +99 -2067
- package/src/routerContext.tsx +8 -2
- package/src/scroll-restoration.tsx +23 -224
- package/src/serializer.ts +7 -0
- package/src/structuralSharing.ts +47 -0
- package/src/typePrimitives.ts +84 -0
- package/src/useBlocker.tsx +297 -15
- package/src/useCanGoBack.ts +5 -0
- package/src/useLoaderData.tsx +80 -0
- package/src/useLoaderDeps.tsx +58 -0
- package/src/useLocation.tsx +41 -0
- package/src/useMatch.tsx +119 -0
- package/src/useNavigate.tsx +41 -61
- package/src/useParams.tsx +88 -23
- package/src/useRouteContext.ts +24 -18
- package/src/useRouter.tsx +4 -5
- package/src/useRouterState.tsx +52 -10
- package/src/useSearch.tsx +87 -24
- package/src/utils.ts +97 -312
- package/dist/cjs/createServerFn.cjs +0 -40
- package/dist/cjs/createServerFn.cjs.map +0 -1
- package/dist/cjs/createServerFn.d.cts +0 -44
- package/dist/cjs/defer.cjs +0 -30
- package/dist/cjs/defer.cjs.map +0 -1
- package/dist/cjs/defer.d.cts +0 -25
- package/dist/cjs/location.d.cts +0 -12
- package/dist/cjs/path.cjs +0 -213
- package/dist/cjs/path.cjs.map +0 -1
- package/dist/cjs/path.d.cts +0 -24
- package/dist/cjs/qss.cjs +0 -45
- package/dist/cjs/qss.cjs.map +0 -1
- package/dist/cjs/qss.d.cts +0 -2
- package/dist/cjs/redirects.cjs +0 -16
- package/dist/cjs/redirects.cjs.map +0 -1
- package/dist/cjs/redirects.d.cts +0 -18
- package/dist/cjs/routeInfo.d.cts +0 -33
- package/dist/cjs/searchParams.cjs +0 -63
- package/dist/cjs/searchParams.cjs.map +0 -1
- package/dist/cjs/searchParams.d.cts +0 -7
- package/dist/esm/createServerFn.d.ts +0 -44
- package/dist/esm/createServerFn.js +0 -40
- package/dist/esm/createServerFn.js.map +0 -1
- package/dist/esm/defer.d.ts +0 -25
- package/dist/esm/defer.js +0 -30
- package/dist/esm/defer.js.map +0 -1
- package/dist/esm/location.d.ts +0 -12
- package/dist/esm/path.d.ts +0 -24
- package/dist/esm/path.js +0 -213
- package/dist/esm/path.js.map +0 -1
- package/dist/esm/qss.d.ts +0 -2
- package/dist/esm/qss.js +0 -45
- package/dist/esm/qss.js.map +0 -1
- package/dist/esm/redirects.d.ts +0 -18
- package/dist/esm/redirects.js +0 -16
- package/dist/esm/redirects.js.map +0 -1
- package/dist/esm/routeInfo.d.ts +0 -33
- package/dist/esm/searchParams.d.ts +0 -7
- package/dist/esm/searchParams.js +0 -63
- package/dist/esm/searchParams.js.map +0 -1
- package/src/createServerFn.ts +0 -107
- package/src/defer.ts +0 -70
- package/src/location.ts +0 -13
- package/src/path.ts +0 -280
- package/src/qss.ts +0 -53
- package/src/redirects.ts +0 -56
- package/src/route.ts +0 -1356
- package/src/routeInfo.ts +0 -62
- package/src/searchParams.ts +0 -79
package/src/link.tsx
CHANGED
|
@@ -1,386 +1,64 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
|
-
import {
|
|
3
|
-
import { useRouterState } from './useRouterState'
|
|
4
|
-
import { useRouter } from './useRouter'
|
|
5
|
-
import { Trim } from './fileRoute'
|
|
6
|
-
import { AnyRoute, ReactNode, RootSearchSchema } from './route'
|
|
7
|
-
import { RouteByPath, RoutePaths, RoutePathsAutoComplete } from './routeInfo'
|
|
8
|
-
import { RegisteredRouter } from './router'
|
|
2
|
+
import { flushSync } from 'react-dom'
|
|
9
3
|
import {
|
|
10
|
-
Expand,
|
|
11
|
-
IsUnion,
|
|
12
|
-
MakeDifferenceOptional,
|
|
13
|
-
NoInfer,
|
|
14
|
-
NonNullableUpdater,
|
|
15
|
-
PickRequired,
|
|
16
|
-
Updater,
|
|
17
|
-
WithoutEmpty,
|
|
18
4
|
deepEqual,
|
|
5
|
+
exactPathTest,
|
|
19
6
|
functionalUpdate,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
: T extends `${infer L}//`
|
|
26
|
-
? `${CleanPath<L>}/`
|
|
27
|
-
: T extends `//${infer L}`
|
|
28
|
-
? `/${CleanPath<L>}`
|
|
29
|
-
: T
|
|
30
|
-
|
|
31
|
-
export type Split<S, TIncludeTrailingSlash = true> = S extends unknown
|
|
32
|
-
? string extends S
|
|
33
|
-
? string[]
|
|
34
|
-
: S extends string
|
|
35
|
-
? CleanPath<S> extends ''
|
|
36
|
-
? []
|
|
37
|
-
: TIncludeTrailingSlash extends true
|
|
38
|
-
? CleanPath<S> extends `${infer T}/`
|
|
39
|
-
? [...Split<T>, '/']
|
|
40
|
-
: CleanPath<S> extends `/${infer U}`
|
|
41
|
-
? Split<U>
|
|
42
|
-
: CleanPath<S> extends `${infer T}/${infer U}`
|
|
43
|
-
? [...Split<T>, ...Split<U>]
|
|
44
|
-
: [S]
|
|
45
|
-
: CleanPath<S> extends `${infer T}/${infer U}`
|
|
46
|
-
? [...Split<T>, ...Split<U>]
|
|
47
|
-
: S extends string
|
|
48
|
-
? [S]
|
|
49
|
-
: never
|
|
50
|
-
: never
|
|
51
|
-
: never
|
|
52
|
-
|
|
53
|
-
export type ParsePathParams<T extends string> = keyof {
|
|
54
|
-
[K in Trim<Split<T>[number], '_'> as K extends `$${infer L}`
|
|
55
|
-
? L extends ''
|
|
56
|
-
? '_splat'
|
|
57
|
-
: L
|
|
58
|
-
: never]: K
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export type Join<T, Delimiter extends string = '/'> = T extends []
|
|
62
|
-
? ''
|
|
63
|
-
: T extends [infer L extends string]
|
|
64
|
-
? L
|
|
65
|
-
: T extends [infer L extends string, ...infer Tail extends [...string[]]]
|
|
66
|
-
? CleanPath<`${L}${Delimiter}${Join<Tail>}`>
|
|
67
|
-
: never
|
|
68
|
-
|
|
69
|
-
export type Last<T extends any[]> = T extends [...infer _, infer L] ? L : never
|
|
70
|
-
|
|
71
|
-
export type RemoveTrailingSlashes<T> = T extends `${infer R}/`
|
|
72
|
-
? RemoveTrailingSlashes<R>
|
|
73
|
-
: T
|
|
74
|
-
|
|
75
|
-
export type RemoveLeadingSlashes<T> = T extends `/${infer R}`
|
|
76
|
-
? RemoveLeadingSlashes<R>
|
|
77
|
-
: T
|
|
78
|
-
|
|
79
|
-
export type SearchPaths<
|
|
80
|
-
TPaths,
|
|
81
|
-
TSearchPath extends string,
|
|
82
|
-
> = TPaths extends `${TSearchPath}/${infer TRest}` ? TRest : never
|
|
83
|
-
|
|
84
|
-
export type SearchRelativePathAutoComplete<
|
|
85
|
-
TTo extends string,
|
|
86
|
-
TSearchPath extends string,
|
|
87
|
-
TPaths,
|
|
88
|
-
SearchedPaths = SearchPaths<TPaths, TSearchPath>,
|
|
89
|
-
> = SearchedPaths extends string ? `${TTo}/${SearchedPaths}` : never
|
|
90
|
-
|
|
91
|
-
export type RelativeToParentPathAutoComplete<
|
|
92
|
-
TFrom extends string,
|
|
93
|
-
TTo extends string,
|
|
94
|
-
TPaths,
|
|
95
|
-
TResolvedPath extends string = RemoveTrailingSlashes<
|
|
96
|
-
ResolveRelativePath<TFrom, TTo>
|
|
97
|
-
>,
|
|
98
|
-
> =
|
|
99
|
-
| SearchRelativePathAutoComplete<TTo, TResolvedPath, TPaths>
|
|
100
|
-
| (TResolvedPath extends '' ? never : `${TTo}/../`)
|
|
101
|
-
|
|
102
|
-
export type RelativeToCurrentPathAutoComplete<
|
|
103
|
-
TFrom extends string,
|
|
104
|
-
TTo extends string,
|
|
105
|
-
TRestTo extends string,
|
|
106
|
-
TPaths,
|
|
107
|
-
TResolvedPath extends
|
|
108
|
-
string = RemoveTrailingSlashes<`${RemoveTrailingSlashes<TFrom>}/${RemoveLeadingSlashes<TRestTo>}`>,
|
|
109
|
-
> = SearchRelativePathAutoComplete<TTo, TResolvedPath, TPaths>
|
|
110
|
-
|
|
111
|
-
export type AbsolutePathAutoComplete<TFrom extends string, TPaths> =
|
|
112
|
-
| (string extends TFrom
|
|
113
|
-
? './'
|
|
114
|
-
: TFrom extends `/`
|
|
115
|
-
? never
|
|
116
|
-
: SearchPaths<
|
|
117
|
-
TPaths,
|
|
118
|
-
RemoveTrailingSlashes<TFrom>
|
|
119
|
-
> extends infer SearchedPaths
|
|
120
|
-
? SearchedPaths extends ''
|
|
121
|
-
? never
|
|
122
|
-
: './'
|
|
123
|
-
: never)
|
|
124
|
-
| (string extends TFrom ? '../' : TFrom extends `/` ? never : '../')
|
|
125
|
-
| TPaths
|
|
126
|
-
|
|
127
|
-
export type RelativeToPathAutoComplete<
|
|
128
|
-
TRouteTree extends AnyRoute,
|
|
129
|
-
TFrom extends string,
|
|
130
|
-
TTo extends string,
|
|
131
|
-
TPaths = RoutePaths<TRouteTree>,
|
|
132
|
-
> = TTo extends `..${string}`
|
|
133
|
-
? RelativeToParentPathAutoComplete<TFrom, RemoveTrailingSlashes<TTo>, TPaths>
|
|
134
|
-
: TTo extends `./${infer TRestTTo}`
|
|
135
|
-
? RelativeToCurrentPathAutoComplete<
|
|
136
|
-
TFrom,
|
|
137
|
-
RemoveTrailingSlashes<TTo>,
|
|
138
|
-
TRestTTo,
|
|
139
|
-
TPaths
|
|
140
|
-
>
|
|
141
|
-
: AbsolutePathAutoComplete<TFrom, TPaths>
|
|
142
|
-
|
|
143
|
-
export type NavigateOptions<
|
|
144
|
-
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
145
|
-
TFrom extends RoutePaths<TRouteTree> | string = string,
|
|
146
|
-
TTo extends string = '',
|
|
147
|
-
TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
|
|
148
|
-
TMaskTo extends string = '',
|
|
149
|
-
> = ToOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {
|
|
150
|
-
// `replace` is a boolean that determines whether the navigation should replace the current history entry or push a new one.
|
|
151
|
-
replace?: boolean
|
|
152
|
-
resetScroll?: boolean
|
|
153
|
-
// If set to `true`, the link's underlying navigate() call will be wrapped in a `React.startTransition` call. Defaults to `true`.
|
|
154
|
-
startTransition?: boolean
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export type ToOptions<
|
|
158
|
-
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
159
|
-
TFrom extends RoutePaths<TRouteTree> | string = string,
|
|
160
|
-
TTo extends string = '',
|
|
161
|
-
TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
|
|
162
|
-
TMaskTo extends string = '',
|
|
163
|
-
> = ToSubOptions<TRouteTree, TFrom, TTo> & {
|
|
164
|
-
mask?: ToMaskOptions<TRouteTree, TMaskFrom, TMaskTo>
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export type ToMaskOptions<
|
|
168
|
-
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
169
|
-
TMaskFrom extends RoutePaths<TRouteTree> | string = string,
|
|
170
|
-
TMaskTo extends string = '',
|
|
171
|
-
> = ToSubOptions<TRouteTree, TMaskFrom, TMaskTo> & {
|
|
172
|
-
unmaskOnReload?: boolean
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export type ToSubOptions<
|
|
176
|
-
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
177
|
-
TFrom extends RoutePaths<TRouteTree> | string = string,
|
|
178
|
-
TTo extends string = '',
|
|
179
|
-
> = {
|
|
180
|
-
to?: ToPathOption<TRouteTree, TFrom, TTo>
|
|
181
|
-
hash?: true | Updater<string>
|
|
182
|
-
state?: true | NonNullableUpdater<HistoryState>
|
|
183
|
-
// 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
|
|
184
|
-
from?: RoutePathsAutoComplete<TRouteTree, TFrom>
|
|
185
|
-
// // When using relative route paths, this option forces resolution from the current path, instead of the route API's path or `from` path
|
|
186
|
-
} & CheckPath<TRouteTree, {}, TFrom, TTo> &
|
|
187
|
-
SearchParamOptions<TRouteTree, TFrom, TTo> &
|
|
188
|
-
PathParamOptions<TRouteTree, TFrom, TTo>
|
|
189
|
-
|
|
190
|
-
type ParamsReducer<TFrom, TTo> = TTo | ((current: TFrom) => TTo)
|
|
191
|
-
|
|
192
|
-
type ParamVariant = 'PATH' | 'SEARCH'
|
|
193
|
-
type ExcludeRootSearchSchema<T, Excluded = Exclude<T, RootSearchSchema>> = [
|
|
194
|
-
Excluded,
|
|
195
|
-
] extends [never]
|
|
196
|
-
? {}
|
|
197
|
-
: Excluded
|
|
198
|
-
|
|
199
|
-
export type ResolveRoute<
|
|
200
|
-
TRouteTree extends AnyRoute,
|
|
201
|
-
TFrom,
|
|
202
|
-
TTo,
|
|
203
|
-
TPath = RemoveTrailingSlashes<
|
|
204
|
-
string extends TTo ? TFrom : ResolveRelativePath<TFrom, TTo>
|
|
205
|
-
>,
|
|
206
|
-
> =
|
|
207
|
-
RouteByPath<TRouteTree, `${TPath & string}/`> extends never
|
|
208
|
-
? RouteByPath<TRouteTree, TPath>
|
|
209
|
-
: RouteByPath<TRouteTree, `${TPath & string}/`>
|
|
210
|
-
|
|
211
|
-
type PostProcessParams<
|
|
212
|
-
T,
|
|
213
|
-
TParamVariant extends ParamVariant,
|
|
214
|
-
> = TParamVariant extends 'SEARCH' ? ExcludeRootSearchSchema<T> : T
|
|
215
|
-
|
|
216
|
-
export type ParamOptions<
|
|
217
|
-
TRouteTree extends AnyRoute,
|
|
218
|
-
TFrom,
|
|
219
|
-
TTo extends string,
|
|
220
|
-
TParamVariant extends ParamVariant,
|
|
221
|
-
TFromRouteType extends
|
|
222
|
-
| 'allParams'
|
|
223
|
-
| 'fullSearchSchema' = TParamVariant extends 'PATH'
|
|
224
|
-
? 'allParams'
|
|
225
|
-
: 'fullSearchSchema',
|
|
226
|
-
TToRouteType extends
|
|
227
|
-
| 'allParams'
|
|
228
|
-
| 'fullSearchSchemaInput' = TParamVariant extends 'PATH'
|
|
229
|
-
? 'allParams'
|
|
230
|
-
: 'fullSearchSchemaInput',
|
|
231
|
-
TFromParams = PostProcessParams<
|
|
232
|
-
RouteByPath<TRouteTree, TFrom>['types'][TFromRouteType],
|
|
233
|
-
TParamVariant
|
|
234
|
-
>,
|
|
235
|
-
TToParams = PostProcessParams<
|
|
236
|
-
ResolveRoute<TRouteTree, TFrom, TTo>['types'][TToRouteType],
|
|
237
|
-
TParamVariant
|
|
238
|
-
>,
|
|
239
|
-
TRelativeToParams = TParamVariant extends 'SEARCH'
|
|
240
|
-
? TToParams
|
|
241
|
-
: true extends IsUnion<TFromParams>
|
|
242
|
-
? TToParams
|
|
243
|
-
: MakeDifferenceOptional<TFromParams, TToParams>,
|
|
244
|
-
TReducer = ParamsReducer<TFromParams, TRelativeToParams>,
|
|
245
|
-
> =
|
|
246
|
-
Expand<WithoutEmpty<PickRequired<TRelativeToParams>>> extends never
|
|
247
|
-
? Partial<MakeParamOption<TParamVariant, true | TReducer>>
|
|
248
|
-
: TFromParams extends Expand<WithoutEmpty<PickRequired<TRelativeToParams>>>
|
|
249
|
-
? MakeParamOption<TParamVariant, true | TReducer>
|
|
250
|
-
: MakeParamOption<TParamVariant, TReducer>
|
|
251
|
-
|
|
252
|
-
type MakeParamOption<
|
|
253
|
-
TParamVariant extends ParamVariant,
|
|
254
|
-
T,
|
|
255
|
-
> = TParamVariant extends 'PATH'
|
|
256
|
-
? MakePathParamOptions<T>
|
|
257
|
-
: MakeSearchParamOptions<T>
|
|
258
|
-
type MakeSearchParamOptions<T> = { search: T }
|
|
259
|
-
type MakePathParamOptions<T> = { params: T }
|
|
260
|
-
|
|
261
|
-
export type SearchParamOptions<
|
|
262
|
-
TRouteTree extends AnyRoute,
|
|
263
|
-
TFrom,
|
|
264
|
-
TTo extends string,
|
|
265
|
-
> = ParamOptions<TRouteTree, TFrom, TTo, 'SEARCH'>
|
|
266
|
-
|
|
267
|
-
export type PathParamOptions<
|
|
268
|
-
TRouteTree extends AnyRoute,
|
|
269
|
-
TFrom,
|
|
270
|
-
TTo extends string,
|
|
271
|
-
> = ParamOptions<TRouteTree, TFrom, TTo, 'PATH'>
|
|
272
|
-
|
|
273
|
-
export type ToPathOption<
|
|
274
|
-
TRouteTree extends AnyRoute = AnyRoute,
|
|
275
|
-
TFrom extends RoutePaths<TRouteTree> | string = string,
|
|
276
|
-
TTo extends string = '',
|
|
277
|
-
> =
|
|
278
|
-
| TTo
|
|
279
|
-
| RelativeToPathAutoComplete<
|
|
280
|
-
TRouteTree,
|
|
281
|
-
NoInfer<TFrom> extends string ? NoInfer<TFrom> : '',
|
|
282
|
-
NoInfer<TTo> & string
|
|
283
|
-
>
|
|
284
|
-
|
|
285
|
-
export interface ActiveOptions {
|
|
286
|
-
exact?: boolean
|
|
287
|
-
includeHash?: boolean
|
|
288
|
-
includeSearch?: boolean
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
export type LinkOptions<
|
|
292
|
-
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
293
|
-
TFrom extends RoutePaths<TRouteTree> | string = string,
|
|
294
|
-
TTo extends string = '',
|
|
295
|
-
TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
|
|
296
|
-
TMaskTo extends string = '',
|
|
297
|
-
> = NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {
|
|
298
|
-
// The standard anchor tag target attribute
|
|
299
|
-
target?: HTMLAnchorElement['target']
|
|
300
|
-
// Defaults to `{ exact: false, includeHash: false }`
|
|
301
|
-
activeOptions?: ActiveOptions
|
|
302
|
-
// 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.
|
|
303
|
-
preload?: false | 'intent'
|
|
304
|
-
// Delay intent preloading by this many milliseconds. If the intent exits before this delay, the preload will be cancelled.
|
|
305
|
-
preloadDelay?: number
|
|
306
|
-
// If true, will render the link without the href attribute
|
|
307
|
-
disabled?: boolean
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
export type CheckPath<TRouteTree extends AnyRoute, TPass, TFrom, TTo> =
|
|
311
|
-
ResolveRoute<TRouteTree, TFrom, TTo> extends never
|
|
312
|
-
? string extends TFrom
|
|
313
|
-
? RemoveTrailingSlashes<TTo> extends '.' | '..'
|
|
314
|
-
? TPass
|
|
315
|
-
: CheckPathError<TRouteTree>
|
|
316
|
-
: CheckPathError<TRouteTree>
|
|
317
|
-
: TPass
|
|
318
|
-
|
|
319
|
-
export type CheckPathError<TRouteTree extends AnyRoute> = {
|
|
320
|
-
to: RoutePaths<TRouteTree>
|
|
321
|
-
}
|
|
7
|
+
preloadWarning,
|
|
8
|
+
removeTrailingSlash,
|
|
9
|
+
} from '@tanstack/router-core'
|
|
10
|
+
import { useRouterState } from './useRouterState'
|
|
11
|
+
import { useRouter } from './useRouter'
|
|
322
12
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
? Join<[TFrom, '/']>
|
|
329
|
-
: TTo extends `./${infer TRest}`
|
|
330
|
-
? ResolveRelativePath<TFrom, TRest>
|
|
331
|
-
: TTo extends `/${infer TRest}`
|
|
332
|
-
? TTo
|
|
333
|
-
: Split<TTo> extends ['..', ...infer ToRest]
|
|
334
|
-
? Split<TFrom> extends [...infer FromRest, infer FromTail]
|
|
335
|
-
? ToRest extends ['/']
|
|
336
|
-
? Join<['/', ...FromRest, '/']>
|
|
337
|
-
: ResolveRelativePath<Join<FromRest>, Join<ToRest>>
|
|
338
|
-
: never
|
|
339
|
-
: Split<TTo> extends ['.', ...infer ToRest]
|
|
340
|
-
? ToRest extends ['/']
|
|
341
|
-
? Join<[TFrom, '/']>
|
|
342
|
-
: ResolveRelativePath<TFrom, Join<ToRest>>
|
|
343
|
-
: CleanPath<Join<['/', ...Split<TFrom>, ...Split<TTo>]>>
|
|
344
|
-
: never
|
|
345
|
-
: never
|
|
346
|
-
|
|
347
|
-
type LinkCurrentTargetElement = {
|
|
348
|
-
preloadTimeout?: null | ReturnType<typeof setTimeout>
|
|
349
|
-
}
|
|
13
|
+
import {
|
|
14
|
+
useForwardedRef,
|
|
15
|
+
useIntersectionObserver,
|
|
16
|
+
useLayoutEffect,
|
|
17
|
+
} from './utils'
|
|
350
18
|
|
|
351
|
-
|
|
19
|
+
import { useMatches } from './Matches'
|
|
20
|
+
import type {
|
|
21
|
+
AnyRouter,
|
|
22
|
+
Constrain,
|
|
23
|
+
LinkCurrentTargetElement,
|
|
24
|
+
LinkOptions,
|
|
25
|
+
RegisteredRouter,
|
|
26
|
+
RoutePaths,
|
|
27
|
+
} from '@tanstack/router-core'
|
|
28
|
+
import type { ReactNode } from 'react'
|
|
29
|
+
import type {
|
|
30
|
+
ValidateLinkOptions,
|
|
31
|
+
ValidateLinkOptionsArray,
|
|
32
|
+
} from './typePrimitives'
|
|
352
33
|
|
|
353
34
|
export function useLinkProps<
|
|
354
|
-
|
|
355
|
-
TFrom extends
|
|
356
|
-
TTo extends string =
|
|
357
|
-
TMaskFrom extends
|
|
358
|
-
TMaskTo extends string = '',
|
|
35
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
36
|
+
const TFrom extends string = string,
|
|
37
|
+
const TTo extends string | undefined = undefined,
|
|
38
|
+
const TMaskFrom extends string = TFrom,
|
|
39
|
+
const TMaskTo extends string = '',
|
|
359
40
|
>(
|
|
360
|
-
options: UseLinkPropsOptions<
|
|
361
|
-
|
|
41
|
+
options: UseLinkPropsOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,
|
|
42
|
+
forwardedRef?: React.ForwardedRef<Element>,
|
|
43
|
+
): React.ComponentPropsWithRef<'a'> {
|
|
362
44
|
const router = useRouter()
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
})
|
|
45
|
+
const [isTransitioning, setIsTransitioning] = React.useState(false)
|
|
46
|
+
const hasRenderFetched = React.useRef(false)
|
|
47
|
+
const innerRef = useForwardedRef(forwardedRef)
|
|
367
48
|
|
|
368
49
|
const {
|
|
369
50
|
// custom props
|
|
370
51
|
activeProps = () => ({ className: 'active' }),
|
|
371
52
|
inactiveProps = () => ({}),
|
|
372
53
|
activeOptions,
|
|
373
|
-
hash,
|
|
374
|
-
search,
|
|
375
|
-
params,
|
|
376
54
|
to,
|
|
377
|
-
state,
|
|
378
|
-
mask,
|
|
379
55
|
preload: userPreload,
|
|
380
56
|
preloadDelay: userPreloadDelay,
|
|
57
|
+
hashScrollIntoView,
|
|
381
58
|
replace,
|
|
382
59
|
startTransition,
|
|
383
60
|
resetScroll,
|
|
61
|
+
viewTransition,
|
|
384
62
|
// element props
|
|
385
63
|
children,
|
|
386
64
|
target,
|
|
@@ -392,72 +70,159 @@ export function useLinkProps<
|
|
|
392
70
|
onMouseEnter,
|
|
393
71
|
onMouseLeave,
|
|
394
72
|
onTouchStart,
|
|
73
|
+
ignoreBlocker,
|
|
395
74
|
...rest
|
|
396
75
|
} = options
|
|
397
76
|
|
|
77
|
+
const {
|
|
78
|
+
// prevent these from being returned
|
|
79
|
+
params: _params,
|
|
80
|
+
search: _search,
|
|
81
|
+
hash: _hash,
|
|
82
|
+
state: _state,
|
|
83
|
+
mask: _mask,
|
|
84
|
+
reloadDocument: _reloadDocument,
|
|
85
|
+
...propsSafeToSpread
|
|
86
|
+
} = rest
|
|
87
|
+
|
|
398
88
|
// If this link simply reloads the current route,
|
|
399
89
|
// make sure it has a new key so it will trigger a data refresh
|
|
400
90
|
|
|
401
91
|
// If this `to` is a valid external URL, return
|
|
402
92
|
// null for LinkUtils
|
|
403
93
|
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
94
|
+
const type: 'internal' | 'external' = React.useMemo(() => {
|
|
95
|
+
try {
|
|
96
|
+
new URL(`${to}`)
|
|
97
|
+
return 'external'
|
|
98
|
+
} catch {}
|
|
99
|
+
return 'internal'
|
|
100
|
+
}, [to])
|
|
101
|
+
|
|
102
|
+
// subscribe to search params to re-build location if it changes
|
|
103
|
+
const currentSearch = useRouterState({
|
|
104
|
+
select: (s) => s.location.search,
|
|
105
|
+
structuralSharing: true as any,
|
|
106
|
+
})
|
|
408
107
|
|
|
409
|
-
|
|
108
|
+
// when `from` is not supplied, use the leaf route of the current matches as the `from` location
|
|
109
|
+
// so relative routing works as expected
|
|
110
|
+
const from = useMatches({
|
|
111
|
+
select: (matches) => options.from ?? matches[matches.length - 1]?.fullPath,
|
|
112
|
+
})
|
|
113
|
+
// Use it as the default `from` location
|
|
114
|
+
const _options = React.useMemo(() => ({ ...options, from }), [options, from])
|
|
410
115
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
116
|
+
const next = React.useMemo(
|
|
117
|
+
() => router.buildLocation(_options as any),
|
|
118
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
119
|
+
[router, _options, currentSearch],
|
|
120
|
+
)
|
|
415
121
|
|
|
416
|
-
const
|
|
417
|
-
|
|
122
|
+
const preload = React.useMemo(() => {
|
|
123
|
+
if (_options.reloadDocument) {
|
|
124
|
+
return false
|
|
125
|
+
}
|
|
126
|
+
return userPreload ?? router.options.defaultPreload
|
|
127
|
+
}, [router.options.defaultPreload, userPreload, _options.reloadDocument])
|
|
418
128
|
const preloadDelay =
|
|
419
129
|
userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0
|
|
420
130
|
|
|
421
131
|
const isActive = useRouterState({
|
|
422
132
|
select: (s) => {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
133
|
+
if (activeOptions?.exact) {
|
|
134
|
+
const testExact = exactPathTest(
|
|
135
|
+
s.location.pathname,
|
|
136
|
+
next.pathname,
|
|
137
|
+
router.basepath,
|
|
138
|
+
)
|
|
139
|
+
if (!testExact) {
|
|
140
|
+
return false
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
const currentPathSplit = removeTrailingSlash(
|
|
144
|
+
s.location.pathname,
|
|
145
|
+
router.basepath,
|
|
146
|
+
).split('/')
|
|
147
|
+
const nextPathSplit = removeTrailingSlash(
|
|
148
|
+
next.pathname,
|
|
149
|
+
router.basepath,
|
|
150
|
+
).split('/')
|
|
151
|
+
|
|
152
|
+
const pathIsFuzzyEqual = nextPathSplit.every(
|
|
153
|
+
(d, i) => d === currentPathSplit[i],
|
|
154
|
+
)
|
|
155
|
+
if (!pathIsFuzzyEqual) {
|
|
156
|
+
return false
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (activeOptions?.includeSearch ?? true) {
|
|
161
|
+
const searchTest = deepEqual(s.location.search, next.search, {
|
|
162
|
+
partial: !activeOptions?.exact,
|
|
163
|
+
ignoreUndefined: !activeOptions?.explicitUndefined,
|
|
164
|
+
})
|
|
165
|
+
if (!searchTest) {
|
|
166
|
+
return false
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (activeOptions?.includeHash) {
|
|
171
|
+
return s.location.hash === next.hash
|
|
172
|
+
}
|
|
173
|
+
return true
|
|
443
174
|
},
|
|
444
175
|
})
|
|
445
176
|
|
|
177
|
+
const doPreload = React.useCallback(() => {
|
|
178
|
+
router.preloadRoute(_options as any).catch((err) => {
|
|
179
|
+
console.warn(err)
|
|
180
|
+
console.warn(preloadWarning)
|
|
181
|
+
})
|
|
182
|
+
}, [_options, router])
|
|
183
|
+
|
|
184
|
+
const preloadViewportIoCallback = React.useCallback(
|
|
185
|
+
(entry: IntersectionObserverEntry | undefined) => {
|
|
186
|
+
if (entry?.isIntersecting) {
|
|
187
|
+
doPreload()
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
[doPreload],
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
useIntersectionObserver(
|
|
194
|
+
innerRef,
|
|
195
|
+
preloadViewportIoCallback,
|
|
196
|
+
{ rootMargin: '100px' },
|
|
197
|
+
{ disabled: !!disabled || !(preload === 'viewport') },
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
useLayoutEffect(() => {
|
|
201
|
+
if (hasRenderFetched.current) {
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
if (!disabled && preload === 'render') {
|
|
205
|
+
doPreload()
|
|
206
|
+
hasRenderFetched.current = true
|
|
207
|
+
}
|
|
208
|
+
}, [disabled, doPreload, preload])
|
|
209
|
+
|
|
446
210
|
if (type === 'external') {
|
|
447
211
|
return {
|
|
448
|
-
...
|
|
212
|
+
...propsSafeToSpread,
|
|
213
|
+
ref: innerRef as React.ComponentPropsWithRef<'a'>['ref'],
|
|
449
214
|
type,
|
|
450
215
|
href: to,
|
|
451
|
-
children,
|
|
452
|
-
target,
|
|
453
|
-
disabled,
|
|
454
|
-
style,
|
|
455
|
-
className,
|
|
456
|
-
onClick,
|
|
457
|
-
onFocus,
|
|
458
|
-
onMouseEnter,
|
|
459
|
-
onMouseLeave,
|
|
460
|
-
onTouchStart,
|
|
216
|
+
...(children && { children }),
|
|
217
|
+
...(target && { target }),
|
|
218
|
+
...(disabled && { disabled }),
|
|
219
|
+
...(style && { style }),
|
|
220
|
+
...(className && { className }),
|
|
221
|
+
...(onClick && { onClick }),
|
|
222
|
+
...(onFocus && { onFocus }),
|
|
223
|
+
...(onMouseEnter && { onMouseEnter }),
|
|
224
|
+
...(onMouseLeave && { onMouseLeave }),
|
|
225
|
+
...(onTouchStart && { onTouchStart }),
|
|
461
226
|
}
|
|
462
227
|
}
|
|
463
228
|
|
|
@@ -472,22 +237,31 @@ export function useLinkProps<
|
|
|
472
237
|
) {
|
|
473
238
|
e.preventDefault()
|
|
474
239
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
}
|
|
240
|
+
flushSync(() => {
|
|
241
|
+
setIsTransitioning(true)
|
|
242
|
+
})
|
|
479
243
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
console.warn(err)
|
|
484
|
-
console.warn(preloadWarning)
|
|
244
|
+
const unsub = router.subscribe('onResolved', () => {
|
|
245
|
+
unsub()
|
|
246
|
+
setIsTransitioning(false)
|
|
485
247
|
})
|
|
486
|
-
|
|
248
|
+
|
|
249
|
+
// All is well? Navigate!
|
|
250
|
+
// N.B. we don't call `router.commitLocation(next) here because we want to run `validateSearch` before committing
|
|
251
|
+
return router.navigate({
|
|
252
|
+
..._options,
|
|
253
|
+
replace,
|
|
254
|
+
resetScroll,
|
|
255
|
+
hashScrollIntoView,
|
|
256
|
+
startTransition,
|
|
257
|
+
viewTransition,
|
|
258
|
+
ignoreBlocker,
|
|
259
|
+
} as any)
|
|
260
|
+
}
|
|
487
261
|
}
|
|
488
262
|
|
|
489
263
|
// The click handler
|
|
490
|
-
const handleFocus = (
|
|
264
|
+
const handleFocus = (_: MouseEvent) => {
|
|
491
265
|
if (disabled) return
|
|
492
266
|
if (preload) {
|
|
493
267
|
doPreload()
|
|
@@ -498,15 +272,15 @@ export function useLinkProps<
|
|
|
498
272
|
|
|
499
273
|
const handleEnter = (e: MouseEvent) => {
|
|
500
274
|
if (disabled) return
|
|
501
|
-
const
|
|
275
|
+
const eventTarget = (e.target || {}) as LinkCurrentTargetElement
|
|
502
276
|
|
|
503
277
|
if (preload) {
|
|
504
|
-
if (
|
|
278
|
+
if (eventTarget.preloadTimeout) {
|
|
505
279
|
return
|
|
506
280
|
}
|
|
507
281
|
|
|
508
|
-
|
|
509
|
-
|
|
282
|
+
eventTarget.preloadTimeout = setTimeout(() => {
|
|
283
|
+
eventTarget.preloadTimeout = null
|
|
510
284
|
doPreload()
|
|
511
285
|
}, preloadDelay)
|
|
512
286
|
}
|
|
@@ -514,18 +288,18 @@ export function useLinkProps<
|
|
|
514
288
|
|
|
515
289
|
const handleLeave = (e: MouseEvent) => {
|
|
516
290
|
if (disabled) return
|
|
517
|
-
const
|
|
291
|
+
const eventTarget = (e.target || {}) as LinkCurrentTargetElement
|
|
518
292
|
|
|
519
|
-
if (
|
|
520
|
-
clearTimeout(
|
|
521
|
-
|
|
293
|
+
if (eventTarget.preloadTimeout) {
|
|
294
|
+
clearTimeout(eventTarget.preloadTimeout)
|
|
295
|
+
eventTarget.preloadTimeout = null
|
|
522
296
|
}
|
|
523
297
|
}
|
|
524
298
|
|
|
525
299
|
const composeHandlers =
|
|
526
|
-
(handlers:
|
|
527
|
-
(e:
|
|
528
|
-
|
|
300
|
+
(handlers: Array<undefined | ((e: any) => void)>) =>
|
|
301
|
+
(e: { persist?: () => void; defaultPrevented: boolean }) => {
|
|
302
|
+
e.persist?.()
|
|
529
303
|
handlers.filter(Boolean).forEach((handler) => {
|
|
530
304
|
if (e.defaultPrevented) return
|
|
531
305
|
handler!(e)
|
|
@@ -534,143 +308,244 @@ export function useLinkProps<
|
|
|
534
308
|
|
|
535
309
|
// Get the active props
|
|
536
310
|
const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> = isActive
|
|
537
|
-
? functionalUpdate(activeProps as any, {}) ?? {}
|
|
311
|
+
? (functionalUpdate(activeProps as any, {}) ?? {})
|
|
538
312
|
: {}
|
|
539
313
|
|
|
540
314
|
// Get the inactive props
|
|
541
315
|
const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =
|
|
542
|
-
isActive ? {} : functionalUpdate(inactiveProps, {})
|
|
316
|
+
isActive ? {} : functionalUpdate(inactiveProps, {})
|
|
317
|
+
|
|
318
|
+
const resolvedClassName = [
|
|
319
|
+
className,
|
|
320
|
+
resolvedActiveProps.className,
|
|
321
|
+
resolvedInactiveProps.className,
|
|
322
|
+
]
|
|
323
|
+
.filter(Boolean)
|
|
324
|
+
.join(' ')
|
|
325
|
+
|
|
326
|
+
const resolvedStyle = {
|
|
327
|
+
...style,
|
|
328
|
+
...resolvedActiveProps.style,
|
|
329
|
+
...resolvedInactiveProps.style,
|
|
330
|
+
}
|
|
543
331
|
|
|
544
332
|
return {
|
|
333
|
+
...propsSafeToSpread,
|
|
545
334
|
...resolvedActiveProps,
|
|
546
335
|
...resolvedInactiveProps,
|
|
547
|
-
...rest,
|
|
548
336
|
href: disabled
|
|
549
337
|
? undefined
|
|
550
338
|
: next.maskedLocation
|
|
551
|
-
? next.maskedLocation.href
|
|
552
|
-
: next.href,
|
|
339
|
+
? router.history.createHref(next.maskedLocation.href)
|
|
340
|
+
: router.history.createHref(next.href),
|
|
341
|
+
ref: innerRef as React.ComponentPropsWithRef<'a'>['ref'],
|
|
553
342
|
onClick: composeHandlers([onClick, handleClick]),
|
|
554
343
|
onFocus: composeHandlers([onFocus, handleFocus]),
|
|
555
344
|
onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),
|
|
556
345
|
onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),
|
|
557
346
|
onTouchStart: composeHandlers([onTouchStart, handleTouchStart]),
|
|
347
|
+
disabled: !!disabled,
|
|
558
348
|
target,
|
|
559
|
-
style:
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
resolvedActiveProps.className,
|
|
568
|
-
resolvedInactiveProps.className,
|
|
569
|
-
]
|
|
570
|
-
.filter(Boolean)
|
|
571
|
-
.join(' ') || undefined,
|
|
572
|
-
...(disabled
|
|
573
|
-
? {
|
|
574
|
-
role: 'link',
|
|
575
|
-
'aria-disabled': true,
|
|
576
|
-
}
|
|
577
|
-
: undefined),
|
|
578
|
-
['data-status']: isActive ? 'active' : undefined,
|
|
349
|
+
...(Object.keys(resolvedStyle).length && { style: resolvedStyle }),
|
|
350
|
+
...(resolvedClassName && { className: resolvedClassName }),
|
|
351
|
+
...(disabled && {
|
|
352
|
+
role: 'link',
|
|
353
|
+
'aria-disabled': true,
|
|
354
|
+
}),
|
|
355
|
+
...(isActive && { 'data-status': 'active', 'aria-current': 'page' }),
|
|
356
|
+
...(isTransitioning && { 'data-transitioning': 'transitioning' }),
|
|
579
357
|
}
|
|
580
358
|
}
|
|
581
359
|
|
|
360
|
+
type UseLinkReactProps<TComp> = TComp extends keyof React.JSX.IntrinsicElements
|
|
361
|
+
? React.JSX.IntrinsicElements[TComp]
|
|
362
|
+
: React.PropsWithoutRef<
|
|
363
|
+
TComp extends React.ComponentType<infer TProps> ? TProps : never
|
|
364
|
+
> &
|
|
365
|
+
React.RefAttributes<
|
|
366
|
+
TComp extends
|
|
367
|
+
| React.FC<{ ref: infer TRef }>
|
|
368
|
+
| React.Component<{ ref: infer TRef }>
|
|
369
|
+
? TRef
|
|
370
|
+
: never
|
|
371
|
+
>
|
|
372
|
+
|
|
582
373
|
export type UseLinkPropsOptions<
|
|
583
|
-
|
|
584
|
-
TFrom extends RoutePaths<
|
|
585
|
-
TTo extends string = '',
|
|
586
|
-
TMaskFrom extends RoutePaths<
|
|
587
|
-
TMaskTo extends string = '',
|
|
588
|
-
> = ActiveLinkOptions<
|
|
589
|
-
|
|
374
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
375
|
+
TFrom extends RoutePaths<TRouter['routeTree']> | string = string,
|
|
376
|
+
TTo extends string | undefined = '.',
|
|
377
|
+
TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,
|
|
378
|
+
TMaskTo extends string = '.',
|
|
379
|
+
> = ActiveLinkOptions<'a', TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &
|
|
380
|
+
UseLinkReactProps<'a'>
|
|
590
381
|
|
|
591
382
|
export type ActiveLinkOptions<
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
383
|
+
TComp = 'a',
|
|
384
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
385
|
+
TFrom extends string = string,
|
|
386
|
+
TTo extends string | undefined = '.',
|
|
387
|
+
TMaskFrom extends string = TFrom,
|
|
388
|
+
TMaskTo extends string = '.',
|
|
389
|
+
> = LinkOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &
|
|
390
|
+
ActiveLinkOptionProps<TComp>
|
|
391
|
+
|
|
392
|
+
type ActiveLinkProps<TComp> = Partial<
|
|
393
|
+
LinkComponentReactProps<TComp> & {
|
|
394
|
+
[key: `data-${string}`]: unknown
|
|
395
|
+
}
|
|
396
|
+
>
|
|
397
|
+
|
|
398
|
+
export interface ActiveLinkOptionProps<TComp = 'a'> {
|
|
399
|
+
/**
|
|
400
|
+
* A function that returns additional props for the `active` state of this link.
|
|
401
|
+
* These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
|
|
402
|
+
*/
|
|
403
|
+
activeProps?: ActiveLinkProps<TComp> | (() => ActiveLinkProps<TComp>)
|
|
404
|
+
/**
|
|
405
|
+
* A function that returns additional props for the `inactive` state of this link.
|
|
406
|
+
* These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
|
|
407
|
+
*/
|
|
408
|
+
inactiveProps?: ActiveLinkProps<TComp> | (() => ActiveLinkProps<TComp>)
|
|
606
409
|
}
|
|
607
410
|
|
|
608
411
|
export type LinkProps<
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
412
|
+
TComp = 'a',
|
|
413
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
414
|
+
TFrom extends string = string,
|
|
415
|
+
TTo extends string | undefined = '.',
|
|
416
|
+
TMaskFrom extends string = TFrom,
|
|
417
|
+
TMaskTo extends string = '.',
|
|
418
|
+
> = ActiveLinkOptions<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &
|
|
419
|
+
LinkPropsChildren
|
|
420
|
+
|
|
421
|
+
export interface LinkPropsChildren {
|
|
615
422
|
// 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
|
|
616
423
|
children?:
|
|
617
424
|
| React.ReactNode
|
|
618
|
-
| ((state: {
|
|
425
|
+
| ((state: {
|
|
426
|
+
isActive: boolean
|
|
427
|
+
isTransitioning: boolean
|
|
428
|
+
}) => React.ReactNode)
|
|
619
429
|
}
|
|
620
430
|
|
|
621
|
-
type
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
431
|
+
type LinkComponentReactProps<TComp> = Omit<
|
|
432
|
+
UseLinkReactProps<TComp>,
|
|
433
|
+
keyof CreateLinkProps
|
|
434
|
+
>
|
|
435
|
+
|
|
436
|
+
export type LinkComponentProps<
|
|
437
|
+
TComp = 'a',
|
|
438
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
439
|
+
TFrom extends string = string,
|
|
440
|
+
TTo extends string | undefined = '.',
|
|
441
|
+
TMaskFrom extends string = TFrom,
|
|
442
|
+
TMaskTo extends string = '.',
|
|
443
|
+
> = LinkComponentReactProps<TComp> &
|
|
444
|
+
LinkProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>
|
|
445
|
+
|
|
446
|
+
export type CreateLinkProps = LinkProps<
|
|
447
|
+
any,
|
|
448
|
+
any,
|
|
449
|
+
string,
|
|
450
|
+
string,
|
|
451
|
+
string,
|
|
452
|
+
string
|
|
453
|
+
>
|
|
454
|
+
|
|
455
|
+
export type LinkComponent<
|
|
456
|
+
in out TComp,
|
|
457
|
+
in out TDefaultFrom extends string = string,
|
|
458
|
+
> = <
|
|
459
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
460
|
+
const TFrom extends string = TDefaultFrom,
|
|
461
|
+
const TTo extends string | undefined = undefined,
|
|
462
|
+
const TMaskFrom extends string = TFrom,
|
|
463
|
+
const TMaskTo extends string = '',
|
|
627
464
|
>(
|
|
628
|
-
props:
|
|
629
|
-
LinkProps<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
|
|
630
|
-
(TComp extends React.FC<infer TProps> | React.Component<infer TProps>
|
|
631
|
-
? TProps
|
|
632
|
-
: TComp extends keyof JSX.IntrinsicElements
|
|
633
|
-
? Omit<React.HTMLProps<TComp>, 'children' | 'preload'>
|
|
634
|
-
: never)
|
|
635
|
-
> &
|
|
636
|
-
React.RefAttributes<
|
|
637
|
-
TComp extends
|
|
638
|
-
| React.FC<{ ref: infer TRef }>
|
|
639
|
-
| React.Component<{ ref: infer TRef }>
|
|
640
|
-
? TRef
|
|
641
|
-
: TComp extends keyof JSX.IntrinsicElements
|
|
642
|
-
? React.ComponentRef<TComp>
|
|
643
|
-
: never
|
|
644
|
-
>,
|
|
465
|
+
props: LinkComponentProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,
|
|
645
466
|
) => React.ReactElement
|
|
646
467
|
|
|
647
|
-
export
|
|
648
|
-
|
|
468
|
+
export interface LinkComponentRoute<
|
|
469
|
+
in out TDefaultFrom extends string = string,
|
|
470
|
+
> {
|
|
471
|
+
defaultFrom: TDefaultFrom
|
|
472
|
+
<
|
|
473
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
474
|
+
const TTo extends string | undefined = undefined,
|
|
475
|
+
const TMaskTo extends string = '',
|
|
476
|
+
>(
|
|
477
|
+
props: LinkComponentProps<
|
|
478
|
+
'a',
|
|
479
|
+
TRouter,
|
|
480
|
+
this['defaultFrom'],
|
|
481
|
+
TTo,
|
|
482
|
+
this['defaultFrom'],
|
|
483
|
+
TMaskTo
|
|
484
|
+
>,
|
|
485
|
+
): React.ReactElement
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
export function createLink<const TComp>(
|
|
489
|
+
Comp: Constrain<TComp, any, (props: CreateLinkProps) => ReactNode>,
|
|
490
|
+
): LinkComponent<TComp> {
|
|
491
|
+
return React.forwardRef(function CreatedLink(props, ref) {
|
|
649
492
|
return <Link {...(props as any)} _asChild={Comp} ref={ref} />
|
|
650
493
|
}) as any
|
|
651
494
|
}
|
|
652
495
|
|
|
653
|
-
export const Link: LinkComponent<'a'> = React.forwardRef
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
496
|
+
export const Link: LinkComponent<'a'> = React.forwardRef<Element, any>(
|
|
497
|
+
(props, ref) => {
|
|
498
|
+
const { _asChild, ...rest } = props
|
|
499
|
+
const {
|
|
500
|
+
type: _type,
|
|
501
|
+
ref: innerRef,
|
|
502
|
+
...linkProps
|
|
503
|
+
} = useLinkProps(rest as any, ref)
|
|
504
|
+
|
|
505
|
+
const children =
|
|
506
|
+
typeof rest.children === 'function'
|
|
507
|
+
? rest.children({
|
|
508
|
+
isActive: (linkProps as any)['data-status'] === 'active',
|
|
509
|
+
})
|
|
510
|
+
: rest.children
|
|
511
|
+
|
|
512
|
+
if (typeof _asChild === 'undefined') {
|
|
513
|
+
// the ReturnType of useLinkProps returns the correct type for a <a> element, not a general component that has a disabled prop
|
|
514
|
+
// @ts-expect-error
|
|
515
|
+
delete linkProps.disabled
|
|
516
|
+
}
|
|
663
517
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
}
|
|
518
|
+
return React.createElement(
|
|
519
|
+
_asChild ? _asChild : 'a',
|
|
520
|
+
{
|
|
521
|
+
...linkProps,
|
|
522
|
+
ref: innerRef,
|
|
523
|
+
},
|
|
524
|
+
children,
|
|
525
|
+
)
|
|
526
|
+
},
|
|
527
|
+
) as any
|
|
673
528
|
|
|
674
529
|
function isCtrlEvent(e: MouseEvent) {
|
|
675
530
|
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
|
|
676
531
|
}
|
|
532
|
+
|
|
533
|
+
export type LinkOptionsFnOptions<
|
|
534
|
+
TOptions,
|
|
535
|
+
TComp,
|
|
536
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
537
|
+
> =
|
|
538
|
+
TOptions extends ReadonlyArray<any>
|
|
539
|
+
? ValidateLinkOptionsArray<TRouter, TOptions, string, TComp>
|
|
540
|
+
: ValidateLinkOptions<TRouter, TOptions, string, TComp>
|
|
541
|
+
|
|
542
|
+
export type LinkOptionsFn<TComp> = <
|
|
543
|
+
const TOptions,
|
|
544
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
545
|
+
>(
|
|
546
|
+
options: LinkOptionsFnOptions<TOptions, TComp, TRouter>,
|
|
547
|
+
) => TOptions
|
|
548
|
+
|
|
549
|
+
export const linkOptions: LinkOptionsFn<'a'> = (options) => {
|
|
550
|
+
return options as any
|
|
551
|
+
}
|