Files
med-notes/.pnpm-store/v10/files/06/6ed1debfaff004e16f85e5159c6b41cff81891d766bb01d9b4c4546407c56fc634fcd4e9fffe4cdf3b0bdcec0950751e22905b34d8041ec23452f988691952
2025-05-09 05:30:08 +02:00

251 lines
7.5 KiB
Plaintext

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<React.JSX.IntrinsicElements['meta'] | undefined>
links?: Array<React.JSX.IntrinsicElements['link'] | undefined>
scripts?: Array<React.JSX.IntrinsicElements['script'] | undefined>
headScripts?: Array<React.JSX.IntrinsicElements['script'] | undefined>
}
}
export function Matches() {
const router = useRouter()
const pendingElement = router.options.defaultPendingComponent ? (
<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 = (
<ResolvedSuspense fallback={pendingElement}>
<Transitioner />
<MatchesInner />
</ResolvedSuspense>
)
return router.options.InnerWrap ? (
<router.options.InnerWrap>{inner}</router.options.InnerWrap>
) : (
inner
)
}
function MatchesInner() {
const matchId = useRouterState({
select: (s) => {
return s.matches[0]?.id
},
})
const resetKey = useRouterState({
select: (s) => s.loadedAt,
})
return (
<matchContext.Provider value={matchId}>
<CatchBoundary
getResetKey={() => 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 ? <Match matchId={matchId} /> : null}
</CatchBoundary>
</matchContext.Provider>
)
}
export type UseMatchRouteOptions<
TRouter extends AnyRouter = RegisteredRouter,
TFrom extends string = string,
TTo extends string | undefined = undefined,
TMaskFrom extends string = TFrom,
TMaskTo extends string = '',
> = ToSubOptionsProps<TRouter, TFrom, TTo> &
DeepPartial<MakeOptionalSearchParams<TRouter, TFrom, TTo>> &
DeepPartial<MakeOptionalPathParams<TRouter, TFrom, TTo>> &
MaskOptions<TRouter, TMaskFrom, TMaskTo> &
MatchRouteOptions
export function useMatchRoute<TRouter extends AnyRouter = RegisteredRouter>() {
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<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,
): false | ResolveRoute<TRouter, TFrom, TTo>['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<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & {
// 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<TFrom, NoInfer<TTo>>
>['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<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>): 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<MakeRouteMatchUnion<TRouter>>,
) => ValidateSelected<TRouter, TSelected, TStructuralSharing>
}
export type UseMatchesResult<
TRouter extends AnyRouter,
TSelected,
> = unknown extends TSelected ? Array<MakeRouteMatchUnion<TRouter>> : TSelected
export function useMatches<
TRouter extends AnyRouter = RegisteredRouter,
TSelected = unknown,
TStructuralSharing extends boolean = boolean,
>(
opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &
StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
): UseMatchesResult<TRouter, TSelected> {
return useRouterState({
select: (state: RouterState<TRouter['routeTree']>) => {
const matches = state.matches
return opts?.select
? opts.select(matches as Array<MakeRouteMatchUnion<TRouter>>)
: matches
},
structuralSharing: opts?.structuralSharing,
} as any) as UseMatchesResult<TRouter, TSelected>
}
export function useParentMatches<
TRouter extends AnyRouter = RegisteredRouter,
TSelected = unknown,
TStructuralSharing extends boolean = boolean,
>(
opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &
StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
): UseMatchesResult<TRouter, TSelected> {
const contextMatchId = React.useContext(matchContext)
return useMatches({
select: (matches: Array<MakeRouteMatchUnion<TRouter>>) => {
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<TRouter, TSelected, TStructuralSharing> &
StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
): UseMatchesResult<TRouter, TSelected> {
const contextMatchId = React.useContext(matchContext)
return useMatches({
select: (matches: Array<MakeRouteMatchUnion<TRouter>>) => {
matches = matches.slice(
matches.findIndex((d) => d.id === contextMatchId) + 1,
)
return opts?.select ? opts.select(matches) : matches
},
structuralSharing: opts?.structuralSharing,
} as any)
}