/** * @deijose/nix-ionic / navigation.ts * * NavigationManager — a single coordination authority for Ionic navigation. * * The IonRouterOutlet handles DOM rendering, transitions, and cache. The * NavigationManager handles: * - Per-tab navigation stacks (extracted from the outlet) * - Navigation state tracking (isTransitioning, pending navigation) * - Navigation hooks (beforeNav, afterNav, onTabChange) * - Programmatic tab switching * - Route-pattern-based cache invalidation hooks * - Guard coordination (delegated to the core router when auto-bootstrapped) * * The outlet delegates navigation state to the manager, keeping a clean * separation: navigation logic lives here, rendering lives in the outlet. * * @example * ```ts * import { NavigationManager } from "@deijose/nix-ionic"; * * const nav = new NavigationManager({ tabs: ["/home", "/search", "/profile"] }); * * // Navigation hooks * nav.beforeNav((path, intent) => { * console.log("navigating to", path); * // return false to cancel * }); * nav.afterNav((path) => { * analytics.track("page_view", { path }); * }); * nav.onTabChange((tab) => { * console.log("active tab:", tab); * }); * * // Programmatic tab switching * nav.switchTab("/search"); * * // The outlet uses the manager for stack/state: * const outlet = new IonRouterOutlet(routes, { navigation: nav }); * ``` */ import { type Signal, type NavigationIntent, type NavigationDirection } from "@deijose/nix-js"; /** A function called before navigation completes. Return false to cancel. */ export type BeforeNavHook = (path: string, intent: NavigationIntent) => boolean | void | Promise; /** A function called after navigation completes. */ export type AfterNavHook = (path: string, direction: NavigationDirection) => void; /** A function called when the active tab changes. */ export type TabChangeHook = (tab: string, previousTab: string | null) => void; /** Options for NavigationManager construction. */ export interface NavigationManagerOptions { /** Tab prefixes for per-tab stack management. */ tabs?: string[]; } /** * Manages per-tab navigation stacks. Each tab has its own back/forward * stack. When the user switches tabs, the stack for the new tab is * restored. */ export declare class StackManager { private _stacks; private _activeTabKey; private _tabPrefixes; constructor(tabs: string[] | undefined); /** Get the tab key for a given path. */ keyForPath(path: string): string; /** Get the active tab key. */ get activeTabKey(): string; /** Get all registered tab prefixes. */ get tabPrefixes(): readonly string[]; /** Get the current stack depth for a tab. */ stackDepth(tabKey?: string): number; /** Get the top of the stack for a tab. */ stackTop(tabKey?: string): string | null; /** Get all entries in a tab's stack (copy). */ stackEntries(tabKey?: string): string[]; /** * Apply a navigation to the stacks. Returns the effective direction. * This is the same logic that was in IonRouterOutlet._stacks.apply(). */ apply(path: string, intent: NavigationIntent): NavigationDirection; /** Reset a specific tab's stack. */ resetTab(tabKey: string): void; /** Clear a specific tab's stack and return the paths that were in it. */ clearTabStack(tabKey: string): string[]; /** Reset all stacks. */ resetAll(): void; private _normalize; } /** * Single coordination authority for Ionic navigation. * * Wraps the per-tab stack manager and adds: * - Navigation hooks (beforeNav, afterNav, onTabChange) * - Transition state tracking (isTransitioning, pending) * - Programmatic tab switching * - Route-pattern-based cache invalidation hooks */ export declare class NavigationManager { private _stacks; private _beforeNavHooks; private _afterNavHooks; private _tabChangeHooks; private _cacheInvalidationHandlers; private _isTransitioning; private _pendingNav; private _lastTabKey; /** Reactive signal: true when the active tab has a back stack. */ readonly canGoBack: Signal; constructor(options?: NavigationManagerOptions); get stacks(): StackManager; get activeTab(): string; get tabPrefixes(): readonly string[]; stackDepth(tabKey?: string): number; stackTop(tabKey?: string): string | null; stackEntries(tabKey?: string): string[]; get isTransitioning(): boolean; get pendingNav(): { path: string; intent: NavigationIntent; } | null; /** Called by the outlet when a transition starts. */ beginTransition(): void; /** Called by the outlet when a transition ends. */ endTransition(): void; /** Queue a pending navigation (called by the outlet when already transitioning). */ setPendingNav(path: string, intent: NavigationIntent): void; /** Clear pending navigation. */ clearPendingNav(): void; /** Consume and return the pending navigation, if any. */ consumePendingNav(): { path: string; intent: NavigationIntent; } | null; /** * Register a hook called before navigation completes. * Return `false` to cancel the navigation. * Multiple hooks are called in registration order. */ beforeNav(hook: BeforeNavHook): () => void; /** * Register a hook called after navigation completes. */ afterNav(hook: AfterNavHook): () => void; /** * Register a hook called when the active tab changes. */ onTabChange(hook: TabChangeHook): () => void; /** * Run beforeNav hooks. Returns true if all hooks allow navigation, * false if any hook cancelled. */ runBeforeNav(path: string, intent: NavigationIntent): Promise; /** * Run afterNav hooks. Called by the outlet after a transition completes. */ runAfterNav(path: string, direction: NavigationDirection): void; /** * Run tab change hooks if the tab actually changed. */ runTabChangeIfNeeded(currentPath: string): void; /** * Update the reactive `canGoBack` signal based on the active tab's * stack depth. Called by the outlet after each transition. */ updateCanGoBack(): void; /** * Switch to a specific tab. Navigates to the tab's current stack top, * or the tab prefix if the stack is empty. * * @example * ```ts * nav.switchTab("/search"); // switches to the search tab * ``` */ switchTab(tabPrefix: string): string | null; /** * Register a cache invalidation handler for a route pattern. * The outlet registers handlers for each route on construction. * * @example * ```ts * nav.registerInvalidationHandler("/user/:id", (params) => { * // called when invalidateRoute("/user/:id", { id: "42" }) is invoked * }); * ``` */ registerInvalidationHandler(routePattern: string, handler: (params?: Record) => void): () => void; /** * Invalidate cached pages for a specific route pattern + params. * If the route has dynamic segments, provide params to target a * specific instance. Omit params to invalidate all instances. * * @example * ```ts * nav.invalidateRoute("/user/:id", { id: "42" }); // invalidate user 42 * nav.invalidateRoute("/search"); // invalidate all search instances * ``` */ invalidateRoute(routePattern: string, params?: Record): void; /** * Invalidate all cached pages matching a glob pattern. * Supports `*` as a wildcard suffix. * * @example * ```ts * nav.invalidatePattern("/admin/*"); // invalidate all admin pages * nav.invalidatePattern("/*"); // invalidate everything * ``` */ invalidatePattern(pattern: string): void; /** Reset all navigation state (stacks, hooks, pending). */ dispose(): void; }