@tanstack/react-router 0.0.1-beta.261 → 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.261",
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.261"
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>,
@@ -1437,136 +1431,6 @@ export class Router<
1437
1431
  return matches
1438
1432
  }
1439
1433
 
1440
- buildLink: BuildLinkFn<TRouteTree> = (dest) => {
1441
- // If this link simply reloads the current route,
1442
- // make sure it has a new key so it will trigger a data refresh
1443
-
1444
- // If this `to` is a valid external URL, return
1445
- // null for LinkUtils
1446
-
1447
- const {
1448
- to,
1449
- preload: userPreload,
1450
- preloadDelay: userPreloadDelay,
1451
- activeOptions,
1452
- disabled,
1453
- target,
1454
- replace,
1455
- resetScroll,
1456
- startTransition,
1457
- } = dest
1458
-
1459
- try {
1460
- new URL(`${to}`)
1461
- return {
1462
- type: 'external',
1463
- href: to as any,
1464
- }
1465
- } catch (e) {}
1466
-
1467
- const nextOpts = dest
1468
- const next = this.buildLocation(nextOpts as any)
1469
-
1470
- const preload = userPreload ?? this.options.defaultPreload
1471
- const preloadDelay =
1472
- userPreloadDelay ?? this.options.defaultPreloadDelay ?? 0
1473
-
1474
- // Compare path/hash for matches
1475
- const currentPathSplit = this.latestLocation.pathname.split('/')
1476
- const nextPathSplit = next.pathname.split('/')
1477
- const pathIsFuzzyEqual = nextPathSplit.every(
1478
- (d, i) => d === currentPathSplit[i],
1479
- )
1480
- // Combine the matches based on user this.options
1481
- const pathTest = activeOptions?.exact
1482
- ? this.latestLocation.pathname === next.pathname
1483
- : pathIsFuzzyEqual
1484
- const hashTest = activeOptions?.includeHash
1485
- ? this.latestLocation.hash === next.hash
1486
- : true
1487
- const searchTest =
1488
- activeOptions?.includeSearch ?? true
1489
- ? deepEqual(this.latestLocation.search, next.search, true)
1490
- : true
1491
-
1492
- // The final "active" test
1493
- const isActive = pathTest && hashTest && searchTest
1494
-
1495
- // The click handler
1496
- const handleClick = (e: MouseEvent) => {
1497
- if (
1498
- !disabled &&
1499
- !isCtrlEvent(e) &&
1500
- !e.defaultPrevented &&
1501
- (!target || target === '_self') &&
1502
- e.button === 0
1503
- ) {
1504
- e.preventDefault()
1505
-
1506
- // All is well? Navigate!
1507
- this.commitLocation({ ...next, replace, resetScroll, startTransition })
1508
- }
1509
- }
1510
-
1511
- // The click handler
1512
- const handleFocus = (e: MouseEvent) => {
1513
- if (preload) {
1514
- this.preloadRoute(nextOpts as any).catch((err) => {
1515
- console.warn(err)
1516
- console.warn(preloadWarning)
1517
- })
1518
- }
1519
- }
1520
-
1521
- const handleTouchStart = (e: TouchEvent) => {
1522
- if (preload) {
1523
- this.preloadRoute(nextOpts as any).catch((err) => {
1524
- console.warn(err)
1525
- console.warn(preloadWarning)
1526
- })
1527
- }
1528
- }
1529
-
1530
- const handleEnter = (e: MouseEvent) => {
1531
- const target = (e.target || {}) as LinkCurrentTargetElement
1532
-
1533
- if (preload) {
1534
- if (target.preloadTimeout) {
1535
- return
1536
- }
1537
-
1538
- target.preloadTimeout = setTimeout(() => {
1539
- target.preloadTimeout = null
1540
- this.preloadRoute(nextOpts as any).catch((err) => {
1541
- console.warn(err)
1542
- console.warn(preloadWarning)
1543
- })
1544
- }, preloadDelay)
1545
- }
1546
- }
1547
-
1548
- const handleLeave = (e: MouseEvent) => {
1549
- const target = (e.target || {}) as LinkCurrentTargetElement
1550
-
1551
- if (target.preloadTimeout) {
1552
- clearTimeout(target.preloadTimeout)
1553
- target.preloadTimeout = null
1554
- }
1555
- }
1556
-
1557
- return {
1558
- type: 'internal',
1559
- next,
1560
- handleFocus,
1561
- handleClick,
1562
- handleEnter,
1563
- handleLeave,
1564
- handleTouchStart,
1565
- isActive,
1566
- disabled,
1567
- }
1568
- }
1569
-
1570
1434
  matchRoute: MatchRouteFn<TRouteTree> = (location, opts) => {
1571
1435
  location = {
1572
1436
  ...location,
@@ -1727,9 +1591,6 @@ export function lazyFn<
1727
1591
  }
1728
1592
  }
1729
1593
 
1730
- function isCtrlEvent(e: MouseEvent) {
1731
- return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
1732
- }
1733
1594
  export class SearchParamError extends Error {}
1734
1595
 
1735
1596
  export class PathParamError extends Error {}