UNPKG

@tanstack/react-router

Version:

Modern and scalable routing for React applications

1 lines 13.9 kB
{"version":3,"file":"Matches.cjs","names":[],"sources":["../../src/Matches.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useStore } from '@tanstack/react-store'\nimport { replaceEqualDeep, rootRouteId } from '@tanstack/router-core'\nimport { isServer } from '@tanstack/router-core/isServer'\nimport { CatchBoundary, ErrorComponent } from './CatchBoundary'\nimport { useRouter } from './useRouter'\nimport { Transitioner } from './Transitioner'\nimport { matchContext } from './matchContext'\nimport { Match } from './Match'\nimport { SafeFragment } from './SafeFragment'\nimport type {\n StructuralSharingOption,\n ValidateSelected,\n} from './structuralSharing'\nimport type {\n AnyRoute,\n AnyRouter,\n DeepPartial,\n Expand,\n MakeOptionalPathParams,\n MakeOptionalSearchParams,\n MakeRouteMatchUnion,\n MaskOptions,\n MatchRouteOptions,\n NoInfer,\n RegisteredRouter,\n ResolveRelativePath,\n ResolveRoute,\n RouteByPath,\n ToSubOptionsProps,\n} from '@tanstack/router-core'\n\ndeclare module '@tanstack/router-core' {\n export interface RouteMatchExtensions {\n meta?: Array<React.JSX.IntrinsicElements['meta'] | undefined>\n links?: Array<React.JSX.IntrinsicElements['link'] | undefined>\n scripts?: Array<React.JSX.IntrinsicElements['script'] | undefined>\n styles?: Array<React.JSX.IntrinsicElements['style'] | undefined>\n headScripts?: Array<React.JSX.IntrinsicElements['script'] | undefined>\n }\n}\n\n/**\n * Internal component that renders the router's active match tree with\n * suspense, error, and not-found boundaries. Rendered by `RouterProvider`.\n */\nexport function Matches() {\n const router = useRouter()\n const rootRoute: AnyRoute = router.routesById[rootRouteId]\n\n const PendingComponent =\n rootRoute.options.pendingComponent ?? router.options.defaultPendingComponent\n\n const pendingElement = PendingComponent ? <PendingComponent /> : null\n\n // Do not render a root Suspense during SSR or hydrating from SSR\n const ResolvedSuspense =\n (isServer ?? router.isServer) ||\n (typeof document !== 'undefined' && router.ssr)\n ? SafeFragment\n : React.Suspense\n\n const inner = (\n <ResolvedSuspense fallback={pendingElement}>\n {!(isServer ?? router.isServer) && <Transitioner />}\n <MatchesInner />\n </ResolvedSuspense>\n )\n\n return router.options.InnerWrap ? (\n <router.options.InnerWrap>{inner}</router.options.InnerWrap>\n ) : (\n inner\n )\n}\n\nfunction MatchesInner() {\n const router = useRouter()\n const _isServer = isServer ?? router.isServer\n const matchId = _isServer\n ? router.stores.firstMatchId.state\n : // eslint-disable-next-line react-hooks/rules-of-hooks\n useStore(router.stores.firstMatchId, (id) => id)\n const resetKey = _isServer\n ? router.stores.loadedAt.state\n : // eslint-disable-next-line react-hooks/rules-of-hooks\n useStore(router.stores.loadedAt, (loadedAt) => loadedAt)\n\n const matchComponent = matchId ? <Match matchId={matchId} /> : null\n\n return (\n <matchContext.Provider value={matchId}>\n {router.options.disableGlobalCatchBoundary ? (\n matchComponent\n ) : (\n <CatchBoundary\n getResetKey={() => resetKey}\n errorComponent={ErrorComponent}\n onCatch={\n process.env.NODE_ENV !== 'production'\n ? (error) => {\n console.warn(\n `Warning: The following error wasn't caught by any route! At the very least, consider setting an 'errorComponent' in your RootRoute!`,\n )\n console.warn(`Warning: ${error.message || error.toString()}`)\n }\n : undefined\n }\n >\n {matchComponent}\n </CatchBoundary>\n )}\n </matchContext.Provider>\n )\n}\n\nexport type UseMatchRouteOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = undefined,\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '',\n> = ToSubOptionsProps<TRouter, TFrom, TTo> &\n DeepPartial<MakeOptionalSearchParams<TRouter, TFrom, TTo>> &\n DeepPartial<MakeOptionalPathParams<TRouter, TFrom, TTo>> &\n MaskOptions<TRouter, TMaskFrom, TMaskTo> &\n MatchRouteOptions\n\n/**\n * Create a matcher function for testing locations against route definitions.\n *\n * The returned function accepts standard navigation options (`to`, `params`,\n * `search`, etc.) and returns either `false` (no match) or the matched params\n * object when the route matches the current or pending location.\n *\n * Useful for conditional rendering and active UI states.\n *\n * @returns A `matchRoute(options)` function that returns `false` or params.\n * @link https://tanstack.com/router/latest/docs/framework/react/api/router/useMatchRouteHook\n */\nexport function useMatchRoute<TRouter extends AnyRouter = RegisteredRouter>() {\n const router = useRouter()\n\n if (!(isServer ?? router.isServer)) {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useStore(router.stores.matchRouteReactivity, (d) => d)\n }\n\n return React.useCallback(\n <\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 opts: UseMatchRouteOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,\n ):\n | false\n | Expand<ResolveRoute<TRouter, TFrom, TTo>['types']['allParams']> => {\n const { pending, caseSensitive, fuzzy, includeSearch, ...rest } = opts\n\n return router.matchRoute(rest as any, {\n pending,\n caseSensitive,\n fuzzy,\n includeSearch,\n })\n },\n [router],\n )\n}\n\nexport type MakeMatchRouteOptions<\n TRouter extends AnyRouter = RegisteredRouter,\n TFrom extends string = string,\n TTo extends string | undefined = undefined,\n TMaskFrom extends string = TFrom,\n TMaskTo extends string = '',\n> = UseMatchRouteOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & {\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 | ((\n params?: RouteByPath<\n TRouter['routeTree'],\n ResolveRelativePath<TFrom, NoInfer<TTo>>\n >['types']['allParams'],\n ) => React.ReactNode)\n | React.ReactNode\n}\n\n/**\n * Component that conditionally renders its children based on whether a route\n * matches the provided `from`/`to` options. If `children` is a function, it\n * receives the matched params object.\n *\n * @link https://tanstack.com/router/latest/docs/framework/react/api/router/matchRouteComponent\n */\nexport function MatchRoute<\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: MakeMatchRouteOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>): any {\n const matchRoute = useMatchRoute()\n const params = matchRoute(props as any) as boolean\n\n if (typeof props.children === 'function') {\n return (props.children as any)(params)\n }\n\n return params ? props.children : null\n}\n\nexport interface UseMatchesBaseOptions<\n TRouter extends AnyRouter,\n TSelected,\n TStructuralSharing,\n> {\n select?: (\n matches: Array<MakeRouteMatchUnion<TRouter>>,\n ) => ValidateSelected<TRouter, TSelected, TStructuralSharing>\n}\n\nexport type UseMatchesResult<\n TRouter extends AnyRouter,\n TSelected,\n> = unknown extends TSelected ? Array<MakeRouteMatchUnion<TRouter>> : TSelected\n\nexport function useMatches<\n TRouter extends AnyRouter = RegisteredRouter,\n TSelected = unknown,\n TStructuralSharing extends boolean = boolean,\n>(\n opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &\n StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,\n): UseMatchesResult<TRouter, TSelected> {\n const router = useRouter<TRouter>()\n const previousResult =\n React.useRef<ValidateSelected<TRouter, TSelected, TStructuralSharing>>(\n undefined,\n )\n\n if (isServer ?? router.isServer) {\n const matches = router.stores.activeMatchesSnapshot.state as Array<\n MakeRouteMatchUnion<TRouter>\n >\n return (opts?.select ? opts.select(matches) : matches) as UseMatchesResult<\n TRouter,\n TSelected\n >\n }\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useStore(router.stores.activeMatchesSnapshot, (matches) => {\n const selected = opts?.select\n ? opts.select(matches as Array<MakeRouteMatchUnion<TRouter>>)\n : (matches as any)\n\n if (opts?.structuralSharing ?? router.options.defaultStructuralSharing) {\n const shared = replaceEqualDeep(previousResult.current, selected)\n previousResult.current = shared\n return shared\n }\n\n return selected\n }) as UseMatchesResult<TRouter, TSelected>\n}\n\n/**\n * Read the full array of active route matches or select a derived subset.\n *\n * Useful for debugging, breadcrumbs, or aggregating metadata across matches.\n *\n * @returns The array of matches (or the selected value).\n * @link https://tanstack.com/router/latest/docs/framework/react/api/router/useMatchesHook\n */\n\n/**\n * Read the full array of active route matches or select a derived subset.\n *\n * Useful for debugging, breadcrumbs, or aggregating metadata across matches.\n *\n * @link https://tanstack.com/router/latest/docs/framework/react/api/router/useMatchesHook\n */\nexport function useParentMatches<\n TRouter extends AnyRouter = RegisteredRouter,\n TSelected = unknown,\n TStructuralSharing extends boolean = boolean,\n>(\n opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &\n StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,\n): UseMatchesResult<TRouter, TSelected> {\n const contextMatchId = React.useContext(matchContext)\n\n return useMatches({\n select: (matches: Array<MakeRouteMatchUnion<TRouter>>) => {\n matches = matches.slice(\n 0,\n matches.findIndex((d) => d.id === contextMatchId),\n )\n return opts?.select ? opts.select(matches) : matches\n },\n structuralSharing: opts?.structuralSharing,\n } as any)\n}\n\n/**\n * Read the array of active route matches that are children of the current\n * match (or selected parent) in the match tree.\n */\nexport function useChildMatches<\n TRouter extends AnyRouter = RegisteredRouter,\n TSelected = unknown,\n TStructuralSharing extends boolean = boolean,\n>(\n opts?: UseMatchesBaseOptions<TRouter, TSelected, TStructuralSharing> &\n StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,\n): UseMatchesResult<TRouter, TSelected> {\n const contextMatchId = React.useContext(matchContext)\n\n return useMatches({\n select: (matches: Array<MakeRouteMatchUnion<TRouter>>) => {\n matches = matches.slice(\n matches.findIndex((d) => d.id === contextMatchId) + 1,\n )\n return opts?.select ? opts.select(matches) : matches\n },\n structuralSharing: opts?.structuralSharing,\n } as any)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA8CA,SAAgB,UAAU;CACxB,MAAM,SAAS,kBAAA,WAAW;CAG1B,MAAM,mBAFsB,OAAO,WAAW,sBAAA,aAGlC,QAAQ,oBAAoB,OAAO,QAAQ;CAEvD,MAAM,iBAAiB,mBAAmB,iBAAA,GAAA,kBAAA,KAAC,kBAAD,EAAoB,CAAA,GAAG;CASjE,MAAM,QACJ,iBAAA,GAAA,kBAAA,OANC,+BAAA,YAAY,OAAO,aACnB,OAAO,aAAa,eAAe,OAAO,MACvC,qBAAA,eACA,MAAM,UAGV;EAAkB,UAAU;YAA5B,CACG,EAAE,+BAAA,YAAY,OAAO,aAAa,iBAAA,GAAA,kBAAA,KAAC,qBAAA,cAAD,EAAgB,CAAA,EACnD,iBAAA,GAAA,kBAAA,KAAC,cAAD,EAAgB,CAAA,CACC;;AAGrB,QAAO,OAAO,QAAQ,YACpB,iBAAA,GAAA,kBAAA,KAAC,OAAO,QAAQ,WAAhB,EAAA,UAA2B,OAAiC,CAAA,GAE5D;;AAIJ,SAAS,eAAe;CACtB,MAAM,SAAS,kBAAA,WAAW;CAC1B,MAAM,YAAY,+BAAA,YAAY,OAAO;CACrC,MAAM,UAAU,YACZ,OAAO,OAAO,aAAa,SAAA,GAAA,sBAAA,UAElB,OAAO,OAAO,eAAe,OAAO,GAAG;CACpD,MAAM,WAAW,YACb,OAAO,OAAO,SAAS,SAAA,GAAA,sBAAA,UAEd,OAAO,OAAO,WAAW,aAAa,SAAS;CAE5D,MAAM,iBAAiB,UAAU,iBAAA,GAAA,kBAAA,KAAC,cAAA,OAAD,EAAgB,SAAW,CAAA,GAAG;AAE/D,QACE,iBAAA,GAAA,kBAAA,KAAC,qBAAA,aAAa,UAAd;EAAuB,OAAO;YAC3B,OAAO,QAAQ,6BACd,iBAEA,iBAAA,GAAA,kBAAA,KAAC,sBAAA,eAAD;GACE,mBAAmB;GACnB,gBAAgB,sBAAA;GAChB,SAAA,QAAA,IAAA,aAC2B,gBACpB,UAAU;AACT,YAAQ,KACN,sIACD;AACD,YAAQ,KAAK,YAAY,MAAM,WAAW,MAAM,UAAU,GAAG;OAE/D,KAAA;aAGL;GACa,CAAA;EAEI,CAAA;;;;;;;;;;;;;;AA4B5B,SAAgB,gBAA8D;CAC5E,MAAM,SAAS,kBAAA,WAAW;AAE1B,KAAI,EAAE,+BAAA,YAAY,OAAO,UAEvB,EAAA,GAAA,sBAAA,UAAS,OAAO,OAAO,uBAAuB,MAAM,EAAE;AAGxD,QAAO,MAAM,aAOT,SAGqE;EACrE,MAAM,EAAE,SAAS,eAAe,OAAO,eAAe,GAAG,SAAS;AAElE,SAAO,OAAO,WAAW,MAAa;GACpC;GACA;GACA;GACA;GACD,CAAC;IAEJ,CAAC,OAAO,CACT;;;;;;;;;AA4BH,SAAgB,WAMd,OAA4E;CAE5E,MAAM,SADa,eAAe,CACR,MAAa;AAEvC,KAAI,OAAO,MAAM,aAAa,WAC5B,QAAQ,MAAM,SAAiB,OAAO;AAGxC,QAAO,SAAS,MAAM,WAAW;;AAkBnC,SAAgB,WAKd,MAEsC;CACtC,MAAM,SAAS,kBAAA,WAAoB;CACnC,MAAM,iBACJ,MAAM,OACJ,KAAA,EACD;AAEH,KAAI,+BAAA,YAAY,OAAO,UAAU;EAC/B,MAAM,UAAU,OAAO,OAAO,sBAAsB;AAGpD,SAAQ,MAAM,SAAS,KAAK,OAAO,QAAQ,GAAG;;AAOhD,SAAA,GAAA,sBAAA,UAAgB,OAAO,OAAO,wBAAwB,YAAY;EAChE,MAAM,WAAW,MAAM,SACnB,KAAK,OAAO,QAA+C,GAC1D;AAEL,MAAI,MAAM,qBAAqB,OAAO,QAAQ,0BAA0B;GACtE,MAAM,UAAA,GAAA,sBAAA,kBAA0B,eAAe,SAAS,SAAS;AACjE,kBAAe,UAAU;AACzB,UAAO;;AAGT,SAAO;GACP;;;;;;;;;;;;;;;;;AAmBJ,SAAgB,iBAKd,MAEsC;CACtC,MAAM,iBAAiB,MAAM,WAAW,qBAAA,aAAa;AAErD,QAAO,WAAW;EAChB,SAAS,YAAiD;AACxD,aAAU,QAAQ,MAChB,GACA,QAAQ,WAAW,MAAM,EAAE,OAAO,eAAe,CAClD;AACD,UAAO,MAAM,SAAS,KAAK,OAAO,QAAQ,GAAG;;EAE/C,mBAAmB,MAAM;EAC1B,CAAQ;;;;;;AAOX,SAAgB,gBAKd,MAEsC;CACtC,MAAM,iBAAiB,MAAM,WAAW,qBAAA,aAAa;AAErD,QAAO,WAAW;EAChB,SAAS,YAAiD;AACxD,aAAU,QAAQ,MAChB,QAAQ,WAAW,MAAM,EAAE,OAAO,eAAe,GAAG,EACrD;AACD,UAAO,MAAM,SAAS,KAAK,OAAO,QAAQ,GAAG;;EAE/C,mBAAmB,MAAM;EAC1B,CAAQ"}