diff --git a/README.md b/README.md index c8b295b95..13d5b095e 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,167 @@ -# Viewers -This repo contains the OHIF DICOM Viewer and Lesion Tracker, and various shared meteor packages. + + +
+

ohif-viewer

+

ohif-viewer is a zero-footprint medical image viewer. It is a configurable and exstensible progressive web application with out of the box support for PACS like orthanc, dcm4che, and Google's Healthcare API.

+
-Documentation is available here: http://docs.ohif.org/ -### Demos -[OHIF Viewer](http://viewer.ohif.org/) - A general-purpose radiology viewer with a variety of tools exposed. +
+ Read The Docs | + Edit the docs +
-[Lesion Tracker](http://lesiontracker.ohif.org/) - A prototype viewer focused on oncology metrics. -Community ---------- +
-Have questions? Try posting on our [google groups forum](https://groups.google.com/forum/#!forum/cornerstone-platform). +[![CircleCI][circleci-image]][circleci-url] +[![codecov][codecov-image]][codecov-url] +[![All Contributors][all-contributors-image]][contributing-url] +[![code style: prettier][prettier-image]][prettier-url] +[![semantic-release][semantic-image]][semantic-url] -### Docker usage -Following the instructions below, the docker image will listen for DICOM connections on port 4242, and for web traffic on port 8042. The default username for the web interface is `orthanc`, and the password is `orthanc`. -#### Temporary data storage -```` -docker run --rm -p 4242:4242 -p 8042:8042 jodogne/orthanc-plugins -```` +[![NPM version][npm-version-image]][npm-url] +[![NPM downloads][npm-downloads-image]][npm-url] +[![MIT License][license-image]][license-url] + + -#### Persistent data storage -1. Create a persistant data volume for Orthanc to use +## Why? - ```` - docker create --name sampledata -v /sampledata jodogne/orthanc-plugins - ```` - - **Note: On Windows, you need to use an absolute path for the data volume, like so:** - - ```` - docker create --name sampledata -v '//C/Users/erik/sampledata' jodogne/orthanc-plugins - ```` +Building a web based medical imaging viewer from scratch is time intensive, hard to get right, and expensive. Instead of re-inventing the wheel, you can use the OHIF Viewer as a rock solid platform to build on top of. The Viewer is a [React][react-url] [Progressive Web Application][pwa-url] that can be embedded in existing applications via it's [packaged source (ohif-viewer)][ohif-viewer-url] or hosted stand-alone. The Viewer exposes [configuration][configuration-url] and [extensions][extensions-url] to support workflow customization and advanced functionality at common integration points. -2. Run Orthanc from Docker with the data volume attached +If you're interested in using the OHIF Viewer, but you're not sure it supports your use case [check out our docs](https://deploy-preview-398--ohif.netlify.com/). Still not sure, or you would like to propose new features? Don't hesitate to [create an issue](https://github.com/OHIF/Viewers/issues) or open a pull request ^_^ - ```` - docker run --volumes-from sampledata -p 4242:4242 -p 8042:8042 jodogne/orthanc-plugins - ```` -3. Upload your data and it will be persisted +## Getting Started + +This readme is specific to testing and developing locally. If you're more interested in production deployment strategies, [you can check out our documentation on publishing](https://deploy-preview-398--ohif.netlify.com/). + +Want to play around before you dig in? [Check out our LIVE Demo](https://viewer.ohif.org/) + +### Setup + +_Requirements:_ + +- [NodeJS & NPM](https://nodejs.org/en/download/) +- [Yarn](https://yarnpkg.com/lang/en/docs/install/) + +_Steps:_ + +1. Fork this repository +2. Clone your forked repository (your `origin`) + - `git clone git@github.com:YOUR_GITHUB_USERNAME/Viewers.git` +3. Add `OHIF/Viewers` as a `remote` repository (the `upstream`) + - `git remote add upstream git@github.com:OHIF/Viewers.git` + + +### Developing Locally + +In your cloned repository's root folder, run: + +```js +// Restore dependencies +yarn install + +// Stands up local server to host Viewer. +// Viewer connects to our public cloud PACS by default +yarn start +``` + +For more advanced local development scenarios, like using your own locally hosted PACS and test data, [check out our Essential: Getting Started](https://deploy-preview-398--ohif.netlify.com/essentials/getting-started.html) guide. + + +### Contributing + +> Large portions of the Viewer's functionality are maintained in other repositories. To get a better understanding of the Viewer's architecture and "where things live", read [our docs on the Viewer's architecture](https://deploy-preview-398--ohif.netlify.com/advanced/architecture.html#diagram) + +It is notoriously difficult to setup multiple dependent repositories for +end-to-end testing and development. That's why we recommend writing and running +unit tests when adding and modifying features. This allows us to program in isolation without a complex setup, and has the added benefit of producing well-tested business logic. + +1. Clone this repository +2. Navigate to the project directory, and `yarn install` +3. To begin making changes, `yarn run dev` +4. To commit changes, run `yarn run cm` + +When creating tests, place the test file "next to" the file you're testing. +[For example](https://github.com/OHIF/Viewers/blob/react/src/index.test.js): + +```js +// File +index.js + +// Test for file +index.test.js +``` + +As you add and modify code, `jest` will watch for uncommitted changes and run +your tests, reporting the results to your terminal. Make a pull request with +your changes to `master`, and a core team member will review your work. If you +have any questions, please don't hesitate to reach out via a GitHub issue. + +## Issues + +_Looking to contribute? Look for the [Good First Issue][good-first-issue] +label._ + +### 🐛 Bugs + +Please file an issue for bugs, missing documentation, or unexpected behavior. + +[**See Bugs**][bugs] + +### 💡 Feature Requests + +Please file an issue to suggest new features. Vote on feature requests by adding +a 👍. This helps maintainers prioritize what to work on. + +[**See Feature Requests**][requests-feature] + +### ❓ Questions + +For questions related to using the library, please visit our support community, +or file an issue on GitHub. + +[Google Group][google-group] + +## License + +MIT © [OHIF](https://github.com/OHIF) + + + + + +[all-contributors-image]: https://img.shields.io/badge/all_contributors-0-orange.svg?style=flat-square +[contributing-url]: https://github.com/OHIF/Viewers/blob/react/CONTRIBUTING.md +[circleci-image]: https://circleci.com/gh/OHIF/Viewers.svg?style=svg +[circleci-url]: https://circleci.com/gh/OHIF/Viewers +[codecov-image]: https://codecov.io/gh/OHIF/Viewers/branch/react/graph/badge.svg +[codecov-url]: https://codecov.io/gh/OHIF/Viewers +[prettier-image]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square +[prettier-url]: https://github.com/prettier/prettier +[semantic-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg +[semantic-url]: https://github.com/semantic-release/semantic-release + +[npm-url]: https://npmjs.org/package/ohif-viewer +[npm-downloads-image]: https://img.shields.io/npm/dm/ohif-viewer.svg?style=flat-square +[npm-version-image]: https://img.shields.io/npm/v/ohif-viewer.svg?style=flat-square +[license-image]: https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square +[license-url]: LICENSE + +[react-url]: https://reactjs.org/ +[pwa-url]: https://developers.google.com/web/progressive-web-apps/ +[ohif-viewer-url]: https://www.npmjs.com/package/ohif-viewer +[configuration-url]: https://deploy-preview-398--ohif.netlify.com/essentials/configuration.html +[extensions-url]: https://deploy-preview-398--ohif.netlify.com/advanced/extensions.html + +[react-viewer]: https://github.com/OHIF/Viewers/tree/react + +[bugs]: https://github.com/OHIF/Viewers/labels/bug +[requests-feature]: https://github.com/OHIF/Viewers/labels/enhancement +[good-first-issue]: https://github.com/OHIF/Viewers/labels/good%20first%20issue +[google-group]: https://groups.google.com/forum/#!forum/cornerstone-platform + diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..1d31fbe36 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,46 @@ +# How To: Documentation Step-by-Step + +We use [GitBook](https://www.gitbook.com/) to create our documentation. It primarily uses markdown, html, css, js, misc. plugins, and configuration to generate high quality, easy to read, and easy to maintain documentation. + +## Getting Started + +_Requirements:_ + +Make sure you have the [`gitbook-cli`](https://www.npmjs.com/package/gitbook-cli) installed globally: + +> `npm install -g gitbook-cli` + +### Editing and Previewing Changes + +Currently, you can only edit and preview a single "book" at a time. We maintain one "book" per API major version. You can find each version's book at: + +_Past Versions:_ + +- Template: + - `/docs/v` +- Examples: + - `/docs/v1` + - `/docs/v2` + +_Latest Version:_ + +The latest version will always be located in `/docs/latest` + +_Live Preview:_ + +In your terminal / command prompt: + +```bash +cd /docs/latest +gitbook install +gitbook serve +``` + +Which should generate output like: + +> starting server... +> serving book on http://localhost:4000 + +Navigating to the the provided URL will show a preview of what the generated book should look like. Any edits you make to the book's markdown files should automatically update in your browser. + +### Publishing diff --git a/docs/latest/SUMMARY.md b/docs/latest/SUMMARY.md index 58aabd9e2..9dc674294 100644 --- a/docs/latest/SUMMARY.md +++ b/docs/latest/SUMMARY.md @@ -2,8 +2,27 @@ - Essentials - [Getting Started](essentials/getting-started.md) + - [Configuration](essentials/configuration.md) - [Troubleshooting](essentials/troubleshooting.md) - [Architecture](architecture/index.md) + - Overview +- Deployment + +--- +- [Advanced](advanced/index.md) +- [Architecture](advanced/architecture.md) + - [Overview](advanced/architecture.md#overview) + - [Business Logic](advanced/architecture.md#business-logic) + - [Component Library](advanced/architecture.md#react-component-library) + - [Extensions](advanced/architecture.md#misc-extensions) + - [Diagram](advanced/architecture.md#diagram) + - [Common Questions](advanced/architecture.md#common-questions) +- [Extensions](advanced/extensions.md) + - [Overview](advanced/extensions.md#overview) + - [Modules](advanced/extensions.md#modules) + - [Registering](advanced/extensions.md#registering-extensions) + - [OHIF Maintained](advanced/extensions.md#ohif-maintained-extensions) + - Connecting to Image Archives - [DICOM Web](connecting-to-image-archives/dicomweb.md) diff --git a/docs/latest/advanced/_maintained-extensions-table.md b/docs/latest/advanced/_maintained-extensions-table.md new file mode 100644 index 000000000..41c0c60f7 --- /dev/null +++ b/docs/latest/advanced/_maintained-extensions-table.md @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ExtensionDescriptionModules
+ + Cornerstone + + + A viewport powered by cornerstone.js. Adds support for 2D DICOM rendering and manipulation, as well as support for the tools features in cornerstone-tools. Also adds "CINE Dialog" to the Toolbar. + Viewport, Toolbar
+ + VTK.js + + + A viewport powered by vtk.js. Adds support for volume renderings and advanced features like MPR. Also adds "3D Rotate" to the Toolbar. + Viewport, Toolbar
+ HTML + + Renders text and HTML content for specific SopClassUIDs. + Viewport, SopClassHandler
+ PDF + + Renders PDFs for a specific SopClassUID. + Viewport, SopClassHandler
+ Microscopy + + Renders Microscopy images for a specific SopClassUID. + Viewport, SopClassHandler
\ No newline at end of file diff --git a/docs/latest/advanced/architecture.md b/docs/latest/advanced/architecture.md new file mode 100644 index 000000000..e91372c4b --- /dev/null +++ b/docs/latest/advanced/architecture.md @@ -0,0 +1,65 @@ +# Architecture + +Looking to extend your instance of the OHIF Viewer? Want learn how to reuse _a portion_ of the Viewer in your own application? Or maybe you want to get involved and draft or suggest a new feature? Regardless, you're in the right place! + +The OHIF Viewer aims to be decoupled, configurable, and extensible; while this allows our code to be used in more ways, it also increases complexity. Below, we aim to demistify that complexity by providing insight into how our Viewer is architected, and the role each of it's dependent libraries plays. + +## Overview + +The [`OHIF/Viewers`](https://github.com/OHIF/Viewers/tree/react) repository contains the source code for the OHIF Medical Imaging Viewer. It is effectively a React [progressive web app](https://developers.google.com/web/progressive-web-apps/) (PWA) that combines the business logic housed in [`OHIF/ohif-core`](https://github.com/OHIF/ohif-core) and the components in our React Component library [`OHIF/react-viewerbase`](https://github.com/OHIF/react-viewerbase). It provides customization for common use cases through [configuration](../essentials/configuration.md) and for adding functionality via [extensions](./extensions.md). + + +### Business Logic + +Our goal is to maintain the majority of our business logic in [`OHIF/ohif-core`](https://github.com/OHIF/ohif-core). `ohif-core` offers pre-packaged solutions for features common to Web-based medical imaging viewers. For example: + +- Hotkeys +- DICOM Web +- Hanging Protocols +- Managing a study's measurements +- Managing a study's DICOM metadata +- A flexible pattern for extensions +- [And many others](https://github.com/OHIF/ohif-core/blob/master/src/index.js#L49-L69) + +It does this while remaining decoupled from any particular view library or +rendering logic. While we use it to power our React Viewer, it can be used with Vue, React, Vanilla JS, or any number of other frameworks. + + +### React Component Library + +[`OHIF/react-viewerbase`](https://github.com/OHIF/react-viewerbase) is a React Component library that contains the reusable components that power the OHIF Viewer. It allows us to build, compose, and test components in isolation; easing the development process by reducing the need to stand-up a local PACS with test case data. + +[Check out our component library!](https://react.ohif.org/) + + +### Misc. Extensions + +Want to add custom logic or UI Components to the OHIF Viewer, but don't want to maintain a fork? We expose common integration points via [extensions](./extensions.md) to make that possible. For a list of extensions maintained by OHIF, [check out this helpful table](./extensions.html#ohif-maintained-extensions). + + +If you find yourself thinking "I wish the Viewer could do X", and you can't accomplish it with an extension today, create a GitHub issue! We're actively looking for ways to improve our extensibility ^_^ + +[Click here to read more about extensions!](./extensions.md) + +### Diagram + +This diagram is a conceptual illustration of how the Viewer is architected. + +0. (optional) `extensions` can be registered with `ohif-core`'s extension manager +1. `ohif-core` provides bussiness logic and a way for `viewer` to access registered extensions +2. The `viewer` composes and provides data to components from our component library (`react-viewerbase`) +3. The `viewer` can be built and served as a stand-alone PWA, or as an embeddable package ([`ohif-viewer`](https://www.npmjs.com/package/ohif-viewer)) + +![Architecture Diagram](../assets/img/architecture-diagram.png) + +
architecture diagram
+ +## Common Questions + +> When should I use the packaged source `ohif-viewer` versus building a PWA from the source? + +... + +> Can I create my own Viewer using Vue.js or Angular.js? + +You can, but you will not be able to leverage as much of the existing code and components. `ohif-core` could still be used for business logic, and to provide a model for extensions. `react-viewerbase` would then become a guide for the components you would need to recreate. \ No newline at end of file diff --git a/docs/latest/advanced/extensions.md b/docs/latest/advanced/extensions.md new file mode 100644 index 000000000..5303dac87 --- /dev/null +++ b/docs/latest/advanced/extensions.md @@ -0,0 +1,111 @@ +# Extensions + +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 + +At a glance, an extension is a class or object that has a `getExtensionId()` method, and one or more "module" methods. You can find an abbreviated extension below, or [view the source](https://github.com/OHIF/Viewers/blob/react/extensions/ohif-cornerstone-extension/src/OHIFCornerstoneExtension.js#L32-L65) of our `cornerstone` viewport extension. + +```js +class myCustomExtension { + + /** Required */ + getExtensionId: () => 'my-extension-id'; + + /** React component that receives props from ConnectLayoutManager + * If more than one viewport module is registered, SopClassHandler + * is used to help determine which component is used */ + getViewportModule: () => reactViewportComponent; + + /** React component that adds buttons/behavior to the viewer Toolbar */ + getToolbarModule: () => reactToolbarComponent; + + /** 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', + type: PLUGIN_TYPES.SOP_CLASS_HANDLER, + sopClassUids: ['string'], + getDisplaySetFromSeries: (series, study, dicomWebClient, authorizationHeaders) => ... + }; + + // Not yet used + getPanelModule: () => null; +} +``` + +### Modules + +There are a few different kinds of modules. Each kind of module 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`](https://github.com/OHIF/ohif-core/blob/43c08a29eff3fb646a0e83a03a236ddd84f4a6e8/src/plugins.js#L1-L6). Information on each type of module, it's API, and how we determine when/where it should be used is included below: + +#### Viewport + +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: + +```js +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 +``` + +Viewport components are managed by the `LayoutManager`. Which Viewport component is used depends on: + +- The Layout Configuration +- Registered SopClassHandlers +- The SopClassUID for visible/selected datasets + +![Cornerstone Viewport](../assets/img/extensions-viewport.png) +
An example of three Viewports
+ +For a complete example implementation, [check out the OHIFCornerstoneViewport](https://github.com/OHIF/Viewers/blob/react/extensions/ohif-cornerstone-extension/src/OHIFCornerstoneViewport.js). + +#### Toolbar + +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. + +![Toolbar Extension](../assets/img/extensions-toolbar.gif) +
A toolbar extension example
+ +Toolbar components are rendered in the `ToolbarRow` component. + +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). + +#### SopClassHandler + +... + +#### Panel + +> The panel module is not yet in use. + +### Registering Extensions + +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. + +In a future version, we will likely expose a way to provide the extensions you would like included at startup. + +_app.js_ + +```js +import { createStore, combineReducers } from 'redux'; +import OHIF from 'ohif-core'; +import OHIFCornerstoneExtension from 'ohif-cornerstone-extension'; + +const combined = combineReducers(OHIF.redux.reducers); +const store = createStore(combined); +const extensions = [ new OHIFCornerstoneExtension() ]; + +// Dispatches the `addPlugin` action to the store +// Adding extension modules to `state.plugins.availablePlugins` +ExtensionManager.registerExtensions(store, extensions); +``` + +## OHIF Maintained Extensions + +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. + +{% include "./_maintained-extensions-table.md" %} diff --git a/docs/latest/advanced/index.md b/docs/latest/advanced/index.md new file mode 100644 index 000000000..c882d000e --- /dev/null +++ b/docs/latest/advanced/index.md @@ -0,0 +1,3 @@ +# Advanced + +Advanced topics go beyond basic configuration and deployment. Their goal is to provide insight into this project's architecture and guidance on leveraging extensions. \ No newline at end of file diff --git a/docs/latest/architecture/index.md b/docs/latest/architecture/index.md deleted file mode 100644 index f2569bf9f..000000000 --- a/docs/latest/architecture/index.md +++ /dev/null @@ -1 +0,0 @@ -# Architecture \ No newline at end of file diff --git a/docs/latest/assets/img/architecture-diagram.png b/docs/latest/assets/img/architecture-diagram.png new file mode 100644 index 000000000..1c43d0108 Binary files /dev/null and b/docs/latest/assets/img/architecture-diagram.png differ diff --git a/docs/latest/assets/img/extensions-toolbar.gif b/docs/latest/assets/img/extensions-toolbar.gif new file mode 100644 index 000000000..88c313f3d Binary files /dev/null and b/docs/latest/assets/img/extensions-toolbar.gif differ diff --git a/docs/latest/assets/img/extensions-viewport.png b/docs/latest/assets/img/extensions-viewport.png new file mode 100644 index 000000000..0ecffda06 Binary files /dev/null and b/docs/latest/assets/img/extensions-viewport.png differ diff --git a/docs/latest/essentials/configuration.md b/docs/latest/essentials/configuration.md new file mode 100644 index 000000000..af4abbfe2 --- /dev/null +++ b/docs/latest/essentials/configuration.md @@ -0,0 +1 @@ +# Configuration \ No newline at end of file diff --git a/extensions/ohif-cornerstone-extension/src/OHIFCornerstoneViewport.js b/extensions/ohif-cornerstone-extension/src/OHIFCornerstoneViewport.js index fb458d11b..c76b99c50 100644 --- a/extensions/ohif-cornerstone-extension/src/OHIFCornerstoneViewport.js +++ b/extensions/ohif-cornerstone-extension/src/OHIFCornerstoneViewport.js @@ -2,7 +2,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import OHIF from 'ohif-core'; import ConnectedCornerstoneViewport from './ConnectedCornerstoneViewport'; -import cornerstoneTools from 'cornerstone-tools'; import cornerstone from 'cornerstone-core'; import './config'; import handleSegmentationStorage from './handleSegmentationStorage.js'; diff --git a/extensions/ohif-cornerstone-extension/src/ToolbarModule.js b/extensions/ohif-cornerstone-extension/src/ToolbarModule.js index 8ee1ff5e2..657441b60 100644 --- a/extensions/ohif-cornerstone-extension/src/ToolbarModule.js +++ b/extensions/ohif-cornerstone-extension/src/ToolbarModule.js @@ -1,5 +1,4 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import ConnectedToolbarSection from './ConnectedToolbarSection'; import { ToolbarButton } from 'react-viewerbase'; import ConnectedCineDialog from './ConnectedCineDialog'; diff --git a/img/architecture-diagram.png b/img/architecture-diagram.png new file mode 100644 index 000000000..1c43d0108 Binary files /dev/null and b/img/architecture-diagram.png differ diff --git a/img/designs/architecture-diagram b/img/designs/architecture-diagram new file mode 100644 index 000000000..bbf6cf58b Binary files /dev/null and b/img/designs/architecture-diagram differ diff --git a/src/connectedComponents/ConnectedLayoutManager.js b/src/connectedComponents/ConnectedLayoutManager.js index 3a682c94b..4a0b5dd2b 100644 --- a/src/connectedComponents/ConnectedLayoutManager.js +++ b/src/connectedComponents/ConnectedLayoutManager.js @@ -3,32 +3,37 @@ import { LayoutManager } from 'react-viewerbase'; import OHIF from 'ohif-core'; const mapStateToProps = state => { - const viewportPluginIds = state.plugins.availablePlugins.filter(plugin => plugin.type === OHIF.plugins.PLUGIN_TYPES.VIEWPORT).map(plugin => plugin.id); - const availablePlugins = {} - viewportPluginIds.forEach(id => { - const plugin = OHIF.plugins.availablePlugins.find(plugin => plugin.id === id); - if (plugin) { - availablePlugins[id] = plugin.component; - } - }); + const viewportPluginIds = state.plugins.availablePlugins + .filter(plugin => plugin.type === OHIF.plugins.PLUGIN_TYPES.VIEWPORT) + .map(plugin => plugin.id); - // TODO Use something like state.plugins.defaultPlugin[OHIF.plugins.PLUGIN_TYPES.VIEWPORT] - let defaultPlugin; - if (viewportPluginIds && viewportPluginIds.length) { - defaultPlugin = viewportPluginIds[0]; + const availablePlugins = {}; + viewportPluginIds.forEach(id => { + const plugin = OHIF.plugins.availablePlugins.find( + plugin => plugin.id === id + ); + if (plugin) { + availablePlugins[id] = plugin.component; } + }); - return { - layout: state.viewports.layout, - activeViewportIndex: state.viewports.activeViewportIndex, - availablePlugins, - defaultPlugin - }; + // TODO Use something like state.plugins.defaultPlugin[OHIF.plugins.PLUGIN_TYPES.VIEWPORT] + let defaultPlugin; + if (viewportPluginIds && viewportPluginIds.length) { + defaultPlugin = viewportPluginIds[0]; + } + + return { + layout: state.viewports.layout, + activeViewportIndex: state.viewports.activeViewportIndex, + availablePlugins, + defaultPlugin + }; }; const ConnectedLayoutManager = connect( - mapStateToProps, - null + mapStateToProps, + null )(LayoutManager); export default ConnectedLayoutManager;