Merge branch 'master' into ci/complete-promoted-deploy
This commit is contained in:
commit
e0fc69c181
@ -11,7 +11,10 @@ export default {
|
||||
* LIFECYCLE HOOKS
|
||||
*/
|
||||
|
||||
preRegistration({ serviceManager, configuration: extensionConfiguration }) {},
|
||||
preRegistration({
|
||||
servicesManager,
|
||||
configuration: extensionConfiguration,
|
||||
}) {},
|
||||
|
||||
/**
|
||||
* MODULE GETTERS
|
||||
|
||||
@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.5.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@1.5.0...@ohif/extension-cornerstone@1.5.1) (2019-11-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [1.5.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@1.4.1...@ohif/extension-cornerstone@1.5.0) (2019-11-13)
|
||||
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone",
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.1",
|
||||
"description": "OHIF extension for Cornerstone",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
|
||||
@ -24,8 +24,8 @@ export default {
|
||||
* @param {object} [configuration={}]
|
||||
* @param {object|array} [configuration.csToolsConfig] - Passed directly to `initCornerstoneTools`
|
||||
*/
|
||||
preRegistration({ serviceManager, configuration = {} }) {
|
||||
init({ serviceManager, configuration });
|
||||
preRegistration({ servicesManager, configuration = {} }) {
|
||||
init({ servicesManager, configuration });
|
||||
},
|
||||
getViewportModule() {
|
||||
return OHIFCornerstoneViewport;
|
||||
|
||||
@ -28,7 +28,7 @@ cornerstone.metaData.addProvider(fallbackMetaDataProvider, -1);
|
||||
* @param {object} configuration
|
||||
* @param {Object|Array} configuration.csToolsConfig
|
||||
*/
|
||||
export default function init({ serviceManager, configuration = {} }) {
|
||||
export default function init({ servicesManager, configuration = {} }) {
|
||||
const { csToolsConfig } = configuration;
|
||||
const { StackManager } = OHIF.utils;
|
||||
const metadataProvider = new OHIF.cornerstone.MetadataProvider();
|
||||
|
||||
@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.53.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.53.7...@ohif/extension-vtk@0.53.8) (2019-11-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.53.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.53.6...@ohif/extension-vtk@0.53.7) (2019-11-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.53.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.53.5...@ohif/extension-vtk@0.53.6) (2019-11-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-vtk",
|
||||
"version": "0.53.6",
|
||||
"version": "0.53.8",
|
||||
"description": "OHIF extension for VTK.js",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@ -52,8 +52,8 @@
|
||||
"react-vtkjs-viewport": "^0.3.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ohif/core": "^1.9.0",
|
||||
"@ohif/ui": "^0.62.1",
|
||||
"@ohif/core": "^1.10.0",
|
||||
"@ohif/ui": "^0.62.3",
|
||||
"cornerstone-tools": "^4.0.9",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
"dcmjs": "^0.6.1",
|
||||
|
||||
@ -3,6 +3,25 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.10.0](https://github.com/OHIF/Viewers/compare/@ohif/core@1.9.1...@ohif/core@1.10.0) (2019-11-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Inject into Extension Modules / improve tests ([f63d8a7](https://github.com/OHIF/Viewers/commit/f63d8a73d867ad9dfd8ee0cad74edce180eb34f0))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.9.1](https://github.com/OHIF/Viewers/compare/@ohif/core@1.9.0...@ohif/core@1.9.1) (2019-11-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [1.9.0](https://github.com/OHIF/Viewers/compare/@ohif/core@1.8.0...@ohif/core@1.9.0) (2019-11-13)
|
||||
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/core",
|
||||
"version": "1.9.0",
|
||||
"version": "1.10.0",
|
||||
"description": "Generic business logic for web-based medical imaging applications",
|
||||
"author": "OHIF Core Team",
|
||||
"license": "MIT",
|
||||
|
||||
@ -68,7 +68,7 @@ export default class ExtensionManager {
|
||||
// preRegistrationHook
|
||||
if (extension.preRegistration) {
|
||||
extension.preRegistration({
|
||||
serviceManager: this._servicesManager,
|
||||
servicesManager: this._servicesManager,
|
||||
configuration,
|
||||
});
|
||||
}
|
||||
@ -110,7 +110,9 @@ export default class ExtensionManager {
|
||||
}
|
||||
|
||||
try {
|
||||
const extensionModule = getModuleFn();
|
||||
const extensionModule = getModuleFn({
|
||||
servicesManager: this._servicesManager,
|
||||
});
|
||||
|
||||
if (!extensionModule) {
|
||||
log.warn(
|
||||
|
||||
@ -40,6 +40,30 @@ describe('ExtensionManager.js', () => {
|
||||
});
|
||||
|
||||
describe('registerExtension()', () => {
|
||||
it('calls preRegistration() for extension', () => {
|
||||
// SUT
|
||||
const fakeExtension = { one: '1', preRegistration: jest.fn() };
|
||||
extensionManager.registerExtension(fakeExtension);
|
||||
|
||||
// Assert
|
||||
expect(fakeExtension.preRegistration.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('calls preRegistration() passing configuration and servicesManager instance for extension', () => {
|
||||
const configuration = { config: 'Some configuration' };
|
||||
extensionManager._servicesManager = { services: { TestService: {} } };
|
||||
|
||||
// SUT
|
||||
const fakeExtension = { one: '1', preRegistration: jest.fn() };
|
||||
extensionManager.registerExtension(fakeExtension, configuration);
|
||||
|
||||
// Assert
|
||||
expect(fakeExtension.preRegistration.mock.calls[0][0]).toEqual({
|
||||
servicesManager: extensionManager._servicesManager,
|
||||
configuration,
|
||||
});
|
||||
});
|
||||
|
||||
it('logs a warning if the extension is null or undefined', () => {
|
||||
const undefinedExtension = undefined;
|
||||
const nullExtension = null;
|
||||
@ -110,6 +134,25 @@ describe('ExtensionManager.js', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('successfully passes a servicesManager instance to each module', () => {
|
||||
extensionManager._servicesManager = { services: { TestService: {} } };
|
||||
|
||||
const extension = {
|
||||
id: 'hello-world',
|
||||
getViewportModule: jest.fn(),
|
||||
getSopClassHandlerModule: jest.fn(),
|
||||
getPanelModule: jest.fn(),
|
||||
getToolbarModule: jest.fn(),
|
||||
getCommandsModule: jest.fn(),
|
||||
};
|
||||
|
||||
extensionManager.registerExtension(extension);
|
||||
|
||||
expect(extension.getViewportModule.mock.calls[0][0]).toEqual({
|
||||
servicesManager: extensionManager._servicesManager,
|
||||
});
|
||||
});
|
||||
|
||||
it('successfully registers a module for each module type', () => {
|
||||
const extension = {
|
||||
id: 'hello-world',
|
||||
|
||||
@ -19,7 +19,7 @@ import ui from './ui';
|
||||
import user from './user.js';
|
||||
import utils from './utils/';
|
||||
|
||||
import { createUiNotificationService } from './services';
|
||||
import { createUINotificationService, createUIModalService } from './services';
|
||||
|
||||
const OHIF = {
|
||||
MODULE_TYPES,
|
||||
@ -46,7 +46,8 @@ const OHIF = {
|
||||
measurements,
|
||||
hangingProtocols,
|
||||
//
|
||||
createUiNotificationService,
|
||||
createUINotificationService,
|
||||
createUIModalService,
|
||||
};
|
||||
|
||||
export {
|
||||
@ -73,7 +74,8 @@ export {
|
||||
measurements,
|
||||
hangingProtocols,
|
||||
//
|
||||
createUiNotificationService,
|
||||
createUINotificationService,
|
||||
createUIModalService,
|
||||
};
|
||||
|
||||
export { OHIF };
|
||||
|
||||
@ -10,7 +10,8 @@ describe('Top level exports', () => {
|
||||
'HotkeysManager',
|
||||
'ServicesManager',
|
||||
//
|
||||
'createUiNotificationService',
|
||||
'createUINotificationService',
|
||||
'createUIModalService',
|
||||
//
|
||||
'utils',
|
||||
'studies',
|
||||
|
||||
@ -1,9 +1,49 @@
|
||||
import log from './../log.js';
|
||||
|
||||
export default class ServicesManager {
|
||||
constructor() {
|
||||
this.services = {};
|
||||
this.registeredServiceNames = [];
|
||||
}
|
||||
|
||||
register(service) {
|
||||
/**
|
||||
*
|
||||
* @param {Object} service
|
||||
*/
|
||||
registerService(service) {
|
||||
if (!service) {
|
||||
log.warn(
|
||||
'Attempting to register a null/undefined service. Exiting early.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let serviceName = service.name;
|
||||
|
||||
if (!serviceName) {
|
||||
log.warn(`Service name not set. Exiting early.`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.registeredServiceNames.includes(serviceName)) {
|
||||
log.warn(
|
||||
`Extension name ${serviceName} has already been registered. Exiting before duplicating services.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.services[service.name] = service;
|
||||
|
||||
// Track service registration
|
||||
this.registeredServiceNames.push(serviceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* An array of services.
|
||||
*
|
||||
* @param {Object[]} services - Array of services
|
||||
*/
|
||||
registerServices(services) {
|
||||
services.forEach(service => this.registerService(service));
|
||||
}
|
||||
}
|
||||
|
||||
70
platform/core/src/services/ServicesManager.test.js
Normal file
70
platform/core/src/services/ServicesManager.test.js
Normal file
@ -0,0 +1,70 @@
|
||||
import ServicesManager from './ServicesManager.js';
|
||||
import log from '../log.js';
|
||||
|
||||
jest.mock('./../log.js');
|
||||
|
||||
describe('ServicesManager.js', () => {
|
||||
let servicesManager;
|
||||
|
||||
beforeEach(() => {
|
||||
servicesManager = new ServicesManager();
|
||||
log.warn.mockClear();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('registerServices()', () => {
|
||||
it('calls registerService() for each service', () => {
|
||||
servicesManager.registerService = jest.fn();
|
||||
|
||||
const fakeServices = [
|
||||
{ name: 'UINotificationTestService', hide: jest.fn() },
|
||||
{ name: 'UIModalTestService', hide: jest.fn() },
|
||||
];
|
||||
|
||||
servicesManager.registerServices(fakeServices);
|
||||
|
||||
expect(servicesManager.registerService.mock.calls.length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('registerService()', () => {
|
||||
it('logs a warning if the service is null or undefined', () => {
|
||||
const undefinedService = undefined;
|
||||
const nullService = null;
|
||||
|
||||
servicesManager.registerService(undefinedService);
|
||||
servicesManager.registerService(nullService);
|
||||
|
||||
expect(log.warn.mock.calls.length).toBe(2);
|
||||
});
|
||||
|
||||
it('logs a warning if the service does not have a name', () => {
|
||||
const serviceWithEmptyName = { name: '', hide: jest.fn() };
|
||||
const serviceWithoutName = { hide: jest.fn() };
|
||||
|
||||
servicesManager.registerService(serviceWithEmptyName);
|
||||
servicesManager.registerService(serviceWithoutName);
|
||||
|
||||
expect(log.warn.mock.calls.length).toBe(2);
|
||||
});
|
||||
|
||||
it('tracks which services have been registered', () => {
|
||||
const service = {
|
||||
name: 'UINotificationService',
|
||||
};
|
||||
|
||||
servicesManager.registerService(service);
|
||||
|
||||
expect(servicesManager.registeredServiceNames).toContain(service.name);
|
||||
});
|
||||
|
||||
it('logs a warning if the service has an name that has already been registered', () => {
|
||||
const service = { name: 'UINotificationService' };
|
||||
|
||||
servicesManager.registerService(service);
|
||||
servicesManager.registerService(service);
|
||||
|
||||
expect(log.warn.mock.calls.length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
88
platform/core/src/services/UIModalService/index.js
Normal file
88
platform/core/src/services/UIModalService/index.js
Normal file
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* A UI Element
|
||||
*
|
||||
* @typedef {ReactElement|HTMLElement} Modal
|
||||
*/
|
||||
|
||||
/**
|
||||
* UI Modal
|
||||
*
|
||||
* @typedef {Object} ModalProps
|
||||
* @property {string} [header=null] -
|
||||
* @property {string} [footer=null] -
|
||||
* @property {string} [backdrop=false] -
|
||||
* @property {string} [keyboard=false] -
|
||||
* @property {number} [show=true] -
|
||||
* @property {string} [closeButton=true] -
|
||||
* @property {string} [title=null] - 'Modal Title'
|
||||
* @property {boolean} [customClassName=null] - '.ModalClass'
|
||||
*/
|
||||
|
||||
const uiModalServicePublicAPI = {
|
||||
name: 'UIModalService',
|
||||
hide,
|
||||
show,
|
||||
setServiceImplementation,
|
||||
};
|
||||
|
||||
const uiModalServiceImplementation = {
|
||||
_hide: () => console.warn('hide() NOT IMPLEMENTED'),
|
||||
_show: () => console.warn('show() NOT IMPLEMENTED'),
|
||||
};
|
||||
|
||||
function createUIModalService() {
|
||||
return uiModalServicePublicAPI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a new UI modal;
|
||||
*
|
||||
* @param {Modal} component React component
|
||||
* @param {ModalProps} props { header, footer, backdrop, keyboard, show, closeButton, title, customClassName }
|
||||
*/
|
||||
function show(
|
||||
component,
|
||||
props = {
|
||||
header: null,
|
||||
footer: null,
|
||||
backdrop: false,
|
||||
keyboard: false,
|
||||
show: true,
|
||||
closeButton: true,
|
||||
title: null,
|
||||
customClassName: null,
|
||||
}
|
||||
) {
|
||||
return uiModalServiceImplementation._show(component, props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides/dismisses the modal, if currently shown
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
function hide() {
|
||||
return uiModalServiceImplementation._hide();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param {*} {
|
||||
* hide: hideImplementation,
|
||||
* show: showImplementation,
|
||||
* }
|
||||
*/
|
||||
function setServiceImplementation({
|
||||
hide: hideImplementation,
|
||||
show: showImplementation,
|
||||
}) {
|
||||
if (hideImplementation) {
|
||||
uiModalServiceImplementation._hide = hideImplementation;
|
||||
}
|
||||
if (showImplementation) {
|
||||
uiModalServiceImplementation._show = showImplementation;
|
||||
}
|
||||
}
|
||||
|
||||
export default createUIModalService;
|
||||
@ -10,7 +10,7 @@
|
||||
* @property {boolean} [autoClose=true]
|
||||
*/
|
||||
|
||||
const uiNotificationServicePublicApi = {
|
||||
const uiNotificationServicePublicAPI = {
|
||||
name: 'UINotificationService',
|
||||
hide,
|
||||
show,
|
||||
@ -22,8 +22,8 @@ const uiNotificationServiceImplementation = {
|
||||
_show: () => console.warn('show() NOT IMPLEMENTED'),
|
||||
};
|
||||
|
||||
function createUiNotificationService() {
|
||||
return uiNotificationServicePublicApi;
|
||||
function createUINotificationService() {
|
||||
return uiNotificationServicePublicAPI;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,4 +81,4 @@ function setServiceImplementation({
|
||||
}
|
||||
}
|
||||
|
||||
export default createUiNotificationService;
|
||||
export default createUINotificationService;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import ServicesManager from './ServicesManager.js';
|
||||
import createUiNotificationService from './UINotificationService';
|
||||
import createUINotificationService from './UINotificationService';
|
||||
import createUIModalService from './UIModalService';
|
||||
|
||||
export { createUiNotificationService, ServicesManager };
|
||||
export { createUINotificationService, createUIModalService, ServicesManager };
|
||||
|
||||
@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.62.3](https://github.com/OHIF/Viewers/compare/@ohif/ui@0.62.2...@ohif/ui@0.62.3) (2019-11-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/ui
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.62.2](https://github.com/OHIF/Viewers/compare/@ohif/ui@0.62.1...@ohif/ui@0.62.2) (2019-11-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/ui
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.62.1](https://github.com/OHIF/Viewers/compare/@ohif/ui@0.62.0...@ohif/ui@0.62.1) (2019-11-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/ui
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/ui",
|
||||
"version": "0.62.1",
|
||||
"version": "0.62.3",
|
||||
"description": "A set of React components for Medical Imaging Viewers",
|
||||
"author": "OHIF Contributors",
|
||||
"license": "MIT",
|
||||
|
||||
@ -13,7 +13,7 @@ const OHIFModal = ({
|
||||
onHide,
|
||||
footer: Footer,
|
||||
header: Header,
|
||||
children: Component,
|
||||
children,
|
||||
}) => (
|
||||
<ReactBootstrapModal
|
||||
className={classNames('modal fade themed in', className)}
|
||||
@ -32,12 +32,9 @@ const OHIFModal = ({
|
||||
{Header && <Header hide={onHide} />}
|
||||
</ReactBootstrapModal.Header>
|
||||
)}
|
||||
<ReactBootstrapModal.Body>
|
||||
{Component && <Component hide={onHide} />}
|
||||
</ReactBootstrapModal.Body>
|
||||
<ReactBootstrapModal.Body>{children}</ReactBootstrapModal.Body>
|
||||
{Footer && (
|
||||
<ReactBootstrapModal.Footer>
|
||||
{' '}
|
||||
<Footer hide={onHide} />
|
||||
</ReactBootstrapModal.Footer>
|
||||
)}
|
||||
@ -52,9 +49,20 @@ OHIFModal.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
title: PropTypes.string,
|
||||
onHide: PropTypes.func,
|
||||
footer: PropTypes.node,
|
||||
header: PropTypes.node,
|
||||
children: PropTypes.node,
|
||||
footer: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
PropTypes.func,
|
||||
]),
|
||||
header: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
PropTypes.func,
|
||||
]),
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
]).isRequired,
|
||||
};
|
||||
|
||||
export default OHIFModal;
|
||||
|
||||
@ -37,8 +37,8 @@ const SnackbarContainer = () => {
|
||||
|
||||
return (
|
||||
<div key={pos} className={`sb-container sb-${pos}`}>
|
||||
{items[pos].map(item => (
|
||||
<div key={item.id}>{renderItem(item)}</div>
|
||||
{items[pos].map((item, index) => (
|
||||
<div key={item.id + index}>{renderItem(item)}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,15 +1,23 @@
|
||||
import React, { useState, createContext, useContext } from 'react';
|
||||
import React, {
|
||||
useState,
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useCallback,
|
||||
} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const ModalContext = createContext(null);
|
||||
const { Provider, Consumer } = ModalContext;
|
||||
const { Provider } = ModalContext;
|
||||
|
||||
export const useModal = () => useContext(ModalContext);
|
||||
|
||||
const ModalProvider = ({ children, modal: Modal }) => {
|
||||
const ModalProvider = ({ children, modal: Modal, service }) => {
|
||||
const DEFAULT_OPTIONS = {
|
||||
component: null /* The component instance inside the modal. */,
|
||||
header: null /* The content inside the modal header. */,
|
||||
footer: null /* The content inside the modal footer. */,
|
||||
backdrop: false /* Should the modal render a backdrop overlay. */,
|
||||
keyboard: false /* Modal is dismissible via the esc key. */,
|
||||
show: true /* Make the Modal visible or hidden. */,
|
||||
@ -20,51 +28,81 @@ const ModalProvider = ({ children, modal: Modal }) => {
|
||||
|
||||
const [options, setOptions] = useState(DEFAULT_OPTIONS);
|
||||
|
||||
/**
|
||||
* Sets the implementation of a modal service that can be used by extensions.
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (service) {
|
||||
service.setServiceImplementation({ hide, show });
|
||||
}
|
||||
}, [hide, service, show]);
|
||||
|
||||
/**
|
||||
* Show the modal and override its configuration props.
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
const show = (component, props = {}) =>
|
||||
setOptions(Object.assign({}, options, props, { component }));
|
||||
const show = useCallback(
|
||||
(component, props = {}) =>
|
||||
setOptions(Object.assign({}, options, props, { component })),
|
||||
[options]
|
||||
);
|
||||
|
||||
/**
|
||||
* Hide the modal and set its properties to default.
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
const hide = () => setOptions(DEFAULT_OPTIONS);
|
||||
const hide = useCallback(() => setOptions(DEFAULT_OPTIONS), [
|
||||
DEFAULT_OPTIONS,
|
||||
]);
|
||||
|
||||
const { component: Component } = options;
|
||||
|
||||
return (
|
||||
<Provider value={{ ...options, show, hide }}>
|
||||
<Consumer>
|
||||
{props => {
|
||||
const { component, footer, header, customClassName } = props;
|
||||
return component ? (
|
||||
<Modal
|
||||
className={classNames(customClassName, component.className)}
|
||||
backdrop={options.backdrop}
|
||||
keyboard={options.keyboard}
|
||||
show={options.show}
|
||||
title={options.title}
|
||||
closeButton={options.closeButton}
|
||||
onHide={hide}
|
||||
footer={footer}
|
||||
header={header}
|
||||
>
|
||||
{component}
|
||||
</Modal>
|
||||
) : null;
|
||||
}}
|
||||
</Consumer>
|
||||
<Provider value={{ show, hide }}>
|
||||
{options.component && (
|
||||
<Modal
|
||||
className={classNames(
|
||||
options.customClassName,
|
||||
options.component.className
|
||||
)}
|
||||
backdrop={options.backdrop}
|
||||
keyboard={options.keyboard}
|
||||
show={options.show}
|
||||
title={options.title}
|
||||
closeButton={options.closeButton}
|
||||
footer={options.footer}
|
||||
header={options.header}
|
||||
onHide={hide}
|
||||
>
|
||||
<Component {...options} show={show} hide={hide} />
|
||||
</Modal>
|
||||
)}
|
||||
{children}
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
||||
ModalProvider.defaultProps = {
|
||||
service: null,
|
||||
};
|
||||
|
||||
ModalProvider.propTypes = {
|
||||
children: PropTypes.node,
|
||||
modal: PropTypes.node,
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
]).isRequired,
|
||||
modal: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
PropTypes.func,
|
||||
]).isRequired,
|
||||
service: PropTypes.shape({
|
||||
setServiceImplementation: PropTypes.func,
|
||||
}),
|
||||
};
|
||||
|
||||
/**
|
||||
@ -74,7 +112,8 @@ ModalProvider.propTypes = {
|
||||
*/
|
||||
export const withModal = Component => {
|
||||
return function WrappedComponent(props) {
|
||||
return <Component {...props} modalContext={{ ...useModal() }} />;
|
||||
const { show, hide } = useModal();
|
||||
return <Component {...props} modal={{ show, hide }} />;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -5,6 +5,8 @@ import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import SnackbarContainer from '../components/snackbar/SnackbarContainer';
|
||||
import SnackbarTypes from '../components/snackbar/SnackbarTypes';
|
||||
|
||||
@ -25,49 +27,62 @@ const SnackbarProvider = ({ children, service }) => {
|
||||
const [count, setCount] = useState(1);
|
||||
const [snackbarItems, setSnackbarItems] = useState([]);
|
||||
|
||||
/**
|
||||
* Sets the implementation of a notification service that can be used by extensions.
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
useEffect(() => {
|
||||
service.setServiceImplementation({ hide, show });
|
||||
if (service) {
|
||||
service.setServiceImplementation({ hide, show });
|
||||
}
|
||||
}, [service, hide, show]);
|
||||
|
||||
const show = useCallback(options => {
|
||||
if (!options || (!options.title && !options.message)) {
|
||||
console.warn(
|
||||
'Snackbar cannot be rendered without required parameters: title | message'
|
||||
);
|
||||
const show = useCallback(
|
||||
options => {
|
||||
if (!options || (!options.title && !options.message)) {
|
||||
console.warn(
|
||||
'Snackbar cannot be rendered without required parameters: title | message'
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const newItem = {
|
||||
...DEFAULT_OPTIONS,
|
||||
...options,
|
||||
id: count,
|
||||
visible: true,
|
||||
};
|
||||
const newItem = {
|
||||
...DEFAULT_OPTIONS,
|
||||
...options,
|
||||
id: count,
|
||||
visible: true,
|
||||
};
|
||||
|
||||
setSnackbarItems(state => [...state, newItem]);
|
||||
setCount(count + 1);
|
||||
});
|
||||
setSnackbarItems(state => [...state, newItem]);
|
||||
setCount(count + 1);
|
||||
},
|
||||
[count, DEFAULT_OPTIONS]
|
||||
);
|
||||
|
||||
const hide = useCallback(id => {
|
||||
const hideItem = items => {
|
||||
const newItems = items.map(item => {
|
||||
if (item.id === id) {
|
||||
item.visible = false;
|
||||
}
|
||||
const hide = useCallback(
|
||||
id => {
|
||||
const hideItem = items => {
|
||||
const newItems = items.map(item => {
|
||||
if (item.id === id) {
|
||||
item.visible = false;
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
return item;
|
||||
});
|
||||
|
||||
return newItems;
|
||||
};
|
||||
return newItems;
|
||||
};
|
||||
|
||||
setSnackbarItems(state => hideItem(state));
|
||||
setSnackbarItems(state => hideItem(state));
|
||||
|
||||
setTimeout(() => {
|
||||
setSnackbarItems(state => [...state.filter(item => item.id !== id)]);
|
||||
}, 1000);
|
||||
});
|
||||
setTimeout(() => {
|
||||
setSnackbarItems(state => [...state.filter(item => item.id !== id)]);
|
||||
}, 1000);
|
||||
},
|
||||
[setSnackbarItems]
|
||||
);
|
||||
|
||||
const hideAll = () => {
|
||||
// reset count
|
||||
@ -95,6 +110,21 @@ const SnackbarProvider = ({ children, service }) => {
|
||||
);
|
||||
};
|
||||
|
||||
SnackbarProvider.defaultProps = {
|
||||
service: null,
|
||||
};
|
||||
|
||||
SnackbarProvider.propTypes = {
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
PropTypes.func,
|
||||
]).isRequired,
|
||||
service: PropTypes.shape({
|
||||
setServiceImplementation: PropTypes.func,
|
||||
}),
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* High Order Component to use the snackbar methods through a Class Component
|
||||
|
||||
@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.8.4](https://github.com/OHIF/Viewers/compare/@ohif/viewer@2.8.3...@ohif/viewer@2.8.4) (2019-11-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.8.3](https://github.com/OHIF/Viewers/compare/@ohif/viewer@2.8.2...@ohif/viewer@2.8.3) (2019-11-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.8.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@2.8.1...@ohif/viewer@2.8.2) (2019-11-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/viewer",
|
||||
"version": "2.8.2",
|
||||
"version": "2.8.4",
|
||||
"description": "OHIF Viewer",
|
||||
"author": "OHIF Contributors",
|
||||
"license": "MIT",
|
||||
@ -45,14 +45,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@ohif/core": "^1.9.0",
|
||||
"@ohif/core": "^1.10.0",
|
||||
"@ohif/extension-cornerstone": "^2.0.0",
|
||||
"@ohif/extension-dicom-html": "^1.0.1",
|
||||
"@ohif/extension-dicom-microscopy": "^0.50.6",
|
||||
"@ohif/extension-dicom-pdf": "^1.0.0",
|
||||
"@ohif/extension-vtk": "^0.53.6",
|
||||
"@ohif/extension-vtk": "^0.53.8",
|
||||
"@ohif/i18n": "^0.52.0",
|
||||
"@ohif/ui": "^0.62.1",
|
||||
"@ohif/ui": "^0.62.3",
|
||||
"@tanem/react-nprogress": "^1.1.25",
|
||||
"classnames": "^2.2.6",
|
||||
"core-js": "^3.2.1",
|
||||
|
||||
@ -8,7 +8,8 @@ import {
|
||||
ExtensionManager,
|
||||
ServicesManager,
|
||||
HotkeysManager,
|
||||
createUiNotificationService,
|
||||
createUINotificationService,
|
||||
createUIModalService,
|
||||
utils,
|
||||
} from '@ohif/core';
|
||||
import React, { Component } from 'react';
|
||||
@ -44,7 +45,8 @@ const commandsManagerConfig = {
|
||||
};
|
||||
|
||||
// Services
|
||||
const UINotificationService = createUiNotificationService();
|
||||
const UINotificationService = createUINotificationService();
|
||||
const UIModalService = createUIModalService();
|
||||
|
||||
const commandsManager = new CommandsManager(commandsManagerConfig);
|
||||
const hotkeysManager = new HotkeysManager(commandsManager);
|
||||
@ -89,7 +91,7 @@ class App extends Component {
|
||||
const { servers, extensions, hotkeys, oidc } = props;
|
||||
|
||||
this.initUserManager(oidc);
|
||||
_initServices([UINotificationService]);
|
||||
_initServices([UINotificationService, UIModalService]);
|
||||
_initExtensions(extensions, hotkeys);
|
||||
_initServers(servers);
|
||||
initWebWorkers();
|
||||
@ -112,7 +114,10 @@ class App extends Component {
|
||||
<Router basename={routerBasename}>
|
||||
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
||||
<SnackbarProvider service={UINotificationService}>
|
||||
<ModalProvider modal={OHIFModal}>
|
||||
<ModalProvider
|
||||
modal={OHIFModal}
|
||||
service={UIModalService}
|
||||
>
|
||||
<OHIFStandaloneViewer userManager={userManager} />
|
||||
</ModalProvider>
|
||||
</SnackbarProvider>
|
||||
@ -133,7 +138,7 @@ class App extends Component {
|
||||
<Router basename={routerBasename}>
|
||||
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
||||
<SnackbarProvider service={UINotificationService}>
|
||||
<ModalProvider modal={OHIFModal}>
|
||||
<ModalProvider modal={OHIFModal} service={UIModalService}>
|
||||
<OHIFStandaloneViewer />
|
||||
</ModalProvider>
|
||||
</SnackbarProvider>
|
||||
@ -180,7 +185,7 @@ class App extends Component {
|
||||
}
|
||||
|
||||
function _initServices(services) {
|
||||
services.forEach(service => servicesManager.register(service));
|
||||
servicesManager.registerServices(services);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -7,10 +7,10 @@ export default {
|
||||
*/
|
||||
id: 'measurements-table',
|
||||
|
||||
preRegistration({ serviceManager, configuration = {} }) {
|
||||
init({ serviceManager, configuration });
|
||||
preRegistration({ servicesManager, configuration = {} }) {
|
||||
init({ servicesManager, configuration });
|
||||
},
|
||||
getPanelModule() {
|
||||
getPanelModule({ servicesManager }) {
|
||||
return {
|
||||
menuOptions: [
|
||||
{
|
||||
|
||||
@ -34,7 +34,7 @@ const MEASUREMENT_ACTION_MAP = {
|
||||
* @export
|
||||
* @param {*} configuration
|
||||
*/
|
||||
export default function init({ serviceManager, configuration = {} }) {
|
||||
export default function init({ servicesManager, configuration = {} }) {
|
||||
// If these tools were already added by a different extension, we want to replace
|
||||
// them with the same tools that have an alternative configuration. By passing in
|
||||
// our custom `getMeasurementLocationCallback`, we can...
|
||||
|
||||
@ -20,7 +20,7 @@ class Header extends Component {
|
||||
t: PropTypes.func.isRequired,
|
||||
userManager: PropTypes.object,
|
||||
user: PropTypes.object,
|
||||
modalContext: PropTypes.object,
|
||||
modal: PropTypes.object,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@ -40,7 +40,7 @@ class Header extends Component {
|
||||
t,
|
||||
user,
|
||||
userManager,
|
||||
modalContext: { show },
|
||||
modal: { show },
|
||||
} = this.props;
|
||||
this.options = [
|
||||
{
|
||||
|
||||
@ -287,7 +287,7 @@ function _handleBuiltIn({ behavior } = {}) {
|
||||
}
|
||||
|
||||
if (behavior === 'DOWNLOAD_SCREEN_SHOT') {
|
||||
this.props.modalContext.show(ConnectedViewportDownloadForm, {
|
||||
this.props.modal.show(ConnectedViewportDownloadForm, {
|
||||
title: this.props.t('Download High Quality Image'),
|
||||
customClassName: 'ViewportDownloadForm',
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user