@tanstack/vue-router 1.141.4 → 1.141.7
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/README.md +1 -1
- package/dist/esm/ClientOnly.js +33 -0
- package/dist/esm/ClientOnly.js.map +1 -0
- package/dist/esm/Match.js +66 -39
- package/dist/esm/Match.js.map +1 -1
- package/dist/esm/Transitioner.js +14 -5
- package/dist/esm/Transitioner.js.map +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lazyRouteComponent.d.ts +0 -6
- package/dist/esm/lazyRouteComponent.js +5 -24
- package/dist/esm/lazyRouteComponent.js.map +1 -1
- package/dist/esm/link.d.ts +20 -22
- package/dist/esm/link.js +3 -2
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/route.d.ts +6 -1
- package/dist/esm/route.js +25 -0
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/ssr/renderRouterToStream.js +1 -1
- package/dist/esm/ssr/renderRouterToStream.js.map +1 -1
- package/dist/source/Match.jsx +90 -63
- package/dist/source/Match.jsx.map +1 -1
- package/dist/source/Transitioner.jsx +12 -5
- package/dist/source/Transitioner.jsx.map +1 -1
- package/dist/source/index.d.ts +2 -1
- package/dist/source/index.jsx +1 -0
- package/dist/source/index.jsx.map +1 -1
- package/dist/source/lazyRouteComponent.d.ts +0 -6
- package/dist/source/lazyRouteComponent.jsx +3 -23
- package/dist/source/lazyRouteComponent.jsx.map +1 -1
- package/dist/source/link.d.ts +20 -22
- package/dist/source/link.jsx +3 -2
- package/dist/source/link.jsx.map +1 -1
- package/dist/source/route.d.ts +6 -1
- package/dist/source/route.js +13 -0
- package/dist/source/route.js.map +1 -1
- package/dist/source/ssr/renderRouterToStream.jsx +1 -1
- package/dist/source/ssr/renderRouterToStream.jsx.map +1 -1
- package/package.json +3 -2
- package/src/Match.tsx +115 -73
- package/src/Transitioner.tsx +15 -6
- package/src/index.tsx +2 -0
- package/src/lazyRouteComponent.tsx +10 -32
- package/src/link.tsx +87 -59
- package/src/route.ts +33 -1
- package/src/ssr/renderRouterToStream.tsx +1 -1
package/src/link.tsx
CHANGED
|
@@ -19,39 +19,48 @@ import type {
|
|
|
19
19
|
RegisteredRouter,
|
|
20
20
|
RoutePaths,
|
|
21
21
|
} from '@tanstack/router-core'
|
|
22
|
+
import type { AnchorHTMLAttributes, ReservedProps } from '@vue/runtime-dom'
|
|
22
23
|
import type {
|
|
23
24
|
ValidateLinkOptions,
|
|
24
25
|
ValidateLinkOptionsArray,
|
|
25
26
|
} from './typePrimitives'
|
|
26
27
|
|
|
27
|
-
// Type definitions to replace missing Vue JSX types
|
|
28
28
|
type EventHandler<TEvent = Event> = (e: TEvent) => void
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
onClick?: EventHandler<MouseEvent>
|
|
33
|
-
onFocus?: EventHandler<FocusEvent>
|
|
34
|
-
// Vue 3's h() function expects lowercase event names after 'on' prefix
|
|
35
|
-
onMouseenter?: EventHandler<MouseEvent>
|
|
36
|
-
onMouseleave?: EventHandler<MouseEvent>
|
|
37
|
-
onMouseover?: EventHandler<MouseEvent>
|
|
38
|
-
onMouseout?: EventHandler<MouseEvent>
|
|
39
|
-
onTouchstart?: EventHandler<TouchEvent>
|
|
40
|
-
// Also accept the camelCase versions for external API compatibility
|
|
41
|
-
onMouseEnter?: EventHandler<MouseEvent>
|
|
42
|
-
onMouseLeave?: EventHandler<MouseEvent>
|
|
43
|
-
onMouseOver?: EventHandler<MouseEvent>
|
|
44
|
-
onMouseOut?: EventHandler<MouseEvent>
|
|
45
|
-
onTouchStart?: EventHandler<TouchEvent>
|
|
46
|
-
[key: string]: any
|
|
29
|
+
|
|
30
|
+
type DataAttributes = {
|
|
31
|
+
[K in `data-${string}`]?: unknown
|
|
47
32
|
}
|
|
48
33
|
|
|
34
|
+
type LinkHTMLAttributes = AnchorHTMLAttributes &
|
|
35
|
+
ReservedProps &
|
|
36
|
+
DataAttributes & {
|
|
37
|
+
// Vue's runtime-dom types use lowercase event names.
|
|
38
|
+
// Also accept camelCase versions for external API compatibility.
|
|
39
|
+
onMouseEnter?: EventHandler<MouseEvent>
|
|
40
|
+
onMouseLeave?: EventHandler<MouseEvent>
|
|
41
|
+
onMouseOver?: EventHandler<MouseEvent>
|
|
42
|
+
onMouseOut?: EventHandler<MouseEvent>
|
|
43
|
+
onTouchStart?: EventHandler<TouchEvent>
|
|
44
|
+
|
|
45
|
+
// `disabled` is not a valid <a> attribute, but is useful when using `asChild`.
|
|
46
|
+
disabled?: boolean
|
|
47
|
+
}
|
|
48
|
+
|
|
49
49
|
interface StyledProps {
|
|
50
|
-
class?:
|
|
51
|
-
style?:
|
|
52
|
-
[key: string]:
|
|
50
|
+
class?: LinkHTMLAttributes['class']
|
|
51
|
+
style?: LinkHTMLAttributes['style']
|
|
52
|
+
[key: string]: unknown
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
type PropsOfComponent<TComp> =
|
|
56
|
+
// Functional components
|
|
57
|
+
TComp extends (props: infer P, ...args: Array<unknown>) => any
|
|
58
|
+
? P
|
|
59
|
+
: // Vue components (defineComponent, class components, etc)
|
|
60
|
+
TComp extends Vue.Component<infer P>
|
|
61
|
+
? P
|
|
62
|
+
: Record<string, unknown>
|
|
63
|
+
|
|
55
64
|
export function useLinkProps<
|
|
56
65
|
TRouter extends AnyRouter = RegisteredRouter,
|
|
57
66
|
TFrom extends RoutePaths<TRouter['routeTree']> | string = string,
|
|
@@ -60,7 +69,7 @@ export function useLinkProps<
|
|
|
60
69
|
TMaskTo extends string = '',
|
|
61
70
|
>(
|
|
62
71
|
options: UseLinkPropsOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,
|
|
63
|
-
):
|
|
72
|
+
): LinkHTMLAttributes {
|
|
64
73
|
const router = useRouter()
|
|
65
74
|
const isTransitioning = Vue.ref(false)
|
|
66
75
|
let hasRenderFetched = false
|
|
@@ -195,6 +204,7 @@ export function useLinkProps<
|
|
|
195
204
|
// Create safe props that can be spread
|
|
196
205
|
const getPropsSafeToSpread = () => {
|
|
197
206
|
const result: Record<string, any> = {}
|
|
207
|
+
const optionRecord = options as unknown as Record<string, unknown>
|
|
198
208
|
for (const key in options) {
|
|
199
209
|
if (
|
|
200
210
|
![
|
|
@@ -233,7 +243,7 @@ export function useLinkProps<
|
|
|
233
243
|
'additionalProps',
|
|
234
244
|
].includes(key)
|
|
235
245
|
) {
|
|
236
|
-
result[key] =
|
|
246
|
+
result[key] = optionRecord[key]
|
|
237
247
|
}
|
|
238
248
|
}
|
|
239
249
|
return result
|
|
@@ -241,7 +251,7 @@ export function useLinkProps<
|
|
|
241
251
|
|
|
242
252
|
if (type.value === 'external') {
|
|
243
253
|
// External links just have simple props
|
|
244
|
-
const externalProps:
|
|
254
|
+
const externalProps: Record<string, unknown> = {
|
|
245
255
|
...getPropsSafeToSpread(),
|
|
246
256
|
ref,
|
|
247
257
|
href: options.to,
|
|
@@ -265,11 +275,11 @@ export function useLinkProps<
|
|
|
265
275
|
}
|
|
266
276
|
})
|
|
267
277
|
|
|
268
|
-
return externalProps
|
|
278
|
+
return externalProps as LinkHTMLAttributes
|
|
269
279
|
}
|
|
270
280
|
|
|
271
281
|
// The click handler
|
|
272
|
-
const handleClick = (e:
|
|
282
|
+
const handleClick = (e: PointerEvent): void => {
|
|
273
283
|
// Check actual element's target attribute as fallback
|
|
274
284
|
const elementTarget = (
|
|
275
285
|
e.currentTarget as HTMLAnchorElement | SVGAElement
|
|
@@ -307,7 +317,7 @@ export function useLinkProps<
|
|
|
307
317
|
startTransition: options.startTransition,
|
|
308
318
|
viewTransition: options.viewTransition,
|
|
309
319
|
ignoreBlocker: options.ignoreBlocker,
|
|
310
|
-
}
|
|
320
|
+
})
|
|
311
321
|
}
|
|
312
322
|
}
|
|
313
323
|
|
|
@@ -448,7 +458,7 @@ export function useLinkProps<
|
|
|
448
458
|
|
|
449
459
|
// Create static event handlers that don't change between renders
|
|
450
460
|
const staticEventHandlers = {
|
|
451
|
-
onClick: composeEventHandlers<
|
|
461
|
+
onClick: composeEventHandlers<PointerEvent>([
|
|
452
462
|
options.onClick,
|
|
453
463
|
handleClick,
|
|
454
464
|
]) as any,
|
|
@@ -480,8 +490,8 @@ export function useLinkProps<
|
|
|
480
490
|
|
|
481
491
|
// Compute all props synchronously to avoid hydration mismatches
|
|
482
492
|
// Using Vue.computed ensures props are calculated at render time, not after
|
|
483
|
-
const computedProps = Vue.computed<
|
|
484
|
-
const result:
|
|
493
|
+
const computedProps = Vue.computed<LinkHTMLAttributes>(() => {
|
|
494
|
+
const result: Record<string, unknown> = {
|
|
485
495
|
...getPropsSafeToSpread(),
|
|
486
496
|
href: href.value,
|
|
487
497
|
ref,
|
|
@@ -523,20 +533,20 @@ export function useLinkProps<
|
|
|
523
533
|
|
|
524
534
|
for (const key of Object.keys(activeP)) {
|
|
525
535
|
if (key !== 'class' && key !== 'style') {
|
|
526
|
-
result[key] = activeP[key]
|
|
536
|
+
result[key] = (activeP as any)[key]
|
|
527
537
|
}
|
|
528
538
|
}
|
|
529
539
|
for (const key of Object.keys(inactiveP)) {
|
|
530
540
|
if (key !== 'class' && key !== 'style') {
|
|
531
|
-
result[key] = inactiveP[key]
|
|
541
|
+
result[key] = (inactiveP as any)[key]
|
|
532
542
|
}
|
|
533
543
|
}
|
|
534
544
|
|
|
535
|
-
return result
|
|
545
|
+
return result as LinkHTMLAttributes
|
|
536
546
|
})
|
|
537
547
|
|
|
538
548
|
// Return the computed ref itself - callers should access .value
|
|
539
|
-
return computedProps as unknown as
|
|
549
|
+
return computedProps as unknown as LinkHTMLAttributes
|
|
540
550
|
}
|
|
541
551
|
|
|
542
552
|
// Type definitions
|
|
@@ -547,7 +557,7 @@ export type UseLinkPropsOptions<
|
|
|
547
557
|
TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,
|
|
548
558
|
TMaskTo extends string = '.',
|
|
549
559
|
> = ActiveLinkOptions<'a', TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &
|
|
550
|
-
|
|
560
|
+
LinkHTMLAttributes
|
|
551
561
|
|
|
552
562
|
export type ActiveLinkOptions<
|
|
553
563
|
TComp = 'a',
|
|
@@ -560,7 +570,9 @@ export type ActiveLinkOptions<
|
|
|
560
570
|
ActiveLinkOptionProps<TComp>
|
|
561
571
|
|
|
562
572
|
type ActiveLinkProps<TComp> = Partial<
|
|
563
|
-
|
|
573
|
+
(TComp extends keyof HTMLElementTagNameMap
|
|
574
|
+
? LinkHTMLAttributes
|
|
575
|
+
: PropsOfComponent<TComp>) & {
|
|
564
576
|
[key: `data-${string}`]: unknown
|
|
565
577
|
}
|
|
566
578
|
>
|
|
@@ -591,15 +603,16 @@ export type LinkProps<
|
|
|
591
603
|
export interface LinkPropsChildren {
|
|
592
604
|
// 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
|
|
593
605
|
children?:
|
|
594
|
-
| Vue.
|
|
595
|
-
| ((state: {
|
|
606
|
+
| Vue.VNodeChild
|
|
607
|
+
| ((state: {
|
|
608
|
+
isActive: boolean
|
|
609
|
+
isTransitioning: boolean
|
|
610
|
+
}) => Vue.VNodeChild)
|
|
596
611
|
}
|
|
597
612
|
|
|
598
613
|
type LinkComponentVueProps<TComp> = TComp extends keyof HTMLElementTagNameMap
|
|
599
|
-
? Omit<
|
|
600
|
-
: TComp
|
|
601
|
-
? Record<string, any>
|
|
602
|
-
: Record<string, any>
|
|
614
|
+
? Omit<LinkHTMLAttributes, keyof CreateLinkProps>
|
|
615
|
+
: Omit<PropsOfComponent<TComp>, keyof CreateLinkProps>
|
|
603
616
|
|
|
604
617
|
export type LinkComponentProps<
|
|
605
618
|
TComp = 'a',
|
|
@@ -620,9 +633,12 @@ export type CreateLinkProps = LinkProps<
|
|
|
620
633
|
string
|
|
621
634
|
>
|
|
622
635
|
|
|
623
|
-
export type LinkComponent<
|
|
636
|
+
export type LinkComponent<
|
|
637
|
+
in out TComp,
|
|
638
|
+
in out TDefaultFrom extends string = string,
|
|
639
|
+
> = <
|
|
624
640
|
TRouter extends AnyRouter = RegisteredRouter,
|
|
625
|
-
const TFrom extends string =
|
|
641
|
+
const TFrom extends string = TDefaultFrom,
|
|
626
642
|
const TTo extends string | undefined = undefined,
|
|
627
643
|
const TMaskFrom extends string = TFrom,
|
|
628
644
|
const TMaskTo extends string = '',
|
|
@@ -630,6 +646,26 @@ export type LinkComponent<TComp> = <
|
|
|
630
646
|
props: LinkComponentProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,
|
|
631
647
|
) => Vue.VNode
|
|
632
648
|
|
|
649
|
+
export interface LinkComponentRoute<
|
|
650
|
+
in out TDefaultFrom extends string = string,
|
|
651
|
+
> {
|
|
652
|
+
defaultFrom: TDefaultFrom
|
|
653
|
+
<
|
|
654
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
655
|
+
const TTo extends string | undefined = undefined,
|
|
656
|
+
const TMaskTo extends string = '',
|
|
657
|
+
>(
|
|
658
|
+
props: LinkComponentProps<
|
|
659
|
+
'a',
|
|
660
|
+
TRouter,
|
|
661
|
+
this['defaultFrom'],
|
|
662
|
+
TTo,
|
|
663
|
+
this['defaultFrom'],
|
|
664
|
+
TMaskTo
|
|
665
|
+
>,
|
|
666
|
+
): Vue.VNode
|
|
667
|
+
}
|
|
668
|
+
|
|
633
669
|
export function createLink<const TComp>(
|
|
634
670
|
Comp: Constrain<TComp, any, (props: CreateLinkProps) => Vue.VNode>,
|
|
635
671
|
): LinkComponent<TComp> {
|
|
@@ -637,7 +673,7 @@ export function createLink<const TComp>(
|
|
|
637
673
|
name: 'CreatedLink',
|
|
638
674
|
inheritAttrs: false,
|
|
639
675
|
setup(_, { attrs, slots }) {
|
|
640
|
-
return () => Vue.h(
|
|
676
|
+
return () => Vue.h(LinkImpl as any, { ...attrs, _asChild: Comp }, slots)
|
|
641
677
|
},
|
|
642
678
|
}) as any
|
|
643
679
|
}
|
|
@@ -676,7 +712,7 @@ const LinkImpl = Vue.defineComponent({
|
|
|
676
712
|
const allProps = { ...props, ...attrs }
|
|
677
713
|
const linkPropsComputed = useLinkProps(
|
|
678
714
|
allProps as any,
|
|
679
|
-
) as unknown as Vue.ComputedRef<
|
|
715
|
+
) as unknown as Vue.ComputedRef<LinkHTMLAttributes>
|
|
680
716
|
|
|
681
717
|
return () => {
|
|
682
718
|
const Component = props._asChild || 'a'
|
|
@@ -700,7 +736,7 @@ const LinkImpl = Vue.defineComponent({
|
|
|
700
736
|
if (Component === 'svg') {
|
|
701
737
|
// Create props without class for svg link
|
|
702
738
|
const svgLinkProps = { ...linkProps }
|
|
703
|
-
delete
|
|
739
|
+
delete svgLinkProps.class
|
|
704
740
|
return Vue.h('svg', {}, [Vue.h('a', svgLinkProps, slotContent)])
|
|
705
741
|
}
|
|
706
742
|
|
|
@@ -723,17 +759,9 @@ const LinkImpl = Vue.defineComponent({
|
|
|
723
759
|
/**
|
|
724
760
|
* Link component with proper TypeScript generics support
|
|
725
761
|
*/
|
|
726
|
-
export const Link = LinkImpl as unknown as
|
|
727
|
-
<
|
|
728
|
-
|
|
729
|
-
TFrom extends RoutePaths<TRouter['routeTree']> | string = string,
|
|
730
|
-
TTo extends string | undefined = '.',
|
|
731
|
-
TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,
|
|
732
|
-
TMaskTo extends string = '.',
|
|
733
|
-
>(
|
|
734
|
-
props: LinkComponentProps<'a', TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,
|
|
735
|
-
): Vue.VNode
|
|
736
|
-
}
|
|
762
|
+
export const Link = LinkImpl as unknown as Vue.Component<unknown> &
|
|
763
|
+
Vue.Component<CreateLinkProps> &
|
|
764
|
+
LinkComponent<'a'>
|
|
737
765
|
|
|
738
766
|
function isCtrlEvent(e: MouseEvent) {
|
|
739
767
|
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
|
package/src/route.ts
CHANGED
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
BaseRouteApi,
|
|
5
5
|
notFound,
|
|
6
6
|
} from '@tanstack/router-core'
|
|
7
|
+
import * as Vue from 'vue'
|
|
8
|
+
import { Link } from './link'
|
|
7
9
|
import { useLoaderData } from './useLoaderData'
|
|
8
10
|
import { useLoaderDeps } from './useLoaderDeps'
|
|
9
11
|
import { useParams } from './useParams'
|
|
@@ -42,8 +44,8 @@ import type { UseMatchRoute } from './useMatch'
|
|
|
42
44
|
import type { UseLoaderDepsRoute } from './useLoaderDeps'
|
|
43
45
|
import type { UseParamsRoute } from './useParams'
|
|
44
46
|
import type { UseSearchRoute } from './useSearch'
|
|
45
|
-
import type * as Vue from 'vue'
|
|
46
47
|
import type { UseRouteContextRoute } from './useRouteContext'
|
|
48
|
+
import type { LinkComponentRoute } from './link'
|
|
47
49
|
|
|
48
50
|
// Structural type for Vue SFC components (.vue files)
|
|
49
51
|
// Uses structural matching to accept Vue components without breaking
|
|
@@ -73,6 +75,7 @@ declare module '@tanstack/router-core' {
|
|
|
73
75
|
useLoaderDeps: UseLoaderDepsRoute<TId>
|
|
74
76
|
useLoaderData: UseLoaderDataRoute<TId>
|
|
75
77
|
useNavigate: () => UseNavigateResult<TFullPath>
|
|
78
|
+
Link: LinkComponentRoute<TFullPath>
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
|
|
@@ -140,6 +143,19 @@ export class RouteApi<
|
|
|
140
143
|
notFound = (opts?: NotFoundError) => {
|
|
141
144
|
return notFound({ routeId: this.id as string, ...opts })
|
|
142
145
|
}
|
|
146
|
+
|
|
147
|
+
Link: LinkComponentRoute<RouteTypesById<TRouter, TId>['fullPath']> = ((
|
|
148
|
+
props,
|
|
149
|
+
ctx?: Vue.SetupContext,
|
|
150
|
+
) => {
|
|
151
|
+
const router = useRouter()
|
|
152
|
+
const fullPath = router.routesById[this.id as string].fullPath
|
|
153
|
+
return Vue.h(
|
|
154
|
+
Link as any,
|
|
155
|
+
{ from: fullPath as never, ...(props as any) },
|
|
156
|
+
ctx?.slots,
|
|
157
|
+
)
|
|
158
|
+
}) as LinkComponentRoute<RouteTypesById<TRouter, TId>['fullPath']>
|
|
143
159
|
}
|
|
144
160
|
|
|
145
161
|
export class Route<
|
|
@@ -277,6 +293,14 @@ export class Route<
|
|
|
277
293
|
useNavigate = (): UseNavigateResult<TFullPath> => {
|
|
278
294
|
return useNavigate({ from: this.fullPath })
|
|
279
295
|
}
|
|
296
|
+
|
|
297
|
+
Link: LinkComponentRoute<TFullPath> = ((props, ctx?: Vue.SetupContext) => {
|
|
298
|
+
return Vue.h(
|
|
299
|
+
Link as any,
|
|
300
|
+
{ from: this.fullPath as never, ...(props as any) },
|
|
301
|
+
ctx?.slots,
|
|
302
|
+
)
|
|
303
|
+
}) as LinkComponentRoute<TFullPath>
|
|
280
304
|
}
|
|
281
305
|
|
|
282
306
|
export function createRoute<
|
|
@@ -515,6 +539,14 @@ export class RootRoute<
|
|
|
515
539
|
useNavigate = (): UseNavigateResult<'/'> => {
|
|
516
540
|
return useNavigate({ from: this.fullPath })
|
|
517
541
|
}
|
|
542
|
+
|
|
543
|
+
Link: LinkComponentRoute<'/'> = ((props, ctx?: Vue.SetupContext) => {
|
|
544
|
+
return Vue.h(
|
|
545
|
+
Link as any,
|
|
546
|
+
{ from: this.fullPath as never, ...(props as any) },
|
|
547
|
+
ctx?.slots,
|
|
548
|
+
)
|
|
549
|
+
}) as LinkComponentRoute<'/'>
|
|
518
550
|
}
|
|
519
551
|
|
|
520
552
|
export function createRouteMask<
|