/** * @jorvel/runtime — parallel routes / named slots (Next.js `@modal` / `@sidebar`). * * A layout can render several independent matched subtrees at once, each keyed * by a slot *name*. Every slot has its own route table and matches the current * pathname independently of the others, so e.g. a `@sidebar` slot can stay on * its section route while the main content navigates, and a `@modal` slot can * pop open for `/photo/:id` over the top of the page that opened it. * * ── API ────────────────────────────────────────────────────────────────────── * * const slots = defineSlots({ * modal: [{ path: '/photo/:id', element: }], * sidebar: [{ path: '/team/*', element: }], * }); * * function Layout() { * return ( * *
{children}
* // renders matched modal subtree * } /> // fallback when nothing matches *
* ); * } * * `` renders the element of the slot's first matching route, or its * `fallback` (then `null`) when nothing matches. `useSlot(name)` returns the * raw match (`{ element, params } | null`) for custom rendering. * * ── Intercepting routes ─────────────────────────────────────────────────────── * * A slot route can declare `intercept: true`. When the URL matches an * intercepting route, the slot renders it BUT the slot also reports an * `interceptedFrom` path — the pathname the user was on *before* the intercept, * captured by {@link InterceptionTracker}. This lets the main content keep * rendering the previous page (modal-over-page) instead of full-navigating. * Read it with `useInterceptedBase()` and feed it to your main router. * * Built on `matchPath` + `usePathname` — no new deps. SSR-safe: matching is pure * and `usePathname` falls back to the server router under SSR. */ import React from 'react'; export interface SlotRoute { /** Match pattern — same syntax as the route matcher (`:param`, `*`). */ path: string; /** Element rendered when this route matches within the slot. */ element: React.ReactNode; /** * Mark this as an intercepting route. While it matches, the slot opens over * the previously-visible page rather than replacing the main content. */ intercept?: boolean; } /** A resolved slot match handed to consumers via {@link useSlot}. */ export interface SlotMatch { element: React.ReactNode; params: Record; /** True when the matched route declared `intercept`. */ intercepted: boolean; } /** Named slot definitions: slot name → its route table. */ export type SlotsDefinition = Record; /** * Identity helper that returns the slots map unchanged, for inference + editor * hints. `const slots = defineSlots({ modal: [...], sidebar: [...] })`. */ export declare function defineSlots(slots: T): T; /** Pure: match a pathname against one slot's route table. */ export declare function matchSlot(routes: SlotRoute[], pathname: string): SlotMatch | null; export interface ParallelRoutesProps { slots: SlotsDefinition; children?: React.ReactNode; /** Override the pathname used for matching (testing / nested mounts). */ pathname?: string; } /** * Provides the slot definitions + current pathname to descendant * `` / `useSlot` consumers. Renders its children as-is — the layout * decides where each `` goes. */ export declare function ParallelRoutes({ slots, children, pathname: pathnameProp }: ParallelRoutesProps): React.ReactElement; /** * Returns the current match for a named slot, or `null`. Throws if used outside * a `` provider so misuse fails loudly in dev. */ export declare function useSlot(name: string): SlotMatch | null; export interface SlotOutletProps { name: string; /** Rendered when the slot has no matching route. Default `null`. */ fallback?: React.ReactNode; } /** * Renders the matched element for the named slot, or `fallback` (then nothing) * when no route in that slot matches the current pathname. */ export declare function SlotOutlet({ name, fallback }: SlotOutletProps): React.ReactElement; /** * Tracks the previously-visited pathname so intercepting slots can keep the main * content on the page that opened them. Wrap your app (above ``) * with this once. */ export declare function InterceptionTracker({ children }: { children?: React.ReactNode; }): React.ReactElement; /** * Returns the base pathname the main content should render while an intercepting * slot is open: the previous path when any slot currently has an intercepting * match, otherwise the live pathname. Use this to drive the main router so the * underlying page stays put behind a modal. * * Must be used inside ``; `` is optional * (without it there is no previous path, so the live pathname is returned). */ export declare function useInterceptedBase(): string; //# sourceMappingURL=parallel-routes.d.ts.map