import type { ReactNode } from 'react';
import { ProceduralDemoPage } from '../pages/ProceduralDemoPage';
import {
getDefaultProceduralDemoId,
getProceduralDemoMachine,
type ProceduralDemoMachineId,
} from '../modules/machines/procedural/proceduralDemoCatalog';
export type ProceduralDemoRouteKind = 'none' | 'catalogue' | 'machine';
export interface ProceduralDemoRouteMatch {
kind: ProceduralDemoRouteKind;
matched: boolean;
basePath?: string;
machineId?: ProceduralDemoMachineId;
}
export interface ProceduralDemoRouteOutletProps {
pathname?: string;
search?: string;
fallback?: ReactNode;
embedded?: boolean;
}
const BASE_SEGMENTS = new Set(['procedural-demo', 'procedural-demos', 'procedural-lab', 'demo-lab']);
function pathSegments(pathname: string): string[] {
return pathname.split('/').map(decodeURIComponent).filter(Boolean);
}
function queryMachineId(search: string): ProceduralDemoMachineId | undefined {
const params = new URLSearchParams(search);
const raw = params.get('machine') ?? params.get('demo') ?? params.get('id');
return getProceduralDemoMachine(raw)?.id;
}
export function matchProceduralDemoRoute(
pathname = typeof window !== 'undefined' ? window.location.pathname : '/',
search = typeof window !== 'undefined' ? window.location.search : '',
): ProceduralDemoRouteMatch {
const segments = pathSegments(pathname);
const queryId = queryMachineId(search);
if (segments.length === 0) {
return { kind: 'none', matched: false };
}
const [first, second, third] = segments;
if (BASE_SEGMENTS.has(first)) {
if (second) {
const machine = getProceduralDemoMachine(second);
if (machine) {
return {
kind: 'machine',
matched: true,
basePath: `/${first}`,
machineId: machine.id,
};
}
}
if (queryId) {
return {
kind: 'machine',
matched: true,
basePath: `/${first}`,
machineId: queryId,
};
}
return {
kind: 'catalogue',
matched: true,
basePath: `/${first}`,
};
}
if ((first === 'viewer' || first === 'machines' || first === 'machine') && second) {
const directMachine = getProceduralDemoMachine(second);
if (directMachine) {
return {
kind: 'machine',
matched: true,
basePath: `/${first}`,
machineId: directMachine.id,
};
}
if ((second === 'procedural' || second === 'procedural-demo' || second === 'procedural-demos') && third) {
const nestedMachine = getProceduralDemoMachine(third);
if (nestedMachine) {
return {
kind: 'machine',
matched: true,
basePath: `/${first}/${second}`,
machineId: nestedMachine.id,
};
}
}
}
if (queryId && (first === 'viewer' || first === 'machines' || first === 'catalogue' || first === 'catalog')) {
return {
kind: 'machine',
matched: true,
basePath: `/${first}`,
machineId: queryId,
};
}
return { kind: 'none', matched: false };
}
export function buildProceduralDemoPath(
machineId: string | undefined = getDefaultProceduralDemoId(),
basePath = '/procedural-demos',
): string {
const machine = getProceduralDemoMachine(machineId) ?? getProceduralDemoMachine(getDefaultProceduralDemoId());
return `${basePath.replace(/\/$/, '')}/${machine?.id ?? getDefaultProceduralDemoId()}`;
}
export const proceduralDemoRoutes = [
{
path: '/procedural-demos',
label: 'Procedural demo catalogue',
element: ,
},
{
path: '/procedural-demos/:machineId',
label: 'Procedural machine demo',
element: ,
},
{
path: '/viewer/:machineId',
label: 'Viewer-compatible procedural machine demo',
element: ,
},
] as const;
export function ProceduralDemoRouteOutlet({
pathname,
search,
fallback = null,
embedded = false,
}: ProceduralDemoRouteOutletProps) {
const match = matchProceduralDemoRoute(pathname, search);
if (!match.matched || match.kind !== 'machine') {
return <>{fallback}>;
}
return ;
}