feat: is disabled flag for sidepanels (#1461)

* chore: updated autofix settings

* chore: remove dead code

* chore: further identify study schema/props

* chore: sketch out beginnings of isDisabled method for sidePanel menuOptions

* chore: set API as studies object

* docs: update docs to include isDisabled computed

* chore: note regarding reactivity

* chore: handle function not set (preserve non-breaking)

* chore: clarify how props are added

* chore: specify left side for built-in series button (thumbnails)

* chore: remove 'from', and push to correct key
This commit is contained in:
Danny Brown 2020-03-05 10:05:51 -05:00 committed by GitHub
commit c785a5e975
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 66 additions and 53 deletions

View File

@ -28,5 +28,8 @@
], ],
"prettier.disableLanguages": ["html"], "prettier.disableLanguages": ["html"],
"prettier.endOfLine": "lf", "prettier.endOfLine": "lf",
"workbench.colorCustomizations": {} "workbench.colorCustomizations": {},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
} }

View File

@ -40,6 +40,10 @@ export default {
from: 'right', from: 'right',
// The target component to toggle open/close // The target component to toggle open/close
target: 'target-component-id', target: 'target-component-id',
// UI Hint; If the target panel is in a "disabled" state
isDisabled: studies => {
return false;
},
// Overrides `defaultContext`, if specified // Overrides `defaultContext`, if specified
context: ['ACTIVE_VIEWPORT:MAGIC'], context: ['ACTIVE_VIEWPORT:MAGIC'],
}, },

View File

