All files / react/src/router router.tsx

100% Statements 68/68
100% Branches 26/26
83.33% Functions 5/6
100% Lines 68/68

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    1x 1x     1x 1x 12x 12x   12x   12x 4x 1x 1x 1x 1x 1x   1x   3x 3x   12x 1x 1x   1x 12x 1x 1x 1x 1x   1x 6x 4x 2x 2x 2x 2x 4x   6x 2x 2x   6x 1x 6x   6x 6x   1x 1x   1x 2x 2x 2x 2x   2x   2x 2x   2x 2x 2x 2x   2x 2x   1x 1x   1x 9x 9x   9x 9x 1x   9x 9x  
import type { RouteOptions, RoutePath, Router, RouteRegistry, UnknownRoute } from '@anchorlib/router';
import type { FC, ReactNode } from 'react';
import { snippet } from '../hoc.js';
import { createEffect } from '../hooks.js';
import type { AnyRoute, RouteComponent } from './types.js';
 
export const RouteViewer = snippet<{ route: UnknownRoute; children?: ReactNode }>(
  ({ route, children }) => {
    const Index = route.index?.renderer;
    const Layout = route.renderer;
 
    if (!route.active) return children;
 
    if (Layout) {
      if (Index && route.index?.active) {
        return (
          <Layout>
            <Index />
            {children}
          </Layout>
        );
      }
 
      return <Layout>{children}</Layout>;
    }
 
    if (Index) {
      return <Index />;
    }
 
    return children;
  },
  'Route',
  'Renderer',
  false
);
 
const CRouteRenderer: FC<{ route: UnknownRoute; registry: RouteRegistry }> = ({ route, registry }) => {
  if (route.renderer) {
    if (route.index?.renderer) {
      (route.renderer as FC).displayName = `Layout(${route.path || '/'})`;
    } else {
      (route.renderer as FC).displayName = `Index(${route.path || '/'})`;
    }
  }
 
  if (route.index?.renderer) {
    (route.index.renderer as FC).displayName = `Index(${route.path || '/'})`;
  }
 
  const children = Array.from(registry).map(([, child]) => {
    return <RouteRenderer key={child.route.path} route={child.route} registry={child} />;
  });
 
  return <RouteViewer route={route}>{children}</RouteViewer>;
};
 
CRouteRenderer.displayName = 'Definition(Route)';
export const RouteRenderer = CRouteRenderer;
 
const CUIRouter: FC<{ router: Router<ReactNode>; root: RouteComponent<AnyRoute> }> = ({ router }) => {
  const activate = async () => {
    await router.activate(location.href);
    window.scrollTo(0, 0);
  };
 
  activate();
 
  createEffect(() => {
    window.addEventListener('popstate', activate);
 
    return () => {
      window.removeEventListener('popstate', activate);
    };
  });
 
  return <RouteRenderer key={'/'} route={router.rootRoute} registry={router.rootRegistry} />;
};
 
CUIRouter.displayName = 'UIRouter';
export const UIRouter = CUIRouter;
 
export function route<T extends AnyRoute>(route: T): RouteComponent<T> {
  const UIRoute: FC<{ children?: ReactNode }> = ({ children }) => children;
  UIRoute.displayName = `Route Factory(${route.path || '/'})`;
 
  (UIRoute as RouteComponent<T>).index = route as T;
  (UIRoute as RouteComponent<T>).route = (path: RoutePath, options?: RouteOptions) =>
    route.route(path as never, options);
 
  return UIRoute as RouteComponent<T>;
}