@tanstack/react-router 0.0.1-beta.9 → 1.0.1

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 (106) hide show
  1. package/LICENSE +21 -0
  2. package/build/cjs/CatchBoundary.js +128 -0
  3. package/build/cjs/CatchBoundary.js.map +1 -0
  4. package/build/cjs/Matches.js +233 -0
  5. package/build/cjs/Matches.js.map +1 -0
  6. package/build/cjs/RouterProvider.js +172 -0
  7. package/build/cjs/RouterProvider.js.map +1 -0
  8. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +2 -22
  9. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
  10. package/build/cjs/awaited.js +43 -0
  11. package/build/cjs/awaited.js.map +1 -0
  12. package/build/cjs/defer.js +37 -0
  13. package/build/cjs/defer.js.map +1 -0
  14. package/build/cjs/fileRoute.js +27 -0
  15. package/build/cjs/fileRoute.js.map +1 -0
  16. package/build/cjs/index.js +130 -0
  17. package/build/cjs/index.js.map +1 -0
  18. package/build/cjs/lazyRouteComponent.js +54 -0
  19. package/build/cjs/lazyRouteComponent.js.map +1 -0
  20. package/build/cjs/link.js +223 -0
  21. package/build/cjs/link.js.map +1 -0
  22. package/build/cjs/path.js +214 -0
  23. package/build/cjs/path.js.map +1 -0
  24. package/build/cjs/qss.js +63 -0
  25. package/build/cjs/qss.js.map +1 -0
  26. package/build/cjs/redirects.js +28 -0
  27. package/build/cjs/redirects.js.map +1 -0
  28. package/build/cjs/route.js +191 -0
  29. package/build/cjs/route.js.map +1 -0
  30. package/build/cjs/router.js +1085 -0
  31. package/build/cjs/router.js.map +1 -0
  32. package/build/cjs/scroll-restoration.js +202 -0
  33. package/build/cjs/scroll-restoration.js.map +1 -0
  34. package/build/cjs/searchParams.js +81 -0
  35. package/build/cjs/searchParams.js.map +1 -0
  36. package/build/cjs/useBlocker.js +55 -0
  37. package/build/cjs/useBlocker.js.map +1 -0
  38. package/build/cjs/useNavigate.js +86 -0
  39. package/build/cjs/useNavigate.js.map +1 -0
  40. package/build/cjs/useParams.js +26 -0
  41. package/build/cjs/useParams.js.map +1 -0
  42. package/build/cjs/useSearch.js +25 -0
  43. package/build/cjs/useSearch.js.map +1 -0
  44. package/build/cjs/utils.js +241 -0
  45. package/build/cjs/utils.js.map +1 -0
  46. package/build/esm/index.js +2302 -2534
  47. package/build/esm/index.js.map +1 -1
  48. package/build/stats-html.html +3498 -2694
  49. package/build/stats-react.json +1204 -44
  50. package/build/types/CatchBoundary.d.ts +36 -0
  51. package/build/types/Matches.d.ts +64 -0
  52. package/build/types/RouterProvider.d.ts +35 -0
  53. package/build/types/awaited.d.ts +9 -0
  54. package/build/types/defer.d.ts +19 -0
  55. package/build/types/fileRoute.d.ts +38 -0
  56. package/build/types/history.d.ts +7 -0
  57. package/build/types/index.d.ts +27 -74
  58. package/build/types/lazyRouteComponent.d.ts +2 -0
  59. package/build/types/link.d.ts +93 -0
  60. package/build/types/location.d.ts +12 -0
  61. package/build/types/path.d.ts +17 -0
  62. package/build/types/qss.d.ts +2 -0
  63. package/build/types/redirects.d.ts +11 -0
  64. package/build/types/route.d.ts +283 -0
  65. package/build/types/routeInfo.d.ts +31 -0
  66. package/build/types/router.d.ts +186 -0
  67. package/build/types/scroll-restoration.d.ts +18 -0
  68. package/build/types/searchParams.d.ts +7 -0
  69. package/build/types/useBlocker.d.ts +9 -0
  70. package/build/types/useNavigate.d.ts +19 -0
  71. package/build/types/useParams.d.ts +7 -0
  72. package/build/types/useSearch.d.ts +7 -0
  73. package/build/types/utils.d.ts +69 -0
  74. package/build/umd/index.development.js +2899 -2493
  75. package/build/umd/index.development.js.map +1 -1
  76. package/build/umd/index.production.js +4 -4
  77. package/build/umd/index.production.js.map +1 -1
  78. package/package.json +12 -10
  79. package/src/CatchBoundary.tsx +101 -0
  80. package/src/Matches.tsx +423 -0
  81. package/src/RouterProvider.tsx +254 -0
  82. package/src/awaited.tsx +40 -0
  83. package/src/defer.ts +55 -0
  84. package/src/fileRoute.ts +152 -0
  85. package/src/history.ts +8 -0
  86. package/src/index.tsx +28 -619
  87. package/src/lazyRouteComponent.tsx +33 -0
  88. package/src/link.tsx +603 -0
  89. package/src/location.ts +13 -0
  90. package/src/path.ts +261 -0
  91. package/src/qss.ts +53 -0
  92. package/src/redirects.ts +39 -0
  93. package/src/route.ts +882 -0
  94. package/src/routeInfo.ts +84 -0
  95. package/src/router.ts +1671 -0
  96. package/src/scroll-restoration.tsx +230 -0
  97. package/src/searchParams.ts +79 -0
  98. package/src/useBlocker.tsx +27 -0
  99. package/src/useNavigate.tsx +111 -0
  100. package/src/useParams.tsx +25 -0
  101. package/src/useSearch.tsx +25 -0
  102. package/src/utils.ts +360 -0
  103. package/build/cjs/react-router/src/index.js +0 -458
  104. package/build/cjs/react-router/src/index.js.map +0 -1
  105. package/build/cjs/router-core/build/esm/index.js +0 -2524
  106. package/build/cjs/router-core/build/esm/index.js.map +0 -1
