@tanstack/router-core 0.0.1-beta.185 → 0.0.1-beta.187
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/fileRoute.js.map +1 -1
- package/build/cjs/history.js +20 -18
- package/build/cjs/history.js.map +1 -1
- package/build/cjs/index.js +1 -0
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/route.js +4 -0
- package/build/cjs/route.js.map +1 -1
- package/build/cjs/router.js +211 -101
- package/build/cjs/router.js.map +1 -1
- package/build/cjs/scroll-restoration.js +1 -1
- package/build/cjs/scroll-restoration.js.map +1 -1
- package/build/cjs/utils.js +15 -0
- package/build/cjs/utils.js.map +1 -1
- package/build/esm/index.js +252 -122
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +132 -132
- package/build/types/fileRoute.d.ts +1 -0
- package/build/types/history.d.ts +8 -3
- package/build/types/link.d.ts +23 -18
- package/build/types/route.d.ts +23 -7
- package/build/types/routeInfo.d.ts +3 -3
- package/build/types/router.d.ts +30 -20
- package/build/types/utils.d.ts +2 -11
- package/build/umd/index.development.js +252 -121
- 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 +1 -1
- package/src/fileRoute.ts +3 -3
- package/src/history.ts +33 -25
- package/src/link.ts +66 -51
- package/src/route.ts +61 -16
- package/src/routeInfo.ts +3 -3
- package/src/router.ts +331 -177
- package/src/scroll-restoration.ts +1 -1
- package/src/utils.ts +21 -17
package/src/router.ts
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
RegisteredRouteComponent,
|
|
30
30
|
RegisteredErrorRouteComponent,
|
|
31
31
|
RegisteredPendingRouteComponent,
|
|
32
|
+
RouteMask,
|
|
32
33
|
} from './route'
|
|
33
34
|
import {
|
|
34
35
|
RoutesById,
|
|
@@ -50,13 +51,15 @@ import {
|
|
|
50
51
|
Updater,
|
|
51
52
|
replaceEqualDeep,
|
|
52
53
|
partialDeepEqual,
|
|
54
|
+
NonNullableUpdater,
|
|
53
55
|
} from './utils'
|
|
54
56
|
import {
|
|
55
57
|
createBrowserHistory,
|
|
56
58
|
createMemoryHistory,
|
|
59
|
+
HistoryLocation,
|
|
60
|
+
LocationState,
|
|
57
61
|
RouterHistory,
|
|
58
62
|
} from './history'
|
|
59
|
-
import warning from 'tiny-warning'
|
|
60
63
|
|
|
61
64
|
//
|
|
62
65
|
|
|
@@ -78,25 +81,20 @@ export type RegisteredRouter = Register extends {
|
|
|
78
81
|
? TRouter
|
|
79
82
|
: AnyRouter
|
|
80
83
|
|
|
81
|
-
export interface
|
|
82
|
-
|
|
83
|
-
export interface ParsedLocation<
|
|
84
|
-
TSearchObj extends AnySearchSchema = {},
|
|
85
|
-
// TState extends LocationState = LocationState,
|
|
86
|
-
> {
|
|
84
|
+
export interface ParsedLocation<TSearchObj extends AnySearchSchema = {}> {
|
|
87
85
|
href: string
|
|
88
86
|
pathname: string
|
|
89
87
|
search: TSearchObj
|
|
90
88
|
searchStr: string
|
|
91
89
|
state: LocationState
|
|
92
90
|
hash: string
|
|
93
|
-
|
|
91
|
+
maskedLocation?: ParsedLocation<TSearchObj>
|
|
92
|
+
unmaskOnReload?: boolean
|
|
94
93
|
}
|
|
95
94
|
|
|
96
95
|
export interface FromLocation {
|
|
97
96
|
pathname: string
|
|
98
97
|
search?: unknown
|
|
99
|
-
key?: string
|
|
100
98
|
hash?: string
|
|
101
99
|
}
|
|
102
100
|
|
|
@@ -158,7 +156,7 @@ export interface RouterOptions<
|
|
|
158
156
|
parseSearch?: SearchParser
|
|
159
157
|
defaultPreload?: false | 'intent'
|
|
160
158
|
defaultPreloadDelay?: number
|
|
161
|
-
|
|
159
|
+
reloadOnWindowFocus?: boolean
|
|
162
160
|
defaultComponent?: RegisteredRouteComponent<
|
|
163
161
|
unknown,
|
|
164
162
|
AnySearchSchema,
|
|
@@ -192,6 +190,8 @@ export interface RouterOptions<
|
|
|
192
190
|
}>
|
|
193
191
|
dehydrate?: () => TDehydrated
|
|
194
192
|
hydrate?: (dehydrated: TDehydrated) => void
|
|
193
|
+
routeMasks?: RouteMask<TRouteTree>[]
|
|
194
|
+
unmaskOnReload?: boolean
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
export interface RouterState<TRouteTree extends AnyRoute = AnyRoute> {
|
|
@@ -216,11 +216,21 @@ export interface BuildNextOptions {
|
|
|
216
216
|
params?: true | Updater<unknown>
|
|
217
217
|
search?: true | Updater<unknown>
|
|
218
218
|
hash?: true | Updater<string>
|
|
219
|
-
state?: LocationState
|
|
220
|
-
|
|
219
|
+
state?: true | NonNullableUpdater<LocationState>
|
|
220
|
+
mask?: {
|
|
221
|
+
to?: string | number | null
|
|
222
|
+
params?: true | Updater<unknown>
|
|
223
|
+
search?: true | Updater<unknown>
|
|
224
|
+
hash?: true | Updater<string>
|
|
225
|
+
state?: true | NonNullableUpdater<LocationState>
|
|
226
|
+
unmaskOnReload?: boolean
|
|
227
|
+
}
|
|
221
228
|
from?: string
|
|
222
|
-
|
|
223
|
-
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface CommitLocationOptions {
|
|
232
|
+
replace?: boolean
|
|
233
|
+
resetScroll?: boolean
|
|
224
234
|
}
|
|
225
235
|
|
|
226
236
|
export interface MatchLocation {
|
|
@@ -228,7 +238,6 @@ export interface MatchLocation {
|
|
|
228
238
|
fuzzy?: boolean
|
|
229
239
|
caseSensitive?: boolean
|
|
230
240
|
from?: string
|
|
231
|
-
fromCurrent?: boolean
|
|
232
241
|
}
|
|
233
242
|
|
|
234
243
|
export interface MatchRouteOptions {
|
|
@@ -328,6 +337,8 @@ export class Router<
|
|
|
328
337
|
state: RouterState<TRouteTree>
|
|
329
338
|
dehydratedData?: TDehydrated
|
|
330
339
|
resetNextScroll = false
|
|
340
|
+
tempLocationKey = `${Math.round(Math.random() * 10000000)}`
|
|
341
|
+
// nextTemporaryLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>
|
|
331
342
|
|
|
332
343
|
constructor(options: RouterConstructorOptions<TRouteTree, TDehydrated>) {
|
|
333
344
|
this.options = {
|
|
@@ -399,15 +410,15 @@ export class Router<
|
|
|
399
410
|
|
|
400
411
|
this.update(options)
|
|
401
412
|
|
|
402
|
-
const
|
|
403
|
-
hash: true,
|
|
404
|
-
fromCurrent: true,
|
|
413
|
+
const nextLocation = this.buildLocation({
|
|
405
414
|
search: true,
|
|
415
|
+
params: true,
|
|
416
|
+
hash: true,
|
|
406
417
|
state: true,
|
|
407
418
|
})
|
|
408
419
|
|
|
409
|
-
if (this.state.location.href !==
|
|
410
|
-
this.#commitLocation({ ...
|
|
420
|
+
if (this.state.location.href !== nextLocation.href) {
|
|
421
|
+
this.#commitLocation({ ...nextLocation, replace: true })
|
|
411
422
|
}
|
|
412
423
|
}
|
|
413
424
|
|
|
@@ -461,8 +472,10 @@ export class Router<
|
|
|
461
472
|
}
|
|
462
473
|
|
|
463
474
|
#onFocus = () => {
|
|
464
|
-
if (this.options.
|
|
465
|
-
this.invalidate(
|
|
475
|
+
if (this.options.reloadOnWindowFocus ?? true) {
|
|
476
|
+
this.invalidate({
|
|
477
|
+
__fromFocus: true,
|
|
478
|
+
})
|
|
466
479
|
}
|
|
467
480
|
}
|
|
468
481
|
|
|
@@ -514,17 +527,6 @@ export class Router<
|
|
|
514
527
|
return this
|
|
515
528
|
}
|
|
516
529
|
|
|
517
|
-
buildNext = (opts: BuildNextOptions): ParsedLocation => {
|
|
518
|
-
const next = this.#buildLocation(opts)
|
|
519
|
-
|
|
520
|
-
const __matches = this.matchRoutes(next.pathname, next.search)
|
|
521
|
-
|
|
522
|
-
return this.#buildLocation({
|
|
523
|
-
...opts,
|
|
524
|
-
__matches,
|
|
525
|
-
})
|
|
526
|
-
}
|
|
527
|
-
|
|
528
530
|
cancelMatches = () => {
|
|
529
531
|
this.state.matches.forEach((match) => {
|
|
530
532
|
this.cancelMatch(match.id)
|
|
@@ -605,7 +607,7 @@ export class Router<
|
|
|
605
607
|
try {
|
|
606
608
|
// Load the matches
|
|
607
609
|
try {
|
|
608
|
-
await this.loadMatches(pendingMatches)
|
|
610
|
+
await this.loadMatches(pendingMatches.map((d) => d.id))
|
|
609
611
|
} catch (err) {
|
|
610
612
|
// swallow this error, since we'll display the
|
|
611
613
|
// errors on the route components
|
|
@@ -680,10 +682,20 @@ export class Router<
|
|
|
680
682
|
prevMatchesById: Record<string, RouteMatch<TRouteTree>>,
|
|
681
683
|
nextMatches: AnyRouteMatch[],
|
|
682
684
|
): Record<string, RouteMatch<TRouteTree>> => {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
685
|
+
let matchesById = { ...prevMatchesById }
|
|
686
|
+
|
|
687
|
+
nextMatches.forEach((match) => {
|
|
688
|
+
if (!matchesById[match.id]) {
|
|
689
|
+
matchesById[match.id] = match
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
matchesById[match.id] = {
|
|
693
|
+
...matchesById[match.id],
|
|
694
|
+
...match,
|
|
695
|
+
}
|
|
696
|
+
})
|
|
697
|
+
|
|
698
|
+
return matchesById
|
|
687
699
|
}
|
|
688
700
|
|
|
689
701
|
getRoute = (id: string): Route => {
|
|
@@ -699,7 +711,8 @@ export class Router<
|
|
|
699
711
|
maxAge?: number
|
|
700
712
|
} = this.state.location,
|
|
701
713
|
) => {
|
|
702
|
-
|
|
714
|
+
let next = this.buildLocation(navigateOpts)
|
|
715
|
+
|
|
703
716
|
const matches = this.matchRoutes(next.pathname, next.search, {
|
|
704
717
|
throwOnError: true,
|
|
705
718
|
})
|
|
@@ -711,12 +724,15 @@ export class Router<
|
|
|
711
724
|
}
|
|
712
725
|
})
|
|
713
726
|
|
|
714
|
-
await this.loadMatches(
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
727
|
+
await this.loadMatches(
|
|
728
|
+
matches.map((d) => d.id),
|
|
729
|
+
{
|
|
730
|
+
preload: true,
|
|
731
|
+
maxAge: navigateOpts.maxAge,
|
|
732
|
+
},
|
|
733
|
+
)
|
|
718
734
|
|
|
719
|
-
return matches
|
|
735
|
+
return [last(matches)!, matches] as const
|
|
720
736
|
}
|
|
721
737
|
|
|
722
738
|
cleanMatches = () => {
|
|
@@ -928,14 +944,13 @@ export class Router<
|
|
|
928
944
|
}
|
|
929
945
|
|
|
930
946
|
loadMatches = async (
|
|
931
|
-
|
|
947
|
+
matchIds: string[],
|
|
932
948
|
opts?: {
|
|
933
949
|
preload?: boolean
|
|
934
950
|
maxAge?: number
|
|
935
951
|
},
|
|
936
952
|
) => {
|
|
937
|
-
const getFreshMatches = () =>
|
|
938
|
-
_resolvedMatches.map((d) => this.getRouteMatch(d.id)!)
|
|
953
|
+
const getFreshMatches = () => matchIds.map((d) => this.getRouteMatch(d)!)
|
|
939
954
|
|
|
940
955
|
if (!opts?.preload) {
|
|
941
956
|
getFreshMatches().forEach((match) => {
|
|
@@ -1029,8 +1044,9 @@ export class Router<
|
|
|
1029
1044
|
}
|
|
1030
1045
|
}
|
|
1031
1046
|
} catch (err) {
|
|
1032
|
-
if (
|
|
1033
|
-
this.navigate(err as any)
|
|
1047
|
+
if (isRedirect(err)) {
|
|
1048
|
+
if (!opts?.preload) this.navigate(err as any)
|
|
1049
|
+
return
|
|
1034
1050
|
}
|
|
1035
1051
|
|
|
1036
1052
|
throw err
|
|
@@ -1155,6 +1171,8 @@ export class Router<
|
|
|
1155
1171
|
navigate = async <
|
|
1156
1172
|
TFrom extends RoutePaths<TRouteTree> = '/',
|
|
1157
1173
|
TTo extends string = '',
|
|
1174
|
+
TMaskFrom extends RoutePaths<TRouteTree> = TFrom,
|
|
1175
|
+
TMaskTo extends string = '',
|
|
1158
1176
|
>({
|
|
1159
1177
|
from,
|
|
1160
1178
|
to = '' as any,
|
|
@@ -1163,7 +1181,7 @@ export class Router<
|
|
|
1163
1181
|
replace,
|
|
1164
1182
|
params,
|
|
1165
1183
|
resetScroll,
|
|
1166
|
-
}: NavigateOptions<TRouteTree, TFrom, TTo>) => {
|
|
1184
|
+
}: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>) => {
|
|
1167
1185
|
// If this link simply reloads the current route,
|
|
1168
1186
|
// make sure it has a new key so it will trigger a data refresh
|
|
1169
1187
|
|
|
@@ -1183,21 +1201,22 @@ export class Router<
|
|
|
1183
1201
|
'Attempting to navigate to external url with this.navigate!',
|
|
1184
1202
|
)
|
|
1185
1203
|
|
|
1186
|
-
return this.#
|
|
1204
|
+
return this.#buildAndCommitLocation({
|
|
1187
1205
|
from: fromString,
|
|
1188
1206
|
to: toString,
|
|
1189
1207
|
search,
|
|
1190
1208
|
hash,
|
|
1191
|
-
replace,
|
|
1192
1209
|
params,
|
|
1210
|
+
replace,
|
|
1193
1211
|
resetScroll,
|
|
1194
1212
|
})
|
|
1195
1213
|
}
|
|
1196
1214
|
|
|
1197
1215
|
matchRoute = <
|
|
1216
|
+
TRouteTree extends AnyRoute = AnyRoute,
|
|
1198
1217
|
TFrom extends RoutePaths<TRouteTree> = '/',
|
|
1199
1218
|
TTo extends string = '',
|
|
1200
|
-
TResolved
|
|
1219
|
+
TResolved = ResolveRelativePath<TFrom, NoInfer<TTo>>,
|
|
1201
1220
|
>(
|
|
1202
1221
|
location: ToOptions<TRouteTree, TFrom, TTo>,
|
|
1203
1222
|
opts?: MatchRouteOptions,
|
|
@@ -1209,7 +1228,7 @@ export class Router<
|
|
|
1209
1228
|
: undefined,
|
|
1210
1229
|
} as any
|
|
1211
1230
|
|
|
1212
|
-
const next = this.
|
|
1231
|
+
const next = this.buildLocation(location)
|
|
1213
1232
|
if (opts?.pending && this.state.status !== 'pending') {
|
|
1214
1233
|
return false
|
|
1215
1234
|
}
|
|
@@ -1254,6 +1273,7 @@ export class Router<
|
|
|
1254
1273
|
preloadDelay: userPreloadDelay,
|
|
1255
1274
|
disabled,
|
|
1256
1275
|
state,
|
|
1276
|
+
mask,
|
|
1257
1277
|
resetScroll,
|
|
1258
1278
|
}: LinkOptions<TRouteTree, TFrom, TTo>): LinkInfo => {
|
|
1259
1279
|
// If this link simply reloads the current route,
|
|
@@ -1278,10 +1298,11 @@ export class Router<
|
|
|
1278
1298
|
hash,
|
|
1279
1299
|
replace,
|
|
1280
1300
|
state,
|
|
1301
|
+
mask,
|
|
1281
1302
|
resetScroll,
|
|
1282
1303
|
}
|
|
1283
1304
|
|
|
1284
|
-
const next = this.
|
|
1305
|
+
const next = this.buildLocation(nextOpts)
|
|
1285
1306
|
|
|
1286
1307
|
preload = preload ?? this.options.defaultPreload
|
|
1287
1308
|
const preloadDelay =
|
|
@@ -1320,7 +1341,7 @@ export class Router<
|
|
|
1320
1341
|
e.preventDefault()
|
|
1321
1342
|
|
|
1322
1343
|
// All is well? Navigate!
|
|
1323
|
-
this.#commitLocation(
|
|
1344
|
+
this.#commitLocation({ ...next, replace, resetScroll })
|
|
1324
1345
|
}
|
|
1325
1346
|
}
|
|
1326
1347
|
|
|
@@ -1600,154 +1621,276 @@ export class Router<
|
|
|
1600
1621
|
#parseLocation = (
|
|
1601
1622
|
previousLocation?: ParsedLocation,
|
|
1602
1623
|
): ParsedLocation<FullSearchSchema<TRouteTree>> => {
|
|
1603
|
-
|
|
1624
|
+
const parse = ({
|
|
1625
|
+
pathname,
|
|
1626
|
+
search,
|
|
1627
|
+
hash,
|
|
1628
|
+
state,
|
|
1629
|
+
}: HistoryLocation): ParsedLocation<FullSearchSchema<TRouteTree>> => {
|
|
1630
|
+
const parsedSearch = this.options.parseSearch(search)
|
|
1604
1631
|
|
|
1605
|
-
|
|
1632
|
+
return {
|
|
1633
|
+
pathname: pathname,
|
|
1634
|
+
searchStr: search,
|
|
1635
|
+
search: replaceEqualDeep(previousLocation?.search, parsedSearch) as any,
|
|
1636
|
+
hash: hash.split('#').reverse()[0] ?? '',
|
|
1637
|
+
href: `${pathname}${search}${hash}`,
|
|
1638
|
+
state: replaceEqualDeep(
|
|
1639
|
+
previousLocation?.state,
|
|
1640
|
+
state,
|
|
1641
|
+
) as LocationState,
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1606
1644
|
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
key
|
|
1645
|
+
const location = parse(this.history.location)
|
|
1646
|
+
|
|
1647
|
+
let { __tempLocation, __tempKey } = location.state
|
|
1648
|
+
|
|
1649
|
+
if (__tempLocation && (!__tempKey || __tempKey === this.tempLocationKey)) {
|
|
1650
|
+
// Sync up the location keys
|
|
1651
|
+
const parsedTempLocation = parse(__tempLocation) as any
|
|
1652
|
+
parsedTempLocation.state.key = location.state.key
|
|
1653
|
+
|
|
1654
|
+
delete parsedTempLocation.state.__tempLocation
|
|
1655
|
+
|
|
1656
|
+
return {
|
|
1657
|
+
...parsedTempLocation,
|
|
1658
|
+
maskedLocation: location,
|
|
1659
|
+
}
|
|
1615
1660
|
}
|
|
1661
|
+
|
|
1662
|
+
return location
|
|
1616
1663
|
}
|
|
1617
1664
|
|
|
1618
|
-
|
|
1619
|
-
|
|
1665
|
+
buildLocation = (opts: BuildNextOptions = {}): ParsedLocation => {
|
|
1666
|
+
const build = (
|
|
1667
|
+
dest: BuildNextOptions & {
|
|
1668
|
+
unmaskOnReload?: boolean
|
|
1669
|
+
} = {},
|
|
1670
|
+
matches?: AnyRouteMatch[],
|
|
1671
|
+
): ParsedLocation => {
|
|
1672
|
+
const from = this.state.location
|
|
1673
|
+
|
|
1674
|
+
const fromPathname = dest.from ?? from.pathname
|
|
1675
|
+
|
|
1676
|
+
let pathname = resolvePath(
|
|
1677
|
+
this.basepath ?? '/',
|
|
1678
|
+
fromPathname,
|
|
1679
|
+
`${dest.to ?? ''}`,
|
|
1680
|
+
)
|
|
1620
1681
|
|
|
1621
|
-
|
|
1622
|
-
? this.state.location.pathname
|
|
1623
|
-
: dest.from ?? this.state.location.pathname
|
|
1682
|
+
const fromMatches = this.matchRoutes(fromPathname, from.search)
|
|
1624
1683
|
|
|
1625
|
-
|
|
1626
|
-
this.basepath ?? '/',
|
|
1627
|
-
fromPathname,
|
|
1628
|
-
`${dest.to ?? ''}`,
|
|
1629
|
-
)
|
|
1684
|
+
const prevParams = { ...last(fromMatches)?.params }
|
|
1630
1685
|
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1686
|
+
let nextParams =
|
|
1687
|
+
(dest.params ?? true) === true
|
|
1688
|
+
? prevParams
|
|
1689
|
+
: functionalUpdate(dest.params!, prevParams)
|
|
1635
1690
|
|
|
1636
|
-
|
|
1691
|
+
if (nextParams) {
|
|
1692
|
+
matches
|
|
1693
|
+
?.map((d) => this.getRoute(d.routeId).options.stringifyParams)
|
|
1694
|
+
.filter(Boolean)
|
|
1695
|
+
.forEach((fn) => {
|
|
1696
|
+
nextParams = { ...nextParams!, ...fn!(nextParams!) }
|
|
1697
|
+
})
|
|
1698
|
+
}
|
|
1637
1699
|
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1700
|
+
pathname = interpolatePath(pathname, nextParams ?? {})
|
|
1701
|
+
|
|
1702
|
+
const preSearchFilters =
|
|
1703
|
+
matches
|
|
1704
|
+
?.map(
|
|
1705
|
+
(match) =>
|
|
1706
|
+
this.getRoute(match.routeId).options.preSearchFilters ?? [],
|
|
1707
|
+
)
|
|
1708
|
+
.flat()
|
|
1709
|
+
.filter(Boolean) ?? []
|
|
1710
|
+
|
|
1711
|
+
const postSearchFilters =
|
|
1712
|
+
matches
|
|
1713
|
+
?.map(
|
|
1714
|
+
(match) =>
|
|
1715
|
+
this.getRoute(match.routeId).options.postSearchFilters ?? [],
|
|
1716
|
+
)
|
|
1717
|
+
.flat()
|
|
1718
|
+
.filter(Boolean) ?? []
|
|
1719
|
+
|
|
1720
|
+
// Pre filters first
|
|
1721
|
+
const preFilteredSearch = preSearchFilters?.length
|
|
1722
|
+
? preSearchFilters?.reduce((prev, next) => next(prev), from.search)
|
|
1723
|
+
: from.search
|
|
1724
|
+
|
|
1725
|
+
// Then the link/navigate function
|
|
1726
|
+
const destSearch =
|
|
1727
|
+
dest.search === true
|
|
1728
|
+
? preFilteredSearch // Preserve resolvedFrom true
|
|
1729
|
+
: dest.search
|
|
1730
|
+
? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
|
|
1731
|
+
: preSearchFilters?.length
|
|
1732
|
+
? preFilteredSearch // Preserve resolvedFrom filters
|
|
1733
|
+
: {}
|
|
1734
|
+
|
|
1735
|
+
// Then post filters
|
|
1736
|
+
const postFilteredSearch = postSearchFilters?.length
|
|
1737
|
+
? postSearchFilters.reduce((prev, next) => next(prev), destSearch)
|
|
1738
|
+
: destSearch
|
|
1739
|
+
|
|
1740
|
+
const search = replaceEqualDeep(from.search, postFilteredSearch)
|
|
1741
|
+
|
|
1742
|
+
const searchStr = this.options.stringifySearch(search)
|
|
1743
|
+
|
|
1744
|
+
const hash =
|
|
1745
|
+
dest.hash === true
|
|
1746
|
+
? from.hash
|
|
1747
|
+
: dest.hash
|
|
1748
|
+
? functionalUpdate(dest.hash!, from.hash)
|
|
1749
|
+
: from.hash
|
|
1750
|
+
|
|
1751
|
+
const hashStr = hash ? `#${hash}` : ''
|
|
1752
|
+
|
|
1753
|
+
let nextState =
|
|
1754
|
+
dest.state === true
|
|
1755
|
+
? from.state
|
|
1756
|
+
: dest.state
|
|
1757
|
+
? functionalUpdate(dest.state, from.state)
|
|
1758
|
+
: from.state
|
|
1759
|
+
|
|
1760
|
+
nextState = replaceEqualDeep(from.state, nextState)
|
|
1642
1761
|
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
})
|
|
1762
|
+
return {
|
|
1763
|
+
pathname,
|
|
1764
|
+
search,
|
|
1765
|
+
searchStr,
|
|
1766
|
+
state: nextState,
|
|
1767
|
+
hash,
|
|
1768
|
+
href: this.history.createHref(`${pathname}${searchStr}${hashStr}`),
|
|
1769
|
+
unmaskOnReload: dest.unmaskOnReload,
|
|
1770
|
+
}
|
|
1650
1771
|
}
|
|
1651
1772
|
|
|
1652
|
-
|
|
1773
|
+
const buildWithMatches = (
|
|
1774
|
+
dest: BuildNextOptions = {},
|
|
1775
|
+
maskedDest?: BuildNextOptions,
|
|
1776
|
+
) => {
|
|
1777
|
+
let next = build(dest)
|
|
1778
|
+
let maskedNext = maskedDest ? build(maskedDest) : undefined
|
|
1653
1779
|
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
?.map(
|
|
1657
|
-
(match) =>
|
|
1658
|
-
this.getRoute(match.routeId).options.preSearchFilters ?? [],
|
|
1659
|
-
)
|
|
1660
|
-
.flat()
|
|
1661
|
-
.filter(Boolean) ?? []
|
|
1662
|
-
|
|
1663
|
-
const postSearchFilters =
|
|
1664
|
-
dest.__matches
|
|
1665
|
-
?.map(
|
|
1666
|
-
(match) =>
|
|
1667
|
-
this.getRoute(match.routeId).options.postSearchFilters ?? [],
|
|
1668
|
-
)
|
|
1669
|
-
.flat()
|
|
1670
|
-
.filter(Boolean) ?? []
|
|
1780
|
+
if (!maskedNext) {
|
|
1781
|
+
let params = {}
|
|
1671
1782
|
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
)
|
|
1678
|
-
: this.state.location.search
|
|
1679
|
-
|
|
1680
|
-
// Then the link/navigate function
|
|
1681
|
-
const destSearch =
|
|
1682
|
-
dest.search === true
|
|
1683
|
-
? preFilteredSearch // Preserve resolvedFrom true
|
|
1684
|
-
: dest.search
|
|
1685
|
-
? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
|
|
1686
|
-
: preSearchFilters?.length
|
|
1687
|
-
? preFilteredSearch // Preserve resolvedFrom filters
|
|
1688
|
-
: {}
|
|
1689
|
-
|
|
1690
|
-
// Then post filters
|
|
1691
|
-
const postFilteredSearch = postSearchFilters?.length
|
|
1692
|
-
? postSearchFilters.reduce((prev, next) => next(prev), destSearch)
|
|
1693
|
-
: destSearch
|
|
1694
|
-
|
|
1695
|
-
const search = replaceEqualDeep(
|
|
1696
|
-
this.state.location.search,
|
|
1697
|
-
postFilteredSearch,
|
|
1698
|
-
)
|
|
1783
|
+
let foundMask = this.options.routeMasks?.find((d) => {
|
|
1784
|
+
const match = matchPathname(this.basepath, next.pathname, {
|
|
1785
|
+
to: d.from,
|
|
1786
|
+
fuzzy: false,
|
|
1787
|
+
})
|
|
1699
1788
|
|
|
1700
|
-
|
|
1789
|
+
if (match) {
|
|
1790
|
+
params = match
|
|
1791
|
+
return true
|
|
1792
|
+
}
|
|
1701
1793
|
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
? this.state.location.hash
|
|
1705
|
-
: functionalUpdate(dest.hash!, this.state.location.hash)
|
|
1794
|
+
return false
|
|
1795
|
+
})
|
|
1706
1796
|
|
|
1707
|
-
|
|
1797
|
+
if (foundMask) {
|
|
1798
|
+
foundMask = {
|
|
1799
|
+
...foundMask,
|
|
1800
|
+
from: interpolatePath(foundMask.from, params) as any,
|
|
1801
|
+
}
|
|
1802
|
+
maskedDest = foundMask
|
|
1803
|
+
maskedNext = build(maskedDest)
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1708
1806
|
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
? this.
|
|
1712
|
-
:
|
|
1807
|
+
const nextMatches = this.matchRoutes(next.pathname, next.search)
|
|
1808
|
+
const maskedMatches = maskedNext
|
|
1809
|
+
? this.matchRoutes(maskedNext.pathname, maskedNext.search)
|
|
1810
|
+
: undefined
|
|
1811
|
+
const maskedFinal = maskedNext
|
|
1812
|
+
? build(maskedDest, maskedMatches)
|
|
1813
|
+
: undefined
|
|
1713
1814
|
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1815
|
+
const final = build(dest, nextMatches)
|
|
1816
|
+
|
|
1817
|
+
if (maskedFinal) {
|
|
1818
|
+
final.maskedLocation = maskedFinal
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
return final
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
if (opts.mask) {
|
|
1825
|
+
return buildWithMatches(opts, {
|
|
1826
|
+
...pick(opts, ['from']),
|
|
1827
|
+
...opts.mask,
|
|
1828
|
+
})
|
|
1722
1829
|
}
|
|
1830
|
+
|
|
1831
|
+
return buildWithMatches(opts)
|
|
1723
1832
|
}
|
|
1724
1833
|
|
|
1725
|
-
#
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1834
|
+
#buildAndCommitLocation = ({
|
|
1835
|
+
replace,
|
|
1836
|
+
resetScroll,
|
|
1837
|
+
...rest
|
|
1838
|
+
}: BuildNextOptions & CommitLocationOptions = {}) => {
|
|
1839
|
+
const location = this.buildLocation(rest)
|
|
1840
|
+
return this.#commitLocation({
|
|
1841
|
+
...location,
|
|
1842
|
+
replace,
|
|
1843
|
+
resetScroll,
|
|
1844
|
+
})
|
|
1845
|
+
}
|
|
1729
1846
|
|
|
1847
|
+
#commitLocation = async (next: ParsedLocation & CommitLocationOptions) => {
|
|
1730
1848
|
if (this.navigateTimeout) clearTimeout(this.navigateTimeout)
|
|
1731
1849
|
|
|
1732
1850
|
let nextAction: 'push' | 'replace' = 'replace'
|
|
1733
1851
|
|
|
1734
|
-
if (!
|
|
1852
|
+
if (!next.replace) {
|
|
1735
1853
|
nextAction = 'push'
|
|
1736
1854
|
}
|
|
1737
1855
|
|
|
1738
1856
|
const isSameUrl = this.state.location.href === next.href
|
|
1739
1857
|
|
|
1740
|
-
if (isSameUrl
|
|
1858
|
+
if (isSameUrl) {
|
|
1741
1859
|
nextAction = 'replace'
|
|
1742
1860
|
}
|
|
1743
1861
|
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1862
|
+
let { maskedLocation, ...nextHistory } = next
|
|
1863
|
+
|
|
1864
|
+
if (maskedLocation) {
|
|
1865
|
+
nextHistory = {
|
|
1866
|
+
...maskedLocation,
|
|
1867
|
+
state: {
|
|
1868
|
+
...maskedLocation.state,
|
|
1869
|
+
__tempKey: undefined,
|
|
1870
|
+
__tempLocation: {
|
|
1871
|
+
...nextHistory,
|
|
1872
|
+
search: nextHistory.searchStr,
|
|
1873
|
+
state: {
|
|
1874
|
+
...nextHistory.state,
|
|
1875
|
+
__tempKey: undefined!,
|
|
1876
|
+
__tempLocation: undefined!,
|
|
1877
|
+
key: undefined!,
|
|
1878
|
+
},
|
|
1879
|
+
},
|
|
1880
|
+
},
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
if (nextHistory.unmaskOnReload ?? this.options.unmaskOnReload ?? false) {
|
|
1884
|
+
nextHistory.state.__tempKey = this.tempLocationKey
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1747
1887
|
|
|
1748
|
-
this.history[nextAction === 'push' ? 'push' : 'replace'](
|
|
1888
|
+
this.history[nextAction === 'push' ? 'push' : 'replace'](
|
|
1889
|
+
nextHistory.href,
|
|
1890
|
+
nextHistory.state,
|
|
1891
|
+
)
|
|
1749
1892
|
|
|
1750
|
-
this.resetNextScroll =
|
|
1893
|
+
this.resetNextScroll = next.resetScroll ?? true
|
|
1751
1894
|
|
|
1752
1895
|
return this.latestLoadPromise
|
|
1753
1896
|
}
|
|
@@ -1814,37 +1957,46 @@ export class Router<
|
|
|
1814
1957
|
invalidate = async (opts?: {
|
|
1815
1958
|
matchId?: string
|
|
1816
1959
|
reload?: boolean
|
|
1960
|
+
__fromFocus?: boolean
|
|
1817
1961
|
}): Promise<void> => {
|
|
1818
1962
|
if (opts?.matchId) {
|
|
1819
1963
|
this.setRouteMatch(opts.matchId, (s) => ({
|
|
1820
1964
|
...s,
|
|
1821
1965
|
invalid: true,
|
|
1822
1966
|
}))
|
|
1967
|
+
|
|
1823
1968
|
const matchIndex = this.state.matches.findIndex(
|
|
1824
1969
|
(d) => d.id === opts.matchId,
|
|
1825
1970
|
)
|
|
1826
1971
|
const childMatch = this.state.matches[matchIndex + 1]
|
|
1827
1972
|
|
|
1828
1973
|
if (childMatch) {
|
|
1829
|
-
return this.invalidate({
|
|
1974
|
+
return this.invalidate({
|
|
1975
|
+
matchId: childMatch.id,
|
|
1976
|
+
reload: false,
|
|
1977
|
+
__fromFocus: opts.__fromFocus,
|
|
1978
|
+
})
|
|
1830
1979
|
}
|
|
1831
1980
|
} else {
|
|
1832
1981
|
this.__store.batch(() => {
|
|
1833
1982
|
Object.values(this.state.matchesById).forEach((match) => {
|
|
1834
|
-
this.
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1983
|
+
const route = this.getRoute(match.routeId)
|
|
1984
|
+
const shouldInvalidate = opts?.__fromFocus
|
|
1985
|
+
? route.options.reloadOnWindowFocus ?? true
|
|
1986
|
+
: true
|
|
1987
|
+
|
|
1988
|
+
if (shouldInvalidate) {
|
|
1989
|
+
this.setRouteMatch(match.id, (s) => ({
|
|
1990
|
+
...s,
|
|
1991
|
+
invalid: true,
|
|
1992
|
+
}))
|
|
1993
|
+
}
|
|
1838
1994
|
})
|
|
1839
1995
|
})
|
|
1840
1996
|
}
|
|
1841
1997
|
|
|
1842
1998
|
if (opts?.reload ?? true) {
|
|
1843
|
-
return this.
|
|
1844
|
-
fromCurrent: true,
|
|
1845
|
-
replace: true,
|
|
1846
|
-
search: true,
|
|
1847
|
-
} as any)
|
|
1999
|
+
return this.load()
|
|
1848
2000
|
}
|
|
1849
2001
|
}
|
|
1850
2002
|
}
|
|
@@ -1879,7 +2031,9 @@ export type Redirect<
|
|
|
1879
2031
|
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
1880
2032
|
TFrom extends RoutePaths<TRouteTree> = '/',
|
|
1881
2033
|
TTo extends string = '',
|
|
1882
|
-
|
|
2034
|
+
TMaskFrom extends RoutePaths<TRouteTree> = TFrom,
|
|
2035
|
+
TMaskTo extends string = '',
|
|
2036
|
+
> = NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {
|
|
1883
2037
|
code?: number
|
|
1884
2038
|
}
|
|
1885
2039
|
|