feat(CustomizationService):Add a customization service (#2984)
* feat(customization):Add a customization service Update UICustomizationService to v3-stable. Also, adds an example customization for tab colors * moved the customization service to ui category
This commit is contained in:
1 parent
32bb7e4c1e
commit
daa56a8449
33 files changed
+1031
-67
No files matched your search
@@ -40,13 +40,9 @@ export default class ExtensionManager {
|
||||
_extensionLifeCycleHooks,
|
||||
} = this;
|
||||
|
||||
const {
|
||||
MeasurementService,
|
||||
ViewportGridService,
|
||||
} = _servicesManager.services;
|
||||
|
||||
MeasurementService.clearMeasurements();
|
||||
ViewportGridService.reset();
|
||||
for (const service of Object.values(_servicesManager.services)) {
|
||||
service?.onModeEnter?.();
|
||||
}
|
||||
|
||||
registeredExtensionIds.forEach(extensionId => {
|
||||
const onModeEnter = _extensionLifeCycleHooks.onModeEnter[extensionId];
|
||||
@@ -69,13 +65,9 @@ export default class ExtensionManager {
|
||||
_extensionLifeCycleHooks,
|
||||
} = this;
|
||||
|
||||
const {
|
||||
MeasurementService,
|
||||
ViewportGridService,
|
||||
} = _servicesManager.services;
|
||||
|
||||
MeasurementService.clearMeasurements();
|
||||
ViewportGridService.reset();
|
||||
for (const service of Object.values(_servicesManager.services)) {
|
||||
service?.onModeExit?.();
|
||||
}
|
||||
|
||||
registeredExtensionIds.forEach(extensionId => {
|
||||
const onModeExit = _extensionLifeCycleHooks.onModeExit[extensionId];
|
||||
@@ -200,6 +192,7 @@ export default class ExtensionManager {
|
||||
case MODULE_TYPES.SOP_CLASS_HANDLER:
|
||||
case MODULE_TYPES.CONTEXT:
|
||||
case MODULE_TYPES.LAYOUT_TEMPLATE:
|
||||
case MODULE_TYPES.CUSTOMIZATION:
|
||||
case MODULE_TYPES.UTILITY:
|
||||
// Default for most extension points,
|
||||
// Just adds each entry ready for consumption by mode.
|
||||
|
||||
@@ -235,6 +235,9 @@ describe('ExtensionManager.js', () => {
|
||||
getUtilityModule: () => {
|
||||
return [{}];
|
||||
},
|
||||
getCustomizationModule: () => {
|
||||
return [{}];
|
||||
},
|
||||
};
|
||||
|
||||
await extensionManager.registerExtension(extension);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
COMMANDS: 'commandsModule',
|
||||
CUSTOMIZATION: 'customizationModule',
|
||||
DATA_SOURCE: 'dataSourcesModule',
|
||||
PANEL: 'panelModule',
|
||||
SOP_CLASS_HANDLER: 'sopClassHandlerModule',
|
||||
|
||||
@@ -24,6 +24,7 @@ describe('Top level exports', () => {
|
||||
'OHIF',
|
||||
//
|
||||
'CineService',
|
||||
'CustomizationServiceRegistration',
|
||||
'UIDialogService',
|
||||
'UIModalService',
|
||||
'UINotificationService',
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
HangingProtocolService,
|
||||
pubSubServiceInterface,
|
||||
UserAuthenticationService,
|
||||
CustomizationServiceRegistration,
|
||||
} from './services';
|
||||
|
||||
import IWebApiDataSource from './DataSources/IWebApiDataSource';
|
||||
@@ -57,6 +58,7 @@ const OHIF = {
|
||||
viewer: {},
|
||||
//
|
||||
CineService,
|
||||
CustomizationServiceRegistration,
|
||||
UIDialogService,
|
||||
UIModalService,
|
||||
UINotificationService,
|
||||
@@ -92,6 +94,7 @@ export {
|
||||
DICOMWeb,
|
||||
//
|
||||
CineService,
|
||||
CustomizationServiceRegistration,
|
||||
UIDialogService,
|
||||
UIModalService,
|
||||
UINotificationService,
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
import CustomizationService from './CustomizationService';
|
||||
import log from '../../log';
|
||||
|
||||
jest.mock('../../log.js', () => ({
|
||||
info: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
}));
|
||||
|
||||
const extensionManager = {
|
||||
registeredExtensionIds: [],
|
||||
moduleEntries: {},
|
||||
|
||||
getModuleEntry: function (id) {
|
||||
return this.moduleEntries[id];
|
||||
},
|
||||
};
|
||||
|
||||
const commandsManager = {};
|
||||
|
||||
const ohifOverlayItem = {
|
||||
id: 'ohif.overlayItem',
|
||||
content: function (props) {
|
||||
return {
|
||||
label: this.label,
|
||||
value: props[this.attribute],
|
||||
ver: 'default',
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const testItem = {
|
||||
id: 'testItem',
|
||||
customizationType: 'ohif.overlayItem',
|
||||
attribute: 'testAttribute',
|
||||
label: 'testItemLabel',
|
||||
};
|
||||
|
||||
describe('CustomizationService.ts', () => {
|
||||
let customizationService;
|
||||
|
||||
let configuration;
|
||||
|
||||
beforeEach(() => {
|
||||
log.warn.mockClear();
|
||||
jest.clearAllMocks();
|
||||
configuration = {};
|
||||
customizationService = new CustomizationService({
|
||||
configuration,
|
||||
commandsManager,
|
||||
});
|
||||
});
|
||||
|
||||
describe('init', () => {
|
||||
it('init succeeds', () => {
|
||||
customizationService.init(extensionManager);
|
||||
});
|
||||
|
||||
it('configurationRegistered', () => {
|
||||
configuration.testItem = testItem;
|
||||
customizationService.init(extensionManager);
|
||||
expect(customizationService.getGlobalCustomization('testItem')).toBe(
|
||||
testItem
|
||||
);
|
||||
});
|
||||
|
||||
it('defaultRegistered', () => {
|
||||
extensionManager.registeredExtensionIds.push('@testExtension');
|
||||
extensionManager.moduleEntries[
|
||||
'@testExtension.customizationModule.default'
|
||||
] = { name: 'default', value: [testItem] };
|
||||
customizationService.init(extensionManager);
|
||||
expect(customizationService.getGlobalCustomization('testItem')).toBe(
|
||||
testItem
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('customizationType', () => {
|
||||
it('inherits type', () => {
|
||||
extensionManager.registeredExtensionIds.push('@testExtension');
|
||||
extensionManager.moduleEntries[
|
||||
'@testExtension.customizationModule.default'
|
||||
] = { name: 'default', value: [ohifOverlayItem] };
|
||||
configuration.testItem = testItem;
|
||||
customizationService.init(extensionManager);
|
||||
|
||||
const item = customizationService.getGlobalCustomization('testItem');
|
||||
|
||||
const props = { testAttribute: 'testAttrValue' };
|
||||
const result = item.content(props);
|
||||
expect(result.label).toBe(testItem.label);
|
||||
expect(result.value).toBe(props.testAttribute);
|
||||
expect(result.ver).toBe('default');
|
||||
});
|
||||
|
||||
it('inline default inherits type', () => {
|
||||
extensionManager.registeredExtensionIds.push('@testExtension');
|
||||
extensionManager.moduleEntries[
|
||||
'@testExtension.customizationModule.default'
|
||||
] = { name: 'default', value: [ohifOverlayItem] };
|
||||
configuration.testItem = testItem;
|
||||
customizationService.init(extensionManager);
|
||||
|
||||
const item = customizationService.getGlobalCustomization('testItem2', {
|
||||
id: 'testItem2',
|
||||
customizationType: 'ohif.overlayItem',
|
||||
label: 'otherLabel',
|
||||
attribute: 'otherAttr',
|
||||
});
|
||||
|
||||
// Customizes the default value, as this is testItem2
|
||||
const props = { otherAttr: 'other attribute value' };
|
||||
const result = item.content(props);
|
||||
expect(result.label).toBe('otherLabel');
|
||||
expect(result.value).toBe(props.otherAttr);
|
||||
expect(result.ver).toBe('default');
|
||||
});
|
||||
});
|
||||
|
||||
describe('mode customization', () => {
|
||||
it('onModeEnter can add extensions', () => {
|
||||
extensionManager.registeredExtensionIds.push('@testExtension');
|
||||
extensionManager.moduleEntries[
|
||||
'@testExtension.customizationModule.default'
|
||||
] = { name: 'default', value: [ohifOverlayItem] };
|
||||
customizationService.init(extensionManager);
|
||||
|
||||
expect(
|
||||
customizationService.getModeCustomization('testItem')
|
||||
).toBeUndefined();
|
||||
|
||||
customizationService.addModeCustomizations([testItem]);
|
||||
|
||||
expect(
|
||||
customizationService.getGlobalCustomization('testItem')
|
||||
).toBeUndefined();
|
||||
|
||||
const item = customizationService.getModeCustomization('testItem');
|
||||
|
||||
const props = { testAttribute: 'testAttrValue' };
|
||||
const result = item.content(props);
|
||||
expect(result.label).toBe(testItem.label);
|
||||
expect(result.value).toBe(props.testAttribute);
|
||||
expect(result.ver).toBe('default');
|
||||
});
|
||||
|
||||
it('global customizations override modes', () => {
|
||||
extensionManager.registeredExtensionIds.push('@testExtension');
|
||||
extensionManager.moduleEntries[
|
||||
'@testExtension.customizationModule.default'
|
||||
] = { name: 'default', value: [ohifOverlayItem] };
|
||||
configuration.testItem = testItem;
|
||||
customizationService.init(extensionManager);
|
||||
|
||||
// Add a mode customization that would otherwise fail below
|
||||
customizationService.addModeCustomizations([
|
||||
{ ...testItem, label: 'other' },
|
||||
]);
|
||||
|
||||
const item = customizationService.getModeCustomization('testItem');
|
||||
|
||||
const props = { testAttribute: 'testAttrValue' };
|
||||
const result = item.content(props);
|
||||
expect(result.label).toBe(testItem.label);
|
||||
expect(result.value).toBe(props.testAttribute);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,268 @@
|
||||
import merge from 'lodash.merge';
|
||||
import { PubSubService } from '../_shared/pubSubServiceInterface';
|
||||
import { Customization, NestedStrings, Obj } from './types';
|
||||
|
||||
const EVENTS = {
|
||||
MODE_CUSTOMIZATION_MODIFIED: 'event::CustomizationService:modeModified',
|
||||
GLOBAL_CUSTOMIZATION_MODIFIED: 'event::CustomizationService:globalModified',
|
||||
};
|
||||
|
||||
const flattenNestedStrings = (
|
||||
strs: NestedStrings | string,
|
||||
ret?: Record<string, string>
|
||||
): Record<string, string> => {
|
||||
if (!ret) ret = {};
|
||||
if (!strs) return ret;
|
||||
if (Array.isArray(strs)) {
|
||||
for (const val of strs) {
|
||||
flattenNestedStrings(val, ret);
|
||||
}
|
||||
} else {
|
||||
ret[strs] = strs;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* The CustomizationService allows for retrieving of custom components
|
||||
* and configuration for mode and global values.
|
||||
* The intent of the items is to provide a react component. This can be
|
||||
* done by straight out providing an entire react component or else can be
|
||||
* done by configuring a react component, or configuring a part of a react
|
||||
* component. These are intended to be fairly indistinguishable in use of
|
||||
* it, although the internals of how that is implemented may need to know
|
||||
* about the customization service.
|
||||
*
|
||||
* A customization value can be:
|
||||
* 1. React function, taking (React, props) and returning a rendered component
|
||||
* For example, createLogoComponentFn renders a component logo for display
|
||||
* 2. Custom UI component configuration, as defined by the component which uses it.
|
||||
* For example, context menus define a complex structure allowing site-determined
|
||||
* context menus to be set.
|
||||
* 3. A string name, being the extension id for retrieving one of the above.
|
||||
*
|
||||
* The default values for the extension come from the app_config value 'whiteLabeling',
|
||||
* The whiteLabelling can have lists of extensions to load for the default global and
|
||||
* mode extensions. These are:
|
||||
* 'globalExtensions' which is a list of extension id's to load for global values
|
||||
* 'modeExtensions' which is a list of extension id's to load for mode values
|
||||
* They default to the list ['*'] if not otherwise provided, which means to check
|
||||
* every module for the given id and to load it/add it to the extensions.
|
||||
*/
|
||||
export default class CustomizationService extends PubSubService {
|
||||
commandsManager: Record<string, unknown>;
|
||||
extensionManager: Record<string, unknown>;
|
||||
|
||||
modeCustomizations: Record<string, Customization> = {};
|
||||
globalCustomizations: Record<string, Customization> = {};
|
||||
configuration: UICustomizationConfiguration;
|
||||
|
||||
constructor({ configuration, commandsManager }) {
|
||||
super(EVENTS);
|
||||
this.commandsManager = commandsManager;
|
||||
this.configuration = configuration || {};
|
||||
}
|
||||
|
||||
public init(extensionManager: ExtensionManager): void {
|
||||
this.extensionManager = extensionManager;
|
||||
this.initDefaults();
|
||||
this.addReferences(this.configuration);
|
||||
}
|
||||
|
||||
initDefaults(): void {
|
||||
this.extensionManager.registeredExtensionIds.forEach(extensionId => {
|
||||
const key = `${extensionId}.customizationModule.default`;
|
||||
const defaultCustomizations = this.findExtensionValue(key);
|
||||
if (!defaultCustomizations) return;
|
||||
const { value } = defaultCustomizations;
|
||||
this.addReference(value, true);
|
||||
});
|
||||
}
|
||||
|
||||
findExtensionValue(value: string): Obj | void {
|
||||
const entry = this.extensionManager.getModuleEntry(value);
|
||||
return entry;
|
||||
}
|
||||
|
||||
public onModeEnter(): void {
|
||||
super.reset();
|
||||
this.modeCustomizations = {};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} interaction - can be undefined to run nothing
|
||||
* @param {*} extraOptions to include in the commands run
|
||||
*/
|
||||
recordInteraction(
|
||||
interaction: Customization | void,
|
||||
extraOptions?: Record<string, unknown>
|
||||
): void {
|
||||
if (!interaction) return;
|
||||
const commandsManager = this.commandsManager;
|
||||
const { commands = [] } = interaction;
|
||||
|
||||
commands.forEach(({ commandName, commandOptions, context }) => {
|
||||
if (commandName) {
|
||||
console.log('Running command', commandName);
|
||||
commandsManager.runCommand(
|
||||
commandName,
|
||||
{
|
||||
interaction,
|
||||
...commandOptions,
|
||||
...extraOptions,
|
||||
},
|
||||
context
|
||||
);
|
||||
} else {
|
||||
console.warn('No command name supplied in', interaction);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getModeCustomizations(): Record<string, Customization> {
|
||||
return this.modeCustomizations;
|
||||
}
|
||||
|
||||
public setModeCustomization(
|
||||
customizationId: string,
|
||||
customization: Customization
|
||||
): void {
|
||||
console.log('** Set mode customization', customizationId, customization);
|
||||
this.modeCustomizations[customizationId] = merge(
|
||||
this.modeCustomizations[customizationId] || {},
|
||||
customization
|
||||
);
|
||||
this._broadcastEvent(this.EVENTS.CUSTOMIZATION_MODIFIED, {
|
||||
buttons: this.modeCustomizations,
|
||||
button: this.modeCustomizations[customizationId],
|
||||
});
|
||||
}
|
||||
|
||||
/** Mode customizations are changes to the behaviour of the extensions
|
||||
* when running in a given mode. Reset clears mode customizations.
|
||||
* Note that global customizations over-ride mode customizations.
|
||||
* @param defautlValue to return if no customization specified.
|
||||
*/
|
||||
public getModeCustomization(
|
||||
customizationId: string,
|
||||
defaultValue?: Customization
|
||||
): Customization | void {
|
||||
const customization =
|
||||
this.globalCustomizations[customizationId] ??
|
||||
this.modeCustomizations[customizationId] ??
|
||||
defaultValue;
|
||||
return this.applyType(customization);
|
||||
}
|
||||
|
||||
/** Applies any inheritance due to UI Type customization */
|
||||
public applyType(customization: Customization): Customization {
|
||||
if (!customization) return customization;
|
||||
const { customizationType } = customization;
|
||||
if (!customizationType) return customization;
|
||||
const parent = this.getModeCustomization(customizationType);
|
||||
return parent
|
||||
? Object.assign(Object.create(parent), customization)
|
||||
: customization;
|
||||
}
|
||||
|
||||
public addModeCustomizations(modeCustomizations): void {
|
||||
if (!modeCustomizations) {
|
||||
return;
|
||||
}
|
||||
this.addReferences(modeCustomizations, false);
|
||||
|
||||
this._broadcastModeCustomizationModified();
|
||||
}
|
||||
|
||||
_broadcastModeCustomizationModified(): void {
|
||||
this._broadcastEvent(EVENTS.MODE_CUSTOMIZATION_MODIFIED, {
|
||||
modeCustomizations: this.modeCustomizations,
|
||||
globalCustomizations: this.globalCustomizations,
|
||||
});
|
||||
}
|
||||
|
||||
/** Global customizations are those that affect parts of the GUI other than
|
||||
* the modes. They include things like settings for the search screen.
|
||||
* Reset does NOT clear global customizations.
|
||||
*/
|
||||
getGlobalCustomization(
|
||||
id: string,
|
||||
defaultValue?: Customization
|
||||
): Customization | void {
|
||||
return this.applyType(this.globalCustomizations[id] ?? defaultValue);
|
||||
}
|
||||
|
||||
setGlobalCustomization(id: string, value: Customization): void {
|
||||
console.log('*** Set global', id, value);
|
||||
this.globalCustomizations[id] = value;
|
||||
this._broadcastGlobalCustomizationModified();
|
||||
}
|
||||
|
||||
protected setConfigGlobalCustomization(
|
||||
configuration: AppConfigCustomization
|
||||
): void {
|
||||
this.globalCustomizations = {};
|
||||
const keys = flattenNestedStrings(configuration.globalCustomizations);
|
||||
this.readCustomizationTypes(
|
||||
v => keys[v.name] && v.customization,
|
||||
this.globalCustomizations
|
||||
);
|
||||
|
||||
// TODO - iterate over customizations, loading them from the extension
|
||||
// manager.
|
||||
this._broadcastGlobalCustomizationModified();
|
||||
}
|
||||
|
||||
_broadcastGlobalCustomizationModified(): void {
|
||||
this._broadcastEvent(EVENTS.GLOBAL_CUSTOMIZATION_MODIFIED, {
|
||||
modeCustomizations: this.modeCustomizations,
|
||||
globalCustomizations: this.globalCustomizations,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A single reference is either an an array, or a single customization value,
|
||||
* whose id is the id in the object, or the parent id.
|
||||
* This allows for general use to register customizationModule entries.
|
||||
*/
|
||||
addReference(
|
||||
value?: Obj | Obj[] | string,
|
||||
isGlobal = true,
|
||||
id?: string
|
||||
): void {
|
||||
if (!value) return;
|
||||
if (typeof value === 'string') {
|
||||
const extensionValue = this.findExtensionValue(value);
|
||||
console.log('Adding extension values', value, extensionValue);
|
||||
this.addReferences(extensionValue);
|
||||
} else if (Array.isArray(value)) {
|
||||
this.addReferences(value, isGlobal);
|
||||
} else {
|
||||
const useId = value.id || id;
|
||||
this[isGlobal ? 'setGlobalCustomization' : 'setModeCustomization'](
|
||||
useId as string,
|
||||
value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** References are:
|
||||
* list of customizations, added in order
|
||||
* object containing a customization id and value
|
||||
* This format allows for the original whitelist format.
|
||||
*/
|
||||
addReferences(references?: Obj | Obj[], isGlobal = true): void {
|
||||
if (!references) return;
|
||||
if (Array.isArray(references)) {
|
||||
references.forEach(item => {
|
||||
this.addReference(item, isGlobal);
|
||||
});
|
||||
} else {
|
||||
for (const key of Object.keys(references)) {
|
||||
const value = references[key];
|
||||
this.addReference(value, isGlobal, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import CustomizationService from './CustomizationService';
|
||||
|
||||
const CustomizationServiceRegistration = {
|
||||
name: 'customizationService',
|
||||
create: ({ configuration = {}, commandsManager }) => {
|
||||
return new CustomizationService({ configuration, commandsManager });
|
||||
},
|
||||
};
|
||||
|
||||
export default CustomizationServiceRegistration;
|
||||
export { CustomizationService, CustomizationServiceRegistration };
|
||||
@@ -0,0 +1,39 @@
|
||||
import Command from '../../types/Command';
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
export type Obj = Record<string, unknown>;
|
||||
|
||||
export interface BaseCustomization extends Obj {
|
||||
id: string;
|
||||
customizationType?: string;
|
||||
description?: string;
|
||||
label?: string;
|
||||
commands?: Command[];
|
||||
}
|
||||
|
||||
export interface LabelCustomization extends BaseCustomization {
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface CodeCustomization extends BaseCustomization {
|
||||
code: string;
|
||||
}
|
||||
|
||||
export interface CommandCustomization extends BaseCustomization {
|
||||
commands: Command[];
|
||||
}
|
||||
|
||||
export type Customization =
|
||||
| BaseCustomization
|
||||
| LabelCustomization
|
||||
| CommandCustomization
|
||||
| CodeCustomization;
|
||||
|
||||
export default Customization;
|
||||
|
||||
export type ComponentReturn = {
|
||||
component: ComponentType;
|
||||
props?: Obj;
|
||||
};
|
||||
|
||||
export type NestedStrings = string[] | NestedStrings[];
|
||||
+22
-3
@@ -52,11 +52,19 @@ export default class ToolBarService {
|
||||
this.buttons = {};
|
||||
}
|
||||
|
||||
onModeEnter() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} interaction
|
||||
* @param {*} interaction - can be undefined to run nothing
|
||||
* @param {*} options is an optional set of extra commandOptions
|
||||
* used for calling the specified interaction. That is, the command is
|
||||
* called with {...commandOptions,...options}
|
||||
*/
|
||||
recordInteraction(interaction) {
|
||||
recordInteraction(interaction, options) {
|
||||
if (!interaction) return;
|
||||
const commandsManager = this._commandsManager;
|
||||
const { groupId, itemId, interactionType, commands } = interaction;
|
||||
|
||||
@@ -64,7 +72,14 @@ export default class ToolBarService {
|
||||
case 'action': {
|
||||
commands.forEach(({ commandName, commandOptions, context }) => {
|
||||
if (commandName) {
|
||||
commandsManager.runCommand(commandName, commandOptions, context);
|
||||
commandsManager.runCommand(
|
||||
commandName,
|
||||
{
|
||||
...commandOptions,
|
||||
...options,
|
||||
},
|
||||
context
|
||||
);
|
||||
}
|
||||
});
|
||||
break;
|
||||
@@ -171,6 +186,10 @@ export default class ToolBarService {
|
||||
}
|
||||
}
|
||||
|
||||
getButton(id) {
|
||||
return this.buttons[id];
|
||||
}
|
||||
|
||||
setButtons(buttons) {
|
||||
this.buttons = buttons;
|
||||
this._broadcastEvent(this.EVENTS.TOOL_BAR_MODIFIED, {
|
||||
File renamed without changes.
@@ -24,6 +24,7 @@ class ViewportGridService {
|
||||
restoreCachedLayout: restoreCachedLayoutImplementation,
|
||||
setLayout: setLayoutImplementation,
|
||||
reset: resetImplementation,
|
||||
onModeExit: onModeExitImplementation,
|
||||
set: setImplementation,
|
||||
}) {
|
||||
if (getStateImplementation) {
|
||||
@@ -50,6 +51,9 @@ class ViewportGridService {
|
||||
if (restoreCachedLayoutImplementation) {
|
||||
this.serviceImplementation._restoreCachedLayout = restoreCachedLayoutImplementation;
|
||||
}
|
||||
if (onModeExitImplementation) {
|
||||
this.serviceImplementation._onModeExit = onModeExitImplementation;
|
||||
}
|
||||
if (setImplementation) {
|
||||
this.serviceImplementation._set = setImplementation;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import guid from '../../utils/guid';
|
||||
import * as Types from '../../Types';
|
||||
|
||||
/**
|
||||
* Consumer must implement:
|
||||
@@ -24,7 +25,7 @@ function subscribe(eventName, callback) {
|
||||
const listenerId = guid();
|
||||
const subscription = { id: listenerId, callback };
|
||||
|
||||
console.info(`Subscribing to '${eventName}'.`);
|
||||
// console.info(`Subscribing to '${eventName}'.`);
|
||||
if (Array.isArray(this.listeners[eventName])) {
|
||||
this.listeners[eventName].push(subscription);
|
||||
} else {
|
||||
@@ -86,3 +87,21 @@ function _broadcastEvent(eventName, callbackProps) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Export a PubSubService class to be used instead of the individual items */
|
||||
export class PubSubService {
|
||||
constructor(EVENTS) {
|
||||
this.EVENTS = EVENTS;
|
||||
this.subscribe = subscribe;
|
||||
this._broadcastEvent = _broadcastEvent;
|
||||
this._unsubscribe = _unsubscribe;
|
||||
this._isValidEvent = _isValidEvent;
|
||||
this.listeners = {};
|
||||
this.unsubscriptions = [];
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.unsubscriptions.forEach(unsub => unsub());
|
||||
this.unsubscriptions = [];
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,20 @@ import ToolBarService from './ToolBarService';
|
||||
import ViewportGridService from './ViewportGridService';
|
||||
import CineService from './CineService';
|
||||
import HangingProtocolService from './HangingProtocolService';
|
||||
import pubSubServiceInterface from './_shared/pubSubServiceInterface';
|
||||
import pubSubServiceInterface, {
|
||||
PubSubService,
|
||||
} from './_shared/pubSubServiceInterface';
|
||||
import UserAuthenticationService from './UserAuthenticationService';
|
||||
import {
|
||||
CustomizationService,
|
||||
CustomizationServiceRegistration,
|
||||
} from './CustomizationService';
|
||||
|
||||
export {
|
||||
MeasurementService,
|
||||
ServicesManager,
|
||||
CustomizationService,
|
||||
CustomizationServiceRegistration,
|
||||
UIDialogService,
|
||||
UIModalService,
|
||||
UINotificationService,
|
||||
@@ -27,5 +35,6 @@ export {
|
||||
HangingProtocolService,
|
||||
CineService,
|
||||
pubSubServiceInterface,
|
||||
PubSubService,
|
||||
UserAuthenticationService,
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface Command {
|
||||
commandName: string;
|
||||
commandOptions?: Record<string, unknown>;
|
||||
context?: string;
|
||||
}
|
||||
|
||||
export default Command;
|
||||
@@ -5,13 +5,21 @@ import {
|
||||
} from './StudyMetadata';
|
||||
|
||||
import Consumer from './Consumer';
|
||||
|
||||
import { ExtensionManager } from '../extensions';
|
||||
import { CustomizationService, PubSubService } from '../services';
|
||||
import * as HangingProtocol from './HangingProtocol';
|
||||
import Command from './Command';
|
||||
|
||||
export * from '../services/CustomizationService/types';
|
||||
|
||||
export type {
|
||||
ExtensionManager,
|
||||
HangingProtocol,
|
||||
StudyMetadata,
|
||||
SeriesMetadata,
|
||||
InstanceMetadata,
|
||||
Consumer,
|
||||
PubSubService,
|
||||
CustomizationService,
|
||||
Command,
|
||||
};
|
||||
Reference in new issue
Block a user