@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/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.260",
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.260"
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 { buildLink } = useRouter()
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
- const linkInfo = buildLink({
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
- } as any)
416
+ }
417
+
418
+ let type: 'internal' | 'external' = 'internal'
419
+
420
+ try {
421
+ new URL(`${to}`)
422
+ type = 'external'
423
+ } catch {}
405
424
 
406
- if (linkInfo.type === 'external') {
407
- const { href } = linkInfo
408
- return { href }
425
+ if (type === 'external') {
426
+ return {
427
+ href: to,
428
+ }
409
429
  }
410
430
 
411
- const {
412
- handleClick,
413
- handleFocus,
414
- handleEnter,
415
- handleLeave,
416
- handleTouchStart,
417
- isActive,
418
- next,
419
- } = linkInfo
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
- const abortController = new AbortController()
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: match.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 {}