Update extension manager to ingest each module type. Add toolbar Manager.

This commit is contained in:
James A. Petts 2020-05-13 11:06:13 +01:00
parent a8340b9b69
commit c42aa37d7f
9 changed files with 186 additions and 85 deletions

View File

@ -23,7 +23,7 @@ export default {
/**
* Only required property. Should be a unique value across all extensions.
*/
id: 'cornerstone',
id: 'org.ohif.cornerstone',
/**
*
@ -39,10 +39,14 @@ export default {
const onNewImageHandler = jumpData => {
commandsManager.runCommand('jumpToImage', jumpData);
};
return <OHIFCornerstoneViewport {...props} onNewImage={onNewImageHandler} />;
return (
<OHIFCornerstoneViewport {...props} onNewImage={onNewImageHandler} />
);
};
return ExtendedOHIFCornerstoneViewport;
return [
{ name: 'cornerstone', component: ExtendedOHIFCornerstoneViewport },
];
},
getToolbarModule() {
return toolbarModule;

View File

@ -15,8 +15,6 @@ import measurementServiceMappingsFactory from './utils/measurementServiceMapping
export default function init({ servicesManager, configuration }) {
const { UIDialogService, MeasurementService } = servicesManager.services;
debugger;
const callInputDialog = (data, event, callback) => {
if (UIDialogService) {
let dialogId = UIDialogService.create({

View File

@ -165,8 +165,6 @@ function createDicomWebApi(dicomWebConfig) {
}
});
debugger;
return imageIds;
},
});

View File

@ -12,12 +12,25 @@ export default function mode({ modeConfiguration }) {
routes: [
{
path: 'viewer',
preInit: ({ toolbarManager }) => {
toolbarManager &&
toolbarManager.setDefaultLoadOut([
[], // Primary
[], // Secondary
]);
init: ({ toolbarManager }) => {
toolbarManager.addButtons([
{
id: 'StackScroll', // If id not given will use default in button definition.
namespace: 'org.ohif.cornerstone.toolbarModule.StackScroll',
},
{
id: 'Zoom',
namespace: 'org.ohif.cornerstone.toolbarModule.Zoom',
},
]);
// Could import layout selector here from org.ohif.default (when it exists!)
toolbarManager.setToolBarLayout([
// Primary
['StackScroll', { label: 'More', subMenu: ['Zoom'] }],
// Secondary
['StackScroll'],
]);
},
layoutTemplate: ({ routeProps }) => {
return {

View File

@ -0,0 +1,55 @@
export default class toolBarManager {
constructor(extensionManager) {
this.toolBarLayout = [];
this.buttons = {};
this.extensionManager = extensionManager;
}
addButtons(buttons) {
buttons.forEach(button => {
const buttonDefinition = this.extensionManager.getModuleEntry(
button.namespace
);
const id = button.id || buttonDefinition.id;
this.buttons[id] = buttonDefinition;
});
console.log(this.buttons);
}
setToolBarLayout(layouts) {
const toolBarLayout = [];
layouts.forEach(layout => {
const toolBarDefinitions = [];
layout.forEach(element => {
if (typeof element === 'object') {
// process submenu.
const subMenuDefinition = { label: element.label, subMenu: [] };
element.subMenu.forEach(subMenuElement => {
const button = this.buttons[subMenuElement];
subMenuDefinition.subMenu.push(button);
});
toolBarDefinitions.push(subMenuDefinition);
} else {
const button = this.buttons[element];
toolBarDefinitions.push(button);
}
});
toolBarLayout.push(toolBarDefinitions);
});
this.toolBarLayout = toolBarLayout;
console.log(this.toolBarLayout);
}
}

View File

@ -17,6 +17,9 @@ export default class ExtensionManager {
this.modules[moduleType] = [];
});
this.dataSourceMap = {};
console.log('modules map');
console.log(this.modulesMap);
}
/**
@ -55,11 +58,9 @@ export default class ExtensionManager {
let extensionId = extension.id;
if (!extensionId) {
extensionId = Math.random()
.toString(36)
.substr(2, 5);
log.warn(`Extension ID not set. Using random string ID: ${extensionId}`);
// Note: Mode framework cannot function without IDs.
log.warn(extension);
throw new Error(`Extension ID not set`);
}
if (this.registeredExtensionIds.includes(extensionId)) {
@ -89,25 +90,43 @@ export default class ExtensionManager {
);
if (extensionModule) {
this._initSpecialModuleTypes(
extensionId,
moduleType,
extensionModule,
dataSources
);
switch (moduleType) {
case MODULE_TYPES.COMMANDS:
this._initCommandsModule(extensionModule);
break;
case MODULE_TYPES.DATA_SOURCE:
this._initDataSourcesModule(
extensionModule,
extensionId,
dataSources
);
break;
case MODULE_TYPES.TOOLBAR:
this._initToolBarModule(extensionModule, extensionId);
break;
case MODULE_TYPES.PANEL:
case MODULE_TYPES.SOP_CLASS_HANDLER:
case MODULE_TYPES.VIEWPORT:
case MODULE_TYPES.CONTEXT:
case MODULE_TYPES.LAYOUT_TEMPLATE:
// Default for most extension points,
// Just adds each entry ready for consumption by mode.
if (!extensionModule.forEach) {
debugger;
}
extensionModule.forEach(element => {
this.modulesMap[
`${extensionId}.${moduleType}.${element.name}`
] = element;
});
}
this.modules[moduleType].push({
extensionId,
module: extensionModule,
});
// TODO -> deal with command modules.
extensionModule.forEach(element => {
this.modulesMap[
`${extensionId}.${moduleType}.${element.name}`
] = element;
});
}
});
@ -124,6 +143,27 @@ export default class ExtensionManager {
return this.dataSourceMap[dataSourceName];
};
_initToolBarModule = (extensionModule, extensionId) => {
let { definitions, defaultContext } = extensionModule;
if (!definitions || Object.keys(definitions).length === 0) {
log.warn('Commands Module contains no command definitions');
return;
}
defaultContext = defaultContext || 'VIEWER';
definitions.forEach(definition => {
console.log(`${extensionId}.${MODULE_TYPES.TOOLBAR}.${definition.id}`);
// TODO -> Deep copy instead of mutation? We only do this once, but would be better.
definition.context = definition.context || defaultContext;
this.modulesMap[
`${extensionId}.${MODULE_TYPES.TOOLBAR}.${definition.id}`
] = definition;
});
};
/**
* @private
* @param {string} moduleType
@ -162,64 +202,52 @@ export default class ExtensionManager {
}
};
_initSpecialModuleTypes = (
extensionId,
moduleType,
extensionModule,
dataSources
) => {
switch (moduleType) {
case 'commandsModule': {
const { definitions, defaultContext } = extensionModule;
if (!definitions || Object.keys(definitions).length === 0) {
log.warn('Commands Module contains no command definitions');
return;
_initDataSourcesModule(extensionModule, extensionId, dataSources) {
extensionModule.forEach(element => {
const namespace = `${extensionId}.${MODULE_TYPES.DATA_SOURCE}.${element.name}`;
dataSources.forEach(dataSource => {
if (dataSource.namespace === namespace) {
const dataSourceInstance = element.createDataSource(
dataSource.configuration
);
if (this.dataSourceMap[dataSource.sourceName]) {
this.dataSourceMap[dataSource.sourceName].push(dataSourceInstance);
} else {
this.dataSourceMap[dataSource.sourceName] = [dataSourceInstance];
}
}
this._initCommandsModule(definitions, defaultContext);
break;
}
case 'dataSourcesModule': {
extensionModule.forEach(element => {
const namespace = `${extensionId}.${moduleType}.${element.name}`;
});
});
dataSources.forEach(dataSource => {
if (dataSource.namespace === namespace) {
const dataSourceInstance = element.createDataSource(
dataSource.configuration
);
if (this.dataSourceMap[dataSource.sourceName]) {
this.dataSourceMap[dataSource.sourceName].push(
dataSourceInstance
);
} else {
this.dataSourceMap[dataSource.sourceName] = [
dataSourceInstance,
];
}
}
});
});
break;
}
default:
// code block
}
};
extensionModule.forEach(element => {
this.modulesMap[
`${extensionId}.${MODULE_TYPES.DATA_SOURCE}.${element.name}`
] = element;
});
}
/**
*
* @private
* @param {Object[]} commandDefinitions
*/
_initCommandsModule = (commandDefinitions, defaultContext = 'VIEWER') => {
_initCommandsModule = extensionModule => {
let { definitions, defaultContext } = extensionModule;
if (!definitions || Object.keys(definitions).length === 0) {
log.warn('Commands Module contains no command definitions');
return;
}
defaultContext = defaultContext || 'VIEWER';
if (!this._commandsManager.getContext(defaultContext)) {
this._commandsManager.createContext(defaultContext);
}
Object.keys(commandDefinitions).forEach(commandName => {
const commandDefinition = commandDefinitions[commandName];
Object.keys(definitions).forEach(commandName => {
const commandDefinition = definitions[commandName];
const commandHasContextThatDoesNotExist =
commandDefinition.context &&
!this._commandsManager.getContext(commandDefinition.context);

View File

@ -20,6 +20,7 @@ import ui from './ui';
import user from './user.js';
import dicomMetadataStore from './dicomMetadataStore';
import displaySetManager from './displaySetManager';
import ToolBarManager from './ToolBarManager';
import utils, { hotkeys } from './utils/';
import {
@ -68,6 +69,7 @@ const OHIF = {
IWebApiDataSource,
dicomMetadataStore,
displaySetManager,
ToolBarManager,
};
export {
@ -105,6 +107,7 @@ export {
IWebApiDataSource,
dicomMetadataStore,
displaySetManager,
ToolBarManager,
};
export { OHIF };

View File

@ -47,8 +47,6 @@ function appInit(appConfigOrFunc, defaultExtensions) {
MeasurementService,
]);
debugger;
/**
* Example: [ext1, ext2, ext3]
* Example2: [[ext1, config], ext2, [ext3, config]]

View File

@ -1,5 +1,5 @@
import React, { useContext, useEffect, useCallback } from 'react';
import { displaySetManager } from '@ohif/core';
import { displaySetManager, ToolBarManager } from '@ohif/core';
import ViewModelContext from './ViewModelContext';
import Compose from './Compose';
@ -9,15 +9,21 @@ export default function ModeRoute({
dataSourceName,
extensionManager,
}) {
const { routes, sopClassHandlers, extensions } = mode;
const { routes, sopClassHandlers, extensions, init } = mode;
const dataSources = extensionManager.getDataSources(dataSourceName);
const toolbarManager = new ToolBarManager(extensionManager);
// TODO: For now assume one unique datasource.
const dataSource = dataSources[0];
const route = routes[0];
route.init({ toolbarManager });
console.log(dataSource);
// Add toolbar state to the view model context?
const { displaySetInstanceUids, setDisplaySetInstanceUids } = useContext(
ViewModelContext
);
@ -42,11 +48,9 @@ export default function ModeRoute({
createDisplaySets();
}, [mode, dataSourceName, location]);
// Deal with toolbar.
// Only handling one route per mode for now
// You can test via http://localhost:3000/example-mode/dicomweb
const layoutTemplateData = routes[0].layoutTemplate({ location });
const layoutTemplateData = route.layoutTemplate({ location });
const layoutTemplateModuleEntry = extensionManager.getModuleEntry(
layoutTemplateData.id
);