feat!: Ability to configure cornerstone tools via extension configuration (#1229)
* Fix ExtensionManager bug and add test to bandaid * Add tools configuration to extension manager preinit * Fix reducing of configs * Merge internal with external configs * Merge internal with external configs * Remove dialog from init in measurementstable * Testing injected configuration * New way to set config * Add new prop to dialog provider to allow disabling last position * Remove code from preinit in cornerstone * Add new prop to dialog provider to allow disabling last position * Add centralize to dialogs * Reorder dialogs when adding them * Fix draggable styles (cursor) * Remove repositioning methods from labelling flow and remove overlay from labelling manager * Fix empty array being set in bringToFront * Add new command to update table and pass commands manager to modules/preinit hook * Ad UIContextMenu service / factory * Use new contextmenu service in measurementspanel extension * Use dialogs for arrow annotate in default * Remove positioning funcionality from tool context menu * Add context menu service * Pass commandsModule to extension * Update edit description dialog and simple dialog to position relative * Remove style code from labelling flow and manager * Remove eventdata from labelling * Remove labelling code from measurement init * Add commandsmanager to provider * Update contextmenu provider and service * Add touchstart and mouseclick to hide contextmenu * Hide labelling if click/touch * Remove labelling and context menu dead code * Fix undefined bug if ViewerMain grid has no children * Fix broken prop on context menu * Update commandsmodule based on master * Fix broken configuration * Update script tag config * Remove cornerstone from toolcontextmenu * Remove cornerstone from toolcontextmenu * Split labelling and context menu providers * Split labelling and context menu providers * Update test * Destructure extensions into new array * CR Update: Move default arrow config to cornerstone instead of default * CR Update: Fix app configuration props structure * CR Update: Fix app configuration prop in script tag and extract commands manager from providers * CR Update: Create custom providers to use commandsManager * CR Update: Use services directly in measurementspanel * CR Update: Pass components to providers * CR Update: Remove position from dialog * CR Update: fix dialog prop check * CR Update: Fix comments * CR Update: Update documentation * CR Update: Add test default configuration * CR Update: Add default empy array to extensions * CR Update: Update i18n configuration all ot match current function configuration * CR Update: Add defaults to injected dependencies in configuration and extension configuration * CR Update: Add defaults to configuration with no args * Update documentation * CR Update: Add default for tools * CR Update: Update config object to i18n * CR Update: spread defaults * CR Update: Add tool configuration example to cornerstone extension * CR Update: Add tool configuration to netlify (testing) * CR Update: Remove netlify config for tools * CR Update: Rollback changes to i18n to be fixed later * CR Update: Update documentation and pass whole cornerstone config object instead of tools key SEE: https://www.conventionalcommits.org/en/v1.0.0/#commit-message-with-both-and-breaking-change-footer BREAKING CHANGE: modifies the exposed react <App /> components props. The contract for providing configuration for the app has changed. Please reference updated documentation for guidance.
This commit is contained in:
1 parent
fd7620ced9
commit
55a580659e
66 files changed
+1464
-1223
No files matched your search
+119
-45
@@ -4,9 +4,13 @@ import { I18nextProvider } from 'react-i18next';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Provider } from 'react-redux';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import OHIFCornerstoneExtension from '@ohif/extension-cornerstone';
|
||||
import { hot } from 'react-hot-loader/root';
|
||||
|
||||
import OHIFCornerstoneExtension from '@ohif/extension-cornerstone';
|
||||
|
||||
import ToolContextMenu from './connectedComponents/ToolContextMenu';
|
||||
import LabellingManager from './components/Labelling/LabellingManager';
|
||||
|
||||
import {
|
||||
SnackbarProvider,
|
||||
ModalProvider,
|
||||
@@ -14,6 +18,11 @@ import {
|
||||
OHIFModal,
|
||||
} from '@ohif/ui';
|
||||
|
||||
import {
|
||||
LabellingFlowProvider,
|
||||
ContextMenuProvider,
|
||||
} from './appCustomProviders';
|
||||
|
||||
import {
|
||||
CommandsManager,
|
||||
ExtensionManager,
|
||||
@@ -22,8 +31,10 @@ import {
|
||||
createUINotificationService,
|
||||
createUIModalService,
|
||||
createUIDialogService,
|
||||
createUIContextMenuService,
|
||||
createUILabellingFlowService,
|
||||
utils,
|
||||
redux as reduxOHIF
|
||||
redux as reduxOHIF,
|
||||
} from '@ohif/core';
|
||||
|
||||
import i18n from '@ohif/i18n';
|
||||
@@ -46,12 +57,12 @@ import OHIFStandaloneViewer from './OHIFStandaloneViewer';
|
||||
/** Store */
|
||||
import { getActiveContexts } from './store/layout/selectors.js';
|
||||
import store from './store';
|
||||
const { setUserPreferences } = reduxOHIF.actions;
|
||||
|
||||
/** Contexts */
|
||||
import WhiteLabellingContext from './context/WhiteLabellingContext';
|
||||
import UserManagerContext from './context/UserManagerContext';
|
||||
import AppContext from './context/AppContext';
|
||||
const { setUserPreferences } = reduxOHIF.actions;
|
||||
|
||||
/** ~~~~~~~~~~~~~ Application Setup */
|
||||
const commandsManagerConfig = {
|
||||
@@ -63,6 +74,8 @@ const commandsManagerConfig = {
|
||||
const UINotificationService = createUINotificationService();
|
||||
const UIModalService = createUIModalService();
|
||||
const UIDialogService = createUIDialogService();
|
||||
const UIContextMenuService = createUIContextMenuService();
|
||||
const UILabellingFlowService = createUILabellingFlowService();
|
||||
|
||||
/** Managers */
|
||||
const commandsManager = new CommandsManager(commandsManagerConfig);
|
||||
@@ -79,23 +92,25 @@ window.store = store;
|
||||
|
||||
class App extends Component {
|
||||
static propTypes = {
|
||||
routerBasename: PropTypes.string.isRequired,
|
||||
servers: PropTypes.object.isRequired,
|
||||
//
|
||||
oidc: PropTypes.array,
|
||||
whiteLabelling: PropTypes.object,
|
||||
extensions: PropTypes.arrayOf(
|
||||
config: PropTypes.oneOfType([
|
||||
PropTypes.func,
|
||||
PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
})
|
||||
),
|
||||
hotkeys: PropTypes.array,
|
||||
routerBasename: PropTypes.string.isRequired,
|
||||
oidc: PropTypes.array,
|
||||
whiteLabelling: PropTypes.object,
|
||||
extensions: PropTypes.array,
|
||||
}),
|
||||
]).isRequired,
|
||||
defaultExtensions: PropTypes.array,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
whiteLabelling: {},
|
||||
oidc: [],
|
||||
extensions: [],
|
||||
config: {
|
||||
whiteLabelling: {},
|
||||
oidc: [],
|
||||
extensions: [],
|
||||
},
|
||||
defaultExtensions: [],
|
||||
};
|
||||
|
||||
_appConfig;
|
||||
@@ -104,31 +119,59 @@ class App extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._appConfig = props;
|
||||
const { config, defaultExtensions } = props;
|
||||
|
||||
const { servers, extensions, hotkeys, oidc } = props;
|
||||
const appDefaultConfig = {
|
||||
cornerstoneExtensionConfig: {},
|
||||
extensions: [],
|
||||
routerBasename: '/',
|
||||
whiteLabelling: {},
|
||||
};
|
||||
|
||||
this._appConfig = {
|
||||
...appDefaultConfig,
|
||||
...(typeof config === 'function' ? config({ servicesManager }) : config),
|
||||
};
|
||||
|
||||
const {
|
||||
servers,
|
||||
hotkeys,
|
||||
cornerstoneExtensionConfig,
|
||||
extensions,
|
||||
oidc,
|
||||
} = this._appConfig;
|
||||
|
||||
this.initUserManager(oidc);
|
||||
_initServices([UINotificationService, UIModalService, UIDialogService]);
|
||||
_initExtensions(extensions, hotkeys);
|
||||
_initServices([
|
||||
UINotificationService,
|
||||
UIModalService,
|
||||
UIDialogService,
|
||||
UIContextMenuService,
|
||||
UILabellingFlowService,
|
||||
]);
|
||||
_initExtensions(
|
||||
[...defaultExtensions, ...extensions],
|
||||
cornerstoneExtensionConfig
|
||||
);
|
||||
|
||||
/*
|
||||
* Must run after extension commands are registered
|
||||
* if there is no hotkeys from localStorage set up from config.
|
||||
*/
|
||||
_initHotkeys(hotkeys);
|
||||
_initServers(servers);
|
||||
initWebWorkers();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { whiteLabelling, routerBasename } = this.props;
|
||||
const userManager = this._userManager;
|
||||
const config = {
|
||||
appConfig: this._appConfig,
|
||||
};
|
||||
|
||||
if (userManager) {
|
||||
const { whiteLabelling, routerBasename } = this._appConfig;
|
||||
if (this._userManager) {
|
||||
return (
|
||||
<AppContext.Provider value={config}>
|
||||
<AppContext.Provider value={{ appConfig: this._appConfig }}>
|
||||
<Provider store={store}>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<OidcProvider store={store} userManager={userManager}>
|
||||
<UserManagerContext.Provider value={userManager}>
|
||||
<OidcProvider store={store} userManager={this._userManager}>
|
||||
<UserManagerContext.Provider value={this._userManager}>
|
||||
<Router basename={routerBasename}>
|
||||
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
||||
<SnackbarProvider service={UINotificationService}>
|
||||
@@ -137,7 +180,21 @@ class App extends Component {
|
||||
modal={OHIFModal}
|
||||
service={UIModalService}
|
||||
>
|
||||
<OHIFStandaloneViewer userManager={userManager} />
|
||||
<LabellingFlowProvider
|
||||
service={UILabellingFlowService}
|
||||
labellingComponent={LabellingManager}
|
||||
commandsManager={commandsManager}
|
||||
>
|
||||
<ContextMenuProvider
|
||||
service={UIContextMenuService}
|
||||
contextMenuComponent={ToolContextMenu}
|
||||
commandsManager={commandsManager}
|
||||
>
|
||||
<OHIFStandaloneViewer
|
||||
userManager={this._userManager}
|
||||
/>
|
||||
</ContextMenuProvider>
|
||||
</LabellingFlowProvider>
|
||||
</ModalProvider>
|
||||
</DialogProvider>
|
||||
</SnackbarProvider>
|
||||
@@ -152,7 +209,7 @@ class App extends Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<AppContext.Provider value={config}>
|
||||
<AppContext.Provider value={{ appConfig: this._appConfig }}>
|
||||
<Provider store={store}>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<Router basename={routerBasename}>
|
||||
@@ -160,7 +217,19 @@ class App extends Component {
|
||||
<SnackbarProvider service={UINotificationService}>
|
||||
<DialogProvider service={UIDialogService}>
|
||||
<ModalProvider modal={OHIFModal} service={UIModalService}>
|
||||
<OHIFStandaloneViewer />
|
||||
<LabellingFlowProvider
|
||||
service={UILabellingFlowService}
|
||||
labellingComponent={LabellingManager}
|
||||
commandsManager={commandsManager}
|
||||
>
|
||||
<ContextMenuProvider
|
||||
service={UIContextMenuService}
|
||||
contextMenuComponent={ToolContextMenu}
|
||||
commandsManager={commandsManager}
|
||||
>
|
||||
<OHIFStandaloneViewer />
|
||||
</ContextMenuProvider>
|
||||
</LabellingFlowProvider>
|
||||
</ModalProvider>
|
||||
</DialogProvider>
|
||||
</SnackbarProvider>
|
||||
@@ -174,10 +243,10 @@ class App extends Component {
|
||||
|
||||
initUserManager(oidc) {
|
||||
if (oidc && !!oidc.length) {
|
||||
const firstOpenIdClient = this.props.oidc[0];
|
||||
const firstOpenIdClient = this._appConfig.oidc[0];
|
||||
|
||||
const { protocol, host } = window.location;
|
||||
const { routerBasename } = this.props;
|
||||
const { routerBasename } = this._appConfig;
|
||||
const baseUri = `${protocol}//${host}${routerBasename}`;
|
||||
|
||||
const redirect_uri = firstOpenIdClient.redirect_uri || '/callback';
|
||||
@@ -213,22 +282,22 @@ function _initServices(services) {
|
||||
/**
|
||||
* @param
|
||||
*/
|
||||
function _initExtensions(extensions, hotkeys) {
|
||||
const defaultExtensions = [
|
||||
function _initExtensions(extensions, cornerstoneExtensionConfig) {
|
||||
const requiredExtensions = [
|
||||
GenericViewerCommands,
|
||||
OHIFCornerstoneExtension,
|
||||
// WARNING: MUST BE REGISTERED _AFTER_ OHIFCORNERSTONEEXTENSION
|
||||
[OHIFCornerstoneExtension, cornerstoneExtensionConfig],
|
||||
/* WARNING: MUST BE REGISTERED _AFTER_ OHIFCornerstoneExtension */
|
||||
MeasurementsPanel,
|
||||
];
|
||||
const mergedExtensions = defaultExtensions.concat(extensions);
|
||||
const mergedExtensions = requiredExtensions.concat(extensions);
|
||||
extensionManager.registerExtensions(mergedExtensions);
|
||||
}
|
||||
|
||||
function _initHotkeys(hotkeys) {
|
||||
const { hotkeyDefinitions = {} } = store.getState().preferences || {};
|
||||
let updateStore = false;
|
||||
let hotkeysToUse = hotkeyDefinitions;
|
||||
|
||||
// Must run after extension commands are registered
|
||||
// if there is no hotkeys from localStorate set up from config
|
||||
if (!Object.keys(hotkeyDefinitions).length) {
|
||||
hotkeysToUse = hotkeys;
|
||||
updateStore = true;
|
||||
@@ -236,7 +305,8 @@ function _initExtensions(extensions, hotkeys) {
|
||||
|
||||
if (hotkeysToUse) {
|
||||
hotkeysManager.setHotkeys(hotkeysToUse);
|
||||
// set default based on app config
|
||||
|
||||
/* Set hotkeys default based on app config. */
|
||||
hotkeysManager.setDefaultHotKeys(hotkeys);
|
||||
|
||||
if (updateStore) {
|
||||
@@ -264,7 +334,9 @@ function _makeAbsoluteIfNecessary(url, base_url) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// Make sure base_url and url are not duplicating slashes
|
||||
/*
|
||||
* Make sure base_url and url are not duplicating slashes.
|
||||
*/
|
||||
if (base_url[base_url.length - 1] === '/') {
|
||||
base_url = base_url.slice(0, base_url.length - 1);
|
||||
}
|
||||
@@ -272,7 +344,9 @@ function _makeAbsoluteIfNecessary(url, base_url) {
|
||||
return base_url + url;
|
||||
}
|
||||
|
||||
// Only wrap/use hot if in dev
|
||||
/*
|
||||
* Only wrap/use hot if in dev.
|
||||
*/
|
||||
const ExportedApp = process.env.NODE_ENV === 'development' ? hot(App) : App;
|
||||
|
||||
export default ExportedApp;
|
||||
|
||||
Reference in new issue
Block a user