@tanstack/react-router 0.0.1-beta.209 → 0.0.1-beta.210
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/CatchBoundary.js +125 -0
- package/build/cjs/CatchBoundary.js.map +1 -0
- package/build/cjs/Matches.js +223 -0
- package/build/cjs/Matches.js.map +1 -0
- package/build/cjs/RouterProvider.js +99 -53
- package/build/cjs/RouterProvider.js.map +1 -1
- package/build/cjs/index.js +46 -37
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/lazyRouteComponent.js +57 -0
- package/build/cjs/lazyRouteComponent.js.map +1 -0
- package/build/cjs/link.js +150 -0
- package/build/cjs/link.js.map +1 -0
- package/build/cjs/route.js +9 -5
- package/build/cjs/route.js.map +1 -1
- package/build/cjs/router.js.map +1 -1
- package/build/cjs/searchParams.js.map +1 -1
- package/build/cjs/useBlocker.js +64 -0
- package/build/cjs/useBlocker.js.map +1 -0
- package/build/cjs/useNavigate.js +78 -0
- package/build/cjs/useNavigate.js.map +1 -0
- package/build/cjs/useParams.js +28 -0
- package/build/cjs/useParams.js.map +1 -0
- package/build/cjs/useSearch.js +27 -0
- package/build/cjs/useSearch.js.map +1 -0
- package/build/cjs/utils.js +30 -1
- package/build/cjs/utils.js.map +1 -1
- package/build/esm/index.js +491 -514
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +484 -208
- package/build/types/CatchBoundary.d.ts +33 -0
- package/build/types/Matches.d.ts +31 -0
- package/build/types/RouterProvider.d.ts +42 -18
- package/build/types/fileRoute.d.ts +7 -7
- package/build/types/index.d.ts +13 -7
- package/build/types/injectHtml.d.ts +0 -0
- package/build/types/lazyRouteComponent.d.ts +2 -0
- package/build/types/link.d.ts +10 -3
- package/build/types/route.d.ts +39 -7
- package/build/types/router.d.ts +6 -7
- package/build/types/useBlocker.d.ts +8 -0
- package/build/types/useNavigate.d.ts +20 -0
- package/build/types/useParams.d.ts +7 -0
- package/build/types/useSearch.d.ts +7 -0
- package/build/types/utils.d.ts +17 -0
- package/build/umd/index.development.js +492 -513
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +1 -1
- package/build/umd/index.production.js.map +1 -1
- package/package.json +2 -2
- package/src/CatchBoundary.tsx +97 -0
- package/src/Matches.tsx +315 -0
- package/src/RouterProvider.tsx +317 -251
- package/src/index.tsx +17 -8
- package/src/injectHtml.ts +28 -0
- package/src/lazyRouteComponent.tsx +33 -0
- package/src/{link.ts → link.tsx} +163 -3
- package/src/location.ts +1 -0
- package/src/route.ts +86 -16
- package/src/router.ts +6 -7
- package/src/searchParams.ts +1 -0
- package/src/useBlocker.tsx +34 -0
- package/src/useNavigate.tsx +109 -0
- package/src/useParams.tsx +25 -0
- package/src/useSearch.tsx +25 -0
- package/src/utils.ts +83 -3
- package/build/cjs/react.js +0 -620
- package/build/cjs/react.js.map +0 -1
- package/build/types/RouteMatch.d.ts +0 -23
- package/build/types/react.d.ts +0 -141
- package/src/RouteMatch.ts +0 -28
- package/src/react.tsx +0 -1013
package/src/RouterProvider.tsx
CHANGED
|
@@ -1,27 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HistoryLocation,
|
|
3
|
+
HistoryState,
|
|
4
|
+
RouterHistory,
|
|
5
|
+
createBrowserHistory,
|
|
6
|
+
} from '@tanstack/history'
|
|
1
7
|
import * as React from 'react'
|
|
2
|
-
import
|
|
8
|
+
import invariant from 'tiny-invariant'
|
|
9
|
+
import warning from 'tiny-warning'
|
|
10
|
+
import { Matches } from './Matches'
|
|
3
11
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from './
|
|
12
|
+
LinkInfo,
|
|
13
|
+
LinkOptions,
|
|
14
|
+
NavigateOptions,
|
|
15
|
+
ResolveRelativePath,
|
|
16
|
+
ToOptions,
|
|
17
|
+
} from './link'
|
|
10
18
|
import { ParsedLocation } from './location'
|
|
11
|
-
import { AnyRouteMatch } from './RouteMatch'
|
|
12
|
-
import { RouteMatch } from './RouteMatch'
|
|
13
|
-
import { isRedirect } from './redirects'
|
|
14
|
-
import {
|
|
15
|
-
functionalUpdate,
|
|
16
|
-
replaceEqualDeep,
|
|
17
|
-
useStableCallback,
|
|
18
|
-
last,
|
|
19
|
-
pick,
|
|
20
|
-
partialDeepEqual,
|
|
21
|
-
NoInfer,
|
|
22
|
-
PickAsRequired,
|
|
23
|
-
} from './utils'
|
|
24
|
-
import { RouterProps, Matches } from './react'
|
|
25
19
|
import {
|
|
26
20
|
cleanPath,
|
|
27
21
|
interpolatePath,
|
|
@@ -32,33 +26,42 @@ import {
|
|
|
32
26
|
trimPath,
|
|
33
27
|
trimPathRight,
|
|
34
28
|
} from './path'
|
|
35
|
-
import
|
|
29
|
+
import { isRedirect } from './redirects'
|
|
30
|
+
import { AnyPathParams, AnyRoute, AnySearchSchema, Route } from './route'
|
|
36
31
|
import {
|
|
37
32
|
FullSearchSchema,
|
|
33
|
+
ParseRoute,
|
|
38
34
|
RouteById,
|
|
35
|
+
RouteIds,
|
|
39
36
|
RoutePaths,
|
|
40
37
|
RoutesById,
|
|
41
38
|
RoutesByPath,
|
|
42
39
|
} from './routeInfo'
|
|
43
40
|
import {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
BuildNextOptions,
|
|
42
|
+
DehydratedRouteMatch,
|
|
43
|
+
RegisteredRouter,
|
|
44
|
+
Router,
|
|
45
|
+
RouterOptions,
|
|
46
|
+
RouterState,
|
|
47
|
+
componentTypes,
|
|
48
|
+
} from './router'
|
|
50
49
|
import {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
NoInfer,
|
|
51
|
+
PickAsRequired,
|
|
52
|
+
functionalUpdate,
|
|
53
|
+
last,
|
|
54
|
+
partialDeepEqual,
|
|
55
|
+
pick,
|
|
56
|
+
replaceEqualDeep,
|
|
57
|
+
useStableCallback,
|
|
58
|
+
} from './utils'
|
|
59
|
+
import { MatchRouteOptions } from './Matches'
|
|
58
60
|
|
|
59
61
|
export interface CommitLocationOptions {
|
|
60
62
|
replace?: boolean
|
|
61
63
|
resetScroll?: boolean
|
|
64
|
+
startTransition?: boolean
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
export interface MatchLocation {
|
|
@@ -68,13 +71,6 @@ export interface MatchLocation {
|
|
|
68
71
|
from?: string
|
|
69
72
|
}
|
|
70
73
|
|
|
71
|
-
export interface MatchRouteOptions {
|
|
72
|
-
pending?: boolean
|
|
73
|
-
caseSensitive?: boolean
|
|
74
|
-
includeSearch?: boolean
|
|
75
|
-
fuzzy?: boolean
|
|
76
|
-
}
|
|
77
|
-
|
|
78
74
|
type LinkCurrentTargetElement = {
|
|
79
75
|
preloadTimeout?: null | ReturnType<typeof setTimeout>
|
|
80
76
|
}
|
|
@@ -83,7 +79,6 @@ export type BuildLinkFn<TRouteTree extends AnyRoute> = <
|
|
|
83
79
|
TFrom extends RoutePaths<TRouteTree> = '/',
|
|
84
80
|
TTo extends string = '',
|
|
85
81
|
>(
|
|
86
|
-
state: RouterState,
|
|
87
82
|
dest: LinkOptions<TRouteTree, TFrom, TTo>,
|
|
88
83
|
) => LinkInfo
|
|
89
84
|
|
|
@@ -104,7 +99,6 @@ export type MatchRouteFn<TRouteTree extends AnyRoute> = <
|
|
|
104
99
|
TTo extends string = '',
|
|
105
100
|
TResolved = ResolveRelativePath<TFrom, NoInfer<TTo>>,
|
|
106
101
|
>(
|
|
107
|
-
state: RouterState<TRouteTree>,
|
|
108
102
|
location: ToOptions<TRouteTree, TFrom, TTo>,
|
|
109
103
|
opts?: MatchRouteOptions,
|
|
110
104
|
) => false | RouteById<TRouteTree, TResolved>['types']['allParams']
|
|
@@ -115,7 +109,9 @@ export type LoadFn = (opts?: {
|
|
|
115
109
|
__dehydratedMatches?: DehydratedRouteMatch[]
|
|
116
110
|
}) => Promise<void>
|
|
117
111
|
|
|
118
|
-
|
|
112
|
+
export type BuildLocationFn<TRouteTree extends AnyRoute> = (
|
|
113
|
+
opts: BuildNextOptions,
|
|
114
|
+
) => ParsedLocation
|
|
119
115
|
|
|
120
116
|
export type RouterContext<
|
|
121
117
|
TRouteTree extends AnyRoute,
|
|
@@ -130,6 +126,7 @@ export type RouterContext<
|
|
|
130
126
|
options: RouterOptions<TRouteTree>
|
|
131
127
|
history: RouterHistory
|
|
132
128
|
load: LoadFn
|
|
129
|
+
buildLocation: BuildLocationFn<TRouteTree>
|
|
133
130
|
}
|
|
134
131
|
|
|
135
132
|
export const routerContext = React.createContext<RouterContext<any>>(null!)
|
|
@@ -138,13 +135,22 @@ if (typeof document !== 'undefined') {
|
|
|
138
135
|
window.__TSR_ROUTER_CONTEXT__ = routerContext as any
|
|
139
136
|
}
|
|
140
137
|
|
|
138
|
+
const preloadWarning = 'Error preloading route! ☝️'
|
|
139
|
+
|
|
140
|
+
function isCtrlEvent(e: MouseEvent) {
|
|
141
|
+
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export class SearchParamError extends Error {}
|
|
145
|
+
|
|
146
|
+
export class PathParamError extends Error {}
|
|
147
|
+
|
|
141
148
|
export function getInitialRouterState(
|
|
142
149
|
location: ParsedLocation,
|
|
143
150
|
): RouterState<any> {
|
|
144
151
|
return {
|
|
145
152
|
status: 'idle',
|
|
146
|
-
|
|
147
|
-
resolvedLocation: location!,
|
|
153
|
+
resolvedLocation: undefined,
|
|
148
154
|
location: location!,
|
|
149
155
|
matches: [],
|
|
150
156
|
pendingMatches: [],
|
|
@@ -239,9 +245,27 @@ export function RouterProvider<
|
|
|
239
245
|
},
|
|
240
246
|
)
|
|
241
247
|
|
|
242
|
-
const [
|
|
248
|
+
const [preState, setState] = React.useState<RouterState<TRouteTree>>(() =>
|
|
243
249
|
getInitialRouterState(parseLocation()),
|
|
244
250
|
)
|
|
251
|
+
const [isTransitioning, startReactTransition] = React.useTransition()
|
|
252
|
+
|
|
253
|
+
const state = React.useMemo<RouterState<TRouteTree>>(
|
|
254
|
+
() => ({
|
|
255
|
+
...preState,
|
|
256
|
+
status: isTransitioning ? 'pending' : 'idle',
|
|
257
|
+
}),
|
|
258
|
+
[preState, isTransitioning],
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
React.useLayoutEffect(() => {
|
|
262
|
+
if (!isTransitioning && state.resolvedLocation !== state.location) {
|
|
263
|
+
setState((s) => ({
|
|
264
|
+
...s,
|
|
265
|
+
resolvedLocation: s.location,
|
|
266
|
+
}))
|
|
267
|
+
}
|
|
268
|
+
})
|
|
245
269
|
|
|
246
270
|
const basepath = `/${trimPath(options.basepath ?? '') ?? ''}`
|
|
247
271
|
|
|
@@ -528,10 +552,8 @@ export function RouterProvider<
|
|
|
528
552
|
},
|
|
529
553
|
)
|
|
530
554
|
|
|
531
|
-
const buildLocation = useStableCallback(
|
|
532
|
-
|
|
533
|
-
opts: BuildNextOptions = {},
|
|
534
|
-
): ParsedLocation => {
|
|
555
|
+
const buildLocation = useStableCallback<BuildLocationFn<TRouteTree>>(
|
|
556
|
+
(opts) => {
|
|
535
557
|
const build = (
|
|
536
558
|
dest: BuildNextOptions & {
|
|
537
559
|
unmaskOnReload?: boolean
|
|
@@ -704,7 +726,10 @@ export function RouterProvider<
|
|
|
704
726
|
)
|
|
705
727
|
|
|
706
728
|
const commitLocation = useStableCallback(
|
|
707
|
-
async (
|
|
729
|
+
async ({
|
|
730
|
+
startTransition,
|
|
731
|
+
...next
|
|
732
|
+
}: ParsedLocation & CommitLocationOptions) => {
|
|
708
733
|
if (navigateTimeoutRef.current) clearTimeout(navigateTimeoutRef.current)
|
|
709
734
|
|
|
710
735
|
const isSameUrl = latestLocationRef.current.href === next.href
|
|
@@ -738,10 +763,18 @@ export function RouterProvider<
|
|
|
738
763
|
}
|
|
739
764
|
}
|
|
740
765
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
766
|
+
const apply = () => {
|
|
767
|
+
history[next.replace ? 'replace' : 'push'](
|
|
768
|
+
nextHistory.href,
|
|
769
|
+
nextHistory.state,
|
|
770
|
+
)
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
if (startTransition ?? true) {
|
|
774
|
+
startReactTransition(apply)
|
|
775
|
+
} else {
|
|
776
|
+
apply()
|
|
777
|
+
}
|
|
745
778
|
}
|
|
746
779
|
|
|
747
780
|
resetNextScrollRef.current = next.resetScroll ?? true
|
|
@@ -754,11 +787,13 @@ export function RouterProvider<
|
|
|
754
787
|
({
|
|
755
788
|
replace,
|
|
756
789
|
resetScroll,
|
|
790
|
+
startTransition,
|
|
757
791
|
...rest
|
|
758
792
|
}: BuildNextOptions & CommitLocationOptions = {}) => {
|
|
759
793
|
const location = buildLocation(rest)
|
|
760
794
|
return commitLocation({
|
|
761
795
|
...location,
|
|
796
|
+
startTransition,
|
|
762
797
|
replace,
|
|
763
798
|
resetScroll,
|
|
764
799
|
})
|
|
@@ -858,7 +893,9 @@ export function RouterProvider<
|
|
|
858
893
|
preload: !!preload,
|
|
859
894
|
context: parentContext,
|
|
860
895
|
location: state.location, // TODO: This might need to be latestLocationRef.current...?
|
|
861
|
-
navigate: (opts) =>
|
|
896
|
+
navigate: (opts) =>
|
|
897
|
+
navigate({ ...opts, from: match.pathname } as any),
|
|
898
|
+
buildLocation,
|
|
862
899
|
})) ?? ({} as any)
|
|
863
900
|
|
|
864
901
|
const context = {
|
|
@@ -931,7 +968,10 @@ export function RouterProvider<
|
|
|
931
968
|
navigate({ ...opts, from: match.pathname }),
|
|
932
969
|
})
|
|
933
970
|
|
|
934
|
-
await Promise.all([
|
|
971
|
+
const [_, loaderContext] = await Promise.all([
|
|
972
|
+
componentsPromise,
|
|
973
|
+
loaderPromise,
|
|
974
|
+
])
|
|
935
975
|
if ((latestPromise = checkLatest())) return await latestPromise
|
|
936
976
|
|
|
937
977
|
matches[index] = match = {
|
|
@@ -1000,7 +1040,7 @@ export function RouterProvider<
|
|
|
1000
1040
|
const load = useStableCallback<LoadFn>(async () => {
|
|
1001
1041
|
const promise = new Promise<void>(async (resolve, reject) => {
|
|
1002
1042
|
const next = latestLocationRef.current
|
|
1003
|
-
const prevLocation = state.resolvedLocation
|
|
1043
|
+
const prevLocation = state.resolvedLocation || state.location
|
|
1004
1044
|
const pathDidChange = !!(next && prevLocation!.href !== next.href)
|
|
1005
1045
|
let latestPromise: Promise<void> | undefined | null
|
|
1006
1046
|
|
|
@@ -1014,12 +1054,6 @@ export function RouterProvider<
|
|
|
1014
1054
|
pathChanged: pathDidChange,
|
|
1015
1055
|
})
|
|
1016
1056
|
|
|
1017
|
-
// Ingest the new location
|
|
1018
|
-
setState((s) => ({
|
|
1019
|
-
...s,
|
|
1020
|
-
location: next,
|
|
1021
|
-
}))
|
|
1022
|
-
|
|
1023
1057
|
// Match the routes
|
|
1024
1058
|
let matches: RouteMatch<any, any>[] = matchRoutes(
|
|
1025
1059
|
next.pathname,
|
|
@@ -1029,9 +1063,9 @@ export function RouterProvider<
|
|
|
1029
1063
|
},
|
|
1030
1064
|
)
|
|
1031
1065
|
|
|
1066
|
+
// Ingest the new matches
|
|
1032
1067
|
setState((s) => ({
|
|
1033
1068
|
...s,
|
|
1034
|
-
status: 'pending',
|
|
1035
1069
|
matches,
|
|
1036
1070
|
}))
|
|
1037
1071
|
|
|
@@ -1063,11 +1097,11 @@ export function RouterProvider<
|
|
|
1063
1097
|
// state.pendingMatches.includes(id),
|
|
1064
1098
|
// )
|
|
1065
1099
|
|
|
1066
|
-
setState((s) => ({
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
}))
|
|
1100
|
+
// setState((s) => ({
|
|
1101
|
+
// ...s,
|
|
1102
|
+
// status: 'idle',
|
|
1103
|
+
// resolvedLocation: s.location,
|
|
1104
|
+
// }))
|
|
1071
1105
|
|
|
1072
1106
|
// TODO:
|
|
1073
1107
|
// ;(
|
|
@@ -1123,134 +1157,132 @@ export function RouterProvider<
|
|
|
1123
1157
|
},
|
|
1124
1158
|
)
|
|
1125
1159
|
|
|
1126
|
-
const buildLink = useStableCallback<BuildLinkFn<TRouteTree>>(
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
// make sure it has a new key so it will trigger a data refresh
|
|
1130
|
-
|
|
1131
|
-
// If this `to` is a valid external URL, return
|
|
1132
|
-
// null for LinkUtils
|
|
1133
|
-
|
|
1134
|
-
const {
|
|
1135
|
-
to,
|
|
1136
|
-
preload: userPreload,
|
|
1137
|
-
preloadDelay: userPreloadDelay,
|
|
1138
|
-
activeOptions,
|
|
1139
|
-
disabled,
|
|
1140
|
-
target,
|
|
1141
|
-
replace,
|
|
1142
|
-
resetScroll,
|
|
1143
|
-
} = dest
|
|
1160
|
+
const buildLink = useStableCallback<BuildLinkFn<TRouteTree>>((dest) => {
|
|
1161
|
+
// If this link simply reloads the current route,
|
|
1162
|
+
// make sure it has a new key so it will trigger a data refresh
|
|
1144
1163
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
return {
|
|
1148
|
-
type: 'external',
|
|
1149
|
-
href: to as any,
|
|
1150
|
-
}
|
|
1151
|
-
} catch (e) {}
|
|
1164
|
+
// If this `to` is a valid external URL, return
|
|
1165
|
+
// null for LinkUtils
|
|
1152
1166
|
|
|
1153
|
-
|
|
1167
|
+
const {
|
|
1168
|
+
to,
|
|
1169
|
+
preload: userPreload,
|
|
1170
|
+
preloadDelay: userPreloadDelay,
|
|
1171
|
+
activeOptions,
|
|
1172
|
+
disabled,
|
|
1173
|
+
target,
|
|
1174
|
+
replace,
|
|
1175
|
+
resetScroll,
|
|
1176
|
+
startTransition,
|
|
1177
|
+
} = dest
|
|
1154
1178
|
|
|
1155
|
-
|
|
1179
|
+
try {
|
|
1180
|
+
new URL(`${to}`)
|
|
1181
|
+
return {
|
|
1182
|
+
type: 'external',
|
|
1183
|
+
href: to as any,
|
|
1184
|
+
}
|
|
1185
|
+
} catch (e) {}
|
|
1186
|
+
|
|
1187
|
+
const nextOpts = dest
|
|
1188
|
+
const next = buildLocation(nextOpts as any)
|
|
1189
|
+
|
|
1190
|
+
const preload = userPreload ?? options.defaultPreload
|
|
1191
|
+
const preloadDelay = userPreloadDelay ?? options.defaultPreloadDelay ?? 0
|
|
1192
|
+
|
|
1193
|
+
// Compare path/hash for matches
|
|
1194
|
+
const currentPathSplit = latestLocationRef.current.pathname.split('/')
|
|
1195
|
+
const nextPathSplit = next.pathname.split('/')
|
|
1196
|
+
const pathIsFuzzyEqual = nextPathSplit.every(
|
|
1197
|
+
(d, i) => d === currentPathSplit[i],
|
|
1198
|
+
)
|
|
1199
|
+
// Combine the matches based on user options
|
|
1200
|
+
const pathTest = activeOptions?.exact
|
|
1201
|
+
? latestLocationRef.current.pathname === next.pathname
|
|
1202
|
+
: pathIsFuzzyEqual
|
|
1203
|
+
const hashTest = activeOptions?.includeHash
|
|
1204
|
+
? latestLocationRef.current.hash === next.hash
|
|
1205
|
+
: true
|
|
1206
|
+
const searchTest =
|
|
1207
|
+
activeOptions?.includeSearch ?? true
|
|
1208
|
+
? partialDeepEqual(latestLocationRef.current.search, next.search)
|
|
1209
|
+
: true
|
|
1156
1210
|
|
|
1157
|
-
|
|
1158
|
-
|
|
1211
|
+
// The final "active" test
|
|
1212
|
+
const isActive = pathTest && hashTest && searchTest
|
|
1159
1213
|
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
(
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
const hashTest = activeOptions?.includeHash
|
|
1171
|
-
? latestLocationRef.current.hash === next.hash
|
|
1172
|
-
: true
|
|
1173
|
-
const searchTest =
|
|
1174
|
-
activeOptions?.includeSearch ?? true
|
|
1175
|
-
? partialDeepEqual(latestLocationRef.current.search, next.search)
|
|
1176
|
-
: true
|
|
1177
|
-
|
|
1178
|
-
// The final "active" test
|
|
1179
|
-
const isActive = pathTest && hashTest && searchTest
|
|
1180
|
-
|
|
1181
|
-
// The click handler
|
|
1182
|
-
const handleClick = (e: MouseEvent) => {
|
|
1183
|
-
if (
|
|
1184
|
-
!disabled &&
|
|
1185
|
-
!isCtrlEvent(e) &&
|
|
1186
|
-
!e.defaultPrevented &&
|
|
1187
|
-
(!target || target === '_self') &&
|
|
1188
|
-
e.button === 0
|
|
1189
|
-
) {
|
|
1190
|
-
e.preventDefault()
|
|
1191
|
-
|
|
1192
|
-
// All is well? Navigate!
|
|
1193
|
-
commitLocation({ ...next, replace, resetScroll })
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1214
|
+
// The click handler
|
|
1215
|
+
const handleClick = (e: MouseEvent) => {
|
|
1216
|
+
if (
|
|
1217
|
+
!disabled &&
|
|
1218
|
+
!isCtrlEvent(e) &&
|
|
1219
|
+
!e.defaultPrevented &&
|
|
1220
|
+
(!target || target === '_self') &&
|
|
1221
|
+
e.button === 0
|
|
1222
|
+
) {
|
|
1223
|
+
e.preventDefault()
|
|
1196
1224
|
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
if (preload) {
|
|
1200
|
-
preloadRoute(nextOpts as any).catch((err) => {
|
|
1201
|
-
console.warn(err)
|
|
1202
|
-
console.warn(preloadWarning)
|
|
1203
|
-
})
|
|
1204
|
-
}
|
|
1225
|
+
// All is well? Navigate!
|
|
1226
|
+
commitLocation({ ...next, replace, resetScroll, startTransition })
|
|
1205
1227
|
}
|
|
1228
|
+
}
|
|
1206
1229
|
|
|
1207
|
-
|
|
1230
|
+
// The click handler
|
|
1231
|
+
const handleFocus = (e: MouseEvent) => {
|
|
1232
|
+
if (preload) {
|
|
1208
1233
|
preloadRoute(nextOpts as any).catch((err) => {
|
|
1209
1234
|
console.warn(err)
|
|
1210
1235
|
console.warn(preloadWarning)
|
|
1211
1236
|
})
|
|
1212
1237
|
}
|
|
1238
|
+
}
|
|
1213
1239
|
|
|
1214
|
-
|
|
1215
|
-
|
|
1240
|
+
const handleTouchStart = (e: TouchEvent) => {
|
|
1241
|
+
preloadRoute(nextOpts as any).catch((err) => {
|
|
1242
|
+
console.warn(err)
|
|
1243
|
+
console.warn(preloadWarning)
|
|
1244
|
+
})
|
|
1245
|
+
}
|
|
1216
1246
|
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
return
|
|
1220
|
-
}
|
|
1247
|
+
const handleEnter = (e: MouseEvent) => {
|
|
1248
|
+
const target = (e.target || {}) as LinkCurrentTargetElement
|
|
1221
1249
|
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
console.warn(err)
|
|
1226
|
-
console.warn(preloadWarning)
|
|
1227
|
-
})
|
|
1228
|
-
}, preloadDelay)
|
|
1250
|
+
if (preload) {
|
|
1251
|
+
if (target.preloadTimeout) {
|
|
1252
|
+
return
|
|
1229
1253
|
}
|
|
1230
|
-
}
|
|
1231
1254
|
|
|
1232
|
-
|
|
1233
|
-
const target = (e.target || {}) as LinkCurrentTargetElement
|
|
1234
|
-
|
|
1235
|
-
if (target.preloadTimeout) {
|
|
1236
|
-
clearTimeout(target.preloadTimeout)
|
|
1255
|
+
target.preloadTimeout = setTimeout(() => {
|
|
1237
1256
|
target.preloadTimeout = null
|
|
1238
|
-
|
|
1257
|
+
preloadRoute(nextOpts as any).catch((err) => {
|
|
1258
|
+
console.warn(err)
|
|
1259
|
+
console.warn(preloadWarning)
|
|
1260
|
+
})
|
|
1261
|
+
}, preloadDelay)
|
|
1239
1262
|
}
|
|
1263
|
+
}
|
|
1240
1264
|
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
handleLeave,
|
|
1248
|
-
handleTouchStart,
|
|
1249
|
-
isActive,
|
|
1250
|
-
disabled,
|
|
1265
|
+
const handleLeave = (e: MouseEvent) => {
|
|
1266
|
+
const target = (e.target || {}) as LinkCurrentTargetElement
|
|
1267
|
+
|
|
1268
|
+
if (target.preloadTimeout) {
|
|
1269
|
+
clearTimeout(target.preloadTimeout)
|
|
1270
|
+
target.preloadTimeout = null
|
|
1251
1271
|
}
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
return {
|
|
1275
|
+
type: 'internal',
|
|
1276
|
+
next,
|
|
1277
|
+
handleFocus,
|
|
1278
|
+
handleClick,
|
|
1279
|
+
handleEnter,
|
|
1280
|
+
handleLeave,
|
|
1281
|
+
handleTouchStart,
|
|
1282
|
+
isActive,
|
|
1283
|
+
disabled,
|
|
1284
|
+
}
|
|
1285
|
+
})
|
|
1254
1286
|
|
|
1255
1287
|
const latestLocationRef = React.useRef(state.location)
|
|
1256
1288
|
|
|
@@ -1258,15 +1290,17 @@ export function RouterProvider<
|
|
|
1258
1290
|
const unsub = history.subscribe(() => {
|
|
1259
1291
|
latestLocationRef.current = parseLocation(latestLocationRef.current)
|
|
1260
1292
|
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1293
|
+
setState((s) => ({
|
|
1294
|
+
...s,
|
|
1295
|
+
status: 'pending',
|
|
1296
|
+
}))
|
|
1297
|
+
if (state.location !== latestLocationRef.current) {
|
|
1298
|
+
try {
|
|
1299
|
+
load()
|
|
1300
|
+
} catch (err) {
|
|
1301
|
+
console.error(err)
|
|
1268
1302
|
}
|
|
1269
|
-
}
|
|
1303
|
+
}
|
|
1270
1304
|
})
|
|
1271
1305
|
|
|
1272
1306
|
const nextLocation = buildLocation({
|
|
@@ -1289,54 +1323,58 @@ export function RouterProvider<
|
|
|
1289
1323
|
|
|
1290
1324
|
if (initialLoad.current) {
|
|
1291
1325
|
initialLoad.current = false
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1326
|
+
startReactTransition(() => {
|
|
1327
|
+
try {
|
|
1328
|
+
load()
|
|
1329
|
+
} catch (err) {
|
|
1330
|
+
console.error(err)
|
|
1331
|
+
}
|
|
1332
|
+
})
|
|
1297
1333
|
}
|
|
1298
1334
|
|
|
1299
|
-
const
|
|
1300
|
-
(
|
|
1301
|
-
|
|
1302
|
-
|
|
1335
|
+
const matchRoute = useStableCallback<MatchRouteFn<TRouteTree>>(
|
|
1336
|
+
(location, opts) => {
|
|
1337
|
+
location = {
|
|
1338
|
+
...location,
|
|
1339
|
+
to: location.to
|
|
1340
|
+
? resolvePathWithBase((location.from || '') as string, location.to)
|
|
1341
|
+
: undefined,
|
|
1342
|
+
} as any
|
|
1303
1343
|
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
: undefined,
|
|
1310
|
-
} as any
|
|
1311
|
-
|
|
1312
|
-
const next = buildLocation(location as any)
|
|
1313
|
-
if (opts?.pending && state.status !== 'pending') {
|
|
1314
|
-
return false
|
|
1315
|
-
}
|
|
1344
|
+
const next = buildLocation(location as any)
|
|
1345
|
+
|
|
1346
|
+
if (opts?.pending && state.status !== 'pending') {
|
|
1347
|
+
return false
|
|
1348
|
+
}
|
|
1316
1349
|
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1350
|
+
const baseLocation = opts?.pending
|
|
1351
|
+
? latestLocationRef.current
|
|
1352
|
+
: state.resolvedLocation
|
|
1320
1353
|
|
|
1321
|
-
|
|
1322
|
-
return false
|
|
1323
|
-
}
|
|
1354
|
+
// const baseLocation = state.resolvedLocation
|
|
1324
1355
|
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
}) as any
|
|
1356
|
+
if (!baseLocation) {
|
|
1357
|
+
return false
|
|
1358
|
+
}
|
|
1329
1359
|
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1360
|
+
const match = matchPathname(basepath, baseLocation.pathname, {
|
|
1361
|
+
...opts,
|
|
1362
|
+
to: next.pathname,
|
|
1363
|
+
}) as any
|
|
1333
1364
|
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1365
|
+
if (!match) {
|
|
1366
|
+
return false
|
|
1367
|
+
}
|
|
1337
1368
|
|
|
1338
|
-
|
|
1339
|
-
|
|
1369
|
+
if (match && (opts?.includeSearch ?? true)) {
|
|
1370
|
+
return partialDeepEqual(baseLocation.search, next.search)
|
|
1371
|
+
? match
|
|
1372
|
+
: false
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
return match
|
|
1376
|
+
},
|
|
1377
|
+
)
|
|
1340
1378
|
|
|
1341
1379
|
const routerContextValue: RouterContext<TRouteTree> = {
|
|
1342
1380
|
routeTree: router.routeTree,
|
|
@@ -1348,6 +1386,7 @@ export function RouterProvider<
|
|
|
1348
1386
|
options,
|
|
1349
1387
|
history,
|
|
1350
1388
|
load,
|
|
1389
|
+
buildLocation,
|
|
1351
1390
|
}
|
|
1352
1391
|
|
|
1353
1392
|
return (
|
|
@@ -1357,35 +1396,62 @@ export function RouterProvider<
|
|
|
1357
1396
|
)
|
|
1358
1397
|
}
|
|
1359
1398
|
|
|
1360
|
-
function mergeMatches<TRouteTree extends AnyRoute>(
|
|
1361
|
-
prevMatchesById: Record<string, RouteMatch<TRouteTree>>,
|
|
1362
|
-
nextMatches: AnyRouteMatch[],
|
|
1363
|
-
): Record<string, RouteMatch<TRouteTree>> {
|
|
1364
|
-
let matchesById = { ...prevMatchesById }
|
|
1365
|
-
|
|
1366
|
-
nextMatches.forEach((match) => {
|
|
1367
|
-
if (!matchesById[match.id]) {
|
|
1368
|
-
matchesById[match.id] = match
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
|
-
matchesById[match.id] = {
|
|
1372
|
-
...matchesById[match.id],
|
|
1373
|
-
...match,
|
|
1374
|
-
}
|
|
1375
|
-
})
|
|
1376
|
-
|
|
1377
|
-
return matchesById
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
function isCtrlEvent(e: MouseEvent) {
|
|
1381
|
-
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
|
|
1382
|
-
}
|
|
1383
|
-
export class SearchParamError extends Error {}
|
|
1384
|
-
export class PathParamError extends Error {}
|
|
1385
|
-
|
|
1386
1399
|
export function getRouteMatch<TRouteTree extends AnyRoute>(
|
|
1387
1400
|
state: RouterState<TRouteTree>,
|
|
1388
1401
|
id: string,
|
|
1389
1402
|
): undefined | RouteMatch<TRouteTree> {
|
|
1390
1403
|
return [...state.pendingMatches, ...state.matches].find((d) => d.id === id)
|
|
1391
1404
|
}
|
|
1405
|
+
|
|
1406
|
+
export function useRouterState<
|
|
1407
|
+
TSelected = RouterState<RegisteredRouter['routeTree']>,
|
|
1408
|
+
>(opts?: {
|
|
1409
|
+
select: (state: RouterState<RegisteredRouter['routeTree']>) => TSelected
|
|
1410
|
+
}): TSelected {
|
|
1411
|
+
const { state } = useRouter()
|
|
1412
|
+
// return useStore(router.__store, opts?.select as any)
|
|
1413
|
+
return opts?.select ? opts.select(state) : (state as any)
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
export type RouterProps<
|
|
1417
|
+
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
1418
|
+
TDehydrated extends Record<string, any> = Record<string, any>,
|
|
1419
|
+
> = Omit<RouterOptions<TRouteTree, TDehydrated>, 'context'> & {
|
|
1420
|
+
router: Router<TRouteTree>
|
|
1421
|
+
context?: Partial<RouterOptions<TRouteTree, TDehydrated>['context']>
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
export function useRouter<
|
|
1425
|
+
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
1426
|
+
>(): RouterContext<TRouteTree> {
|
|
1427
|
+
const resolvedContext = window.__TSR_ROUTER_CONTEXT__ || routerContext
|
|
1428
|
+
const value = React.useContext(resolvedContext)
|
|
1429
|
+
warning(value, 'useRouter must be used inside a <RouterProvider> component!')
|
|
1430
|
+
return value as any
|
|
1431
|
+
}
|
|
1432
|
+
export interface RouteMatch<
|
|
1433
|
+
TRouteTree extends AnyRoute = AnyRoute,
|
|
1434
|
+
TRouteId extends RouteIds<TRouteTree> = ParseRoute<TRouteTree>['id'],
|
|
1435
|
+
> {
|
|
1436
|
+
id: string
|
|
1437
|
+
routeId: TRouteId
|
|
1438
|
+
pathname: string
|
|
1439
|
+
params: RouteById<TRouteTree, TRouteId>['types']['allParams']
|
|
1440
|
+
status: 'pending' | 'success' | 'error'
|
|
1441
|
+
isFetching: boolean
|
|
1442
|
+
invalid: boolean
|
|
1443
|
+
error: unknown
|
|
1444
|
+
paramsError: unknown
|
|
1445
|
+
searchError: unknown
|
|
1446
|
+
updatedAt: number
|
|
1447
|
+
loadPromise?: Promise<void>
|
|
1448
|
+
__resolveLoadPromise?: () => void
|
|
1449
|
+
context: RouteById<TRouteTree, TRouteId>['types']['allContext']
|
|
1450
|
+
routeSearch: RouteById<TRouteTree, TRouteId>['types']['searchSchema']
|
|
1451
|
+
search: FullSearchSchema<TRouteTree> &
|
|
1452
|
+
RouteById<TRouteTree, TRouteId>['types']['fullSearchSchema']
|
|
1453
|
+
fetchedAt: number
|
|
1454
|
+
abortController: AbortController
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
export type AnyRouteMatch = RouteMatch<any>
|