Merge branch 'feat/v2-main' into feat/OHIF-177
This commit is contained in:
28 files changed
+513
-47
No files matched your search
@@ -33,7 +33,7 @@
|
||||
"@ohif/ui": "^0.50.0",
|
||||
"cornerstone-core": "^2.3.0",
|
||||
"cornerstone-math": "^0.1.8",
|
||||
"cornerstone-tools": "4.15.1",
|
||||
"cornerstone-tools": "4.16.0",
|
||||
"dcmjs": "0.14.0",
|
||||
"cornerstone-wado-image-loader": "^3.1.2",
|
||||
"dicom-parser": "^1.8.3",
|
||||
|
||||
@@ -2,33 +2,32 @@ import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ToolbarButton } from '@ohif/ui';
|
||||
|
||||
function NestedMenu({ children }) {
|
||||
function NestedMenu({ children, label, icon, isActive }) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
function closeNestedMenu() {
|
||||
if (isOpen) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
const toggleNestedMenu = () => setIsOpen(!isOpen);
|
||||
|
||||
const closeNestedMenu = () => {
|
||||
if (isOpen) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('click', closeNestedMenu);
|
||||
return () => {
|
||||
window.removeEventListener('click', closeNestedMenu);
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
const dropdownContent = isOpen ? children : undefined;
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
id="More"
|
||||
label="More"
|
||||
icon="tool-more-menu"
|
||||
onClick={() => {
|
||||
setIsOpen(!isOpen);
|
||||
}}
|
||||
dropdownContent={dropdownContent}
|
||||
isActive={isOpen}
|
||||
id="NestedMenu"
|
||||
label={label}
|
||||
icon={icon}
|
||||
onClick={toggleNestedMenu}
|
||||
dropdownContent={isOpen && children}
|
||||
isActive={isActive || isOpen}
|
||||
type="primary"
|
||||
/>
|
||||
);
|
||||
@@ -36,6 +35,13 @@ function NestedMenu({ children }) {
|
||||
|
||||
NestedMenu.propTypes = {
|
||||
children: PropTypes.any.isRequired,
|
||||
icon: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
};
|
||||
|
||||
NestedMenu.defaultProps = {
|
||||
icon: "tool-more-menu",
|
||||
label: "More",
|
||||
};
|
||||
|
||||
export default NestedMenu;
|
||||
@@ -55,7 +55,20 @@ function ViewerLayout({
|
||||
};
|
||||
};
|
||||
|
||||
const defaultTool = { icon: 'tool-more-menu', label: 'More', isActive: false };
|
||||
const [toolbars, setToolbars] = useState({ primary: [], secondary: [] });
|
||||
const [activeTool, setActiveTool] = useState(defaultTool);
|
||||
|
||||
const setActiveToolHandler = (tool, isNested) => {
|
||||
setActiveTool(isNested ? tool : defaultTool);
|
||||
};
|
||||
|
||||
const onPrimaryClickHandler = (evt, btn) => {
|
||||
if (btn.props && btn.props.commands && evt.value && btn.props.commands[evt.value]) {
|
||||
const { commandName, commandOptions } = btn.props.commands[evt.value];
|
||||
commandsManager.runCommand(commandName, commandOptions);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const { unsubscribe } = ToolBarService.subscribe(
|
||||
@@ -63,8 +76,8 @@ function ViewerLayout({
|
||||
() => {
|
||||
console.warn('~~~ TOOL BAR MODIFIED EVENT CAUGHT');
|
||||
const updatedToolbars = {
|
||||
primary: ToolBarService.getButtonSection('primary'),
|
||||
secondary: ToolBarService.getButtonSection('secondary'),
|
||||
primary: ToolBarService.getButtonSection('primary', { onClick: onPrimaryClickHandler, setActiveTool: setActiveToolHandler }),
|
||||
secondary: ToolBarService.getButtonSection('secondary', { setActiveTool: setActiveToolHandler }),
|
||||
};
|
||||
setToolbars(updatedToolbars);
|
||||
}
|
||||
@@ -86,11 +99,10 @@ function ViewerLayout({
|
||||
|
||||
if (!isNested) {
|
||||
const { id, Component, componentProps } = toolDef;
|
||||
|
||||
return <Component key={id} id={id} {...componentProps} />;
|
||||
} else {
|
||||
return (
|
||||
<NestedMenu>
|
||||
<NestedMenu isActive={activeTool.isActive} icon={activeTool.icon} label={activeTool.label}>
|
||||
<div className="flex">
|
||||
{toolDef.map(x => {
|
||||
const { id, Component, componentProps } = x;
|
||||
|
||||
@@ -9,7 +9,7 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
|
||||
{
|
||||
name: 'ohif.divider',
|
||||
defaultComponent: ToolbarDivider,
|
||||
clickHandler: () => {},
|
||||
clickHandler: () => { },
|
||||
},
|
||||
{
|
||||
name: 'ohif.action',
|
||||
@@ -30,7 +30,7 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
|
||||
optionalConfig: [],
|
||||
requiredProps: [],
|
||||
optionalProps: [],
|
||||
clickHandler: (evt, clickedBtn, btnSectionName) => {
|
||||
clickHandler: (evt, clickedBtn, btnSectionName, metadata, viewerProps) => {
|
||||
const { props } = clickedBtn;
|
||||
const allButtons = toolbarService.getButtons();
|
||||
|
||||
@@ -47,6 +47,10 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
|
||||
clickedBtn.config.groupName === btn.config.groupName
|
||||
) {
|
||||
btn.props.isActive = false;
|
||||
|
||||
if (viewerProps.setActiveTool) {
|
||||
viewerProps.setActiveTool(props, metadata.isNested);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -63,7 +67,7 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
|
||||
{
|
||||
name: 'ohif.layoutSelector',
|
||||
defaultComponent: ToolbarLayoutSelector,
|
||||
clickHandler: (evt, clickedBtn, btnSectionName) => {},
|
||||
clickHandler: (evt, clickedBtn, btnSectionName) => { },
|
||||
},
|
||||
{
|
||||
name: 'ohif.toggle',
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.50.0",
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-tools": "4.15.1",
|
||||
"cornerstone-tools": "4.16.0",
|
||||
"dcmjs": "0.14.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.8.6",
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"@ohif/ui": "^0.50.0",
|
||||
"cornerstone-core": "^2.3.0",
|
||||
"cornerstone-math": "^0.1.8",
|
||||
"cornerstone-tools": "4.15.1",
|
||||
"cornerstone-tools": "4.16.0",
|
||||
"dcmjs": "0.14.0",
|
||||
"cornerstone-wado-image-loader": "^3.1.2",
|
||||
"dicom-parser": "^1.8.3",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cornerstone from 'cornerstone-core';
|
||||
import cornerstoneTools from 'cornerstone-tools';
|
||||
import CornerstoneViewport from 'react-cornerstone-viewport';
|
||||
import OHIF, { DicomMetadataStore } from '@ohif/core';
|
||||
import {
|
||||
@@ -13,6 +14,18 @@ import debounce from 'lodash.debounce';
|
||||
import throttle from 'lodash.throttle';
|
||||
import { useTrackedMeasurements } from './../getContextModule';
|
||||
|
||||
// TODO -> Get this list from the list of tracked measurements.
|
||||
const {
|
||||
ArrowAnnotateTool,
|
||||
BidirectionalTool,
|
||||
EllipticalRoiTool,
|
||||
LengthTool,
|
||||
} = cornerstoneTools;
|
||||
|
||||
const BaseAnnotationTool = cornerstoneTools.importInternal(
|
||||
'base/BaseAnnotationTool'
|
||||
);
|
||||
|
||||
// const cine = viewportSpecificData.cine;
|
||||
|
||||
// isPlaying = cine.isPlaying === true;
|
||||
@@ -27,6 +40,7 @@ function TrackedCornerstoneViewport({
|
||||
viewportIndex,
|
||||
}) {
|
||||
const [trackedMeasurements] = useTrackedMeasurements();
|
||||
|
||||
const [
|
||||
{ activeViewportIndex, viewports },
|
||||
dispatchViewportGrid,
|
||||
@@ -34,6 +48,8 @@ function TrackedCornerstoneViewport({
|
||||
// viewportIndex, onSubmit
|
||||
const [viewportDialogState, viewportDialogApi] = useViewportDialog();
|
||||
const [viewportData, setViewportData] = useState(null);
|
||||
const [element, setElement] = useState(null);
|
||||
const [isTracked, setIsTracked] = useState(false);
|
||||
// TODO: Still needed? Better way than import `OHIF` and destructure?
|
||||
// Why is this managed by `core`?
|
||||
useEffect(() => {
|
||||
@@ -42,6 +58,75 @@ function TrackedCornerstoneViewport({
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
const allTools = cornerstoneTools.store.state.tools;
|
||||
const toolsForElement = allTools.filter(tool => tool.element === element);
|
||||
|
||||
toolsForElement.forEach(tool => {
|
||||
if (
|
||||
tool instanceof ArrowAnnotateTool ||
|
||||
tool instanceof BidirectionalTool ||
|
||||
tool instanceof EllipticalRoiTool ||
|
||||
tool instanceof LengthTool
|
||||
) {
|
||||
const configuration = tool.configuration;
|
||||
|
||||
configuration.renderDashed = !isTracked;
|
||||
|
||||
tool.configuration = configuration;
|
||||
}
|
||||
});
|
||||
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
|
||||
if (enabledElement.image) {
|
||||
cornerstone.updateImage(element);
|
||||
}
|
||||
}, [isTracked]);
|
||||
|
||||
const onElementEnabled = evt => {
|
||||
const eventData = evt.detail;
|
||||
const targetElement = eventData.element;
|
||||
|
||||
const allTools = cornerstoneTools.store.state.tools;
|
||||
|
||||
const toolsForElement = allTools.filter(
|
||||
tool => tool.element === targetElement
|
||||
);
|
||||
|
||||
toolsForElement.forEach(tool => {
|
||||
if (
|
||||
tool instanceof ArrowAnnotateTool ||
|
||||
tool instanceof BidirectionalTool ||
|
||||
tool instanceof EllipticalRoiTool ||
|
||||
tool instanceof LengthTool
|
||||
) {
|
||||
const configuration = tool.configuration;
|
||||
|
||||
configuration.renderDashed = !isTracked;
|
||||
|
||||
tool.configuration = configuration;
|
||||
} else if (tool instanceof BaseAnnotationTool) {
|
||||
const configuration = tool.configuration;
|
||||
|
||||
configuration.renderDashed = true;
|
||||
|
||||
tool.configuration = configuration;
|
||||
}
|
||||
});
|
||||
|
||||
const enabledElement = cornerstone.getEnabledElement(targetElement);
|
||||
|
||||
if (enabledElement.image) {
|
||||
cornerstone.updateImage(targetElement);
|
||||
}
|
||||
|
||||
setElement(targetElement);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const {
|
||||
StudyInstanceUID,
|
||||
@@ -116,6 +201,7 @@ function TrackedCornerstoneViewport({
|
||||
vp => vp.displaySetInstanceUID === displaySet.displaySetInstanceUID
|
||||
);
|
||||
const { trackedSeries } = trackedMeasurements.context;
|
||||
|
||||
const {
|
||||
Modality,
|
||||
SeriesDate,
|
||||
@@ -131,6 +217,11 @@ function TrackedCornerstoneViewport({
|
||||
SliceThickness,
|
||||
} = displaySet.images[0];
|
||||
|
||||
|
||||
if (trackedSeries.includes(SeriesInstanceUID) !== isTracked) {
|
||||
setIsTracked(!isTracked);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewportActionBar
|
||||
@@ -159,6 +250,7 @@ function TrackedCornerstoneViewport({
|
||||
{/* TODO: Viewport interface to accept stack or layers of content like this? */}
|
||||
<div className="relative flex flex-row w-full h-full">
|
||||
<CornerstoneViewport
|
||||
onElementEnabled={onElementEnabled}
|
||||
viewportIndex={viewportIndex}
|
||||
imageIds={imageIds}
|
||||
imageIdIndex={currentImageIdIndex}
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ohif/core": "^2.9.6",
|
||||
"cornerstone-tools": "4.15.1",
|
||||
"cornerstone-tools": "4.16.0",
|
||||
"@ohif/ui": "^2.0.0",
|
||||
"cornerstone-core": "^2.3.0",
|
||||
"cornerstone-wado-image-loader": "^3.1.2",
|
||||
|
||||
Reference in new issue
Block a user