@tanstack/react-router 0.0.1-alpha.1 → 0.0.1-alpha.11
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/react-router/src/index.js +201 -149
- package/build/cjs/react-router/src/index.js.map +1 -1
- package/build/cjs/router-core/build/esm/index.js +1395 -1232
- package/build/cjs/router-core/build/esm/index.js.map +1 -1
- package/build/esm/index.js +1482 -1285
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +129 -0
- package/build/types/index.d.ts +74 -0
- package/build/umd/index.development.js +1504 -1302
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +2 -2
- package/build/umd/index.production.js.map +1 -1
- package/package.json +3 -3
- package/src/index.tsx +296 -235
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/react-router",
|
|
3
3
|
"author": "Tanner Linsley",
|
|
4
|
-
"version": "0.0.1-alpha.
|
|
4
|
+
"version": "0.0.1-alpha.11",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "tanstack/router",
|
|
7
7
|
"homepage": "https://react-router.tanstack.com/",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"node": ">=12"
|
|
32
32
|
},
|
|
33
33
|
"files": [
|
|
34
|
-
"build",
|
|
34
|
+
"build/**",
|
|
35
35
|
"src"
|
|
36
36
|
],
|
|
37
37
|
"peerDependencies": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@babel/runtime": "^7.16.7",
|
|
43
|
-
"@tanstack/router-core": "0.0.1-alpha.
|
|
43
|
+
"@tanstack/router-core": "0.0.1-alpha.11",
|
|
44
44
|
"use-sync-external-store": "^1.2.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
package/src/index.tsx
CHANGED
|
@@ -4,10 +4,15 @@ import { useSyncExternalStore } from 'use-sync-external-store/shim'
|
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
AnyRoute,
|
|
7
|
+
CheckId,
|
|
8
|
+
CheckPath,
|
|
9
|
+
Expand,
|
|
10
|
+
resolvePath,
|
|
7
11
|
RootRouteId,
|
|
8
12
|
rootRouteId,
|
|
9
|
-
Route,
|
|
10
13
|
Router,
|
|
14
|
+
RouterState,
|
|
15
|
+
ToIdOption,
|
|
11
16
|
} from '@tanstack/router-core'
|
|
12
17
|
import {
|
|
13
18
|
warning,
|
|
@@ -29,6 +34,7 @@ import {
|
|
|
29
34
|
ResolveRelativePath,
|
|
30
35
|
NoInfer,
|
|
31
36
|
ToOptions,
|
|
37
|
+
invariant,
|
|
32
38
|
} from '@tanstack/router-core'
|
|
33
39
|
|
|
34
40
|
export * from '@tanstack/router-core'
|
|
@@ -36,59 +42,68 @@ export * from '@tanstack/router-core'
|
|
|
36
42
|
declare module '@tanstack/router-core' {
|
|
37
43
|
interface FrameworkGenerics {
|
|
38
44
|
Element: React.ReactNode
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}) => Promise<React.ReactNode>
|
|
42
|
-
SyncOrAsyncElement: React.ReactNode | FrameworkGenerics['AsyncElement']
|
|
45
|
+
// Any is required here so import() will work without having to do import().then(d => d.default)
|
|
46
|
+
SyncOrAsyncElement: React.ReactNode | (() => Promise<any>)
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
interface Router<
|
|
46
50
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
47
51
|
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
48
|
-
>
|
|
49
|
-
|
|
50
|
-
'linkProps' | 'Link' | 'MatchRoute'
|
|
51
|
-
> {
|
|
52
|
+
> {
|
|
53
|
+
useState: () => RouterState
|
|
52
54
|
useRoute: <TId extends keyof TAllRouteInfo['routeInfoById']>(
|
|
53
55
|
routeId: TId,
|
|
54
56
|
) => Route<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
|
|
55
57
|
useMatch: <TId extends keyof TAllRouteInfo['routeInfoById']>(
|
|
56
58
|
routeId: TId,
|
|
57
59
|
) => RouteMatch<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
60
|
+
linkProps: <TTo extends string = '.'>(
|
|
61
|
+
props: LinkPropsOptions<TAllRouteInfo, '/', TTo> &
|
|
62
|
+
React.AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
63
|
+
) => React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
64
|
+
Link: <TTo extends string = '.'>(
|
|
65
|
+
props: LinkPropsOptions<TAllRouteInfo, '/', TTo> &
|
|
66
|
+
React.AnchorHTMLAttributes<HTMLAnchorElement> &
|
|
67
|
+
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
|
|
68
|
+
// 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
|
|
69
|
+
children?:
|
|
70
|
+
| React.ReactNode
|
|
71
|
+
| ((state: { isActive: boolean }) => React.ReactNode)
|
|
72
|
+
},
|
|
73
|
+
) => JSX.Element
|
|
74
|
+
MatchRoute: <TTo extends string = '.'>(
|
|
75
|
+
props: ToOptions<TAllRouteInfo, '/', TTo> &
|
|
76
|
+
MatchRouteOptions & {
|
|
77
|
+
// 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
|
|
78
|
+
children?:
|
|
79
|
+
| React.ReactNode
|
|
80
|
+
| ((
|
|
81
|
+
params: RouteInfoByPath<
|
|
82
|
+
TAllRouteInfo,
|
|
83
|
+
ResolveRelativePath<'/', NoInfer<TTo>>
|
|
84
|
+
>['allParams'],
|
|
85
|
+
) => React.ReactNode)
|
|
86
|
+
},
|
|
87
|
+
) => JSX.Element
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
interface Route<
|
|
89
91
|
TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
|
|
90
92
|
TRouteInfo extends AnyRouteInfo = RouteInfo,
|
|
91
93
|
> {
|
|
94
|
+
useRoute: <
|
|
95
|
+
TTo extends string = '.',
|
|
96
|
+
TResolved extends string = ResolveRelativePath<
|
|
97
|
+
TRouteInfo['id'],
|
|
98
|
+
NoInfer<TTo>
|
|
99
|
+
>,
|
|
100
|
+
>(
|
|
101
|
+
routeId: CheckId<
|
|
102
|
+
TAllRouteInfo,
|
|
103
|
+
TResolved,
|
|
104
|
+
ToIdOption<TAllRouteInfo, TRouteInfo['id'], TTo>
|
|
105
|
+
>,
|
|
106
|
+
) => Route<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TResolved]>
|
|
92
107
|
linkProps: <TTo extends string = '.'>(
|
|
93
108
|
props: LinkPropsOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo> &
|
|
94
109
|
React.AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
@@ -174,203 +189,228 @@ const useRouterSubscription = (router: Router<any, any>) => {
|
|
|
174
189
|
export function createReactRouter<
|
|
175
190
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
176
191
|
>(opts: RouterOptions<TRouteConfig>): Router<TRouteConfig> {
|
|
192
|
+
const makeRouteExt = (
|
|
193
|
+
route: AnyRoute,
|
|
194
|
+
router: Router<any, any>,
|
|
195
|
+
): Pick<AnyRoute, 'useRoute' | 'linkProps' | 'Link' | 'MatchRoute'> => {
|
|
196
|
+
return {
|
|
197
|
+
useRoute: (subRouteId = '.' as any) => {
|
|
198
|
+
const resolvedRouteId = router.resolvePath(
|
|
199
|
+
route.routeId,
|
|
200
|
+
subRouteId as string,
|
|
201
|
+
)
|
|
202
|
+
const resolvedRoute = router.getRoute(resolvedRouteId)
|
|
203
|
+
useRouterSubscription(router)
|
|
204
|
+
invariant(
|
|
205
|
+
resolvedRoute,
|
|
206
|
+
`Could not find a route for route "${
|
|
207
|
+
resolvedRouteId as string
|
|
208
|
+
}"! Did you forget to add it to your route config?`,
|
|
209
|
+
)
|
|
210
|
+
return resolvedRoute
|
|
211
|
+
},
|
|
212
|
+
linkProps: (options) => {
|
|
213
|
+
const {
|
|
214
|
+
// custom props
|
|
215
|
+
type,
|
|
216
|
+
children,
|
|
217
|
+
target,
|
|
218
|
+
activeProps = () => ({ className: 'active' }),
|
|
219
|
+
inactiveProps = () => ({}),
|
|
220
|
+
activeOptions,
|
|
221
|
+
disabled,
|
|
222
|
+
// fromCurrent,
|
|
223
|
+
hash,
|
|
224
|
+
search,
|
|
225
|
+
params,
|
|
226
|
+
to,
|
|
227
|
+
preload,
|
|
228
|
+
preloadDelay,
|
|
229
|
+
preloadMaxAge,
|
|
230
|
+
replace,
|
|
231
|
+
// element props
|
|
232
|
+
style,
|
|
233
|
+
className,
|
|
234
|
+
onClick,
|
|
235
|
+
onFocus,
|
|
236
|
+
onMouseEnter,
|
|
237
|
+
onMouseLeave,
|
|
238
|
+
onTouchStart,
|
|
239
|
+
onTouchEnd,
|
|
240
|
+
...rest
|
|
241
|
+
} = options
|
|
242
|
+
|
|
243
|
+
const linkInfo = route.buildLink(options)
|
|
244
|
+
|
|
245
|
+
if (linkInfo.type === 'external') {
|
|
246
|
+
const { href } = linkInfo
|
|
247
|
+
return { href }
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const {
|
|
251
|
+
handleClick,
|
|
252
|
+
handleFocus,
|
|
253
|
+
handleEnter,
|
|
254
|
+
handleLeave,
|
|
255
|
+
isActive,
|
|
256
|
+
next,
|
|
257
|
+
} = linkInfo
|
|
258
|
+
|
|
259
|
+
const composeHandlers =
|
|
260
|
+
(handlers: (undefined | ((e: any) => void))[]) =>
|
|
261
|
+
(e: React.SyntheticEvent) => {
|
|
262
|
+
e.persist()
|
|
263
|
+
handlers.forEach((handler) => {
|
|
264
|
+
if (handler) handler(e)
|
|
265
|
+
})
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Get the active props
|
|
269
|
+
const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> =
|
|
270
|
+
isActive ? functionalUpdate(activeProps, {}) ?? {} : {}
|
|
271
|
+
|
|
272
|
+
// Get the inactive props
|
|
273
|
+
const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =
|
|
274
|
+
isActive ? {} : functionalUpdate(inactiveProps, {}) ?? {}
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
...resolvedActiveProps,
|
|
278
|
+
...resolvedInactiveProps,
|
|
279
|
+
...rest,
|
|
280
|
+
href: disabled ? undefined : next.href,
|
|
281
|
+
onClick: composeHandlers([handleClick, onClick]),
|
|
282
|
+
onFocus: composeHandlers([handleFocus, onFocus]),
|
|
283
|
+
onMouseEnter: composeHandlers([handleEnter, onMouseEnter]),
|
|
284
|
+
onMouseLeave: composeHandlers([handleLeave, onMouseLeave]),
|
|
285
|
+
target,
|
|
286
|
+
style: {
|
|
287
|
+
...style,
|
|
288
|
+
...resolvedActiveProps.style,
|
|
289
|
+
...resolvedInactiveProps.style,
|
|
290
|
+
},
|
|
291
|
+
className:
|
|
292
|
+
[
|
|
293
|
+
className,
|
|
294
|
+
resolvedActiveProps.className,
|
|
295
|
+
resolvedInactiveProps.className,
|
|
296
|
+
]
|
|
297
|
+
.filter(Boolean)
|
|
298
|
+
.join(' ') || undefined,
|
|
299
|
+
...(disabled
|
|
300
|
+
? {
|
|
301
|
+
role: 'link',
|
|
302
|
+
'aria-disabled': true,
|
|
303
|
+
}
|
|
304
|
+
: undefined),
|
|
305
|
+
['data-status']: isActive ? 'active' : undefined,
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
Link: React.forwardRef((props: any, ref) => {
|
|
309
|
+
const linkProps = route.linkProps(props)
|
|
310
|
+
|
|
311
|
+
useRouterSubscription(router)
|
|
312
|
+
|
|
313
|
+
return (
|
|
314
|
+
<a
|
|
315
|
+
{...{
|
|
316
|
+
ref: ref as any,
|
|
317
|
+
...linkProps,
|
|
318
|
+
children:
|
|
319
|
+
typeof props.children === 'function'
|
|
320
|
+
? props.children({
|
|
321
|
+
isActive: (linkProps as any)['data-status'] === 'active',
|
|
322
|
+
})
|
|
323
|
+
: props.children,
|
|
324
|
+
}}
|
|
325
|
+
/>
|
|
326
|
+
)
|
|
327
|
+
}) as any,
|
|
328
|
+
MatchRoute: (opts) => {
|
|
329
|
+
const { pending, caseSensitive, children, ...rest } = opts
|
|
330
|
+
|
|
331
|
+
const params = route.matchRoute(rest as any, {
|
|
332
|
+
pending,
|
|
333
|
+
caseSensitive,
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
if (!params) {
|
|
337
|
+
return null
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return typeof opts.children === 'function'
|
|
341
|
+
? opts.children(params as any)
|
|
342
|
+
: (opts.children as any)
|
|
343
|
+
},
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
177
347
|
const coreRouter = createRouter<TRouteConfig>({
|
|
178
348
|
...opts,
|
|
179
349
|
createRouter: (router) => {
|
|
180
|
-
const routerExt: Pick<Router<any, any>, '
|
|
181
|
-
|
|
182
|
-
const route = router.getRoute(routeId)
|
|
350
|
+
const routerExt: Pick<Router<any, any>, 'useMatch' | 'useState'> = {
|
|
351
|
+
useState: () => {
|
|
183
352
|
useRouterSubscription(router)
|
|
184
|
-
|
|
185
|
-
throw new Error(
|
|
186
|
-
`Could not find a route for route "${
|
|
187
|
-
routeId as string
|
|
188
|
-
}"! Did you forget to add it to your route config?`,
|
|
189
|
-
)
|
|
190
|
-
}
|
|
191
|
-
return route
|
|
353
|
+
return router.state
|
|
192
354
|
},
|
|
193
355
|
useMatch: (routeId) => {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
356
|
+
useRouterSubscription(router)
|
|
357
|
+
|
|
358
|
+
invariant(
|
|
359
|
+
routeId !== rootRouteId,
|
|
360
|
+
`"${rootRouteId}" cannot be used with useMatch! Did you mean to useRoute("${rootRouteId}")?`,
|
|
361
|
+
)
|
|
362
|
+
|
|
199
363
|
const runtimeMatch = useMatch()
|
|
200
364
|
const match = router.state.matches.find((d) => d.routeId === routeId)
|
|
201
365
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (runtimeMatch.routeId !== match?.routeId) {
|
|
211
|
-
throw new Error(
|
|
212
|
-
`useMatch('${
|
|
213
|
-
match?.routeId as string
|
|
214
|
-
}') is being called in a component that is meant to render the '${
|
|
215
|
-
runtimeMatch.routeId
|
|
216
|
-
}' route. Did you mean to 'useRoute(${
|
|
217
|
-
match?.routeId as string
|
|
218
|
-
})' instead?`,
|
|
219
|
-
)
|
|
220
|
-
}
|
|
366
|
+
invariant(
|
|
367
|
+
match,
|
|
368
|
+
`Could not find a match for route "${
|
|
369
|
+
routeId as string
|
|
370
|
+
}" being rendered in this component!`,
|
|
371
|
+
)
|
|
221
372
|
|
|
222
|
-
|
|
373
|
+
invariant(
|
|
374
|
+
runtimeMatch.routeId == match?.routeId,
|
|
375
|
+
`useMatch('${
|
|
376
|
+
match?.routeId as string
|
|
377
|
+
}') is being called in a component that is meant to render the '${
|
|
378
|
+
runtimeMatch.routeId
|
|
379
|
+
}' route. Did you mean to 'useRoute(${
|
|
380
|
+
match?.routeId as string
|
|
381
|
+
})' instead?`,
|
|
382
|
+
)
|
|
223
383
|
|
|
224
384
|
if (!match) {
|
|
225
|
-
|
|
385
|
+
invariant('Match not found!')
|
|
226
386
|
}
|
|
227
387
|
|
|
228
388
|
return match
|
|
229
389
|
},
|
|
230
390
|
}
|
|
231
391
|
|
|
232
|
-
|
|
392
|
+
const routeExt = makeRouteExt(router.getRoute('/'), router)
|
|
393
|
+
|
|
394
|
+
Object.assign(router, routerExt, routeExt)
|
|
233
395
|
},
|
|
234
396
|
createRoute: ({ router, route }) => {
|
|
235
|
-
const routeExt
|
|
236
|
-
linkProps: (options) => {
|
|
237
|
-
const {
|
|
238
|
-
// custom props
|
|
239
|
-
type,
|
|
240
|
-
children,
|
|
241
|
-
target,
|
|
242
|
-
activeProps = () => ({ className: 'active' }),
|
|
243
|
-
inactiveProps = () => ({}),
|
|
244
|
-
activeOptions,
|
|
245
|
-
disabled,
|
|
246
|
-
// fromCurrent,
|
|
247
|
-
hash,
|
|
248
|
-
search,
|
|
249
|
-
params,
|
|
250
|
-
to,
|
|
251
|
-
preload,
|
|
252
|
-
preloadDelay,
|
|
253
|
-
preloadMaxAge,
|
|
254
|
-
replace,
|
|
255
|
-
// element props
|
|
256
|
-
style,
|
|
257
|
-
className,
|
|
258
|
-
onClick,
|
|
259
|
-
onFocus,
|
|
260
|
-
onMouseEnter,
|
|
261
|
-
onMouseLeave,
|
|
262
|
-
onTouchStart,
|
|
263
|
-
onTouchEnd,
|
|
264
|
-
...rest
|
|
265
|
-
} = options
|
|
266
|
-
|
|
267
|
-
const linkInfo = route.buildLink(options)
|
|
268
|
-
|
|
269
|
-
if (linkInfo.type === 'external') {
|
|
270
|
-
const { href } = linkInfo
|
|
271
|
-
return { href }
|
|
272
|
-
}
|
|
397
|
+
const routeExt = makeRouteExt(route, router)
|
|
273
398
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
e.persist()
|
|
287
|
-
handlers.forEach((handler) => {
|
|
288
|
-
if (handler) handler(e)
|
|
289
|
-
})
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Get the active props
|
|
293
|
-
const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> =
|
|
294
|
-
isActive ? functionalUpdate(activeProps) ?? {} : {}
|
|
295
|
-
|
|
296
|
-
// Get the inactive props
|
|
297
|
-
const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =
|
|
298
|
-
isActive ? {} : functionalUpdate(inactiveProps) ?? {}
|
|
299
|
-
|
|
300
|
-
return {
|
|
301
|
-
...resolvedActiveProps,
|
|
302
|
-
...resolvedInactiveProps,
|
|
303
|
-
...rest,
|
|
304
|
-
href: disabled ? undefined : next.href,
|
|
305
|
-
onClick: composeHandlers([handleClick, onClick]),
|
|
306
|
-
onFocus: composeHandlers([handleFocus, onFocus]),
|
|
307
|
-
onMouseEnter: composeHandlers([handleEnter, onMouseEnter]),
|
|
308
|
-
onMouseLeave: composeHandlers([handleLeave, onMouseLeave]),
|
|
309
|
-
target,
|
|
310
|
-
style: {
|
|
311
|
-
...style,
|
|
312
|
-
...resolvedActiveProps.style,
|
|
313
|
-
...resolvedInactiveProps.style,
|
|
314
|
-
},
|
|
315
|
-
className:
|
|
316
|
-
[
|
|
317
|
-
className,
|
|
318
|
-
resolvedActiveProps.className,
|
|
319
|
-
resolvedInactiveProps.className,
|
|
320
|
-
]
|
|
321
|
-
.filter(Boolean)
|
|
322
|
-
.join(' ') || undefined,
|
|
323
|
-
...(disabled
|
|
324
|
-
? {
|
|
325
|
-
role: 'link',
|
|
326
|
-
'aria-disabled': true,
|
|
327
|
-
}
|
|
328
|
-
: undefined),
|
|
329
|
-
['data-status']: isActive ? 'active' : undefined,
|
|
330
|
-
}
|
|
331
|
-
},
|
|
332
|
-
Link: React.forwardRef((props: any, ref) => {
|
|
333
|
-
const linkProps = route.linkProps(props)
|
|
334
|
-
|
|
335
|
-
useRouterSubscription(router)
|
|
336
|
-
|
|
337
|
-
return (
|
|
338
|
-
<a
|
|
339
|
-
{...{
|
|
340
|
-
ref: ref as any,
|
|
341
|
-
...linkProps,
|
|
342
|
-
children:
|
|
343
|
-
typeof props.children === 'function'
|
|
344
|
-
? props.children({
|
|
345
|
-
isActive:
|
|
346
|
-
(linkProps as any)['data-status'] === 'active',
|
|
347
|
-
})
|
|
348
|
-
: props.children,
|
|
349
|
-
}}
|
|
350
|
-
/>
|
|
351
|
-
)
|
|
352
|
-
}) as any,
|
|
353
|
-
MatchRoute: (opts) => {
|
|
354
|
-
const { pending, caseSensitive, children, ...rest } = opts
|
|
355
|
-
|
|
356
|
-
const params = route.matchRoute(rest as any, {
|
|
357
|
-
pending,
|
|
358
|
-
caseSensitive,
|
|
359
|
-
})
|
|
360
|
-
|
|
361
|
-
// useRouterSubscription(router)
|
|
362
|
-
|
|
363
|
-
if (!params) {
|
|
364
|
-
return null
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
return typeof opts.children === 'function'
|
|
368
|
-
? opts.children(params as any)
|
|
369
|
-
: (opts.children as any)
|
|
370
|
-
},
|
|
399
|
+
Object.assign(route, routeExt)
|
|
400
|
+
},
|
|
401
|
+
createElement: async (element) => {
|
|
402
|
+
if (typeof element === 'function') {
|
|
403
|
+
const res = (await element()) as any
|
|
404
|
+
|
|
405
|
+
// Support direct import() calls
|
|
406
|
+
if (typeof res === 'object' && res.default) {
|
|
407
|
+
return React.createElement(res.default)
|
|
408
|
+
} else {
|
|
409
|
+
return res
|
|
410
|
+
}
|
|
371
411
|
}
|
|
372
412
|
|
|
373
|
-
|
|
413
|
+
return element
|
|
374
414
|
},
|
|
375
415
|
})
|
|
376
416
|
|
|
@@ -392,14 +432,11 @@ export function RouterProvider<
|
|
|
392
432
|
>({ children, router, ...rest }: RouterProps<TRouteConfig, TAllRouteInfo>) {
|
|
393
433
|
router.update(rest)
|
|
394
434
|
|
|
395
|
-
|
|
396
|
-
(cb) => router.subscribe(() => cb()),
|
|
397
|
-
() => router.state,
|
|
398
|
-
)
|
|
435
|
+
useRouterSubscription(router)
|
|
399
436
|
|
|
400
437
|
useLayoutEffect(() => {
|
|
401
|
-
router.mount()
|
|
402
|
-
}, [])
|
|
438
|
+
return router.mount()
|
|
439
|
+
}, [router])
|
|
403
440
|
|
|
404
441
|
return (
|
|
405
442
|
<routerContext.Provider value={{ router }}>
|
|
@@ -410,7 +447,7 @@ export function RouterProvider<
|
|
|
410
447
|
)
|
|
411
448
|
}
|
|
412
449
|
|
|
413
|
-
|
|
450
|
+
function useRouter(): Router {
|
|
414
451
|
const value = React.useContext(routerContext)
|
|
415
452
|
warning(!value, 'useRouter must be used inside a <Router> component!')
|
|
416
453
|
|
|
@@ -419,21 +456,21 @@ export function useRouter(): Router {
|
|
|
419
456
|
return value.router as Router
|
|
420
457
|
}
|
|
421
458
|
|
|
422
|
-
|
|
459
|
+
function useMatches(): RouteMatch[] {
|
|
423
460
|
return React.useContext(matchesContext)
|
|
424
461
|
}
|
|
425
462
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
|
|
463
|
+
// function useParentMatches(): RouteMatch[] {
|
|
464
|
+
// const router = useRouter()
|
|
465
|
+
// const match = useMatch()
|
|
466
|
+
// const matches = router.state.matches
|
|
467
|
+
// return matches.slice(
|
|
468
|
+
// 0,
|
|
469
|
+
// matches.findIndex((d) => d.matchId === match.matchId) - 1,
|
|
470
|
+
// )
|
|
471
|
+
// }
|
|
472
|
+
|
|
473
|
+
function useMatch<T>(): RouteMatch {
|
|
437
474
|
return useMatches()?.[0] as RouteMatch
|
|
438
475
|
}
|
|
439
476
|
|
|
@@ -445,7 +482,7 @@ export function Outlet() {
|
|
|
445
482
|
|
|
446
483
|
if (!childMatch) return null
|
|
447
484
|
|
|
448
|
-
const element = ((
|
|
485
|
+
const element = ((): React.ReactNode => {
|
|
449
486
|
if (!childMatch) {
|
|
450
487
|
return null
|
|
451
488
|
}
|
|
@@ -465,7 +502,7 @@ export function Outlet() {
|
|
|
465
502
|
throw childMatch.error
|
|
466
503
|
}
|
|
467
504
|
|
|
468
|
-
return <
|
|
505
|
+
return <DefaultErrorBoundary error={childMatch.error} />
|
|
469
506
|
}
|
|
470
507
|
|
|
471
508
|
if (childMatch.status === 'loading' || childMatch.status === 'idle') {
|
|
@@ -482,7 +519,7 @@ export function Outlet() {
|
|
|
482
519
|
}
|
|
483
520
|
|
|
484
521
|
return (childMatch.__.element as any) ?? router.options.defaultElement
|
|
485
|
-
})() as JSX.Element
|
|
522
|
+
})() as JSX.Element
|
|
486
523
|
|
|
487
524
|
const catchElement =
|
|
488
525
|
childMatch?.options.catchElement ?? router.options.defaultCatchElement
|
|
@@ -516,7 +553,7 @@ class CatchBoundary extends React.Component<{
|
|
|
516
553
|
})
|
|
517
554
|
}
|
|
518
555
|
render() {
|
|
519
|
-
const catchElement = this.props.catchElement ??
|
|
556
|
+
const catchElement = this.props.catchElement ?? DefaultErrorBoundary
|
|
520
557
|
|
|
521
558
|
if (this.state.error) {
|
|
522
559
|
return typeof catchElement === 'function'
|
|
@@ -528,7 +565,7 @@ class CatchBoundary extends React.Component<{
|
|
|
528
565
|
}
|
|
529
566
|
}
|
|
530
567
|
|
|
531
|
-
export function
|
|
568
|
+
export function DefaultErrorBoundary({ error }: { error: any }) {
|
|
532
569
|
return (
|
|
533
570
|
<div style={{ padding: '.5rem', maxWidth: '100%' }}>
|
|
534
571
|
<strong style={{ fontSize: '1.2rem' }}>Something went wrong!</strong>
|
|
@@ -566,3 +603,27 @@ export function DefaultCatchBoundary({ error }: { error: any }) {
|
|
|
566
603
|
</div>
|
|
567
604
|
)
|
|
568
605
|
}
|
|
606
|
+
|
|
607
|
+
export function usePrompt(message: string, when: boolean | any): void {
|
|
608
|
+
const router = useRouter()
|
|
609
|
+
|
|
610
|
+
React.useEffect(() => {
|
|
611
|
+
if (!when) return
|
|
612
|
+
|
|
613
|
+
let unblock = router.history.block((transition) => {
|
|
614
|
+
if (window.confirm(message)) {
|
|
615
|
+
unblock()
|
|
616
|
+
transition.retry()
|
|
617
|
+
} else {
|
|
618
|
+
router.location.pathname = window.location.pathname
|
|
619
|
+
}
|
|
620
|
+
})
|
|
621
|
+
|
|
622
|
+
return unblock
|
|
623
|
+
}, [when, location, message])
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
export function Prompt({ message, when, children }: PromptProps) {
|
|
627
|
+
usePrompt(message, when ?? true)
|
|
628
|
+
return (children ?? null) as React.ReactNode
|
|
629
|
+
}
|