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