2019-04-19 06:59:46 +02:00
# Extensions
2019-06-02 21:44:01 +02:00
Extensions add new functionality to the viewer by registering one or more
modules. They go one step further than configuration in that they allow us to
inject custom React components, so long as they adhere to the module's
interface. This can be something as simple as adding a new button to the
toolbar, or as complex as a new viewport capable of rendering volumes in 3D.
- [Overview ](#overview )
- [Modules ](#modules )
2019-06-18 21:19:04 +02:00
- [Commands ](#commands )
2019-06-18 21:29:43 +02:00
- [Hotkeys ](#hotkeys )
2019-06-02 21:44:01 +02:00
- [Toolbar ](#toolbar )
- [Panel ](#panel )
2019-06-18 21:19:04 +02:00
- [Viewport ](#viewport )
2019-06-18 21:29:43 +02:00
- [SOP Class Handler ](#sopclasshandler )
2019-04-19 06:59:46 +02:00
## Overview
2019-06-18 21:19:04 +02:00
At a glance, an extension is a javascript object that has an `id` property, and
one or more "module" methods. You can find an abbreviated extension below, or
[view the source][example-ext-src] of our example extension.
2019-04-19 06:59:46 +02:00
```js
2019-06-18 21:19:04 +02:00
export default {
/**
* Only required property. Should be a unique value across all extensions.
*/
id: 'example-extension',
/**
2019-06-18 21:29:43 +02:00
* Registers one or more named commands scoped to a context. Commands are
* the primary means for...
2019-06-18 21:19:04 +02:00
*/
getCommandsModule() {
return {
2019-06-18 21:29:43 +02:00
defaultContext: 'VIEWER'
actions: { ... },
definitions: { ... }
2019-06-18 21:19:04 +02:00
}
},
2019-06-18 21:29:43 +02:00
/**
* Allows you to provide toolbar definitions that will be merged with any
* existing application toolbar configuration. Used to determine which
* buttons should be visible when, their order, what happens when they're
* clicked, etc.
*/
getToolbarModule() {
return {
definitions: [ ... ],
defaultContext: 'ACTIVE_VIEWPORT::CORNERSTONE'
}
}
2019-06-18 22:11:38 +02:00
/**
* Not yet implemented
*/
2019-06-18 21:29:43 +02:00
getPanelModule: () => null,
2019-06-18 21:19:04 +02:00
/**
* Registers a ReactComponent that should be used to render data in a
* Viewport. The first registered viewport is our "default viewport". If
* more than one viewport is registered, we use `SopClassHandlers` to
* determine which viewport should be used.
*/
getViewportModule: () => reactViewportComponent,
2019-04-19 06:59:46 +02:00
/** Provides a whitelist of SOPClassUIDs the viewport is capable of rendering.
* Can modify default behavior for methods like `getDisplaySetFromSeries` */
getSopClassHandler: () => {
id: 'some-other-unique-id',
2019-06-18 21:29:43 +02:00
sopClassUids: [ ... ],
getDisplaySetFromSeries: (series, study, dicomWebClient, authorizationHeaders) => { ... }
2019-06-18 21:19:04 +02:00
},
2019-04-19 06:59:46 +02:00
}
```
### Modules
2019-06-18 22:11:38 +02:00
There are a few different module types. Each module type allows us to extend the
viewer in a different way, and provides a consistent API for us to do so. You
can find a full list of the different types of modules
[`in ohif-core`][module-types]. Information on each type of module, it's API,
and how we determine when/where it should be used is included below.
> NOTE: Modifying the extensions/modules registered to the OHIF Viewer currently
> requires us to import and pass extensions to the ExtensionManager in
> `src/App.js`, then rebuild the application. Long-term, we intend to make it
> possible to accomplish this without a build step.
#### Commands
2019-06-19 03:09:08 +02:00
The Commands Module allows us to register one or more commands scoped to
specific contexts. Commands can be run by [hotkeys][#], [toolbar buttons][#],
and any registered custom react component (like a [viewport][#] or [panel][#]).
Here is a simple example commands module:
```js
{
getCommandsModule() {
return {
actions: {
speak: ({ viewports, words }) => {
console.log(viewports, words);
},
},
definitions: {
rotateViewportCW: {
commandFn: actions.rotateViewport,
storeContexts: ['viewports'],
options: { rotation: 90 }
},
rotateViewportCCW: {
commandFn: actions.rotateViewport,
storeContexts: ['viewports'],
options: { rotation: -90 },
context: 'ACTIVE_VIEWER::CORNERSTONE'
},
},
defaultContext: 'VIEWER'
}
}
}
```
2019-04-19 06:59:46 +02:00
#### Viewport
2019-06-02 21:44:01 +02:00
An extension can register a Viewport Module by providing a `getViewportModule()`
method that returns a React Component. The React component will receive the
following props:
2019-04-19 06:59:46 +02:00
```js
2019-04-19 16:45:40 +02:00
children: PropTypes.arrayOf(PropTypes.element)
studies: PropTypes.object,
displaySet: PropTypes.object,
viewportData: PropTypes.object, // { studies, displaySet }
viewportIndex: PropTypes.number,
children: PropTypes.node,
customProps: PropTypes.object
2019-04-19 06:59:46 +02:00
```
2019-06-02 21:44:01 +02:00
Viewport components are managed by the `LayoutManager` . Which Viewport component
is used depends on:
2019-04-19 16:45:40 +02:00
- The Layout Configuration
- Registered SopClassHandlers
- The SopClassUID for visible/selected datasets
2019-04-19 18:41:25 +02:00

2019-04-25 21:31:01 +02:00
2019-04-19 16:45:40 +02:00
< center > < i > An example of three Viewports< / i > < / center >
2019-06-02 21:44:01 +02:00
For a complete example implementation,
[check out the OHIFCornerstoneViewport ](https://github.com/OHIF/Viewers/blob/react/extensions/ohif-cornerstone-extension/src/OHIFCornerstoneViewport.js ).
2019-04-19 16:45:40 +02:00
2019-04-19 06:59:46 +02:00
#### Toolbar
2019-06-02 21:44:01 +02:00
An extension can register a Toolbar Module by providing a `getToolbarModule()`
method that returns a React Component. The component does not receive any props.
If you want to modify or react to state, you will need to connect to the redux
store.
2019-04-19 18:41:25 +02:00

2019-04-25 21:31:01 +02:00
2019-04-19 18:41:25 +02:00
< center > < i > A toolbar extension example< / i > < / center >
Toolbar components are rendered in the `ToolbarRow` component.
2019-06-02 21:44:01 +02:00
For a complete example implementation,
[check out the OHIFCornerstoneViewport's Toolbar Module ](https://github.com/OHIF/Viewers/blob/react/extensions/ohif-cornerstone-extension/src/ToolbarModule.js ).
2019-04-19 06:59:46 +02:00
#### SopClassHandler
...
#### Panel
2019-04-19 18:41:25 +02:00
> The panel module is not yet in use.
2019-04-19 06:59:46 +02:00
2019-06-02 21:44:01 +02:00
#### Hotkeys
...
2019-04-19 06:59:46 +02:00
### Registering Extensions
2019-06-02 21:44:01 +02:00
Extensions are registered for the application at startup. The
`ExtensionManager` , exposed by `ohif-core` , registers a list of extensions with
our application's store. Each module provided by the extension becomes available
via `state.plugins.availablePlugins` , and consists of three parts: id, type
([PLUGIN_TYPE](https://github.com/OHIF/ohif-core/blob/43c08a29eff3fb646a0e83a03a236ddd84f4a6e8/src/plugins.js#L1-L6)),
and the return value of the module method.
2019-04-19 06:59:46 +02:00
2019-06-02 21:44:01 +02:00
In a future version, we will likely expose a way to provide the extensions you
would like included at startup.
2019-04-19 06:59:46 +02:00
_app.js_
2019-04-19 16:45:40 +02:00
2019-04-19 06:59:46 +02:00
```js
2019-08-13 21:36:46 +02:00
import { createStore, combineReducers } from "redux";
import OHIF from "@ohif/core";
import OHIFCornerstoneExtension from "ohif-cornerstone-extension";
2019-04-19 06:59:46 +02:00
const combined = combineReducers(OHIF.redux.reducers);
const store = createStore(combined);
2019-04-25 21:31:01 +02:00
const extensions = [new OHIFCornerstoneExtension()];
2019-04-19 06:59:46 +02:00
// Dispatches the `addPlugin` action to the store
// Adding extension modules to `state.plugins.availablePlugins`
ExtensionManager.registerExtensions(store, extensions);
```
## OHIF Maintained Extensions
2019-06-02 21:44:01 +02:00
A small number of powerful extensions for popular use cases are maintained by
OHIF. They're co-located in the
[`OHIF/Viewers` ](https://github.com/OHIF/Viewers/tree/react/ ) repository, in the
top level [`extensions/` ](https://github.com/OHIF/Viewers/tree/react/extensions )
directory.
2019-04-19 06:59:46 +02:00
{% include "./_maintained-extensions-table.md" %}
2019-06-18 21:19:04 +02:00
<!--
Links
-->
<!-- prettier - ignore - start -->
[example-ext-src]: https://github.com/OHIF/Viewers/blob/master/extensions/_ohif-example-extension/src/index.js)
2019-06-18 22:11:38 +02:00
[module-types]: https://github.com/OHIF/ohif-core/blob/43c08a29eff3fb646a0e83a03a236ddd84f4a6e8/src/plugins.js#L1-L6
2019-06-18 21:19:04 +02:00
<!-- prettier - ignore - end -->