@tanstack/react-router 1.45.15 → 1.46.0
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/dist/cjs/link.cjs +64 -33
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/link.d.cts +25 -3
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +76 -23
- package/dist/cjs/utils.cjs +34 -0
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +38 -0
- package/dist/esm/link.d.ts +25 -3
- package/dist/esm/link.js +65 -34
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/router.d.ts +76 -23
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/utils.d.ts +38 -0
- package/dist/esm/utils.js +34 -0
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/link.tsx +106 -51
- package/src/router.ts +76 -23
- package/src/utils.ts +84 -0
package/dist/esm/link.js
CHANGED
|
@@ -4,12 +4,13 @@ import * as React from "react";
|
|
|
4
4
|
import { flushSync } from "react-dom";
|
|
5
5
|
import { useRouterState } from "./useRouterState.js";
|
|
6
6
|
import { useRouter } from "./useRouter.js";
|
|
7
|
-
import { deepEqual, functionalUpdate } from "./utils.js";
|
|
7
|
+
import { useForwardedRef, deepEqual, useIntersectionObserver, functionalUpdate } from "./utils.js";
|
|
8
8
|
import { removeTrailingSlash, exactPathTest } from "./path.js";
|
|
9
9
|
const preloadWarning = "Error preloading route! ☝️";
|
|
10
|
-
function useLinkProps(options) {
|
|
10
|
+
function useLinkProps(options, forwardedRef) {
|
|
11
11
|
const router = useRouter();
|
|
12
12
|
const [isTransitioning, setIsTransitioning] = React.useState(false);
|
|
13
|
+
const innerRef = useForwardedRef(forwardedRef);
|
|
13
14
|
const {
|
|
14
15
|
// custom props
|
|
15
16
|
activeProps = () => ({ className: "active" }),
|
|
@@ -41,14 +42,22 @@ function useLinkProps(options) {
|
|
|
41
42
|
ignoreBlocker,
|
|
42
43
|
...rest
|
|
43
44
|
} = options;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
const type = React.useMemo(() => {
|
|
46
|
+
try {
|
|
47
|
+
new URL(`${to}`);
|
|
48
|
+
return "external";
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
return "internal";
|
|
52
|
+
}, [to]);
|
|
53
|
+
const next = React.useMemo(
|
|
54
|
+
() => router.buildLocation(options),
|
|
55
|
+
[router, options]
|
|
56
|
+
);
|
|
57
|
+
const preload = React.useMemo(
|
|
58
|
+
() => userPreload ?? router.options.defaultPreload,
|
|
59
|
+
[router.options.defaultPreload, userPreload]
|
|
60
|
+
);
|
|
52
61
|
const preloadDelay = userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0;
|
|
53
62
|
const isActive = useRouterState({
|
|
54
63
|
select: (s) => {
|
|
@@ -69,9 +78,34 @@ function useLinkProps(options) {
|
|
|
69
78
|
return pathTest && hashTest && searchTest;
|
|
70
79
|
}
|
|
71
80
|
});
|
|
81
|
+
const doPreload = React.useCallback(() => {
|
|
82
|
+
router.preloadRoute(options).catch((err) => {
|
|
83
|
+
console.warn(err);
|
|
84
|
+
console.warn(preloadWarning);
|
|
85
|
+
});
|
|
86
|
+
}, [options, router]);
|
|
87
|
+
const viewportIntersectionCallback = React.useCallback(
|
|
88
|
+
(entry) => {
|
|
89
|
+
if (entry == null ? void 0 : entry.isIntersecting) {
|
|
90
|
+
doPreload();
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
[doPreload]
|
|
94
|
+
);
|
|
95
|
+
useIntersectionObserver(
|
|
96
|
+
innerRef,
|
|
97
|
+
{
|
|
98
|
+
rootMargin: "100px"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
disabled: !!disabled || preload !== "viewport"
|
|
102
|
+
},
|
|
103
|
+
viewportIntersectionCallback
|
|
104
|
+
);
|
|
72
105
|
if (type === "external") {
|
|
73
106
|
return {
|
|
74
107
|
...rest,
|
|
108
|
+
ref: innerRef,
|
|
75
109
|
type,
|
|
76
110
|
href: to,
|
|
77
111
|
...children && { children },
|
|
@@ -106,13 +140,7 @@ function useLinkProps(options) {
|
|
|
106
140
|
});
|
|
107
141
|
}
|
|
108
142
|
};
|
|
109
|
-
const
|
|
110
|
-
router.preloadRoute(options).catch((err) => {
|
|
111
|
-
console.warn(err);
|
|
112
|
-
console.warn(preloadWarning);
|
|
113
|
-
});
|
|
114
|
-
};
|
|
115
|
-
const handleFocus = (e) => {
|
|
143
|
+
const handleFocus = (_) => {
|
|
116
144
|
if (disabled) return;
|
|
117
145
|
if (preload) {
|
|
118
146
|
doPreload();
|
|
@@ -165,6 +193,7 @@ function useLinkProps(options) {
|
|
|
165
193
|
...resolvedInactiveProps,
|
|
166
194
|
...rest,
|
|
167
195
|
href: disabled ? void 0 : next.maskedLocation ? router.history.createHref(next.maskedLocation.href) : router.history.createHref(next.href),
|
|
196
|
+
ref: innerRef,
|
|
168
197
|
onClick: composeHandlers([onClick, handleClick]),
|
|
169
198
|
onFocus: composeHandlers([onFocus, handleFocus]),
|
|
170
199
|
onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),
|
|
@@ -187,24 +216,26 @@ function createLink(Comp) {
|
|
|
187
216
|
return /* @__PURE__ */ jsx(Link, { ...props, _asChild: Comp, ref });
|
|
188
217
|
});
|
|
189
218
|
}
|
|
190
|
-
const Link = React.forwardRef(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
219
|
+
const Link = React.forwardRef(
|
|
220
|
+
(props, ref) => {
|
|
221
|
+
const { _asChild, ...rest } = props;
|
|
222
|
+
const { type, ref: innerRef, ...linkProps } = useLinkProps(rest, ref);
|
|
223
|
+
const children = typeof rest.children === "function" ? rest.children({
|
|
224
|
+
isActive: linkProps["data-status"] === "active"
|
|
225
|
+
}) : rest.children;
|
|
226
|
+
if (typeof _asChild === "undefined") {
|
|
227
|
+
delete linkProps.disabled;
|
|
228
|
+
}
|
|
229
|
+
return React.createElement(
|
|
230
|
+
_asChild ? _asChild : "a",
|
|
231
|
+
{
|
|
232
|
+
...linkProps,
|
|
233
|
+
ref: innerRef
|
|
234
|
+
},
|
|
235
|
+
children
|
|
236
|
+
);
|
|
198
237
|
}
|
|
199
|
-
|
|
200
|
-
_asChild ? _asChild : "a",
|
|
201
|
-
{
|
|
202
|
-
...linkProps,
|
|
203
|
-
ref
|
|
204
|
-
},
|
|
205
|
-
children
|
|
206
|
-
);
|
|
207
|
-
});
|
|
238
|
+
);
|
|
208
239
|
function isCtrlEvent(e) {
|
|
209
240
|
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
|
|
210
241
|
}
|
package/dist/esm/link.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"link.js","sources":["../../src/link.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { flushSync } from 'react-dom'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\nimport { deepEqual, functionalUpdate } from './utils'\nimport { exactPathTest, removeTrailingSlash } from './path'\nimport type { AnyRouter, ParsedLocation } from '.'\nimport type { HistoryState } from '@tanstack/history'\nimport type {\n AllParams,\n CatchAllPaths,\n FullSearchSchema,\n FullSearchSchemaInput,\n RouteByPath,\n RouteByToPath,\n RoutePaths,\n RouteToPath,\n} from './routeInfo'\nimport type { RegisteredRouter } from './router'\nimport type {\n Expand,\n MakeDifferenceOptional,\n NoInfer,\n NonNullableUpdater,\n PickRequired,\n Updater,\n WithoutEmpty,\n} from './utils'\n\nexport type CleanPath<T extends string> = T extends `${infer L}//${infer R}`\n ? CleanPath<`${CleanPath<L>}/${CleanPath<R>}`>\n : T extends `${infer L}//`\n ? `${CleanPath<L>}/`\n : T extends `//${infer L}`\n ? `/${CleanPath<L>}`\n : T\n\nexport type Split<TValue, TIncludeTrailingSlash = true> = TValue extends unknown\n ? string extends TValue\n ? Array<string>\n : TValue extends string\n ? CleanPath<TValue> extends ''\n ? []\n : TIncludeTrailingSlash extends true\n ? CleanPath<TValue> extends `${infer T}/`\n ? [...Split<T>, '/']\n : CleanPath<TValue> extends `/${infer U}`\n ? Split<U>\n : CleanPath<TValue> extends `${infer T}/${infer U}`\n ? [...Split<T>, ...Split<U>]\n : [TValue]\n : CleanPath<TValue> extends `${infer T}/${infer U}`\n ? [...Split<T>, ...Split<U>]\n : TValue extends string\n ? [TValue]\n : never\n : never\n : never\n\nexport type ParsePathParams<\n T extends string,\n TAcc = never,\n> = T extends `${string}$${infer TPossiblyParam}`\n ? TPossiblyParam extends `${infer TParam}/${infer TRest}`\n ? ParsePathParams<TRest, TParam extends '' ? '_splat' : TParam | TAcc>\n : TPossiblyParam extends ''\n ? '_splat'\n : TPossiblyParam | TAcc\n : TAcc\n\nexport type Join<T, TDelimiter extends string = '/'> = T extends []\n ? ''\n : T extends [infer L extends string]\n ? L\n : T extends [\n infer L extends string,\n ...infer Tail extends [...Array<string>],\n ]\n ? CleanPath<`${L}${TDelimiter}${Join<Tail>}`>\n : never\n\nexport type Last<T extends Array<any>> = T extends [...infer _, infer L]\n ? L\n : never\n\nexport type RemoveTrailingSlashes<T> = T extends `${infer R}/` ? R : T\n\nexport type RemoveLeadingSlashes<T> = T extends `/${infer R}` ? R : T\n\nexport type ResolvePaths<TRouter extends AnyRouter, TSearchPath> =\n RouteByPath<\n TRouter['routeTree'],\n RemoveTrailingSlashes<TSearchPath>\n > extends never\n ? RouteToPath<TRouter, TRouter['routeTree']>\n : RouteToPath<\n TRouter,\n RouteByPath<TRouter['routeTree'], RemoveTrailingSlashes<TSearchPath>>\n >\n\nexport type SearchPaths<\n TRouter extends AnyRouter,\n TSearchPath extends string,\n TPaths = ResolvePaths<TRouter, TSearchPath>,\n> = TPaths extends `${RemoveTrailingSlashes<TSearchPath>}${infer TRest}`\n ? TRest\n : never\n\nexport type SearchRelativePathAutoComplete<\n TRouter extends AnyRouter,\n TTo extends string,\n TSearchPath extends string,\n> = `${TTo}/${RemoveLeadingSlashes<SearchPaths<TRouter, TSearchPath>>}`\n\nexport type RelativeToParentPathAutoComplete<\n TRouter extends AnyRouter,\n TFrom extends string,\n TTo extends string,\n TResolvedPath extends string = RemoveTrailingSlashes<\n ResolveRelativePath<TFrom, TTo>\n >,\n> =\n | SearchRelativePathAutoComplete<TRouter, TTo, TResolvedPath>\n | (TResolvedPath extends '' ? never : `${TTo}/../`)\n\nexport type RelativeToCurrentPathAutoComplete<\n TRouter extends AnyRouter,\n TFrom extends string,\n TTo extends string,\n TRestTo extends string,\n TResolvedPath extends\n string = RemoveTrailingSlashes<`${RemoveTrailingSlashes<TFrom>}/${RemoveLeadingSlashes<TRestTo>}`>,\n> = SearchRelativePathAutoComplete<TRouter, TTo, TResolvedPath>\n\nexport type AbsolutePathAutoComplete<\n TRouter extends AnyRouter,\n TFrom extends string,\n> =\n | (string extends TFrom\n ? './'\n : TFrom extends `/`\n ? never\n : SearchPaths<TRouter, TFrom> extends ''\n ? never\n : './')\n | (string extends TFrom ? '../' : TFrom extends `/` ? never : '../')\n | RouteToPath<TRouter, TRouter['routeTree']>\n | (TFrom extends '/'\n ? never\n : string extends TFrom\n ? never\n : RemoveLeadingSlashes<SearchPaths<TRouter, TFrom>>)\n\nexport type RelativeToPathAutoComplete<\n TRouter extends AnyRouter,\n TFrom extends string,\n TTo extends string,\n> = TTo extends `..${string}`\n ? RelativeToParentPathAutoComplete<TRouter, TFrom, RemoveTrailingSlashes<TTo>>\n : TTo extends `./${infer TRestTTo}`\n ? RelativeToCurrentPathAutoComplete<\n TRouter,\n TFrom,\n RemoveTrailingSlashes<TTo>,\n TRestTTo\n >\n : AbsolutePathAutoComplete<TRouter, TFrom>\n\nexport type NavigateOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,\n TMaskTo extends string = '',\n> = ToOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & NavigateOptionProps\n\nexport interface NavigateOptionProps {\n // `replace` is a boolean that determines whether the navigation should replace the current history entry or push a new one.\n replace?: boolean\n resetScroll?: boolean\n /** @deprecated All navigations now use startTransition under the hood */\n startTransition?: boolean\n // if set to `true`, the router will wrap the resulting navigation in a document.startViewTransition() call.\n viewTransition?: boolean\n ignoreBlocker?: boolean\n}\n\nexport type ToOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,\n TMaskTo extends string = '',\n> = ToSubOptions<TRouter, TFrom, TTo> & MaskOptions<TRouter, TMaskFrom, TMaskTo>\n\nexport interface MaskOptions<\n in out TRouter extends AnyRouter,\n in out TMaskFrom extends RoutePaths<TRouter['routeTree']> | string,\n in out TMaskTo extends string,\n> {\n _fromLocation?: ParsedLocation\n mask?: ToMaskOptions<TRouter, TMaskFrom, TMaskTo>\n}\n\nexport type ToMaskOptions<\n TRouteTree extends AnyRouter = RegisteredRouter,\n TMaskFrom extends RoutePaths<TRouteTree['routeTree']> | string = string,\n TMaskTo extends string = '',\n> = ToSubOptions<TRouteTree, TMaskFrom, TMaskTo> & {\n unmaskOnReload?: boolean\n}\n\nexport type ToSubOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n TTo extends string = '',\n> = ToSubOptionsProps<TRouter, TFrom, TTo> &\n SearchParamOptions<TRouter, TFrom, TTo> &\n PathParamOptions<TRouter, TFrom, TTo>\n\nexport interface ToSubOptionsProps<\n in out TRouter extends AnyRouter = RegisteredRouter,\n in out TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n in out TTo extends string = '',\n> {\n to?: ToPathOption<TRouter, TFrom, TTo> & {}\n hash?: true | Updater<string>\n state?: true | NonNullableUpdater<HistoryState>\n // The source route path. This is automatically set when using route-level APIs, but for type-safe relative routing on the router itself, this is required\n from?: FromPathOption<TRouter, TFrom> & {}\n}\n\nexport type ParamsReducerFn<\n in out TRouter extends AnyRouter,\n in out TParamVariant extends ParamVariant,\n in out TFrom,\n in out TTo,\n> = (\n current: Expand<ResolveFromParams<TRouter, TParamVariant, TFrom>>,\n) => Expand<ResolveRelativeToParams<TRouter, TParamVariant, TFrom, TTo>>\n\ntype ParamsReducer<\n TRouter extends AnyRouter,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n> =\n | Expand<ResolveRelativeToParams<TRouter, TParamVariant, TFrom, TTo>>\n | (ParamsReducerFn<TRouter, TParamVariant, TFrom, TTo> & {})\n\ntype ParamVariant = 'PATH' | 'SEARCH'\n\nexport type ResolveRoute<\n TRouter extends AnyRouter,\n TFrom,\n TTo,\n TPath = ResolveRelativePath<TFrom, TTo>,\n> = TPath extends string\n ? string extends TTo\n ? RouteByPath<TRouter['routeTree'], TPath>\n : RouteByToPath<TRouter, TPath>\n : never\n\ntype ResolveFromParamType<TParamVariant extends ParamVariant> =\n TParamVariant extends 'PATH' ? 'allParams' : 'fullSearchSchema'\n\ntype ResolveFromAllParams<\n TRouter extends AnyRouter,\n TParamVariant extends ParamVariant,\n> = TParamVariant extends 'PATH'\n ? AllParams<TRouter['routeTree']>\n : FullSearchSchema<TRouter['routeTree']>\n\ntype ResolveFromParams<\n TRouter extends AnyRouter,\n TParamVariant extends ParamVariant,\n TFrom,\n> = string extends TFrom\n ? ResolveFromAllParams<TRouter, TParamVariant>\n : RouteByPath<\n TRouter['routeTree'],\n TFrom\n >['types'][ResolveFromParamType<TParamVariant>]\n\ntype ResolveToParamType<TParamVariant extends ParamVariant> =\n TParamVariant extends 'PATH' ? 'allParams' : 'fullSearchSchemaInput'\n\ntype ResolveAllToParams<\n TRouter extends AnyRouter,\n TParamVariant extends ParamVariant,\n> = TParamVariant extends 'PATH'\n ? AllParams<TRouter['routeTree']>\n : FullSearchSchemaInput<TRouter['routeTree']>\n\nexport type ResolveToParams<\n TRouter extends AnyRouter,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n> =\n ResolveRelativePath<TFrom, TTo> extends infer TPath\n ? string extends TPath\n ? ResolveAllToParams<TRouter, TParamVariant>\n : TPath extends CatchAllPaths\n ? ResolveAllToParams<TRouter, TParamVariant>\n : ResolveRoute<\n TRouter,\n TFrom,\n TTo\n >['types'][ResolveToParamType<TParamVariant>]\n : never\n\ntype ResolveRelativeToParams<\n TRouter extends AnyRouter,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n TToParams = ResolveToParams<TRouter, TParamVariant, TFrom, TTo>,\n> = TParamVariant extends 'SEARCH'\n ? TToParams\n : string extends TFrom\n ? TToParams\n : MakeDifferenceOptional<\n ResolveFromParams<TRouter, TParamVariant, TFrom>,\n TToParams\n >\n\ninterface MakeOptionalSearchParams<\n in out TRouter extends AnyRouter,\n in out TFrom,\n in out TTo,\n> {\n search?: true | (ParamsReducer<TRouter, 'SEARCH', TFrom, TTo> & {})\n}\n\ninterface MakeOptionalPathParams<\n in out TRouter extends AnyRouter,\n in out TFrom,\n in out TTo,\n> {\n params?: true | (ParamsReducer<TRouter, 'PATH', TFrom, TTo> & {})\n}\n\ntype MakeRequiredParamsReducer<\n TRouter extends AnyRouter,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n> =\n | (string extends TFrom\n ? never\n : ResolveFromParams<TRouter, TParamVariant, TFrom> extends WithoutEmpty<\n PickRequired<\n ResolveRelativeToParams<TRouter, TParamVariant, TFrom, TTo>\n >\n >\n ? true\n : never)\n | (ParamsReducer<TRouter, TParamVariant, TFrom, TTo> & {})\n\nexport interface MakeRequiredPathParams<\n in out TRouter extends AnyRouter,\n in out TFrom,\n in out TTo,\n> {\n params: MakeRequiredParamsReducer<TRouter, 'PATH', TFrom, TTo> & {}\n}\n\nexport interface MakeRequiredSearchParams<\n in out TRouter extends AnyRouter,\n in out TFrom,\n in out TTo,\n> {\n search: MakeRequiredParamsReducer<TRouter, 'SEARCH', TFrom, TTo> & {}\n}\n\nexport type IsRequiredParams<TParams> =\n Record<never, never> extends TParams ? never : true\n\nexport type IsRequired<\n TRouter extends AnyRouter,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n> =\n ResolveRelativePath<TFrom, TTo> extends infer TPath\n ? string extends TPath\n ? never\n : TPath extends CatchAllPaths\n ? never\n : IsRequiredParams<\n ResolveRelativeToParams<TRouter, TParamVariant, TFrom, TTo>\n >\n : never\n\nexport type SearchParamOptions<\n TRouter extends AnyRouter,\n TFrom,\n TTo extends string,\n> =\n IsRequired<TRouter, 'SEARCH', TFrom, TTo> extends never\n ? MakeOptionalSearchParams<TRouter, TFrom, TTo>\n : MakeRequiredSearchParams<TRouter, TFrom, TTo>\n\nexport type PathParamOptions<\n TRouter extends AnyRouter,\n TFrom,\n TTo extends string,\n> =\n IsRequired<TRouter, 'PATH', TFrom, TTo> extends never\n ? MakeOptionalPathParams<TRouter, TFrom, TTo>\n : MakeRequiredPathParams<TRouter, TFrom, TTo>\n\nexport type ToPathOption<\n TRouter extends AnyRouter = AnyRouter,\n TFrom extends string = string,\n TTo extends string = string,\n> =\n | CheckPath<TRouter, TTo, never, TFrom, TTo>\n | RelativeToPathAutoComplete<\n TRouter,\n NoInfer<TFrom> extends string ? NoInfer<TFrom> : '',\n NoInfer<TTo> & string\n >\n\nexport type CheckFromPath<\n TRouter extends AnyRouter,\n TPass,\n TFail,\n TFrom,\n> = string extends TFrom\n ? TPass\n : RouteByPath<TRouter['routeTree'], TFrom> extends never\n ? TFail\n : TPass\n\nexport type FromPathOption<TRouter extends AnyRouter, TFrom> =\n | CheckFromPath<\n TRouter,\n string extends TFrom ? TFrom & {} : TFrom,\n never,\n TFrom\n >\n | RoutePaths<TRouter['routeTree']>\n\nexport interface ActiveOptions {\n exact?: boolean\n includeHash?: boolean\n includeSearch?: boolean\n}\n\nexport type LinkOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string = '',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '',\n> = NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & LinkOptionsProps\n\nexport interface LinkOptionsProps {\n // The standard anchor tag target attribute\n target?: HTMLAnchorElement['target']\n // Defaults to `{ exact: false, includeHash: false }`\n activeOptions?: ActiveOptions\n // If set, will preload the linked route on hover and cache it for this many milliseconds in hopes that the user will eventually navigate there.\n preload?: false | 'intent'\n // Delay intent preloading by this many milliseconds. If the intent exits before this delay, the preload will be cancelled.\n preloadDelay?: number\n // If true, will render the link without the href attribute\n disabled?: boolean\n}\n\nexport type CheckPath<TRouter extends AnyRouter, TPass, TFail, TFrom, TTo> =\n string extends ResolveRelativePath<TFrom, TTo>\n ? TPass\n : ResolveRelativePath<TFrom, TTo> extends CatchAllPaths\n ? TPass\n : ResolveRoute<TRouter, TFrom, TTo> extends never\n ? TFail\n : TPass\n\nexport type ResolveRelativePath<TFrom, TTo = '.'> = string extends TFrom\n ? TTo\n : string extends TTo\n ? TFrom\n : TFrom extends string\n ? TTo extends string\n ? TTo extends '.'\n ? TFrom\n : TTo extends `./`\n ? Join<[TFrom, '/']>\n : TTo extends `./${infer TRest}`\n ? ResolveRelativePath<TFrom, TRest>\n : TTo extends `/${infer TRest}`\n ? TTo\n : Split<TTo> extends ['..', ...infer ToRest]\n ? Split<TFrom> extends [...infer FromRest, infer FromTail]\n ? ToRest extends ['/']\n ? Join<['/', ...FromRest, '/']>\n : ResolveRelativePath<Join<FromRest>, Join<ToRest>>\n : never\n : Split<TTo> extends ['.', ...infer ToRest]\n ? ToRest extends ['/']\n ? Join<[TFrom, '/']>\n : ResolveRelativePath<TFrom, Join<ToRest>>\n : CleanPath<Join<['/', ...Split<TFrom>, ...Split<TTo>]>>\n : never\n : never\n\n// type Test1 = ResolveRelativePath<'/', '/posts'>\n// // ^?\n// type Test4 = ResolveRelativePath<'/posts/1/comments', '../..'>\n// // ^?\n// type Test5 = ResolveRelativePath<'/posts/1/comments', '../../..'>\n// // ^?\n// type Test6 = ResolveRelativePath<'/posts/1/comments', './1'>\n// // ^?\n// type Test7 = ResolveRelativePath<'/posts/1/comments', './1/2'>\n// // ^?\n// type Test8 = ResolveRelativePath<'/posts/1/comments', '../edit'>\n// // ^?\n// type Test9 = ResolveRelativePath<'/posts/1/comments', '1'>\n// // ^?\n// type Test10 = ResolveRelativePath<'/posts/1/comments', './1'>\n// // ^?\n// type Test11 = ResolveRelativePath<'/posts/1/comments', './1/2'>\n// // ^?\n\ntype LinkCurrentTargetElement = {\n preloadTimeout?: null | ReturnType<typeof setTimeout>\n}\n\nconst preloadWarning = 'Error preloading route! ☝️'\n\nexport function useLinkProps<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,\n TMaskTo extends string = '',\n>(\n options: UseLinkPropsOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,\n): React.AnchorHTMLAttributes<HTMLAnchorElement> {\n const router = useRouter()\n const [isTransitioning, setIsTransitioning] = React.useState(false)\n\n const {\n // custom props\n activeProps = () => ({ className: 'active' }),\n inactiveProps = () => ({}),\n activeOptions,\n hash,\n search,\n params,\n to,\n state,\n mask,\n preload: userPreload,\n preloadDelay: userPreloadDelay,\n replace,\n startTransition,\n resetScroll,\n viewTransition,\n // element props\n children,\n target,\n disabled,\n style,\n className,\n onClick,\n onFocus,\n onMouseEnter,\n onMouseLeave,\n onTouchStart,\n ignoreBlocker,\n ...rest\n } = options\n\n // If this link simply reloads the current route,\n // make sure it has a new key so it will trigger a data refresh\n\n // If this `to` is a valid external URL, return\n // null for LinkUtils\n\n let type: 'internal' | 'external' = 'internal'\n\n try {\n new URL(`${to}`)\n type = 'external'\n } catch {}\n\n const next = router.buildLocation(options as any)\n const preload = userPreload ?? router.options.defaultPreload\n const preloadDelay =\n userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0\n\n const isActive = useRouterState({\n select: (s) => {\n // Compare path/hash for matches\n const currentPathSplit = removeTrailingSlash(\n s.location.pathname,\n router.basepath,\n ).split('/')\n const nextPathSplit = removeTrailingSlash(\n next.pathname,\n router.basepath,\n ).split('/')\n const pathIsFuzzyEqual = nextPathSplit.every(\n (d, i) => d === currentPathSplit[i],\n )\n // Combine the matches based on user router.options\n const pathTest = activeOptions?.exact\n ? exactPathTest(s.location.pathname, next.pathname, router.basepath)\n : pathIsFuzzyEqual\n const hashTest = activeOptions?.includeHash\n ? s.location.hash === next.hash\n : true\n const searchTest =\n (activeOptions?.includeSearch ?? true)\n ? deepEqual(s.location.search, next.search, !activeOptions?.exact)\n : true\n\n // The final \"active\" test\n return pathTest && hashTest && searchTest\n },\n })\n\n if (type === 'external') {\n return {\n ...rest,\n type,\n href: to,\n ...(children && { children }),\n ...(target && { target }),\n ...(disabled && { disabled }),\n ...(style && { style }),\n ...(className && { className }),\n ...(onClick && { onClick }),\n ...(onFocus && { onFocus }),\n ...(onMouseEnter && { onMouseEnter }),\n ...(onMouseLeave && { onMouseLeave }),\n ...(onTouchStart && { onTouchStart }),\n }\n }\n\n // The click handler\n const handleClick = (e: MouseEvent) => {\n if (\n !disabled &&\n !isCtrlEvent(e) &&\n !e.defaultPrevented &&\n (!target || target === '_self') &&\n e.button === 0\n ) {\n e.preventDefault()\n\n flushSync(() => {\n setIsTransitioning(true)\n })\n\n const unsub = router.subscribe('onResolved', () => {\n unsub()\n setIsTransitioning(false)\n })\n\n // All is well? Navigate!\n router.commitLocation({\n ...next,\n replace,\n resetScroll,\n startTransition,\n viewTransition,\n ignoreBlocker,\n })\n }\n }\n\n const doPreload = () => {\n router.preloadRoute(options as any).catch((err) => {\n console.warn(err)\n console.warn(preloadWarning)\n })\n }\n\n // The click handler\n const handleFocus = (e: MouseEvent) => {\n if (disabled) return\n if (preload) {\n doPreload()\n }\n }\n\n const handleTouchStart = handleFocus\n\n const handleEnter = (e: MouseEvent) => {\n if (disabled) return\n const eventTarget = (e.target || {}) as LinkCurrentTargetElement\n\n if (preload) {\n if (eventTarget.preloadTimeout) {\n return\n }\n\n eventTarget.preloadTimeout = setTimeout(() => {\n eventTarget.preloadTimeout = null\n doPreload()\n }, preloadDelay)\n }\n }\n\n const handleLeave = (e: MouseEvent) => {\n if (disabled) return\n const eventTarget = (e.target || {}) as LinkCurrentTargetElement\n\n if (eventTarget.preloadTimeout) {\n clearTimeout(eventTarget.preloadTimeout)\n eventTarget.preloadTimeout = null\n }\n }\n\n const composeHandlers =\n (handlers: Array<undefined | ((e: any) => void)>) =>\n (e: { persist?: () => void; defaultPrevented: boolean }) => {\n e.persist?.()\n handlers.filter(Boolean).forEach((handler) => {\n if (e.defaultPrevented) return\n handler!(e)\n })\n }\n\n // Get the active props\n const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> = isActive\n ? (functionalUpdate(activeProps as any, {}) ?? {})\n : {}\n\n // Get the inactive props\n const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =\n isActive ? {} : functionalUpdate(inactiveProps, {})\n\n const resolvedClassName = [\n className,\n resolvedActiveProps.className,\n resolvedInactiveProps.className,\n ]\n .filter(Boolean)\n .join(' ')\n\n const resolvedStyle = {\n ...style,\n ...resolvedActiveProps.style,\n ...resolvedInactiveProps.style,\n }\n\n return {\n ...resolvedActiveProps,\n ...resolvedInactiveProps,\n ...rest,\n href: disabled\n ? undefined\n : next.maskedLocation\n ? router.history.createHref(next.maskedLocation.href)\n : router.history.createHref(next.href),\n onClick: composeHandlers([onClick, handleClick]),\n onFocus: composeHandlers([onFocus, handleFocus]),\n onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),\n onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),\n onTouchStart: composeHandlers([onTouchStart, handleTouchStart]),\n disabled: !!disabled,\n target,\n ...(Object.keys(resolvedStyle).length && { style: resolvedStyle }),\n ...(resolvedClassName && { className: resolvedClassName }),\n ...(disabled && {\n role: 'link',\n 'aria-disabled': true,\n }),\n ...(isActive && { 'data-status': 'active', 'aria-current': 'page' }),\n ...(isTransitioning && { 'data-transitioning': 'transitioning' }),\n }\n}\n\nexport type UseLinkPropsOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,\n TMaskTo extends string = '',\n> = ActiveLinkOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &\n React.AnchorHTMLAttributes<HTMLAnchorElement>\n\nexport type ActiveLinkOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string = '',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '',\n> = LinkOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & ActiveLinkOptionProps\n\nexport interface ActiveLinkOptionProps {\n /**\n * A function that returns additional props for the `active` state of this link.\n * These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)\n */\n activeProps?:\n | React.AnchorHTMLAttributes<HTMLAnchorElement>\n | (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)\n /**\n * A function that returns additional props for the `inactive` state of this link.\n * These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)\n */\n inactiveProps?:\n | React.AnchorHTMLAttributes<HTMLAnchorElement>\n | (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)\n}\n\nexport type LinkProps<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string = '',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '',\n> = ActiveLinkOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &\n LinkPropsChildren\n\nexport interface LinkPropsChildren {\n // 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\n children?:\n | React.ReactNode\n | ((state: {\n isActive: boolean\n isTransitioning: boolean\n }) => React.ReactNode)\n}\n\ntype LinkComponentReactProps<TComp> = React.PropsWithoutRef<\n TComp extends React.FC<infer TProps> | React.Component<infer TProps>\n ? TProps\n : TComp extends keyof React.JSX.IntrinsicElements\n ? Omit<React.HTMLProps<TComp>, 'children' | 'preload'>\n : never\n> &\n React.RefAttributes<\n TComp extends\n | React.FC<{ ref: infer TRef }>\n | React.Component<{ ref: infer TRef }>\n ? TRef\n : TComp extends keyof React.JSX.IntrinsicElements\n ? React.ComponentRef<TComp>\n : never\n >\n\nexport type LinkComponentProps<\n TComp,\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string = '',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '',\n> = LinkComponentReactProps<TComp> &\n LinkProps<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>\n\nexport type LinkComponent<TComp> = <\n TRouter extends RegisteredRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string = '',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '',\n>(\n props: LinkComponentProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,\n) => React.ReactElement\n\nexport function createLink<const TComp>(Comp: TComp): LinkComponent<TComp> {\n return React.forwardRef(function CreatedLink(props, ref) {\n return <Link {...(props as any)} _asChild={Comp} ref={ref} />\n }) as any\n}\n\nexport const Link: LinkComponent<'a'> = React.forwardRef((props: any, ref) => {\n const { _asChild, ...rest } = props\n const { type, ...linkProps } = useLinkProps(rest)\n\n const children =\n typeof rest.children === 'function'\n ? rest.children({\n isActive: (linkProps as any)['data-status'] === 'active',\n })\n : rest.children\n\n if (typeof _asChild === 'undefined') {\n // the ReturnType of useLinkProps returns the correct type for a <a> element, not a general component that has a delete prop\n // @ts-expect-error\n delete linkProps.disabled\n }\n\n return React.createElement(\n _asChild ? _asChild : 'a',\n {\n ...linkProps,\n ref,\n },\n children,\n )\n}) as any\n\nfunction isCtrlEvent(e: MouseEvent) {\n return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)\n}\n"],"names":[],"mappings":";;;;;;;;AAshBA;AAEO;AASL;AACA;AAEM;AAAA;AAAA;;AAGmB;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACS;AACK;AACd;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AASL;AAEI;AACE;AACG;AAAA;AACD;AAEF;AACA;AACN;AAGA;AAAgC;AAG5B;AAAyB;AACZ;AACJ;AAET;AAAsB;AACf;AACE;AAET;AAAuC;AACH;AAG9B;AAGN;AAGA;AAMA;AAA+B;AACjC;AAGF;AACS;AAAA;AACF;AACH;AACM;AACqB;AACJ;AACI;AACN;AACQ;AACJ;AACA;AACU;AACA;AACA;AAAA;AAKjC;AACJ;AAOE;AAEA;AACE;AAAuB;AAGzB;AACQ;AACN;AAAwB;AAI1B;AAAsB;AACjB;AACH;AACA;AACA;AACA;AACA;AACD;AACH;AAGF;AACE;AACE;AACA;AAA2B;AAC5B;AAIG;AACJ;AACA;AACY;;AACZ;AAGF;AAEM;AACJ;AACM;AAEN;AACE;AACE;AAAA;AAGU;AACV;AACU;;AACG;AACjB;AAGI;AACJ;AACM;AAEN;AACE;AACA;AAA6B;AAC/B;AAGF;;AAGI;AACA;AACE;AACA;AAAU;AACX;AAIC;AAKN;AAGA;AAA0B;AACxB;AACoB;AACE;AAKxB;AAAsB;AACjB;AACoB;AACE;AAGpB;AAAA;AACF;AACA;AACA;AAKsC;AACM;AACA;AACU;AACA;AACK;AAClD;AACZ;AACgE;AACR;AACxC;AACR;AACW;AACnB;AACkE;AACH;AAEnE;AA4FO;AACL;AACE;AAA2D;AAE/D;AAEO;AACL;AACA;AAEA;AAEoB;AACoC;AAIpD;AAGF;AAAiB;AAGnB;AAAa;AACW;AACtB;AACK;AACH;AACF;AACA;AAEJ;AAEA;AACS;AACT;;;;;;"}
|
|
1
|
+
{"version":3,"file":"link.js","sources":["../../src/link.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { flushSync } from 'react-dom'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\nimport {\n deepEqual,\n functionalUpdate,\n useForwardedRef,\n useIntersectionObserver,\n} from './utils'\nimport { exactPathTest, removeTrailingSlash } from './path'\nimport type { AnyRouter, ParsedLocation } from '.'\nimport type { HistoryState } from '@tanstack/history'\nimport type {\n AllParams,\n CatchAllPaths,\n FullSearchSchema,\n FullSearchSchemaInput,\n RouteByPath,\n RouteByToPath,\n RoutePaths,\n RouteToPath,\n} from './routeInfo'\nimport type { RegisteredRouter } from './router'\nimport type {\n Expand,\n MakeDifferenceOptional,\n NoInfer,\n NonNullableUpdater,\n PickRequired,\n Updater,\n WithoutEmpty,\n} from './utils'\n\nexport type CleanPath<T extends string> = T extends `${infer L}//${infer R}`\n ? CleanPath<`${CleanPath<L>}/${CleanPath<R>}`>\n : T extends `${infer L}//`\n ? `${CleanPath<L>}/`\n : T extends `//${infer L}`\n ? `/${CleanPath<L>}`\n : T\n\nexport type Split<TValue, TIncludeTrailingSlash = true> = TValue extends unknown\n ? string extends TValue\n ? Array<string>\n : TValue extends string\n ? CleanPath<TValue> extends ''\n ? []\n : TIncludeTrailingSlash extends true\n ? CleanPath<TValue> extends `${infer T}/`\n ? [...Split<T>, '/']\n : CleanPath<TValue> extends `/${infer U}`\n ? Split<U>\n : CleanPath<TValue> extends `${infer T}/${infer U}`\n ? [...Split<T>, ...Split<U>]\n : [TValue]\n : CleanPath<TValue> extends `${infer T}/${infer U}`\n ? [...Split<T>, ...Split<U>]\n : TValue extends string\n ? [TValue]\n : never\n : never\n : never\n\nexport type ParsePathParams<\n T extends string,\n TAcc = never,\n> = T extends `${string}$${infer TPossiblyParam}`\n ? TPossiblyParam extends `${infer TParam}/${infer TRest}`\n ? ParsePathParams<TRest, TParam extends '' ? '_splat' : TParam | TAcc>\n : TPossiblyParam extends ''\n ? '_splat'\n : TPossiblyParam | TAcc\n : TAcc\n\nexport type Join<T, TDelimiter extends string = '/'> = T extends []\n ? ''\n : T extends [infer L extends string]\n ? L\n : T extends [\n infer L extends string,\n ...infer Tail extends [...Array<string>],\n ]\n ? CleanPath<`${L}${TDelimiter}${Join<Tail>}`>\n : never\n\nexport type Last<T extends Array<any>> = T extends [...infer _, infer L]\n ? L\n : never\n\nexport type RemoveTrailingSlashes<T> = T extends `${infer R}/` ? R : T\n\nexport type RemoveLeadingSlashes<T> = T extends `/${infer R}` ? R : T\n\nexport type ResolvePaths<TRouter extends AnyRouter, TSearchPath> =\n RouteByPath<\n TRouter['routeTree'],\n RemoveTrailingSlashes<TSearchPath>\n > extends never\n ? RouteToPath<TRouter, TRouter['routeTree']>\n : RouteToPath<\n TRouter,\n RouteByPath<TRouter['routeTree'], RemoveTrailingSlashes<TSearchPath>>\n >\n\nexport type SearchPaths<\n TRouter extends AnyRouter,\n TSearchPath extends string,\n TPaths = ResolvePaths<TRouter, TSearchPath>,\n> = TPaths extends `${RemoveTrailingSlashes<TSearchPath>}${infer TRest}`\n ? TRest\n : never\n\nexport type SearchRelativePathAutoComplete<\n TRouter extends AnyRouter,\n TTo extends string,\n TSearchPath extends string,\n> = `${TTo}/${RemoveLeadingSlashes<SearchPaths<TRouter, TSearchPath>>}`\n\nexport type RelativeToParentPathAutoComplete<\n TRouter extends AnyRouter,\n TFrom extends string,\n TTo extends string,\n TResolvedPath extends string = RemoveTrailingSlashes<\n ResolveRelativePath<TFrom, TTo>\n >,\n> =\n | SearchRelativePathAutoComplete<TRouter, TTo, TResolvedPath>\n | (TResolvedPath extends '' ? never : `${TTo}/../`)\n\nexport type RelativeToCurrentPathAutoComplete<\n TRouter extends AnyRouter,\n TFrom extends string,\n TTo extends string,\n TRestTo extends string,\n TResolvedPath extends\n string = RemoveTrailingSlashes<`${RemoveTrailingSlashes<TFrom>}/${RemoveLeadingSlashes<TRestTo>}`>,\n> = SearchRelativePathAutoComplete<TRouter, TTo, TResolvedPath>\n\nexport type AbsolutePathAutoComplete<\n TRouter extends AnyRouter,\n TFrom extends string,\n> =\n | (string extends TFrom\n ? './'\n : TFrom extends `/`\n ? never\n : SearchPaths<TRouter, TFrom> extends ''\n ? never\n : './')\n | (string extends TFrom ? '../' : TFrom extends `/` ? never : '../')\n | RouteToPath<TRouter, TRouter['routeTree']>\n | (TFrom extends '/'\n ? never\n : string extends TFrom\n ? never\n : RemoveLeadingSlashes<SearchPaths<TRouter, TFrom>>)\n\nexport type RelativeToPathAutoComplete<\n TRouter extends AnyRouter,\n TFrom extends string,\n TTo extends string,\n> = TTo extends `..${string}`\n ? RelativeToParentPathAutoComplete<TRouter, TFrom, RemoveTrailingSlashes<TTo>>\n : TTo extends `./${infer TRestTTo}`\n ? RelativeToCurrentPathAutoComplete<\n TRouter,\n TFrom,\n RemoveTrailingSlashes<TTo>,\n TRestTTo\n >\n : AbsolutePathAutoComplete<TRouter, TFrom>\n\nexport type NavigateOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,\n TMaskTo extends string = '',\n> = ToOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & NavigateOptionProps\n\nexport interface NavigateOptionProps {\n // `replace` is a boolean that determines whether the navigation should replace the current history entry or push a new one.\n replace?: boolean\n resetScroll?: boolean\n /** @deprecated All navigations now use startTransition under the hood */\n startTransition?: boolean\n // if set to `true`, the router will wrap the resulting navigation in a document.startViewTransition() call.\n viewTransition?: boolean\n ignoreBlocker?: boolean\n}\n\nexport type ToOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,\n TMaskTo extends string = '',\n> = ToSubOptions<TRouter, TFrom, TTo> & MaskOptions<TRouter, TMaskFrom, TMaskTo>\n\nexport interface MaskOptions<\n in out TRouter extends AnyRouter,\n in out TMaskFrom extends RoutePaths<TRouter['routeTree']> | string,\n in out TMaskTo extends string,\n> {\n _fromLocation?: ParsedLocation\n mask?: ToMaskOptions<TRouter, TMaskFrom, TMaskTo>\n}\n\nexport type ToMaskOptions<\n TRouteTree extends AnyRouter = RegisteredRouter,\n TMaskFrom extends RoutePaths<TRouteTree['routeTree']> | string = string,\n TMaskTo extends string = '',\n> = ToSubOptions<TRouteTree, TMaskFrom, TMaskTo> & {\n unmaskOnReload?: boolean\n}\n\nexport type ToSubOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n TTo extends string = '',\n> = ToSubOptionsProps<TRouter, TFrom, TTo> &\n SearchParamOptions<TRouter, TFrom, TTo> &\n PathParamOptions<TRouter, TFrom, TTo>\n\nexport interface ToSubOptionsProps<\n in out TRouter extends AnyRouter = RegisteredRouter,\n in out TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n in out TTo extends string = '',\n> {\n to?: ToPathOption<TRouter, TFrom, TTo> & {}\n hash?: true | Updater<string>\n state?: true | NonNullableUpdater<HistoryState>\n // The source route path. This is automatically set when using route-level APIs, but for type-safe relative routing on the router itself, this is required\n from?: FromPathOption<TRouter, TFrom> & {}\n}\n\nexport type ParamsReducerFn<\n in out TRouter extends AnyRouter,\n in out TParamVariant extends ParamVariant,\n in out TFrom,\n in out TTo,\n> = (\n current: Expand<ResolveFromParams<TRouter, TParamVariant, TFrom>>,\n) => Expand<ResolveRelativeToParams<TRouter, TParamVariant, TFrom, TTo>>\n\ntype ParamsReducer<\n TRouter extends AnyRouter,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n> =\n | Expand<ResolveRelativeToParams<TRouter, TParamVariant, TFrom, TTo>>\n | (ParamsReducerFn<TRouter, TParamVariant, TFrom, TTo> & {})\n\ntype ParamVariant = 'PATH' | 'SEARCH'\n\nexport type ResolveRoute<\n TRouter extends AnyRouter,\n TFrom,\n TTo,\n TPath = ResolveRelativePath<TFrom, TTo>,\n> = TPath extends string\n ? TFrom extends TPath\n ? RouteByPath<TRouter['routeTree'], TPath>\n : RouteByToPath<TRouter, TPath>\n : never\n\ntype ResolveFromParamType<TParamVariant extends ParamVariant> =\n TParamVariant extends 'PATH' ? 'allParams' : 'fullSearchSchema'\n\ntype ResolveFromAllParams<\n TRouter extends AnyRouter,\n TParamVariant extends ParamVariant,\n> = TParamVariant extends 'PATH'\n ? AllParams<TRouter['routeTree']>\n : FullSearchSchema<TRouter['routeTree']>\n\ntype ResolveFromParams<\n TRouter extends AnyRouter,\n TParamVariant extends ParamVariant,\n TFrom,\n> = string extends TFrom\n ? ResolveFromAllParams<TRouter, TParamVariant>\n : RouteByPath<\n TRouter['routeTree'],\n TFrom\n >['types'][ResolveFromParamType<TParamVariant>]\n\ntype ResolveToParamType<TParamVariant extends ParamVariant> =\n TParamVariant extends 'PATH' ? 'allParams' : 'fullSearchSchemaInput'\n\ntype ResolveAllToParams<\n TRouter extends AnyRouter,\n TParamVariant extends ParamVariant,\n> = TParamVariant extends 'PATH'\n ? AllParams<TRouter['routeTree']>\n : FullSearchSchemaInput<TRouter['routeTree']>\n\nexport type ResolveToParams<\n TRouter extends AnyRouter,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n> =\n ResolveRelativePath<TFrom, TTo> extends infer TPath\n ? string extends TPath\n ? ResolveAllToParams<TRouter, TParamVariant>\n : TPath extends CatchAllPaths\n ? ResolveAllToParams<TRouter, TParamVariant>\n : ResolveRoute<\n TRouter,\n TFrom,\n TTo\n >['types'][ResolveToParamType<TParamVariant>]\n : never\n\ntype ResolveRelativeToParams<\n TRouter extends AnyRouter,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n TToParams = ResolveToParams<TRouter, TParamVariant, TFrom, TTo>,\n> = TParamVariant extends 'SEARCH'\n ? TToParams\n : string extends TFrom\n ? TToParams\n : MakeDifferenceOptional<\n ResolveFromParams<TRouter, TParamVariant, TFrom>,\n TToParams\n >\n\ninterface MakeOptionalSearchParams<\n in out TRouter extends AnyRouter,\n in out TFrom,\n in out TTo,\n> {\n search?: true | (ParamsReducer<TRouter, 'SEARCH', TFrom, TTo> & {})\n}\n\ninterface MakeOptionalPathParams<\n in out TRouter extends AnyRouter,\n in out TFrom,\n in out TTo,\n> {\n params?: true | (ParamsReducer<TRouter, 'PATH', TFrom, TTo> & {})\n}\n\ntype MakeRequiredParamsReducer<\n TRouter extends AnyRouter,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n> =\n | (string extends TFrom\n ? never\n : ResolveFromParams<TRouter, TParamVariant, TFrom> extends WithoutEmpty<\n PickRequired<\n ResolveRelativeToParams<TRouter, TParamVariant, TFrom, TTo>\n >\n >\n ? true\n : never)\n | (ParamsReducer<TRouter, TParamVariant, TFrom, TTo> & {})\n\nexport interface MakeRequiredPathParams<\n in out TRouter extends AnyRouter,\n in out TFrom,\n in out TTo,\n> {\n params: MakeRequiredParamsReducer<TRouter, 'PATH', TFrom, TTo> & {}\n}\n\nexport interface MakeRequiredSearchParams<\n in out TRouter extends AnyRouter,\n in out TFrom,\n in out TTo,\n> {\n search: MakeRequiredParamsReducer<TRouter, 'SEARCH', TFrom, TTo> & {}\n}\n\nexport type IsRequiredParams<TParams> =\n Record<never, never> extends TParams ? never : true\n\nexport type IsRequired<\n TRouter extends AnyRouter,\n TParamVariant extends ParamVariant,\n TFrom,\n TTo,\n> =\n ResolveRelativePath<TFrom, TTo> extends infer TPath\n ? string extends TPath\n ? never\n : TPath extends CatchAllPaths\n ? never\n : IsRequiredParams<\n ResolveRelativeToParams<TRouter, TParamVariant, TFrom, TTo>\n >\n : never\n\nexport type SearchParamOptions<\n TRouter extends AnyRouter,\n TFrom,\n TTo extends string,\n> =\n IsRequired<TRouter, 'SEARCH', TFrom, TTo> extends never\n ? MakeOptionalSearchParams<TRouter, TFrom, TTo>\n : MakeRequiredSearchParams<TRouter, TFrom, TTo>\n\nexport type PathParamOptions<\n TRouter extends AnyRouter,\n TFrom,\n TTo extends string,\n> =\n IsRequired<TRouter, 'PATH', TFrom, TTo> extends never\n ? MakeOptionalPathParams<TRouter, TFrom, TTo>\n : MakeRequiredPathParams<TRouter, TFrom, TTo>\n\nexport type ToPathOption<\n TRouter extends AnyRouter = AnyRouter,\n TFrom extends string = string,\n TTo extends string = string,\n> =\n | CheckPath<TRouter, TTo, never, TFrom, TTo>\n | RelativeToPathAutoComplete<\n TRouter,\n NoInfer<TFrom> extends string ? NoInfer<TFrom> : '',\n NoInfer<TTo> & string\n >\n\nexport type CheckFromPath<\n TRouter extends AnyRouter,\n TPass,\n TFail,\n TFrom,\n> = string extends TFrom\n ? TPass\n : RouteByPath<TRouter['routeTree'], TFrom> extends never\n ? TFail\n : TPass\n\nexport type FromPathOption<TRouter extends AnyRouter, TFrom> =\n | CheckFromPath<\n TRouter,\n string extends TFrom ? TFrom & {} : TFrom,\n never,\n TFrom\n >\n | RoutePaths<TRouter['routeTree']>\n\nexport interface ActiveOptions {\n exact?: boolean\n includeHash?: boolean\n includeSearch?: boolean\n}\n\nexport type LinkOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string = '',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '',\n> = NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & LinkOptionsProps\n\nexport interface LinkOptionsProps {\n /**\n * The standard anchor tag target attribute\n */\n target?: HTMLAnchorElement['target']\n /**\n * Configurable options to determine if the link should be considered active or not\n * @default {exact:true,includeHash:true}\n */\n activeOptions?: ActiveOptions\n /**\n * The preloading strategy for this link\n * - `false` - No preloading\n * - `'intent'` - Preload the linked route on hover and cache it for this many milliseconds in hopes that the user will eventually navigate there.\n * - `'viewport'` - Preload the linked route when it enters the viewport\n */\n preload?: false | 'intent' | 'viewport'\n /**\n * When a preload strategy is set, this delays the preload by this many milliseconds.\n * If the user exits the link before this delay, the preload will be cancelled.\n */\n preloadDelay?: number\n /**\n * Control whether the link should be disabled or not\n * If set to `true`, the link will be rendered without an `href` attribute\n * @default false\n */\n disabled?: boolean\n}\n\nexport type CheckPath<TRouter extends AnyRouter, TPass, TFail, TFrom, TTo> =\n string extends ResolveRelativePath<TFrom, TTo>\n ? TPass\n : ResolveRelativePath<TFrom, TTo> extends CatchAllPaths\n ? TPass\n : ResolveRoute<TRouter, TFrom, TTo> extends never\n ? TFail\n : TPass\n\nexport type ResolveRelativePath<TFrom, TTo = '.'> = string extends TFrom\n ? TTo\n : string extends TTo\n ? TFrom\n : TFrom extends string\n ? TTo extends string\n ? TTo extends '.'\n ? TFrom\n : TTo extends `./`\n ? Join<[TFrom, '/']>\n : TTo extends `./${infer TRest}`\n ? ResolveRelativePath<TFrom, TRest>\n : TTo extends `/${infer TRest}`\n ? TTo\n : Split<TTo> extends ['..', ...infer ToRest]\n ? Split<TFrom> extends [...infer FromRest, infer FromTail]\n ? ToRest extends ['/']\n ? Join<['/', ...FromRest, '/']>\n : ResolveRelativePath<Join<FromRest>, Join<ToRest>>\n : never\n : Split<TTo> extends ['.', ...infer ToRest]\n ? ToRest extends ['/']\n ? Join<[TFrom, '/']>\n : ResolveRelativePath<TFrom, Join<ToRest>>\n : CleanPath<Join<['/', ...Split<TFrom>, ...Split<TTo>]>>\n : never\n : never\n\n// type Test1 = ResolveRelativePath<'/', '/posts'>\n// // ^?\n// type Test4 = ResolveRelativePath<'/posts/1/comments', '../..'>\n// // ^?\n// type Test5 = ResolveRelativePath<'/posts/1/comments', '../../..'>\n// // ^?\n// type Test6 = ResolveRelativePath<'/posts/1/comments', './1'>\n// // ^?\n// type Test7 = ResolveRelativePath<'/posts/1/comments', './1/2'>\n// // ^?\n// type Test8 = ResolveRelativePath<'/posts/1/comments', '../edit'>\n// // ^?\n// type Test9 = ResolveRelativePath<'/posts/1/comments', '1'>\n// // ^?\n// type Test10 = ResolveRelativePath<'/posts/1/comments', './1'>\n// // ^?\n// type Test11 = ResolveRelativePath<'/posts/1/comments', './1/2'>\n// // ^?\n\ntype LinkCurrentTargetElement = {\n preloadTimeout?: null | ReturnType<typeof setTimeout>\n}\n\nconst preloadWarning = 'Error preloading route! ☝️'\n\nexport function useLinkProps<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,\n TMaskTo extends string = '',\n>(\n options: UseLinkPropsOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,\n forwardedRef?: React.ForwardedRef<Element>,\n): React.ComponentPropsWithRef<'a'> {\n const router = useRouter()\n const [isTransitioning, setIsTransitioning] = React.useState(false)\n const innerRef = useForwardedRef(forwardedRef)\n\n const {\n // custom props\n activeProps = () => ({ className: 'active' }),\n inactiveProps = () => ({}),\n activeOptions,\n hash,\n search,\n params,\n to,\n state,\n mask,\n preload: userPreload,\n preloadDelay: userPreloadDelay,\n replace,\n startTransition,\n resetScroll,\n viewTransition,\n // element props\n children,\n target,\n disabled,\n style,\n className,\n onClick,\n onFocus,\n onMouseEnter,\n onMouseLeave,\n onTouchStart,\n ignoreBlocker,\n ...rest\n } = options\n\n // If this link simply reloads the current route,\n // make sure it has a new key so it will trigger a data refresh\n\n // If this `to` is a valid external URL, return\n // null for LinkUtils\n\n const type: 'internal' | 'external' = React.useMemo(() => {\n try {\n new URL(`${to}`)\n return 'external'\n } catch {}\n return 'internal'\n }, [to])\n\n const next = React.useMemo(\n () => router.buildLocation(options as any),\n [router, options],\n )\n const preload = React.useMemo(\n () => userPreload ?? router.options.defaultPreload,\n [router.options.defaultPreload, userPreload],\n )\n const preloadDelay =\n userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0\n\n const isActive = useRouterState({\n select: (s) => {\n // Compare path/hash for matches\n const currentPathSplit = removeTrailingSlash(\n s.location.pathname,\n router.basepath,\n ).split('/')\n const nextPathSplit = removeTrailingSlash(\n next.pathname,\n router.basepath,\n ).split('/')\n const pathIsFuzzyEqual = nextPathSplit.every(\n (d, i) => d === currentPathSplit[i],\n )\n // Combine the matches based on user router.options\n const pathTest = activeOptions?.exact\n ? exactPathTest(s.location.pathname, next.pathname, router.basepath)\n : pathIsFuzzyEqual\n const hashTest = activeOptions?.includeHash\n ? s.location.hash === next.hash\n : true\n const searchTest =\n (activeOptions?.includeSearch ?? true)\n ? deepEqual(s.location.search, next.search, !activeOptions?.exact)\n : true\n\n // The final \"active\" test\n return pathTest && hashTest && searchTest\n },\n })\n\n const doPreload = React.useCallback(() => {\n router.preloadRoute(options as any).catch((err) => {\n console.warn(err)\n console.warn(preloadWarning)\n })\n }, [options, router])\n\n const viewportIntersectionCallback = React.useCallback(\n (entry: IntersectionObserverEntry | undefined) => {\n if (entry?.isIntersecting) {\n doPreload()\n }\n },\n [doPreload],\n )\n\n useIntersectionObserver(\n innerRef,\n {\n rootMargin: '100px',\n },\n {\n disabled: !!disabled || preload !== 'viewport',\n },\n viewportIntersectionCallback,\n )\n\n if (type === 'external') {\n return {\n ...rest,\n ref: innerRef as React.ComponentPropsWithRef<'a'>['ref'],\n type,\n href: to,\n ...(children && { children }),\n ...(target && { target }),\n ...(disabled && { disabled }),\n ...(style && { style }),\n ...(className && { className }),\n ...(onClick && { onClick }),\n ...(onFocus && { onFocus }),\n ...(onMouseEnter && { onMouseEnter }),\n ...(onMouseLeave && { onMouseLeave }),\n ...(onTouchStart && { onTouchStart }),\n }\n }\n\n // The click handler\n const handleClick = (e: MouseEvent) => {\n if (\n !disabled &&\n !isCtrlEvent(e) &&\n !e.defaultPrevented &&\n (!target || target === '_self') &&\n e.button === 0\n ) {\n e.preventDefault()\n\n flushSync(() => {\n setIsTransitioning(true)\n })\n\n const unsub = router.subscribe('onResolved', () => {\n unsub()\n setIsTransitioning(false)\n })\n\n // All is well? Navigate!\n router.commitLocation({\n ...next,\n replace,\n resetScroll,\n startTransition,\n viewTransition,\n ignoreBlocker,\n })\n }\n }\n\n // The click handler\n const handleFocus = (_: MouseEvent) => {\n if (disabled) return\n if (preload) {\n doPreload()\n }\n }\n\n const handleTouchStart = handleFocus\n\n const handleEnter = (e: MouseEvent) => {\n if (disabled) return\n const eventTarget = (e.target || {}) as LinkCurrentTargetElement\n\n if (preload) {\n if (eventTarget.preloadTimeout) {\n return\n }\n\n eventTarget.preloadTimeout = setTimeout(() => {\n eventTarget.preloadTimeout = null\n doPreload()\n }, preloadDelay)\n }\n }\n\n const handleLeave = (e: MouseEvent) => {\n if (disabled) return\n const eventTarget = (e.target || {}) as LinkCurrentTargetElement\n\n if (eventTarget.preloadTimeout) {\n clearTimeout(eventTarget.preloadTimeout)\n eventTarget.preloadTimeout = null\n }\n }\n\n const composeHandlers =\n (handlers: Array<undefined | ((e: any) => void)>) =>\n (e: { persist?: () => void; defaultPrevented: boolean }) => {\n e.persist?.()\n handlers.filter(Boolean).forEach((handler) => {\n if (e.defaultPrevented) return\n handler!(e)\n })\n }\n\n // Get the active props\n const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> = isActive\n ? (functionalUpdate(activeProps as any, {}) ?? {})\n : {}\n\n // Get the inactive props\n const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =\n isActive ? {} : functionalUpdate(inactiveProps, {})\n\n const resolvedClassName = [\n className,\n resolvedActiveProps.className,\n resolvedInactiveProps.className,\n ]\n .filter(Boolean)\n .join(' ')\n\n const resolvedStyle = {\n ...style,\n ...resolvedActiveProps.style,\n ...resolvedInactiveProps.style,\n }\n\n return {\n ...resolvedActiveProps,\n ...resolvedInactiveProps,\n ...rest,\n href: disabled\n ? undefined\n : next.maskedLocation\n ? router.history.createHref(next.maskedLocation.href)\n : router.history.createHref(next.href),\n ref: innerRef as React.ComponentPropsWithRef<'a'>['ref'],\n onClick: composeHandlers([onClick, handleClick]),\n onFocus: composeHandlers([onFocus, handleFocus]),\n onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),\n onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),\n onTouchStart: composeHandlers([onTouchStart, handleTouchStart]),\n disabled: !!disabled,\n target,\n ...(Object.keys(resolvedStyle).length && { style: resolvedStyle }),\n ...(resolvedClassName && { className: resolvedClassName }),\n ...(disabled && {\n role: 'link',\n 'aria-disabled': true,\n }),\n ...(isActive && { 'data-status': 'active', 'aria-current': 'page' }),\n ...(isTransitioning && { 'data-transitioning': 'transitioning' }),\n }\n}\n\nexport type UseLinkPropsOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n TTo extends string = '',\n TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,\n TMaskTo extends string = '',\n> = ActiveLinkOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &\n React.AnchorHTMLAttributes<HTMLAnchorElement>\n\nexport type ActiveLinkOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string = '',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '',\n> = LinkOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & ActiveLinkOptionProps\n\nexport interface ActiveLinkOptionProps {\n /**\n * A function that returns additional props for the `active` state of this link.\n * These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)\n */\n activeProps?:\n | React.AnchorHTMLAttributes<HTMLAnchorElement>\n | (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)\n /**\n * A function that returns additional props for the `inactive` state of this link.\n * These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)\n */\n inactiveProps?:\n | React.AnchorHTMLAttributes<HTMLAnchorElement>\n | (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)\n}\n\nexport type LinkProps<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string = '',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '',\n> = ActiveLinkOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &\n LinkPropsChildren\n\nexport interface LinkPropsChildren {\n // 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\n children?:\n | React.ReactNode\n | ((state: {\n isActive: boolean\n isTransitioning: boolean\n }) => React.ReactNode)\n}\n\ntype LinkComponentReactProps<TComp> = React.PropsWithoutRef<\n TComp extends React.FC<infer TProps> | React.Component<infer TProps>\n ? TProps\n : TComp extends keyof React.JSX.IntrinsicElements\n ? Omit<React.HTMLProps<TComp>, 'children' | 'preload'>\n : never\n> &\n React.RefAttributes<\n TComp extends\n | React.FC<{ ref: infer TRef }>\n | React.Component<{ ref: infer TRef }>\n ? TRef\n : TComp extends keyof React.JSX.IntrinsicElements\n ? React.ComponentRef<TComp>\n : never\n >\n\nexport type LinkComponentProps<\n TComp,\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string = '',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '',\n> = LinkComponentReactProps<TComp> &\n LinkProps<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>\n\nexport type LinkComponent<TComp> = <\n TRouter extends RegisteredRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string = '',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '',\n>(\n props: LinkComponentProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,\n) => React.ReactElement\n\nexport function createLink<const TComp>(Comp: TComp): LinkComponent<TComp> {\n return React.forwardRef(function CreatedLink(props, ref) {\n return <Link {...(props as any)} _asChild={Comp} ref={ref} />\n }) as any\n}\n\nexport const Link: LinkComponent<'a'> = React.forwardRef<Element, any>(\n (props, ref) => {\n const { _asChild, ...rest } = props\n const { type, ref: innerRef, ...linkProps } = useLinkProps(rest, ref)\n\n const children =\n typeof rest.children === 'function'\n ? rest.children({\n isActive: (linkProps as any)['data-status'] === 'active',\n })\n : rest.children\n\n if (typeof _asChild === 'undefined') {\n // the ReturnType of useLinkProps returns the correct type for a <a> element, not a general component that has a delete prop\n // @ts-expect-error\n delete linkProps.disabled\n }\n\n return React.createElement(\n _asChild ? _asChild : 'a',\n {\n ...linkProps,\n ref: innerRef,\n },\n children,\n )\n },\n) as any\n\nfunction isCtrlEvent(e: MouseEvent) {\n return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)\n}\n"],"names":[],"mappings":";;;;;;;;AA4iBA;AAEgB;AAUd;AACA;AACM;AAEA;AAAA;AAAA;;AAGmB;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACS;AACK;AACd;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AASC;AACA;AACE;AACG;AAAA;AACD;AACD;AAAA;AAGT;AAAmB;AACwB;AACzB;AAElB;AAAsB;AACgB;AACO;AAE7C;AAGA;AAAgC;AAG5B;AAAyB;AACZ;AACJ;AAET;AAAsB;AACf;AACE;AAET;AAAuC;AACH;AAG9B;AAGN;AAGA;AAMA;AAA+B;AACjC;AAGI;AACJ;AACE;AACA;AAA2B;AAC5B;AAGH;AAA2C;AAEvC;AACY;;AACZ;AACF;AACU;AAGZ;AAAA;AACE;AACA;AACc;AACd;AACA;AACsC;AACtC;AACA;AAGF;AACS;AAAA;AACF;AACE;AACL;AACM;AACqB;AACJ;AACI;AACN;AACQ;AACJ;AACA;AACU;AACA;AACA;AAAA;AAKjC;AACJ;AAOE;AAEA;AACE;AAAuB;AAGzB;AACQ;AACN;AAAwB;AAI1B;AAAsB;AACjB;AACH;AACA;AACA;AACA;AACA;AACD;AACH;AAII;AACJ;AACA;AACY;;AACZ;AAGF;AAEM;AACJ;AACM;AAEN;AACE;AACE;AAAA;AAGU;AACV;AACU;;AACG;AACjB;AAGI;AACJ;AACM;AAEN;AACE;AACA;AAA6B;AAC/B;AAGF;;AAGI;AACA;AACE;AACA;AAAU;AACX;AAIC;AAKN;AAGA;AAA0B;AACxB;AACoB;AACE;AAKxB;AAAsB;AACjB;AACoB;AACE;AAGpB;AAAA;AACF;AACA;AACA;AAKsC;AACpC;AAC0C;AACA;AACU;AACA;AACK;AAClD;AACZ;AACgE;AACR;AACxC;AACR;AACW;AACnB;AACkE;AACH;AAEnE;AA4FO;AACL;AACE;AAA2D;AAE/D;AAEO;AAAuC;AAE1C;AACM;AAEN;AAEoB;AACoC;AAIpD;AAGF;AAAiB;AAGnB;AAAa;AACW;AACtB;AACK;AACE;AACP;AACA;AAAA;AAGN;AAEA;AACS;AACT;;;;;;"}
|
package/dist/esm/router.d.ts
CHANGED
|
@@ -48,173 +48,214 @@ export type TrailingSlashOption = 'always' | 'never' | 'preserve';
|
|
|
48
48
|
export interface RouterOptions<TRouteTree extends AnyRoute, TTrailingSlashOption extends TrailingSlashOption, TDehydrated extends Record<string, any> = Record<string, any>, TSerializedError extends Record<string, any> = Record<string, any>> {
|
|
49
49
|
/**
|
|
50
50
|
* The history object that will be used to manage the browser history.
|
|
51
|
+
*
|
|
51
52
|
* If not provided, a new createBrowserHistory instance will be created and used.
|
|
53
|
+
*
|
|
52
54
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#history-property)
|
|
53
55
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/history-types)
|
|
54
56
|
*/
|
|
55
57
|
history?: RouterHistory;
|
|
56
58
|
/**
|
|
57
59
|
* A function that will be used to stringify search params when generating links.
|
|
58
|
-
*
|
|
60
|
+
*
|
|
61
|
+
* @default defaultStringifySearch
|
|
59
62
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#stringifysearch-method)
|
|
60
63
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/custom-search-param-serialization)
|
|
61
64
|
*/
|
|
62
65
|
stringifySearch?: SearchSerializer;
|
|
63
66
|
/**
|
|
64
67
|
* A function that will be used to parse search params when parsing the current location.
|
|
65
|
-
*
|
|
68
|
+
*
|
|
69
|
+
* @default defaultParseSearch
|
|
66
70
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#parsesearch-method)
|
|
67
71
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/custom-search-param-serialization)
|
|
68
72
|
*/
|
|
69
73
|
parseSearch?: SearchParser;
|
|
70
74
|
/**
|
|
71
|
-
* Defaults to `false`
|
|
72
75
|
* If `false`, routes will not be preloaded by default in any way.
|
|
76
|
+
*
|
|
73
77
|
* If `'intent'`, routes will be preloaded by default when the user hovers over a link or a `touchstart` event is detected on a `<Link>`.
|
|
78
|
+
*
|
|
79
|
+
* If `'viewport'`, routes will be preloaded by default when they are within the viewport.
|
|
80
|
+
*
|
|
81
|
+
* @default false
|
|
74
82
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultpreload-property)
|
|
75
83
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/preloading)
|
|
76
84
|
*/
|
|
77
|
-
defaultPreload?: false | 'intent';
|
|
85
|
+
defaultPreload?: false | 'intent' | 'viewport';
|
|
78
86
|
/**
|
|
79
|
-
* Defaults to 50
|
|
80
87
|
* The delay in milliseconds that a route must be hovered over or touched before it is preloaded.
|
|
88
|
+
*
|
|
89
|
+
* @default 50
|
|
81
90
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultpreloaddelay-property)
|
|
82
91
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/preloading#preload-delay)
|
|
83
92
|
*/
|
|
84
93
|
defaultPreloadDelay?: number;
|
|
85
94
|
/**
|
|
86
|
-
* Defaults to `Outlet`
|
|
87
95
|
* The default `component` a route should use if no component is provided.
|
|
96
|
+
*
|
|
97
|
+
* @default Outlet
|
|
88
98
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultcomponent-property)
|
|
89
99
|
*/
|
|
90
100
|
defaultComponent?: RouteComponent;
|
|
91
101
|
/**
|
|
92
|
-
* Defaults to `ErrorComponent`
|
|
93
102
|
* The default `errorComponent` a route should use if no error component is provided.
|
|
103
|
+
*
|
|
104
|
+
* @default ErrorComponent
|
|
94
105
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaulterrorcomponent-property)
|
|
95
106
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#handling-errors-with-routeoptionserrorcomponent)
|
|
96
107
|
*/
|
|
97
108
|
defaultErrorComponent?: ErrorRouteComponent;
|
|
98
109
|
/**
|
|
99
110
|
* The default `pendingComponent` a route should use if no pending component is provided.
|
|
111
|
+
*
|
|
100
112
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultpendingcomponent-property)
|
|
101
113
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#showing-a-pending-component)
|
|
102
114
|
*/
|
|
103
115
|
defaultPendingComponent?: RouteComponent;
|
|
104
116
|
/**
|
|
105
|
-
* Defaults to `1000`
|
|
106
117
|
* The default `pendingMs` a route should use if no pendingMs is provided.
|
|
118
|
+
*
|
|
119
|
+
* @default 1000
|
|
107
120
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultpendingms-property)
|
|
108
121
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#avoiding-pending-component-flash)
|
|
109
122
|
*/
|
|
110
123
|
defaultPendingMs?: number;
|
|
111
124
|
/**
|
|
112
|
-
* Defaults to `500`
|
|
113
125
|
* The default `pendingMinMs` a route should use if no pendingMinMs is provided.
|
|
126
|
+
*
|
|
127
|
+
* @default 500
|
|
114
128
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultpendingminms-property)
|
|
115
129
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#avoiding-pending-component-flash)
|
|
116
130
|
*/
|
|
117
131
|
defaultPendingMinMs?: number;
|
|
118
132
|
/**
|
|
119
|
-
*
|
|
120
|
-
*
|
|
133
|
+
* The default `staleTime` a route should use if no staleTime is provided. This is the time in milliseconds that a route will be considered fresh.
|
|
134
|
+
*
|
|
135
|
+
* @default 0
|
|
121
136
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultstaletime-property)
|
|
122
137
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#key-options)
|
|
123
138
|
*/
|
|
124
139
|
defaultStaleTime?: number;
|
|
125
140
|
/**
|
|
126
|
-
* Defaults to `30_000` ms (30 seconds)
|
|
127
141
|
* The default `preloadStaleTime` a route should use if no preloadStaleTime is provided.
|
|
142
|
+
*
|
|
143
|
+
* @default 30_000 `(30 seconds)`
|
|
128
144
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultpreloadstaletime-property)
|
|
129
145
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/preloading)
|
|
130
146
|
*/
|
|
131
147
|
defaultPreloadStaleTime?: number;
|
|
132
148
|
/**
|
|
133
149
|
* Defaults to `routerOptions.defaultGcTime`, which defaults to 30 minutes.
|
|
150
|
+
*
|
|
134
151
|
* The default `defaultPreloadGcTime` a route should use if no preloadGcTime is provided.
|
|
152
|
+
*
|
|
135
153
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultpreloadgctime-property)
|
|
136
154
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/preloading)
|
|
137
155
|
*/
|
|
138
156
|
defaultPreloadGcTime?: number;
|
|
139
157
|
/**
|
|
140
158
|
* The default `onCatch` handler for errors caught by the Router ErrorBoundary
|
|
159
|
+
*
|
|
141
160
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultoncatch-property)
|
|
142
161
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#handling-errors-with-routeoptionsoncatch)
|
|
143
162
|
*/
|
|
144
163
|
defaultOnCatch?: (error: Error, errorInfo: React.ErrorInfo) => void;
|
|
164
|
+
/**
|
|
165
|
+
* If set to `true`, all navigation actions will wrapped in `document.startViewTransition()`.
|
|
166
|
+
*/
|
|
145
167
|
defaultViewTransition?: boolean;
|
|
146
168
|
/**
|
|
147
169
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/not-found-errors#the-notfoundmode-option)
|
|
148
170
|
*/
|
|
149
171
|
notFoundMode?: 'root' | 'fuzzy';
|
|
150
172
|
/**
|
|
151
|
-
* Defaults to 30 minutes.
|
|
152
173
|
* The default `gcTime` a route should use if no
|
|
174
|
+
*
|
|
175
|
+
* @default 1_800_000 `(30 minutes)`
|
|
153
176
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultgctime-property)
|
|
154
177
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#key-options)
|
|
155
178
|
*/
|
|
156
179
|
defaultGcTime?: number;
|
|
157
180
|
/**
|
|
158
|
-
* Defaults to `false`
|
|
159
181
|
* If `true`, all routes will be matched as case-sensitive.
|
|
182
|
+
*
|
|
183
|
+
* @default false
|
|
160
184
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#casesensitive-property)
|
|
161
185
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/route-trees#case-sensitivity)
|
|
162
186
|
*/
|
|
163
187
|
caseSensitive?: boolean;
|
|
164
188
|
/**
|
|
165
|
-
*
|
|
189
|
+
* __Required*__
|
|
190
|
+
*
|
|
166
191
|
* The route tree that will be used to configure the router instance.
|
|
192
|
+
*
|
|
167
193
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#routetree-property)
|
|
168
194
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/route-trees)
|
|
169
195
|
*/
|
|
170
196
|
routeTree?: TRouteTree;
|
|
171
197
|
/**
|
|
172
|
-
* Defaults to `/`
|
|
173
198
|
* The basepath for then entire router. This is useful for mounting a router instance at a subpath.
|
|
199
|
+
*
|
|
200
|
+
* @default '/'
|
|
174
201
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#basepath-property)
|
|
175
202
|
*/
|
|
176
203
|
basepath?: string;
|
|
177
204
|
/**
|
|
178
|
-
* Optional or required if the root route was created with [`createRootRouteWithContext()`](https://tanstack.com/router/latest/docs/framework/react/api/router/createRootRouteWithContextFunction).
|
|
179
205
|
* The root context that will be provided to all routes in the route tree.
|
|
206
|
+
*
|
|
180
207
|
* This can be used to provide a context to all routes in the tree without having to provide it to each route individually.
|
|
208
|
+
*
|
|
209
|
+
* Optional or required if the root route was created with [`createRootRouteWithContext()`](https://tanstack.com/router/latest/docs/framework/react/api/router/createRootRouteWithContextFunction).
|
|
210
|
+
*
|
|
181
211
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#context-property)
|
|
182
212
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/router-context)
|
|
183
213
|
*/
|
|
184
214
|
context?: InferRouterContext<TRouteTree>;
|
|
185
215
|
/**
|
|
186
216
|
* A function that will be called when the router is dehydrated.
|
|
217
|
+
*
|
|
187
218
|
* The return value of this function will be serialized and stored in the router's dehydrated state.
|
|
219
|
+
*
|
|
188
220
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#dehydrate-method)
|
|
189
221
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/external-data-loading#critical-dehydrationhydration)
|
|
190
222
|
*/
|
|
191
223
|
dehydrate?: () => TDehydrated;
|
|
192
224
|
/**
|
|
193
225
|
* A function that will be called when the router is hydrated.
|
|
226
|
+
*
|
|
194
227
|
* The return value of this function will be serialized and stored in the router's dehydrated state.
|
|
228
|
+
*
|
|
195
229
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#hydrate-method)
|
|
196
230
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/external-data-loading#critical-dehydrationhydration)
|
|
197
231
|
*/
|
|
198
232
|
hydrate?: (dehydrated: TDehydrated) => void;
|
|
199
233
|
/**
|
|
200
234
|
* An array of route masks that will be used to mask routes in the route tree.
|
|
235
|
+
*
|
|
201
236
|
* Route masking is when you display a route at a different path than the one it is configured to match, like a modal popup that when shared will unmask to the modal's content instead of the modal's context.
|
|
237
|
+
*
|
|
202
238
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#routemasks-property)
|
|
203
239
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/route-masking)
|
|
204
240
|
*/
|
|
205
241
|
routeMasks?: Array<RouteMask<TRouteTree>>;
|
|
206
242
|
/**
|
|
207
|
-
* Defaults to `false`
|
|
208
243
|
* If `true`, route masks will, by default, be removed when the page is reloaded.
|
|
244
|
+
*
|
|
209
245
|
* This can be overridden on a per-mask basis by setting the `unmaskOnReload` option on the mask, or on a per-navigation basis by setting the `unmaskOnReload` option in the `Navigate` options.
|
|
246
|
+
*
|
|
247
|
+
* @default false
|
|
210
248
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#unmaskonreload-property)
|
|
211
249
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/route-masking#unmasking-on-page-reload)
|
|
212
250
|
*/
|
|
213
251
|
unmaskOnReload?: boolean;
|
|
214
252
|
/**
|
|
215
253
|
* A component that will be used to wrap the entire router.
|
|
254
|
+
*
|
|
216
255
|
* This is useful for providing a context to the entire router.
|
|
256
|
+
*
|
|
217
257
|
* Only non-DOM-rendering components like providers should be used, anything else will cause a hydration error.
|
|
258
|
+
*
|
|
218
259
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#wrap-property)
|
|
219
260
|
*/
|
|
220
261
|
Wrap?: (props: {
|
|
@@ -222,48 +263,60 @@ export interface RouterOptions<TRouteTree extends AnyRoute, TTrailingSlashOption
|
|
|
222
263
|
}) => React.JSX.Element;
|
|
223
264
|
/**
|
|
224
265
|
* A component that will be used to wrap the inner contents of the router.
|
|
266
|
+
*
|
|
225
267
|
* This is useful for providing a context to the inner contents of the router where you also need access to the router context and hooks.
|
|
268
|
+
*
|
|
226
269
|
* Only non-DOM-rendering components like providers should be used, anything else will cause a hydration error.
|
|
270
|
+
*
|
|
227
271
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#innerwrap-property)
|
|
228
272
|
*/
|
|
229
273
|
InnerWrap?: (props: {
|
|
230
274
|
children: any;
|
|
231
275
|
}) => React.JSX.Element;
|
|
232
276
|
/**
|
|
233
|
-
* @deprecated
|
|
234
277
|
* Use `notFoundComponent` instead.
|
|
278
|
+
*
|
|
279
|
+
* @deprecated
|
|
235
280
|
* See https://tanstack.com/router/v1/docs/guide/not-found-errors#migrating-from-notfoundroute for more info.
|
|
236
281
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#notfoundroute-property)
|
|
237
282
|
*/
|
|
238
283
|
notFoundRoute?: AnyRoute;
|
|
239
284
|
/**
|
|
240
|
-
* Defaults to `NotFound`
|
|
241
285
|
* The default `notFoundComponent` a route should use if no notFound component is provided.
|
|
286
|
+
*
|
|
287
|
+
* @default NotFound
|
|
242
288
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultnotfoundcomponent-property)
|
|
243
289
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/not-found-errors#default-router-wide-not-found-handling)
|
|
244
290
|
*/
|
|
245
291
|
defaultNotFoundComponent?: NotFoundRouteComponent;
|
|
246
292
|
/**
|
|
247
293
|
* The transformer that will be used when sending data between the server and the client during SSR.
|
|
294
|
+
*
|
|
248
295
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#transformer-property)
|
|
249
296
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/ssr#data-transformers)
|
|
250
297
|
*/
|
|
251
298
|
transformer?: RouterTransformer;
|
|
252
299
|
/**
|
|
253
300
|
* The serializer object that will be used to determine how errors are serialized and deserialized between the server and the client.
|
|
301
|
+
*
|
|
254
302
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#errorserializer-property)
|
|
255
303
|
*/
|
|
256
304
|
errorSerializer?: RouterErrorSerializer<TSerializedError>;
|
|
257
305
|
/**
|
|
258
|
-
* Defaults to `never`
|
|
259
306
|
* Configures how trailing slashes are treated.
|
|
260
|
-
*
|
|
307
|
+
*
|
|
308
|
+
* - `'always'` will add a trailing slash if not present
|
|
309
|
+
* - `'never'` will remove the trailing slash if present
|
|
310
|
+
* - `'preserve'` will not modify the trailing slash.
|
|
311
|
+
*
|
|
312
|
+
* @default 'never'
|
|
261
313
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#trailingslash-property)
|
|
262
314
|
*/
|
|
263
315
|
trailingSlash?: TTrailingSlashOption;
|
|
264
316
|
/**
|
|
265
|
-
* Defaults to `typeof document !== 'undefined'`
|
|
266
317
|
* While usually automatic, sometimes it can be useful to force the router into a server-side state, e.g. when using the router in a non-browser environment that has access to a global.document object.
|
|
318
|
+
*
|
|
319
|
+
* @default typeof document !== 'undefined'
|
|
267
320
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#isserver property)
|
|
268
321
|
*/
|
|
269
322
|
isServer?: boolean;
|