@tanstack/react-router 0.0.1-beta.9 → 1.0.0

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.
Files changed (229) hide show
  1. package/LICENSE +21 -0
  2. package/build/cjs/CatchBoundary.js +128 -0
  3. package/build/cjs/CatchBoundary.js.map +1 -0
  4. package/build/cjs/Matches.js +233 -0
  5. package/build/cjs/Matches.js.map +1 -0
  6. package/build/cjs/RouterProvider.js +170 -0
  7. package/build/cjs/RouterProvider.js.map +1 -0
  8. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +2 -22
  9. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
  10. package/build/cjs/_virtual/with-selector.development.js +16 -0
  11. package/build/cjs/_virtual/with-selector.development.js.map +1 -0
  12. package/build/cjs/_virtual/with-selector.js +16 -0
  13. package/build/cjs/_virtual/with-selector.js.map +1 -0
  14. package/build/cjs/_virtual/with-selector.production.min.js +16 -0
  15. package/build/cjs/_virtual/with-selector.production.min.js.map +1 -0
  16. package/build/cjs/awaited.js +43 -0
  17. package/build/cjs/awaited.js.map +1 -0
  18. package/build/cjs/build/esm/index.js +79 -0
  19. package/build/cjs/build/esm/index.js.map +1 -0
  20. package/build/cjs/defer.js +37 -0
  21. package/build/cjs/defer.js.map +1 -0
  22. package/build/cjs/fileRoute.js +27 -0
  23. package/build/cjs/fileRoute.js.map +1 -0
  24. package/build/cjs/index.js +130 -0
  25. package/build/cjs/index.js.map +1 -0
  26. package/build/cjs/lazyRouteComponent.js +54 -0
  27. package/build/cjs/lazyRouteComponent.js.map +1 -0
  28. package/build/cjs/link.js +223 -0
  29. package/build/cjs/link.js.map +1 -0
  30. package/build/cjs/node_modules/.pnpm/@tanstack_react-store@0.2.1_react-dom@18.2.0_react@18.2.0/node_modules/@tanstack/react-store/build/modern/index.js +47 -0
  31. package/build/cjs/node_modules/.pnpm/@tanstack_react-store@0.2.1_react-dom@18.2.0_react@18.2.0/node_modules/@tanstack/react-store/build/modern/index.js.map +1 -0
  32. package/build/cjs/node_modules/.pnpm/@tanstack_store@0.1.3/node_modules/@tanstack/store/build/modern/index.js +70 -0
  33. package/build/cjs/node_modules/.pnpm/@tanstack_store@0.1.3/node_modules/@tanstack/store/build/modern/index.js.map +1 -0
  34. package/build/cjs/node_modules/.pnpm/use-sync-external-store@1.2.0_react@18.2.0/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.js +188 -0
  35. package/build/cjs/node_modules/.pnpm/use-sync-external-store@1.2.0_react@18.2.0/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.js.map +1 -0
  36. package/build/cjs/node_modules/.pnpm/use-sync-external-store@1.2.0_react@18.2.0/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.js +39 -0
  37. package/build/cjs/node_modules/.pnpm/use-sync-external-store@1.2.0_react@18.2.0/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.js.map +1 -0
  38. package/build/cjs/node_modules/.pnpm/use-sync-external-store@1.2.0_react@18.2.0/node_modules/use-sync-external-store/shim/with-selector.js +26 -0
  39. package/build/cjs/node_modules/.pnpm/use-sync-external-store@1.2.0_react@18.2.0/node_modules/use-sync-external-store/shim/with-selector.js.map +1 -0
  40. package/build/cjs/packages/react-router/src/CatchBoundary.js +123 -0
  41. package/build/cjs/packages/react-router/src/CatchBoundary.js.map +1 -0
  42. package/build/cjs/packages/react-router/src/Matches.js +235 -0
  43. package/build/cjs/packages/react-router/src/Matches.js.map +1 -0
  44. package/build/cjs/packages/react-router/src/RouterProvider.js +144 -0
  45. package/build/cjs/packages/react-router/src/RouterProvider.js.map +1 -0
  46. package/build/cjs/packages/react-router/src/awaited.js +43 -0
  47. package/build/cjs/packages/react-router/src/awaited.js.map +1 -0
  48. package/build/cjs/packages/react-router/src/defer.js +37 -0
  49. package/build/cjs/packages/react-router/src/defer.js.map +1 -0
  50. package/build/cjs/packages/react-router/src/fileRoute.js +27 -0
  51. package/build/cjs/packages/react-router/src/fileRoute.js.map +1 -0
  52. package/build/cjs/packages/react-router/src/index.js +61 -0
  53. package/build/cjs/packages/react-router/src/index.js.map +1 -0
  54. package/build/cjs/packages/react-router/src/lazyRouteComponent.js +54 -0
  55. package/build/cjs/packages/react-router/src/lazyRouteComponent.js.map +1 -0
  56. package/build/cjs/packages/react-router/src/link.js +148 -0
  57. package/build/cjs/packages/react-router/src/link.js.map +1 -0
  58. package/build/cjs/packages/react-router/src/path.js +209 -0
  59. package/build/cjs/packages/react-router/src/path.js.map +1 -0
  60. package/build/cjs/packages/react-router/src/qss.js +63 -0
  61. package/build/cjs/packages/react-router/src/qss.js.map +1 -0
  62. package/build/cjs/packages/react-router/src/react.js +634 -0
  63. package/build/cjs/packages/react-router/src/react.js.map +1 -0
  64. package/build/cjs/packages/react-router/src/redirects.js +25 -0
  65. package/build/cjs/packages/react-router/src/redirects.js.map +1 -0
  66. package/build/cjs/packages/react-router/src/route.js +134 -0
  67. package/build/cjs/packages/react-router/src/route.js.map +1 -0
  68. package/build/cjs/packages/react-router/src/router.js +1111 -0
  69. package/build/cjs/packages/react-router/src/router.js.map +1 -0
  70. package/build/cjs/packages/react-router/src/scroll-restoration.js +53 -0
  71. package/build/cjs/packages/react-router/src/scroll-restoration.js.map +1 -0
  72. package/build/cjs/packages/react-router/src/searchParams.js +81 -0
  73. package/build/cjs/packages/react-router/src/searchParams.js.map +1 -0
  74. package/build/cjs/packages/react-router/src/useBlocker.js +61 -0
  75. package/build/cjs/packages/react-router/src/useBlocker.js.map +1 -0
  76. package/build/cjs/packages/react-router/src/useNavigate.js +75 -0
  77. package/build/cjs/packages/react-router/src/useNavigate.js.map +1 -0
  78. package/build/cjs/packages/react-router/src/useParams.js +26 -0
  79. package/build/cjs/packages/react-router/src/useParams.js.map +1 -0
  80. package/build/cjs/packages/react-router/src/useSearch.js +25 -0
  81. package/build/cjs/packages/react-router/src/useSearch.js.map +1 -0
  82. package/build/cjs/packages/react-router/src/utils.js +239 -0
  83. package/build/cjs/packages/react-router/src/utils.js.map +1 -0
  84. package/build/cjs/path.js +214 -0
  85. package/build/cjs/path.js.map +1 -0
  86. package/build/cjs/qss.js +63 -0
  87. package/build/cjs/qss.js.map +1 -0
  88. package/build/cjs/react/CatchBoundary.js +123 -0
  89. package/build/cjs/react/CatchBoundary.js.map +1 -0
  90. package/build/cjs/react/awaited.js +43 -0
  91. package/build/cjs/react/awaited.js.map +1 -0
  92. package/build/cjs/react/defer.js +37 -0
  93. package/build/cjs/react/defer.js.map +1 -0
  94. package/build/cjs/react.js +650 -0
  95. package/build/cjs/react.js.map +1 -0
  96. package/build/cjs/redirects.js +28 -0
  97. package/build/cjs/redirects.js.map +1 -0
  98. package/build/cjs/route.js +191 -0
  99. package/build/cjs/route.js.map +1 -0
  100. package/build/cjs/router.js +1085 -0
  101. package/build/cjs/router.js.map +1 -0
  102. package/build/cjs/routerConfig.js +209 -0
  103. package/build/cjs/routerConfig.js.map +1 -0
  104. package/build/cjs/scroll-restoration.js +202 -0
  105. package/build/cjs/scroll-restoration.js.map +1 -0
  106. package/build/cjs/searchParams.js +81 -0
  107. package/build/cjs/searchParams.js.map +1 -0
  108. package/build/cjs/src/CatchBoundary.js +126 -0
  109. package/build/cjs/src/CatchBoundary.js.map +1 -0
  110. package/build/cjs/src/Matches.js +235 -0
  111. package/build/cjs/src/Matches.js.map +1 -0
  112. package/build/cjs/src/RouterProvider.js +1051 -0
  113. package/build/cjs/src/RouterProvider.js.map +1 -0
  114. package/build/cjs/src/awaited.js +45 -0
  115. package/build/cjs/src/awaited.js.map +1 -0
  116. package/build/cjs/src/defer.js +39 -0
  117. package/build/cjs/src/defer.js.map +1 -0
  118. package/build/cjs/src/fileRoute.js +29 -0
  119. package/build/cjs/src/fileRoute.js.map +1 -0
  120. package/build/cjs/src/index.js +134 -0
  121. package/build/cjs/src/index.js.map +1 -0
  122. package/build/cjs/src/lazyRouteComponent.js +57 -0
  123. package/build/cjs/src/lazyRouteComponent.js.map +1 -0
  124. package/build/cjs/src/link.js +151 -0
  125. package/build/cjs/src/link.js.map +1 -0
  126. package/build/cjs/src/path.js +211 -0
  127. package/build/cjs/src/path.js.map +1 -0
  128. package/build/cjs/src/qss.js +65 -0
  129. package/build/cjs/src/qss.js.map +1 -0
  130. package/build/cjs/src/redirects.js +27 -0
  131. package/build/cjs/src/redirects.js.map +1 -0
  132. package/build/cjs/src/route.js +139 -0
  133. package/build/cjs/src/route.js.map +1 -0
  134. package/build/cjs/src/router.js +203 -0
  135. package/build/cjs/src/router.js.map +1 -0
  136. package/build/cjs/src/scroll-restoration.js +186 -0
  137. package/build/cjs/src/scroll-restoration.js.map +1 -0
  138. package/build/cjs/src/searchParams.js +83 -0
  139. package/build/cjs/src/searchParams.js.map +1 -0
  140. package/build/cjs/src/useBlocker.js +64 -0
  141. package/build/cjs/src/useBlocker.js.map +1 -0
  142. package/build/cjs/src/useNavigate.js +78 -0
  143. package/build/cjs/src/useNavigate.js.map +1 -0
  144. package/build/cjs/src/useParams.js +28 -0
  145. package/build/cjs/src/useParams.js.map +1 -0
  146. package/build/cjs/src/useSearch.js +27 -0
  147. package/build/cjs/src/useSearch.js.map +1 -0
  148. package/build/cjs/src/utils.js +230 -0
  149. package/build/cjs/src/utils.js.map +1 -0
  150. package/build/cjs/useBlocker.js +55 -0
  151. package/build/cjs/useBlocker.js.map +1 -0
  152. package/build/cjs/useNavigate.js +86 -0
  153. package/build/cjs/useNavigate.js.map +1 -0
  154. package/build/cjs/useParams.js +26 -0
  155. package/build/cjs/useParams.js.map +1 -0
  156. package/build/cjs/useSearch.js +25 -0
  157. package/build/cjs/useSearch.js.map +1 -0
  158. package/build/cjs/useStore.js +99 -0
  159. package/build/cjs/useStore.js.map +1 -0
  160. package/build/cjs/utils.js +241 -0
  161. package/build/cjs/utils.js.map +1 -0
  162. package/build/esm/index.js +2300 -2534
  163. package/build/esm/index.js.map +1 -1
  164. package/build/stats-html.html +3498 -2694
  165. package/build/stats-react.json +1204 -44
  166. package/build/types/CatchBoundary.d.ts +36 -0
  167. package/build/types/Matches.d.ts +64 -0
  168. package/build/types/RouteMatch.d.ts +23 -0
  169. package/build/types/RouterProvider.d.ts +35 -0
  170. package/build/types/awaited.d.ts +9 -0
  171. package/build/types/defer.d.ts +19 -0
  172. package/build/types/fileRoute.d.ts +38 -0
  173. package/build/types/history.d.ts +7 -0
  174. package/build/types/index.d.ts +919 -58
  175. package/build/types/injectHtml.d.ts +0 -0
  176. package/build/types/lazyRouteComponent.d.ts +2 -0
  177. package/build/types/link.d.ts +93 -0
  178. package/build/types/location.d.ts +12 -0
  179. package/build/types/path.d.ts +17 -0
  180. package/build/types/qss.d.ts +2 -0
  181. package/build/types/react/CatchBoundary.d.ts +33 -0
  182. package/build/types/react/awaited.d.ts +9 -0
  183. package/build/types/react/defer.d.ts +19 -0
  184. package/build/types/react.d.ts +141 -0
  185. package/build/types/redirects.d.ts +11 -0
  186. package/build/types/route.d.ts +283 -0
  187. package/build/types/routeInfo.d.ts +31 -0
  188. package/build/types/router.d.ts +186 -0
  189. package/build/types/scroll-restoration.d.ts +18 -0
  190. package/build/types/searchParams.d.ts +7 -0
  191. package/build/types/useBlocker.d.ts +9 -0
  192. package/build/types/useNavigate.d.ts +19 -0
  193. package/build/types/useParams.d.ts +7 -0
  194. package/build/types/useSearch.d.ts +7 -0
  195. package/build/types/useStore.d.ts +12 -0
  196. package/build/types/utils.d.ts +69 -0
  197. package/build/umd/index.development.js +2897 -2493
  198. package/build/umd/index.development.js.map +1 -1
  199. package/build/umd/index.production.js +4 -4
  200. package/build/umd/index.production.js.map +1 -1
  201. package/package.json +12 -10
  202. package/src/CatchBoundary.tsx +101 -0
  203. package/src/Matches.tsx +423 -0
  204. package/src/RouterProvider.tsx +252 -0
  205. package/src/awaited.tsx +40 -0
  206. package/src/defer.ts +55 -0
  207. package/src/fileRoute.ts +152 -0
  208. package/src/history.ts +8 -0
  209. package/src/index.tsx +28 -619
  210. package/src/lazyRouteComponent.tsx +33 -0
  211. package/src/link.tsx +603 -0
  212. package/src/location.ts +13 -0
  213. package/src/path.ts +261 -0
  214. package/src/qss.ts +53 -0
  215. package/src/redirects.ts +39 -0
  216. package/src/route.ts +882 -0
  217. package/src/routeInfo.ts +84 -0
  218. package/src/router.ts +1671 -0
  219. package/src/scroll-restoration.tsx +230 -0
  220. package/src/searchParams.ts +79 -0
  221. package/src/useBlocker.tsx +27 -0
  222. package/src/useNavigate.tsx +111 -0
  223. package/src/useParams.tsx +25 -0
  224. package/src/useSearch.tsx +25 -0
  225. package/src/utils.ts +360 -0
  226. package/build/cjs/react-router/src/index.js +0 -458
  227. package/build/cjs/react-router/src/index.js.map +0 -1
  228. package/build/cjs/router-core/build/esm/index.js +0 -2524
  229. package/build/cjs/router-core/build/esm/index.js.map +0 -1
