diff --git a/.webpack/webpack.base.js b/.webpack/webpack.base.js
index 16663b355..40249f39b 100644
--- a/.webpack/webpack.base.js
+++ b/.webpack/webpack.base.js
@@ -34,7 +34,7 @@ module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
const config = {
mode: isProdBuild ? 'production' : 'development',
- devtool: isProdBuild ? 'source-map' : 'eval',
+ devtool: isProdBuild ? 'source-map' : 'eval-source-map',
entry: {
app: `${SRC_DIR}/index.js`,
},
diff --git a/extensions/cornerstone/src/OHIFCornerstoneViewport.js b/extensions/cornerstone/src/OHIFCornerstoneViewport.js
index c866e1716..f1dd4197c 100644
--- a/extensions/cornerstone/src/OHIFCornerstoneViewport.js
+++ b/extensions/cornerstone/src/OHIFCornerstoneViewport.js
@@ -4,14 +4,21 @@ import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cornerstone-tools';
import CornerstoneViewport from 'react-cornerstone-viewport';
import OHIF from '@ohif/core';
-import ViewportLoadingIndicator from './ViewportLoadingIndicator';
-import setCornerstoneMeasurementActive from './_shared/setCornerstoneMeasurementActive';
-import ViewportOverlay from './ViewportOverlay';
-
import { useCine, useViewportGrid } from '@ohif/ui';
+import ViewportLoadingIndicator from './ViewportLoadingIndicator';
+import setCornerstoneMeasurementActive from './_shared/setCornerstoneMeasurementActive';
+import setActiveAndPassiveToolsForElement from './_shared/setActiveAndPassiveToolsForElement';
+import getTools from './_shared/getTools';
+
+import ViewportOverlay from './ViewportOverlay';
+
const scrollToIndex = cornerstoneTools.importInternal('util/scrollToIndex');
+const BaseAnnotationTool = cornerstoneTools.importInternal(
+ 'base/BaseAnnotationTool'
+);
+
const { StackManager } = OHIF.utils;
function OHIFCornerstoneViewport({
@@ -45,6 +52,42 @@ function OHIFCornerstoneViewport({
element.addEventListener(cornerstone.EVENTS.IMAGE_RENDERED, handler);
};
+ const defaultOnElementEnabled = evt => {
+ const eventData = evt.detail;
+ const targetElement = eventData.element;
+ const tools = getTools();
+ const toolAlias = ToolBarService.state.primaryToolId;
+
+ // Activate appropriate tool bindings for element
+ setActiveAndPassiveToolsForElement(targetElement, tools);
+ cornerstoneTools.setToolActiveForElement(targetElement, toolAlias, {
+ mouseButtonMask: 1,
+ });
+
+ // Set dashed, based on tracking, for this viewport
+ const allTools = cornerstoneTools.store.state.tools;
+ const toolsForElement = allTools.filter(
+ tool => tool.element === targetElement
+ );
+
+ toolsForElement.forEach(tool => {
+ if (tool instanceof BaseAnnotationTool) {
+ const configuration = tool.configuration;
+
+ configuration.renderDashed = true;
+
+ tool.configuration = configuration;
+ }
+ });
+
+ // Update image after setting tool config
+ const enabledElement = cornerstone.getEnabledElement(targetElement);
+
+ if (enabledElement.image) {
+ cornerstone.updateImage(targetElement);
+ }
+ };
+
useEffect(() => {
isMounted.current = true;
return () => {
@@ -174,7 +217,9 @@ function OHIFCornerstoneViewport({
return (
Our long-term plan is to make OHIF-v3 capable of installing extensions from
+> `npm` with a command line. Until then, please use the instructions below to
+> manually install extensions.
+
+## Installing an Extension
+
+### 1) Extension Files Copy
+
+We use a [Template Extension](https://github.com/OHIF/extension-template)
+repository to describe the necessary steps to use a new extension. You can use
+this repository as a starting point to create your own extension.
+
+As you can see in the extension code base, folders structure are similar to the
+OHIF-maintained extensions. Let's look at our `Template Extension`:
+
+- `src/index.js`: The most important file in any extension. This file is where
+ extensions' authors hav defined the extension modules, lifecycle hooks, and
+ other configurations.
+
+For instance the `Template Extension` has the following modules which will be
+registered in OHIF by [Extension Manager](./extension.md)
+
+Each extension has an ID which is used to register the extension in OHIF. For
+instance, for the `Template Extension`, the extension ID is
+`extension.template`.
+
+```js {2} title="templateExtension/src/index.js"
+export default {
+ id: 'extension.template',
+ getPanelModule,
+ getCommandsModule,
+};
+```
+
+#### Package.json
+
+Extension package name is defined in the `package.json` file. The `package.json`
+file is a JSON file that defines the extension name, version, and dependencies.
+For instance for the Template extension, the `package.json` file looks like
+this:
+
+```js {2} title="templateExtension/package.json"
+{
+ "name": "@ohif/extension-template",
+ "version": "1.0.0",
+ "description": "A template extension to show extension installation",
+ ...
+}
+```
+
+Note 1: We will use the `@ohif/extension-template` inside OHIF to let OHIF know
+about existence of this extension.
+
+Note 2: You don't need to use the `@ohif` scope for your extensions. You can use
+any scope you want.
+
+Note 3: Although folders names are not important and the `package.json` file
+contains the mode name, we recommend to use the same name as the folder name.
+
+
+
+### 2) Configuring OHIF
+
+There are couple of places inside OHIF which we need to modify in order to add
+the extension. The following lines of code should be added to the OHIF:
+
+#### Viewer's package.json
+
+```js {8} titl="platform/viewer/package.json"
+/* ... */
+"dependencies": {
+ /* ... */
+ "@babel/runtime": "7.7.6",
+ "@ohif/core": "^2.5.1",
+ "@ohif/extension-cornerstone": "^2.4.0",
+ "@ohif/extension-measurement-tracking": "^0.0.1",
+ "@ohif/extension-template": "^0.0.1",
+ /* ... */
+}
+```
+
+#### index.js
+
+```js {4,13} title="platform/viewer/src/index.js"
+/* ... */
+import OHIFMeasurementTrackingExtension from '@ohif/extension-measurement-tracking';
+import OHIFDICOMSRExtension from '@ohif/extension-dicom-sr';
+import OHIFTemplateExtension from '@ohif/extension-template';
+
+const appProps = {
+ config: window ? window.config : {},
+ defaultExtensions: [
+ OHIFDefaultExtension,
+ OHIFCornerstoneExtension,
+ OHIFMeasurementTrackingExtension,
+ OHIFDICOMSRExtension,
+ OHIFTemplateExtension,
+ ],
+};
+/* ... */
+```
+
+After you followed the above steps, you should run `yarn install` in the root
+folder of the OHIF repository to install the registered extension.
+
+Now you have added the extension to the OHIF, and its modules (layout, commands,
+panels, toolbars, hangingProtocols, etc.) are made available to the OHIF
+`modes`. Read more on how to consume extensions
+[here](../modes/index.md#consuming-extensions)
+
+Congrats! 🎉
diff --git a/platform/docs/docs/platform/modes/installation.md b/platform/docs/docs/platform/modes/installation.md
new file mode 100644
index 000000000..74a627564
--- /dev/null
+++ b/platform/docs/docs/platform/modes/installation.md
@@ -0,0 +1,208 @@
+---
+sidebar_position: 5
+sidebar_label: Installation
+---
+
+# Mode: Installation
+
+OHIF-v3 provides the ability to utilize external modes and extensions. In this
+document we will describe how to add/install external modes.
+
+> Our long-term plan is to make OHIF-v3 capable of installing modes at runtime,
+> however in the meantime, please use the instructions below to manually install
+> modes and their extensions.
+
+## Installing a Mode
+
+### 1) Mode Files Copy
+
+We use a [Template Mode](https://github.com/OHIF/mode-template) repository to
+demonstrate how to install an external mode. This repository also includes all
+the files required to create a new mode. You can use this repository as a
+starting point to create your own mode.
+
+As you can see in the Template mode
+[code base](https://github.com/OHIF/mode-template), folders structure are
+similar to the OHIF-maintained modes. Let's have more detailed look at the
+structure of the `Template mode`:
+
+- `src/index.js`: The most important file in any mode. This file is where modes'
+ authors hav defined the mode configurations such as:
+ - The layout and the panels for left and right side.
+ - LifeCycle hooks such as `onModeEnter` and `onModeExit`
+- Other files/folders/configs: .webpack, LICENSE, README.md, babel.config.js
+
+Note: It is highly recommended to use the `Template Mode` as a starting point
+for your own mode. This way, you can easily reuse the necessary files and
+folders.
+
+#### Package.json
+
+Mode name is defined in the `package.json` file. The `package.json` file is a
+JSON file that defines the mode name, version, and dependencies. For instance
+for the Template mode, the `package.json` file looks like this:
+
+```js {2} title="templateMode/package.json"
+{
+ "name": "@ohif/mode-template",
+ "version": "0.0.1",
+ "description": "A template mode for installation demonstration",
+ ...
+}
+```
+
+Note 1: We will use the `@ohif/mode-template` inside OHIF to let OHIF know about
+existence of this mode.
+
+Note 2: You don't need to use the `@ohif` scope for your modes/extensions. You
+can use any scope you want or none at all.
+
+Note 3: Although folders names are not important and the `package.json` file
+contains the mode name, we recommend to use the same name for the folder name.
+
+
+
+### 2) Configuring OHIF
+
+In order to install/register the mode, we must make changes to a few areas
+inside OHIF. The OHIF should be updated using the following lines of code:
+
+#### Viewer's package.json
+
+```js {6} titl="platform/viewer/package.json"
+/* ... */
+"dependencies": {
+ /* ... */
+ "@ohif/i18n": "^0.52.8",
+ "@ohif/mode-longitudinal": "^0.0.1",
+ "@ohif/mode-template": "^0.0.1",
+ "@ohif/ui": "^2.0.0",
+ "@types/react": "^16.0.0",
+ /* ... */
+}
+```
+
+#### App.jsx
+
+```js {3} title="platform/viewer/src/App.jsx"
+/* ... */
+import '@ohif/mode-longitudinal';
+import '@ohif/mode-template';
+/* ... */
+```
+
+#### appInit.js
+
+```js {4} title="platform/viewer/src/appInit.js"
+/* ... */
+if (!appConfig.modes.length) {
+ appConfig.modes.push(window.longitudinalMode);
+ appConfig.modes.push(window.templateMode);
+}
+/* ... */
+```
+
+Note that we are assigning mode configuration objects from the `window` object;
+therefore, we should use the reference to the name of the mode which were
+defined in the last line of `src/index.js` file in mode configuration
+
+```js {8} title="templateMode/src/index.js"
+/* ... */
+export default function mode({ modeConfiguration }) {
+ return {
+ /** */
+ };
+}
+
+window.templateMode = mode({});
+```
+
+### Required Extensions for a Mode
+
+Some modes require external extensions to be installed. For instance, the
+`@ohif/mode-longitudinal` mode requires the `@ohif/cornerstone` extension to be
+registered/installed in OHIF which is available in the OHIF-v3 repository.
+
+How do we know that a mode requires an extension? (Until we have a more proper
+dependency management for modes and extensions) you can take a look inside the
+mode itself. Mode is a configuration file that defines the layout
+(layoutModule), panels (panelModule), viewport (viewportModule), and tools
+(commands) that are used to create an application at a given route. By looking
+inside the mode configuration file (`index.js`), you can see which extensions
+are required by the mode in the `extensions` property.
+
+```js {12-16} title="platform/viewer/src/appInit.js"
+export default function mode({ modeConfiguration }) {
+ return {
+ id: 'template',
+ displayName: 'Template Mode',
+ /** ... */
+
+ routes: [
+ {
+ /** ... */
+ },
+ ],
+ extensions: [
+ 'extension.template',
+ 'org.ohif.default',
+ 'org.ohif.cornerstone',
+ ],
+ /** ... */
+ };
+}
+```
+
+As seen, our `Template Mode` requires the `org.ohif.default`,
+`org.ohif.cornerstone` and `extension.template` extensions.
+
+> Note: Currently extensions dependencies are not handled by OHIF from the
+> `extensions` property. We will be adding this feature in the future.
+
+In addition to the `extensions` property, the `mode` configuration object also
+has the reference for each module that is used. For instance, the `index.js`
+file in the `@ohif/mode-template` mode looks like this:
+
+```js {10} title="clockMode/src/index.js"
+// ....
+routes: [
+ {
+ path: "template",
+ layoutTemplate: ({ location, servicesManager }) => {
+ return {
+ id: ohif.layout,
+ props: {
+ leftPanels: [],
+ rightPanels: ["extension.template.panelModule.clockPanel"],
+ viewports: [
+ {
+ namespace: "org.ohif.cornerstone.viewportModule.cornerstone",
+ displaySetsToDisplay: ["org.ohif.default.sopClassHandlerModule.stack"],
+ },
+ ],
+ },
+ };
+ },
+ },
+],
+// ....
+```
+
+As seen, the right panel is defined as
+`"extension.template.panelModule.clockPanel"` which means that the
+`@ohif/mode-template` mode requires the `extension.template`. You can read more
+about installing extensions in the
+[Extension Installation](../extensions/installation.md)
+
+> Additionally you can check the commands that the toolbar buttons will execute
+> in the `toolbarButtons` and see if any of them requires an extension.
+
+After you installed the extension, you need to run `yarn install` in the root
+folder of the OHIF repository to install the registered extension and modes.
+
+Running `yarn dev` will then start the application with the installed mode, by
+navigating to the `/template` route (e.g.,
+http://localhost:3000/template?StudyInstanceUIDs=1.3.6.1.4.1.14519.5.2.1.2744.7002.150059977302243314164020079415)
+you can see the clock panel. Congrats! 🎉
+
+
diff --git a/platform/docs/package.json b/platform/docs/package.json
index 7f1dfdf40..f352b2a7b 100644
--- a/platform/docs/package.json
+++ b/platform/docs/package.json
@@ -15,6 +15,7 @@
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start --port 8001",
+ "dev": "docusaurus start --port 8001",
"docs": "docusaurus start --port 8001",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
diff --git a/platform/docs/src/css/custom.css b/platform/docs/src/css/custom.css
index ded58bf18..a7517bbd5 100644
--- a/platform/docs/src/css/custom.css
+++ b/platform/docs/src/css/custom.css
@@ -207,7 +207,6 @@ article header h3 {
--ifm-button-background-color: #e8f7f7;
}
-
.admonition-icon svg {
fill: #0151d9;
}
@@ -225,6 +224,19 @@ article header h3 {
margin-top: 0;
}
+.docusaurus-highlight-code-line {
+ background-color: rgb(206, 208, 211);
+ display: block;
+ margin: 0 calc(-1 * var(--ifm-pre-padding));
+ padding: 0 var(--ifm-pre-padding);
+}
+
+/* If you have a different syntax highlighting theme for dark mode. */
+html[data-theme='dark'] .docusaurus-highlight-code-line {
+ /* Color which works with dark mode syntax highlighting theme */
+ background-color: rgb(100, 100, 100);
+}
+
/* .DocSearch {
display: none;
} */