import * as React from 'react' import warning from 'tiny-warning' import { CatchBoundary, ErrorComponent } from './CatchBoundary' import { useRouterState } from './useRouterState' import { useRouter } from './useRouter' import { Transitioner } from './Transitioner' import { matchContext } from './matchContext' import { Match } from './Match' import { SafeFragment } from './SafeFragment' import type { StructuralSharingOption, ValidateSelected, } from './structuralSharing' import type { ReactNode } from './route' import type { AnyRouter, DeepPartial, MakeOptionalPathParams, MakeOptionalSearchParams, MakeRouteMatchUnion, MaskOptions, MatchRouteOptions, NoInfer, RegisteredRouter, ResolveRelativePath, ResolveRoute, RouteByPath, RouterState, ToSubOptionsProps, } from '@tanstack/router-core' declare module '@tanstack/router-core' { export interface RouteMatchExtensions { meta?: Array links?: Array scripts?: Array headScripts?: Array } } export function Matches() { const router = useRouter() const pendingElement = router.options.defaultPendingComponent ? ( ) : null // Do not render a root Suspense during SSR or hydrating from SSR const ResolvedSuspense = router.isServer || (typeof document !== 'undefined' && router.clientSsr) ? SafeFragment : React.Suspense const inner = ( ) return router.options.InnerWrap ? ( {inner} ) : ( inner ) } function MatchesInner() { const matchId = useRouterState({ select: (s) => { return s.matches[0]?.id }, }) const resetKey = useRouterState({ select: (s) => s.loadedAt, }) return ( resetKey} errorComponent={ErrorComponent} onCatch={(error) => { warning( false, `The following error wasn't caught by any route! At the very least, consider setting an 'errorComponent' in your RootRoute!`, ) warning(false, error.message || error.toString()) }} > {matchId ? : null} ) } export type UseMatchRouteOptions< TRouter extends AnyRouter = RegisteredRouter, TFrom extends string = string, TTo extends string | undefined = undefined, TMaskFrom extends string = TFrom, TMaskTo extends string = '', > = ToSubOptionsProps & DeepPartial> & DeepPartial> & MaskOptions & MatchRouteOptions export function useMatchRoute() { const router = useRouter() useRouterState({ select: (s) => [s.location.href, s.resolvedLocation?.href, s.status], structuralSharing: true as any, }) return React.useCallback( < const TFrom extends string = string, const TTo extends string | undefined = undefined, const TMaskFrom extends string = TFrom, const TMaskTo extends string = '', >( opts: UseMatchRouteOptions, ): false | ResolveRoute['types']['allParams'] => { const { pending, caseSensitive, fuzzy, includeSearch, ...rest } = opts return router.matchRoute(rest as any, { pending, caseSensitive, fuzzy, includeSearch, }) }, [router], ) } export type MakeMatchRouteOptions< TRouter extends AnyRouter = RegisteredRouter, TFrom extends string = string, TTo extends string | undefined = undefined, TMaskFrom extends string = TFrom, TMaskTo extends string = '', > = UseMatchRouteOptions & { // 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 children?: | (( params?: RouteByPath< TRouter['routeTree'], ResolveRelativePath> >['types']['allParams'], ) => ReactNode) | React.ReactNode } export function MatchRoute< TRouter extends AnyRouter = RegisteredRouter, const TFrom extends string = string, const TTo extends string | undefined = undefined, const TMaskFrom extends string = TFrom, const TMaskTo extends string = '', >(props: MakeMatchRouteOptions): any { const matchRoute = useMatchRoute() const params = matchRoute(props as any) as boolean if (typeof props.children === 'function') { return (props.children as any)(params) } return params ? props.children : null } export interface UseMatchesBaseOptions< TRouter extends AnyRouter, TSelected, TStructuralSharing, > { select?: ( matches: Array>, ) => ValidateSelected } export type UseMatchesResult< TRouter extends AnyRouter, TSelected, > = unknown extends TSelected ? Array> : TSelected export function useMatches< TRouter extends AnyRouter = RegisteredRouter, TSelected = unknown, TStructuralSharing extends boolean = boolean, >( opts?: UseMatchesBaseOptions & StructuralSharingOption, ): UseMatchesResult { return useRouterState({ select: (state: RouterState) => { const matches = state.matches return opts?.select ? opts.select(matches as Array>) : matches }, structuralSharing: opts?.structuralSharing, } as any) as UseMatchesResult } export function useParentMatches< TRouter extends AnyRouter = RegisteredRouter, TSelected = unknown, TStructuralSharing extends boolean = boolean, >( opts?: UseMatchesBaseOptions & StructuralSharingOption, ): UseMatchesResult { const contextMatchId = React.useContext(matchContext) return useMatches({ select: (matches: Array>) => { matches = matches.slice( 0, matches.findIndex((d) => d.id === contextMatchId), ) return opts?.select ? opts.select(matches) : matches }, structuralSharing: opts?.structuralSharing, } as any) } export function useChildMatches< TRouter extends AnyRouter = RegisteredRouter, TSelected = unknown, TStructuralSharing extends boolean = boolean, >( opts?: UseMatchesBaseOptions & StructuralSharingOption, ): UseMatchesResult { const contextMatchId = React.useContext(matchContext) return useMatches({ select: (matches: Array>) => { matches = matches.slice( matches.findIndex((d) => d.id === contextMatchId) + 1, ) return opts?.select ? opts.select(matches) : matches }, structuralSharing: opts?.structuralSharing, } as any) }