@tanstack/react-router 0.0.1-beta.209 → 0.0.1-beta.210
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/CatchBoundary.js +125 -0
- package/build/cjs/CatchBoundary.js.map +1 -0
- package/build/cjs/Matches.js +223 -0
- package/build/cjs/Matches.js.map +1 -0
- package/build/cjs/RouterProvider.js +99 -53
- package/build/cjs/RouterProvider.js.map +1 -1
- package/build/cjs/index.js +46 -37
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/lazyRouteComponent.js +57 -0
- package/build/cjs/lazyRouteComponent.js.map +1 -0
- package/build/cjs/link.js +150 -0
- package/build/cjs/link.js.map +1 -0
- package/build/cjs/route.js +9 -5
- package/build/cjs/route.js.map +1 -1
- package/build/cjs/router.js.map +1 -1
- package/build/cjs/searchParams.js.map +1 -1
- package/build/cjs/useBlocker.js +64 -0
- package/build/cjs/useBlocker.js.map +1 -0
- package/build/cjs/useNavigate.js +78 -0
- package/build/cjs/useNavigate.js.map +1 -0
- package/build/cjs/useParams.js +28 -0
- package/build/cjs/useParams.js.map +1 -0
- package/build/cjs/useSearch.js +27 -0
- package/build/cjs/useSearch.js.map +1 -0
- package/build/cjs/utils.js +30 -1
- package/build/cjs/utils.js.map +1 -1
- package/build/esm/index.js +491 -514
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +484 -208
- package/build/types/CatchBoundary.d.ts +33 -0
- package/build/types/Matches.d.ts +31 -0
- package/build/types/RouterProvider.d.ts +42 -18
- package/build/types/fileRoute.d.ts +7 -7
- package/build/types/index.d.ts +13 -7
- package/build/types/injectHtml.d.ts +0 -0
- package/build/types/lazyRouteComponent.d.ts +2 -0
- package/build/types/link.d.ts +10 -3
- package/build/types/route.d.ts +39 -7
- package/build/types/router.d.ts +6 -7
- package/build/types/useBlocker.d.ts +8 -0
- package/build/types/useNavigate.d.ts +20 -0
- package/build/types/useParams.d.ts +7 -0
- package/build/types/useSearch.d.ts +7 -0
- package/build/types/utils.d.ts +17 -0
- package/build/umd/index.development.js +492 -513
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +1 -1
- package/build/umd/index.production.js.map +1 -1
- package/package.json +2 -2
- package/src/CatchBoundary.tsx +97 -0
- package/src/Matches.tsx +315 -0
- package/src/RouterProvider.tsx +317 -251
- package/src/index.tsx +17 -8
- package/src/injectHtml.ts +28 -0
- package/src/lazyRouteComponent.tsx +33 -0
- package/src/{link.ts → link.tsx} +163 -3
- package/src/location.ts +1 -0
- package/src/route.ts +86 -16
- package/src/router.ts +6 -7
- package/src/searchParams.ts +1 -0
- package/src/useBlocker.tsx +34 -0
- package/src/useNavigate.tsx +109 -0
- package/src/useParams.tsx +25 -0
- package/src/useSearch.tsx +25 -0
- package/src/utils.ts +83 -3
- package/build/cjs/react.js +0 -620
- package/build/cjs/react.js.map +0 -1
- package/build/types/RouteMatch.d.ts +0 -23
- package/build/types/react.d.ts +0 -141
- package/src/RouteMatch.ts +0 -28
- package/src/react.tsx +0 -1013
package/src/index.tsx
CHANGED
|
@@ -2,19 +2,28 @@
|
|
|
2
2
|
export * from '@tanstack/history'
|
|
3
3
|
export { default as invariant } from 'tiny-invariant'
|
|
4
4
|
export { default as warning } from 'tiny-warning'
|
|
5
|
+
// export * from './awaited
|
|
6
|
+
// export * from './defer'
|
|
7
|
+
export * from './CatchBoundary'
|
|
8
|
+
export * from './fileRoute'
|
|
9
|
+
export * from './history'
|
|
10
|
+
export * from './index'
|
|
11
|
+
// export * from './injectHtml'
|
|
12
|
+
export * from './lazyRouteComponent'
|
|
5
13
|
export * from './link'
|
|
14
|
+
export * from './location'
|
|
15
|
+
export * from './Matches'
|
|
6
16
|
export * from './path'
|
|
7
17
|
export * from './qss'
|
|
18
|
+
export * from './redirects'
|
|
8
19
|
export * from './route'
|
|
9
|
-
export * from './fileRoute'
|
|
10
20
|
export * from './routeInfo'
|
|
11
21
|
export * from './router'
|
|
12
|
-
export * from './searchParams'
|
|
13
|
-
export * from './utils'
|
|
14
|
-
export * from './react'
|
|
15
|
-
export * from './history'
|
|
16
|
-
export * from './RouteMatch'
|
|
17
|
-
export * from './redirects'
|
|
18
|
-
export * from './location'
|
|
19
22
|
export * from './RouterProvider'
|
|
20
23
|
// export * from './scroll-restoration'
|
|
24
|
+
export * from './searchParams'
|
|
25
|
+
export * from './useBlocker'
|
|
26
|
+
export * from './useNavigate'
|
|
27
|
+
export * from './useParams'
|
|
28
|
+
export * from './useSearch'
|
|
29
|
+
export * from './utils'
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// export function useInjectHtml() {
|
|
2
|
+
// const { } = useRouter()
|
|
3
|
+
// return React.useCallback(
|
|
4
|
+
// (html: string | (() => Promise<string> | string)) => {
|
|
5
|
+
// router.injectHtml(html)
|
|
6
|
+
// },
|
|
7
|
+
// [],
|
|
8
|
+
// )
|
|
9
|
+
// }
|
|
10
|
+
// export function useDehydrate() {
|
|
11
|
+
// const { } = useRouter()
|
|
12
|
+
// return React.useCallback(function dehydrate<T>(
|
|
13
|
+
// key: any,
|
|
14
|
+
// data: T | (() => Promise<T> | T),
|
|
15
|
+
// ) {
|
|
16
|
+
// return router.dehydrateData(key, data)
|
|
17
|
+
// },
|
|
18
|
+
// [])
|
|
19
|
+
// }
|
|
20
|
+
// export function useHydrate() {
|
|
21
|
+
// const { } = useRouter()
|
|
22
|
+
// return function hydrate<T = unknown>(key: any) {
|
|
23
|
+
// return router.hydrateData(key) as T
|
|
24
|
+
// }
|
|
25
|
+
// }
|
|
26
|
+
// This is the messiest thing ever... I'm either seriously tired (likely) or
|
|
27
|
+
// there has to be a better way to reset error boundaries when the
|
|
28
|
+
// router's location key changes.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { AsyncRouteComponent } from './route'
|
|
3
|
+
|
|
4
|
+
export function lazyRouteComponent<
|
|
5
|
+
T extends Record<string, any>,
|
|
6
|
+
TKey extends keyof T = 'default',
|
|
7
|
+
>(
|
|
8
|
+
importer: () => Promise<T>,
|
|
9
|
+
exportName?: TKey,
|
|
10
|
+
): T[TKey] extends (props: infer TProps) => any
|
|
11
|
+
? AsyncRouteComponent<TProps>
|
|
12
|
+
: never {
|
|
13
|
+
let loadPromise: Promise<any>
|
|
14
|
+
|
|
15
|
+
const load = () => {
|
|
16
|
+
if (!loadPromise) {
|
|
17
|
+
loadPromise = importer()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return loadPromise
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const lazyComp = React.lazy(async () => {
|
|
24
|
+
const moduleExports = await load()
|
|
25
|
+
const comp = moduleExports[exportName ?? 'default']
|
|
26
|
+
return {
|
|
27
|
+
default: comp,
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
;(lazyComp as any).preload = load
|
|
31
|
+
|
|
32
|
+
return lazyComp as any
|
|
33
|
+
}
|
package/src/{link.ts → link.tsx}
RENAMED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { useMatch } from './Matches'
|
|
3
|
+
import { useRouter } from './RouterProvider'
|
|
1
4
|
import { Trim } from './fileRoute'
|
|
2
|
-
import {
|
|
5
|
+
import { LocationState, ParsedLocation } from './location'
|
|
6
|
+
import { AnyRoute, ReactNode } from './route'
|
|
3
7
|
import {
|
|
4
8
|
AllParams,
|
|
5
9
|
FullSearchSchema,
|
|
@@ -8,8 +12,7 @@ import {
|
|
|
8
12
|
RoutePaths,
|
|
9
13
|
} from './routeInfo'
|
|
10
14
|
import { RegisteredRouter } from './router'
|
|
11
|
-
import {
|
|
12
|
-
import { ParsedLocation } from './location'
|
|
15
|
+
import { MakeLinkOptions, MakeLinkPropsOptions } from './useNavigate'
|
|
13
16
|
import {
|
|
14
17
|
Expand,
|
|
15
18
|
NoInfer,
|
|
@@ -17,6 +20,7 @@ import {
|
|
|
17
20
|
PickRequired,
|
|
18
21
|
UnionToIntersection,
|
|
19
22
|
Updater,
|
|
23
|
+
functionalUpdate,
|
|
20
24
|
} from './utils'
|
|
21
25
|
|
|
22
26
|
export type LinkInfo =
|
|
@@ -136,6 +140,8 @@ export type NavigateOptions<
|
|
|
136
140
|
// `replace` is a boolean that determines whether the navigation should replace the current history entry or push a new one.
|
|
137
141
|
replace?: boolean
|
|
138
142
|
resetScroll?: boolean
|
|
143
|
+
// If set to `true`, the link's underlying navigate() call will be wrapped in a `React.startTransition` call. Defaults to `true`.
|
|
144
|
+
startTransition?: boolean
|
|
139
145
|
}
|
|
140
146
|
|
|
141
147
|
export type ToOptions<
|
|
@@ -345,3 +351,157 @@ export type ResolveRelativePath<TFrom, TTo = '.'> = TFrom extends string
|
|
|
345
351
|
: CleanPath<Join<['/', ...Split<TFrom>, ...Split<TTo>]>>
|
|
346
352
|
: never
|
|
347
353
|
: never
|
|
354
|
+
|
|
355
|
+
export function useLinkProps<
|
|
356
|
+
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
357
|
+
TFrom extends RoutePaths<TRouteTree> = '/',
|
|
358
|
+
TTo extends string = '',
|
|
359
|
+
TMaskFrom extends RoutePaths<TRouteTree> = '/',
|
|
360
|
+
TMaskTo extends string = '',
|
|
361
|
+
>(
|
|
362
|
+
options: MakeLinkPropsOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
|
|
363
|
+
): React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
364
|
+
const { buildLink } = useRouter()
|
|
365
|
+
const match = useMatch({
|
|
366
|
+
strict: false,
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
const {
|
|
370
|
+
// custom props
|
|
371
|
+
type,
|
|
372
|
+
children,
|
|
373
|
+
target,
|
|
374
|
+
activeProps = () => ({ className: 'active' }),
|
|
375
|
+
inactiveProps = () => ({}),
|
|
376
|
+
activeOptions,
|
|
377
|
+
disabled,
|
|
378
|
+
hash,
|
|
379
|
+
search,
|
|
380
|
+
params,
|
|
381
|
+
to,
|
|
382
|
+
state,
|
|
383
|
+
mask,
|
|
384
|
+
preload,
|
|
385
|
+
preloadDelay,
|
|
386
|
+
replace,
|
|
387
|
+
startTransition,
|
|
388
|
+
// element props
|
|
389
|
+
style,
|
|
390
|
+
className,
|
|
391
|
+
onClick,
|
|
392
|
+
onFocus,
|
|
393
|
+
onMouseEnter,
|
|
394
|
+
onMouseLeave,
|
|
395
|
+
onTouchStart,
|
|
396
|
+
...rest
|
|
397
|
+
} = options
|
|
398
|
+
|
|
399
|
+
const linkInfo = buildLink({
|
|
400
|
+
from: options.to ? match.pathname : undefined,
|
|
401
|
+
...options,
|
|
402
|
+
} as any)
|
|
403
|
+
|
|
404
|
+
if (linkInfo.type === 'external') {
|
|
405
|
+
const { href } = linkInfo
|
|
406
|
+
return { href }
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const {
|
|
410
|
+
handleClick,
|
|
411
|
+
handleFocus,
|
|
412
|
+
handleEnter,
|
|
413
|
+
handleLeave,
|
|
414
|
+
handleTouchStart,
|
|
415
|
+
isActive,
|
|
416
|
+
next,
|
|
417
|
+
} = linkInfo
|
|
418
|
+
|
|
419
|
+
const composeHandlers =
|
|
420
|
+
(handlers: (undefined | ((e: any) => void))[]) =>
|
|
421
|
+
(e: React.SyntheticEvent) => {
|
|
422
|
+
if (e.persist) e.persist()
|
|
423
|
+
handlers.filter(Boolean).forEach((handler) => {
|
|
424
|
+
if (e.defaultPrevented) return
|
|
425
|
+
handler!(e)
|
|
426
|
+
})
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Get the active props
|
|
430
|
+
const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> = isActive
|
|
431
|
+
? functionalUpdate(activeProps as any, {}) ?? {}
|
|
432
|
+
: {}
|
|
433
|
+
|
|
434
|
+
// Get the inactive props
|
|
435
|
+
const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =
|
|
436
|
+
isActive ? {} : functionalUpdate(inactiveProps, {}) ?? {}
|
|
437
|
+
|
|
438
|
+
return {
|
|
439
|
+
...resolvedActiveProps,
|
|
440
|
+
...resolvedInactiveProps,
|
|
441
|
+
...rest,
|
|
442
|
+
href: disabled
|
|
443
|
+
? undefined
|
|
444
|
+
: next.maskedLocation
|
|
445
|
+
? next.maskedLocation.href
|
|
446
|
+
: next.href,
|
|
447
|
+
onClick: composeHandlers([onClick, handleClick]),
|
|
448
|
+
onFocus: composeHandlers([onFocus, handleFocus]),
|
|
449
|
+
onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),
|
|
450
|
+
onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),
|
|
451
|
+
onTouchStart: composeHandlers([onTouchStart, handleTouchStart]),
|
|
452
|
+
target,
|
|
453
|
+
style: {
|
|
454
|
+
...style,
|
|
455
|
+
...resolvedActiveProps.style,
|
|
456
|
+
...resolvedInactiveProps.style,
|
|
457
|
+
},
|
|
458
|
+
className:
|
|
459
|
+
[
|
|
460
|
+
className,
|
|
461
|
+
resolvedActiveProps.className,
|
|
462
|
+
resolvedInactiveProps.className,
|
|
463
|
+
]
|
|
464
|
+
.filter(Boolean)
|
|
465
|
+
.join(' ') || undefined,
|
|
466
|
+
...(disabled
|
|
467
|
+
? {
|
|
468
|
+
role: 'link',
|
|
469
|
+
'aria-disabled': true,
|
|
470
|
+
}
|
|
471
|
+
: undefined),
|
|
472
|
+
['data-status']: isActive ? 'active' : undefined,
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export interface LinkComponent<TProps extends Record<string, any> = {}> {
|
|
477
|
+
<
|
|
478
|
+
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
479
|
+
TFrom extends RoutePaths<TRouteTree> = '/',
|
|
480
|
+
TTo extends string = '',
|
|
481
|
+
TMaskFrom extends RoutePaths<TRouteTree> = '/',
|
|
482
|
+
TMaskTo extends string = '',
|
|
483
|
+
>(
|
|
484
|
+
props: MakeLinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
|
|
485
|
+
TProps &
|
|
486
|
+
React.RefAttributes<HTMLAnchorElement>,
|
|
487
|
+
): ReactNode
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
export const Link: LinkComponent = React.forwardRef((props: any, ref) => {
|
|
491
|
+
const linkProps = useLinkProps(props)
|
|
492
|
+
|
|
493
|
+
return (
|
|
494
|
+
<a
|
|
495
|
+
{...{
|
|
496
|
+
ref: ref as any,
|
|
497
|
+
...linkProps,
|
|
498
|
+
children:
|
|
499
|
+
typeof props.children === 'function'
|
|
500
|
+
? props.children({
|
|
501
|
+
isActive: (linkProps as any)['data-status'] === 'active',
|
|
502
|
+
})
|
|
503
|
+
: props.children,
|
|
504
|
+
}}
|
|
505
|
+
/>
|
|
506
|
+
)
|
|
507
|
+
}) as any
|
package/src/location.ts
CHANGED
package/src/route.ts
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
+
import { HistoryLocation } from '@tanstack/history'
|
|
2
|
+
import * as React from 'react'
|
|
1
3
|
import invariant from 'tiny-invariant'
|
|
2
|
-
import {
|
|
4
|
+
import { useMatch } from './Matches'
|
|
5
|
+
import { AnyRouteMatch } from './RouterProvider'
|
|
6
|
+
import { NavigateOptions, ParsePathParams, ToSubOptions } from './link'
|
|
7
|
+
import { ParsedLocation } from './location'
|
|
3
8
|
import { joinPaths, trimPath } from './path'
|
|
9
|
+
import { RoutePaths } from './routeInfo'
|
|
4
10
|
import { AnyRouter } from './router'
|
|
5
|
-
import {
|
|
11
|
+
import { useParams } from './useParams'
|
|
12
|
+
import { useSearch } from './useSearch'
|
|
6
13
|
import {
|
|
14
|
+
Assign,
|
|
7
15
|
Expand,
|
|
8
16
|
IsAny,
|
|
9
17
|
NoInfer,
|
|
10
18
|
PickRequired,
|
|
11
19
|
UnionToIntersection,
|
|
12
|
-
Assign,
|
|
13
20
|
} from './utils'
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
ErrorRouteComponent,
|
|
17
|
-
PendingRouteComponent,
|
|
18
|
-
RouteComponent,
|
|
19
|
-
RouteProps,
|
|
20
|
-
useMatch,
|
|
21
|
-
useParams,
|
|
22
|
-
useSearch,
|
|
23
|
-
} from './react'
|
|
24
|
-
import { HistoryLocation } from '@tanstack/history'
|
|
25
|
-
import { ParsedLocation } from './location'
|
|
21
|
+
import { BuildLocationFn, NavigateFn } from './RouterProvider'
|
|
26
22
|
|
|
27
23
|
export const rootRouteId = '__root__' as const
|
|
28
24
|
export type RootRouteId = typeof rootRouteId
|
|
29
25
|
export type AnyPathParams = {}
|
|
26
|
+
|
|
30
27
|
export type AnySearchSchema = {}
|
|
28
|
+
|
|
31
29
|
export type AnyContext = {}
|
|
30
|
+
|
|
32
31
|
export interface RouteContext {}
|
|
32
|
+
|
|
33
33
|
export interface RouteMeta {}
|
|
34
34
|
|
|
35
35
|
export type PreloadableObj = { preload?: () => Promise<void> }
|
|
@@ -155,7 +155,8 @@ type BeforeLoadFn<
|
|
|
155
155
|
params: TAllParams
|
|
156
156
|
context: TParentRoute['types']['allContext']
|
|
157
157
|
location: ParsedLocation
|
|
158
|
-
navigate:
|
|
158
|
+
navigate: NavigateFn<AnyRoute>
|
|
159
|
+
buildLocation: BuildLocationFn<AnyRoute>
|
|
159
160
|
}) => Promise<TRouteContext> | TRouteContext | void
|
|
160
161
|
|
|
161
162
|
export type UpdatableRouteOptions<
|
|
@@ -714,3 +715,72 @@ export function createRouteMask<
|
|
|
714
715
|
): RouteMask<TRouteTree> {
|
|
715
716
|
return opts as any
|
|
716
717
|
}
|
|
718
|
+
|
|
719
|
+
export type RouteProps<
|
|
720
|
+
TFullSearchSchema extends Record<string, any> = AnySearchSchema,
|
|
721
|
+
TAllParams extends AnyPathParams = AnyPathParams,
|
|
722
|
+
TAllContext extends Record<string, any> = AnyContext,
|
|
723
|
+
> = {
|
|
724
|
+
useMatch: <TSelected = TAllContext>(opts?: {
|
|
725
|
+
select?: (search: TAllContext) => TSelected
|
|
726
|
+
}) => TSelected
|
|
727
|
+
useRouteContext: <TSelected = TAllContext>(opts?: {
|
|
728
|
+
select?: (search: TAllContext) => TSelected
|
|
729
|
+
}) => TSelected
|
|
730
|
+
useSearch: <TSelected = TFullSearchSchema>(opts?: {
|
|
731
|
+
select?: (search: TFullSearchSchema) => TSelected
|
|
732
|
+
}) => TSelected
|
|
733
|
+
useParams: <TSelected = TAllParams>(opts?: {
|
|
734
|
+
select?: (search: TAllParams) => TSelected
|
|
735
|
+
}) => TSelected
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
export type ErrorRouteProps<
|
|
739
|
+
TFullSearchSchema extends Record<string, any> = AnySearchSchema,
|
|
740
|
+
TAllParams extends AnyPathParams = AnyPathParams,
|
|
741
|
+
TAllContext extends Record<string, any> = AnyContext,
|
|
742
|
+
> = {
|
|
743
|
+
error: unknown
|
|
744
|
+
info: { componentStack: string }
|
|
745
|
+
} & RouteProps<TFullSearchSchema, TAllParams, TAllContext>
|
|
746
|
+
|
|
747
|
+
export type PendingRouteProps<
|
|
748
|
+
TFullSearchSchema extends Record<string, any> = AnySearchSchema,
|
|
749
|
+
TAllParams extends AnyPathParams = AnyPathParams,
|
|
750
|
+
TAllContext extends Record<string, any> = AnyContext,
|
|
751
|
+
> = RouteProps<TFullSearchSchema, TAllParams, TAllContext>
|
|
752
|
+
//
|
|
753
|
+
|
|
754
|
+
export type ReactNode = any
|
|
755
|
+
|
|
756
|
+
export type SyncRouteComponent<TProps> =
|
|
757
|
+
| ((props: TProps) => ReactNode)
|
|
758
|
+
| React.LazyExoticComponent<(props: TProps) => ReactNode>
|
|
759
|
+
|
|
760
|
+
export type AsyncRouteComponent<TProps> = SyncRouteComponent<TProps> & {
|
|
761
|
+
preload?: () => Promise<void>
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
export type RouteComponent<
|
|
765
|
+
TFullSearchSchema extends Record<string, any>,
|
|
766
|
+
TAllParams extends AnyPathParams,
|
|
767
|
+
TAllContext extends Record<string, any>,
|
|
768
|
+
> = AsyncRouteComponent<RouteProps<TFullSearchSchema, TAllParams, TAllContext>>
|
|
769
|
+
|
|
770
|
+
export type ErrorRouteComponent<
|
|
771
|
+
TFullSearchSchema extends Record<string, any>,
|
|
772
|
+
TAllParams extends AnyPathParams,
|
|
773
|
+
TAllContext extends Record<string, any>,
|
|
774
|
+
> = AsyncRouteComponent<
|
|
775
|
+
ErrorRouteProps<TFullSearchSchema, TAllParams, TAllContext>
|
|
776
|
+
>
|
|
777
|
+
|
|
778
|
+
export type PendingRouteComponent<
|
|
779
|
+
TFullSearchSchema extends Record<string, any>,
|
|
780
|
+
TAllParams extends AnyPathParams,
|
|
781
|
+
TAllContext extends Record<string, any>,
|
|
782
|
+
> = AsyncRouteComponent<
|
|
783
|
+
PendingRouteProps<TFullSearchSchema, TAllParams, TAllContext>
|
|
784
|
+
>
|
|
785
|
+
|
|
786
|
+
export type AnyRouteComponent = RouteComponent<any, any, any>
|
package/src/router.ts
CHANGED
|
@@ -16,8 +16,8 @@ import {
|
|
|
16
16
|
ErrorRouteComponent,
|
|
17
17
|
PendingRouteComponent,
|
|
18
18
|
RouteComponent,
|
|
19
|
-
} from './
|
|
20
|
-
import { RouteMatch } from './
|
|
19
|
+
} from './route'
|
|
20
|
+
import { RouteMatch } from './RouterProvider'
|
|
21
21
|
import { ParsedLocation } from './location'
|
|
22
22
|
import { LocationState } from './location'
|
|
23
23
|
import { SearchSerializer, SearchParser } from './searchParams'
|
|
@@ -93,12 +93,11 @@ export interface RouterOptions<
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
export interface RouterState<TRouteTree extends AnyRoute = AnyRoute> {
|
|
96
|
-
status: '
|
|
97
|
-
isFetching: boolean
|
|
96
|
+
status: 'pending' | 'idle'
|
|
98
97
|
matches: RouteMatch<TRouteTree>[]
|
|
99
98
|
pendingMatches: RouteMatch<TRouteTree>[]
|
|
100
99
|
location: ParsedLocation<FullSearchSchema<TRouteTree>>
|
|
101
|
-
resolvedLocation: ParsedLocation<FullSearchSchema<TRouteTree>>
|
|
100
|
+
resolvedLocation: undefined | ParsedLocation<FullSearchSchema<TRouteTree>>
|
|
102
101
|
lastUpdated: number
|
|
103
102
|
}
|
|
104
103
|
|
|
@@ -149,13 +148,13 @@ export const componentTypes = [
|
|
|
149
148
|
export type RouterEvents = {
|
|
150
149
|
onBeforeLoad: {
|
|
151
150
|
type: 'onBeforeLoad'
|
|
152
|
-
from: ParsedLocation
|
|
151
|
+
from: undefined | ParsedLocation
|
|
153
152
|
to: ParsedLocation
|
|
154
153
|
pathChanged: boolean
|
|
155
154
|
}
|
|
156
155
|
onLoad: {
|
|
157
156
|
type: 'onLoad'
|
|
158
|
-
from: ParsedLocation
|
|
157
|
+
from: undefined | ParsedLocation
|
|
159
158
|
to: ParsedLocation
|
|
160
159
|
pathChanged: boolean
|
|
161
160
|
}
|
package/src/searchParams.ts
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { ReactNode } from './route'
|
|
3
|
+
import { useRouter } from './RouterProvider'
|
|
4
|
+
|
|
5
|
+
export function useBlocker(
|
|
6
|
+
message: string,
|
|
7
|
+
condition: boolean | any = true,
|
|
8
|
+
): void {
|
|
9
|
+
const { history } = useRouter()
|
|
10
|
+
|
|
11
|
+
React.useEffect(() => {
|
|
12
|
+
if (!condition) return
|
|
13
|
+
|
|
14
|
+
let unblock = history.block((retry, cancel) => {
|
|
15
|
+
if (window.confirm(message)) {
|
|
16
|
+
unblock()
|
|
17
|
+
retry()
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
return unblock
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function Block({ message, condition, children }: PromptProps) {
|
|
26
|
+
useBlocker(message, condition)
|
|
27
|
+
return (children ?? null) as ReactNode
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type PromptProps = {
|
|
31
|
+
message: string
|
|
32
|
+
condition?: boolean | any
|
|
33
|
+
children?: ReactNode
|
|
34
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { useMatch } from './Matches'
|
|
3
|
+
import { useRouter } from './RouterProvider'
|
|
4
|
+
import { LinkOptions, NavigateOptions } from './link'
|
|
5
|
+
import { AnyRoute } from './route'
|
|
6
|
+
import { RoutePaths } from './routeInfo'
|
|
7
|
+
import { RegisteredRouter } from './router'
|
|
8
|
+
import { useLayoutEffect } from './utils'
|
|
9
|
+
|
|
10
|
+
export function useNavigate<
|
|
11
|
+
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
12
|
+
TDefaultFrom extends RoutePaths<TRouteTree> = '/',
|
|
13
|
+
>(defaultOpts?: { from?: TDefaultFrom }) {
|
|
14
|
+
const { navigate } = useRouter()
|
|
15
|
+
const match = useMatch({
|
|
16
|
+
strict: false,
|
|
17
|
+
})
|
|
18
|
+
return React.useCallback(
|
|
19
|
+
<
|
|
20
|
+
TFrom extends RoutePaths<TRouteTree> = TDefaultFrom,
|
|
21
|
+
TTo extends string = '',
|
|
22
|
+
TMaskFrom extends RoutePaths<TRouteTree> = '/',
|
|
23
|
+
TMaskTo extends string = '',
|
|
24
|
+
>(
|
|
25
|
+
opts?: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
|
|
26
|
+
) => {
|
|
27
|
+
return navigate({
|
|
28
|
+
from: opts?.to ? match.pathname : undefined,
|
|
29
|
+
...defaultOpts,
|
|
30
|
+
...(opts as any),
|
|
31
|
+
})
|
|
32
|
+
},
|
|
33
|
+
[],
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function typedNavigate<
|
|
38
|
+
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
39
|
+
TDefaultFrom extends RoutePaths<TRouteTree> = '/',
|
|
40
|
+
>(navigate: (opts: NavigateOptions<any>) => Promise<void>) {
|
|
41
|
+
return navigate as <
|
|
42
|
+
TFrom extends RoutePaths<TRouteTree> = TDefaultFrom,
|
|
43
|
+
TTo extends string = '',
|
|
44
|
+
TMaskFrom extends RoutePaths<TRouteTree> = '/',
|
|
45
|
+
TMaskTo extends string = '',
|
|
46
|
+
>(
|
|
47
|
+
opts?: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
|
|
48
|
+
) => Promise<void>
|
|
49
|
+
} //
|
|
50
|
+
|
|
51
|
+
export function Navigate<
|
|
52
|
+
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
53
|
+
TFrom extends RoutePaths<TRouteTree> = '/',
|
|
54
|
+
TTo extends string = '',
|
|
55
|
+
TMaskFrom extends RoutePaths<TRouteTree> = '/',
|
|
56
|
+
TMaskTo extends string = '',
|
|
57
|
+
>(props: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>): null {
|
|
58
|
+
const { navigate } = useRouter()
|
|
59
|
+
const match = useMatch({ strict: false })
|
|
60
|
+
|
|
61
|
+
useLayoutEffect(() => {
|
|
62
|
+
navigate({
|
|
63
|
+
from: props.to ? match.pathname : undefined,
|
|
64
|
+
...props,
|
|
65
|
+
} as any)
|
|
66
|
+
}, [])
|
|
67
|
+
|
|
68
|
+
return null
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type MakeLinkPropsOptions<
|
|
72
|
+
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
73
|
+
TFrom extends RoutePaths<TRouteTree> = '/',
|
|
74
|
+
TTo extends string = '',
|
|
75
|
+
TMaskFrom extends RoutePaths<TRouteTree> = '/',
|
|
76
|
+
TMaskTo extends string = '',
|
|
77
|
+
> = LinkPropsOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
|
|
78
|
+
React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
79
|
+
|
|
80
|
+
export type MakeLinkOptions<
|
|
81
|
+
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
82
|
+
TFrom extends RoutePaths<TRouteTree> = '/',
|
|
83
|
+
TTo extends string = '',
|
|
84
|
+
TMaskFrom extends RoutePaths<TRouteTree> = '/',
|
|
85
|
+
TMaskTo extends string = '',
|
|
86
|
+
> = LinkPropsOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
|
|
87
|
+
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
|
|
88
|
+
// 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
|
|
89
|
+
children?:
|
|
90
|
+
| React.ReactNode
|
|
91
|
+
| ((state: { isActive: boolean }) => React.ReactNode)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type LinkPropsOptions<
|
|
95
|
+
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
96
|
+
TFrom extends RoutePaths<TRouteTree> = '/',
|
|
97
|
+
TTo extends string = '',
|
|
98
|
+
TMaskFrom extends RoutePaths<TRouteTree> = '/',
|
|
99
|
+
TMaskTo extends string = '',
|
|
100
|
+
> = LinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {
|
|
101
|
+
// 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)
|
|
102
|
+
activeProps?:
|
|
103
|
+
| React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
104
|
+
| (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
|
|
105
|
+
// 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)
|
|
106
|
+
inactiveProps?:
|
|
107
|
+
| React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
108
|
+
| (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
|
|
109
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AnyRoute } from './route'
|
|
2
|
+
import { RouteIds, RouteById, AllParams } from './routeInfo'
|
|
3
|
+
import { RegisteredRouter } from './router'
|
|
4
|
+
import { last } from './utils'
|
|
5
|
+
import { useRouterState } from './RouterProvider'
|
|
6
|
+
import { StrictOrFrom } from './utils'
|
|
7
|
+
|
|
8
|
+
export function useParams<
|
|
9
|
+
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
10
|
+
TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
|
|
11
|
+
TDefaultSelected = AllParams<TRouteTree> &
|
|
12
|
+
RouteById<TRouteTree, TFrom>['types']['allParams'],
|
|
13
|
+
TSelected = TDefaultSelected,
|
|
14
|
+
>(
|
|
15
|
+
opts: StrictOrFrom<TFrom> & {
|
|
16
|
+
select?: (search: TDefaultSelected) => TSelected
|
|
17
|
+
},
|
|
18
|
+
): TSelected {
|
|
19
|
+
return useRouterState({
|
|
20
|
+
select: (state: any) => {
|
|
21
|
+
const params = (last(state.matches) as any)?.params
|
|
22
|
+
return opts?.select ? opts.select(params) : params
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AnyRoute } from './route'
|
|
2
|
+
import { RouteIds, RouteById } from './routeInfo'
|
|
3
|
+
import { RegisteredRouter } from './router'
|
|
4
|
+
import { RouteMatch } from './RouterProvider'
|
|
5
|
+
import { useMatch } from './Matches'
|
|
6
|
+
import { StrictOrFrom } from './utils'
|
|
7
|
+
|
|
8
|
+
export function useSearch<
|
|
9
|
+
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
10
|
+
TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
|
|
11
|
+
TStrict extends boolean = true,
|
|
12
|
+
TSearch = RouteById<TRouteTree, TFrom>['types']['fullSearchSchema'],
|
|
13
|
+
TSelected = TSearch,
|
|
14
|
+
>(
|
|
15
|
+
opts: StrictOrFrom<TFrom> & {
|
|
16
|
+
select?: (search: TSearch) => TSelected
|
|
17
|
+
},
|
|
18
|
+
): TStrict extends true ? TSelected : TSelected | undefined {
|
|
19
|
+
return useMatch({
|
|
20
|
+
...(opts as any),
|
|
21
|
+
select: (match: RouteMatch) => {
|
|
22
|
+
return opts?.select ? opts.select(match.search as TSearch) : match.search
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
}
|