From f84c1a4125095eeb8f1df1c82a55261eb650da69 Mon Sep 17 00:00:00 2001 From: Veljo Lasn Date: Mon, 29 Jul 2024 14:38:16 +0300 Subject: [PATCH 1/2] feat: add support for multi-route modes --- platform/app/src/routes/Mode/Mode.tsx | 10 ++- platform/app/src/routes/buildModeRoutes.tsx | 89 +++++++++++++++++++-- 2 files changed, 90 insertions(+), 9 deletions(-) diff --git a/platform/app/src/routes/Mode/Mode.tsx b/platform/app/src/routes/Mode/Mode.tsx index 07b1512dfdd..be74ca6f26c 100644 --- a/platform/app/src/routes/Mode/Mode.tsx +++ b/platform/app/src/routes/Mode/Mode.tsx @@ -16,6 +16,7 @@ const { getSplitParam } = utils; export default function ModeRoute({ mode, + modeRoutePath, dataSourceName, extensionManager, servicesManager, @@ -84,7 +85,13 @@ export default function ModeRoute({ const dataSource = extensionManager.getActiveDataSource()[0]; // Only handling one route per mode for now - const route = mode.routes[0]; + let route = mode.routes[0]; + if (modeRoutePath) { + route = mode.routes.find(route => route.path === modeRoutePath); + if (!route) { + throw new Error(`Route not found for path: ${modeRoutePath}`); + } + } useEffect(() => { const loadExtensions = async () => { @@ -398,6 +405,7 @@ function createCombinedContextProvider(extensionManager, servicesManager, comman ModeRoute.propTypes = { mode: PropTypes.object.isRequired, + modeRoutePath: PropTypes.string, dataSourceName: PropTypes.string, extensionManager: PropTypes.object, servicesManager: PropTypes.object, diff --git a/platform/app/src/routes/buildModeRoutes.tsx b/platform/app/src/routes/buildModeRoutes.tsx index 77dfde04e67..d3efc3a394b 100644 --- a/platform/app/src/routes/buildModeRoutes.tsx +++ b/platform/app/src/routes/buildModeRoutes.tsx @@ -4,7 +4,8 @@ import ModeRoute from '@routes/Mode'; /* Routes uniquely define an entry point to: - A mode - - Linked to a data source + - A mode route + - A data source - With a specified data set. The full route template is: @@ -20,6 +21,14 @@ import ModeRoute from '@routes/Mode'; A default source can be specified at the app level configuration, and then that source is used if :sourceType is omitted: /:modeId/:modeRoute/?queryParameters=example + + Additionally, not specifying a modeRoute will default to the first route defined in the mode: + + /:modeId/?queryParameters=example + + You can still specify a sourceType in this case - the first mode route will be used with the specified sourceType: + + /:modeId/:sourceType/?queryParameters=example */ export default function buildModeRoutes({ modes, @@ -38,12 +47,16 @@ export default function buildModeRoutes({ dataSourceNames.push(sourceName); } }); - + const allPaths = new Set(); modes.forEach(mode => { - // todo: for each route. add route to path. dataSourceNames.forEach(dataSourceName => { - const path = `/${mode.routeName}/${dataSourceName}`; - + const datasourcePath = `/${mode.routeName}/${dataSourceName}`; + if (allPaths.has(datasourcePath)) { + console.warn( + `Duplicate path '${datasourcePath}' in buildModeRoutes, mode ${mode.routeName}, dataSource ${dataSourceName}` + ); + } + allPaths.add(datasourcePath); // TODO move up. const children = () => ( { + const path = `/${mode.routeName}/${route.path}/${dataSourceName}`; + if (allPaths.has(path)) { + console.warn( + `Duplicate path '${path}' in buildModeRoutes, mode ${mode.routeName}, route ${route.path}, dataSource ${dataSourceName}` + ); + } + allPaths.add(path); + // TODO move up. + const children = () => ( + + ); + + routes.push({ + path, + children, + private: true, + }); + }); }); // Add active DataSource route. // This is the DataSource route for the active data source defined in ExtensionManager.getActiveDataSource - const path = `/${mode.routeName}`; + const modePath = `/${mode.routeName}`; // TODO move up. const children = () => ( @@ -78,11 +119,43 @@ export default function buildModeRoutes({ /> ); + if (allPaths.has(modePath)) { + console.warn(`Duplicate path '${modePath}' in buildModeRoutes, mode ${mode.routeName}`); + } + allPaths.add(modePath); + routes.push({ - path, + path: modePath, children, private: true, }); + + mode.routes.forEach(route => { + const path = `/${mode.routeName}/${route.path}`; + if (allPaths.has(path)) { + console.warn( + `Duplicate path '${path}' in buildModeRoutes, mode ${mode.routeName}, route ${route.path}` + ); + } + allPaths.add(path); + + const children = () => ( + + ); + + routes.push({ + path, + children, + private: true, + }); + }); }); return routes; From 6ee404f1fd6925cc59f353354ecff4cc96a6771e Mon Sep 17 00:00:00 2001 From: Veljo Lasn Date: Mon, 29 Jul 2024 14:38:30 +0300 Subject: [PATCH 2/2] feat: add a multi-route example to basic-dev-mode --- modes/basic-dev-mode/src/index.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/modes/basic-dev-mode/src/index.ts b/modes/basic-dev-mode/src/index.ts index 649dd81cd16..d3fbb1e16a2 100644 --- a/modes/basic-dev-mode/src/index.ts +++ b/modes/basic-dev-mode/src/index.ts @@ -127,6 +127,9 @@ function modeFactory({ modeConfiguration }) { }; }, routes: [ + // This example has multiple routes & multiple layouts + // Navigating to /dev/no-panels with this mode registered will render a layout without panels + // navigating to just /dev/ will render the 0th route's layout { path: 'viewer-cs3d', /*init: ({ servicesManager, extensionManager }) => { @@ -157,6 +160,27 @@ function modeFactory({ modeConfiguration }) { }; }, }, + { + path: 'no-panels', + layoutTemplate: ({ location, servicesManager }) => { + return { + id: ohif.layout, + props: { + // TODO: Should be optional, or required to pass empty array for slots? + leftPanels: [ohif.thumbnailList], + rightPanels: [ohif.measurements], + rightPanelClosed: true, + leftPanelClosed: true, + viewports: [ + { + namespace: cs3d.viewport, + displaySetsToDisplay: [ohif.sopClassHandler], + }, + ], + }, + }; + }, + }, ], extensions: extensionDependencies, hangingProtocol: 'default',