@tanstack/react-router 0.0.1-beta.18 → 0.0.1-beta.181
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/LICENSE +21 -0
- package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +1 -19
- package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
- package/build/cjs/awaited.js +45 -0
- package/build/cjs/awaited.js.map +1 -0
- package/build/cjs/index.js +64 -0
- package/build/cjs/index.js.map +1 -0
- package/build/cjs/react.js +644 -0
- package/build/cjs/react.js.map +1 -0
- package/build/cjs/scroll-restoration.js +56 -0
- package/build/cjs/scroll-restoration.js.map +1 -0
- package/build/esm/index.js +555 -2738
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +59 -49
- package/build/stats-react.json +283 -47
- package/build/types/awaited.d.ts +8 -0
- package/build/types/index.d.ts +4 -86
- package/build/types/react.d.ts +167 -0
- package/build/types/scroll-restoration.d.ts +3 -0
- package/build/umd/index.development.js +2358 -2384
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +25 -4
- package/build/umd/index.production.js.map +1 -1
- package/package.json +11 -11
- package/src/awaited.tsx +40 -0
- package/src/index.tsx +5 -596
- package/src/react.tsx +1215 -0
- package/src/scroll-restoration.tsx +27 -0
- package/build/cjs/react-router/src/index.js +0 -413
- package/build/cjs/react-router/src/index.js.map +0 -1
- package/build/cjs/router-core/build/esm/index.js +0 -2497
- package/build/cjs/router-core/build/esm/index.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/react-router",
|
|
3
3
|
"author": "Tanner Linsley",
|
|
4
|
-
"version": "0.0.1-beta.
|
|
4
|
+
"version": "0.0.1-beta.181",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "tanstack/router",
|
|
7
|
-
"homepage": "https://tanstack.com/router
|
|
7
|
+
"homepage": "https://tanstack.com/router",
|
|
8
8
|
"description": "",
|
|
9
9
|
"publishConfig": {
|
|
10
10
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
"keywords": [
|
|
13
13
|
"react",
|
|
14
14
|
"location",
|
|
15
|
-
"@tanstack/react-router",
|
|
16
15
|
"router",
|
|
17
16
|
"routing",
|
|
18
17
|
"async",
|
|
@@ -24,7 +23,7 @@
|
|
|
24
23
|
"url": "https://github.com/sponsors/tannerlinsley"
|
|
25
24
|
},
|
|
26
25
|
"module": "build/esm/index.js",
|
|
27
|
-
"main": "build/cjs/
|
|
26
|
+
"main": "build/cjs/index.js",
|
|
28
27
|
"browser": "build/umd/index.production.js",
|
|
29
28
|
"types": "build/types/index.d.ts",
|
|
30
29
|
"engines": {
|
|
@@ -41,12 +40,13 @@
|
|
|
41
40
|
},
|
|
42
41
|
"dependencies": {
|
|
43
42
|
"@babel/runtime": "^7.16.7",
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
43
|
+
"tiny-invariant": "^1.3.1",
|
|
44
|
+
"tiny-warning": "^1.0.3",
|
|
45
|
+
"@tanstack/react-store": "^0.0.1",
|
|
46
|
+
"@gisatcz/cross-package-react-context": "^0.2.0",
|
|
47
|
+
"@tanstack/router-core": "0.0.1-beta.181"
|
|
47
48
|
},
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"babel-plugin-transform-async-to-promises": "^0.8.18"
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "rollup --config rollup.config.js"
|
|
51
51
|
}
|
|
52
|
-
}
|
|
52
|
+
}
|
package/src/awaited.tsx
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { DeferredPromise, isDehydratedDeferred } from '@tanstack/router-core'
|
|
2
|
+
import { useRouter } from './react'
|
|
3
|
+
|
|
4
|
+
export type AwaitOptions<T> = {
|
|
5
|
+
promise: DeferredPromise<T>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function useAwaited<T>({ promise }: AwaitOptions<T>): [T] {
|
|
9
|
+
const router = useRouter()
|
|
10
|
+
|
|
11
|
+
let state = promise.__deferredState
|
|
12
|
+
const key = `__TSR__DEFERRED__${state.uid}`
|
|
13
|
+
|
|
14
|
+
if (isDehydratedDeferred(promise)) {
|
|
15
|
+
state = router.hydrateData(key)!
|
|
16
|
+
promise = Promise.resolve(state.data) as DeferredPromise<any>
|
|
17
|
+
promise.__deferredState = state
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (state.status === 'pending') {
|
|
21
|
+
throw promise
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (state.status === 'error') {
|
|
25
|
+
throw state.error
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
router.dehydrateData(key, state)
|
|
29
|
+
|
|
30
|
+
return [state.data]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function Await<T>(
|
|
34
|
+
props: AwaitOptions<T> & {
|
|
35
|
+
children: (result: T) => JSX.Element
|
|
36
|
+
},
|
|
37
|
+
) {
|
|
38
|
+
const awaited = useAwaited(props)
|
|
39
|
+
return props.children(...awaited)
|
|
40
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -1,598 +1,7 @@
|
|
|
1
|
-
import * as React from 'react'
|
|
2
|
-
|
|
3
|
-
import { useSyncExternalStore } from 'use-sync-external-store/shim'
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
AnyRoute,
|
|
7
|
-
CheckId,
|
|
8
|
-
rootRouteId,
|
|
9
|
-
Router,
|
|
10
|
-
RouterState,
|
|
11
|
-
ToIdOption,
|
|
12
|
-
} from '@tanstack/router-core'
|
|
13
|
-
import {
|
|
14
|
-
warning,
|
|
15
|
-
RouterOptions,
|
|
16
|
-
RouteMatch,
|
|
17
|
-
MatchRouteOptions,
|
|
18
|
-
RouteConfig,
|
|
19
|
-
AnyRouteConfig,
|
|
20
|
-
AnyAllRouteInfo,
|
|
21
|
-
DefaultAllRouteInfo,
|
|
22
|
-
functionalUpdate,
|
|
23
|
-
createRouter,
|
|
24
|
-
AnyRouteInfo,
|
|
25
|
-
AllRouteInfo,
|
|
26
|
-
RouteInfo,
|
|
27
|
-
ValidFromPath,
|
|
28
|
-
LinkOptions,
|
|
29
|
-
RouteInfoByPath,
|
|
30
|
-
ResolveRelativePath,
|
|
31
|
-
NoInfer,
|
|
32
|
-
ToOptions,
|
|
33
|
-
invariant,
|
|
34
|
-
} from '@tanstack/router-core'
|
|
35
|
-
|
|
36
|
-
export * from '@tanstack/router-core'
|
|
37
|
-
|
|
38
|
-
export { lazyWithPreload as lazy } from 'react-lazy-with-preload/lib/index'
|
|
39
|
-
export type { PreloadableComponent as LazyComponent } from 'react-lazy-with-preload'
|
|
40
|
-
|
|
41
|
-
type SyncRouteComponent = (props?: {}) => React.ReactNode
|
|
42
|
-
export type RouteComponent = SyncRouteComponent & {
|
|
43
|
-
preload?: () => Promise<{
|
|
44
|
-
default: SyncRouteComponent
|
|
45
|
-
}>
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
declare module '@tanstack/router-core' {
|
|
49
|
-
interface FrameworkGenerics {
|
|
50
|
-
Component: RouteComponent
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
interface Router<
|
|
54
|
-
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
55
|
-
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
56
|
-
> {
|
|
57
|
-
useState: () => RouterState
|
|
58
|
-
useRoute: <TId extends keyof TAllRouteInfo['routeInfoById']>(
|
|
59
|
-
routeId: TId,
|
|
60
|
-
) => Route<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
|
|
61
|
-
useMatch: <
|
|
62
|
-
TId extends keyof TAllRouteInfo['routeInfoById'],
|
|
63
|
-
TStrict extends true | false = true,
|
|
64
|
-
>(
|
|
65
|
-
routeId: TId,
|
|
66
|
-
opts?: { strict?: TStrict },
|
|
67
|
-
) => TStrict extends true
|
|
68
|
-
? RouteMatch<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
|
|
69
|
-
:
|
|
70
|
-
| RouteMatch<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
|
|
71
|
-
| undefined
|
|
72
|
-
linkProps: <TTo extends string = '.'>(
|
|
73
|
-
props: LinkPropsOptions<TAllRouteInfo, '/', TTo> &
|
|
74
|
-
React.AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
75
|
-
) => React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
76
|
-
Link: <TTo extends string = '.'>(
|
|
77
|
-
props: LinkPropsOptions<TAllRouteInfo, '/', TTo> &
|
|
78
|
-
React.AnchorHTMLAttributes<HTMLAnchorElement> &
|
|
79
|
-
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
|
|
80
|
-
// 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
|
|
81
|
-
children?:
|
|
82
|
-
| React.ReactNode
|
|
83
|
-
| ((state: { isActive: boolean }) => React.ReactNode)
|
|
84
|
-
},
|
|
85
|
-
) => JSX.Element
|
|
86
|
-
MatchRoute: <TTo extends string = '.'>(
|
|
87
|
-
props: ToOptions<TAllRouteInfo, '/', TTo> &
|
|
88
|
-
MatchRouteOptions & {
|
|
89
|
-
// 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
|
|
90
|
-
children?:
|
|
91
|
-
| React.ReactNode
|
|
92
|
-
| ((
|
|
93
|
-
params: RouteInfoByPath<
|
|
94
|
-
TAllRouteInfo,
|
|
95
|
-
ResolveRelativePath<'/', NoInfer<TTo>>
|
|
96
|
-
>['allParams'],
|
|
97
|
-
) => React.ReactNode)
|
|
98
|
-
},
|
|
99
|
-
) => JSX.Element
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
interface Route<
|
|
103
|
-
TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
|
|
104
|
-
TRouteInfo extends AnyRouteInfo = RouteInfo,
|
|
105
|
-
> {
|
|
106
|
-
useRoute: <
|
|
107
|
-
TTo extends string = '.',
|
|
108
|
-
TResolved extends string = ResolveRelativePath<
|
|
109
|
-
TRouteInfo['id'],
|
|
110
|
-
NoInfer<TTo>
|
|
111
|
-
>,
|
|
112
|
-
>(
|
|
113
|
-
routeId: CheckId<
|
|
114
|
-
TAllRouteInfo,
|
|
115
|
-
TResolved,
|
|
116
|
-
ToIdOption<TAllRouteInfo, TRouteInfo['id'], TTo>
|
|
117
|
-
>,
|
|
118
|
-
opts?: { strict?: boolean },
|
|
119
|
-
) => Route<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TResolved]>
|
|
120
|
-
linkProps: <TTo extends string = '.'>(
|
|
121
|
-
props: LinkPropsOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo> &
|
|
122
|
-
React.AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
123
|
-
) => React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
124
|
-
Link: <TTo extends string = '.'>(
|
|
125
|
-
props: LinkPropsOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo> &
|
|
126
|
-
React.AnchorHTMLAttributes<HTMLAnchorElement> &
|
|
127
|
-
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
|
|
128
|
-
// 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
|
|
129
|
-
children?:
|
|
130
|
-
| React.ReactNode
|
|
131
|
-
| ((state: { isActive: boolean }) => React.ReactNode)
|
|
132
|
-
},
|
|
133
|
-
) => JSX.Element
|
|
134
|
-
MatchRoute: <TTo extends string = '.'>(
|
|
135
|
-
props: ToOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo> &
|
|
136
|
-
MatchRouteOptions & {
|
|
137
|
-
// 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
|
|
138
|
-
children?:
|
|
139
|
-
| React.ReactNode
|
|
140
|
-
| ((
|
|
141
|
-
params: RouteInfoByPath<
|
|
142
|
-
TAllRouteInfo,
|
|
143
|
-
ResolveRelativePath<TRouteInfo['fullPath'], NoInfer<TTo>>
|
|
144
|
-
>['allParams'],
|
|
145
|
-
) => React.ReactNode)
|
|
146
|
-
},
|
|
147
|
-
) => JSX.Element
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
type LinkPropsOptions<
|
|
152
|
-
TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
|
|
153
|
-
TFrom extends ValidFromPath<TAllRouteInfo> = '/',
|
|
154
|
-
TTo extends string = '.',
|
|
155
|
-
> = LinkOptions<TAllRouteInfo, TFrom, TTo> & {
|
|
156
|
-
// A function that returns additional props for the `active` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
|
|
157
|
-
activeProps?:
|
|
158
|
-
| React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
159
|
-
| (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
|
|
160
|
-
// A function that returns additional props for the `inactive` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
|
|
161
|
-
inactiveProps?:
|
|
162
|
-
| React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
163
|
-
| (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export type PromptProps = {
|
|
167
|
-
message: string
|
|
168
|
-
when?: boolean | any
|
|
169
|
-
children?: React.ReactNode
|
|
170
|
-
}
|
|
171
|
-
|
|
172
1
|
//
|
|
173
2
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
typeof window !== 'undefined' &&
|
|
180
|
-
window.document &&
|
|
181
|
-
window.document.createElement,
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
const useLayoutEffect = isDOM ? React.useLayoutEffect : React.useEffect
|
|
185
|
-
|
|
186
|
-
export type MatchesProviderProps = {
|
|
187
|
-
value: RouteMatch[]
|
|
188
|
-
children: React.ReactNode
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export function MatchesProvider(props: MatchesProviderProps) {
|
|
192
|
-
return <matchesContext.Provider {...props} />
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const useRouterSubscription = (router: Router<any, any>) => {
|
|
196
|
-
useSyncExternalStore(
|
|
197
|
-
(cb) => router.subscribe(() => cb()),
|
|
198
|
-
() => router.state,
|
|
199
|
-
() => router.state,
|
|
200
|
-
)
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
export function createReactRouter<
|
|
204
|
-
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
205
|
-
>(opts: RouterOptions<TRouteConfig>): Router<TRouteConfig> {
|
|
206
|
-
const makeRouteExt = (
|
|
207
|
-
route: AnyRoute,
|
|
208
|
-
router: Router<any, any>,
|
|
209
|
-
): Pick<AnyRoute, 'useRoute' | 'linkProps' | 'Link' | 'MatchRoute'> => {
|
|
210
|
-
return {
|
|
211
|
-
useRoute: (subRouteId = '.' as any) => {
|
|
212
|
-
const resolvedRouteId = router.resolvePath(
|
|
213
|
-
route.routeId,
|
|
214
|
-
subRouteId as string,
|
|
215
|
-
)
|
|
216
|
-
const resolvedRoute = router.getRoute(resolvedRouteId)
|
|
217
|
-
useRouterSubscription(router)
|
|
218
|
-
invariant(
|
|
219
|
-
resolvedRoute,
|
|
220
|
-
`Could not find a route for route "${
|
|
221
|
-
resolvedRouteId as string
|
|
222
|
-
}"! Did you forget to add it to your route config?`,
|
|
223
|
-
)
|
|
224
|
-
return resolvedRoute
|
|
225
|
-
},
|
|
226
|
-
linkProps: (options) => {
|
|
227
|
-
const {
|
|
228
|
-
// custom props
|
|
229
|
-
type,
|
|
230
|
-
children,
|
|
231
|
-
target,
|
|
232
|
-
activeProps = () => ({ className: 'active' }),
|
|
233
|
-
inactiveProps = () => ({}),
|
|
234
|
-
activeOptions,
|
|
235
|
-
disabled,
|
|
236
|
-
// fromCurrent,
|
|
237
|
-
hash,
|
|
238
|
-
search,
|
|
239
|
-
params,
|
|
240
|
-
to,
|
|
241
|
-
preload,
|
|
242
|
-
preloadDelay,
|
|
243
|
-
preloadMaxAge,
|
|
244
|
-
replace,
|
|
245
|
-
// element props
|
|
246
|
-
style,
|
|
247
|
-
className,
|
|
248
|
-
onClick,
|
|
249
|
-
onFocus,
|
|
250
|
-
onMouseEnter,
|
|
251
|
-
onMouseLeave,
|
|
252
|
-
onTouchStart,
|
|
253
|
-
onTouchEnd,
|
|
254
|
-
...rest
|
|
255
|
-
} = options
|
|
256
|
-
|
|
257
|
-
const linkInfo = route.buildLink(options)
|
|
258
|
-
|
|
259
|
-
if (linkInfo.type === 'external') {
|
|
260
|
-
const { href } = linkInfo
|
|
261
|
-
return { href }
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const {
|
|
265
|
-
handleClick,
|
|
266
|
-
handleFocus,
|
|
267
|
-
handleEnter,
|
|
268
|
-
handleLeave,
|
|
269
|
-
isActive,
|
|
270
|
-
next,
|
|
271
|
-
} = linkInfo
|
|
272
|
-
|
|
273
|
-
const reactHandleClick = (e: Event) => {
|
|
274
|
-
React.startTransition(() => {
|
|
275
|
-
handleClick(e)
|
|
276
|
-
})
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const composeHandlers =
|
|
280
|
-
(handlers: (undefined | ((e: any) => void))[]) =>
|
|
281
|
-
(e: React.SyntheticEvent) => {
|
|
282
|
-
e.persist()
|
|
283
|
-
handlers.forEach((handler) => {
|
|
284
|
-
if (handler) handler(e)
|
|
285
|
-
})
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// Get the active props
|
|
289
|
-
const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> =
|
|
290
|
-
isActive ? functionalUpdate(activeProps, {}) ?? {} : {}
|
|
291
|
-
|
|
292
|
-
// Get the inactive props
|
|
293
|
-
const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =
|
|
294
|
-
isActive ? {} : functionalUpdate(inactiveProps, {}) ?? {}
|
|
295
|
-
|
|
296
|
-
return {
|
|
297
|
-
...resolvedActiveProps,
|
|
298
|
-
...resolvedInactiveProps,
|
|
299
|
-
...rest,
|
|
300
|
-
href: disabled ? undefined : next.href,
|
|
301
|
-
onClick: composeHandlers([reactHandleClick, onClick]),
|
|
302
|
-
onFocus: composeHandlers([handleFocus, onFocus]),
|
|
303
|
-
onMouseEnter: composeHandlers([handleEnter, onMouseEnter]),
|
|
304
|
-
onMouseLeave: composeHandlers([handleLeave, onMouseLeave]),
|
|
305
|
-
target,
|
|
306
|
-
style: {
|
|
307
|
-
...style,
|
|
308
|
-
...resolvedActiveProps.style,
|
|
309
|
-
...resolvedInactiveProps.style,
|
|
310
|
-
},
|
|
311
|
-
className:
|
|
312
|
-
[
|
|
313
|
-
className,
|
|
314
|
-
resolvedActiveProps.className,
|
|
315
|
-
resolvedInactiveProps.className,
|
|
316
|
-
]
|
|
317
|
-
.filter(Boolean)
|
|
318
|
-
.join(' ') || undefined,
|
|
319
|
-
...(disabled
|
|
320
|
-
? {
|
|
321
|
-
role: 'link',
|
|
322
|
-
'aria-disabled': true,
|
|
323
|
-
}
|
|
324
|
-
: undefined),
|
|
325
|
-
['data-status']: isActive ? 'active' : undefined,
|
|
326
|
-
}
|
|
327
|
-
},
|
|
328
|
-
Link: React.forwardRef((props: any, ref) => {
|
|
329
|
-
const linkProps = route.linkProps(props)
|
|
330
|
-
|
|
331
|
-
useRouterSubscription(router)
|
|
332
|
-
|
|
333
|
-
return (
|
|
334
|
-
<a
|
|
335
|
-
{...{
|
|
336
|
-
ref: ref as any,
|
|
337
|
-
...linkProps,
|
|
338
|
-
children:
|
|
339
|
-
typeof props.children === 'function'
|
|
340
|
-
? props.children({
|
|
341
|
-
isActive: (linkProps as any)['data-status'] === 'active',
|
|
342
|
-
})
|
|
343
|
-
: props.children,
|
|
344
|
-
}}
|
|
345
|
-
/>
|
|
346
|
-
)
|
|
347
|
-
}) as any,
|
|
348
|
-
MatchRoute: (opts) => {
|
|
349
|
-
const { pending, caseSensitive, children, ...rest } = opts
|
|
350
|
-
|
|
351
|
-
const params = route.matchRoute(rest as any, {
|
|
352
|
-
pending,
|
|
353
|
-
caseSensitive,
|
|
354
|
-
})
|
|
355
|
-
|
|
356
|
-
if (!params) {
|
|
357
|
-
return null
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
return typeof opts.children === 'function'
|
|
361
|
-
? opts.children(params as any)
|
|
362
|
-
: (opts.children as any)
|
|
363
|
-
},
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
const coreRouter = createRouter<TRouteConfig>({
|
|
368
|
-
...opts,
|
|
369
|
-
createRouter: (router) => {
|
|
370
|
-
const routerExt: Pick<Router<any, any>, 'useMatch' | 'useState'> = {
|
|
371
|
-
useState: () => {
|
|
372
|
-
useRouterSubscription(router)
|
|
373
|
-
return router.state
|
|
374
|
-
},
|
|
375
|
-
useMatch: (routeId, opts) => {
|
|
376
|
-
useRouterSubscription(router)
|
|
377
|
-
|
|
378
|
-
invariant(
|
|
379
|
-
routeId !== rootRouteId,
|
|
380
|
-
`"${rootRouteId}" cannot be used with useMatch! Did you mean to useRoute("${rootRouteId}")?`,
|
|
381
|
-
)
|
|
382
|
-
|
|
383
|
-
const runtimeMatch = useMatches()?.[0]!
|
|
384
|
-
const match = router.state.matches.find((d) => d.routeId === routeId)
|
|
385
|
-
|
|
386
|
-
if (opts?.strict ?? true) {
|
|
387
|
-
invariant(
|
|
388
|
-
match,
|
|
389
|
-
`Could not find an active match for "${routeId as string}"!`,
|
|
390
|
-
)
|
|
391
|
-
|
|
392
|
-
invariant(
|
|
393
|
-
runtimeMatch.routeId == match?.routeId,
|
|
394
|
-
`useMatch("${
|
|
395
|
-
match?.routeId as string
|
|
396
|
-
}") is being called in a component that is meant to render the '${
|
|
397
|
-
runtimeMatch.routeId
|
|
398
|
-
}' route. Did you mean to 'useMatch("${
|
|
399
|
-
match?.routeId as string
|
|
400
|
-
}", { strict: false })' or 'useRoute("${
|
|
401
|
-
match?.routeId as string
|
|
402
|
-
}")' instead?`,
|
|
403
|
-
)
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
return match as any
|
|
407
|
-
},
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
const routeExt = makeRouteExt(router.getRoute('/'), router)
|
|
411
|
-
|
|
412
|
-
Object.assign(router, routerExt, routeExt)
|
|
413
|
-
},
|
|
414
|
-
createRoute: ({ router, route }) => {
|
|
415
|
-
const routeExt = makeRouteExt(route, router)
|
|
416
|
-
|
|
417
|
-
Object.assign(route, routeExt)
|
|
418
|
-
},
|
|
419
|
-
loadComponent: async (component) => {
|
|
420
|
-
if (component.preload && typeof document !== 'undefined') {
|
|
421
|
-
component.preload()
|
|
422
|
-
// return await component.preload()
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
return component as any
|
|
426
|
-
},
|
|
427
|
-
})
|
|
428
|
-
|
|
429
|
-
return coreRouter as any
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
export type RouterProps<
|
|
433
|
-
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
434
|
-
TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
|
|
435
|
-
> = RouterOptions<TRouteConfig> & {
|
|
436
|
-
router: Router<TRouteConfig, TAllRouteInfo>
|
|
437
|
-
// Children will default to `<Outlet />` if not provided
|
|
438
|
-
children?: React.ReactNode
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
export function RouterProvider<
|
|
442
|
-
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
443
|
-
TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
|
|
444
|
-
>({ children, router, ...rest }: RouterProps<TRouteConfig, TAllRouteInfo>) {
|
|
445
|
-
router.update(rest)
|
|
446
|
-
|
|
447
|
-
useRouterSubscription(router)
|
|
448
|
-
React.useEffect(() => {
|
|
449
|
-
return router.mount()
|
|
450
|
-
}, [router])
|
|
451
|
-
|
|
452
|
-
return (
|
|
453
|
-
<routerContext.Provider value={{ router }}>
|
|
454
|
-
<MatchesProvider value={router.state.matches}>
|
|
455
|
-
{children ?? <Outlet />}
|
|
456
|
-
</MatchesProvider>
|
|
457
|
-
</routerContext.Provider>
|
|
458
|
-
)
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
export function useRouter(): Router {
|
|
462
|
-
const value = React.useContext(routerContext)
|
|
463
|
-
warning(!value, 'useRouter must be used inside a <Router> component!')
|
|
464
|
-
|
|
465
|
-
useRouterSubscription(value.router)
|
|
466
|
-
|
|
467
|
-
return value.router as Router
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
export function useMatches(): RouteMatch[] {
|
|
471
|
-
return React.useContext(matchesContext)
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
export function Outlet() {
|
|
475
|
-
const router = useRouter()
|
|
476
|
-
const matches = useMatches().slice(1)
|
|
477
|
-
const match = matches[0]
|
|
478
|
-
|
|
479
|
-
const defaultPending = React.useCallback(() => null, [])
|
|
480
|
-
|
|
481
|
-
if (!match) {
|
|
482
|
-
return null
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
const PendingComponent = (match.__.pendingComponent ??
|
|
486
|
-
router.options.defaultPendingComponent ??
|
|
487
|
-
defaultPending) as any
|
|
488
|
-
|
|
489
|
-
const errorComponent =
|
|
490
|
-
match.__.errorComponent ?? router.options.defaultErrorComponent
|
|
491
|
-
|
|
492
|
-
return (
|
|
493
|
-
<MatchesProvider value={matches}>
|
|
494
|
-
<React.Suspense fallback={<PendingComponent />}>
|
|
495
|
-
<CatchBoundary errorComponent={errorComponent}>
|
|
496
|
-
{
|
|
497
|
-
((): React.ReactNode => {
|
|
498
|
-
if (match.status === 'error') {
|
|
499
|
-
throw match.error
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
if (match.status === 'success') {
|
|
503
|
-
return React.createElement(
|
|
504
|
-
(match.__.component as any) ??
|
|
505
|
-
router.options.defaultComponent ??
|
|
506
|
-
Outlet,
|
|
507
|
-
)
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
if (match.__.loadPromise) {
|
|
511
|
-
console.log(match.matchId, 'suspend')
|
|
512
|
-
throw match.__.loadPromise
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
invariant(false, 'This should never happen!')
|
|
516
|
-
})() as JSX.Element
|
|
517
|
-
}
|
|
518
|
-
</CatchBoundary>
|
|
519
|
-
</React.Suspense>
|
|
520
|
-
</MatchesProvider>
|
|
521
|
-
)
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
class CatchBoundary extends React.Component<{
|
|
525
|
-
children: any
|
|
526
|
-
errorComponent: any
|
|
527
|
-
}> {
|
|
528
|
-
state = {
|
|
529
|
-
error: false,
|
|
530
|
-
}
|
|
531
|
-
componentDidCatch(error: any, info: any) {
|
|
532
|
-
console.error(error)
|
|
533
|
-
|
|
534
|
-
this.setState({
|
|
535
|
-
error,
|
|
536
|
-
info,
|
|
537
|
-
})
|
|
538
|
-
}
|
|
539
|
-
render() {
|
|
540
|
-
const errorComponent = this.props.errorComponent ?? DefaultErrorBoundary
|
|
541
|
-
|
|
542
|
-
if (this.state.error) {
|
|
543
|
-
return React.createElement(errorComponent, this.state)
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
return this.props.children
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
export function DefaultErrorBoundary({ error }: { error: any }) {
|
|
551
|
-
return (
|
|
552
|
-
<div style={{ padding: '.5rem', maxWidth: '100%' }}>
|
|
553
|
-
<strong style={{ fontSize: '1.2rem' }}>Something went wrong!</strong>
|
|
554
|
-
<div style={{ height: '.5rem' }} />
|
|
555
|
-
<div>
|
|
556
|
-
<pre>
|
|
557
|
-
{error.message ? (
|
|
558
|
-
<code
|
|
559
|
-
style={{
|
|
560
|
-
fontSize: '.7em',
|
|
561
|
-
border: '1px solid red',
|
|
562
|
-
borderRadius: '.25rem',
|
|
563
|
-
padding: '.5rem',
|
|
564
|
-
color: 'red',
|
|
565
|
-
}}
|
|
566
|
-
>
|
|
567
|
-
{error.message}
|
|
568
|
-
</code>
|
|
569
|
-
) : null}
|
|
570
|
-
</pre>
|
|
571
|
-
</div>
|
|
572
|
-
</div>
|
|
573
|
-
)
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
export function usePrompt(message: string, when: boolean | any): void {
|
|
577
|
-
const router = useRouter()
|
|
578
|
-
|
|
579
|
-
React.useEffect(() => {
|
|
580
|
-
if (!when) return
|
|
581
|
-
|
|
582
|
-
let unblock = router.history.block((transition) => {
|
|
583
|
-
if (window.confirm(message)) {
|
|
584
|
-
unblock()
|
|
585
|
-
transition.retry()
|
|
586
|
-
} else {
|
|
587
|
-
router.location.pathname = window.location.pathname
|
|
588
|
-
}
|
|
589
|
-
})
|
|
590
|
-
|
|
591
|
-
return unblock
|
|
592
|
-
}, [when, location, message])
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
export function Prompt({ message, when, children }: PromptProps) {
|
|
596
|
-
usePrompt(message, when ?? true)
|
|
597
|
-
return (children ?? null) as React.ReactNode
|
|
598
|
-
}
|
|
3
|
+
export { useStore } from '@tanstack/react-store'
|
|
4
|
+
export * from '@tanstack/router-core'
|
|
5
|
+
export * from './react'
|
|
6
|
+
export * from './scroll-restoration'
|
|
7
|
+
export * from './awaited'
|