@tanstack/react-router 0.0.1-beta.3 → 0.0.1-beta.31
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/_virtual/_rollupPluginBabelHelpers.js +0 -5
- package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
- package/build/cjs/index.js +448 -0
- package/build/cjs/index.js.map +1 -0
- package/build/esm/index.js +204 -2660
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +59 -49
- package/build/stats-react.json +115 -33
- package/build/types/index.d.ts +66 -36
- package/build/umd/index.development.js +585 -691
- 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 +4 -3
- package/src/index.tsx +357 -211
- package/build/cjs/react-router/src/index.js +0 -465
- package/build/cjs/react-router/src/index.js.map +0 -1
- package/build/cjs/router-core/build/esm/index.js +0 -2493
- package/build/cjs/router-core/build/esm/index.js.map +0 -1
package/src/index.tsx
CHANGED
|
@@ -6,7 +6,9 @@ import {
|
|
|
6
6
|
AnyRoute,
|
|
7
7
|
CheckId,
|
|
8
8
|
rootRouteId,
|
|
9
|
-
|
|
9
|
+
Route,
|
|
10
|
+
RegisteredAllRouteInfo,
|
|
11
|
+
RegisteredRouter,
|
|
10
12
|
RouterState,
|
|
11
13
|
ToIdOption,
|
|
12
14
|
} from '@tanstack/router-core'
|
|
@@ -31,15 +33,113 @@ import {
|
|
|
31
33
|
NoInfer,
|
|
32
34
|
ToOptions,
|
|
33
35
|
invariant,
|
|
36
|
+
Router,
|
|
34
37
|
} from '@tanstack/router-core'
|
|
35
38
|
|
|
36
39
|
export * from '@tanstack/router-core'
|
|
37
40
|
|
|
41
|
+
export type SyncRouteComponent<TProps = {}> = (
|
|
42
|
+
props: TProps,
|
|
43
|
+
) => JSX.Element | React.ReactNode
|
|
44
|
+
|
|
45
|
+
export type RouteComponent<TProps = {}> = SyncRouteComponent<TProps> & {
|
|
46
|
+
preload?: () => Promise<SyncRouteComponent<TProps>>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function lazy(
|
|
50
|
+
importer: () => Promise<{ default: SyncRouteComponent }>,
|
|
51
|
+
): RouteComponent {
|
|
52
|
+
const lazyComp = React.lazy(importer as any)
|
|
53
|
+
let promise: Promise<SyncRouteComponent>
|
|
54
|
+
let resolvedComp: SyncRouteComponent
|
|
55
|
+
|
|
56
|
+
const forwardedComp = React.forwardRef((props, ref) => {
|
|
57
|
+
const resolvedCompRef = React.useRef(resolvedComp || lazyComp)
|
|
58
|
+
return React.createElement(
|
|
59
|
+
resolvedCompRef.current as any,
|
|
60
|
+
{ ...(ref ? { ref } : {}), ...props } as any,
|
|
61
|
+
)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const finalComp = forwardedComp as unknown as RouteComponent
|
|
65
|
+
|
|
66
|
+
finalComp.preload = () => {
|
|
67
|
+
if (!promise) {
|
|
68
|
+
promise = importer().then((module) => {
|
|
69
|
+
resolvedComp = module.default
|
|
70
|
+
return resolvedComp
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return promise
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return finalComp
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
type LinkPropsOptions<
|
|
81
|
+
TAllRouteInfo extends AnyAllRouteInfo,
|
|
82
|
+
TFrom extends ValidFromPath<TAllRouteInfo>,
|
|
83
|
+
TTo extends string,
|
|
84
|
+
> = LinkOptions<TAllRouteInfo, TFrom, TTo> & {
|
|
85
|
+
// 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)
|
|
86
|
+
activeProps?:
|
|
87
|
+
| React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
88
|
+
| (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
|
|
89
|
+
// 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)
|
|
90
|
+
inactiveProps?:
|
|
91
|
+
| React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
92
|
+
| (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
type MakeMatchRouteOptions<
|
|
96
|
+
TAllRouteInfo extends AnyAllRouteInfo,
|
|
97
|
+
TFrom extends ValidFromPath<TAllRouteInfo>,
|
|
98
|
+
TTo extends string,
|
|
99
|
+
> = ToOptions<TAllRouteInfo, TFrom, TTo> &
|
|
100
|
+
MatchRouteOptions & {
|
|
101
|
+
// 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
|
|
102
|
+
children?:
|
|
103
|
+
| React.ReactNode
|
|
104
|
+
| ((
|
|
105
|
+
params: RouteInfoByPath<
|
|
106
|
+
TAllRouteInfo,
|
|
107
|
+
ResolveRelativePath<TFrom, NoInfer<TTo>>
|
|
108
|
+
>['allParams'],
|
|
109
|
+
) => React.ReactNode)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
type MakeLinkPropsOptions<
|
|
113
|
+
TAllRouteInfo extends AnyAllRouteInfo,
|
|
114
|
+
TFrom extends ValidFromPath<TAllRouteInfo>,
|
|
115
|
+
TTo extends string,
|
|
116
|
+
> = LinkPropsOptions<TAllRouteInfo, TFrom, TTo> &
|
|
117
|
+
React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
118
|
+
|
|
119
|
+
type MakeLinkOptions<
|
|
120
|
+
TAllRouteInfo extends AnyAllRouteInfo,
|
|
121
|
+
TFrom extends ValidFromPath<TAllRouteInfo>,
|
|
122
|
+
TTo extends string,
|
|
123
|
+
> = LinkPropsOptions<TAllRouteInfo, TFrom, TTo> &
|
|
124
|
+
React.AnchorHTMLAttributes<HTMLAnchorElement> &
|
|
125
|
+
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
|
|
126
|
+
// 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
|
|
127
|
+
children?:
|
|
128
|
+
| React.ReactNode
|
|
129
|
+
| ((state: { isActive: boolean }) => React.ReactNode)
|
|
130
|
+
}
|
|
131
|
+
|
|
38
132
|
declare module '@tanstack/router-core' {
|
|
39
133
|
interface FrameworkGenerics {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
134
|
+
Component: RouteComponent
|
|
135
|
+
ErrorComponent: RouteComponent<{
|
|
136
|
+
error: unknown
|
|
137
|
+
info: { componentStack: string }
|
|
138
|
+
}>
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
interface RouterOptions<TRouteConfig, TRouterContext> {
|
|
142
|
+
// ssrFooter?: () => JSX.Element | React.ReactNode
|
|
43
143
|
}
|
|
44
144
|
|
|
45
145
|
interface Router<
|
|
@@ -50,36 +150,25 @@ declare module '@tanstack/router-core' {
|
|
|
50
150
|
useRoute: <TId extends keyof TAllRouteInfo['routeInfoById']>(
|
|
51
151
|
routeId: TId,
|
|
52
152
|
) => Route<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
|
|
53
|
-
useMatch: <
|
|
153
|
+
useMatch: <
|
|
154
|
+
TId extends keyof TAllRouteInfo['routeInfoById'],
|
|
155
|
+
TStrict extends boolean = true,
|
|
156
|
+
>(
|
|
54
157
|
routeId: TId,
|
|
55
|
-
|
|
158
|
+
opts?: { strict?: TStrict },
|
|
159
|
+
) => TStrict extends true
|
|
160
|
+
? RouteMatch<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
|
|
161
|
+
:
|
|
162
|
+
| RouteMatch<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
|
|
163
|
+
| undefined
|
|
56
164
|
linkProps: <TTo extends string = '.'>(
|
|
57
|
-
props:
|
|
58
|
-
React.AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
165
|
+
props: MakeLinkPropsOptions<TAllRouteInfo, '/', TTo>,
|
|
59
166
|
) => React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
60
167
|
Link: <TTo extends string = '.'>(
|
|
61
|
-
props:
|
|
62
|
-
React.AnchorHTMLAttributes<HTMLAnchorElement> &
|
|
63
|
-
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
|
|
64
|
-
// 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
|
|
65
|
-
children?:
|
|
66
|
-
| React.ReactNode
|
|
67
|
-
| ((state: { isActive: boolean }) => React.ReactNode)
|
|
68
|
-
},
|
|
168
|
+
props: MakeLinkOptions<TAllRouteInfo, '/', TTo>,
|
|
69
169
|
) => JSX.Element
|
|
70
170
|
MatchRoute: <TTo extends string = '.'>(
|
|
71
|
-
props:
|
|
72
|
-
MatchRouteOptions & {
|
|
73
|
-
// 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
|
|
74
|
-
children?:
|
|
75
|
-
| React.ReactNode
|
|
76
|
-
| ((
|
|
77
|
-
params: RouteInfoByPath<
|
|
78
|
-
TAllRouteInfo,
|
|
79
|
-
ResolveRelativePath<'/', NoInfer<TTo>>
|
|
80
|
-
>['allParams'],
|
|
81
|
-
) => React.ReactNode)
|
|
82
|
-
},
|
|
171
|
+
props: MakeMatchRouteOptions<TAllRouteInfo, '/', TTo>,
|
|
83
172
|
) => JSX.Element
|
|
84
173
|
}
|
|
85
174
|
|
|
@@ -99,53 +188,20 @@ declare module '@tanstack/router-core' {
|
|
|
99
188
|
TResolved,
|
|
100
189
|
ToIdOption<TAllRouteInfo, TRouteInfo['id'], TTo>
|
|
101
190
|
>,
|
|
191
|
+
opts?: { strict?: boolean },
|
|
102
192
|
) => Route<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TResolved]>
|
|
103
193
|
linkProps: <TTo extends string = '.'>(
|
|
104
|
-
props:
|
|
105
|
-
React.AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
194
|
+
props: MakeLinkPropsOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo>,
|
|
106
195
|
) => React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
107
196
|
Link: <TTo extends string = '.'>(
|
|
108
|
-
props:
|
|
109
|
-
React.AnchorHTMLAttributes<HTMLAnchorElement> &
|
|
110
|
-
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
|
|
111
|
-
// 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
|
|
112
|
-
children?:
|
|
113
|
-
| React.ReactNode
|
|
114
|
-
| ((state: { isActive: boolean }) => React.ReactNode)
|
|
115
|
-
},
|
|
197
|
+
props: MakeLinkOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo>,
|
|
116
198
|
) => JSX.Element
|
|
117
199
|
MatchRoute: <TTo extends string = '.'>(
|
|
118
|
-
props:
|
|
119
|
-
MatchRouteOptions & {
|
|
120
|
-
// 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
|
|
121
|
-
children?:
|
|
122
|
-
| React.ReactNode
|
|
123
|
-
| ((
|
|
124
|
-
params: RouteInfoByPath<
|
|
125
|
-
TAllRouteInfo,
|
|
126
|
-
ResolveRelativePath<TRouteInfo['fullPath'], NoInfer<TTo>>
|
|
127
|
-
>['allParams'],
|
|
128
|
-
) => React.ReactNode)
|
|
129
|
-
},
|
|
200
|
+
props: MakeMatchRouteOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo>,
|
|
130
201
|
) => JSX.Element
|
|
131
202
|
}
|
|
132
203
|
}
|
|
133
204
|
|
|
134
|
-
type LinkPropsOptions<
|
|
135
|
-
TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
|
|
136
|
-
TFrom extends ValidFromPath<TAllRouteInfo> = '/',
|
|
137
|
-
TTo extends string = '.',
|
|
138
|
-
> = LinkOptions<TAllRouteInfo, TFrom, TTo> & {
|
|
139
|
-
// 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)
|
|
140
|
-
activeProps?:
|
|
141
|
-
| React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
142
|
-
| (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
|
|
143
|
-
// 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)
|
|
144
|
-
inactiveProps?:
|
|
145
|
-
| React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
146
|
-
| (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
205
|
export type PromptProps = {
|
|
150
206
|
message: string
|
|
151
207
|
when?: boolean | any
|
|
@@ -154,20 +210,22 @@ export type PromptProps = {
|
|
|
154
210
|
|
|
155
211
|
//
|
|
156
212
|
|
|
157
|
-
|
|
158
|
-
|
|
213
|
+
export function Link<TTo extends string = '.'>(
|
|
214
|
+
props: MakeLinkOptions<RegisteredAllRouteInfo, '/', TTo>,
|
|
215
|
+
): JSX.Element {
|
|
216
|
+
const router = useRouter()
|
|
217
|
+
return <router.Link {...(props as any)} />
|
|
218
|
+
}
|
|
159
219
|
|
|
160
|
-
|
|
161
|
-
const isDOM = Boolean(
|
|
162
|
-
typeof window !== 'undefined' &&
|
|
163
|
-
window.document &&
|
|
164
|
-
window.document.createElement,
|
|
165
|
-
)
|
|
220
|
+
type MatchesContextValue = RouteMatch[]
|
|
166
221
|
|
|
167
|
-
const
|
|
222
|
+
export const matchesContext = React.createContext<MatchesContextValue>(null!)
|
|
223
|
+
export const routerContext = React.createContext<{ router: RegisteredRouter }>(
|
|
224
|
+
null!,
|
|
225
|
+
)
|
|
168
226
|
|
|
169
227
|
export type MatchesProviderProps = {
|
|
170
|
-
value:
|
|
228
|
+
value: MatchesContextValue
|
|
171
229
|
children: React.ReactNode
|
|
172
230
|
}
|
|
173
231
|
|
|
@@ -175,7 +233,7 @@ export function MatchesProvider(props: MatchesProviderProps) {
|
|
|
175
233
|
return <matchesContext.Provider {...props} />
|
|
176
234
|
}
|
|
177
235
|
|
|
178
|
-
const useRouterSubscription = (router: Router<any, any>) => {
|
|
236
|
+
const useRouterSubscription = (router: Router<any, any, any>) => {
|
|
179
237
|
useSyncExternalStore(
|
|
180
238
|
(cb) => router.subscribe(() => cb()),
|
|
181
239
|
() => router.state,
|
|
@@ -185,10 +243,14 @@ const useRouterSubscription = (router: Router<any, any>) => {
|
|
|
185
243
|
|
|
186
244
|
export function createReactRouter<
|
|
187
245
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
188
|
-
|
|
246
|
+
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
247
|
+
TRouterContext = unknown,
|
|
248
|
+
>(
|
|
249
|
+
opts: RouterOptions<TRouteConfig, TRouterContext>,
|
|
250
|
+
): Router<TRouteConfig, TAllRouteInfo, TRouterContext> {
|
|
189
251
|
const makeRouteExt = (
|
|
190
252
|
route: AnyRoute,
|
|
191
|
-
router: Router<any, any>,
|
|
253
|
+
router: Router<any, any, any>,
|
|
192
254
|
): Pick<AnyRoute, 'useRoute' | 'linkProps' | 'Link' | 'MatchRoute'> => {
|
|
193
255
|
return {
|
|
194
256
|
useRoute: (subRouteId = '.' as any) => {
|
|
@@ -237,7 +299,7 @@ export function createReactRouter<
|
|
|
237
299
|
...rest
|
|
238
300
|
} = options
|
|
239
301
|
|
|
240
|
-
const linkInfo = route.buildLink(options)
|
|
302
|
+
const linkInfo = route.buildLink(options as any)
|
|
241
303
|
|
|
242
304
|
if (linkInfo.type === 'external') {
|
|
243
305
|
const { href } = linkInfo
|
|
@@ -253,11 +315,21 @@ export function createReactRouter<
|
|
|
253
315
|
next,
|
|
254
316
|
} = linkInfo
|
|
255
317
|
|
|
318
|
+
const reactHandleClick = (e: Event) => {
|
|
319
|
+
if (React.startTransition)
|
|
320
|
+
// This is a hack for react < 18
|
|
321
|
+
React.startTransition(() => {
|
|
322
|
+
handleClick(e)
|
|
323
|
+
})
|
|
324
|
+
else handleClick(e)
|
|
325
|
+
}
|
|
326
|
+
|
|
256
327
|
const composeHandlers =
|
|
257
328
|
(handlers: (undefined | ((e: any) => void))[]) =>
|
|
258
329
|
(e: React.SyntheticEvent) => {
|
|
259
|
-
e.persist()
|
|
330
|
+
if (e.persist) e.persist()
|
|
260
331
|
handlers.forEach((handler) => {
|
|
332
|
+
if (e.defaultPrevented) return
|
|
261
333
|
if (handler) handler(e)
|
|
262
334
|
})
|
|
263
335
|
}
|
|
@@ -275,7 +347,7 @@ export function createReactRouter<
|
|
|
275
347
|
...resolvedInactiveProps,
|
|
276
348
|
...rest,
|
|
277
349
|
href: disabled ? undefined : next.href,
|
|
278
|
-
onClick: composeHandlers([
|
|
350
|
+
onClick: composeHandlers([reactHandleClick, onClick]),
|
|
279
351
|
onFocus: composeHandlers([handleFocus, onFocus]),
|
|
280
352
|
onMouseEnter: composeHandlers([handleEnter, onMouseEnter]),
|
|
281
353
|
onMouseLeave: composeHandlers([handleLeave, onMouseLeave]),
|
|
@@ -344,12 +416,12 @@ export function createReactRouter<
|
|
|
344
416
|
const coreRouter = createRouter<TRouteConfig>({
|
|
345
417
|
...opts,
|
|
346
418
|
createRouter: (router) => {
|
|
347
|
-
const routerExt: Pick<Router<any, any>, 'useMatch' | 'useState'> = {
|
|
419
|
+
const routerExt: Pick<Router<any, any, any>, 'useMatch' | 'useState'> = {
|
|
348
420
|
useState: () => {
|
|
349
421
|
useRouterSubscription(router)
|
|
350
422
|
return router.state
|
|
351
423
|
},
|
|
352
|
-
useMatch: (routeId) => {
|
|
424
|
+
useMatch: (routeId, opts) => {
|
|
353
425
|
useRouterSubscription(router)
|
|
354
426
|
|
|
355
427
|
invariant(
|
|
@@ -357,36 +429,34 @@ export function createReactRouter<
|
|
|
357
429
|
`"${rootRouteId}" cannot be used with useMatch! Did you mean to useRoute("${rootRouteId}")?`,
|
|
358
430
|
)
|
|
359
431
|
|
|
360
|
-
const
|
|
432
|
+
const nearestMatch = useNearestMatch()
|
|
361
433
|
const match = router.state.matches.find((d) => d.routeId === routeId)
|
|
362
434
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
routeId as string
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
if (!match) {
|
|
382
|
-
invariant('Match not found!')
|
|
435
|
+
if (opts?.strict ?? true) {
|
|
436
|
+
invariant(
|
|
437
|
+
match,
|
|
438
|
+
`Could not find an active match for "${routeId as string}"!`,
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
invariant(
|
|
442
|
+
nearestMatch.routeId == match?.routeId,
|
|
443
|
+
`useMatch("${
|
|
444
|
+
match?.routeId as string
|
|
445
|
+
}") is being called in a component that is meant to render the '${
|
|
446
|
+
nearestMatch.routeId
|
|
447
|
+
}' route. Did you mean to 'useMatch("${
|
|
448
|
+
match?.routeId as string
|
|
449
|
+
}", { strict: false })' or 'useRoute("${
|
|
450
|
+
match?.routeId as string
|
|
451
|
+
}")' instead?`,
|
|
452
|
+
)
|
|
383
453
|
}
|
|
384
454
|
|
|
385
|
-
return match
|
|
455
|
+
return match as any
|
|
386
456
|
},
|
|
387
457
|
}
|
|
388
458
|
|
|
389
|
-
const routeExt = makeRouteExt(router.getRoute(
|
|
459
|
+
const routeExt = makeRouteExt(router.getRoute(rootRouteId), router)
|
|
390
460
|
|
|
391
461
|
Object.assign(router, routerExt, routeExt)
|
|
392
462
|
},
|
|
@@ -395,19 +465,13 @@ export function createReactRouter<
|
|
|
395
465
|
|
|
396
466
|
Object.assign(route, routeExt)
|
|
397
467
|
},
|
|
398
|
-
|
|
399
|
-
if (typeof
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
// Support direct import() calls
|
|
403
|
-
if (typeof res === 'object' && res.default) {
|
|
404
|
-
return React.createElement(res.default)
|
|
405
|
-
} else {
|
|
406
|
-
return res
|
|
407
|
-
}
|
|
468
|
+
loadComponent: async (component) => {
|
|
469
|
+
if (component.preload && typeof document !== 'undefined') {
|
|
470
|
+
component.preload()
|
|
471
|
+
// return await component.preload()
|
|
408
472
|
}
|
|
409
473
|
|
|
410
|
-
return
|
|
474
|
+
return component as any
|
|
411
475
|
},
|
|
412
476
|
})
|
|
413
477
|
|
|
@@ -417,125 +481,184 @@ export function createReactRouter<
|
|
|
417
481
|
export type RouterProps<
|
|
418
482
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
419
483
|
TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
children?: React.ReactNode
|
|
484
|
+
TRouterContext = unknown,
|
|
485
|
+
> = RouterOptions<TRouteConfig, TRouterContext> & {
|
|
486
|
+
router: Router<TRouteConfig, TAllRouteInfo, TRouterContext>
|
|
424
487
|
}
|
|
425
488
|
|
|
426
489
|
export function RouterProvider<
|
|
427
490
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
428
491
|
TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
|
|
429
|
-
|
|
492
|
+
TRouterContext = unknown,
|
|
493
|
+
>({
|
|
494
|
+
router,
|
|
495
|
+
...rest
|
|
496
|
+
}: RouterProps<TRouteConfig, TAllRouteInfo, TRouterContext>) {
|
|
430
497
|
router.update(rest)
|
|
431
498
|
|
|
432
499
|
useRouterSubscription(router)
|
|
433
|
-
|
|
434
|
-
useLayoutEffect(() => {
|
|
500
|
+
React.useEffect(() => {
|
|
435
501
|
return router.mount()
|
|
436
502
|
}, [router])
|
|
437
503
|
|
|
438
504
|
return (
|
|
439
|
-
|
|
440
|
-
<
|
|
441
|
-
{
|
|
442
|
-
|
|
443
|
-
|
|
505
|
+
<>
|
|
506
|
+
<routerContext.Provider value={{ router: router as any }}>
|
|
507
|
+
<MatchesProvider value={[undefined!, ...router.state.matches]}>
|
|
508
|
+
<Outlet />
|
|
509
|
+
</MatchesProvider>
|
|
510
|
+
</routerContext.Provider>
|
|
511
|
+
</>
|
|
444
512
|
)
|
|
445
513
|
}
|
|
446
514
|
|
|
447
|
-
function useRouter():
|
|
515
|
+
export function useRouter(): RegisteredRouter {
|
|
448
516
|
const value = React.useContext(routerContext)
|
|
449
517
|
warning(!value, 'useRouter must be used inside a <Router> component!')
|
|
450
518
|
|
|
451
519
|
useRouterSubscription(value.router)
|
|
452
520
|
|
|
453
|
-
return value.router
|
|
521
|
+
return value.router
|
|
454
522
|
}
|
|
455
523
|
|
|
456
|
-
function useMatches(): RouteMatch[] {
|
|
524
|
+
export function useMatches(): RouteMatch[] {
|
|
457
525
|
return React.useContext(matchesContext)
|
|
458
526
|
}
|
|
459
527
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
528
|
+
export function useMatch<
|
|
529
|
+
TId extends keyof RegisteredAllRouteInfo['routeInfoById'],
|
|
530
|
+
TStrict extends boolean = true,
|
|
531
|
+
>(
|
|
532
|
+
routeId: TId,
|
|
533
|
+
opts?: { strict?: TStrict },
|
|
534
|
+
): TStrict extends true
|
|
535
|
+
? RouteMatch<
|
|
536
|
+
RegisteredAllRouteInfo,
|
|
537
|
+
RegisteredAllRouteInfo['routeInfoById'][TId]
|
|
538
|
+
>
|
|
539
|
+
:
|
|
540
|
+
| RouteMatch<
|
|
541
|
+
RegisteredAllRouteInfo,
|
|
542
|
+
RegisteredAllRouteInfo['routeInfoById'][TId]
|
|
543
|
+
>
|
|
544
|
+
| undefined {
|
|
475
545
|
const router = useRouter()
|
|
476
|
-
|
|
546
|
+
return router.useMatch(routeId as any, opts) as any
|
|
547
|
+
}
|
|
477
548
|
|
|
478
|
-
|
|
549
|
+
export function useNearestMatch(): RouteMatch<
|
|
550
|
+
RegisteredAllRouteInfo,
|
|
551
|
+
RouteInfo
|
|
552
|
+
> {
|
|
553
|
+
const runtimeMatch = useMatches()[0]
|
|
479
554
|
|
|
480
|
-
|
|
555
|
+
invariant(runtimeMatch, `Could not find a nearest match!`)
|
|
481
556
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
return null
|
|
485
|
-
}
|
|
557
|
+
return runtimeMatch as any
|
|
558
|
+
}
|
|
486
559
|
|
|
487
|
-
|
|
488
|
-
|
|
560
|
+
export function useRoute<
|
|
561
|
+
TId extends keyof RegisteredAllRouteInfo['routeInfoById'],
|
|
562
|
+
>(
|
|
563
|
+
routeId: TId,
|
|
564
|
+
): Route<RegisteredAllRouteInfo, RegisteredAllRouteInfo['routeInfoById'][TId]> {
|
|
565
|
+
const router = useRouter()
|
|
566
|
+
return router.useRoute(routeId as any) as any
|
|
567
|
+
}
|
|
489
568
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
569
|
+
export function useSearch<
|
|
570
|
+
TId extends keyof RegisteredAllRouteInfo['routeInfoById'] = keyof RegisteredAllRouteInfo['routeInfoById'],
|
|
571
|
+
>(_routeId?: TId): RegisteredAllRouteInfo['fullSearchSchema'] {
|
|
572
|
+
return useRouter().state.location.search
|
|
573
|
+
}
|
|
494
574
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
575
|
+
export function linkProps<TTo extends string = '.'>(
|
|
576
|
+
props: MakeLinkPropsOptions<RegisteredAllRouteInfo, '/', TTo>,
|
|
577
|
+
): React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
578
|
+
const router = useRouter()
|
|
579
|
+
return router.linkProps(props as any)
|
|
580
|
+
}
|
|
501
581
|
|
|
502
|
-
|
|
503
|
-
|
|
582
|
+
export function MatchRoute<TTo extends string = '.'>(
|
|
583
|
+
props: MakeMatchRouteOptions<RegisteredAllRouteInfo, '/', TTo>,
|
|
584
|
+
): JSX.Element {
|
|
585
|
+
const router = useRouter()
|
|
586
|
+
return React.createElement(router.MatchRoute, props as any)
|
|
587
|
+
}
|
|
504
588
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
589
|
+
export function Outlet() {
|
|
590
|
+
const router = useRouter()
|
|
591
|
+
const matches = useMatches().slice(1)
|
|
592
|
+
const match = matches[0]
|
|
509
593
|
|
|
510
|
-
|
|
511
|
-
return (pendingElement as any) ?? null
|
|
512
|
-
}
|
|
513
|
-
}
|
|
594
|
+
const defaultPending = React.useCallback(() => null, [])
|
|
514
595
|
|
|
515
|
-
|
|
516
|
-
|
|
596
|
+
if (!match) {
|
|
597
|
+
return null
|
|
598
|
+
}
|
|
517
599
|
|
|
518
|
-
|
|
519
|
-
|
|
600
|
+
const PendingComponent = (match.__.pendingComponent ??
|
|
601
|
+
router.options.defaultPendingComponent ??
|
|
602
|
+
defaultPending) as any
|
|
520
603
|
|
|
521
|
-
const
|
|
522
|
-
|
|
604
|
+
const errorComponent =
|
|
605
|
+
match.__.errorComponent ?? router.options.defaultErrorComponent
|
|
523
606
|
|
|
524
607
|
return (
|
|
525
|
-
<MatchesProvider value={matches}
|
|
526
|
-
<
|
|
608
|
+
<MatchesProvider value={matches}>
|
|
609
|
+
<React.Suspense fallback={<PendingComponent />}>
|
|
610
|
+
<CatchBoundary
|
|
611
|
+
key={match.routeId}
|
|
612
|
+
errorComponent={errorComponent}
|
|
613
|
+
match={match as any}
|
|
614
|
+
>
|
|
615
|
+
{
|
|
616
|
+
((): React.ReactNode => {
|
|
617
|
+
if (match.status === 'error') {
|
|
618
|
+
throw match.error
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (match.status === 'success') {
|
|
622
|
+
return React.createElement(
|
|
623
|
+
(match.__.component as any) ??
|
|
624
|
+
router.options.defaultComponent ??
|
|
625
|
+
Outlet,
|
|
626
|
+
)
|
|
627
|
+
}
|
|
628
|
+
throw match.__.loadPromise
|
|
629
|
+
})() as JSX.Element
|
|
630
|
+
}
|
|
631
|
+
</CatchBoundary>
|
|
632
|
+
</React.Suspense>
|
|
633
|
+
{/* Provide a suffix suspense boundary to make sure the router is
|
|
634
|
+
ready to be dehydrated on the server */}
|
|
635
|
+
{/* {router.options.ssrFooter && match.matchId === rootRouteId ? (
|
|
636
|
+
<React.Suspense fallback={null}>
|
|
637
|
+
{(() => {
|
|
638
|
+
if (router.state.pending) {
|
|
639
|
+
throw router.navigationPromise
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return router.options.ssrFooter()
|
|
643
|
+
})()}
|
|
644
|
+
</React.Suspense>
|
|
645
|
+
) : null} */}
|
|
527
646
|
</MatchesProvider>
|
|
528
647
|
)
|
|
529
648
|
}
|
|
530
649
|
|
|
531
650
|
class CatchBoundary extends React.Component<{
|
|
532
651
|
children: any
|
|
533
|
-
|
|
652
|
+
errorComponent: any
|
|
653
|
+
match: RouteMatch
|
|
534
654
|
}> {
|
|
535
655
|
state = {
|
|
536
656
|
error: false,
|
|
657
|
+
info: undefined,
|
|
537
658
|
}
|
|
659
|
+
|
|
538
660
|
componentDidCatch(error: any, info: any) {
|
|
661
|
+
console.error(`Error in route match: ${this.props.match.matchId}`)
|
|
539
662
|
console.error(error)
|
|
540
663
|
|
|
541
664
|
this.setState({
|
|
@@ -543,23 +666,59 @@ class CatchBoundary extends React.Component<{
|
|
|
543
666
|
info,
|
|
544
667
|
})
|
|
545
668
|
}
|
|
546
|
-
|
|
547
|
-
this.setState({
|
|
548
|
-
error: false,
|
|
549
|
-
info: false,
|
|
550
|
-
})
|
|
551
|
-
}
|
|
669
|
+
|
|
552
670
|
render() {
|
|
553
|
-
|
|
671
|
+
return (
|
|
672
|
+
<CatchBoundaryInner
|
|
673
|
+
{...this.props}
|
|
674
|
+
errorState={this.state}
|
|
675
|
+
reset={() => this.setState({})}
|
|
676
|
+
/>
|
|
677
|
+
)
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// This is the messiest thing ever... I'm either seriously tired (likely) or
|
|
682
|
+
// there has to be a better way to reset error boundaries when the
|
|
683
|
+
// router's location key changes.
|
|
684
|
+
function CatchBoundaryInner(props: {
|
|
685
|
+
children: any
|
|
686
|
+
errorComponent: any
|
|
687
|
+
errorState: { error: unknown; info: any }
|
|
688
|
+
reset: () => void
|
|
689
|
+
}) {
|
|
690
|
+
const [activeErrorState, setActiveErrorState] = React.useState(
|
|
691
|
+
props.errorState,
|
|
692
|
+
)
|
|
693
|
+
const router = useRouter()
|
|
694
|
+
const errorComponent = props.errorComponent ?? DefaultErrorBoundary
|
|
695
|
+
|
|
696
|
+
React.useEffect(() => {
|
|
697
|
+
if (activeErrorState) {
|
|
698
|
+
let prevKey = router.state.location.key
|
|
699
|
+
return router.subscribe(() => {
|
|
700
|
+
if (router.state.location.key !== prevKey) {
|
|
701
|
+
prevKey = router.state.location.key
|
|
702
|
+
setActiveErrorState({} as any)
|
|
703
|
+
}
|
|
704
|
+
})
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
return
|
|
708
|
+
}, [activeErrorState])
|
|
554
709
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
: catchElement
|
|
710
|
+
React.useEffect(() => {
|
|
711
|
+
if (props.errorState.error) {
|
|
712
|
+
setActiveErrorState(props.errorState)
|
|
559
713
|
}
|
|
714
|
+
props.reset()
|
|
715
|
+
}, [props.errorState.error])
|
|
560
716
|
|
|
561
|
-
|
|
717
|
+
if (activeErrorState.error) {
|
|
718
|
+
return React.createElement(errorComponent, activeErrorState)
|
|
562
719
|
}
|
|
720
|
+
|
|
721
|
+
return props.children
|
|
563
722
|
}
|
|
564
723
|
|
|
565
724
|
export function DefaultErrorBoundary({ error }: { error: any }) {
|
|
@@ -584,19 +743,6 @@ export function DefaultErrorBoundary({ error }: { error: any }) {
|
|
|
584
743
|
) : null}
|
|
585
744
|
</pre>
|
|
586
745
|
</div>
|
|
587
|
-
<div style={{ height: '1rem' }} />
|
|
588
|
-
<div
|
|
589
|
-
style={{
|
|
590
|
-
fontSize: '.8em',
|
|
591
|
-
borderLeft: '3px solid rgba(127, 127, 127, 1)',
|
|
592
|
-
paddingLeft: '.5rem',
|
|
593
|
-
opacity: 0.5,
|
|
594
|
-
}}
|
|
595
|
-
>
|
|
596
|
-
If you are the owner of this website, it's highly recommended that you
|
|
597
|
-
configure your own custom Catch/Error boundaries for the router. You can
|
|
598
|
-
optionally configure a boundary for each route.
|
|
599
|
-
</div>
|
|
600
746
|
</div>
|
|
601
747
|
)
|
|
602
748
|
}
|
|
@@ -612,7 +758,7 @@ export function usePrompt(message: string, when: boolean | any): void {
|
|
|
612
758
|
unblock()
|
|
613
759
|
transition.retry()
|
|
614
760
|
} else {
|
|
615
|
-
router.location.pathname = window.location.pathname
|
|
761
|
+
router.state.location.pathname = window.location.pathname
|
|
616
762
|
}
|
|
617
763
|
})
|
|
618
764
|
|