diff --git a/docs/latest/advanced/extensions.md b/docs/latest/advanced/extensions.md index b2c8b71a6..d9e1718e4 100644 --- a/docs/latest/advanced/extensions.md +++ b/docs/latest/advanced/extensions.md @@ -160,6 +160,7 @@ 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. +The given toolbar must determine its set of elements and the context of them. The set of elements will be listed on toolbar `definitions`. ![Toolbar Extension](../assets/img/extensions-toolbar.gif) @@ -170,6 +171,31 @@ 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/master/extensions/cornerstone/src/toolbarModule.js). +##### Toolbar Custom Component + +Toolbar elements can define its own custom react component to be consumed when rendering it. So far, it accepts `Functional` and `Class` Components. For that, you just need to expose your `CustomToolbarComponent` as the value of key `CustomComponent`. In case the property `CustomComponent` is not present, a default toolbar component will be used to render it. See bellow + +```js +definitions: [ +... + { + id: 'Custom', + label: 'Custom', + icon: 'custom-icon', + CustomComponent: CustomToolbarComponent, + } +... +] + +``` +`CustomComponent` components will receive the following props: +- parentContext: parent context. (In most of the cases it will be a ToolbarRow instance) +- toolbarClickCallback: callback method when clicking on toolbar +- button: its own definition object +- key: react key prop +- activeButtons: list of active elements +- isActive: if current + #### SopClassHandler ... diff --git a/platform/viewer/src/connectedComponents/ToolbarRow.js b/platform/viewer/src/connectedComponents/ToolbarRow.js index 0ddad8b7c..1612f44e0 100644 --- a/platform/viewer/src/connectedComponents/ToolbarRow.js +++ b/platform/viewer/src/connectedComponents/ToolbarRow.js @@ -150,45 +150,82 @@ class ToolbarRow extends Component { } } +function _getCustomButtonComponent(button, activeButtons) { + const CustomComponent = button.CustomComponent; + const isValidComponent = typeof CustomComponent === 'function'; + + // Check if its a valid customComponent. Later on an CustomToolbarComponent interface could be implemented. + if (isValidComponent) { + const parentContext = this; + const isActive = activeButtons.includes(button.id); + + return ( + + ); + } +} + +function _getExpandableButtonComponent(button, activeButtons) { + // Iterate over button definitions and update `onClick` behavior + let activeCommand; + const childButtons = button.buttons.map(childButton => { + childButton.onClick = _handleToolbarButtonClick.bind(this, childButton); + + if (activeButtons.indexOf(childButton.id) > -1) { + activeCommand = childButton.id; + } + + return childButton; + }); + + return ( + + ); +} + +function _getDefaultButtonComponent(button, activeButtons) { + return ( + + ); +} /** * Determine which extension buttons should be showing, if they're * active, and what their onClick behavior should be. */ function _getButtonComponents(toolbarButtons, activeButtons) { - return toolbarButtons.map((button, index) => { - let activeCommand = undefined; + const _this = this; + return toolbarButtons.map(button => { + const hasCustomComponent = button.CustomComponent; + const hasNestedButtonDefinitions = button.buttons && button.buttons.length; - if (button.buttons && button.buttons.length) { - // Iterate over button definitions and update `onClick` behavior - const childButtons = button.buttons.map(childButton => { - childButton.onClick = _handleToolbarButtonClick.bind(this, childButton); - - if (activeButtons.indexOf(childButton.id) > -1) { - activeCommand = childButton.id; - } - - return childButton; - }); - - return ( - - ); + if (hasCustomComponent) { + return _getCustomButtonComponent.call(_this, button, activeButtons); } - return ( - - ); + + if (hasNestedButtonDefinitions) { + return _getExpandableButtonComponent.call(_this, button, activeButtons); + } + + return _getDefaultButtonComponent.call(_this, button, activeButtons); }); }