package/src/link.tsx ADDED
@@ -0,0 +1,603 @@
1
+ import * as React from 'react'
2
+ import { useMatch } from './Matches'
3
+ import { useRouter, useRouterState } from './RouterProvider'
4
+ import { Trim } from './fileRoute'
5
+ import { AnyRoute, ReactNode } from './route'
6
+ import {
7
+ AllParams,
8
+ FullSearchSchema,
9
+ RouteByPath,
10
+ RouteIds,
11
+ RoutePaths,
12
+ } from './routeInfo'
13
+ import { RegisteredRouter } from './router'
14
+ import { LinkProps, UseLinkPropsOptions } from './useNavigate'
15
+ import {
16
+ Expand,
17
+ NoInfer,
18
+ NonNullableUpdater,
19
+ PickRequired,
20
+ UnionToIntersection,
21
+ Updater,
22
+ deepEqual,
23
+ functionalUpdate,
24
+ } from './utils'
25
+ import { HistoryState } from '@tanstack/history'
26
+
27
+ export type CleanPath<T extends string> = T extends `${infer L}//${infer R}`
28
+ ? CleanPath<`${CleanPath<L>}/${CleanPath<R>}`>
29
+ : T extends `${infer L}//`
30
+ ? `${CleanPath<L>}/`
31
+ : T extends `//${infer L}`
32
+ ? `/${CleanPath<L>}`
33
+ : T
34
+
35
+ export type Split<S, TIncludeTrailingSlash = true> = S extends unknown
36
+ ? string extends S
37
+ ? string[]
38
+ : S extends string
39
+ ? CleanPath<S> extends ''
40
+ ? []
41
+ : TIncludeTrailingSlash extends true
42
+ ? CleanPath<S> extends `${infer T}/`
43
+ ? [...Split<T>, '/']
44
+ : CleanPath<S> extends `/${infer U}`
45
+ ? Split<U>
46
+ : CleanPath<S> extends `${infer T}/${infer U}`
47
+ ? [...Split<T>, ...Split<U>]
48
+ : [S]
49
+ : CleanPath<S> extends `${infer T}/${infer U}`
50
+ ? [...Split<T>, ...Split<U>]
51
+ : S extends string
52
+ ? [S]
53
+ : never
54
+ : never
55
+ : never
56
+
57
+ export type ParsePathParams<T extends string> = keyof {
58
+ [K in Trim<Split<T>[number], '_'> as K extends `$${infer L}` ? L : 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 RelativeToPathAutoComplete<
72
+ AllPaths extends string,
73
+ TFrom extends string,
74
+ TTo extends string,
75
+ SplitPaths extends string[] = Split<AllPaths, false>,
76
+ > = TTo extends `..${infer _}`
77
+ ? SplitPaths extends [
78
+ ...Split<ResolveRelativePath<TFrom, TTo>, false>,
79
+ ...infer TToRest,
80
+ ]
81
+ ? `${CleanPath<
82
+ Join<
83
+ [
84
+ ...Split<TTo, false>,
85
+ ...(
86
+ | TToRest
87
+ | (Split<
88
+ ResolveRelativePath<TFrom, TTo>,
89
+ false
90
+ >['length'] extends 1
91
+ ? never
92
+ : ['../'])
93
+ ),
94
+ ]
95
+ >
96
+ >}`
97
+ : never
98
+ : TTo extends `./${infer RestTTo}`
99
+ ? SplitPaths extends [
100
+ ...Split<TFrom, false>,
101
+ ...Split<RestTTo, false>,
102
+ ...infer RestPath,
103
+ ]
104
+ ? `${TTo}${Join<RestPath>}`
105
+ : never
106
+ :
107
+ | (TFrom extends `/`
108
+ ? never
109
+ : SplitPaths extends [...Split<TFrom, false>, ...infer RestPath]
110
+ ? Join<RestPath> extends { length: 0 }
111
+ ? never
112
+ : './'
113
+ : never)
114
+ | (TFrom extends `/` ? never : '../')
115
+ | AllPaths
116
+
117
+ export type NavigateOptions<
118
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
119
+ TFrom extends RoutePaths<TRouteTree> = '/',
120
+ TTo extends string = '',
121
+ TMaskFrom extends RoutePaths<TRouteTree> = TFrom,
122
+ TMaskTo extends string = '',
123
+ > = ToOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {
124
+ // `replace` is a boolean that determines whether the navigation should replace the current history entry or push a new one.
125
+ replace?: boolean
126
+ resetScroll?: boolean
127
+ // If set to `true`, the link's underlying navigate() call will be wrapped in a `React.startTransition` call. Defaults to `true`.
128
+ startTransition?: boolean
129
+ }
130
+
131
+ export type ToOptions<
132
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
133
+ TFrom extends RoutePaths<TRouteTree> = '/',
134
+ TTo extends string = '',
135
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
136
+ TMaskTo extends string = '',
137
+ > = ToSubOptions<TRouteTree, TFrom, TTo> & {
138
+ mask?: ToMaskOptions<TRouteTree, TMaskFrom, TMaskTo>
139
+ }
140
+
141
+ export type ToMaskOptions<
142
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
143
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
144
+ TMaskTo extends string = '',
145
+ > = ToSubOptions<TRouteTree, TMaskFrom, TMaskTo> & {
146
+ unmaskOnReload?: boolean
147
+ }
148
+
149
+ export type ToSubOptions<
150
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
151
+ TFrom extends RoutePaths<TRouteTree> = '/',
152
+ TTo extends string = '',
153
+ TResolved = ResolveRelativePath<TFrom, NoInfer<TTo>>,
154
+ > = {
155
+ to?: ToPathOption<TRouteTree, TFrom, TTo>
156
+ // The new has string or a function to update it
157
+ hash?: true | Updater<string>
158
+ // State to pass to the history stack
159
+ state?: true | NonNullableUpdater<HistoryState>
160
+ // 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
161
+ from?: TFrom
162
+ // // When using relative route paths, this option forces resolution from the current path, instead of the route API's path or `from` path
163
+ } & CheckPath<TRouteTree, NoInfer<TResolved>, {}> &
164
+ SearchParamOptions<TRouteTree, TFrom, TTo, TResolved> &
165
+ PathParamOptions<TRouteTree, TFrom, TResolved>
166
+
167
+ export type SearchParamOptions<
168
+ TRouteTree extends AnyRoute,
169
+ TFrom,
170
+ TTo,
171
+ TResolved = ResolveRelativePath<TFrom, NoInfer<TTo>>,
172
+ TFromSearchEnsured = '/' extends TFrom
173
+ ? FullSearchSchema<TRouteTree>
174
+ : Expand<
175
+ PickRequired<
176
+ RouteByPath<TRouteTree, TFrom>['types']['fullSearchSchema']
177
+ >
178
+ >,
179
+ TFromSearchOptional = Omit<
180
+ FullSearchSchema<TRouteTree>,
181
+ keyof TFromSearchEnsured
182
+ >,
183
+ TFromSearch = Expand<TFromSearchEnsured & TFromSearchOptional>,
184
+ TToSearch = '' extends TTo
185
+ ? FullSearchSchema<TRouteTree>
186
+ : Expand<RouteByPath<TRouteTree, TResolved>['types']['fullSearchSchema']>,
187
+ > = keyof PickRequired<TToSearch> extends never
188
+ ? {
189
+ search?: true | SearchReducer<TFromSearch, TToSearch>
190
+ }
191
+ : {
192
+ search: TFromSearchEnsured extends PickRequired<TToSearch>
193
+ ? true | SearchReducer<TFromSearch, TToSearch>
194
+ : SearchReducer<TFromSearch, TToSearch>
195
+ }
196
+
197
+ type SearchReducer<TFrom, TTo> = TTo | ((current: TFrom) => TTo)
198
+
199
+ export type PathParamOptions<
200
+ TRouteTree extends AnyRoute,
201
+ TFrom,
202
+ TTo,
203
+ TFromParamsEnsured = Expand<
204
+ UnionToIntersection<
205
+ PickRequired<RouteByPath<TRouteTree, TFrom>['types']['allParams']>
206
+ >
207
+ >,
208
+ TFromParamsOptional = Omit<AllParams<TRouteTree>, keyof TFromParamsEnsured>,
209
+ TFromParams = Expand<TFromParamsOptional & TFromParamsEnsured>,
210
+ TToParams = Expand<RouteByPath<TRouteTree, TTo>['types']['allParams']>,
211
+ > = never extends TToParams
212
+ ? {
213
+ params?: true | ParamsReducer<Partial<TFromParams>, Partial<TFromParams>>
214
+ }
215
+ : keyof PickRequired<TToParams> extends never
216
+ ? {
217
+ params?: true | ParamsReducer<TFromParams, TToParams>
218
+ }
219
+ : {
220
+ params: TFromParamsEnsured extends PickRequired<TToParams>
221
+ ? true | ParamsReducer<TFromParams, TToParams>
222
+ : ParamsReducer<TFromParams, TToParams>
223
+ }
224
+
225
+ type ParamsReducer<TFrom, TTo> = TTo | ((current: TFrom) => TTo)
226
+
227
+ export type ToPathOption<
228
+ TRouteTree extends AnyRoute = AnyRoute,
229
+ TFrom extends RoutePaths<TRouteTree> = '/',
230
+ TTo extends string = '',
231
+ > =
232
+ | TTo
233
+ | RelativeToPathAutoComplete<
234
+ RoutePaths<TRouteTree>,
235
+ NoInfer<TFrom> extends string ? NoInfer<TFrom> : '',
236
+ NoInfer<TTo> & string
237
+ >
238
+
239
+ export type ToIdOption<
240
+ TRouteTree extends AnyRoute = AnyRoute,
241
+ TFrom extends RoutePaths<TRouteTree> = '/',
242
+ TTo extends string = '',
243
+ > =
244
+ | TTo
245
+ | RelativeToPathAutoComplete<
246
+ RouteIds<TRouteTree>,
247
+ NoInfer<TFrom> extends string ? NoInfer<TFrom> : '',
248
+ NoInfer<TTo> & string
249
+ >
250
+
251
+ export interface ActiveOptions {
252
+ exact?: boolean
253
+ includeHash?: boolean
254
+ includeSearch?: boolean
255
+ }
256
+
257
+ export type LinkOptions<
258
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
259
+ TFrom extends RoutePaths<TRouteTree> = '/',
260
+ TTo extends string = '',
261
+ TMaskFrom extends RoutePaths<TRouteTree> = TFrom,
262
+ TMaskTo extends string = '',
263
+ > = NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {
264
+ // The standard anchor tag target attribute
265
+ target?: HTMLAnchorElement['target']
266
+ // Defaults to `{ exact: false, includeHash: false }`
267
+ activeOptions?: ActiveOptions
268
+ // 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.
269
+ preload?: false | 'intent'
270
+ // Delay intent preloading by this many milliseconds. If the intent exits before this delay, the preload will be cancelled.
271
+ preloadDelay?: number
272
+ // If true, will render the link without the href attribute
273
+ disabled?: boolean
274
+ }
275
+
276
+ export type CheckRelativePath<
277
+ TRouteTree extends AnyRoute,
278
+ TFrom,
279
+ TTo,
280
+ > = TTo extends string
281
+ ? TFrom extends string
282
+ ? ResolveRelativePath<TFrom, TTo> extends RoutePaths<TRouteTree>
283
+ ? {}
284
+ : {
285
+ Error: `${TFrom} + ${TTo} resolves to ${ResolveRelativePath<
286
+ TFrom,
287
+ TTo
288
+ >}, which is not a valid route path.`
289
+ 'Valid Route Paths': RoutePaths<TRouteTree>
290
+ }
291
+ : {}
292
+ : {}
293
+
294
+ export type CheckPath<TRouteTree extends AnyRoute, TPath, TPass> = Exclude<
295
+ TPath,
296
+ RoutePaths<TRouteTree>
297
+ > extends never
298
+ ? TPass
299
+ : CheckPathError<TRouteTree, Exclude<TPath, RoutePaths<TRouteTree>>>
300
+
301
+ export type CheckPathError<TRouteTree extends AnyRoute, TInvalids> = {
302
+ to: RoutePaths<TRouteTree>
303
+ }
304
+
305
+ export type CheckId<TRouteTree extends AnyRoute, TPath, TPass> = Exclude<
306
+ TPath,
307
+ RouteIds<TRouteTree>
308
+ > extends never
309
+ ? TPass
310
+ : CheckIdError<TRouteTree, Exclude<TPath, RouteIds<TRouteTree>>>
311
+
312
+ export type CheckIdError<TRouteTree extends AnyRoute, TInvalids> = {
313
+ Error: `${TInvalids extends string
314
+ ? TInvalids
315
+ : never} is not a valid route ID.`
316
+ 'Valid Route IDs': RouteIds<TRouteTree>
317
+ }
318
+
319
+ export type ResolveRelativePath<TFrom, TTo = '.'> = TFrom extends string
320
+ ? TTo extends string
321
+ ? TTo extends '.'
322
+ ? TFrom
323
+ : TTo extends `./`
324
+ ? Join<[TFrom, '/']>
325
+ : TTo extends `./${infer TRest}`
326
+ ? ResolveRelativePath<TFrom, TRest>
327
+ : TTo extends `/${infer TRest}`
328
+ ? TTo
329
+ : Split<TTo> extends ['..', ...infer ToRest]
330
+ ? Split<TFrom> extends [...infer FromRest, infer FromTail]
331
+ ? ToRest extends ['/']
332
+ ? Join<[...FromRest, '/']>
333
+ : ResolveRelativePath<Join<FromRest>, Join<ToRest>>
334
+ : never
335
+ : Split<TTo> extends ['.', ...infer ToRest]
336
+ ? ToRest extends ['/']
337
+ ? Join<[TFrom, '/']>
338
+ : ResolveRelativePath<TFrom, Join<ToRest>>
339
+ : CleanPath<Join<['/', ...Split<TFrom>, ...Split<TTo>]>>
340
+ : never
341
+ : never
342
+
343
+ type LinkCurrentTargetElement = {
344
+ preloadTimeout?: null | ReturnType<typeof setTimeout>
345
+ }
346
+
347
+ const preloadWarning = 'Error preloading route! ☝️'
348
+
349
+ export function useLinkProps<
350
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
351
+ TFrom extends RoutePaths<TRouteTree> = '/',
352
+ TTo extends string = '',
353
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
354
+ TMaskTo extends string = '',
355
+ >(
356
+ options: UseLinkPropsOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
357
+ ): React.AnchorHTMLAttributes<HTMLAnchorElement> {
358
+ const router = useRouter()
359
+ const matchPathname = useMatch({
360
+ strict: false,
361
+ select: (s) => s.pathname,
362
+ })
363
+
364
+ const {
365
+ // custom props
366
+ children,
367
+ target,
368
+ activeProps = () => ({ className: 'active' }),
369
+ inactiveProps = () => ({}),
370
+ activeOptions,
371
+ disabled,
372
+ hash,
373
+ search,
374
+ params,
375
+ to,
376
+ state,
377
+ mask,
378
+ preload: userPreload,
379
+ preloadDelay: userPreloadDelay,
380
+ replace,
381
+ startTransition,
382
+ resetScroll,
383
+ // element props
384
+ style,
385
+ className,
386
+ onClick,
387
+ onFocus,
388
+ onMouseEnter,
389
+ onMouseLeave,
390
+ onTouchStart,
391
+ ...rest
392
+ } = options
393
+
394
+ // If this link simply reloads the current route,
395
+ // make sure it has a new key so it will trigger a data refresh
396
+
397
+ // If this `to` is a valid external URL, return
398
+ // null for LinkUtils
399
+
400
+ const dest = {
401
+ from: options.to ? matchPathname : undefined,
402
+ ...options,
403
+ }
404
+
405
+ let type: 'internal' | 'external' = 'internal'
406
+
407
+ try {
408
+ new URL(`${to}`)
409
+ type = 'external'
410
+ } catch {}
411
+
412
+ if (type === 'external') {
413
+ return {
414
+ href: to,
415
+ }
416
+ }
417
+
418
+ const next = router.buildLocation(dest as any)
419
+
420
+ const preload = userPreload ?? router.options.defaultPreload
421
+ const preloadDelay =
422
+ userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0
423
+
424
+ const isActive = useRouterState({
425
+ select: (s) => {
426
+ // Compare path/hash for matches
427
+ const currentPathSplit = s.location.pathname.split('/')
428
+ const nextPathSplit = next.pathname.split('/')
429
+ const pathIsFuzzyEqual = nextPathSplit.every(
430
+ (d, i) => d === currentPathSplit[i],
431
+ )
432
+ // Combine the matches based on user router.options
433
+ const pathTest = activeOptions?.exact
434
+ ? s.location.pathname === next.pathname
435
+ : pathIsFuzzyEqual
436
+ const hashTest = activeOptions?.includeHash
437
+ ? s.location.hash === next.hash
438
+ : true
439
+ const searchTest =
440
+ activeOptions?.includeSearch ?? true
441
+ ? deepEqual(s.location.search, next.search, !activeOptions?.exact)
442
+ : true
443
+
444
+ // The final "active" test
445
+ return pathTest && hashTest && searchTest
446
+ },
447
+ })
448
+
449
+ // The click handler
450
+ const handleClick = (e: MouseEvent) => {
451
+ if (
452
+ !disabled &&
453
+ !isCtrlEvent(e) &&
454
+ !e.defaultPrevented &&
455
+ (!target || target === '_self') &&
456
+ e.button === 0
457
+ ) {
458
+ e.preventDefault()
459
+
460
+ // All is well? Navigate!
461
+ router.commitLocation({ ...next, replace, resetScroll, startTransition })
462
+ }
463
+ }
464
+
465
+ // The click handler
466
+ const handleFocus = (e: MouseEvent) => {
467
+ if (preload) {
468
+ router.preloadRoute(dest as any).catch((err) => {
469
+ console.warn(err)
470
+ console.warn(preloadWarning)
471
+ })
472
+ }
473
+ }
474
+
475
+ const handleTouchStart = (e: TouchEvent) => {
476
+ if (preload) {
477
+ router.preloadRoute(dest as any).catch((err) => {
478
+ console.warn(err)
479
+ console.warn(preloadWarning)
480
+ })
481
+ }
482
+ }
483
+
484
+ const handleEnter = (e: MouseEvent) => {
485
+ const target = (e.target || {}) as LinkCurrentTargetElement
486
+
487
+ if (preload) {
488
+ if (target.preloadTimeout) {
489
+ return
490
+ }
491
+
492
+ target.preloadTimeout = setTimeout(() => {
493
+ target.preloadTimeout = null
494
+ router.preloadRoute(dest as any).catch((err) => {
495
+ console.warn(err)
496
+ console.warn(preloadWarning)
497
+ })
498
+ }, preloadDelay)
499
+ }
500
+ }
501
+
502
+ const handleLeave = (e: MouseEvent) => {
503
+ const target = (e.target || {}) as LinkCurrentTargetElement
504
+
505
+ if (target.preloadTimeout) {
506
+ clearTimeout(target.preloadTimeout)
507
+ target.preloadTimeout = null
508
+ }
509
+ }
510
+
511
+ const composeHandlers =
512
+ (handlers: (undefined | ((e: any) => void))[]) =>
513
+ (e: React.SyntheticEvent) => {
514
+ if (e.persist) e.persist()
515
+ handlers.filter(Boolean).forEach((handler) => {
516
+ if (e.defaultPrevented) return
517
+ handler!(e)
518
+ })
519
+ }
520
+
521
+ // Get the active props
522
+ const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> = isActive
523
+ ? functionalUpdate(activeProps as any, {}) ?? {}
524
+ : {}
525
+
526
+ // Get the inactive props
527
+ const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =
528
+ isActive ? {} : functionalUpdate(inactiveProps, {}) ?? {}
529
+
530
+ return {
531
+ ...resolvedActiveProps,
532
+ ...resolvedInactiveProps,
533
+ ...rest,
534
+ href: disabled
535
+ ? undefined
536
+ : next.maskedLocation
537
+ ? next.maskedLocation.href
538
+ : next.href,
539
+ onClick: composeHandlers([onClick, handleClick]),
540
+ onFocus: composeHandlers([onFocus, handleFocus]),
541
+ onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),
542
+ onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),
543
+ onTouchStart: composeHandlers([onTouchStart, handleTouchStart]),
544
+ target,
545
+ style: {
546
+ ...style,
547
+ ...resolvedActiveProps.style,
548
+ ...resolvedInactiveProps.style,
549
+ },
550
+ className:
551
+ [
552
+ className,
553
+ resolvedActiveProps.className,
554
+ resolvedInactiveProps.className,
555
+ ]
556
+ .filter(Boolean)
557
+ .join(' ') || undefined,
558
+ ...(disabled
559
+ ? {
560
+ role: 'link',
561
+ 'aria-disabled': true,
562
+ }
563
+ : undefined),
564
+ ['data-status']: isActive ? 'active' : undefined,
565
+ }
566
+ }
567
+
568
+ export interface LinkComponent<TProps extends Record<string, any> = {}> {
569
+ <
570
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
571
+ TFrom extends RoutePaths<TRouteTree> = '/',
572
+ TTo extends string = '',
573
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
574
+ TMaskTo extends string = '',
575
+ >(
576
+ props: LinkProps<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
577
+ TProps &
578
+ React.RefAttributes<HTMLAnchorElement>,
579
+ ): ReactNode
580
+ }
581
+
582
+ export const Link: LinkComponent = React.forwardRef((props: any, ref) => {
583
+ const linkProps = useLinkProps(props)
584
+
585
+ return (
586
+ <a
587
+ {...{
588
+ ref: ref as any,
589
+ ...linkProps,
590
+ children:
591
+ typeof props.children === 'function'
592
+ ? props.children({
593
+ isActive: (linkProps as any)['data-status'] === 'active',
594
+ })
595
+ : props.children,
596
+ }}
597
+ />
598
+ )
599
+ }) as any
600
+
601
+ function isCtrlEvent(e: MouseEvent) {
602
+ return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
603
+ }
@@ -0,0 +1,13 @@
1
+ import { HistoryState } from '@tanstack/history'
2
+ import { AnySearchSchema } from './route'
3
+
4
+ export interface ParsedLocation<TSearchObj extends AnySearchSchema = {}> {
5
+ href: string
6
+ pathname: string
7
+ search: TSearchObj
8
+ searchStr: string
9
+ state: HistoryState
10
+ hash: string
11
+ maskedLocation?: ParsedLocation<TSearchObj>
12
+ unmaskOnReload?: boolean
13
+ }