@tanstack/react-router 1.120.4-alpha.15 → 1.120.4-alpha.19

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 CHANGED
@@ -7,6 +7,7 @@ const routerCore = require("@tanstack/router-core");
7
7
  const useRouterState = require("./useRouterState.cjs");
8
8
  const useRouter = require("./useRouter.cjs");
9
9
  const utils = require("./utils.cjs");
10
+ const useMatch = require("./useMatch.cjs");
10
11
  const Matches = require("./Matches.cjs");
11
12
  function _interopNamespaceDefault(e) {
12
13
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
@@ -79,12 +80,15 @@ function useLinkProps(options, forwardedRef) {
79
80
  select: (s) => s.location.search,
80
81
  structuralSharing: true
81
82
  });
82
- const from = Matches.useMatches({
83
- select: (matches) => {
84
- var _a;
85
- return options.from ?? ((_a = matches[matches.length - 1]) == null ? void 0 : _a.fullPath);
86
- }
83
+ const isRelativeFromPath = options.relative === "path";
84
+ const nearestFrom = useMatch.useMatch({
85
+ strict: false,
86
+ select: (match) => isRelativeFromPath ? void 0 : match.fullPath
87
+ });
88
+ const leafFrom = Matches.useMatches({
89
+ select: (matches) => isRelativeFromPath ? matches[matches.length - 1].fullPath : void 0
87
90
  });
91
+ const from = options.from ?? (isRelativeFromPath ? leafFrom : nearestFrom);
88
92
  const _options = React__namespace.useMemo(() => ({ ...options, from }), [options, from]);
89
93
  const next = React__namespace.useMemo(
90
94
  () => router.buildLocation(_options),
@@ -1 +1 @@
1
- {"version":3,"file":"link.cjs","sources":["../../src/link.tsx"],"sourcesContent":["import * as React from 'react'\nimport { flushSync } from 'react-dom'\nimport {\n deepEqual,\n exactPathTest,\n functionalUpdate,\n preloadWarning,\n removeTrailingSlash,\n} from '@tanstack/router-core'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\n\nimport {\n useForwardedRef,\n useIntersectionObserver,\n useLayoutEffect,\n} from './utils'\n\nimport { useMatches } from './Matches'\nimport type {\n AnyRouter,\n Constrain,\n LinkCurrentTargetElement,\n LinkOptions,\n RegisteredRouter,\n RoutePaths,\n} from '@tanstack/router-core'\nimport type { ReactNode } from 'react'\nimport type {\n ValidateLinkOptions,\n ValidateLinkOptionsArray,\n} from './typePrimitives'\n\nexport function useLinkProps<\n TRouter extends AnyRouter = RegisteredRouter,\n const TFrom extends string = string,\n const TTo extends string | undefined = undefined,\n const TMaskFrom extends string = TFrom,\n const 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 hasRenderFetched = React.useRef(false)\n const innerRef = useForwardedRef(forwardedRef)\n\n const {\n // custom props\n activeProps = () => ({ className: 'active' }),\n inactiveProps = () => ({}),\n activeOptions,\n to,\n preload: userPreload,\n preloadDelay: userPreloadDelay,\n hashScrollIntoView,\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 const {\n // prevent these from being returned\n params: _params,\n search: _search,\n hash: _hash,\n state: _state,\n mask: _mask,\n reloadDocument: _reloadDocument,\n ...propsSafeToSpread\n } = rest\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 // subscribe to search params to re-build location if it changes\n const currentSearch = useRouterState({\n select: (s) => s.location.search,\n structuralSharing: true as any,\n })\n\n // when `from` is not supplied, use the leaf route of the current matches as the `from` location\n // so relative routing works as expected\n const from = useMatches({\n select: (matches) => options.from ?? matches[matches.length - 1]?.fullPath,\n })\n // Use it as the default `from` location\n const _options = React.useMemo(() => ({ ...options, from }), [options, from])\n\n const next = React.useMemo(\n () => router.buildLocation(_options as any),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [router, _options, currentSearch],\n )\n\n const preload = React.useMemo(() => {\n if (_options.reloadDocument) {\n return false\n }\n return userPreload ?? router.options.defaultPreload\n }, [router.options.defaultPreload, userPreload, _options.reloadDocument])\n const preloadDelay =\n userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0\n\n const isActive = useRouterState({\n select: (s) => {\n if (activeOptions?.exact) {\n const testExact = exactPathTest(\n s.location.pathname,\n next.pathname,\n router.basepath,\n )\n if (!testExact) {\n return false\n }\n } else {\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\n const pathIsFuzzyEqual = nextPathSplit.every(\n (d, i) => d === currentPathSplit[i],\n )\n if (!pathIsFuzzyEqual) {\n return false\n }\n }\n\n if (activeOptions?.includeSearch ?? true) {\n const searchTest = deepEqual(s.location.search, next.search, {\n partial: !activeOptions?.exact,\n ignoreUndefined: !activeOptions?.explicitUndefined,\n })\n if (!searchTest) {\n return false\n }\n }\n\n if (activeOptions?.includeHash) {\n return s.location.hash === next.hash\n }\n return true\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 preloadViewportIoCallback = React.useCallback(\n (entry: IntersectionObserverEntry | undefined) => {\n if (entry?.isIntersecting) {\n doPreload()\n }\n },\n [doPreload],\n )\n\n useIntersectionObserver(\n innerRef,\n preloadViewportIoCallback,\n { rootMargin: '100px' },\n { disabled: !!disabled || !(preload === 'viewport') },\n )\n\n useLayoutEffect(() => {\n if (hasRenderFetched.current) {\n return\n }\n if (!disabled && preload === 'render') {\n doPreload()\n hasRenderFetched.current = true\n }\n }, [disabled, doPreload, preload])\n\n if (type === 'external') {\n return {\n ...propsSafeToSpread,\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 // N.B. we don't call `router.commitLocation(next) here because we want to run `validateSearch` before committing\n return router.navigate({\n ..._options,\n replace,\n resetScroll,\n hashScrollIntoView,\n startTransition,\n viewTransition,\n ignoreBlocker,\n } as any)\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 ...propsSafeToSpread,\n ...resolvedActiveProps,\n ...resolvedInactiveProps,\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\ntype UseLinkReactProps<TComp> = TComp extends keyof React.JSX.IntrinsicElements\n ? React.JSX.IntrinsicElements[TComp]\n : React.PropsWithoutRef<\n TComp extends React.ComponentType<infer TProps> ? TProps : never\n > &\n React.RefAttributes<\n TComp extends\n | React.FC<{ ref: infer TRef }>\n | React.Component<{ ref: infer TRef }>\n ? TRef\n : never\n >\n\nexport type UseLinkPropsOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,\n TMaskTo extends string = '.',\n> = ActiveLinkOptions<'a', TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &\n UseLinkReactProps<'a'>\n\nexport type ActiveLinkOptions<\n TComp = 'a',\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = LinkOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &\n ActiveLinkOptionProps<TComp>\n\ntype ActiveLinkProps<TComp> = Partial<\n LinkComponentReactProps<TComp> & {\n [key: `data-${string}`]: unknown\n }\n>\n\nexport interface ActiveLinkOptionProps<TComp = 'a'> {\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?: ActiveLinkProps<TComp> | (() => ActiveLinkProps<TComp>)\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?: ActiveLinkProps<TComp> | (() => ActiveLinkProps<TComp>)\n}\n\nexport type LinkProps<\n TComp = 'a',\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = ActiveLinkOptions<TComp, 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> = Omit<\n UseLinkReactProps<TComp>,\n keyof CreateLinkProps\n>\n\nexport type LinkComponentProps<\n TComp = 'a',\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = LinkComponentReactProps<TComp> &\n LinkProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>\n\nexport type CreateLinkProps = LinkProps<\n any,\n any,\n string,\n string,\n string,\n string\n>\n\nexport type LinkComponent<\n in out TComp,\n in out TDefaultFrom extends string = string,\n> = <\n TRouter extends AnyRouter = RegisteredRouter,\n const TFrom extends string = TDefaultFrom,\n const TTo extends string | undefined = undefined,\n const TMaskFrom extends string = TFrom,\n const TMaskTo extends string = '',\n>(\n props: LinkComponentProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,\n) => React.ReactElement\n\nexport interface LinkComponentRoute<\n in out TDefaultFrom extends string = string,\n> {\n defaultFrom: TDefaultFrom\n <\n TRouter extends AnyRouter = RegisteredRouter,\n const TTo extends string | undefined = undefined,\n const TMaskTo extends string = '',\n >(\n props: LinkComponentProps<\n 'a',\n TRouter,\n this['defaultFrom'],\n TTo,\n this['defaultFrom'],\n TMaskTo\n >,\n ): React.ReactElement\n}\n\nexport function createLink<const TComp>(\n Comp: Constrain<TComp, any, (props: CreateLinkProps) => ReactNode>,\n): 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 {\n type: _type,\n ref: innerRef,\n ...linkProps\n } = useLinkProps(rest as any, 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 disabled 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\nexport type LinkOptionsFnOptions<\n TOptions,\n TComp,\n TRouter extends AnyRouter = RegisteredRouter,\n> =\n TOptions extends ReadonlyArray<any>\n ? ValidateLinkOptionsArray<TRouter, TOptions, string, TComp>\n : ValidateLinkOptions<TRouter, TOptions, string, TComp>\n\nexport type LinkOptionsFn<TComp> = <\n const TOptions,\n TRouter extends AnyRouter = RegisteredRouter,\n>(\n options: LinkOptionsFnOptions<TOptions, TComp, TRouter>,\n) => TOptions\n\nexport const linkOptions: LinkOptionsFn<'a'> = (options) => {\n return options as any\n}\n"],"names":["useRouter","React","useForwardedRef","useRouterState","useMatches","exactPathTest","removeTrailingSlash","deepEqual","preloadWarning","useIntersectionObserver","useLayoutEffect","flushSync","functionalUpdate"],"mappings":"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"}
1
+ {"version":3,"file":"link.cjs","sources":["../../src/link.tsx"],"sourcesContent":["import * as React from 'react'\nimport { flushSync } from 'react-dom'\nimport {\n deepEqual,\n exactPathTest,\n functionalUpdate,\n preloadWarning,\n removeTrailingSlash,\n} from '@tanstack/router-core'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\n\nimport {\n useForwardedRef,\n useIntersectionObserver,\n useLayoutEffect,\n} from './utils'\n\nimport { useMatch } from './useMatch'\nimport { useMatches } from './Matches'\nimport type {\n AnyRouter,\n Constrain,\n LinkCurrentTargetElement,\n LinkOptions,\n RegisteredRouter,\n RoutePaths,\n} from '@tanstack/router-core'\nimport type { ReactNode } from 'react'\nimport type {\n ValidateLinkOptions,\n ValidateLinkOptionsArray,\n} from './typePrimitives'\n\nexport function useLinkProps<\n TRouter extends AnyRouter = RegisteredRouter,\n const TFrom extends string = string,\n const TTo extends string | undefined = undefined,\n const TMaskFrom extends string = TFrom,\n const 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 hasRenderFetched = React.useRef(false)\n const innerRef = useForwardedRef(forwardedRef)\n\n const {\n // custom props\n activeProps = () => ({ className: 'active' }),\n inactiveProps = () => ({}),\n activeOptions,\n to,\n preload: userPreload,\n preloadDelay: userPreloadDelay,\n hashScrollIntoView,\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 const {\n // prevent these from being returned\n params: _params,\n search: _search,\n hash: _hash,\n state: _state,\n mask: _mask,\n reloadDocument: _reloadDocument,\n ...propsSafeToSpread\n } = rest\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 // subscribe to search params to re-build location if it changes\n const currentSearch = useRouterState({\n select: (s) => s.location.search,\n structuralSharing: true as any,\n })\n\n const isRelativeFromPath = options.relative === 'path'\n\n // when `from` is not supplied, use the nearest parent match's full path as the `from` location\n // so relative routing works as expected. Try to stay out of rerenders as much as possible.\n const nearestFrom = useMatch({\n strict: false,\n select: (match) => (isRelativeFromPath ? undefined : match.fullPath),\n })\n\n // When no from and relative is path, use the leaf match as the from location\n // Avoid rerenders as much as possible.\n const leafFrom = useMatches({\n select: (matches) =>\n isRelativeFromPath ? matches[matches.length - 1]!.fullPath : undefined,\n })\n\n const from = options.from ?? (isRelativeFromPath ? leafFrom : nearestFrom)\n\n // Use it as the default `from` location\n const _options = React.useMemo(() => ({ ...options, from }), [options, from])\n\n const next = React.useMemo(\n () => router.buildLocation(_options as any),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [router, _options, currentSearch],\n )\n\n const preload = React.useMemo(() => {\n if (_options.reloadDocument) {\n return false\n }\n return userPreload ?? router.options.defaultPreload\n }, [router.options.defaultPreload, userPreload, _options.reloadDocument])\n const preloadDelay =\n userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0\n\n const isActive = useRouterState({\n select: (s) => {\n if (activeOptions?.exact) {\n const testExact = exactPathTest(\n s.location.pathname,\n next.pathname,\n router.basepath,\n )\n if (!testExact) {\n return false\n }\n } else {\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\n const pathIsFuzzyEqual = nextPathSplit.every(\n (d, i) => d === currentPathSplit[i],\n )\n if (!pathIsFuzzyEqual) {\n return false\n }\n }\n\n if (activeOptions?.includeSearch ?? true) {\n const searchTest = deepEqual(s.location.search, next.search, {\n partial: !activeOptions?.exact,\n ignoreUndefined: !activeOptions?.explicitUndefined,\n })\n if (!searchTest) {\n return false\n }\n }\n\n if (activeOptions?.includeHash) {\n return s.location.hash === next.hash\n }\n return true\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 preloadViewportIoCallback = React.useCallback(\n (entry: IntersectionObserverEntry | undefined) => {\n if (entry?.isIntersecting) {\n doPreload()\n }\n },\n [doPreload],\n )\n\n useIntersectionObserver(\n innerRef,\n preloadViewportIoCallback,\n { rootMargin: '100px' },\n { disabled: !!disabled || !(preload === 'viewport') },\n )\n\n useLayoutEffect(() => {\n if (hasRenderFetched.current) {\n return\n }\n if (!disabled && preload === 'render') {\n doPreload()\n hasRenderFetched.current = true\n }\n }, [disabled, doPreload, preload])\n\n if (type === 'external') {\n return {\n ...propsSafeToSpread,\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 // N.B. we don't call `router.commitLocation(next) here because we want to run `validateSearch` before committing\n return router.navigate({\n ..._options,\n replace,\n resetScroll,\n hashScrollIntoView,\n startTransition,\n viewTransition,\n ignoreBlocker,\n } as any)\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 ...propsSafeToSpread,\n ...resolvedActiveProps,\n ...resolvedInactiveProps,\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\ntype UseLinkReactProps<TComp> = TComp extends keyof React.JSX.IntrinsicElements\n ? React.JSX.IntrinsicElements[TComp]\n : React.PropsWithoutRef<\n TComp extends React.ComponentType<infer TProps> ? TProps : never\n > &\n React.RefAttributes<\n TComp extends\n | React.FC<{ ref: infer TRef }>\n | React.Component<{ ref: infer TRef }>\n ? TRef\n : never\n >\n\nexport type UseLinkPropsOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,\n TMaskTo extends string = '.',\n> = ActiveLinkOptions<'a', TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &\n UseLinkReactProps<'a'>\n\nexport type ActiveLinkOptions<\n TComp = 'a',\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = LinkOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &\n ActiveLinkOptionProps<TComp>\n\ntype ActiveLinkProps<TComp> = Partial<\n LinkComponentReactProps<TComp> & {\n [key: `data-${string}`]: unknown\n }\n>\n\nexport interface ActiveLinkOptionProps<TComp = 'a'> {\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?: ActiveLinkProps<TComp> | (() => ActiveLinkProps<TComp>)\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?: ActiveLinkProps<TComp> | (() => ActiveLinkProps<TComp>)\n}\n\nexport type LinkProps<\n TComp = 'a',\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = ActiveLinkOptions<TComp, 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> = Omit<\n UseLinkReactProps<TComp>,\n keyof CreateLinkProps\n>\n\nexport type LinkComponentProps<\n TComp = 'a',\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = LinkComponentReactProps<TComp> &\n LinkProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>\n\nexport type CreateLinkProps = LinkProps<\n any,\n any,\n string,\n string,\n string,\n string\n>\n\nexport type LinkComponent<\n in out TComp,\n in out TDefaultFrom extends string = string,\n> = <\n TRouter extends AnyRouter = RegisteredRouter,\n const TFrom extends string = TDefaultFrom,\n const TTo extends string | undefined = undefined,\n const TMaskFrom extends string = TFrom,\n const TMaskTo extends string = '',\n>(\n props: LinkComponentProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,\n) => React.ReactElement\n\nexport interface LinkComponentRoute<\n in out TDefaultFrom extends string = string,\n> {\n defaultFrom: TDefaultFrom\n <\n TRouter extends AnyRouter = RegisteredRouter,\n const TTo extends string | undefined = undefined,\n const TMaskTo extends string = '',\n >(\n props: LinkComponentProps<\n 'a',\n TRouter,\n this['defaultFrom'],\n TTo,\n this['defaultFrom'],\n TMaskTo\n >,\n ): React.ReactElement\n}\n\nexport function createLink<const TComp>(\n Comp: Constrain<TComp, any, (props: CreateLinkProps) => ReactNode>,\n): 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 {\n type: _type,\n ref: innerRef,\n ...linkProps\n } = useLinkProps(rest as any, 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 disabled 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\nexport type LinkOptionsFnOptions<\n TOptions,\n TComp,\n TRouter extends AnyRouter = RegisteredRouter,\n> =\n TOptions extends ReadonlyArray<any>\n ? ValidateLinkOptionsArray<TRouter, TOptions, string, TComp>\n : ValidateLinkOptions<TRouter, TOptions, string, TComp>\n\nexport type LinkOptionsFn<TComp> = <\n const TOptions,\n TRouter extends AnyRouter = RegisteredRouter,\n>(\n options: LinkOptionsFnOptions<TOptions, TComp, TRouter>,\n) => TOptions\n\nexport const linkOptions: LinkOptionsFn<'a'> = (options) => {\n return options as any\n}\n"],"names":["useRouter","React","useForwardedRef","useRouterState","useMatch","useMatches","exactPathTest","removeTrailingSlash","deepEqual","preloadWarning","useIntersectionObserver","useLayoutEffect","flushSync","functionalUpdate"],"mappings":"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"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const React = require("react");
4
4
  const useRouter = require("./useRouter.cjs");
5
+ const useMatch = require("./useMatch.cjs");
5
6
  function _interopNamespaceDefault(e) {
6
7
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
7
8
  if (e) {
@@ -20,28 +21,34 @@ function _interopNamespaceDefault(e) {
20
21
  }
21
22
  const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
22
23
  function useNavigate(_defaultOpts) {
23
- const { navigate } = useRouter.useRouter();
24
+ const { navigate, state } = useRouter.useRouter();
25
+ const matchIndex = useMatch.useMatch({
26
+ strict: false,
27
+ select: (match) => match.index
28
+ });
24
29
  return React__namespace.useCallback(
25
30
  (options) => {
31
+ const isRelativeFromPath = options.relative === "path";
32
+ const from = options.from ?? (_defaultOpts == null ? void 0 : _defaultOpts.from) ?? (isRelativeFromPath ? state.matches[state.matches.length - 1].fullPath : state.matches[matchIndex].fullPath);
26
33
  return navigate({
27
- from: _defaultOpts == null ? void 0 : _defaultOpts.from,
28
- ...options
34
+ ...options,
35
+ from
29
36
  });
30
37
  },
38
+ // eslint-disable-next-line react-hooks/exhaustive-deps
31
39
  [_defaultOpts == null ? void 0 : _defaultOpts.from, navigate]
32
40
  );
33
41
  }
34
42
  function Navigate(props) {
35
43
  const router = useRouter.useRouter();
44
+ const navigate = useNavigate();
36
45
  const previousPropsRef = React__namespace.useRef(null);
37
46
  React__namespace.useEffect(() => {
38
47
  if (previousPropsRef.current !== props) {
39
- router.navigate({
40
- ...props
41
- });
48
+ navigate(props);
42
49
  previousPropsRef.current = props;
43
50
  }
44
- }, [router, props]);
51
+ }, [router, props, navigate]);
45
52
  return null;
46
53
  }
47
54
  exports.Navigate = Navigate;
@@ -1 +1 @@
1
- {"version":3,"file":"useNavigate.cjs","sources":["../../src/useNavigate.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useRouter } from './useRouter'\nimport type {\n AnyRouter,\n FromPathOption,\n NavigateOptions,\n RegisteredRouter,\n UseNavigateResult,\n} from '@tanstack/router-core'\n\nexport function useNavigate<\n TRouter extends AnyRouter = RegisteredRouter,\n TDefaultFrom extends string = string,\n>(_defaultOpts?: {\n from?: FromPathOption<TRouter, TDefaultFrom>\n}): UseNavigateResult<TDefaultFrom> {\n const { navigate } = useRouter()\n\n return React.useCallback(\n (options: NavigateOptions) => {\n return navigate({\n from: _defaultOpts?.from,\n ...options,\n })\n },\n [_defaultOpts?.from, navigate],\n ) as UseNavigateResult<TDefaultFrom>\n}\n\nexport function Navigate<\n TRouter extends AnyRouter = RegisteredRouter,\n const TFrom extends string = string,\n const TTo extends string | undefined = undefined,\n const TMaskFrom extends string = TFrom,\n const TMaskTo extends string = '',\n>(props: NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>): null {\n const router = useRouter()\n\n const previousPropsRef = React.useRef<NavigateOptions<\n TRouter,\n TFrom,\n TTo,\n TMaskFrom,\n TMaskTo\n > | null>(null)\n React.useEffect(() => {\n if (previousPropsRef.current !== props) {\n router.navigate({\n ...props,\n })\n previousPropsRef.current = props\n }\n }, [router, props])\n return null\n}\n"],"names":["useRouter","React"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAUO,SAAS,YAGd,cAEkC;AAC5B,QAAA,EAAE,SAAS,IAAIA,oBAAU;AAE/B,SAAOC,iBAAM;AAAA,IACX,CAAC,YAA6B;AAC5B,aAAO,SAAS;AAAA,QACd,MAAM,6CAAc;AAAA,QACpB,GAAG;AAAA,MAAA,CACJ;AAAA,IACH;AAAA,IACA,CAAC,6CAAc,MAAM,QAAQ;AAAA,EAC/B;AACF;AAEO,SAAS,SAMd,OAAuE;AACvE,QAAM,SAASD,UAAAA,UAAU;AAEnB,QAAA,mBAAmBC,iBAAM,OAMrB,IAAI;AACdA,mBAAM,UAAU,MAAM;AAChB,QAAA,iBAAiB,YAAY,OAAO;AACtC,aAAO,SAAS;AAAA,QACd,GAAG;AAAA,MAAA,CACJ;AACD,uBAAiB,UAAU;AAAA,IAAA;AAAA,EAC7B,GACC,CAAC,QAAQ,KAAK,CAAC;AACX,SAAA;AACT;;;"}
1
+ {"version":3,"file":"useNavigate.cjs","sources":["../../src/useNavigate.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useRouter } from './useRouter'\nimport { useMatch } from './useMatch'\nimport type {\n AnyRouter,\n FromPathOption,\n NavigateOptions,\n RegisteredRouter,\n UseNavigateResult,\n} from '@tanstack/router-core'\n\nexport function useNavigate<\n TRouter extends AnyRouter = RegisteredRouter,\n TDefaultFrom extends string = string,\n>(_defaultOpts?: {\n from?: FromPathOption<TRouter, TDefaultFrom>\n}): UseNavigateResult<TDefaultFrom> {\n const { navigate, state } = useRouter()\n\n // Just get the index of the current match to avoid rerenders\n // as much as possible\n const matchIndex = useMatch({\n strict: false,\n select: (match) => match.index,\n })\n\n return React.useCallback(\n (options: NavigateOptions) => {\n const isRelativeFromPath = options.relative === 'path'\n\n const from =\n options.from ??\n _defaultOpts?.from ??\n (isRelativeFromPath\n ? state.matches[state.matches.length - 1]!.fullPath\n : state.matches[matchIndex]!.fullPath)\n\n return navigate({\n ...options,\n from: from,\n })\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [_defaultOpts?.from, navigate],\n ) as UseNavigateResult<TDefaultFrom>\n}\n\nexport function Navigate<\n TRouter extends AnyRouter = RegisteredRouter,\n const TFrom extends string = string,\n const TTo extends string | undefined = undefined,\n const TMaskFrom extends string = TFrom,\n const TMaskTo extends string = '',\n>(props: NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>): null {\n const router = useRouter()\n const navigate = useNavigate()\n\n const previousPropsRef = React.useRef<NavigateOptions<\n TRouter,\n TFrom,\n TTo,\n TMaskFrom,\n TMaskTo\n > | null>(null)\n React.useEffect(() => {\n if (previousPropsRef.current !== props) {\n navigate(props)\n previousPropsRef.current = props\n }\n }, [router, props, navigate])\n return null\n}\n"],"names":["useRouter","useMatch","React"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAWO,SAAS,YAGd,cAEkC;AAClC,QAAM,EAAE,UAAU,MAAM,IAAIA,oBAAU;AAItC,QAAM,aAAaC,SAAAA,SAAS;AAAA,IAC1B,QAAQ;AAAA,IACR,QAAQ,CAAC,UAAU,MAAM;AAAA,EAAA,CAC1B;AAED,SAAOC,iBAAM;AAAA,IACX,CAAC,YAA6B;AACtB,YAAA,qBAAqB,QAAQ,aAAa;AAEhD,YAAM,OACJ,QAAQ,SACR,6CAAc,UACb,qBACG,MAAM,QAAQ,MAAM,QAAQ,SAAS,CAAC,EAAG,WACzC,MAAM,QAAQ,UAAU,EAAG;AAEjC,aAAO,SAAS;AAAA,QACd,GAAG;AAAA,QACH;AAAA,MAAA,CACD;AAAA,IACH;AAAA;AAAA,IAEA,CAAC,6CAAc,MAAM,QAAQ;AAAA,EAC/B;AACF;AAEO,SAAS,SAMd,OAAuE;AACvE,QAAM,SAASF,UAAAA,UAAU;AACzB,QAAM,WAAW,YAAY;AAEvB,QAAA,mBAAmBE,iBAAM,OAMrB,IAAI;AACdA,mBAAM,UAAU,MAAM;AAChB,QAAA,iBAAiB,YAAY,OAAO;AACtC,eAAS,KAAK;AACd,uBAAiB,UAAU;AAAA,IAAA;AAAA,EAE5B,GAAA,CAAC,QAAQ,OAAO,QAAQ,CAAC;AACrB,SAAA;AACT;;;"}
package/dist/esm/link.js CHANGED
@@ -5,6 +5,7 @@ import { preloadWarning, functionalUpdate, exactPathTest, removeTrailingSlash, d
5
5
  import { useRouterState } from "./useRouterState.js";
6
6
  import { useRouter } from "./useRouter.js";
7
7
  import { useForwardedRef, useIntersectionObserver, useLayoutEffect } from "./utils.js";
8
+ import { useMatch } from "./useMatch.js";
8
9
  import { useMatches } from "./Matches.js";
9
10
  function useLinkProps(options, forwardedRef) {
10
11
  const router = useRouter();
@@ -60,12 +61,15 @@ function useLinkProps(options, forwardedRef) {
60
61
  select: (s) => s.location.search,
61
62
  structuralSharing: true
62
63
  });
63
- const from = useMatches({
64
- select: (matches) => {
65
- var _a;
66
- return options.from ?? ((_a = matches[matches.length - 1]) == null ? void 0 : _a.fullPath);
67
- }
64
+ const isRelativeFromPath = options.relative === "path";
65
+ const nearestFrom = useMatch({
66
+ strict: false,
67
+ select: (match) => isRelativeFromPath ? void 0 : match.fullPath
68
+ });
69
+ const leafFrom = useMatches({
70
+ select: (matches) => isRelativeFromPath ? matches[matches.length - 1].fullPath : void 0
68
71
  });
72
+ const from = options.from ?? (isRelativeFromPath ? leafFrom : nearestFrom);
69
73
  const _options = React.useMemo(() => ({ ...options, from }), [options, from]);
70
74
  const next = React.useMemo(
71
75
  () => router.buildLocation(_options),
@@ -1 +1 @@
1
- {"version":3,"file":"link.js","sources":["../../src/link.tsx"],"sourcesContent":["import * as React from 'react'\nimport { flushSync } from 'react-dom'\nimport {\n deepEqual,\n exactPathTest,\n functionalUpdate,\n preloadWarning,\n removeTrailingSlash,\n} from '@tanstack/router-core'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\n\nimport {\n useForwardedRef,\n useIntersectionObserver,\n useLayoutEffect,\n} from './utils'\n\nimport { useMatches } from './Matches'\nimport type {\n AnyRouter,\n Constrain,\n LinkCurrentTargetElement,\n LinkOptions,\n RegisteredRouter,\n RoutePaths,\n} from '@tanstack/router-core'\nimport type { ReactNode } from 'react'\nimport type {\n ValidateLinkOptions,\n ValidateLinkOptionsArray,\n} from './typePrimitives'\n\nexport function useLinkProps<\n TRouter extends AnyRouter = RegisteredRouter,\n const TFrom extends string = string,\n const TTo extends string | undefined = undefined,\n const TMaskFrom extends string = TFrom,\n const 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 hasRenderFetched = React.useRef(false)\n const innerRef = useForwardedRef(forwardedRef)\n\n const {\n // custom props\n activeProps = () => ({ className: 'active' }),\n inactiveProps = () => ({}),\n activeOptions,\n to,\n preload: userPreload,\n preloadDelay: userPreloadDelay,\n hashScrollIntoView,\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 const {\n // prevent these from being returned\n params: _params,\n search: _search,\n hash: _hash,\n state: _state,\n mask: _mask,\n reloadDocument: _reloadDocument,\n ...propsSafeToSpread\n } = rest\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 // subscribe to search params to re-build location if it changes\n const currentSearch = useRouterState({\n select: (s) => s.location.search,\n structuralSharing: true as any,\n })\n\n // when `from` is not supplied, use the leaf route of the current matches as the `from` location\n // so relative routing works as expected\n const from = useMatches({\n select: (matches) => options.from ?? matches[matches.length - 1]?.fullPath,\n })\n // Use it as the default `from` location\n const _options = React.useMemo(() => ({ ...options, from }), [options, from])\n\n const next = React.useMemo(\n () => router.buildLocation(_options as any),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [router, _options, currentSearch],\n )\n\n const preload = React.useMemo(() => {\n if (_options.reloadDocument) {\n return false\n }\n return userPreload ?? router.options.defaultPreload\n }, [router.options.defaultPreload, userPreload, _options.reloadDocument])\n const preloadDelay =\n userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0\n\n const isActive = useRouterState({\n select: (s) => {\n if (activeOptions?.exact) {\n const testExact = exactPathTest(\n s.location.pathname,\n next.pathname,\n router.basepath,\n )\n if (!testExact) {\n return false\n }\n } else {\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\n const pathIsFuzzyEqual = nextPathSplit.every(\n (d, i) => d === currentPathSplit[i],\n )\n if (!pathIsFuzzyEqual) {\n return false\n }\n }\n\n if (activeOptions?.includeSearch ?? true) {\n const searchTest = deepEqual(s.location.search, next.search, {\n partial: !activeOptions?.exact,\n ignoreUndefined: !activeOptions?.explicitUndefined,\n })\n if (!searchTest) {\n return false\n }\n }\n\n if (activeOptions?.includeHash) {\n return s.location.hash === next.hash\n }\n return true\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 preloadViewportIoCallback = React.useCallback(\n (entry: IntersectionObserverEntry | undefined) => {\n if (entry?.isIntersecting) {\n doPreload()\n }\n },\n [doPreload],\n )\n\n useIntersectionObserver(\n innerRef,\n preloadViewportIoCallback,\n { rootMargin: '100px' },\n { disabled: !!disabled || !(preload === 'viewport') },\n )\n\n useLayoutEffect(() => {\n if (hasRenderFetched.current) {\n return\n }\n if (!disabled && preload === 'render') {\n doPreload()\n hasRenderFetched.current = true\n }\n }, [disabled, doPreload, preload])\n\n if (type === 'external') {\n return {\n ...propsSafeToSpread,\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 // N.B. we don't call `router.commitLocation(next) here because we want to run `validateSearch` before committing\n return router.navigate({\n ..._options,\n replace,\n resetScroll,\n hashScrollIntoView,\n startTransition,\n viewTransition,\n ignoreBlocker,\n } as any)\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 ...propsSafeToSpread,\n ...resolvedActiveProps,\n ...resolvedInactiveProps,\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\ntype UseLinkReactProps<TComp> = TComp extends keyof React.JSX.IntrinsicElements\n ? React.JSX.IntrinsicElements[TComp]\n : React.PropsWithoutRef<\n TComp extends React.ComponentType<infer TProps> ? TProps : never\n > &\n React.RefAttributes<\n TComp extends\n | React.FC<{ ref: infer TRef }>\n | React.Component<{ ref: infer TRef }>\n ? TRef\n : never\n >\n\nexport type UseLinkPropsOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,\n TMaskTo extends string = '.',\n> = ActiveLinkOptions<'a', TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &\n UseLinkReactProps<'a'>\n\nexport type ActiveLinkOptions<\n TComp = 'a',\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = LinkOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &\n ActiveLinkOptionProps<TComp>\n\ntype ActiveLinkProps<TComp> = Partial<\n LinkComponentReactProps<TComp> & {\n [key: `data-${string}`]: unknown\n }\n>\n\nexport interface ActiveLinkOptionProps<TComp = 'a'> {\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?: ActiveLinkProps<TComp> | (() => ActiveLinkProps<TComp>)\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?: ActiveLinkProps<TComp> | (() => ActiveLinkProps<TComp>)\n}\n\nexport type LinkProps<\n TComp = 'a',\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = ActiveLinkOptions<TComp, 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> = Omit<\n UseLinkReactProps<TComp>,\n keyof CreateLinkProps\n>\n\nexport type LinkComponentProps<\n TComp = 'a',\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = LinkComponentReactProps<TComp> &\n LinkProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>\n\nexport type CreateLinkProps = LinkProps<\n any,\n any,\n string,\n string,\n string,\n string\n>\n\nexport type LinkComponent<\n in out TComp,\n in out TDefaultFrom extends string = string,\n> = <\n TRouter extends AnyRouter = RegisteredRouter,\n const TFrom extends string = TDefaultFrom,\n const TTo extends string | undefined = undefined,\n const TMaskFrom extends string = TFrom,\n const TMaskTo extends string = '',\n>(\n props: LinkComponentProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,\n) => React.ReactElement\n\nexport interface LinkComponentRoute<\n in out TDefaultFrom extends string = string,\n> {\n defaultFrom: TDefaultFrom\n <\n TRouter extends AnyRouter = RegisteredRouter,\n const TTo extends string | undefined = undefined,\n const TMaskTo extends string = '',\n >(\n props: LinkComponentProps<\n 'a',\n TRouter,\n this['defaultFrom'],\n TTo,\n this['defaultFrom'],\n TMaskTo\n >,\n ): React.ReactElement\n}\n\nexport function createLink<const TComp>(\n Comp: Constrain<TComp, any, (props: CreateLinkProps) => ReactNode>,\n): 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 {\n type: _type,\n ref: innerRef,\n ...linkProps\n } = useLinkProps(rest as any, 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 disabled 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\nexport type LinkOptionsFnOptions<\n TOptions,\n TComp,\n TRouter extends AnyRouter = RegisteredRouter,\n> =\n TOptions extends ReadonlyArray<any>\n ? ValidateLinkOptionsArray<TRouter, TOptions, string, TComp>\n : ValidateLinkOptions<TRouter, TOptions, string, TComp>\n\nexport type LinkOptionsFn<TComp> = <\n const TOptions,\n TRouter extends AnyRouter = RegisteredRouter,\n>(\n options: LinkOptionsFnOptions<TOptions, TComp, TRouter>,\n) => TOptions\n\nexport const linkOptions: LinkOptionsFn<'a'> = (options) => {\n return options as any\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"link.js","sources":["../../src/link.tsx"],"sourcesContent":["import * as React from 'react'\nimport { flushSync } from 'react-dom'\nimport {\n deepEqual,\n exactPathTest,\n functionalUpdate,\n preloadWarning,\n removeTrailingSlash,\n} from '@tanstack/router-core'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\n\nimport {\n useForwardedRef,\n useIntersectionObserver,\n useLayoutEffect,\n} from './utils'\n\nimport { useMatch } from './useMatch'\nimport { useMatches } from './Matches'\nimport type {\n AnyRouter,\n Constrain,\n LinkCurrentTargetElement,\n LinkOptions,\n RegisteredRouter,\n RoutePaths,\n} from '@tanstack/router-core'\nimport type { ReactNode } from 'react'\nimport type {\n ValidateLinkOptions,\n ValidateLinkOptionsArray,\n} from './typePrimitives'\n\nexport function useLinkProps<\n TRouter extends AnyRouter = RegisteredRouter,\n const TFrom extends string = string,\n const TTo extends string | undefined = undefined,\n const TMaskFrom extends string = TFrom,\n const 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 hasRenderFetched = React.useRef(false)\n const innerRef = useForwardedRef(forwardedRef)\n\n const {\n // custom props\n activeProps = () => ({ className: 'active' }),\n inactiveProps = () => ({}),\n activeOptions,\n to,\n preload: userPreload,\n preloadDelay: userPreloadDelay,\n hashScrollIntoView,\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 const {\n // prevent these from being returned\n params: _params,\n search: _search,\n hash: _hash,\n state: _state,\n mask: _mask,\n reloadDocument: _reloadDocument,\n ...propsSafeToSpread\n } = rest\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 // subscribe to search params to re-build location if it changes\n const currentSearch = useRouterState({\n select: (s) => s.location.search,\n structuralSharing: true as any,\n })\n\n const isRelativeFromPath = options.relative === 'path'\n\n // when `from` is not supplied, use the nearest parent match's full path as the `from` location\n // so relative routing works as expected. Try to stay out of rerenders as much as possible.\n const nearestFrom = useMatch({\n strict: false,\n select: (match) => (isRelativeFromPath ? undefined : match.fullPath),\n })\n\n // When no from and relative is path, use the leaf match as the from location\n // Avoid rerenders as much as possible.\n const leafFrom = useMatches({\n select: (matches) =>\n isRelativeFromPath ? matches[matches.length - 1]!.fullPath : undefined,\n })\n\n const from = options.from ?? (isRelativeFromPath ? leafFrom : nearestFrom)\n\n // Use it as the default `from` location\n const _options = React.useMemo(() => ({ ...options, from }), [options, from])\n\n const next = React.useMemo(\n () => router.buildLocation(_options as any),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [router, _options, currentSearch],\n )\n\n const preload = React.useMemo(() => {\n if (_options.reloadDocument) {\n return false\n }\n return userPreload ?? router.options.defaultPreload\n }, [router.options.defaultPreload, userPreload, _options.reloadDocument])\n const preloadDelay =\n userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0\n\n const isActive = useRouterState({\n select: (s) => {\n if (activeOptions?.exact) {\n const testExact = exactPathTest(\n s.location.pathname,\n next.pathname,\n router.basepath,\n )\n if (!testExact) {\n return false\n }\n } else {\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\n const pathIsFuzzyEqual = nextPathSplit.every(\n (d, i) => d === currentPathSplit[i],\n )\n if (!pathIsFuzzyEqual) {\n return false\n }\n }\n\n if (activeOptions?.includeSearch ?? true) {\n const searchTest = deepEqual(s.location.search, next.search, {\n partial: !activeOptions?.exact,\n ignoreUndefined: !activeOptions?.explicitUndefined,\n })\n if (!searchTest) {\n return false\n }\n }\n\n if (activeOptions?.includeHash) {\n return s.location.hash === next.hash\n }\n return true\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 preloadViewportIoCallback = React.useCallback(\n (entry: IntersectionObserverEntry | undefined) => {\n if (entry?.isIntersecting) {\n doPreload()\n }\n },\n [doPreload],\n )\n\n useIntersectionObserver(\n innerRef,\n preloadViewportIoCallback,\n { rootMargin: '100px' },\n { disabled: !!disabled || !(preload === 'viewport') },\n )\n\n useLayoutEffect(() => {\n if (hasRenderFetched.current) {\n return\n }\n if (!disabled && preload === 'render') {\n doPreload()\n hasRenderFetched.current = true\n }\n }, [disabled, doPreload, preload])\n\n if (type === 'external') {\n return {\n ...propsSafeToSpread,\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 // N.B. we don't call `router.commitLocation(next) here because we want to run `validateSearch` before committing\n return router.navigate({\n ..._options,\n replace,\n resetScroll,\n hashScrollIntoView,\n startTransition,\n viewTransition,\n ignoreBlocker,\n } as any)\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 ...propsSafeToSpread,\n ...resolvedActiveProps,\n ...resolvedInactiveProps,\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\ntype UseLinkReactProps<TComp> = TComp extends keyof React.JSX.IntrinsicElements\n ? React.JSX.IntrinsicElements[TComp]\n : React.PropsWithoutRef<\n TComp extends React.ComponentType<infer TProps> ? TProps : never\n > &\n React.RefAttributes<\n TComp extends\n | React.FC<{ ref: infer TRef }>\n | React.Component<{ ref: infer TRef }>\n ? TRef\n : never\n >\n\nexport type UseLinkPropsOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends RoutePaths<TRouter['routeTree']> | string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,\n TMaskTo extends string = '.',\n> = ActiveLinkOptions<'a', TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &\n UseLinkReactProps<'a'>\n\nexport type ActiveLinkOptions<\n TComp = 'a',\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = LinkOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> &\n ActiveLinkOptionProps<TComp>\n\ntype ActiveLinkProps<TComp> = Partial<\n LinkComponentReactProps<TComp> & {\n [key: `data-${string}`]: unknown\n }\n>\n\nexport interface ActiveLinkOptionProps<TComp = 'a'> {\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?: ActiveLinkProps<TComp> | (() => ActiveLinkProps<TComp>)\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?: ActiveLinkProps<TComp> | (() => ActiveLinkProps<TComp>)\n}\n\nexport type LinkProps<\n TComp = 'a',\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = ActiveLinkOptions<TComp, 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> = Omit<\n UseLinkReactProps<TComp>,\n keyof CreateLinkProps\n>\n\nexport type LinkComponentProps<\n TComp = 'a',\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = '.',\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '.',\n> = LinkComponentReactProps<TComp> &\n LinkProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>\n\nexport type CreateLinkProps = LinkProps<\n any,\n any,\n string,\n string,\n string,\n string\n>\n\nexport type LinkComponent<\n in out TComp,\n in out TDefaultFrom extends string = string,\n> = <\n TRouter extends AnyRouter = RegisteredRouter,\n const TFrom extends string = TDefaultFrom,\n const TTo extends string | undefined = undefined,\n const TMaskFrom extends string = TFrom,\n const TMaskTo extends string = '',\n>(\n props: LinkComponentProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,\n) => React.ReactElement\n\nexport interface LinkComponentRoute<\n in out TDefaultFrom extends string = string,\n> {\n defaultFrom: TDefaultFrom\n <\n TRouter extends AnyRouter = RegisteredRouter,\n const TTo extends string | undefined = undefined,\n const TMaskTo extends string = '',\n >(\n props: LinkComponentProps<\n 'a',\n TRouter,\n this['defaultFrom'],\n TTo,\n this['defaultFrom'],\n TMaskTo\n >,\n ): React.ReactElement\n}\n\nexport function createLink<const TComp>(\n Comp: Constrain<TComp, any, (props: CreateLinkProps) => ReactNode>,\n): 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 {\n type: _type,\n ref: innerRef,\n ...linkProps\n } = useLinkProps(rest as any, 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 disabled 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\nexport type LinkOptionsFnOptions<\n TOptions,\n TComp,\n TRouter extends AnyRouter = RegisteredRouter,\n> =\n TOptions extends ReadonlyArray<any>\n ? ValidateLinkOptionsArray<TRouter, TOptions, string, TComp>\n : ValidateLinkOptions<TRouter, TOptions, string, TComp>\n\nexport type LinkOptionsFn<TComp> = <\n const TOptions,\n TRouter extends AnyRouter = RegisteredRouter,\n>(\n options: LinkOptionsFnOptions<TOptions, TComp, TRouter>,\n) => TOptions\n\nexport const linkOptions: LinkOptionsFn<'a'> = (options) => {\n return options as any\n}\n"],"names":[],"mappings":"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"}
@@ -1,28 +1,35 @@
1
1
  import * as React from "react";
2
2
  import { useRouter } from "./useRouter.js";
3
+ import { useMatch } from "./useMatch.js";
3
4
  function useNavigate(_defaultOpts) {
4
- const { navigate } = useRouter();
5
+ const { navigate, state } = useRouter();
6
+ const matchIndex = useMatch({
7
+ strict: false,
8
+ select: (match) => match.index
9
+ });
5
10
  return React.useCallback(
6
11
  (options) => {
12
+ const isRelativeFromPath = options.relative === "path";
13
+ const from = options.from ?? (_defaultOpts == null ? void 0 : _defaultOpts.from) ?? (isRelativeFromPath ? state.matches[state.matches.length - 1].fullPath : state.matches[matchIndex].fullPath);
7
14
  return navigate({
8
- from: _defaultOpts == null ? void 0 : _defaultOpts.from,
9
- ...options
15
+ ...options,
16
+ from
10
17
  });
11
18
  },
19
+ // eslint-disable-next-line react-hooks/exhaustive-deps
12
20
  [_defaultOpts == null ? void 0 : _defaultOpts.from, navigate]
13
21
  );
14
22
  }
15
23
  function Navigate(props) {
16
24
  const router = useRouter();
25
+ const navigate = useNavigate();
17
26
  const previousPropsRef = React.useRef(null);
18
27
  React.useEffect(() => {
19
28
  if (previousPropsRef.current !== props) {
20
- router.navigate({
21
- ...props
22
- });
29
+ navigate(props);
23
30
  previousPropsRef.current = props;
24
31
  }
25
- }, [router, props]);
32
+ }, [router, props, navigate]);
26
33
  return null;
27
34
  }
28
35
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"useNavigate.js","sources":["../../src/useNavigate.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useRouter } from './useRouter'\nimport type {\n AnyRouter,\n FromPathOption,\n NavigateOptions,\n RegisteredRouter,\n UseNavigateResult,\n} from '@tanstack/router-core'\n\nexport function useNavigate<\n TRouter extends AnyRouter = RegisteredRouter,\n TDefaultFrom extends string = string,\n>(_defaultOpts?: {\n from?: FromPathOption<TRouter, TDefaultFrom>\n}): UseNavigateResult<TDefaultFrom> {\n const { navigate } = useRouter()\n\n return React.useCallback(\n (options: NavigateOptions) => {\n return navigate({\n from: _defaultOpts?.from,\n ...options,\n })\n },\n [_defaultOpts?.from, navigate],\n ) as UseNavigateResult<TDefaultFrom>\n}\n\nexport function Navigate<\n TRouter extends AnyRouter = RegisteredRouter,\n const TFrom extends string = string,\n const TTo extends string | undefined = undefined,\n const TMaskFrom extends string = TFrom,\n const TMaskTo extends string = '',\n>(props: NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>): null {\n const router = useRouter()\n\n const previousPropsRef = React.useRef<NavigateOptions<\n TRouter,\n TFrom,\n TTo,\n TMaskFrom,\n TMaskTo\n > | null>(null)\n React.useEffect(() => {\n if (previousPropsRef.current !== props) {\n router.navigate({\n ...props,\n })\n previousPropsRef.current = props\n }\n }, [router, props])\n return null\n}\n"],"names":[],"mappings":";;AAUO,SAAS,YAGd,cAEkC;AAC5B,QAAA,EAAE,SAAS,IAAI,UAAU;AAE/B,SAAO,MAAM;AAAA,IACX,CAAC,YAA6B;AAC5B,aAAO,SAAS;AAAA,QACd,MAAM,6CAAc;AAAA,QACpB,GAAG;AAAA,MAAA,CACJ;AAAA,IACH;AAAA,IACA,CAAC,6CAAc,MAAM,QAAQ;AAAA,EAC/B;AACF;AAEO,SAAS,SAMd,OAAuE;AACvE,QAAM,SAAS,UAAU;AAEnB,QAAA,mBAAmB,MAAM,OAMrB,IAAI;AACd,QAAM,UAAU,MAAM;AAChB,QAAA,iBAAiB,YAAY,OAAO;AACtC,aAAO,SAAS;AAAA,QACd,GAAG;AAAA,MAAA,CACJ;AACD,uBAAiB,UAAU;AAAA,IAAA;AAAA,EAC7B,GACC,CAAC,QAAQ,KAAK,CAAC;AACX,SAAA;AACT;"}
1
+ {"version":3,"file":"useNavigate.js","sources":["../../src/useNavigate.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useRouter } from './useRouter'\nimport { useMatch } from './useMatch'\nimport type {\n AnyRouter,\n FromPathOption,\n NavigateOptions,\n RegisteredRouter,\n UseNavigateResult,\n} from '@tanstack/router-core'\n\nexport function useNavigate<\n TRouter extends AnyRouter = RegisteredRouter,\n TDefaultFrom extends string = string,\n>(_defaultOpts?: {\n from?: FromPathOption<TRouter, TDefaultFrom>\n}): UseNavigateResult<TDefaultFrom> {\n const { navigate, state } = useRouter()\n\n // Just get the index of the current match to avoid rerenders\n // as much as possible\n const matchIndex = useMatch({\n strict: false,\n select: (match) => match.index,\n })\n\n return React.useCallback(\n (options: NavigateOptions) => {\n const isRelativeFromPath = options.relative === 'path'\n\n const from =\n options.from ??\n _defaultOpts?.from ??\n (isRelativeFromPath\n ? state.matches[state.matches.length - 1]!.fullPath\n : state.matches[matchIndex]!.fullPath)\n\n return navigate({\n ...options,\n from: from,\n })\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [_defaultOpts?.from, navigate],\n ) as UseNavigateResult<TDefaultFrom>\n}\n\nexport function Navigate<\n TRouter extends AnyRouter = RegisteredRouter,\n const TFrom extends string = string,\n const TTo extends string | undefined = undefined,\n const TMaskFrom extends string = TFrom,\n const TMaskTo extends string = '',\n>(props: NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>): null {\n const router = useRouter()\n const navigate = useNavigate()\n\n const previousPropsRef = React.useRef<NavigateOptions<\n TRouter,\n TFrom,\n TTo,\n TMaskFrom,\n TMaskTo\n > | null>(null)\n React.useEffect(() => {\n if (previousPropsRef.current !== props) {\n navigate(props)\n previousPropsRef.current = props\n }\n }, [router, props, navigate])\n return null\n}\n"],"names":[],"mappings":";;;AAWO,SAAS,YAGd,cAEkC;AAClC,QAAM,EAAE,UAAU,MAAM,IAAI,UAAU;AAItC,QAAM,aAAa,SAAS;AAAA,IAC1B,QAAQ;AAAA,IACR,QAAQ,CAAC,UAAU,MAAM;AAAA,EAAA,CAC1B;AAED,SAAO,MAAM;AAAA,IACX,CAAC,YAA6B;AACtB,YAAA,qBAAqB,QAAQ,aAAa;AAEhD,YAAM,OACJ,QAAQ,SACR,6CAAc,UACb,qBACG,MAAM,QAAQ,MAAM,QAAQ,SAAS,CAAC,EAAG,WACzC,MAAM,QAAQ,UAAU,EAAG;AAEjC,aAAO,SAAS;AAAA,QACd,GAAG;AAAA,QACH;AAAA,MAAA,CACD;AAAA,IACH;AAAA;AAAA,IAEA,CAAC,6CAAc,MAAM,QAAQ;AAAA,EAC/B;AACF;AAEO,SAAS,SAMd,OAAuE;AACvE,QAAM,SAAS,UAAU;AACzB,QAAM,WAAW,YAAY;AAEvB,QAAA,mBAAmB,MAAM,OAMrB,IAAI;AACd,QAAM,UAAU,MAAM;AAChB,QAAA,iBAAiB,YAAY,OAAO;AACtC,eAAS,KAAK;AACd,uBAAiB,UAAU;AAAA,IAAA;AAAA,EAE5B,GAAA,CAAC,QAAQ,OAAO,QAAQ,CAAC;AACrB,SAAA;AACT;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
- "version": "1.120.4-alpha.15",
3
+ "version": "1.120.4-alpha.19",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -54,7 +54,7 @@
54
54
  "tiny-invariant": "^1.3.3",
55
55
  "tiny-warning": "^1.0.3",
56
56
  "@tanstack/history": "1.120.4-alpha.1",
57
- "@tanstack/router-core": "1.120.4-alpha.15"
57
+ "@tanstack/router-core": "1.120.4-alpha.19"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@testing-library/jest-dom": "^6.6.3",
package/src/link.tsx CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  useLayoutEffect,
17
17
  } from './utils'
18
18
 
19
+ import { useMatch } from './useMatch'
19
20
  import { useMatches } from './Matches'
20
21
  import type {
21
22
  AnyRouter,
@@ -105,11 +106,24 @@ export function useLinkProps<
105
106
  structuralSharing: true as any,
106
107
  })
107
108
 
108
- // when `from` is not supplied, use the leaf route of the current matches as the `from` location
109
- // so relative routing works as expected
110
- const from = useMatches({
111
- select: (matches) => options.from ?? matches[matches.length - 1]?.fullPath,
109
+ const isRelativeFromPath = options.relative === 'path'
110
+
111
+ // when `from` is not supplied, use the nearest parent match's full path as the `from` location
112
+ // so relative routing works as expected. Try to stay out of rerenders as much as possible.
113
+ const nearestFrom = useMatch({
114
+ strict: false,
115
+ select: (match) => (isRelativeFromPath ? undefined : match.fullPath),
116
+ })
117
+
118
+ // When no from and relative is path, use the leaf match as the from location
119
+ // Avoid rerenders as much as possible.
120
+ const leafFrom = useMatches({
121
+ select: (matches) =>
122
+ isRelativeFromPath ? matches[matches.length - 1]!.fullPath : undefined,
112
123
  })
124
+
125
+ const from = options.from ?? (isRelativeFromPath ? leafFrom : nearestFrom)
126
+
113
127
  // Use it as the default `from` location
114
128
  const _options = React.useMemo(() => ({ ...options, from }), [options, from])
115
129
 
@@ -1,5 +1,6 @@
1
1
  import * as React from 'react'
2
2
  import { useRouter } from './useRouter'
3
+ import { useMatch } from './useMatch'
3
4
  import type {
4
5
  AnyRouter,
5
6
  FromPathOption,
@@ -14,15 +15,32 @@ export function useNavigate<
14
15
  >(_defaultOpts?: {
15
16
  from?: FromPathOption<TRouter, TDefaultFrom>
16
17
  }): UseNavigateResult<TDefaultFrom> {
17
- const { navigate } = useRouter()
18
+ const { navigate, state } = useRouter()
19
+
20
+ // Just get the index of the current match to avoid rerenders
21
+ // as much as possible
22
+ const matchIndex = useMatch({
23
+ strict: false,
24
+ select: (match) => match.index,
25
+ })
18
26
 
19
27
  return React.useCallback(
20
28
  (options: NavigateOptions) => {
29
+ const isRelativeFromPath = options.relative === 'path'
30
+
31
+ const from =
32
+ options.from ??
33
+ _defaultOpts?.from ??
34
+ (isRelativeFromPath
35
+ ? state.matches[state.matches.length - 1]!.fullPath
36
+ : state.matches[matchIndex]!.fullPath)
37
+
21
38
  return navigate({
22
- from: _defaultOpts?.from,
23
39
  ...options,
40
+ from: from,
24
41
  })
25
42
  },
43
+ // eslint-disable-next-line react-hooks/exhaustive-deps
26
44
  [_defaultOpts?.from, navigate],
27
45
  ) as UseNavigateResult<TDefaultFrom>
28
46
  }
@@ -35,6 +53,7 @@ export function Navigate<
35
53
  const TMaskTo extends string = '',
36
54
  >(props: NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>): null {
37
55
  const router = useRouter()
56
+ const navigate = useNavigate()
38
57
 
39
58
  const previousPropsRef = React.useRef<NavigateOptions<
40
59
  TRouter,
@@ -45,11 +64,9 @@ export function Navigate<
45
64
  > | null>(null)
46
65
  React.useEffect(() => {
47
66
  if (previousPropsRef.current !== props) {
48
- router.navigate({
49
- ...props,
50
- })
67
+ navigate(props)
51
68
  previousPropsRef.current = props
52
69
  }
53
- }, [router, props])
70
+ }, [router, props, navigate])
54
71
  return null
55
72
  }