@@ -0,0 +1,230 @@
1
+ import * as React from 'react'
2
+
3
+ const useLayoutEffect =
4
+ typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect
5
+
6
+ import { ParsedLocation } from './location'
7
+ import { useRouter } from './RouterProvider'
8
+ import { NonNullableUpdater, functionalUpdate } from './utils'
9
+
10
+ const windowKey = 'window'
11
+ const delimiter = '___'
12
+
13
+ let weakScrolledElements = new WeakSet<any>()
14
+
15
+ type CacheValue = Record<string, { scrollX: number; scrollY: number }>
16
+ type CacheState = {
17
+ cached: CacheValue
18
+ next: CacheValue
19
+ }
20
+
21
+ type Cache = {
22
+ state: CacheState
23
+ set: (updater: NonNullableUpdater<CacheState>) => void
24
+ }
25
+
26
+ const sessionsStorage = typeof window !== 'undefined' && window.sessionStorage
27
+
28
+ let cache: Cache = sessionsStorage
29
+ ? (() => {
30
+ const storageKey = 'tsr-scroll-restoration-v2'
31
+
32
+ const state: CacheState = JSON.parse(
33
+ window.sessionStorage.getItem(storageKey) || 'null',
34
+ ) || { cached: {}, next: {} }
35
+
36
+ return {
37
+ state,
38
+ set: (updater) => {
39
+ cache.state = functionalUpdate(updater, cache.state)
40
+ window.sessionStorage.setItem(storageKey, JSON.stringify(cache.state))
41
+ },
42
+ }
43
+ })()
44
+ : (undefined as any)
45
+
46
+ export type ScrollRestorationOptions = {
47
+ getKey?: (location: ParsedLocation) => string
48
+ }
49
+
50
+ const defaultGetKey = (location: ParsedLocation) => location.state.key!
51
+
52
+ export function useScrollRestoration(options?: ScrollRestorationOptions) {
53
+ const router = useRouter()
54
+
55
+ useLayoutEffect(() => {
56
+ const getKey = options?.getKey || defaultGetKey
57
+
58
+ const { history } = window
59
+ if (history.scrollRestoration) {
60
+ history.scrollRestoration = 'manual'
61
+ }
62
+
63
+ const onScroll = (event: Event) => {
64
+ if (weakScrolledElements.has(event.target)) return
65
+ weakScrolledElements.add(event.target)
66
+
67
+ let elementSelector = ''
68
+
69
+ if (event.target === document || event.target === window) {
70
+ elementSelector = windowKey
71
+ } else {
72
+ const attrId = (event.target as Element).getAttribute(
73
+ 'data-scroll-restoration-id',
74
+ )
75
+
76
+ if (attrId) {
77
+ elementSelector = `[data-scroll-restoration-id="${attrId}"]`
78
+ } else {
79
+ elementSelector = getCssSelector(event.target)
80
+ }
81
+ }
82
+
83
+ if (!cache.state.next[elementSelector]) {
84
+ cache.set((c) => ({
85
+ ...c,
86
+ next: {
87
+ ...c.next,
88
+ [elementSelector]: {
89
+ scrollX: NaN,
90
+ scrollY: NaN,
91
+ },
92
+ },
93
+ }))
94
+ }
95
+ }
96
+
97
+ if (typeof document !== 'undefined') {
98
+ document.addEventListener('scroll', onScroll, true)
99
+ }
100
+
101
+ const unsubOnBeforeLoad = router.subscribe('onBeforeLoad', (event) => {
102
+ if (event.pathChanged) {
103
+ const restoreKey = getKey(event.fromLocation)
104
+ for (const elementSelector in cache.state.next) {
105
+ const entry = cache.state.next[elementSelector]!
106
+ if (elementSelector === windowKey) {
107
+ entry.scrollX = window.scrollX || 0
108
+ entry.scrollY = window.scrollY || 0
109
+ } else if (elementSelector) {
110
+ const element = document.querySelector(elementSelector)
111
+ entry.scrollX = element?.scrollLeft || 0
112
+ entry.scrollY = element?.scrollTop || 0
113
+ }
114
+
115
+ cache.set((c) => {
116
+ const next = { ...c.next }
117
+ delete next[elementSelector]
118
+
119
+ return {
120
+ ...c,
121
+ next,
122
+ cached: {
123
+ ...c.cached,
124
+ [[restoreKey, elementSelector].join(delimiter)]: entry,
125
+ },
126
+ }
127
+ })
128
+ }
129
+ }
130
+ })
131
+
132
+ const unsubOnResolved = router.subscribe('onResolved', (event) => {
133
+ if (event.pathChanged) {
134
+ if (!router.resetNextScroll) {
135
+ return
136
+ }
137
+
138
+ router.resetNextScroll = true
139
+
140
+ const getKey = options?.getKey || defaultGetKey
141
+
142
+ const restoreKey = getKey(event.toLocation)
143
+ let windowRestored = false
144
+
145
+ for (const cacheKey in cache.state.cached) {
146
+ const entry = cache.state.cached[cacheKey]!
147
+ const [key, elementSelector] = cacheKey.split(delimiter)
148
+ if (key === restoreKey) {
149
+ if (elementSelector === windowKey) {
150
+ windowRestored = true
151
+ window.scrollTo(entry.scrollX, entry.scrollY)
152
+ } else if (elementSelector) {
153
+ const element = document.querySelector(elementSelector)
154
+ if (element) {
155
+ element.scrollLeft = entry.scrollX
156
+ element.scrollTop = entry.scrollY
157
+ }
158
+ }
159
+ }
160
+ }
161
+
162
+ if (!windowRestored) {
163
+ window.scrollTo(0, 0)
164
+ }
165
+
166
+ cache.set((c) => ({ ...c, next: {} }))
167
+ weakScrolledElements = new WeakSet<any>()
168
+ }
169
+ })
170
+
171
+ return () => {
172
+ document.removeEventListener('scroll', onScroll)
173
+ unsubOnBeforeLoad()
174
+ unsubOnResolved()
175
+ }
176
+ }, [])
177
+ }
178
+
179
+ export function ScrollRestoration(props: ScrollRestorationOptions) {
180
+ useScrollRestoration(props)
181
+ return null
182
+ }
183
+
184
+ export function useElementScrollRestoration(
185
+ options: (
186
+ | {
187
+ id: string
188
+ getElement?: () => Element | undefined | null
189
+ }
190
+ | {
191
+ id?: string
192
+ getElement: () => Element | undefined | null
193
+ }
194
+ ) & {
195
+ getKey?: (location: ParsedLocation) => string
196
+ },
197
+ ) {
198
+ const router = useRouter()
199
+ const getKey = options?.getKey || defaultGetKey
200
+
201
+ let elementSelector = ''
202
+
203
+ if (options.id) {
204
+ elementSelector = `[data-scroll-restoration-id="${options.id}"]`
205
+ } else {
206
+ const element = options.getElement?.()
207
+ if (!element) {
208
+ return
209
+ }
210
+ elementSelector = getCssSelector(element)
211
+ }
212
+
213
+ const restoreKey = getKey(router.latestLocation)
214
+ const cacheKey = [restoreKey, elementSelector].join(delimiter)
215
+ return cache.state.cached[cacheKey]
216
+ }
217
+
218
+ function getCssSelector(el: any): string {
219
+ let path = [],
220
+ parent
221
+ while ((parent = el.parentNode)) {
222
+ path.unshift(
223
+ `${el.tagName}:nth-child(${
224
+ ([].indexOf as any).call(parent.children, el) + 1
225
+ })`,
226
+ )
227
+ el = parent
228
+ }
229
+ return `${path.join(' > ')}`.toLowerCase()
230
+ }
@@ -0,0 +1,79 @@
1
+ import { decode, encode } from './qss'
2
+ import { AnySearchSchema } from './route'
3
+
4
+ export const defaultParseSearch = parseSearchWith(JSON.parse)
5
+ export const defaultStringifySearch = stringifySearchWith(
6
+ JSON.stringify,
7
+ JSON.parse,
8
+ )
9
+
10
+ export function parseSearchWith(parser: (str: string) => any) {
11
+ return (searchStr: string): AnySearchSchema => {
12
+ if (searchStr.substring(0, 1) === '?') {
13
+ searchStr = searchStr.substring(1)
14
+ }
15
+
16
+ let query: Record<string, unknown> = decode(searchStr)
17
+
18
+ // Try to parse any query params that might be json
19
+ for (let key in query) {
20
+ const value = query[key]
21
+ if (typeof value === 'string') {
22
+ try {
23
+ query[key] = parser(value)
24
+ } catch (err) {
25
+ //
26
+ }
27
+ }
28
+ }
29
+
30
+ return query
31
+ }
32
+ }
33
+
34
+ export function stringifySearchWith(
35
+ stringify: (search: any) => string,
36
+ parser?: (str: string) => any,
37
+ ) {
38
+ function stringifyValue(val: any) {
39
+ if (typeof val === 'object' && val !== null) {
40
+ try {
41
+ return stringify(val)
42
+ } catch (err) {
43
+ // silent
44
+ }
45
+ } else if (typeof val === 'string' && typeof parser === 'function') {
46
+ try {
47
+ // Check if it's a valid parseable string.
48
+ // If it is, then stringify it again.
49
+ parser(val)
50
+ return stringify(val)
51
+ } catch (err) {
52
+ // silent
53
+ }
54
+ }
55
+ return val
56
+ }
57
+
58
+ return (search: Record<string, any>) => {
59
+ search = { ...search }
60
+
61
+ if (search) {
62
+ Object.keys(search).forEach((key) => {
63
+ const val = search[key]
64
+ if (typeof val === 'undefined' || val === undefined) {
65
+ delete search[key]
66
+ } else {
67
+ search[key] = stringifyValue(val)
68
+ }
69
+ })
70
+ }
71
+
72
+ const searchStr = encode(search as Record<string, string>).toString()
73
+
74
+ return searchStr ? `?${searchStr}` : ''
75
+ }
76
+ }
77
+
78
+ export type SearchSerializer = (searchObj: Record<string, any>) => string
79
+ export type SearchParser = (searchStr: string) => Record<string, any>
@@ -0,0 +1,27 @@
1
+ import * as React from 'react'
2
+ import { ReactNode } from './route'
3
+ import { useRouter } from './RouterProvider'
4
+ import { BlockerFn } from '@tanstack/history'
5
+
6
+ export function useBlocker(
7
+ blockerFn: BlockerFn,
8
+ condition: boolean | any = true,
9
+ ): void {
10
+ const { history } = useRouter()
11
+
12
+ React.useEffect(() => {
13
+ if (!condition) return
14
+ return history.block(blockerFn)
15
+ })
16
+ }
17
+
18
+ export function Block({ blocker, condition, children }: PromptProps) {
19
+ useBlocker(blocker, condition)
20
+ return (children ?? null) as ReactNode
21
+ }
22
+
23
+ export type PromptProps = {
24
+ blocker: BlockerFn
25
+ condition?: boolean | any
26
+ children?: ReactNode
27
+ }
@@ -0,0 +1,111 @@
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 matchPathname = useMatch({
16
+ strict: false,
17
+ select: (s) => s.pathname,
18
+ })
19
+ return React.useCallback(
20
+ <
21
+ TFrom extends RoutePaths<TRouteTree> = TDefaultFrom,
22
+ TTo extends string = '',
23
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
24
+ TMaskTo extends string = '',
25
+ >(
26
+ opts?: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
27
+ ) => {
28
+ return navigate({
29
+ from: opts?.to ? matchPathname : undefined,
30
+ ...defaultOpts,
31
+ ...(opts as any),
32
+ })
33
+ },
34
+ [],
35
+ )
36
+ }
37
+
38
+ // NOTE: I don't know of anyone using this. It's undocumented, so let's wait until someone needs it
39
+ // export function typedNavigate<
40
+ // TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
41
+ // TDefaultFrom extends RoutePaths<TRouteTree> = '/',
42
+ // >(navigate: (opts: NavigateOptions<any>) => Promise<void>) {
43
+ // return navigate as <
44
+ // TFrom extends RoutePaths<TRouteTree> = TDefaultFrom,
45
+ // TTo extends string = '',
46
+ // TMaskFrom extends RoutePaths<TRouteTree> = '/',
47
+ // TMaskTo extends string = '',
48
+ // >(
49
+ // opts?: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
50
+ // ) => Promise<void>
51
+ // } //
52
+
53
+ export function Navigate<
54
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
55
+ TFrom extends RoutePaths<TRouteTree> = '/',
56
+ TTo extends string = '',
57
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
58
+ TMaskTo extends string = '',
59
+ >(props: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>): null {
60
+ const { navigate } = useRouter()
61
+ const match = useMatch({ strict: false })
62
+
63
+ React.useEffect(() => {
64
+ navigate({
65
+ from: props.to ? match.pathname : undefined,
66
+ ...props,
67
+ } as any)
68
+ }, [])
69
+
70
+ return null
71
+ }
72
+
73
+ export type UseLinkPropsOptions<
74
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
75
+ TFrom extends RoutePaths<TRouteTree> = '/',
76
+ TTo extends string = '',
77
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
78
+ TMaskTo extends string = '',
79
+ > = ActiveLinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
80
+ React.AnchorHTMLAttributes<HTMLAnchorElement>
81
+
82
+ export type LinkProps<
83
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
84
+ TFrom extends RoutePaths<TRouteTree> = '/',
85
+ TTo extends string = '',
86
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
87
+ TMaskTo extends string = '',
88
+ > = ActiveLinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
89
+ Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
90
+ // 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
91
+ children?:
92
+ | React.ReactNode
93
+ | ((state: { isActive: boolean }) => React.ReactNode)
94
+ }
95
+
96
+ export type ActiveLinkOptions<
97
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
98
+ TFrom extends RoutePaths<TRouteTree> = '/',
99
+ TTo extends string = '',
100
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
101
+ TMaskTo extends string = '',
102
+ > = LinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {
103
+ // 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)
104
+ activeProps?:
105
+ | React.AnchorHTMLAttributes<HTMLAnchorElement>
106
+ | (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
107
+ // 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)
108
+ inactiveProps?:
109
+ | React.AnchorHTMLAttributes<HTMLAnchorElement>
110
+ | (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
111
+ }
@@ -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 './Matches'
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
+ }