fix: Service consistency typing (#3309)

* Use more consistent type/structure for services

* Fix restoring SR so the tests described work

* One more typed service

* Added types for the Cornerstone library

* Couple more type fixes
This commit is contained in:
Bill Wallace 2023-04-18 11:17:50 -04:00 committed by GitHub
parent 47c25f4364
commit 6cf271d006
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 309 additions and 299 deletions

View File

@ -30,17 +30,6 @@ const extension = {
*/ */
id, id,
/**
* Perform any pre-registration tasks here. This is called before the extension
* is registered. Usually we run tasks such as: configuring the libraries
* (e.g. cornerstone, cornerstoneTools, ...) or registering any services that
* this extension is providing.
*/
preRegistration: ({
servicesManager,
commandsManager,
configuration = {},
}) => {},
/** /**
* PanelModule should provide a list of panels that will be available in OHIF * PanelModule should provide a list of panels that will be available in OHIF
* for Modes to consume and render. Each panel is defined by a {name, * for Modes to consume and render. Each panel is defined by a {name,

View File

@ -32,8 +32,8 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests" "test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
}, },
"peerDependencies": { "peerDependencies": {
"@ohif/core": "^3.0.0", "@ohif/core": ">=3.0.0",
"@ohif/extension-cornerstone": "^3.0.0", "@ohif/extension-cornerstone": ">=3.0.0",
"@ohif/extension-measurement-tracking": "^3.0.0", "@ohif/extension-measurement-tracking": "^3.0.0",
"@ohif/ui": "^2.0.0", "@ohif/ui": "^2.0.0",
"dcmjs": "^0.29.5", "dcmjs": "^0.29.5",

View File

@ -2,6 +2,8 @@ import PropTypes from 'prop-types';
import React, { useCallback, useContext, useEffect, useState } from 'react'; import React, { useCallback, useContext, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import OHIF, { utils, ServicesManager, ExtensionManager } from '@ohif/core'; import OHIF, { utils, ServicesManager, ExtensionManager } from '@ohif/core';
import { CornerstoneServices } from '@ohif/extension-cornerstone/types';
import { setTrackingUniqueIdentifiersForElement } from '../tools/modules/dicomSRModule'; import { setTrackingUniqueIdentifiersForElement } from '../tools/modules/dicomSRModule';
import { import {
@ -37,7 +39,7 @@ function OHIFCornerstoneSRViewport(props) {
displaySetService, displaySetService,
cornerstoneViewportService, cornerstoneViewportService,
measurementService, measurementService,
} = servicesManager.services; } = servicesManager.services as CornerstoneServices;
// SR viewport will always have a single display set // SR viewport will always have a single display set
if (displaySets.length > 1) { if (displaySets.length > 1) {
@ -91,10 +93,12 @@ function OHIFCornerstoneSRViewport(props) {
SeriesInstanceUIDs[0] SeriesInstanceUIDs[0]
); );
if (displaySets.length) { if (displaySets.length) {
viewportGridService.setDisplaySetsForViewport({ viewportGridService.setDisplaySetsForViewports([
viewportIndex: activeViewportIndex, {
displaySetInstanceUIDs: [displaySets[0].displaySetInstanceUID], viewportIndex: activeViewportIndex,
}); displaySetInstanceUIDs: [displaySets[0].displaySetInstanceUID],
},
]);
} }
}; };
} }
@ -422,6 +426,7 @@ OHIFCornerstoneSRViewport.propTypes = {
children: PropTypes.node, children: PropTypes.node,
customProps: PropTypes.object, customProps: PropTypes.object,
viewportOptions: PropTypes.object, viewportOptions: PropTypes.object,
viewportLabel: PropTypes.string,
servicesManager: PropTypes.instanceOf(ServicesManager).isRequired, servicesManager: PropTypes.instanceOf(ServicesManager).isRequired,
extensionManager: PropTypes.instanceOf(ExtensionManager).isRequired, extensionManager: PropTypes.instanceOf(ExtensionManager).isRequired,
}; };

View File

@ -7,6 +7,11 @@
"repository": "OHIF/Viewers", "repository": "OHIF/Viewers",
"main": "dist/index.umd.js", "main": "dist/index.umd.js",
"module": "src/index.tsx", "module": "src/index.tsx",
"types": "src/types/index.ts",
"exports": {
".": "./src/index.tsx",
"./types": "./src/types/index.ts"
},
"engines": { "engines": {
"node": ">=10", "node": ">=10",
"npm": ">=6", "npm": ">=6",

View File

@ -10,17 +10,18 @@ import {
utilities as csUtils, utilities as csUtils,
CONSTANTS, CONSTANTS,
} from '@cornerstonejs/core'; } from '@cornerstonejs/core';
import { CinePlayer, useCine, useViewportGrid } from '@ohif/ui';
import {
IStackViewport,
IVolumeViewport,
} from '@cornerstonejs/core/dist/esm/types';
import { setEnabledElement } from '../state'; import { setEnabledElement } from '../state';
import './OHIFCornerstoneViewport.css'; import './OHIFCornerstoneViewport.css';
import CornerstoneOverlays from './Overlays/CornerstoneOverlays'; import CornerstoneOverlays from './Overlays/CornerstoneOverlays';
import {
IStackViewport,
IVolumeViewport,
} from '@cornerstonejs/core/dist/esm/types';
import getSOPInstanceAttributes from '../utils/measurementServiceMappings/utils/getSOPInstanceAttributes'; import getSOPInstanceAttributes from '../utils/measurementServiceMappings/utils/getSOPInstanceAttributes';
import { CinePlayer, useCine, useViewportGrid } from '@ohif/ui'; import CornerstoneServices from '../types/CornerstoneServices';
const STACK = 'stack'; const STACK = 'stack';
@ -129,7 +130,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
cornerstoneCacheService, cornerstoneCacheService,
viewportGridService, viewportGridService,
stateSyncService, stateSyncService,
} = servicesManager.services; } = servicesManager.services as CornerstoneServices;
const cineHandler = () => { const cineHandler = () => {
if (!cines || !cines[viewportIndex] || !enabledVPElement) { if (!cines || !cines[viewportIndex] || !enabledVPElement) {

View File

@ -7,7 +7,7 @@ import {
imageRetrievalPoolManager, imageRetrievalPoolManager,
} from '@cornerstonejs/core'; } from '@cornerstonejs/core';
import { Enums as cs3DToolsEnums } from '@cornerstonejs/tools'; import { Enums as cs3DToolsEnums } from '@cornerstonejs/tools';
import { Types } from '@ohif/core'; import { ServicesManager, Types } from '@ohif/core';
import init from './init'; import init from './init';
import getCommandsModule from './commandsModule'; import getCommandsModule from './commandsModule';
@ -16,10 +16,11 @@ import ToolGroupService from './services/ToolGroupService';
import SyncGroupService from './services/SyncGroupService'; import SyncGroupService from './services/SyncGroupService';
import SegmentationService from './services/SegmentationService'; import SegmentationService from './services/SegmentationService';
import CornerstoneCacheService from './services/CornerstoneCacheService'; import CornerstoneCacheService from './services/CornerstoneCacheService';
import CornerstoneViewportService from './services/ViewportService/CornerstoneViewportService';
import * as CornerstoneExtensionTypes from './types';
import { toolNames } from './initCornerstoneTools'; import { toolNames } from './initCornerstoneTools';
import { getEnabledElement, reset as enabledElementReset } from './state'; import { getEnabledElement, reset as enabledElementReset } from './state';
import CornerstoneViewportService from './services/ViewportService/CornerstoneViewportService';
import dicomLoaderService from './utils/dicomLoaderService'; import dicomLoaderService from './utils/dicomLoaderService';
import { registerColormap } from './utils/colormap/transferFunctionHelpers'; import { registerColormap } from './utils/colormap/transferFunctionHelpers';
@ -72,18 +73,11 @@ const cornerstoneExtension: Types.Extensions.Extension = {
props: Types.Extensions.ExtensionParams props: Types.Extensions.ExtensionParams
): Promise<void> { ): Promise<void> {
const { servicesManager } = props; const { servicesManager } = props;
// Todo: we should be consistent with how services get registered. Use REGISTRATION static method for all servicesManager.registerService(CornerstoneViewportService.REGISTRATION);
servicesManager.registerService( servicesManager.registerService(ToolGroupService.REGISTRATION);
CornerstoneViewportService(servicesManager) servicesManager.registerService(SyncGroupService.REGISTRATION);
); servicesManager.registerService(SegmentationService.REGISTRATION);
servicesManager.registerService( servicesManager.registerService(CornerstoneCacheService.REGISTRATION);
ToolGroupService.REGISTRATION(servicesManager)
);
servicesManager.registerService(SyncGroupService(servicesManager));
servicesManager.registerService(SegmentationService(servicesManager));
servicesManager.registerService(
CornerstoneCacheService.REGISTRATION(servicesManager)
);
return init.call(this, props); return init.call(this, props);
}, },
@ -145,5 +139,5 @@ const cornerstoneExtension: Types.Extensions.Extension = {
}; };
export type { PublicViewportOptions }; export type { PublicViewportOptions };
export { measurementMappingUtils }; export { measurementMappingUtils, CornerstoneExtensionTypes };
export default cornerstoneExtension; export default cornerstoneExtension;

View File

@ -1,4 +1,4 @@
import { ServicesManager } from '@ohif/core'; import { ServicesManager, Types } from '@ohif/core';
import { cache as cs3DCache, Enums, volumeLoader } from '@cornerstonejs/core'; import { cache as cs3DCache, Enums, volumeLoader } from '@cornerstonejs/core';
import getCornerstoneViewportType from '../../utils/getCornerstoneViewportType'; import getCornerstoneViewportType from '../../utils/getCornerstoneViewportType';
@ -10,20 +10,21 @@ import {
const VOLUME_LOADER_SCHEME = 'cornerstoneStreamingImageVolume'; const VOLUME_LOADER_SCHEME = 'cornerstoneStreamingImageVolume';
class CornerstoneCacheService { class CornerstoneCacheService {
static REGISTRATION = (serviceManager: ServicesManager) => { static REGISTRATION = {
return { name: 'cornerstoneCacheService',
name: 'cornerstoneCacheService', altName: 'CornerstoneCacheService',
altName: 'CornerstoneCacheService', create: ({
create: ({ configuration = {} }) => { servicesManager,
return new CornerstoneCacheService(serviceManager); }: Types.Extensions.ExtensionParams): CornerstoneCacheService => {
}, return new CornerstoneCacheService(servicesManager);
}; },
}; };
stackImageIds: Map<string, string[]> = new Map(); stackImageIds: Map<string, string[]> = new Map();
volumeImageIds: Map<string, string[]> = new Map(); volumeImageIds: Map<string, string[]> = new Map();
readonly servicesManager: ServicesManager;
constructor(servicesManager) { constructor(servicesManager: ServicesManager) {
this.servicesManager = servicesManager; this.servicesManager = servicesManager;
} }

View File

@ -1,5 +1,6 @@
import cloneDeep from 'lodash.clonedeep'; import cloneDeep from 'lodash.clonedeep';
import { Types as OhifTypes, ServicesManager, PubSubService } from '@ohif/core';
import { import {
cache, cache,
eventTarget, eventTarget,
@ -16,7 +17,6 @@ import {
Types as cstTypes, Types as cstTypes,
utilities as cstUtils, utilities as cstUtils,
} from '@cornerstonejs/tools'; } from '@cornerstonejs/tools';
import { pubSubServiceInterface } from '@ohif/core';
import isEqual from 'lodash.isequal'; import isEqual from 'lodash.isequal';
import { easeInOutBell } from '../../utils/transitions'; import { easeInOutBell } from '../../utils/transitions';
import { import {
@ -47,19 +47,26 @@ const EVENTS = {
const VALUE_TYPES = {}; const VALUE_TYPES = {};
class SegmentationService { class SegmentationService extends PubSubService {
listeners = {}; static REGISTRATION = {
name: 'segmentationService',
altName: 'SegmentationService',
create: ({
servicesManager,
}: OhifTypes.Extensions.ExtensionParams): SegmentationService => {
return new SegmentationService({ servicesManager });
},
};
segmentations: Record<string, Segmentation>; segmentations: Record<string, Segmentation>;
servicesManager = null; readonly servicesManager: ServicesManager;
highlightIntervalId = null; highlightIntervalId = null;
_broadcastEvent: (eventName: string, callbackProps: any) => void;
readonly EVENTS = EVENTS; readonly EVENTS = EVENTS;
constructor({ servicesManager }) { constructor({ servicesManager }) {
super(EVENTS);
this.segmentations = {}; this.segmentations = {};
this.listeners = {};
Object.assign(this, pubSubServiceInterface);
this.servicesManager = servicesManager; this.servicesManager = servicesManager;
this._initSegmentationService(); this._initSegmentationService();

View File

@ -1,11 +1,3 @@
import SegmentationService from './SegmentationService'; import SegmentationService from './SegmentationService';
export default function ExtendedSegmentationService(servicesManager) { export default SegmentationService;
return {
name: 'segmentationService',
altName: 'SegmentationService',
create: ({ configuration = {} }) => {
return new SegmentationService({ servicesManager });
},
};
}

View File

@ -4,7 +4,7 @@ import {
Synchronizer, Synchronizer,
} from '@cornerstonejs/tools'; } from '@cornerstonejs/tools';
import { pubSubServiceInterface } from '@ohif/core'; import { pubSubServiceInterface, Types, ServicesManager } from '@ohif/core';
const EVENTS = { const EVENTS = {
TOOL_GROUP_CREATED: 'event::cornerstone::syncgroupservice:toolgroupcreated', TOOL_GROUP_CREATED: 'event::cornerstone::syncgroupservice:toolgroupcreated',
@ -37,7 +37,17 @@ const asSyncGroup = (syncGroup: string | SyncGroup): SyncGroup =>
typeof syncGroup === 'string' ? { type: syncGroup } : syncGroup; typeof syncGroup === 'string' ? { type: syncGroup } : syncGroup;
export default class SyncGroupService { export default class SyncGroupService {
serviceManager: any; static REGISTRATION = {
name: 'syncGroupService',
altName: 'SyncGroupService',
create: ({
servicesManager,
}: Types.Extensions.ExtensionParams): SyncGroupService => {
return new SyncGroupService(servicesManager);
},
};
servicesManager: ServicesManager;
listeners: { [key: string]: (...args: any[]) => void } = {}; listeners: { [key: string]: (...args: any[]) => void } = {};
EVENTS: { [key: string]: string }; EVENTS: { [key: string]: string };
synchronizerCreators: Record<string, SyncCreator> = { synchronizerCreators: Record<string, SyncCreator> = {
@ -47,8 +57,8 @@ export default class SyncGroupService {
[STACKIMAGE]: synchronizers.createStackImageSynchronizer, [STACKIMAGE]: synchronizers.createStackImageSynchronizer,
}; };
constructor(serviceManager) { constructor(serviceManager: ServicesManager) {
this.serviceManager = serviceManager; this.servicesManager = serviceManager;
this.listeners = {}; this.listeners = {};
this.EVENTS = EVENTS; this.EVENTS = EVENTS;
// //

View File

@ -1,11 +1,3 @@
import SyncGroupService from './SyncGroupService'; import SyncGroupService from './SyncGroupService';
export default function ExtendedSyncGroupService(serviceManager) { export default SyncGroupService;
return {
altName: 'SyncGroupService',
name: 'syncGroupService',
create: ({ configuration = {} }) => {
return new SyncGroupService(serviceManager);
},
};
}

View File

@ -1,6 +1,6 @@
import { ToolGroupManager, Enums, Types } from '@cornerstonejs/tools'; import { ToolGroupManager, Enums, Types } from '@cornerstonejs/tools';
import { pubSubServiceInterface } from '@ohif/core'; import { Types as OhifTypes, pubSubServiceInterface } from '@ohif/core';
const EVENTS = { const EVENTS = {
VIEWPORT_ADDED: 'event::cornerstone::toolgroupservice:viewportadded', VIEWPORT_ADDED: 'event::cornerstone::toolgroupservice:viewportadded',
@ -20,14 +20,14 @@ type Tools = {
}; };
export default class ToolGroupService { export default class ToolGroupService {
public static REGISTRATION = serviceManager => { public static REGISTRATION = {
return { name: 'toolGroupService',
name: 'toolGroupService', altName: 'ToolGroupService',
altName: 'ToolGroupService', create: ({
create: ({ configuration = {} }) => { servicesManager,
return new ToolGroupService(serviceManager); }: OhifTypes.Extensions.ExtensionParams): ToolGroupService => {
}, return new ToolGroupService(servicesManager);
}; },
}; };
serviceManager: any; serviceManager: any;

View File

@ -1,4 +1,5 @@
import { PubSubService } from '@ohif/core'; import { PubSubService, ServicesManager } from '@ohif/core';
import * as OhifTypes from '@ohif/core/types';
import { import {
RenderingEngine, RenderingEngine,
StackViewport, StackViewport,
@ -43,6 +44,16 @@ const EVENTS = {
*/ */
class CornerstoneViewportService extends PubSubService class CornerstoneViewportService extends PubSubService
implements IViewportService { implements IViewportService {
static REGISTRATION = {
name: 'cornerstoneViewportService',
altName: 'CornerstoneViewportService',
create: ({
servicesManager,
}: OhifTypes.Extensions.ExtensionParams): CornerstoneViewportService => {
return new CornerstoneViewportService(servicesManager);
},
};
renderingEngine: Types.IRenderingEngine | null; renderingEngine: Types.IRenderingEngine | null;
viewportsInfo: Map<number, ViewportInfo> = new Map(); viewportsInfo: Map<number, ViewportInfo> = new Map();
viewportsById: Map<string, ViewportInfo> = new Map(); viewportsById: Map<string, ViewportInfo> = new Map();
@ -55,7 +66,7 @@ class CornerstoneViewportService extends PubSubService
resizeRefreshMode: 'debounce'; resizeRefreshMode: 'debounce';
servicesManager = null; servicesManager = null;
constructor(servicesManager) { constructor(servicesManager: ServicesManager) {
super(EVENTS); super(EVENTS);
this.renderingEngine = null; this.renderingEngine = null;
this.viewportGridResizeObserver = null; this.viewportGridResizeObserver = null;
@ -71,7 +82,7 @@ class CornerstoneViewportService extends PubSubService
viewportIndex: number, viewportIndex: number,
viewportOptions: PublicViewportOptions, viewportOptions: PublicViewportOptions,
elementRef: HTMLDivElement elementRef: HTMLDivElement
) { ): void {
// Use the provided viewportId // Use the provided viewportId
// Not providing a viewportId is frowned upon because it does weird things // Not providing a viewportId is frowned upon because it does weird things
// on moving them around, but it does mostly work. // on moving them around, but it does mostly work.
@ -855,14 +866,4 @@ class CornerstoneViewportService extends PubSubService
} }
} }
export default function CornerstoneViewportServiceRegistration(serviceManager) { export default CornerstoneViewportService;
return {
name: 'cornerstoneViewportService',
altName: 'CornerstoneViewportService',
create: ({ configuration = {} }) => {
return new CornerstoneViewportService(serviceManager);
},
};
}
export { CornerstoneViewportService, CornerstoneViewportServiceRegistration };

View File

@ -0,0 +1,16 @@
import { Types } from '@ohif/core';
import ToolGroupService from './services/ToolGroupService';
import SyncGroupService from './services/SyncGroupService';
import SegmentationService from './services/SegmentationService';
import CornerstoneCacheService from './services/CornerstoneCacheService';
import CornerstoneViewportService from './services/ViewportService/CornerstoneViewportService';
interface CornerstoneServices extends Types.Services {
cornerstoneViewportService: CornerstoneViewportService;
toolGroupService: ToolGroupService;
syncGroupService: SyncGroupService;
segmentationService: SegmentationService;
cornerstoneCacheService: CornerstoneCacheService;
}
export default CornerstoneServices;

View File

@ -1,5 +1,7 @@
import * as CornerstoneCacheService from './CornerstoneCacheService' import * as CornerstoneCacheService from './CornerstoneCacheService'
import CornerstoneServices from './CornerstoneServices';
export type { export type {
CornerstoneCacheService CornerstoneCacheService,
CornerstoneServices,
} }

View File

@ -1,5 +1,5 @@
import ContextMenuController from './ContextMenuController'; import ContextMenuController from './ContextMenuController';
import ContextMenuItemsBuilder from './ContextMenuItemsBuilder'; import * as ContextMenuItemsBuilder from './ContextMenuItemsBuilder';
import defaultContextMenu from './defaultContextMenu'; import defaultContextMenu from './defaultContextMenu';
import * as CustomizeableContextMenuTypes from './types'; import * as CustomizeableContextMenuTypes from './types';

View File

@ -7,6 +7,7 @@
"repository": "OHIF/Viewers", "repository": "OHIF/Viewers",
"main": "dist/index.umd.js", "main": "dist/index.umd.js",
"module": "src/index.ts", "module": "src/index.ts",
"types": "src/types/index.ts",
"sideEffects": "false", "sideEffects": "false",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@ -37,9 +37,12 @@ export interface ExtensionParams extends ExtensionConstructor {
export interface Extension { export interface Extension {
id: string; id: string;
preRegistration?: (p: ExtensionParams) => Promise<void> | void; preRegistration?: (p: ExtensionParams) => Promise<void> | void;
onModeExit?: () => void;
getHangingProtocolModule?: (p: ExtensionParams) => unknown; getHangingProtocolModule?: (p: ExtensionParams) => unknown;
getCommandsModule?: (p: ExtensionParams) => CommandsModule; getCommandsModule?: (p: ExtensionParams) => CommandsModule;
getViewportModule?: (p: ExtensionParams) => unknown;
getUtilityModule?: (p: ExtensionParams) => unknown;
onModeEnter?: () => void;
onModeExit?: () => void;
} }
export type ExtensionRegister = { export type ExtensionRegister = {

View File

@ -1,91 +0,0 @@
/**
* UI Modal
*
* @typedef {Object} ModalProps
* @property {ReactElement|HTMLElement} [content=null] Modal content.
* @property {Object} [contentProps=null] Modal content props.
* @property {boolean} [shouldCloseOnEsc=false] Modal is dismissible via the esc key.
* @property {boolean} [isOpen=true] Make the Modal visible or hidden.
* @property {boolean} [closeButton=true] Should the modal body render the close button.
* @property {string} [title=null] Should the modal render the title independently of the body content.
* @property {string} [customClassName=null] The custom class to style the modal.
*/
const name = 'uiModalService';
const publicAPI = {
name,
hide: _hide,
show: _show,
setServiceImplementation,
};
const serviceImplementation = {
_hide: () => console.warn('hide() NOT IMPLEMENTED'),
_show: () => console.warn('show() NOT IMPLEMENTED'),
};
/**
* Show a new UI modal;
*
* @param {ModalProps} props { content, contentProps, shouldCloseOnEsc, isOpen, closeButton, title, customClassName }
*/
function _show({
content = null,
contentProps = null,
shouldCloseOnEsc = true,
isOpen = true,
closeButton = true,
title = null,
customClassName = null,
}) {
return serviceImplementation._show({
content,
contentProps,
shouldCloseOnEsc,
isOpen,
closeButton,
title,
customClassName,
});
}
/**
* Hides/dismisses the modal, if currently shown
*
* @returns void
*/
function _hide() {
return serviceImplementation._hide();
}
/**
*
*
* @param {*} {
* hide: hideImplementation,
* show: showImplementation,
* }
*/
function setServiceImplementation({
hide: hideImplementation,
show: showImplementation,
}) {
if (hideImplementation) {
serviceImplementation._hide = hideImplementation;
}
if (showImplementation) {
serviceImplementation._show = showImplementation;
}
}
// TODO - export TS Type
export default {
REGISTRATION: {
name,
altName: 'UIModalService',
create: ({ configuration = {} }) => {
return publicAPI;
},
},
};

View File

@ -0,0 +1,87 @@
/**
* UI Modal
*
* @typedef {Object} ModalProps
* @property {ReactElement|HTMLElement} [content=null] Modal content.
* @property {Object} [contentProps=null] Modal content props.
* @property {boolean} [shouldCloseOnEsc=false] Modal is dismissible via the esc key.
* @property {boolean} [isOpen=true] Make the Modal visible or hidden.
* @property {boolean} [closeButton=true] Should the modal body render the close button.
* @property {string} [title=null] Should the modal render the title independently of the body content.
* @property {string} [customClassName=null] The custom class to style the modal.
*/
const name = 'uiModalService';
const serviceImplementation = {
_hide: () => console.warn('hide() NOT IMPLEMENTED'),
_show: () => console.warn('show() NOT IMPLEMENTED'),
};
class UIModalService {
static REGISTRATION = {
name,
altName: 'UIModalService',
create: (): UIModalService => {
return new UIModalService();
},
};
readonly name = name;
/**
* Show a new UI modal;
*
* @param {ModalProps} props { content, contentProps, shouldCloseOnEsc, isOpen, closeButton, title, customClassName }
*/
show({
content = null,
contentProps = null,
shouldCloseOnEsc = true,
isOpen = true,
closeButton = true,
title = null,
customClassName = null,
}) {
return serviceImplementation._show({
content,
contentProps,
shouldCloseOnEsc,
isOpen,
closeButton,
title,
customClassName,
});
}
/**
* Hides/dismisses the modal, if currently shown
*
* @returns void
*/
hide() {
return serviceImplementation._hide();
}
/**
*
*
* @param {*} {
* hide: hideImplementation,
* show: showImplementation,
* }
*/
setServiceImplementation({
hide: hideImplementation,
show: showImplementation,
}) {
if (hideImplementation) {
serviceImplementation._hide = hideImplementation;
}
if (showImplementation) {
serviceImplementation._show = showImplementation;
}
}
}
export default UIModalService;

View File

@ -1,101 +0,0 @@
/**
* A UI Notification
*
* @typedef {Object} Notification
* @property {string} title -
* @property {string} message -
* @property {number} [duration=5000] - in ms
* @property {string} [position="bottomRight"] -"topLeft" | "topCenter | "topRight" | "bottomLeft" | "bottomCenter" | "bottomRight"
* @property {string} [type="info"] - "info" | "error" | "warning" | "success"
* @property {boolean} [autoClose=true]
*/
const name = 'uiNotificationService';
const serviceShowRequestQueue = [];
const publicAPI = {
name,
hide: _hide,
show: _show,
setServiceImplementation,
};
const serviceImplementation = {
_hide: () => console.warn('hide() NOT IMPLEMENTED'),
_show: showArguments => {
serviceShowRequestQueue.push(showArguments);
console.warn('show() NOT IMPLEMENTED');
},
};
/**
* Create and show a new UI notification; returns the
* ID of the created notification.
*
* @param {Notification} notification { title, message, duration, position, type, autoClose}
* @returns {number} id
*/
function _show({
title,
message,
duration = 5000,
position = 'bottomRight',
type = 'info',
autoClose = true,
}) {
return serviceImplementation._show({
title,
message,
duration,
position,
type,
autoClose,
});
}
/**
* Hides/dismisses the notification, if currently shown
*
* @param {number} id - id of the notification to hide/dismiss
* @returns undefined
*/
function _hide(id) {
return serviceImplementation._hide({ id });
}
/**
*
*
* @param {*} {
* hide: hideImplementation,
* show: showImplementation,
* }
*/
function setServiceImplementation({
hide: hideImplementation,
show: showImplementation,
}) {
if (hideImplementation) {
serviceImplementation._hide = hideImplementation;
}
if (showImplementation) {
serviceImplementation._show = showImplementation;
while (serviceShowRequestQueue.length > 0) {
const showArguments = serviceShowRequestQueue.pop();
serviceImplementation._show(showArguments);
}
}
}
export default {
REGISTRATION: {
name,
altName: 'UINotificationService',
create: ({ configuration = {} }) => {
return publicAPI;
},
},
};

View File

@ -0,0 +1,94 @@
/**
* A UI Notification
*
* @typedef {Object} Notification
* @property {string} title -
* @property {string} message -
* @property {number} [duration=5000] - in ms
* @property {string} [position="bottomRight"] -"topLeft" | "topCenter | "topRight" | "bottomLeft" | "bottomCenter" | "bottomRight"
* @property {string} [type="info"] - "info" | "error" | "warning" | "success"
* @property {boolean} [autoClose=true]
*/
const serviceShowRequestQueue = [];
const serviceImplementation = {
_hide: () => console.warn('hide() NOT IMPLEMENTED'),
_show: showArguments => {
serviceShowRequestQueue.push(showArguments);
console.warn('show() NOT IMPLEMENTED');
},
};
class UINotificationService {
static REGISTRATION = {
name: 'uiNotificationService',
altName: 'UINotificationService',
create: (): UINotificationService => {
return new UINotificationService();
},
};
/**
*
*
* @param {*} {
* hide: hideImplementation,
* show: showImplementation,
* }
*/
public setServiceImplementation({
hide: hideImplementation,
show: showImplementation,
}): void {
if (hideImplementation) {
serviceImplementation._hide = hideImplementation;
}
if (showImplementation) {
serviceImplementation._show = showImplementation;
while (serviceShowRequestQueue.length > 0) {
const showArguments = serviceShowRequestQueue.pop();
serviceImplementation._show(showArguments);
}
}
}
/**
* Hides/dismisses the notification, if currently shown
*
* @param {number} id - id of the notification to hide/dismiss
* @returns undefined
*/
public hide(id: string) {
return serviceImplementation._hide({ id });
}
/**
* Create and show a new UI notification; returns the
* ID of the created notification.
*
* @param {Notification} notification { title, message, duration, position, type, autoClose}
* @returns {number} id
*/
show({
title,
message,
duration = 5000,
position = 'bottomRight',
type = 'info',
autoClose = true,
}) {
return serviceImplementation._show({
title,
message,
duration,
position,
type,
autoClose,
});
}
}
export default UINotificationService;

View File

@ -6,6 +6,8 @@ import {
ToolbarService, ToolbarService,
DisplaySetService, DisplaySetService,
StateSyncService, StateSyncService,
UINotificationService,
UIModalService,
} from '../services'; } from '../services';
/** /**
@ -22,8 +24,8 @@ export default interface Services {
cornerstoneViewportService?: Record<string, unknown>; cornerstoneViewportService?: Record<string, unknown>;
uiDialogService?: Record<string, unknown>; uiDialogService?: Record<string, unknown>;
toolGroupService?: Record<string, unknown>; toolGroupService?: Record<string, unknown>;
uiNotificationService?: Record<string, unknown>; uiNotificationService?: UINotificationService;
uiModalService?: Record<string, unknown>; uiModalService?: UIModalService;
uiViewportDialogService?: Record<string, unknown>; uiViewportDialogService?: Record<string, unknown>;
viewportGridService?: ViewportGridService; viewportGridService?: ViewportGridService;
syncGroupService?: Record<string, unknown>; syncGroupService?: Record<string, unknown>;

View File

@ -1,6 +1,6 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ThumbnailType from './ThumbnailType'; import ThumbnailType from './ThumbnailType';
import { PresentationIds } from '../contextProviders/getPresentationIds'; import type { PresentationIds } from '../contextProviders/getPresentationIds';
// A few miscellaneous types declared inline here. // A few miscellaneous types declared inline here.