@tanstack/react-router 0.0.1-beta.260 → 0.0.1-beta.262
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/link.js +96 -22
- package/build/cjs/link.js.map +1 -1
- package/build/cjs/router.js +5 -116
- package/build/cjs/router.js.map +1 -1
- package/build/esm/index.js +101 -138
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +355 -355
- package/build/types/router.d.ts +1 -2
- package/build/umd/index.development.js +101 -138
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +1 -1
- package/build/umd/index.production.js.map +1 -1
- package/package.json +2 -2
- package/src/link.tsx +126 -19
- package/src/router.ts +5 -142
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/react-router",
|
|
3
3
|
"author": "Tanner Linsley",
|
|
4
|
-
"version": "0.0.1-beta.
|
|
4
|
+
"version": "0.0.1-beta.262",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "tanstack/router",
|
|
7
7
|
"homepage": "https://tanstack.com/router",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@tanstack/store": "^0.1.3",
|
|
45
45
|
"tiny-invariant": "^1.3.1",
|
|
46
46
|
"tiny-warning": "^1.0.3",
|
|
47
|
-
"@tanstack/history": "0.0.1-beta.
|
|
47
|
+
"@tanstack/history": "0.0.1-beta.262"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "rollup --config rollup.config.js"
|
package/src/link.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import { useMatch } from './Matches'
|
|
3
|
-
import { useRouter } from './RouterProvider'
|
|
3
|
+
import { useRouter, useRouterState } from './RouterProvider'
|
|
4
4
|
import { Trim } from './fileRoute'
|
|
5
5
|
import { LocationState, ParsedLocation } from './location'
|
|
6
6
|
import { AnyRoute, ReactNode } from './route'
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
PickRequired,
|
|
21
21
|
UnionToIntersection,
|
|
22
22
|
Updater,
|
|
23
|
+
deepEqual,
|
|
23
24
|
functionalUpdate,
|
|
24
25
|
} from './utils'
|
|
25
26
|
|
|
@@ -352,6 +353,12 @@ export type ResolveRelativePath<TFrom, TTo = '.'> = TFrom extends string
|
|
|
352
353
|
: never
|
|
353
354
|
: never
|
|
354
355
|
|
|
356
|
+
type LinkCurrentTargetElement = {
|
|
357
|
+
preloadTimeout?: null | ReturnType<typeof setTimeout>
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const preloadWarning = 'Error preloading route! ☝️'
|
|
361
|
+
|
|
355
362
|
export function useLinkProps<
|
|
356
363
|
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
357
364
|
TFrom extends RoutePaths<TRouteTree> = '/',
|
|
@@ -361,7 +368,7 @@ export function useLinkProps<
|
|
|
361
368
|
>(
|
|
362
369
|
options: MakeLinkPropsOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
|
|
363
370
|
): React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
364
|
-
const
|
|
371
|
+
const router = useRouter()
|
|
365
372
|
const matchPathname = useMatch({
|
|
366
373
|
strict: false,
|
|
367
374
|
select: (s) => s.pathname,
|
|
@@ -369,7 +376,6 @@ export function useLinkProps<
|
|
|
369
376
|
|
|
370
377
|
const {
|
|
371
378
|
// custom props
|
|
372
|
-
type,
|
|
373
379
|
children,
|
|
374
380
|
target,
|
|
375
381
|
activeProps = () => ({ className: 'active' }),
|
|
@@ -382,8 +388,8 @@ export function useLinkProps<
|
|
|
382
388
|
to,
|
|
383
389
|
state,
|
|
384
390
|
mask,
|
|
385
|
-
preload,
|
|
386
|
-
preloadDelay,
|
|
391
|
+
preload: userPreload,
|
|
392
|
+
preloadDelay: userPreloadDelay,
|
|
387
393
|
replace,
|
|
388
394
|
startTransition,
|
|
389
395
|
resetScroll,
|
|
@@ -398,25 +404,122 @@ export function useLinkProps<
|
|
|
398
404
|
...rest
|
|
399
405
|
} = options
|
|
400
406
|
|
|
401
|
-
|
|
407
|
+
// If this link simply reloads the current route,
|
|
408
|
+
// make sure it has a new key so it will trigger a data refresh
|
|
409
|
+
|
|
410
|
+
// If this `to` is a valid external URL, return
|
|
411
|
+
// null for LinkUtils
|
|
412
|
+
|
|
413
|
+
const dest = {
|
|
402
414
|
from: options.to ? matchPathname : undefined,
|
|
403
415
|
...options,
|
|
404
|
-
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
let type: 'internal' | 'external' = 'internal'
|
|
419
|
+
|
|
420
|
+
try {
|
|
421
|
+
new URL(`${to}`)
|
|
422
|
+
type = 'external'
|
|
423
|
+
} catch {}
|
|
405
424
|
|
|
406
|
-
if (
|
|
407
|
-
|
|
408
|
-
|
|
425
|
+
if (type === 'external') {
|
|
426
|
+
return {
|
|
427
|
+
href: to,
|
|
428
|
+
}
|
|
409
429
|
}
|
|
410
430
|
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
431
|
+
const next = router.buildLocation(dest as any)
|
|
432
|
+
|
|
433
|
+
const preload = userPreload ?? router.options.defaultPreload
|
|
434
|
+
const preloadDelay =
|
|
435
|
+
userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0
|
|
436
|
+
|
|
437
|
+
const isActive = useRouterState({
|
|
438
|
+
select: (s) => {
|
|
439
|
+
// Compare path/hash for matches
|
|
440
|
+
const currentPathSplit = s.location.pathname.split('/')
|
|
441
|
+
const nextPathSplit = next.pathname.split('/')
|
|
442
|
+
const pathIsFuzzyEqual = nextPathSplit.every(
|
|
443
|
+
(d, i) => d === currentPathSplit[i],
|
|
444
|
+
)
|
|
445
|
+
// Combine the matches based on user router.options
|
|
446
|
+
const pathTest = activeOptions?.exact
|
|
447
|
+
? s.location.pathname === next.pathname
|
|
448
|
+
: pathIsFuzzyEqual
|
|
449
|
+
const hashTest = activeOptions?.includeHash
|
|
450
|
+
? s.location.hash === next.hash
|
|
451
|
+
: true
|
|
452
|
+
const searchTest =
|
|
453
|
+
activeOptions?.includeSearch ?? true
|
|
454
|
+
? deepEqual(s.location.search, next.search, true)
|
|
455
|
+
: true
|
|
456
|
+
|
|
457
|
+
// The final "active" test
|
|
458
|
+
return pathTest && hashTest && searchTest
|
|
459
|
+
},
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
// The click handler
|
|
463
|
+
const handleClick = (e: MouseEvent) => {
|
|
464
|
+
if (
|
|
465
|
+
!disabled &&
|
|
466
|
+
!isCtrlEvent(e) &&
|
|
467
|
+
!e.defaultPrevented &&
|
|
468
|
+
(!target || target === '_self') &&
|
|
469
|
+
e.button === 0
|
|
470
|
+
) {
|
|
471
|
+
e.preventDefault()
|
|
472
|
+
|
|
473
|
+
// All is well? Navigate!
|
|
474
|
+
router.commitLocation({ ...next, replace, resetScroll, startTransition })
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// The click handler
|
|
479
|
+
const handleFocus = (e: MouseEvent) => {
|
|
480
|
+
if (preload) {
|
|
481
|
+
router.preloadRoute(dest as any).catch((err) => {
|
|
482
|
+
console.warn(err)
|
|
483
|
+
console.warn(preloadWarning)
|
|
484
|
+
})
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const handleTouchStart = (e: TouchEvent) => {
|
|
489
|
+
if (preload) {
|
|
490
|
+
router.preloadRoute(dest as any).catch((err) => {
|
|
491
|
+
console.warn(err)
|
|
492
|
+
console.warn(preloadWarning)
|
|
493
|
+
})
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const handleEnter = (e: MouseEvent) => {
|
|
498
|
+
const target = (e.target || {}) as LinkCurrentTargetElement
|
|
499
|
+
|
|
500
|
+
if (preload) {
|
|
501
|
+
if (target.preloadTimeout) {
|
|
502
|
+
return
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
target.preloadTimeout = setTimeout(() => {
|
|
506
|
+
target.preloadTimeout = null
|
|
507
|
+
router.preloadRoute(dest as any).catch((err) => {
|
|
508
|
+
console.warn(err)
|
|
509
|
+
console.warn(preloadWarning)
|
|
510
|
+
})
|
|
511
|
+
}, preloadDelay)
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const handleLeave = (e: MouseEvent) => {
|
|
516
|
+
const target = (e.target || {}) as LinkCurrentTargetElement
|
|
517
|
+
|
|
518
|
+
if (target.preloadTimeout) {
|
|
519
|
+
clearTimeout(target.preloadTimeout)
|
|
520
|
+
target.preloadTimeout = null
|
|
521
|
+
}
|
|
522
|
+
}
|
|
420
523
|
|
|
421
524
|
const composeHandlers =
|
|
422
525
|
(handlers: (undefined | ((e: any) => void))[]) =>
|
|
@@ -507,3 +610,7 @@ export const Link: LinkComponent = React.forwardRef((props: any, ref) => {
|
|
|
507
610
|
/>
|
|
508
611
|
)
|
|
509
612
|
}) as any
|
|
613
|
+
|
|
614
|
+
function isCtrlEvent(e: MouseEvent) {
|
|
615
|
+
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
|
|
616
|
+
}
|
package/src/router.ts
CHANGED
|
@@ -218,12 +218,6 @@ export type RouterListener<TRouterEvent extends RouterEvent> = {
|
|
|
218
218
|
fn: ListenerFn<TRouterEvent>
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
type LinkCurrentTargetElement = {
|
|
222
|
-
preloadTimeout?: null | ReturnType<typeof setTimeout>
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const preloadWarning = 'Error preloading route! ☝️'
|
|
226
|
-
|
|
227
221
|
export class Router<
|
|
228
222
|
TRouteTree extends AnyRoute = AnyRoute,
|
|
229
223
|
TDehydrated extends Record<string, any> = Record<string, any>,
|
|
@@ -1003,13 +997,14 @@ export class Router<
|
|
|
1003
997
|
}))
|
|
1004
998
|
}
|
|
1005
999
|
|
|
1006
|
-
|
|
1000
|
+
|
|
1007
1001
|
|
|
1008
1002
|
// Check each match middleware to see if the route can be accessed
|
|
1009
1003
|
try {
|
|
1010
1004
|
for (let [index, match] of matches.entries()) {
|
|
1011
1005
|
const parentMatch = matches[index - 1]
|
|
1012
1006
|
const route = this.looseRoutesById[match.routeId]!
|
|
1007
|
+
const abortController = new AbortController()
|
|
1013
1008
|
|
|
1014
1009
|
const handleErrorAndRedirect = (err: any, code: string) => {
|
|
1015
1010
|
err.routerCode = code
|
|
@@ -1034,7 +1029,7 @@ export class Router<
|
|
|
1034
1029
|
error: err,
|
|
1035
1030
|
status: 'error',
|
|
1036
1031
|
updatedAt: Date.now(),
|
|
1037
|
-
abortController,
|
|
1032
|
+
abortController: new AbortController(),
|
|
1038
1033
|
}
|
|
1039
1034
|
}
|
|
1040
1035
|
|
|
@@ -1053,7 +1048,7 @@ export class Router<
|
|
|
1053
1048
|
const beforeLoadContext =
|
|
1054
1049
|
(await route.options.beforeLoad?.({
|
|
1055
1050
|
search: match.search,
|
|
1056
|
-
abortController
|
|
1051
|
+
abortController,
|
|
1057
1052
|
params: match.params,
|
|
1058
1053
|
preload: !!preload,
|
|
1059
1054
|
context: parentContext,
|
|
@@ -1077,6 +1072,7 @@ export class Router<
|
|
|
1077
1072
|
matches[index] = match = {
|
|
1078
1073
|
...match,
|
|
1079
1074
|
context: replaceEqualDeep(match.context, context),
|
|
1075
|
+
abortController,
|
|
1080
1076
|
}
|
|
1081
1077
|
} catch (err) {
|
|
1082
1078
|
handleErrorAndRedirect(err, 'BEFORE_LOAD')
|
|
@@ -1435,136 +1431,6 @@ export class Router<
|
|
|
1435
1431
|
return matches
|
|
1436
1432
|
}
|
|
1437
1433
|
|
|
1438
|
-
buildLink: BuildLinkFn<TRouteTree> = (dest) => {
|
|
1439
|
-
// If this link simply reloads the current route,
|
|
1440
|
-
// make sure it has a new key so it will trigger a data refresh
|
|
1441
|
-
|
|
1442
|
-
// If this `to` is a valid external URL, return
|
|
1443
|
-
// null for LinkUtils
|
|
1444
|
-
|
|
1445
|
-
const {
|
|
1446
|
-
to,
|
|
1447
|
-
preload: userPreload,
|
|
1448
|
-
preloadDelay: userPreloadDelay,
|
|
1449
|
-
activeOptions,
|
|
1450
|
-
disabled,
|
|
1451
|
-
target,
|
|
1452
|
-
replace,
|
|
1453
|
-
resetScroll,
|
|
1454
|
-
startTransition,
|
|
1455
|
-
} = dest
|
|
1456
|
-
|
|
1457
|
-
try {
|
|
1458
|
-
new URL(`${to}`)
|
|
1459
|
-
return {
|
|
1460
|
-
type: 'external',
|
|
1461
|
-
href: to as any,
|
|
1462
|
-
}
|
|
1463
|
-
} catch (e) {}
|
|
1464
|
-
|
|
1465
|
-
const nextOpts = dest
|
|
1466
|
-
const next = this.buildLocation(nextOpts as any)
|
|
1467
|
-
|
|
1468
|
-
const preload = userPreload ?? this.options.defaultPreload
|
|
1469
|
-
const preloadDelay =
|
|
1470
|
-
userPreloadDelay ?? this.options.defaultPreloadDelay ?? 0
|
|
1471
|
-
|
|
1472
|
-
// Compare path/hash for matches
|
|
1473
|
-
const currentPathSplit = this.latestLocation.pathname.split('/')
|
|
1474
|
-
const nextPathSplit = next.pathname.split('/')
|
|
1475
|
-
const pathIsFuzzyEqual = nextPathSplit.every(
|
|
1476
|
-
(d, i) => d === currentPathSplit[i],
|
|
1477
|
-
)
|
|
1478
|
-
// Combine the matches based on user this.options
|
|
1479
|
-
const pathTest = activeOptions?.exact
|
|
1480
|
-
? this.latestLocation.pathname === next.pathname
|
|
1481
|
-
: pathIsFuzzyEqual
|
|
1482
|
-
const hashTest = activeOptions?.includeHash
|
|
1483
|
-
? this.latestLocation.hash === next.hash
|
|
1484
|
-
: true
|
|
1485
|
-
const searchTest =
|
|
1486
|
-
activeOptions?.includeSearch ?? true
|
|
1487
|
-
? deepEqual(this.latestLocation.search, next.search, true)
|
|
1488
|
-
: true
|
|
1489
|
-
|
|
1490
|
-
// The final "active" test
|
|
1491
|
-
const isActive = pathTest && hashTest && searchTest
|
|
1492
|
-
|
|
1493
|
-
// The click handler
|
|
1494
|
-
const handleClick = (e: MouseEvent) => {
|
|
1495
|
-
if (
|
|
1496
|
-
!disabled &&
|
|
1497
|
-
!isCtrlEvent(e) &&
|
|
1498
|
-
!e.defaultPrevented &&
|
|
1499
|
-
(!target || target === '_self') &&
|
|
1500
|
-
e.button === 0
|
|
1501
|
-
) {
|
|
1502
|
-
e.preventDefault()
|
|
1503
|
-
|
|
1504
|
-
// All is well? Navigate!
|
|
1505
|
-
this.commitLocation({ ...next, replace, resetScroll, startTransition })
|
|
1506
|
-
}
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
|
-
// The click handler
|
|
1510
|
-
const handleFocus = (e: MouseEvent) => {
|
|
1511
|
-
if (preload) {
|
|
1512
|
-
this.preloadRoute(nextOpts as any).catch((err) => {
|
|
1513
|
-
console.warn(err)
|
|
1514
|
-
console.warn(preloadWarning)
|
|
1515
|
-
})
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
const handleTouchStart = (e: TouchEvent) => {
|
|
1520
|
-
if (preload) {
|
|
1521
|
-
this.preloadRoute(nextOpts as any).catch((err) => {
|
|
1522
|
-
console.warn(err)
|
|
1523
|
-
console.warn(preloadWarning)
|
|
1524
|
-
})
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
|
|
1528
|
-
const handleEnter = (e: MouseEvent) => {
|
|
1529
|
-
const target = (e.target || {}) as LinkCurrentTargetElement
|
|
1530
|
-
|
|
1531
|
-
if (preload) {
|
|
1532
|
-
if (target.preloadTimeout) {
|
|
1533
|
-
return
|
|
1534
|
-
}
|
|
1535
|
-
|
|
1536
|
-
target.preloadTimeout = setTimeout(() => {
|
|
1537
|
-
target.preloadTimeout = null
|
|
1538
|
-
this.preloadRoute(nextOpts as any).catch((err) => {
|
|
1539
|
-
console.warn(err)
|
|
1540
|
-
console.warn(preloadWarning)
|
|
1541
|
-
})
|
|
1542
|
-
}, preloadDelay)
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
const handleLeave = (e: MouseEvent) => {
|
|
1547
|
-
const target = (e.target || {}) as LinkCurrentTargetElement
|
|
1548
|
-
|
|
1549
|
-
if (target.preloadTimeout) {
|
|
1550
|
-
clearTimeout(target.preloadTimeout)
|
|
1551
|
-
target.preloadTimeout = null
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
return {
|
|
1556
|
-
type: 'internal',
|
|
1557
|
-
next,
|
|
1558
|
-
handleFocus,
|
|
1559
|
-
handleClick,
|
|
1560
|
-
handleEnter,
|
|
1561
|
-
handleLeave,
|
|
1562
|
-
handleTouchStart,
|
|
1563
|
-
isActive,
|
|
1564
|
-
disabled,
|
|
1565
|
-
}
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1568
1434
|
matchRoute: MatchRouteFn<TRouteTree> = (location, opts) => {
|
|
1569
1435
|
location = {
|
|
1570
1436
|
...location,
|
|
@@ -1725,9 +1591,6 @@ export function lazyFn<
|
|
|
1725
1591
|
}
|
|
1726
1592
|
}
|
|
1727
1593
|
|
|
1728
|
-
function isCtrlEvent(e: MouseEvent) {
|
|
1729
|
-
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
|
|
1730
|
-
}
|
|
1731
1594
|
export class SearchParamError extends Error {}
|
|
1732
1595
|
|
|
1733
1596
|
export class PathParamError extends Error {}
|