@tanstack/react-router 0.0.1-beta.38 → 0.0.1-beta.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +0 -13
- package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
- package/build/cjs/index.js +306 -241
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.js +296 -252
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +87 -56
- package/build/types/index.d.ts +50 -42
- package/build/umd/index.development.js +1368 -858
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +2 -2
- package/build/umd/index.production.js.map +1 -1
- package/package.json +3 -2
- package/src/index.tsx +468 -421
- package/src/uSES/useSyncExternalStore.ts +16 -0
- package/src/uSES/useSyncExternalStoreShim.ts +20 -0
- package/src/uSES/useSyncExternalStoreShimClient.ts +87 -0
- package/src/uSES/useSyncExternalStoreShimServer.ts +20 -0
package/src/index.tsx
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
|
|
3
3
|
import { useSyncExternalStore } from 'use-sync-external-store/shim'
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
// import { useSyncExternalStore } from './uSES/useSyncExternalStoreShim'
|
|
6
|
+
import { createEffect, createRoot, untrack, unwrap } from '@solidjs/reactivity'
|
|
7
|
+
import { createStore } from '@solidjs/reactivity'
|
|
4
8
|
|
|
5
9
|
import {
|
|
6
|
-
AnyRoute,
|
|
7
|
-
CheckId,
|
|
8
|
-
rootRouteId,
|
|
9
10
|
Route,
|
|
10
11
|
RegisteredAllRouteInfo,
|
|
11
12
|
RegisteredRouter,
|
|
12
|
-
|
|
13
|
-
ToIdOption,
|
|
13
|
+
RouterStore,
|
|
14
14
|
last,
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
sharedClone,
|
|
16
|
+
Action,
|
|
17
17
|
warning,
|
|
18
18
|
RouterOptions,
|
|
19
19
|
RouteMatch,
|
|
@@ -24,9 +24,7 @@ import {
|
|
|
24
24
|
DefaultAllRouteInfo,
|
|
25
25
|
functionalUpdate,
|
|
26
26
|
createRouter,
|
|
27
|
-
AnyRouteInfo,
|
|
28
27
|
AllRouteInfo,
|
|
29
|
-
RouteInfo,
|
|
30
28
|
ValidFromPath,
|
|
31
29
|
LinkOptions,
|
|
32
30
|
RouteInfoByPath,
|
|
@@ -35,54 +33,42 @@ import {
|
|
|
35
33
|
ToOptions,
|
|
36
34
|
invariant,
|
|
37
35
|
Router,
|
|
36
|
+
Expand,
|
|
38
37
|
} from '@tanstack/router-core'
|
|
39
38
|
|
|
40
39
|
export * from '@tanstack/router-core'
|
|
41
40
|
|
|
42
|
-
export
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
export * from '@solidjs/reactivity'
|
|
42
|
+
|
|
43
|
+
type ReactNode = any
|
|
44
|
+
|
|
45
|
+
export type SyncRouteComponent<TProps = {}> = (props: TProps) => ReactNode
|
|
45
46
|
|
|
46
47
|
export type RouteComponent<TProps = {}> = SyncRouteComponent<TProps> & {
|
|
47
|
-
preload?: () => Promise<
|
|
48
|
+
preload?: () => Promise<void>
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
export function lazy(
|
|
51
52
|
importer: () => Promise<{ default: SyncRouteComponent }>,
|
|
52
53
|
): RouteComponent {
|
|
53
54
|
const lazyComp = React.lazy(importer as any)
|
|
54
|
-
let
|
|
55
|
-
let resolvedComp: SyncRouteComponent
|
|
56
|
-
|
|
57
|
-
const forwardedComp = React.forwardRef((props, ref) => {
|
|
58
|
-
const resolvedCompRef = React.useRef(resolvedComp || lazyComp)
|
|
59
|
-
return React.createElement(
|
|
60
|
-
resolvedCompRef.current as any,
|
|
61
|
-
{ ...(ref ? { ref } : {}), ...props } as any,
|
|
62
|
-
)
|
|
63
|
-
})
|
|
55
|
+
let preloaded: Promise<SyncRouteComponent>
|
|
64
56
|
|
|
65
|
-
const finalComp =
|
|
57
|
+
const finalComp = lazyComp as unknown as RouteComponent
|
|
66
58
|
|
|
67
|
-
finalComp.preload = () => {
|
|
68
|
-
if (!
|
|
69
|
-
|
|
70
|
-
resolvedComp = module.default
|
|
71
|
-
return resolvedComp
|
|
72
|
-
})
|
|
59
|
+
finalComp.preload = async () => {
|
|
60
|
+
if (!preloaded) {
|
|
61
|
+
await importer()
|
|
73
62
|
}
|
|
74
|
-
|
|
75
|
-
return promise
|
|
76
63
|
}
|
|
77
64
|
|
|
78
65
|
return finalComp
|
|
79
66
|
}
|
|
80
67
|
|
|
81
68
|
export type LinkPropsOptions<
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
> = LinkOptions<TAllRouteInfo, TFrom, TTo> & {
|
|
69
|
+
TFrom extends RegisteredAllRouteInfo['routePaths'] = '/',
|
|
70
|
+
TTo extends string = '.',
|
|
71
|
+
> = LinkOptions<RegisteredAllRouteInfo, TFrom, TTo> & {
|
|
86
72
|
// 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)
|
|
87
73
|
activeProps?:
|
|
88
74
|
| React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
@@ -93,41 +79,40 @@ export type LinkPropsOptions<
|
|
|
93
79
|
| (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
|
|
94
80
|
}
|
|
95
81
|
|
|
82
|
+
export type MakeUseMatchRouteOptions<
|
|
83
|
+
TFrom extends RegisteredAllRouteInfo['routePaths'] = '/',
|
|
84
|
+
TTo extends string = '.',
|
|
85
|
+
> = ToOptions<RegisteredAllRouteInfo, TFrom, TTo> & MatchRouteOptions
|
|
86
|
+
|
|
96
87
|
export type MakeMatchRouteOptions<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
> = ToOptions<TAllRouteInfo, TFrom, TTo> &
|
|
88
|
+
TFrom extends RegisteredAllRouteInfo['routePaths'] = '/',
|
|
89
|
+
TTo extends string = '.',
|
|
90
|
+
> = ToOptions<RegisteredAllRouteInfo, TFrom, TTo> &
|
|
101
91
|
MatchRouteOptions & {
|
|
102
92
|
// 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
|
|
103
93
|
children?:
|
|
104
|
-
|
|
|
94
|
+
| ReactNode
|
|
105
95
|
| ((
|
|
106
96
|
params: RouteInfoByPath<
|
|
107
|
-
|
|
97
|
+
RegisteredAllRouteInfo,
|
|
108
98
|
ResolveRelativePath<TFrom, NoInfer<TTo>>
|
|
109
99
|
>['allParams'],
|
|
110
|
-
) =>
|
|
100
|
+
) => ReactNode)
|
|
111
101
|
}
|
|
112
102
|
|
|
113
103
|
export type MakeLinkPropsOptions<
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
> = LinkPropsOptions<TAllRouteInfo, TFrom, TTo> &
|
|
118
|
-
React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
104
|
+
TFrom extends ValidFromPath<RegisteredAllRouteInfo> = '/',
|
|
105
|
+
TTo extends string = '.',
|
|
106
|
+
> = LinkPropsOptions<TFrom, TTo> & React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
119
107
|
|
|
120
108
|
export type MakeLinkOptions<
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
> = LinkPropsOptions<TAllRouteInfo, TFrom, TTo> &
|
|
109
|
+
TFrom extends RegisteredAllRouteInfo['routePaths'] = '/',
|
|
110
|
+
TTo extends string = '.',
|
|
111
|
+
> = LinkPropsOptions<TFrom, TTo> &
|
|
125
112
|
React.AnchorHTMLAttributes<HTMLAnchorElement> &
|
|
126
113
|
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
|
|
127
114
|
// 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
|
|
128
|
-
children?:
|
|
129
|
-
| React.ReactNode
|
|
130
|
-
| ((state: { isActive: boolean }) => React.ReactNode)
|
|
115
|
+
children?: ReactNode | ((state: { isActive: boolean }) => ReactNode)
|
|
131
116
|
}
|
|
132
117
|
|
|
133
118
|
declare module '@tanstack/router-core' {
|
|
@@ -140,84 +125,160 @@ declare module '@tanstack/router-core' {
|
|
|
140
125
|
}
|
|
141
126
|
|
|
142
127
|
interface RouterOptions<TRouteConfig, TRouterContext> {
|
|
143
|
-
// ssrFooter?: () => JSX.Element |
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
interface Router<
|
|
147
|
-
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
148
|
-
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
149
|
-
> {
|
|
150
|
-
useState: () => RouterState
|
|
151
|
-
useRoute: <TId extends keyof TAllRouteInfo['routeInfoById']>(
|
|
152
|
-
routeId: TId,
|
|
153
|
-
) => Route<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
|
|
154
|
-
useMatch: <
|
|
155
|
-
TId extends keyof TAllRouteInfo['routeInfoById'],
|
|
156
|
-
TStrict extends boolean = true,
|
|
157
|
-
>(
|
|
158
|
-
routeId: TId,
|
|
159
|
-
opts?: { strict?: TStrict },
|
|
160
|
-
) => TStrict extends true
|
|
161
|
-
? RouteMatch<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
|
|
162
|
-
:
|
|
163
|
-
| RouteMatch<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
|
|
164
|
-
| undefined
|
|
165
|
-
linkProps: <TTo extends string = '.'>(
|
|
166
|
-
props: MakeLinkPropsOptions<TAllRouteInfo, '/', TTo>,
|
|
167
|
-
) => React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
168
|
-
Link: <TTo extends string = '.'>(
|
|
169
|
-
props: MakeLinkOptions<TAllRouteInfo, '/', TTo>,
|
|
170
|
-
) => JSX.Element
|
|
171
|
-
MatchRoute: <TTo extends string = '.'>(
|
|
172
|
-
props: MakeMatchRouteOptions<TAllRouteInfo, '/', TTo>,
|
|
173
|
-
) => JSX.Element
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
interface Route<
|
|
177
|
-
TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
|
|
178
|
-
TRouteInfo extends AnyRouteInfo = RouteInfo,
|
|
179
|
-
> {
|
|
180
|
-
useRoute: <
|
|
181
|
-
TTo extends string = '.',
|
|
182
|
-
TResolved extends string = ResolveRelativePath<
|
|
183
|
-
TRouteInfo['id'],
|
|
184
|
-
NoInfer<TTo>
|
|
185
|
-
>,
|
|
186
|
-
>(
|
|
187
|
-
routeId: CheckId<
|
|
188
|
-
TAllRouteInfo,
|
|
189
|
-
TResolved,
|
|
190
|
-
ToIdOption<TAllRouteInfo, TRouteInfo['id'], TTo>
|
|
191
|
-
>,
|
|
192
|
-
opts?: { strict?: boolean },
|
|
193
|
-
) => Route<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TResolved]>
|
|
194
|
-
linkProps: <TTo extends string = '.'>(
|
|
195
|
-
props: MakeLinkPropsOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo>,
|
|
196
|
-
) => React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
197
|
-
Link: <TTo extends string = '.'>(
|
|
198
|
-
props: MakeLinkOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo>,
|
|
199
|
-
) => JSX.Element
|
|
200
|
-
MatchRoute: <TTo extends string = '.'>(
|
|
201
|
-
props: MakeMatchRouteOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo>,
|
|
202
|
-
) => JSX.Element
|
|
128
|
+
// ssrFooter?: () => JSX.Element | Node
|
|
203
129
|
}
|
|
204
130
|
}
|
|
205
131
|
|
|
206
132
|
export type PromptProps = {
|
|
207
133
|
message: string
|
|
208
134
|
when?: boolean | any
|
|
209
|
-
children?:
|
|
135
|
+
children?: ReactNode
|
|
210
136
|
}
|
|
211
137
|
|
|
212
138
|
//
|
|
213
139
|
|
|
214
|
-
export function
|
|
215
|
-
|
|
216
|
-
|
|
140
|
+
export function useLinkProps<
|
|
141
|
+
TFrom extends ValidFromPath<RegisteredAllRouteInfo> = '/',
|
|
142
|
+
TTo extends string = '.',
|
|
143
|
+
>(
|
|
144
|
+
options: MakeLinkPropsOptions<TFrom, TTo>,
|
|
145
|
+
): React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
217
146
|
const router = useRouter()
|
|
218
|
-
|
|
147
|
+
|
|
148
|
+
const {
|
|
149
|
+
// custom props
|
|
150
|
+
type,
|
|
151
|
+
children,
|
|
152
|
+
target,
|
|
153
|
+
activeProps = () => ({ className: 'active' }),
|
|
154
|
+
inactiveProps = () => ({}),
|
|
155
|
+
activeOptions,
|
|
156
|
+
disabled,
|
|
157
|
+
// fromCurrent,
|
|
158
|
+
hash,
|
|
159
|
+
search,
|
|
160
|
+
params,
|
|
161
|
+
to,
|
|
162
|
+
preload,
|
|
163
|
+
preloadDelay,
|
|
164
|
+
preloadMaxAge,
|
|
165
|
+
replace,
|
|
166
|
+
// element props
|
|
167
|
+
style,
|
|
168
|
+
className,
|
|
169
|
+
onClick,
|
|
170
|
+
onFocus,
|
|
171
|
+
onMouseEnter,
|
|
172
|
+
onMouseLeave,
|
|
173
|
+
onTouchStart,
|
|
174
|
+
onTouchEnd,
|
|
175
|
+
...rest
|
|
176
|
+
} = options
|
|
177
|
+
|
|
178
|
+
const linkInfo = router.buildLink(options as any)
|
|
179
|
+
|
|
180
|
+
if (linkInfo.type === 'external') {
|
|
181
|
+
const { href } = linkInfo
|
|
182
|
+
return { href }
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const { handleClick, handleFocus, handleEnter, handleLeave, isActive, next } =
|
|
186
|
+
linkInfo
|
|
187
|
+
|
|
188
|
+
const reactHandleClick = (e: Event) => {
|
|
189
|
+
if (React.startTransition) {
|
|
190
|
+
// This is a hack for react < 18
|
|
191
|
+
React.startTransition(() => {
|
|
192
|
+
handleClick(e)
|
|
193
|
+
})
|
|
194
|
+
} else {
|
|
195
|
+
handleClick(e)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const composeHandlers =
|
|
200
|
+
(handlers: (undefined | ((e: any) => void))[]) =>
|
|
201
|
+
(e: React.SyntheticEvent) => {
|
|
202
|
+
if (e.persist) e.persist()
|
|
203
|
+
handlers.filter(Boolean).forEach((handler) => {
|
|
204
|
+
if (e.defaultPrevented) return
|
|
205
|
+
handler!(e)
|
|
206
|
+
})
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Get the active props
|
|
210
|
+
const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> = isActive
|
|
211
|
+
? functionalUpdate(activeProps, {}) ?? {}
|
|
212
|
+
: {}
|
|
213
|
+
|
|
214
|
+
// Get the inactive props
|
|
215
|
+
const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =
|
|
216
|
+
isActive ? {} : functionalUpdate(inactiveProps, {}) ?? {}
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
...resolvedActiveProps,
|
|
220
|
+
...resolvedInactiveProps,
|
|
221
|
+
...rest,
|
|
222
|
+
href: disabled ? undefined : next.href,
|
|
223
|
+
onClick: composeHandlers([onClick, reactHandleClick]),
|
|
224
|
+
onFocus: composeHandlers([onFocus, handleFocus]),
|
|
225
|
+
onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),
|
|
226
|
+
onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),
|
|
227
|
+
target,
|
|
228
|
+
style: {
|
|
229
|
+
...style,
|
|
230
|
+
...resolvedActiveProps.style,
|
|
231
|
+
...resolvedInactiveProps.style,
|
|
232
|
+
},
|
|
233
|
+
className:
|
|
234
|
+
[
|
|
235
|
+
className,
|
|
236
|
+
resolvedActiveProps.className,
|
|
237
|
+
resolvedInactiveProps.className,
|
|
238
|
+
]
|
|
239
|
+
.filter(Boolean)
|
|
240
|
+
.join(' ') || undefined,
|
|
241
|
+
...(disabled
|
|
242
|
+
? {
|
|
243
|
+
role: 'link',
|
|
244
|
+
'aria-disabled': true,
|
|
245
|
+
}
|
|
246
|
+
: undefined),
|
|
247
|
+
['data-status']: isActive ? 'active' : undefined,
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export interface LinkFn<
|
|
252
|
+
TDefaultFrom extends RegisteredAllRouteInfo['routePaths'] = '/',
|
|
253
|
+
TDefaultTo extends string = '.',
|
|
254
|
+
> {
|
|
255
|
+
<
|
|
256
|
+
TFrom extends RegisteredAllRouteInfo['routePaths'] = TDefaultFrom,
|
|
257
|
+
TTo extends string = TDefaultTo,
|
|
258
|
+
>(
|
|
259
|
+
props: MakeLinkOptions<TFrom, TTo>,
|
|
260
|
+
): ReactNode
|
|
219
261
|
}
|
|
220
262
|
|
|
263
|
+
export const Link: LinkFn = React.forwardRef((props: any, ref) => {
|
|
264
|
+
const linkProps = useLinkProps(props)
|
|
265
|
+
|
|
266
|
+
return (
|
|
267
|
+
<a
|
|
268
|
+
{...{
|
|
269
|
+
ref: ref as any,
|
|
270
|
+
...linkProps,
|
|
271
|
+
children:
|
|
272
|
+
typeof props.children === 'function'
|
|
273
|
+
? props.children({
|
|
274
|
+
isActive: (linkProps as any)['data-status'] === 'active',
|
|
275
|
+
})
|
|
276
|
+
: props.children,
|
|
277
|
+
}}
|
|
278
|
+
/>
|
|
279
|
+
)
|
|
280
|
+
}) as any
|
|
281
|
+
|
|
221
282
|
type MatchesContextValue = RouteMatch[]
|
|
222
283
|
|
|
223
284
|
export const matchesContext = React.createContext<MatchesContextValue>(null!)
|
|
@@ -227,246 +288,85 @@ export const routerContext = React.createContext<{ router: RegisteredRouter }>(
|
|
|
227
288
|
|
|
228
289
|
export type MatchesProviderProps = {
|
|
229
290
|
value: MatchesContextValue
|
|
230
|
-
children:
|
|
291
|
+
children: ReactNode
|
|
231
292
|
}
|
|
232
293
|
|
|
233
|
-
|
|
234
|
-
return <matchesContext.Provider {...props} />
|
|
235
|
-
}
|
|
294
|
+
const EMPTY = {}
|
|
236
295
|
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
)
|
|
243
|
-
}
|
|
296
|
+
export const __useStoreValue = <TSeed, TReturn>(
|
|
297
|
+
seed: () => TSeed,
|
|
298
|
+
selector?: (seed: TSeed) => TReturn,
|
|
299
|
+
): TReturn => {
|
|
300
|
+
const valueRef = React.useRef<TReturn>(EMPTY as any)
|
|
244
301
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
>(
|
|
250
|
-
opts: RouterOptions<TRouteConfig, TRouterContext>,
|
|
251
|
-
): Router<TRouteConfig, TAllRouteInfo, TRouterContext> {
|
|
252
|
-
const makeRouteExt = (
|
|
253
|
-
route: AnyRoute,
|
|
254
|
-
router: Router<any, any, any>,
|
|
255
|
-
): Pick<AnyRoute, 'useRoute' | 'linkProps' | 'Link' | 'MatchRoute'> => {
|
|
256
|
-
return {
|
|
257
|
-
useRoute: (subRouteId = '.' as any) => {
|
|
258
|
-
const resolvedRouteId = router.resolvePath(
|
|
259
|
-
route.routeId,
|
|
260
|
-
subRouteId as string,
|
|
261
|
-
)
|
|
262
|
-
const resolvedRoute = router.getRoute(resolvedRouteId)
|
|
263
|
-
useRouterSubscription(router)
|
|
264
|
-
invariant(
|
|
265
|
-
resolvedRoute,
|
|
266
|
-
`Could not find a route for route "${
|
|
267
|
-
resolvedRouteId as string
|
|
268
|
-
}"! Did you forget to add it to your route config?`,
|
|
269
|
-
)
|
|
270
|
-
return resolvedRoute
|
|
271
|
-
},
|
|
272
|
-
linkProps: (options) => {
|
|
273
|
-
const {
|
|
274
|
-
// custom props
|
|
275
|
-
type,
|
|
276
|
-
children,
|
|
277
|
-
target,
|
|
278
|
-
activeProps = () => ({ className: 'active' }),
|
|
279
|
-
inactiveProps = () => ({}),
|
|
280
|
-
activeOptions,
|
|
281
|
-
disabled,
|
|
282
|
-
// fromCurrent,
|
|
283
|
-
hash,
|
|
284
|
-
search,
|
|
285
|
-
params,
|
|
286
|
-
to,
|
|
287
|
-
preload,
|
|
288
|
-
preloadDelay,
|
|
289
|
-
preloadMaxAge,
|
|
290
|
-
replace,
|
|
291
|
-
// element props
|
|
292
|
-
style,
|
|
293
|
-
className,
|
|
294
|
-
onClick,
|
|
295
|
-
onFocus,
|
|
296
|
-
onMouseEnter,
|
|
297
|
-
onMouseLeave,
|
|
298
|
-
onTouchStart,
|
|
299
|
-
onTouchEnd,
|
|
300
|
-
...rest
|
|
301
|
-
} = options
|
|
302
|
-
|
|
303
|
-
const linkInfo = route.buildLink(options as any)
|
|
304
|
-
|
|
305
|
-
if (linkInfo.type === 'external') {
|
|
306
|
-
const { href } = linkInfo
|
|
307
|
-
return { href }
|
|
308
|
-
}
|
|
302
|
+
// If there is no selector, track the seed
|
|
303
|
+
// If there is a selector, do not track the seed
|
|
304
|
+
const getValue = () =>
|
|
305
|
+
(!selector ? seed() : selector(untrack(() => seed()))) as TReturn
|
|
309
306
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
handleLeave,
|
|
315
|
-
isActive,
|
|
316
|
-
next,
|
|
317
|
-
} = linkInfo
|
|
318
|
-
|
|
319
|
-
const reactHandleClick = (e: Event) => {
|
|
320
|
-
if (React.startTransition)
|
|
321
|
-
// This is a hack for react < 18
|
|
322
|
-
React.startTransition(() => {
|
|
323
|
-
handleClick(e)
|
|
324
|
-
})
|
|
325
|
-
else handleClick(e)
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
const composeHandlers =
|
|
329
|
-
(handlers: (undefined | ((e: any) => void))[]) =>
|
|
330
|
-
(e: React.SyntheticEvent) => {
|
|
331
|
-
if (e.persist) e.persist()
|
|
332
|
-
handlers.forEach((handler) => {
|
|
333
|
-
if (e.defaultPrevented) return
|
|
334
|
-
if (handler) handler(e)
|
|
335
|
-
})
|
|
336
|
-
}
|
|
307
|
+
// If empty, initialize the value
|
|
308
|
+
if (valueRef.current === EMPTY) {
|
|
309
|
+
valueRef.current = sharedClone(undefined, getValue())
|
|
310
|
+
}
|
|
337
311
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
onMouseLeave: composeHandlers([handleLeave, onMouseLeave]),
|
|
355
|
-
target,
|
|
356
|
-
style: {
|
|
357
|
-
...style,
|
|
358
|
-
...resolvedActiveProps.style,
|
|
359
|
-
...resolvedInactiveProps.style,
|
|
360
|
-
},
|
|
361
|
-
className:
|
|
362
|
-
[
|
|
363
|
-
className,
|
|
364
|
-
resolvedActiveProps.className,
|
|
365
|
-
resolvedInactiveProps.className,
|
|
366
|
-
]
|
|
367
|
-
.filter(Boolean)
|
|
368
|
-
.join(' ') || undefined,
|
|
369
|
-
...(disabled
|
|
370
|
-
? {
|
|
371
|
-
role: 'link',
|
|
372
|
-
'aria-disabled': true,
|
|
373
|
-
}
|
|
374
|
-
: undefined),
|
|
375
|
-
['data-status']: isActive ? 'active' : undefined,
|
|
376
|
-
}
|
|
377
|
-
},
|
|
378
|
-
Link: React.forwardRef((props: any, ref) => {
|
|
379
|
-
const linkProps = route.linkProps(props)
|
|
380
|
-
|
|
381
|
-
useRouterSubscription(router)
|
|
382
|
-
|
|
383
|
-
return (
|
|
384
|
-
<a
|
|
385
|
-
{...{
|
|
386
|
-
ref: ref as any,
|
|
387
|
-
...linkProps,
|
|
388
|
-
children:
|
|
389
|
-
typeof props.children === 'function'
|
|
390
|
-
? props.children({
|
|
391
|
-
isActive: (linkProps as any)['data-status'] === 'active',
|
|
392
|
-
})
|
|
393
|
-
: props.children,
|
|
394
|
-
}}
|
|
395
|
-
/>
|
|
312
|
+
// Snapshot should just return the current cached value
|
|
313
|
+
const getSnapshot = React.useCallback(() => valueRef.current, [])
|
|
314
|
+
|
|
315
|
+
const getStore = React.useCallback((cb: () => void) => {
|
|
316
|
+
// A root is necessary to track effects
|
|
317
|
+
return createRoot(() => {
|
|
318
|
+
createEffect(() => {
|
|
319
|
+
// Read and update the value
|
|
320
|
+
// getValue will handle which values are accessed and
|
|
321
|
+
// thus tracked.
|
|
322
|
+
// sharedClone will both recursively track the end result
|
|
323
|
+
// and ensure that the previous value is structurally shared
|
|
324
|
+
// into the new version.
|
|
325
|
+
valueRef.current = unwrap(
|
|
326
|
+
// Unwrap the value to get rid of any proxy structures
|
|
327
|
+
sharedClone(valueRef.current, getValue()),
|
|
396
328
|
)
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const params = route.matchRoute(rest as any, {
|
|
402
|
-
pending,
|
|
403
|
-
caseSensitive,
|
|
404
|
-
})
|
|
329
|
+
cb()
|
|
330
|
+
})
|
|
331
|
+
})
|
|
332
|
+
}, [])
|
|
405
333
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
}
|
|
334
|
+
return useSyncExternalStore(getStore, getSnapshot, getSnapshot)
|
|
335
|
+
}
|
|
409
336
|
|
|
410
|
-
|
|
411
|
-
? opts.children(params as any)
|
|
412
|
-
: (opts.children as any)
|
|
413
|
-
},
|
|
414
|
-
}
|
|
415
|
-
}
|
|
337
|
+
const [store, setStore] = createStore({ foo: 'foo', bar: { baz: 'baz' } })
|
|
416
338
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
createRouter: (router) => {
|
|
420
|
-
const routerExt: Pick<Router<any, any, any>, 'useMatch' | 'useState'> = {
|
|
421
|
-
useState: () => {
|
|
422
|
-
useRouterSubscription(router)
|
|
423
|
-
return router.state
|
|
424
|
-
},
|
|
425
|
-
useMatch: (routeId, opts) => {
|
|
426
|
-
useRouterSubscription(router)
|
|
427
|
-
|
|
428
|
-
const nearestMatch = useNearestMatch()
|
|
429
|
-
const match = router.state.currentMatches.find(
|
|
430
|
-
(d) => d.routeId === routeId,
|
|
431
|
-
)
|
|
432
|
-
|
|
433
|
-
if (opts?.strict ?? true) {
|
|
434
|
-
invariant(
|
|
435
|
-
match,
|
|
436
|
-
`Could not find an active match for "${routeId as string}"!`,
|
|
437
|
-
)
|
|
438
|
-
|
|
439
|
-
invariant(
|
|
440
|
-
nearestMatch.routeId == match?.routeId,
|
|
441
|
-
`useMatch("${
|
|
442
|
-
match?.routeId as string
|
|
443
|
-
}") is being called in a component that is meant to render the '${
|
|
444
|
-
nearestMatch.routeId
|
|
445
|
-
}' route. Did you mean to 'useMatch("${
|
|
446
|
-
match?.routeId as string
|
|
447
|
-
}", { strict: false })' or 'useRoute("${
|
|
448
|
-
match?.routeId as string
|
|
449
|
-
}")' instead?`,
|
|
450
|
-
)
|
|
451
|
-
}
|
|
339
|
+
createRoot(() => {
|
|
340
|
+
let prev: any
|
|
452
341
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
342
|
+
createEffect(() => {
|
|
343
|
+
console.log('effect')
|
|
344
|
+
const next = sharedClone(prev, store)
|
|
345
|
+
console.log(next)
|
|
346
|
+
prev = untrack(() => next)
|
|
347
|
+
})
|
|
348
|
+
})
|
|
456
349
|
|
|
457
|
-
|
|
350
|
+
setStore((s) => {
|
|
351
|
+
s.foo = '1'
|
|
352
|
+
})
|
|
458
353
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
const routeExt = makeRouteExt(route, router)
|
|
354
|
+
setStore((s) => {
|
|
355
|
+
s.bar.baz = '2'
|
|
356
|
+
})
|
|
463
357
|
|
|
464
|
-
|
|
465
|
-
|
|
358
|
+
export function createReactRouter<
|
|
359
|
+
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
360
|
+
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
361
|
+
TRouterContext = unknown,
|
|
362
|
+
>(
|
|
363
|
+
opts: RouterOptions<TRouteConfig, TRouterContext>,
|
|
364
|
+
): Router<TRouteConfig, TAllRouteInfo, TRouterContext> {
|
|
365
|
+
const coreRouter = createRouter<TRouteConfig>({
|
|
366
|
+
...opts,
|
|
466
367
|
loadComponent: async (component) => {
|
|
467
|
-
if (component.preload
|
|
468
|
-
component.preload()
|
|
469
|
-
// return await component.preload()
|
|
368
|
+
if (component.preload) {
|
|
369
|
+
await component.preload()
|
|
470
370
|
}
|
|
471
371
|
|
|
472
372
|
return component as any
|
|
@@ -494,17 +394,21 @@ export function RouterProvider<
|
|
|
494
394
|
}: RouterProps<TRouteConfig, TAllRouteInfo, TRouterContext>) {
|
|
495
395
|
router.update(rest)
|
|
496
396
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
397
|
+
const [, , currentMatches] = __useStoreValue(
|
|
398
|
+
() => router.store,
|
|
399
|
+
(s) => [s.status, s.pendingMatches, s.currentMatches],
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
React.useEffect(router.mount, [router])
|
|
403
|
+
|
|
404
|
+
console.log('current', currentMatches)
|
|
501
405
|
|
|
502
406
|
return (
|
|
503
407
|
<>
|
|
504
408
|
<routerContext.Provider value={{ router: router as any }}>
|
|
505
|
-
<
|
|
409
|
+
<matchesContext.Provider value={[undefined!, ...currentMatches]}>
|
|
506
410
|
<Outlet />
|
|
507
|
-
</
|
|
411
|
+
</matchesContext.Provider>
|
|
508
412
|
</routerContext.Provider>
|
|
509
413
|
</>
|
|
510
414
|
)
|
|
@@ -513,77 +417,191 @@ export function RouterProvider<
|
|
|
513
417
|
export function useRouter(): RegisteredRouter {
|
|
514
418
|
const value = React.useContext(routerContext)
|
|
515
419
|
warning(!value, 'useRouter must be used inside a <Router> component!')
|
|
516
|
-
|
|
517
|
-
useRouterSubscription(value.router)
|
|
518
|
-
|
|
519
420
|
return value.router
|
|
520
421
|
}
|
|
521
422
|
|
|
423
|
+
export function useRouterStore<T = RouterStore>(
|
|
424
|
+
selector?: (state: Router['store']) => T,
|
|
425
|
+
): T {
|
|
426
|
+
const router = useRouter()
|
|
427
|
+
return __useStoreValue(() => router.store, selector)
|
|
428
|
+
}
|
|
429
|
+
|
|
522
430
|
export function useMatches(): RouteMatch[] {
|
|
523
431
|
return React.useContext(matchesContext)
|
|
524
432
|
}
|
|
525
433
|
|
|
526
434
|
export function useMatch<
|
|
527
|
-
|
|
435
|
+
TFrom extends keyof RegisteredAllRouteInfo['routeInfoById'],
|
|
528
436
|
TStrict extends boolean = true,
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
:
|
|
538
|
-
|
|
539
|
-
RegisteredAllRouteInfo,
|
|
540
|
-
RegisteredAllRouteInfo['routeInfoById'][TId]
|
|
541
|
-
>
|
|
542
|
-
| undefined {
|
|
437
|
+
TRouteMatch = RouteMatch<
|
|
438
|
+
RegisteredAllRouteInfo,
|
|
439
|
+
RegisteredAllRouteInfo['routeInfoById'][TFrom]
|
|
440
|
+
>,
|
|
441
|
+
// TSelected = TRouteMatch,
|
|
442
|
+
>(opts?: {
|
|
443
|
+
from: TFrom
|
|
444
|
+
strict?: TStrict
|
|
445
|
+
// select?: (match: TRouteMatch) => TSelected
|
|
446
|
+
}): TStrict extends true ? TRouteMatch : TRouteMatch | undefined {
|
|
543
447
|
const router = useRouter()
|
|
544
|
-
|
|
545
|
-
|
|
448
|
+
const nearestMatch = useMatches()[0]!
|
|
449
|
+
const match = opts?.from
|
|
450
|
+
? router.store.currentMatches.find((d) => d.routeId === opts?.from)
|
|
451
|
+
: nearestMatch
|
|
452
|
+
|
|
453
|
+
invariant(
|
|
454
|
+
match,
|
|
455
|
+
`Could not find ${
|
|
456
|
+
opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'
|
|
457
|
+
}`,
|
|
458
|
+
)
|
|
546
459
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
460
|
+
if (opts?.strict ?? true) {
|
|
461
|
+
invariant(
|
|
462
|
+
nearestMatch.routeId == match?.routeId,
|
|
463
|
+
`useMatch("${
|
|
464
|
+
match?.routeId as string
|
|
465
|
+
}") is being called in a component that is meant to render the '${
|
|
466
|
+
nearestMatch.routeId
|
|
467
|
+
}' route. Did you mean to 'useMatch("${
|
|
468
|
+
match?.routeId as string
|
|
469
|
+
}", { strict: false })' or 'useRoute("${
|
|
470
|
+
match?.routeId as string
|
|
471
|
+
}")' instead?`,
|
|
472
|
+
)
|
|
473
|
+
}
|
|
552
474
|
|
|
553
|
-
|
|
475
|
+
__useStoreValue(() => match!.store)
|
|
554
476
|
|
|
555
|
-
return
|
|
477
|
+
return match as any
|
|
556
478
|
}
|
|
557
479
|
|
|
558
480
|
export function useRoute<
|
|
559
|
-
TId extends keyof RegisteredAllRouteInfo['routeInfoById'],
|
|
481
|
+
TId extends keyof RegisteredAllRouteInfo['routeInfoById'] = '/',
|
|
560
482
|
>(
|
|
561
483
|
routeId: TId,
|
|
562
484
|
): Route<RegisteredAllRouteInfo, RegisteredAllRouteInfo['routeInfoById'][TId]> {
|
|
563
485
|
const router = useRouter()
|
|
564
|
-
|
|
486
|
+
const resolvedRoute = router.getRoute(routeId as any)
|
|
487
|
+
|
|
488
|
+
invariant(
|
|
489
|
+
resolvedRoute,
|
|
490
|
+
`Could not find a route for route "${
|
|
491
|
+
routeId as string
|
|
492
|
+
}"! Did you forget to add it to your route config?`,
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
return resolvedRoute as any
|
|
565
496
|
}
|
|
566
497
|
|
|
567
|
-
export function
|
|
568
|
-
|
|
498
|
+
export function useLoaderData<
|
|
499
|
+
TFrom extends keyof RegisteredAllRouteInfo['routeInfoById'] = '/',
|
|
500
|
+
TStrict extends boolean = true,
|
|
501
|
+
TLoaderData = RegisteredAllRouteInfo['routeInfoById'][TFrom]['loaderData'],
|
|
502
|
+
TSelected = TLoaderData,
|
|
503
|
+
>(opts?: {
|
|
504
|
+
from: TFrom
|
|
505
|
+
strict?: TStrict
|
|
506
|
+
select?: (loaderData: TLoaderData) => TSelected
|
|
507
|
+
}): TStrict extends true ? TSelected : TSelected | undefined {
|
|
508
|
+
const match = useMatch(opts) as any
|
|
509
|
+
return __useStoreValue(() => match?.store.loaderData, opts?.select)
|
|
569
510
|
}
|
|
570
511
|
|
|
571
|
-
export function
|
|
572
|
-
|
|
512
|
+
export function useSearch<
|
|
513
|
+
TFrom extends keyof RegisteredAllRouteInfo['routeInfoById'],
|
|
514
|
+
TStrict extends boolean = true,
|
|
515
|
+
TSearch = RegisteredAllRouteInfo['routeInfoById'][TFrom]['fullSearchSchema'],
|
|
516
|
+
TSelected = TSearch,
|
|
517
|
+
>(opts?: {
|
|
518
|
+
from: TFrom
|
|
519
|
+
strict?: TStrict
|
|
520
|
+
select?: (search: TSearch) => TSelected
|
|
521
|
+
}): TStrict extends true ? TSelected : TSelected | undefined {
|
|
522
|
+
const match = useMatch(opts)
|
|
523
|
+
return __useStoreValue(() => match?.store.search, opts?.select) as any
|
|
573
524
|
}
|
|
574
525
|
|
|
575
|
-
export function
|
|
576
|
-
|
|
577
|
-
|
|
526
|
+
export function useParams<
|
|
527
|
+
TFrom extends keyof RegisteredAllRouteInfo['routeInfoById'] = '/',
|
|
528
|
+
TDefaultSelected = Expand<
|
|
529
|
+
RegisteredAllRouteInfo['allParams'] &
|
|
530
|
+
RegisteredAllRouteInfo['routeInfoById'][TFrom]['allParams']
|
|
531
|
+
>,
|
|
532
|
+
TSelected = TDefaultSelected,
|
|
533
|
+
>(opts?: {
|
|
534
|
+
from: TFrom
|
|
535
|
+
select?: (search: TDefaultSelected) => TSelected
|
|
536
|
+
}): TSelected {
|
|
578
537
|
const router = useRouter()
|
|
579
|
-
return
|
|
538
|
+
return __useStoreValue(
|
|
539
|
+
() => last(router.store.currentMatches)?.params as any,
|
|
540
|
+
opts?.select,
|
|
541
|
+
)
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
export function useNavigate<
|
|
545
|
+
TDefaultFrom extends keyof RegisteredAllRouteInfo['routeInfoById'] = '/',
|
|
546
|
+
>(defaultOpts: { from?: TDefaultFrom }) {
|
|
547
|
+
return <
|
|
548
|
+
TFrom extends keyof RegisteredAllRouteInfo['routeInfoById'] = TDefaultFrom,
|
|
549
|
+
TTo extends string = '.',
|
|
550
|
+
>(
|
|
551
|
+
opts: MakeLinkOptions<TFrom, TTo>,
|
|
552
|
+
) => {
|
|
553
|
+
const router = useRouter()
|
|
554
|
+
return router.navigate({ ...defaultOpts, ...(opts as any) })
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
export function useAction<
|
|
559
|
+
TFrom extends keyof RegisteredAllRouteInfo['routeInfoById'] = '/',
|
|
560
|
+
TFromRoute extends RegisteredAllRouteInfo['routeInfoById'][TFrom] = RegisteredAllRouteInfo['routeInfoById'][TFrom],
|
|
561
|
+
>(opts: {
|
|
562
|
+
from: TFrom
|
|
563
|
+
}): Action<TFromRoute['actionPayload'], TFromRoute['actionResponse']> {
|
|
564
|
+
const route = useRoute(opts.from)
|
|
565
|
+
const action = route.action
|
|
566
|
+
__useStoreValue(() => action)
|
|
567
|
+
return action as any
|
|
580
568
|
}
|
|
581
569
|
|
|
582
|
-
export function
|
|
583
|
-
props: MakeMatchRouteOptions<RegisteredAllRouteInfo, '/', TTo>,
|
|
584
|
-
): JSX.Element {
|
|
570
|
+
export function useMatchRoute() {
|
|
585
571
|
const router = useRouter()
|
|
586
|
-
|
|
572
|
+
|
|
573
|
+
return <
|
|
574
|
+
TFrom extends ValidFromPath<RegisteredAllRouteInfo> = '/',
|
|
575
|
+
TTo extends string = '.',
|
|
576
|
+
>(
|
|
577
|
+
opts: MakeUseMatchRouteOptions<TFrom, TTo>,
|
|
578
|
+
) => {
|
|
579
|
+
const { pending, caseSensitive, ...rest } = opts
|
|
580
|
+
|
|
581
|
+
return router.matchRoute(rest as any, {
|
|
582
|
+
pending,
|
|
583
|
+
caseSensitive,
|
|
584
|
+
})
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
export function MatchRoute<
|
|
589
|
+
TFrom extends ValidFromPath<RegisteredAllRouteInfo> = '/',
|
|
590
|
+
TTo extends string = '.',
|
|
591
|
+
>(props: MakeMatchRouteOptions<TFrom, TTo>): any {
|
|
592
|
+
const matchRoute = useMatchRoute()
|
|
593
|
+
const params = matchRoute(props)
|
|
594
|
+
|
|
595
|
+
if (!params) {
|
|
596
|
+
return null
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
return React.createElement(
|
|
600
|
+
typeof props.children === 'function'
|
|
601
|
+
? (props.children as any)(params)
|
|
602
|
+
: props.children,
|
|
603
|
+
props as any,
|
|
604
|
+
)
|
|
587
605
|
}
|
|
588
606
|
|
|
589
607
|
export function Outlet() {
|
|
@@ -593,6 +611,31 @@ export function Outlet() {
|
|
|
593
611
|
|
|
594
612
|
const defaultPending = React.useCallback(() => null, [])
|
|
595
613
|
|
|
614
|
+
__useStoreValue(() => match?.store)
|
|
615
|
+
|
|
616
|
+
const Inner = React.useCallback((props: { match: RouteMatch }): any => {
|
|
617
|
+
if (props.match.store.status === 'error') {
|
|
618
|
+
throw props.match.store.error
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (props.match.store.status === 'success') {
|
|
622
|
+
return React.createElement(
|
|
623
|
+
(props.match.__.component as any) ??
|
|
624
|
+
router.options.defaultComponent ??
|
|
625
|
+
Outlet,
|
|
626
|
+
)
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (props.match.store.status === 'loading') {
|
|
630
|
+
throw props.match.__.loadPromise
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
invariant(
|
|
634
|
+
false,
|
|
635
|
+
'Idle routeMatch status encountered during rendering! You should never see this. File an issue!',
|
|
636
|
+
)
|
|
637
|
+
}, [])
|
|
638
|
+
|
|
596
639
|
if (!match) {
|
|
597
640
|
return null
|
|
598
641
|
}
|
|
@@ -605,29 +648,14 @@ export function Outlet() {
|
|
|
605
648
|
match.__.errorComponent ?? router.options.defaultErrorComponent
|
|
606
649
|
|
|
607
650
|
return (
|
|
608
|
-
<
|
|
651
|
+
<matchesContext.Provider value={matches}>
|
|
609
652
|
<React.Suspense fallback={<PendingComponent />}>
|
|
610
653
|
<CatchBoundary
|
|
611
654
|
key={match.routeId}
|
|
612
655
|
errorComponent={errorComponent}
|
|
613
656
|
match={match as any}
|
|
614
657
|
>
|
|
615
|
-
{
|
|
616
|
-
((): React.ReactNode => {
|
|
617
|
-
if (match.status === 'error') {
|
|
618
|
-
throw match.error
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
if (match.status === 'success') {
|
|
622
|
-
return React.createElement(
|
|
623
|
-
(match.__.component as any) ??
|
|
624
|
-
router.options.defaultComponent ??
|
|
625
|
-
Outlet,
|
|
626
|
-
)
|
|
627
|
-
}
|
|
628
|
-
throw match.__.loadPromise
|
|
629
|
-
})() as JSX.Element
|
|
630
|
-
}
|
|
658
|
+
<Inner match={match} />
|
|
631
659
|
</CatchBoundary>
|
|
632
660
|
</React.Suspense>
|
|
633
661
|
{/* Provide a suffix suspense boundary to make sure the router is
|
|
@@ -635,7 +663,7 @@ export function Outlet() {
|
|
|
635
663
|
{/* {router.options.ssrFooter && match.matchId === rootRouteId ? (
|
|
636
664
|
<React.Suspense fallback={null}>
|
|
637
665
|
{(() => {
|
|
638
|
-
if (router.
|
|
666
|
+
if (router.store.pending) {
|
|
639
667
|
throw router.navigationPromise
|
|
640
668
|
}
|
|
641
669
|
|
|
@@ -643,7 +671,7 @@ export function Outlet() {
|
|
|
643
671
|
})()}
|
|
644
672
|
</React.Suspense>
|
|
645
673
|
) : null} */}
|
|
646
|
-
</
|
|
674
|
+
</matchesContext.Provider>
|
|
647
675
|
)
|
|
648
676
|
}
|
|
649
677
|
|
|
@@ -695,13 +723,15 @@ function CatchBoundaryInner(props: {
|
|
|
695
723
|
|
|
696
724
|
React.useEffect(() => {
|
|
697
725
|
if (activeErrorState) {
|
|
698
|
-
let prevKey = router.
|
|
699
|
-
return
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
726
|
+
let prevKey = router.store.currentLocation.key
|
|
727
|
+
return createRoot(() =>
|
|
728
|
+
createEffect(() => {
|
|
729
|
+
if (router.store.currentLocation.key !== prevKey) {
|
|
730
|
+
prevKey = router.store.currentLocation.key
|
|
731
|
+
setActiveErrorState({} as any)
|
|
732
|
+
}
|
|
733
|
+
}),
|
|
734
|
+
)
|
|
705
735
|
}
|
|
706
736
|
|
|
707
737
|
return
|
|
@@ -714,7 +744,7 @@ function CatchBoundaryInner(props: {
|
|
|
714
744
|
props.reset()
|
|
715
745
|
}, [props.errorState.error])
|
|
716
746
|
|
|
717
|
-
if (
|
|
747
|
+
if (props.errorState.error) {
|
|
718
748
|
return React.createElement(errorComponent, activeErrorState)
|
|
719
749
|
}
|
|
720
750
|
|
|
@@ -758,7 +788,7 @@ export function usePrompt(message: string, when: boolean | any): void {
|
|
|
758
788
|
unblock()
|
|
759
789
|
transition.retry()
|
|
760
790
|
} else {
|
|
761
|
-
router.
|
|
791
|
+
router.store.currentLocation.pathname = window.location.pathname
|
|
762
792
|
}
|
|
763
793
|
})
|
|
764
794
|
|
|
@@ -768,5 +798,22 @@ export function usePrompt(message: string, when: boolean | any): void {
|
|
|
768
798
|
|
|
769
799
|
export function Prompt({ message, when, children }: PromptProps) {
|
|
770
800
|
usePrompt(message, when ?? true)
|
|
771
|
-
return (children ?? null) as
|
|
801
|
+
return (children ?? null) as ReactNode
|
|
772
802
|
}
|
|
803
|
+
|
|
804
|
+
// function circularStringify(obj: any) {
|
|
805
|
+
// const seen = new Set()
|
|
806
|
+
|
|
807
|
+
// return (
|
|
808
|
+
// JSON.stringify(obj, (_, value) => {
|
|
809
|
+
// if (typeof value === 'function') {
|
|
810
|
+
// return undefined
|
|
811
|
+
// }
|
|
812
|
+
// if (typeof value === 'object' && value !== null) {
|
|
813
|
+
// if (seen.has(value)) return
|
|
814
|
+
// seen.add(value)
|
|
815
|
+
// }
|
|
816
|
+
// return value
|
|
817
|
+
// }) || ''
|
|
818
|
+
// )
|
|
819
|
+
// }
|