All files router-view.ts

15.62% Statements 5/32
100% Branches 0/0
0% Functions 0/1
15.62% Lines 5/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 1141x             1x           1x                                                                                                                     1x                                                                               1x  
import {
    type ComponentType,
    createElement,
    isValidElement,
    type ReactElement,
    useMemo
} from 'react';
import {
    RouterViewDepthContext,
    useRoute,
    useRouterViewDepth
} from './context';
import type { RouterViewProps } from './types';
import { resolveComponent } from './util';
 
/**
 * RouterView component that renders the matched route component.
 * Acts as a placeholder where route components are rendered based on the current route.
 * Supports nested routing with automatic depth tracking.
 *
 * @param props - Component props
 * @param props.fallback - Optional fallback component when no route matches
 *
 * @example
 * ```tsx
 * // Basic usage
 * import { RouterView } from '@esmx/router-react';
 *
 * function App() {
 *   return (
 *     <div>
 *       <nav>
 *         <RouterLink to="/">Home</RouterLink>
 *         <RouterLink to="/about">About</RouterLink>
 *       </nav>
 *       <RouterView />
 *     </div>
 *   );
 * }
 * ```
 *
 * @example
 * ```tsx
 * // Nested routing
 * // Routes: [
 * //   { path: '/users', component: UsersLayout, children: [
 * //     { path: ':id', component: UserProfile }
 * //   ]}
 * // ]
 *
 * function UsersLayout() {
 *   return (
 *     <div>
 *       <h1>Users</h1>
 *       <RouterView /> // Renders UserProfile for /users/:id
 *     </div>
 *   );
 * }
 * ```
 *
 * @example
 * ```tsx
 * // With fallback
 * import { RouterView } from '@esmx/router-react';
 *
 * function App() {
 *   return (
 *     <RouterView fallback={<div>Page not found</div>} />
 *   );
 * }
 * ```
 */
export function RouterView({ fallback }: RouterViewProps): ReactElement | null {
    const route = useRoute();
    const depth = useRouterViewDepth();
 
    // Get the matched route at current depth
    const matchedRoute = route.matched[depth];
 
    // Resolve the component from the matched route
    const Component = useMemo(() => {
        if (!matchedRoute?.component) {
            return null;
        }
        return resolveComponent(matchedRoute.component) as ComponentType<any>;
    }, [matchedRoute?.component]);
 
    // Render fallback if no component found
    if (!Component) {
        if (fallback) {
            // If fallback is already a ReactElement, return it
            if (isValidElement(fallback)) {
                return fallback;
            }
            // If fallback is a component, render it
            if (typeof fallback === 'function') {
                return createElement(fallback as ComponentType);
            }
            // Return null for other cases (shouldn't happen with proper typing)
            return null;
        }
        return null;
    }
 
    // Provide incremented depth for nested RouterViews using createElement
    return createElement(
        RouterViewDepthContext.Provider,
        { value: depth + 1 },
        createElement(Component, { key: matchedRoute.compilePath })
    );
}
 
RouterView.displayName = 'RouterView';