@tanstack/react-router 0.0.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +49 -0
- package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
- package/build/cjs/index.js +364 -0
- package/build/cjs/index.js.map +1 -0
- package/build/cjs/location-core/build/esm/index.js +2214 -0
- package/build/cjs/location-core/build/esm/index.js.map +1 -0
- package/build/cjs/react-location/src/index.js +311 -0
- package/build/cjs/react-location/src/index.js.map +1 -0
- package/build/cjs/react-router/src/index.js +413 -0
- package/build/cjs/react-router/src/index.js.map +1 -0
- package/build/cjs/router-core/build/esm/index.js +2330 -0
- package/build/cjs/router-core/build/esm/index.js.map +1 -0
- package/build/cjs/src/index.js +335 -0
- package/build/cjs/src/index.js.map +1 -0
- package/build/esm/index.js +2686 -0
- package/build/esm/index.js.map +1 -0
- package/build/stats-html.html +4034 -0
- package/build/stats-react.json +129 -0
- package/build/types/createRoutes.test.d.ts +0 -0
- package/build/types/index.d.ts +68 -0
- package/build/types/qss.d.ts +2 -0
- package/build/umd/index.development.js +2737 -0
- package/build/umd/index.development.js.map +1 -0
- package/build/umd/index.production.js +22 -0
- package/build/umd/index.production.js.map +1 -0
- package/package.json +50 -0
- package/src/createRoutes.test.tsx +310 -0
- package/src/index.tsx +568 -0
- package/src/qss.ts +53 -0
package/src/index.tsx
ADDED
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
import { useSyncExternalStore } from 'use-sync-external-store/shim'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
AnyRoute,
|
|
7
|
+
RootRouteId,
|
|
8
|
+
rootRouteId,
|
|
9
|
+
Route,
|
|
10
|
+
Router,
|
|
11
|
+
} from '@tanstack/router-core'
|
|
12
|
+
import {
|
|
13
|
+
warning,
|
|
14
|
+
RouterOptions,
|
|
15
|
+
RouteMatch,
|
|
16
|
+
MatchRouteOptions,
|
|
17
|
+
RouteConfig,
|
|
18
|
+
AnyRouteConfig,
|
|
19
|
+
AnyAllRouteInfo,
|
|
20
|
+
DefaultAllRouteInfo,
|
|
21
|
+
functionalUpdate,
|
|
22
|
+
createRouter,
|
|
23
|
+
AnyRouteInfo,
|
|
24
|
+
AllRouteInfo,
|
|
25
|
+
RouteInfo,
|
|
26
|
+
ValidFromPath,
|
|
27
|
+
LinkOptions,
|
|
28
|
+
RouteInfoByPath,
|
|
29
|
+
ResolveRelativePath,
|
|
30
|
+
NoInfer,
|
|
31
|
+
ToOptions,
|
|
32
|
+
} from '@tanstack/router-core'
|
|
33
|
+
|
|
34
|
+
export * from '@tanstack/router-core'
|
|
35
|
+
|
|
36
|
+
declare module '@tanstack/router-core' {
|
|
37
|
+
interface FrameworkGenerics {
|
|
38
|
+
Element: React.ReactNode
|
|
39
|
+
AsyncElement: (opts: {
|
|
40
|
+
params: Record<string, string>
|
|
41
|
+
}) => Promise<React.ReactNode>
|
|
42
|
+
SyncOrAsyncElement: React.ReactNode | FrameworkGenerics['AsyncElement']
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface Router<
|
|
46
|
+
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
47
|
+
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
48
|
+
> extends Pick<
|
|
49
|
+
Route<TAllRouteInfo, TAllRouteInfo['routeInfoById'][RootRouteId]>,
|
|
50
|
+
'linkProps' | 'Link' | 'MatchRoute'
|
|
51
|
+
> {
|
|
52
|
+
useRoute: <TId extends keyof TAllRouteInfo['routeInfoById']>(
|
|
53
|
+
routeId: TId,
|
|
54
|
+
) => Route<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
|
|
55
|
+
useMatch: <TId extends keyof TAllRouteInfo['routeInfoById']>(
|
|
56
|
+
routeId: TId,
|
|
57
|
+
) => RouteMatch<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
|
|
58
|
+
// linkProps: <TTo extends string = '.'>(
|
|
59
|
+
// props: LinkPropsOptions<TAllRouteInfo, '/', TTo> &
|
|
60
|
+
// React.AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
61
|
+
// ) => React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
62
|
+
// Link: <TTo extends string = '.'>(
|
|
63
|
+
// props: LinkPropsOptions<TAllRouteInfo, '/', TTo> &
|
|
64
|
+
// React.AnchorHTMLAttributes<HTMLAnchorElement> &
|
|
65
|
+
// Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
|
|
66
|
+
// // 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
|
|
67
|
+
// children?:
|
|
68
|
+
// | React.ReactNode
|
|
69
|
+
// | ((state: { isActive: boolean }) => React.ReactNode)
|
|
70
|
+
// },
|
|
71
|
+
// ) => JSX.Element
|
|
72
|
+
// MatchRoute: <TTo extends string = '.'>(
|
|
73
|
+
// props: ToOptions<TAllRouteInfo, '/', TTo> &
|
|
74
|
+
// MatchRouteOptions & {
|
|
75
|
+
// // 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
|
|
76
|
+
// children?:
|
|
77
|
+
// | React.ReactNode
|
|
78
|
+
// | ((
|
|
79
|
+
// params: RouteInfoByPath<
|
|
80
|
+
// TAllRouteInfo,
|
|
81
|
+
// ResolveRelativePath<'/', NoInfer<TTo>>
|
|
82
|
+
// >['allParams'],
|
|
83
|
+
// ) => React.ReactNode)
|
|
84
|
+
// },
|
|
85
|
+
// ) => JSX.Element
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
interface Route<
|
|
89
|
+
TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
|
|
90
|
+
TRouteInfo extends AnyRouteInfo = RouteInfo,
|
|
91
|
+
> {
|
|
92
|
+
linkProps: <TTo extends string = '.'>(
|
|
93
|
+
props: LinkPropsOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo> &
|
|
94
|
+
React.AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
95
|
+
) => React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
96
|
+
Link: <TTo extends string = '.'>(
|
|
97
|
+
props: LinkPropsOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo> &
|
|
98
|
+
React.AnchorHTMLAttributes<HTMLAnchorElement> &
|
|
99
|
+
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
|
|
100
|
+
// 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
|
|
101
|
+
children?:
|
|
102
|
+
| React.ReactNode
|
|
103
|
+
| ((state: { isActive: boolean }) => React.ReactNode)
|
|
104
|
+
},
|
|
105
|
+
) => JSX.Element
|
|
106
|
+
MatchRoute: <TTo extends string = '.'>(
|
|
107
|
+
props: ToOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo> &
|
|
108
|
+
MatchRouteOptions & {
|
|
109
|
+
// 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
|
|
110
|
+
children?:
|
|
111
|
+
| React.ReactNode
|
|
112
|
+
| ((
|
|
113
|
+
params: RouteInfoByPath<
|
|
114
|
+
TAllRouteInfo,
|
|
115
|
+
ResolveRelativePath<TRouteInfo['fullPath'], NoInfer<TTo>>
|
|
116
|
+
>['allParams'],
|
|
117
|
+
) => React.ReactNode)
|
|
118
|
+
},
|
|
119
|
+
) => JSX.Element
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
type LinkPropsOptions<
|
|
124
|
+
TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
|
|
125
|
+
TFrom extends ValidFromPath<TAllRouteInfo> = '/',
|
|
126
|
+
TTo extends string = '.',
|
|
127
|
+
> = LinkOptions<TAllRouteInfo, TFrom, TTo> & {
|
|
128
|
+
// 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)
|
|
129
|
+
activeProps?:
|
|
130
|
+
| React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
131
|
+
| (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
|
|
132
|
+
// 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)
|
|
133
|
+
inactiveProps?:
|
|
134
|
+
| React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
135
|
+
| (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export type PromptProps = {
|
|
139
|
+
message: string
|
|
140
|
+
when?: boolean | any
|
|
141
|
+
children?: React.ReactNode
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
//
|
|
145
|
+
|
|
146
|
+
const matchesContext = React.createContext<RouteMatch[]>(null!)
|
|
147
|
+
const routerContext = React.createContext<{ router: Router<any, any> }>(null!)
|
|
148
|
+
|
|
149
|
+
// Detect if we're in the DOM
|
|
150
|
+
const isDOM = Boolean(
|
|
151
|
+
typeof window !== 'undefined' &&
|
|
152
|
+
window.document &&
|
|
153
|
+
window.document.createElement,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
const useLayoutEffect = isDOM ? React.useLayoutEffect : React.useEffect
|
|
157
|
+
|
|
158
|
+
export type MatchesProviderProps = {
|
|
159
|
+
value: RouteMatch[]
|
|
160
|
+
children: React.ReactNode
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function MatchesProvider(props: MatchesProviderProps) {
|
|
164
|
+
return <matchesContext.Provider {...props} />
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const useRouterSubscription = (router: Router<any, any>) => {
|
|
168
|
+
useSyncExternalStore(
|
|
169
|
+
(cb) => router.subscribe(() => cb()),
|
|
170
|
+
() => router.state,
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function createReactRouter<
|
|
175
|
+
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
176
|
+
>(opts: RouterOptions<TRouteConfig>): Router<TRouteConfig> {
|
|
177
|
+
const coreRouter = createRouter<TRouteConfig>({
|
|
178
|
+
...opts,
|
|
179
|
+
createRouter: (router) => {
|
|
180
|
+
const routerExt: Pick<Router<any, any>, 'useRoute' | 'useMatch'> = {
|
|
181
|
+
useRoute: (routeId) => {
|
|
182
|
+
const route = router.getRoute(routeId)
|
|
183
|
+
useRouterSubscription(router)
|
|
184
|
+
if (!route) {
|
|
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
|
|
192
|
+
},
|
|
193
|
+
useMatch: (routeId) => {
|
|
194
|
+
if (routeId === rootRouteId) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
`"${rootRouteId}" cannot be used with useMatch! Did you mean to useRoute("${rootRouteId}")?`,
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
const runtimeMatch = useMatch()
|
|
200
|
+
const match = router.state.matches.find((d) => d.routeId === routeId)
|
|
201
|
+
|
|
202
|
+
if (!match) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
`Could not find a match for route "${
|
|
205
|
+
routeId as string
|
|
206
|
+
}" being rendered in this component!`,
|
|
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
|
+
}
|
|
221
|
+
|
|
222
|
+
useRouterSubscription(router)
|
|
223
|
+
|
|
224
|
+
if (!match) {
|
|
225
|
+
throw new Error('Match not found!')
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return match
|
|
229
|
+
},
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
Object.assign(router, routerExt)
|
|
233
|
+
},
|
|
234
|
+
createRoute: ({ router, route }) => {
|
|
235
|
+
const routeExt: Pick<AnyRoute, 'linkProps' | 'Link' | 'MatchRoute'> = {
|
|
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
|
+
}
|
|
273
|
+
|
|
274
|
+
const {
|
|
275
|
+
handleClick,
|
|
276
|
+
handleFocus,
|
|
277
|
+
handleEnter,
|
|
278
|
+
handleLeave,
|
|
279
|
+
isActive,
|
|
280
|
+
next,
|
|
281
|
+
} = linkInfo
|
|
282
|
+
|
|
283
|
+
const composeHandlers =
|
|
284
|
+
(handlers: (undefined | ((e: any) => void))[]) =>
|
|
285
|
+
(e: React.SyntheticEvent) => {
|
|
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
|
+
},
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
Object.assign(route, routeExt)
|
|
374
|
+
},
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
return coreRouter as any
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export type RouterProps<
|
|
381
|
+
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
382
|
+
TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
|
|
383
|
+
> = RouterOptions<TRouteConfig> & {
|
|
384
|
+
router: Router<TRouteConfig, TAllRouteInfo>
|
|
385
|
+
// Children will default to `<Outlet />` if not provided
|
|
386
|
+
children?: React.ReactNode
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export function RouterProvider<
|
|
390
|
+
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
391
|
+
TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
|
|
392
|
+
>({ children, router, ...rest }: RouterProps<TRouteConfig, TAllRouteInfo>) {
|
|
393
|
+
router.update(rest)
|
|
394
|
+
|
|
395
|
+
useSyncExternalStore(
|
|
396
|
+
(cb) => router.subscribe(() => cb()),
|
|
397
|
+
() => router.state,
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
useLayoutEffect(() => {
|
|
401
|
+
router.mount()
|
|
402
|
+
}, [])
|
|
403
|
+
|
|
404
|
+
return (
|
|
405
|
+
<routerContext.Provider value={{ router }}>
|
|
406
|
+
<MatchesProvider value={router.state.matches}>
|
|
407
|
+
{children ?? <Outlet />}
|
|
408
|
+
</MatchesProvider>
|
|
409
|
+
</routerContext.Provider>
|
|
410
|
+
)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
export function useRouter(): Router {
|
|
414
|
+
const value = React.useContext(routerContext)
|
|
415
|
+
warning(!value, 'useRouter must be used inside a <Router> component!')
|
|
416
|
+
|
|
417
|
+
useRouterSubscription(value.router)
|
|
418
|
+
|
|
419
|
+
return value.router as Router
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
export function useMatches(): RouteMatch[] {
|
|
423
|
+
return React.useContext(matchesContext)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export function useParentMatches(): RouteMatch[] {
|
|
427
|
+
const router = useRouter()
|
|
428
|
+
const match = useMatch()
|
|
429
|
+
const matches = router.state.matches
|
|
430
|
+
return matches.slice(
|
|
431
|
+
0,
|
|
432
|
+
matches.findIndex((d) => d.matchId === match.matchId) - 1,
|
|
433
|
+
)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export function useMatch<T>(): RouteMatch {
|
|
437
|
+
return useMatches()?.[0] as RouteMatch
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export function Outlet() {
|
|
441
|
+
const router = useRouter()
|
|
442
|
+
const [, ...matches] = useMatches()
|
|
443
|
+
|
|
444
|
+
const childMatch = matches[0]
|
|
445
|
+
|
|
446
|
+
if (!childMatch) return null
|
|
447
|
+
|
|
448
|
+
const element = (((): React.ReactNode => {
|
|
449
|
+
if (!childMatch) {
|
|
450
|
+
return null
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const errorElement =
|
|
454
|
+
childMatch.__.errorElement ?? router.options.defaultErrorElement
|
|
455
|
+
|
|
456
|
+
if (childMatch.status === 'error') {
|
|
457
|
+
if (errorElement) {
|
|
458
|
+
return errorElement as any
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (
|
|
462
|
+
childMatch.options.useErrorBoundary ||
|
|
463
|
+
router.options.useErrorBoundary
|
|
464
|
+
) {
|
|
465
|
+
throw childMatch.error
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return <DefaultCatchBoundary error={childMatch.error} />
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (childMatch.status === 'loading' || childMatch.status === 'idle') {
|
|
472
|
+
if (childMatch.isPending) {
|
|
473
|
+
const pendingElement =
|
|
474
|
+
childMatch.__.pendingElement ?? router.options.defaultPendingElement
|
|
475
|
+
|
|
476
|
+
if (childMatch.options.pendingMs || pendingElement) {
|
|
477
|
+
return (pendingElement as any) ?? null
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return null
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return (childMatch.__.element as any) ?? router.options.defaultElement
|
|
485
|
+
})() as JSX.Element) ?? <Outlet />
|
|
486
|
+
|
|
487
|
+
const catchElement =
|
|
488
|
+
childMatch?.options.catchElement ?? router.options.defaultCatchElement
|
|
489
|
+
|
|
490
|
+
return (
|
|
491
|
+
<MatchesProvider value={matches} key={childMatch.matchId}>
|
|
492
|
+
<CatchBoundary catchElement={catchElement}>{element}</CatchBoundary>
|
|
493
|
+
</MatchesProvider>
|
|
494
|
+
)
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
class CatchBoundary extends React.Component<{
|
|
498
|
+
children: any
|
|
499
|
+
catchElement: any
|
|
500
|
+
}> {
|
|
501
|
+
state = {
|
|
502
|
+
error: false,
|
|
503
|
+
}
|
|
504
|
+
componentDidCatch(error: any, info: any) {
|
|
505
|
+
console.error(error)
|
|
506
|
+
|
|
507
|
+
this.setState({
|
|
508
|
+
error,
|
|
509
|
+
info,
|
|
510
|
+
})
|
|
511
|
+
}
|
|
512
|
+
reset = () => {
|
|
513
|
+
this.setState({
|
|
514
|
+
error: false,
|
|
515
|
+
info: false,
|
|
516
|
+
})
|
|
517
|
+
}
|
|
518
|
+
render() {
|
|
519
|
+
const catchElement = this.props.catchElement ?? DefaultCatchBoundary
|
|
520
|
+
|
|
521
|
+
if (this.state.error) {
|
|
522
|
+
return typeof catchElement === 'function'
|
|
523
|
+
? catchElement(this.state)
|
|
524
|
+
: catchElement
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return this.props.children
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
export function DefaultCatchBoundary({ error }: { error: any }) {
|
|
532
|
+
return (
|
|
533
|
+
<div style={{ padding: '.5rem', maxWidth: '100%' }}>
|
|
534
|
+
<strong style={{ fontSize: '1.2rem' }}>Something went wrong!</strong>
|
|
535
|
+
<div style={{ height: '.5rem' }} />
|
|
536
|
+
<div>
|
|
537
|
+
<pre>
|
|
538
|
+
{error.message ? (
|
|
539
|
+
<code
|
|
540
|
+
style={{
|
|
541
|
+
fontSize: '.7em',
|
|
542
|
+
border: '1px solid red',
|
|
543
|
+
borderRadius: '.25rem',
|
|
544
|
+
padding: '.5rem',
|
|
545
|
+
color: 'red',
|
|
546
|
+
}}
|
|
547
|
+
>
|
|
548
|
+
{error.message}
|
|
549
|
+
</code>
|
|
550
|
+
) : null}
|
|
551
|
+
</pre>
|
|
552
|
+
</div>
|
|
553
|
+
<div style={{ height: '1rem' }} />
|
|
554
|
+
<div
|
|
555
|
+
style={{
|
|
556
|
+
fontSize: '.8em',
|
|
557
|
+
borderLeft: '3px solid rgba(127, 127, 127, 1)',
|
|
558
|
+
paddingLeft: '.5rem',
|
|
559
|
+
opacity: 0.5,
|
|
560
|
+
}}
|
|
561
|
+
>
|
|
562
|
+
If you are the owner of this website, it's highly recommended that you
|
|
563
|
+
configure your own custom Catch/Error boundaries for the router. You can
|
|
564
|
+
optionally configure a boundary for each route.
|
|
565
|
+
</div>
|
|
566
|
+
</div>
|
|
567
|
+
)
|
|
568
|
+
}
|
package/src/qss.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
// We're inlining qss here for compression's sake, but we've included it as a hard dependency for the MIT license it requires.
|
|
4
|
+
|
|
5
|
+
export function encode(obj, pfx?: string) {
|
|
6
|
+
var k,
|
|
7
|
+
i,
|
|
8
|
+
tmp,
|
|
9
|
+
str = ''
|
|
10
|
+
|
|
11
|
+
for (k in obj) {
|
|
12
|
+
if ((tmp = obj[k]) !== void 0) {
|
|
13
|
+
if (Array.isArray(tmp)) {
|
|
14
|
+
for (i = 0; i < tmp.length; i++) {
|
|
15
|
+
str && (str += '&')
|
|
16
|
+
str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp[i])
|
|
17
|
+
}
|
|
18
|
+
} else {
|
|
19
|
+
str && (str += '&')
|
|
20
|
+
str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return (pfx || '') + str
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function toValue(mix) {
|
|
29
|
+
if (!mix) return ''
|
|
30
|
+
var str = decodeURIComponent(mix)
|
|
31
|
+
if (str === 'false') return false
|
|
32
|
+
if (str === 'true') return true
|
|
33
|
+
return +str * 0 === 0 ? +str : str
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function decode(str) {
|
|
37
|
+
var tmp,
|
|
38
|
+
k,
|
|
39
|
+
out = {},
|
|
40
|
+
arr = str.split('&')
|
|
41
|
+
|
|
42
|
+
while ((tmp = arr.shift())) {
|
|
43
|
+
tmp = tmp.split('=')
|
|
44
|
+
k = tmp.shift()
|
|
45
|
+
if (out[k] !== void 0) {
|
|
46
|
+
out[k] = [].concat(out[k], toValue(tmp.shift()))
|
|
47
|
+
} else {
|
|
48
|
+
out[k] = toValue(tmp.shift())
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return out
|
|
53
|
+
}
|