@ -21,7 +21,6 @@ export default {
/** /**
* MODULE GETTERS * MODULE GETTERS
*/ */
getViewportModule() { getViewportModule() {
return '... react component ...'; return '... react component ...';
}, },
@ -98,6 +97,9 @@ const panelModule = {
icon: 'th-list', icon: 'th-list',
label: 'Segments', label: 'Segments',
target: 'segment-panel', target: 'segment-panel',
isDisabled: studies => {
return false;
},
}, },
], ],
components: [ components: [

View File

@ -25,9 +25,17 @@ class ToolbarRow extends Component {
isRightSidePanelOpen: PropTypes.bool.isRequired, isRightSidePanelOpen: PropTypes.bool.isRequired,
selectedLeftSidePanel: PropTypes.string.isRequired, selectedLeftSidePanel: PropTypes.string.isRequired,
selectedRightSidePanel: PropTypes.string.isRequired, selectedRightSidePanel: PropTypes.string.isRequired,
handleSidePanelChange: PropTypes.func, handleSidePanelChange: PropTypes.func.isRequired,
activeContexts: PropTypes.arrayOf(PropTypes.string).isRequired, activeContexts: PropTypes.arrayOf(PropTypes.string).isRequired,
studies: PropTypes.array, studies: PropTypes.array,
t: PropTypes.func.isRequired,
// NOTE: withDialog, withModal HOCs
dialog: PropTypes.any,
modal: PropTypes.any,
};
static defaultProps = {
studies: [],
}; };
constructor(props) { constructor(props) {
@ -50,30 +58,30 @@ class ToolbarRow extends Component {
this._handleBuiltIn = _handleBuiltIn.bind(this); this._handleBuiltIn = _handleBuiltIn.bind(this);
const panelModules = extensionManager.modules[MODULE_TYPES.PANEL]; const panelModules = extensionManager.modules[MODULE_TYPES.PANEL];
this.buttonGroups = { this.buttonGroups = {
left: [ left: [],
// TODO: This should come from extensions, instead of being baked in
{
value: 'studies',
icon: 'th-large',
bottomLabel: this.props.t('Series'),
},
],
right: [], right: [],
}; };
// ~ FIND MENU OPTIONS
panelModules.forEach(panelExtension => { panelModules.forEach(panelExtension => {
const panelModule = panelExtension.module; const panelModule = panelExtension.module;
const defaultContexts = Array.from(panelModule.defaultContext); const defaultContexts = Array.from(panelModule.defaultContext);
// MENU OPTIONS
panelModule.menuOptions.forEach(menuOption => { panelModule.menuOptions.forEach(menuOption => {
const contexts = Array.from(menuOption.context || defaultContexts); const contexts = Array.from(menuOption.context || defaultContexts);
const hasActiveContext = this.props.activeContexts.some(actx =>
const activeContextIncludesAnyPanelContexts = this.props.activeContexts.some( contexts.includes(actx)
actx => contexts.includes(actx)
); );
if (activeContextIncludesAnyPanelContexts) {
// It's a bit beefy to pass studies; probably only need to be reactive on `studyInstanceUIDs` and activeViewport?
// Note: This does not cleanly handle `studies` prop updating with panel open
const isDisabled =
typeof menuOption.isDisabled === 'function' &&
menuOption.isDisabled(this.props.studies);
if (hasActiveContext && !isDisabled) {
const menuOptionEntry = { const menuOptionEntry = {
value: menuOption.target, value: menuOption.target,
icon: menuOption.icon, icon: menuOption.icon,
@ -85,6 +93,13 @@ class ToolbarRow extends Component {
} }
}); });
}); });
// TODO: This should come from extensions, instead of being baked in
this.buttonGroups.left.unshift({
value: 'studies',
icon: 'th-large',
bottomLabel: this.props.t('Series'),
});
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
@ -222,6 +237,10 @@ function _getButtonComponents(toolbarButtons, activeButtons) {
} }
/** /**
* TODO: DEPRECATE
* This is used exclusively in `extensions/cornerstone/src`
* We have better ways with new UI Services to trigger "builtin" behaviors
*
* A handy way for us to handle different button types. IE. firing commands for * A handy way for us to handle different button types. IE. firing commands for
* buttons, or initiation built in behavior. * buttons, or initiation built in behavior.
* *
@ -273,7 +292,7 @@ function _getVisibleToolbarButtons() {
function _handleBuiltIn(button) { function _handleBuiltIn(button) {
/* TODO: Keep cine button active until its unselected. */ /* TODO: Keep cine button active until its unselected. */
const { dialog, modal, t } = this.props; const { dialog, t } = this.props;
const { dialogId } = this.state; const { dialogId } = this.state;
const { id, options } = button; const { id, options } = button;

View File

@ -18,45 +18,30 @@ import WhiteLabellingContext from '../context/WhiteLabellingContext.js';
import UserManagerContext from '../context/UserManagerContext'; import UserManagerContext from '../context/UserManagerContext';
import './Viewer.css'; import './Viewer.css';
/**
* Inits OHIF Hanging Protocol's onReady.
* It waits for OHIF Hanging Protocol to be ready to instantiate the ProtocolEngine
* Hanging Protocol will use OHIF LayoutManager to render viewports properly
*/
/*const initHangingProtocol = () => {
// When Hanging Protocol is ready
HP.ProtocolStore.onReady(() => {
// Gets all StudyMetadata objects: necessary for Hanging Protocol to access study metadata
const studyMetadataList = OHIF.viewer.StudyMetadataList.all();
// Instantiate StudyMetadataSource: necessary for Hanging Protocol to get study metadata
const studyMetadataSource = new OHIF.studies.classes.OHIFStudyMetadataSource();
// Get prior studies map
const studyPriorsMap = OHIF.studylist.functions.getStudyPriorsMap(studyMetadataList);
// Creates Protocol Engine object with required arguments
const ProtocolEngine = new HP.ProtocolEngine(layoutManager, studyMetadataList, studyPriorsMap, studyMetadataSource);
// Sets up Hanging Protocol engine
HP.setEngine(ProtocolEngine);
});
};*/
/*const viewportUtils = OHIF.viewerbase.viewportUtils;
OHIF.viewer.functionList = {
toggleCineDialog: viewportUtils.toggleCineDialog,
toggleCinePlay: viewportUtils.toggleCinePlay,
clearTools: viewportUtils.clearTools,
resetViewport: viewportUtils.resetViewport,
invert: viewportUtils.invert
};*/
class Viewer extends Component { class Viewer extends Component {
static propTypes = { static propTypes = {
studies: PropTypes.array, studies: PropTypes.arrayOf(
PropTypes.shape({
studyInstanceUid: PropTypes.string.isRequired,
studyDate: PropTypes.string,
displaySets: PropTypes.arrayOf(
PropTypes.shape({
displaySetInstanceUid: PropTypes.string.isRequired,
seriesDescription: PropTypes.string,
seriesNumber: PropTypes.number,
instanceNumber: PropTypes.number,
numImageFrames: PropTypes.number,
modality: PropTypes.string.isRequired,
images: PropTypes.arrayOf(
PropTypes.shape({
getImageId: PropTypes.func.isRequired,
})
),
})
),
})
),
studyInstanceUids: PropTypes.array, studyInstanceUids: PropTypes.array,
activeServer: PropTypes.shape({ activeServer: PropTypes.shape({
type: PropTypes.string, type: PropTypes.string,