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..7e7cf08d0 100644 --- a/docs/latest/SUMMARY.md +++ b/docs/latest/SUMMARY.md @@ -4,6 +4,17 @@ - [Getting Started](essentials/getting-started.md) - [Troubleshooting](essentials/troubleshooting.md) - [Architecture](architecture/index.md) + - Overview +- Deployment + +--- +- [Advanced](advanced/index.md) +- [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/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/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/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/generateStaticSite.sh b/generateStaticSite.sh index c29fb5d74..3036f8c8b 100755 --- a/generateStaticSite.sh +++ b/generateStaticSite.sh @@ -45,14 +45,14 @@ echo $DEPLOY_PRIME_URL export ROOT_URL=$DEPLOY_PRIME_URL/demo # cat package.json -yarn install -yarn build +# yarn install +# yarn build -cd example -yarn install -yarn run prepare -sed -i "s,http://localhost:5000,${ROOT_URL},g" index.html -sed -i 's,"routerBasename": "/","routerBasename": "/demo",g' index.html -rm -rf node_modules -mkdir docs/latest/_book/demo/ -cp -R * docs/latest/_book/demo/ +# cd example +# yarn install +# yarn run prepare +# sed -i "s,http://localhost:5000,${ROOT_URL},g" index.html +# sed -i 's,"routerBasename": "/","routerBasename": "/demo",g' index.html +# rm -rf node_modules +# mkdir docs/latest/_book/demo/ +# cp -R * docs/latest/_book/demo/ 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;