/**
* @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