@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.
Files changed (72) hide show
  1. package/build/cjs/CatchBoundary.js +125 -0
  2. package/build/cjs/CatchBoundary.js.map +1 -0
  3. package/build/cjs/Matches.js +223 -0
  4. package/build/cjs/Matches.js.map +1 -0
  5. package/build/cjs/RouterProvider.js +99 -53
  6. package/build/cjs/RouterProvider.js.map +1 -1
  7. package/build/cjs/index.js +46 -37
  8. package/build/cjs/index.js.map +1 -1
  9. package/build/cjs/lazyRouteComponent.js +57 -0
  10. package/build/cjs/lazyRouteComponent.js.map +1 -0
  11. package/build/cjs/link.js +150 -0
  12. package/build/cjs/link.js.map +1 -0
  13. package/build/cjs/route.js +9 -5
  14. package/build/cjs/route.js.map +1 -1
  15. package/build/cjs/router.js.map +1 -1
  16. package/build/cjs/searchParams.js.map +1 -1
  17. package/build/cjs/useBlocker.js +64 -0
  18. package/build/cjs/useBlocker.js.map +1 -0
  19. package/build/cjs/useNavigate.js +78 -0
  20. package/build/cjs/useNavigate.js.map +1 -0
  21. package/build/cjs/useParams.js +28 -0
  22. package/build/cjs/useParams.js.map +1 -0
  23. package/build/cjs/useSearch.js +27 -0
  24. package/build/cjs/useSearch.js.map +1 -0
  25. package/build/cjs/utils.js +30 -1
  26. package/build/cjs/utils.js.map +1 -1
  27. package/build/esm/index.js +491 -514
  28. package/build/esm/index.js.map +1 -1
  29. package/build/stats-html.html +1 -1
  30. package/build/stats-react.json +484 -208
  31. package/build/types/CatchBoundary.d.ts +33 -0
  32. package/build/types/Matches.d.ts +31 -0
  33. package/build/types/RouterProvider.d.ts +42 -18
  34. package/build/types/fileRoute.d.ts +7 -7
  35. package/build/types/index.d.ts +13 -7
  36. package/build/types/injectHtml.d.ts +0 -0
  37. package/build/types/lazyRouteComponent.d.ts +2 -0
  38. package/build/types/link.d.ts +10 -3
  39. package/build/types/route.d.ts +39 -7
  40. package/build/types/router.d.ts +6 -7
  41. package/build/types/useBlocker.d.ts +8 -0
  42. package/build/types/useNavigate.d.ts +20 -0
  43. package/build/types/useParams.d.ts +7 -0
  44. package/build/types/useSearch.d.ts +7 -0
  45. package/build/types/utils.d.ts +17 -0
  46. package/build/umd/index.development.js +492 -513
  47. package/build/umd/index.development.js.map +1 -1
  48. package/build/umd/index.production.js +1 -1
  49. package/build/umd/index.production.js.map +1 -1
  50. package/package.json +2 -2
  51. package/src/CatchBoundary.tsx +97 -0
  52. package/src/Matches.tsx +315 -0
  53. package/src/RouterProvider.tsx +317 -251
  54. package/src/index.tsx +17 -8
  55. package/src/injectHtml.ts +28 -0
  56. package/src/lazyRouteComponent.tsx +33 -0
  57. package/src/{link.ts → link.tsx} +163 -3
  58. package/src/location.ts +1 -0
  59. package/src/route.ts +86 -16
  60. package/src/router.ts +6 -7
  61. package/src/searchParams.ts +1 -0
  62. package/src/useBlocker.tsx +34 -0
  63. package/src/useNavigate.tsx +109 -0
  64. package/src/useParams.tsx +25 -0
  65. package/src/useSearch.tsx +25 -0
  66. package/src/utils.ts +83 -3
  67. package/build/cjs/react.js +0 -620
  68. package/build/cjs/react.js.map +0 -1
  69. package/build/types/RouteMatch.d.ts +0 -23
  70. package/build/types/react.d.ts +0 -141
  71. package/src/RouteMatch.ts +0 -28
  72. 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
+ }
@@ -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 { AnyRoute } from './route'
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 { LocationState } from './location'
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
@@ -11,4 +11,5 @@ export interface ParsedLocation<TSearchObj extends AnySearchSchema = {}> {
11
11
  maskedLocation?: ParsedLocation<TSearchObj>
12
12
  unmaskOnReload?: boolean
13
13
  }
14
+
14
15
  export interface LocationState {}
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 { RoutePaths } from './routeInfo'
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 { AnyRouteMatch } from './RouteMatch'
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 { NavigateOptions, ParsePathParams, ToSubOptions } from './link'
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: (opts: NavigateOptions<AnyRoute>) => Promise<void>
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 './react'
20
- import { RouteMatch } from './RouteMatch'
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: 'idle' | 'pending'
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
  }
@@ -74,5 +74,6 @@ export function stringifySearchWith(
74
74
  return searchStr ? `?${searchStr}` : ''
75
75
  }
76
76
  }
77
+
77
78
  export type SearchSerializer = (searchObj: Record<string, any>) => string
78
79
  export type SearchParser = (searchStr: string) => Record<string, any>
@@ -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
+ }