feat: New @ohif/viewer project structure + routes (#1674)
* fix pagination size * clean up, start a very basic route configuration * quick navigation between routes * configure webpack to read .env variables * Move container components to Routes directory * Fix naming of recipe file * Clean up source folder * update yarn.lock * ci: update netlify deploy preview to build viewer project * update PWA entrypoint to pass configuration on window to App as props * fix: for missing key * Pass routerBasename to our React Router component * Simplify config for meow Co-authored-by: dannyrb <danny.ri.brown@gmail.com>
This commit is contained in:
1 parent
9a8bede80e
commit
01b314522d
116 files changed
+22080
-4971
No files matched your search
@@ -1,420 +0,0 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { MeasurementTable } from '@ohif/ui';
|
||||
import OHIF, { DICOMSR } from '@ohif/core';
|
||||
import moment from 'moment';
|
||||
import cornerstone from 'cornerstone-core';
|
||||
|
||||
import jumpToRowItem from './jumpToRowItem.js';
|
||||
|
||||
const { setViewportSpecificData } = OHIF.redux.actions;
|
||||
const { MeasurementApi } = OHIF.measurements;
|
||||
|
||||
/**
|
||||
* Takes a list of objects and a property and return the list grouped by the property
|
||||
*
|
||||
* @param {Array} list - The objects to be grouped by
|
||||
* @param {string} props - The property to group the objects
|
||||
* @returns {Object}
|
||||
*/
|
||||
function groupBy(list, props) {
|
||||
return list.reduce((a, b) => {
|
||||
(a[b[props]] = a[b[props]] || []).push(b);
|
||||
return a;
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a list of tools grouped and return all tools separately
|
||||
*
|
||||
* @param {Array} [toolGroups=[]] - The grouped tools
|
||||
* @returns {Array} - The list of all tools on all groups
|
||||
*/
|
||||
function getAllTools(toolGroups = []) {
|
||||
let tools = [];
|
||||
toolGroups.forEach(toolGroup => (tools = tools.concat(toolGroup.childTools)));
|
||||
|
||||
return tools;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes measurementData and build the measurement text to be used into the table
|
||||
*
|
||||
* @param {Object} [measurementData={}]
|
||||
* @param {string} measurementData.location - The measurement location
|
||||
* @param {string} measurementData.description - The measurement description
|
||||
* @returns {string}
|
||||
*/
|
||||
function getMeasurementText(measurementData = {}) {
|
||||
const defaultText = '...';
|
||||
const { location = '', description = '' } = measurementData;
|
||||
const result = location + (description ? ` (${description})` : '');
|
||||
|
||||
return result || defaultText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a list of measurements grouped by measurement numbers and return each measurement data by available timepoint
|
||||
*
|
||||
* @param {Array} measurementNumberList - The list of measurements
|
||||
* @param {Array} timepoints - The list of available timepoints
|
||||
* @param {Function} displayFunction - The function that builds the display text by each tool
|
||||
* @returns
|
||||
*/
|
||||
function getDataForEachMeasurementNumber(
|
||||
measurementNumberList,
|
||||
timepoints,
|
||||
displayFunction
|
||||
) {
|
||||
const data = [];
|
||||
// on each measurement number we should get each measurement data by available timepoint
|
||||
measurementNumberList.forEach(measurement => {
|
||||
timepoints.forEach(timepoint => {
|
||||
const eachData = {
|
||||
displayText: '...',
|
||||
};
|
||||
if (measurement.timepointId === timepoint.timepointId) {
|
||||
eachData.displayText = displayFunction(measurement);
|
||||
}
|
||||
data.push(eachData);
|
||||
});
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a measurement toolName and return if any warnings
|
||||
*
|
||||
* @param {string} toolName - The tool name
|
||||
* @returns {string}
|
||||
*/
|
||||
function getWarningsForMeasurement(toolName) {
|
||||
const isToolSupported = DICOMSR.isToolSupported(toolName);
|
||||
|
||||
return {
|
||||
hasWarnings: !isToolSupported,
|
||||
warningTitle: isToolSupported ? '' : 'Unsupported Tool',
|
||||
warningList: isToolSupported
|
||||
? []
|
||||
: [`${toolName} cannot be persisted at this time`],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Take measurements from MeasurementAPI structure and convert into a measurementTable structure
|
||||
*
|
||||
* @param {Object} toolCollections - The list of all measurement grouped by groupTool and toolName
|
||||
* @param {Array} timepoints - The list of available timepoints
|
||||
* @returns
|
||||
*/
|
||||
function convertMeasurementsToTableData(toolCollections, timepoints) {
|
||||
const config = OHIF.measurements.MeasurementApi.getConfiguration();
|
||||
const toolGroups = config.measurementTools;
|
||||
const tools = getAllTools(toolGroups);
|
||||
|
||||
const tableMeasurements = toolGroups.map(toolGroup => {
|
||||
return {
|
||||
groupName: toolGroup.name,
|
||||
groupId: toolGroup.id,
|
||||
measurements: [],
|
||||
};
|
||||
});
|
||||
|
||||
Object.keys(toolCollections).forEach(toolId => {
|
||||
const toolMeasurements = toolCollections[toolId];
|
||||
const tool = tools.find(tool => tool.id === toolId);
|
||||
const { displayFunction } = tool.options.measurementTable;
|
||||
|
||||
// Group by measurementNumber so we can display then all in the same line
|
||||
const groupedMeasurements = groupBy(toolMeasurements, 'measurementNumber');
|
||||
|
||||
Object.keys(groupedMeasurements).forEach(groupedMeasurementsIndex => {
|
||||
const measurementNumberList =
|
||||
groupedMeasurements[groupedMeasurementsIndex];
|
||||
const measurementData = measurementNumberList[0];
|
||||
const {
|
||||
measurementNumber,
|
||||
lesionNamingNumber,
|
||||
toolType,
|
||||
} = measurementData;
|
||||
const measurementId = measurementData._id;
|
||||
|
||||
const {
|
||||
hasWarnings,
|
||||
warningTitle,
|
||||
warningList,
|
||||
} = getWarningsForMeasurement(toolType);
|
||||
|
||||
//check if all measurements with same measurementNumber will have same LABEL
|
||||
const tableMeasurement = {
|
||||
itemNumber: lesionNamingNumber,
|
||||
label: getMeasurementText(measurementData),
|
||||
measurementId,
|
||||
measurementNumber,
|
||||
lesionNamingNumber,
|
||||
toolType,
|
||||
hasWarnings,
|
||||
warningTitle,
|
||||
warningList,
|
||||
isSplitLesion: false, //TODO
|
||||
data: getDataForEachMeasurementNumber(
|
||||
measurementNumberList,
|
||||
timepoints,
|
||||
displayFunction
|
||||
),
|
||||
};
|
||||
|
||||
// find the group object for the tool
|
||||
const toolGroupMeasurements = tableMeasurements.find(group => {
|
||||
return group.groupId === tool.toolGroup;
|
||||
});
|
||||
// inject the new measurement for this measurementNumer
|
||||
toolGroupMeasurements.measurements.push(tableMeasurement);
|
||||
});
|
||||
});
|
||||
|
||||
// Sort measurements by lesion naming number
|
||||
tableMeasurements.forEach(tm => {
|
||||
tm.measurements.sort((m1, m2) =>
|
||||
m1.lesionNamingNumber > m2.lesionNamingNumber ? 1 : -1
|
||||
);
|
||||
});
|
||||
|
||||
return tableMeasurements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a list of available timepoints and return a list header information for each timepoint
|
||||
*
|
||||
* @param {Array} timepoints - The list of available timepoints
|
||||
* @param {string} timepoints[].latestDate - The date of the last study taken on the timepoint
|
||||
* @returns {{label: string, key: string, date: string}[]}
|
||||
*/
|
||||
function convertTimepointsToTableData(timepoints) {
|
||||
if (!timepoints || !timepoints.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
label: 'Study date:',
|
||||
key: 'StudyDate',
|
||||
date: moment(timepoints[0].latestDate).format('DD-MMM-YY'),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes server type and return a function or undefined
|
||||
*
|
||||
* @param {string} serverType - The server type
|
||||
* @returns {undefined|Function}
|
||||
*/
|
||||
function getSaveFunction(serverType) {
|
||||
if (serverType === 'dicomWeb') {
|
||||
return () => {
|
||||
const measurementApi = OHIF.measurements.MeasurementApi.Instance;
|
||||
const promise = measurementApi.storeMeasurements();
|
||||
return promise;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const { timepointManager, servers } = state;
|
||||
const { timepoints, measurements } = timepointManager;
|
||||
const activeServer = servers.servers.find(a => a.active === true);
|
||||
const saveFunction = getSaveFunction(activeServer.type);
|
||||
|
||||
return {
|
||||
timepoints: convertTimepointsToTableData(timepoints),
|
||||
measurementCollection: convertMeasurementsToTableData(
|
||||
measurements,
|
||||
timepoints
|
||||
),
|
||||
timepointManager: state.timepointManager,
|
||||
viewports: state.viewports,
|
||||
saveFunction,
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
||||
return {
|
||||
dispatchRelabel: (event, measurementData, viewportsState) => {
|
||||
event.persist();
|
||||
|
||||
const activeViewportIndex =
|
||||
(viewportsState && viewportsState.activeViewportIndex) || 0;
|
||||
|
||||
const enabledElements = cornerstone.getEnabledElements();
|
||||
if (!enabledElements || enabledElements.length <= activeViewportIndex) {
|
||||
OHIF.log.error('Failed to find the enabled element');
|
||||
return;
|
||||
}
|
||||
|
||||
const { toolType, measurementId } = measurementData;
|
||||
const tool = MeasurementApi.Instance.tools[toolType].find(measurement => {
|
||||
return measurement._id === measurementId;
|
||||
});
|
||||
|
||||
// Clone the tool not to set empty location initially
|
||||
const toolForLocation = Object.assign({}, tool, { location: null });
|
||||
|
||||
if (ownProps.onRelabel) {
|
||||
ownProps.onRelabel(toolForLocation);
|
||||
}
|
||||
},
|
||||
dispatchEditDescription: (event, measurementData, viewportsState) => {
|
||||
event.persist();
|
||||
|
||||
const activeViewportIndex =
|
||||
(viewportsState && viewportsState.activeViewportIndex) || 0;
|
||||
|
||||
const enabledElements = cornerstone.getEnabledElements();
|
||||
if (!enabledElements || enabledElements.length <= activeViewportIndex) {
|
||||
OHIF.log.error('Failed to find the enabled element');
|
||||
return;
|
||||
}
|
||||
|
||||
const { toolType, measurementId } = measurementData;
|
||||
const tool = MeasurementApi.Instance.tools[toolType].find(measurement => {
|
||||
return measurement._id === measurementId;
|
||||
});
|
||||
|
||||
if (ownProps.onEditDescription) {
|
||||
ownProps.onEditDescription(tool);
|
||||
}
|
||||
},
|
||||
dispatchJumpToRowItem: (
|
||||
measurementData,
|
||||
viewportsState,
|
||||
timepointManagerState,
|
||||
options
|
||||
) => {
|
||||
const actionData = jumpToRowItem(
|
||||
measurementData,
|
||||
viewportsState,
|
||||
timepointManagerState,
|
||||
dispatch,
|
||||
options
|
||||
);
|
||||
|
||||
actionData.viewportSpecificData.forEach(viewportSpecificData => {
|
||||
const { viewportIndex, displaySet } = viewportSpecificData;
|
||||
|
||||
dispatch(setViewportSpecificData(viewportIndex, displaySet));
|
||||
});
|
||||
|
||||
const { toolType, measurementNumber } = measurementData;
|
||||
const measurementApi = MeasurementApi.Instance;
|
||||
|
||||
Object.keys(measurementApi.tools).forEach(toolType => {
|
||||
const measurements = measurementApi.tools[toolType];
|
||||
|
||||
measurements.forEach(measurement => {
|
||||
measurement.active = false;
|
||||
});
|
||||
});
|
||||
|
||||
const measurementsToActive = measurementApi.tools[toolType].filter(
|
||||
measurement => {
|
||||
return measurement.measurementNumber === measurementNumber;
|
||||
}
|
||||
);
|
||||
|
||||
measurementsToActive.forEach(measurementToActive => {
|
||||
measurementToActive.active = true;
|
||||
});
|
||||
|
||||
measurementApi.syncMeasurementsAndToolData();
|
||||
|
||||
cornerstone.getEnabledElements().forEach(enabledElement => {
|
||||
cornerstone.updateImage(enabledElement.element);
|
||||
});
|
||||
|
||||
// Needs to update viewports.layout state to set layout
|
||||
//const layout = actionData.layout;
|
||||
//dispatch(setLayout(layout))
|
||||
|
||||
// Needs to update viewports.activeViewportIndex to the first updated viewport
|
||||
//const viewportIndex = actionData.viewportIndex;
|
||||
//dispatch(setViewportActive(viewportIndex));
|
||||
|
||||
// Needs to update timepointsManager.measurements state to set active measurementId
|
||||
// TODO: Not yet implemented
|
||||
//dispatch(setActiveMeasurement(measurementData.measurementId))
|
||||
|
||||
// (later): Needs to set some property on state.extensions.cornerstone to synchronize viewport scrolling
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const mergeProps = (propsFromState, propsFromDispatch, ownProps) => {
|
||||
const { timepoints, saveFunction, measurementCollection } = propsFromState;
|
||||
const { onSaveComplete, selectedMeasurementNumber } = ownProps;
|
||||
|
||||
return {
|
||||
timepoints,
|
||||
saveFunction,
|
||||
measurementCollection,
|
||||
onSaveComplete,
|
||||
selectedMeasurementNumber,
|
||||
...propsFromDispatch,
|
||||
onItemClick: (event, measurementData) => {
|
||||
// TODO: Add timepointId to .data for measurementData?
|
||||
// TODO: Tooltype should be on the level below? This should
|
||||
// provide the entire row item?
|
||||
|
||||
const viewportsState = propsFromState.viewports;
|
||||
const timepointManagerState = propsFromState.timepointManager;
|
||||
|
||||
// TODO: invertViewportTimepointsOrder should be stored in / read from user preferences
|
||||
// TODO: childToolKey should come from the measurement table when it supports child tools
|
||||
const options = {
|
||||
invertViewportTimepointsOrder: false,
|
||||
childToolKey: null,
|
||||
};
|
||||
|
||||
propsFromDispatch.dispatchJumpToRowItem(
|
||||
measurementData,
|
||||
viewportsState,
|
||||
timepointManagerState,
|
||||
options
|
||||
);
|
||||
},
|
||||
onRelabelClick: (event, measurementData) => {
|
||||
const viewportsState = propsFromState.viewports;
|
||||
propsFromDispatch.dispatchRelabel(event, measurementData, viewportsState);
|
||||
},
|
||||
onEditDescriptionClick: (event, measurementData) => {
|
||||
const viewportsState = propsFromState.viewports;
|
||||
propsFromDispatch.dispatchEditDescription(
|
||||
event,
|
||||
measurementData,
|
||||
viewportsState
|
||||
);
|
||||
},
|
||||
onDeleteClick: (event, measurementData) => {
|
||||
const { MeasurementHandlers } = OHIF.measurements;
|
||||
|
||||
MeasurementHandlers.onRemoved({
|
||||
detail: {
|
||||
toolType: measurementData.toolType,
|
||||
measurementData: {
|
||||
_id: measurementData.measurementId,
|
||||
lesionNamingNumber: measurementData.lesionNamingNumber,
|
||||
measurementNumber: measurementData.measurementNumber,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedMeasurementTable = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
mergeProps
|
||||
)(MeasurementTable);
|
||||
|
||||
export default ConnectedMeasurementTable;
|
||||
@@ -1,87 +0,0 @@
|
||||
import React from 'react';
|
||||
import ConnectedMeasurementTable from './ConnectedMeasurementTable.js';
|
||||
import init from './init.js';
|
||||
|
||||
import LabellingFlow from '../../components/Labelling/LabellingFlow';
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id: 'measurements-table',
|
||||
|
||||
preRegistration({ servicesManager, commandsManager, configuration = {} }) {
|
||||
init({ servicesManager, commandsManager, configuration });
|
||||
},
|
||||
|
||||
getPanelModule({ servicesManager, commandsManager }) {
|
||||
const { UINotificationService, UIDialogService } = servicesManager.services;
|
||||
|
||||
const showLabellingDialog = (props, measurementData) => {
|
||||
if (!UIDialogService) {
|
||||
console.warn('Unable to show dialog; no UI Dialog Service available.');
|
||||
return;
|
||||
}
|
||||
|
||||
UIDialogService.dismiss({ id: 'labelling' });
|
||||
UIDialogService.create({
|
||||
id: 'labelling',
|
||||
centralize: true,
|
||||
isDraggable: false,
|
||||
showOverlay: true,
|
||||
content: LabellingFlow,
|
||||
contentProps: {
|
||||
measurementData,
|
||||
labellingDoneCallback: () =>
|
||||
UIDialogService.dismiss({ id: 'labelling' }),
|
||||
updateLabelling: ({ location, description, response }) => {
|
||||
measurementData.location = location || measurementData.location;
|
||||
measurementData.description = description || '';
|
||||
measurementData.response = response || measurementData.response;
|
||||
|
||||
commandsManager.runCommand(
|
||||
'updateTableWithNewMeasurementData',
|
||||
measurementData
|
||||
);
|
||||
},
|
||||
...props,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const ExtendedConnectedMeasurementTable = () => (
|
||||
<ConnectedMeasurementTable
|
||||
onRelabel={tool =>
|
||||
showLabellingDialog(
|
||||
{ editLocation: true, skipAddLabelButton: true },
|
||||
tool
|
||||
)
|
||||
}
|
||||
onEditDescription={tool =>
|
||||
showLabellingDialog({ editDescriptionOnDialog: true }, tool)
|
||||
}
|
||||
onSaveComplete={message => {
|
||||
if (UINotificationService) {
|
||||
UINotificationService.show(message);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
return {
|
||||
menuOptions: [
|
||||
{
|
||||
icon: 'list',
|
||||
label: 'Measurements',
|
||||
target: 'measurement-panel',
|
||||
},
|
||||
],
|
||||
components: [
|
||||
{
|
||||
id: 'measurement-panel',
|
||||
component: ExtendedConnectedMeasurementTable,
|
||||
},
|
||||
],
|
||||
defaultContext: ['VIEWER'],
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -1,251 +0,0 @@
|
||||
import OHIF from '@ohif/core';
|
||||
import cornerstone from 'cornerstone-core';
|
||||
import csTools from 'cornerstone-tools';
|
||||
import throttle from 'lodash.throttle';
|
||||
|
||||
import LabellingFlow from '../../components/Labelling/LabellingFlow';
|
||||
import ToolContextMenu from '../../connectedComponents/ToolContextMenu';
|
||||
|
||||
const {
|
||||
onAdded,
|
||||
onRemoved,
|
||||
onModified,
|
||||
} = OHIF.measurements.MeasurementHandlers;
|
||||
|
||||
const MEASUREMENT_ACTION_MAP = {
|
||||
added: onAdded,
|
||||
removed: onRemoved,
|
||||
modified: throttle(event => {
|
||||
return onModified(event);
|
||||
}, 300),
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @export
|
||||
* @param {Object} servicesManager
|
||||
* @param {Object} configuration
|
||||
*/
|
||||
export default function init({
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
configuration,
|
||||
}) {
|
||||
const { UIDialogService } = servicesManager.services;
|
||||
|
||||
// TODO: MEASUREMENT_COMPLETED (not present in initial implementation)
|
||||
const onMeasurementsChanged = (action, event) => {
|
||||
return MEASUREMENT_ACTION_MAP[action](event);
|
||||
};
|
||||
const onMeasurementAdded = onMeasurementsChanged.bind(this, 'added');
|
||||
const onMeasurementRemoved = onMeasurementsChanged.bind(this, 'removed');
|
||||
const onMeasurementModified = onMeasurementsChanged.bind(this, 'modified');
|
||||
const onLabelmapModified = onMeasurementsChanged.bind(
|
||||
this,
|
||||
'labelmapModified'
|
||||
);
|
||||
|
||||
const _getDefaultPosition = event => ({
|
||||
x: (event && event.currentPoints.client.x) || 0,
|
||||
y: (event && event.currentPoints.client.y) || 0,
|
||||
});
|
||||
|
||||
const _updateLabellingHandler = (labellingData, measurementData) => {
|
||||
const { location, description, response } = labellingData;
|
||||
|
||||
if (location) {
|
||||
measurementData.location = location;
|
||||
}
|
||||
|
||||
measurementData.description = description || '';
|
||||
|
||||
if (response) {
|
||||
measurementData.response = response;
|
||||
}
|
||||
|
||||
commandsManager.runCommand(
|
||||
'updateTableWithNewMeasurementData',
|
||||
measurementData
|
||||
);
|
||||
};
|
||||
|
||||
const showLabellingDialog = (props, contentProps, measurementData) => {
|
||||
if (!UIDialogService) {
|
||||
console.warn('Unable to show dialog; no UI Dialog Service available.');
|
||||
return;
|
||||
}
|
||||
|
||||
UIDialogService.create({
|
||||
id: 'labelling',
|
||||
isDraggable: false,
|
||||
showOverlay: true,
|
||||
centralize: true,
|
||||
content: LabellingFlow,
|
||||
contentProps: {
|
||||
measurementData,
|
||||
labellingDoneCallback: () =>
|
||||
UIDialogService.dismiss({ id: 'labelling' }),
|
||||
updateLabelling: labellingData =>
|
||||
_updateLabellingHandler(labellingData, measurementData),
|
||||
...contentProps,
|
||||
},
|
||||
...props,
|
||||
});
|
||||
};
|
||||
|
||||
const onRightClick = event => {
|
||||
if (!UIDialogService) {
|
||||
console.warn('Unable to show dialog; no UI Dialog Service available.');
|
||||
return;
|
||||
}
|
||||
|
||||
UIDialogService.dismiss({ id: 'context-menu' });
|
||||
UIDialogService.create({
|
||||
id: 'context-menu',
|
||||
isDraggable: false,
|
||||
preservePosition: false,
|
||||
defaultPosition: _getDefaultPosition(event.detail),
|
||||
content: ToolContextMenu,
|
||||
contentProps: {
|
||||
eventData: event.detail,
|
||||
onDelete: (nearbyToolData, eventData) => {
|
||||
const element = eventData.element;
|
||||
commandsManager.runCommand('removeToolState', {
|
||||
element,
|
||||
toolType: nearbyToolData.toolType,
|
||||
tool: nearbyToolData.tool,
|
||||
});
|
||||
},
|
||||
onClose: () => UIDialogService.dismiss({ id: 'context-menu' }),
|
||||
onSetLabel: (eventData, measurementData) => {
|
||||
showLabellingDialog(
|
||||
{ centralize: true, isDraggable: false },
|
||||
{ skipAddLabelButton: true, editLocation: true },
|
||||
measurementData
|
||||
);
|
||||
},
|
||||
onSetDescription: (eventData, measurementData) => {
|
||||
showLabellingDialog(
|
||||
{ defaultPosition: _getDefaultPosition(eventData) },
|
||||
{ editDescriptionOnDialog: true },
|
||||
measurementData
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onTouchPress = event => {
|
||||
if (!UIDialogService) {
|
||||
console.warn('Unable to show dialog; no UI Dialog Service available.');
|
||||
return;
|
||||
}
|
||||
|
||||
UIDialogService.create({
|
||||
eventData: event.detail,
|
||||
content: ToolContextMenu,
|
||||
contentProps: {
|
||||
isTouchEvent: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onTouchStart = () => resetLabelligAndContextMenu();
|
||||
|
||||
const onMouseClick = () => resetLabelligAndContextMenu();
|
||||
|
||||
const resetLabelligAndContextMenu = () => {
|
||||
if (!UIDialogService) {
|
||||
console.warn('Unable to show dialog; no UI Dialog Service available.');
|
||||
return;
|
||||
}
|
||||
|
||||
UIDialogService.dismiss({ id: 'context-menu' });
|
||||
UIDialogService.dismiss({ id: 'labelling' });
|
||||
};
|
||||
|
||||
// TODO: This makes scrolling painfully slow
|
||||
// const onNewImage = ...
|
||||
|
||||
/*
|
||||
* Because click gives us the native "mouse up", buttons will always be `0`
|
||||
* Need to fallback to event.which;
|
||||
*
|
||||
*/
|
||||
const handleClick = cornerstoneMouseClickEvent => {
|
||||
const mouseUpEvent = cornerstoneMouseClickEvent.detail.event;
|
||||
const isRightClick = mouseUpEvent.which === 3;
|
||||
|
||||
if (isRightClick) {
|
||||
onRightClick(cornerstoneMouseClickEvent);
|
||||
} else {
|
||||
onMouseClick(cornerstoneMouseClickEvent);
|
||||
}
|
||||
};
|
||||
|
||||
function elementEnabledHandler(evt) {
|
||||
const element = evt.detail.element;
|
||||
|
||||
element.addEventListener(
|
||||
csTools.EVENTS.MEASUREMENT_ADDED,
|
||||
onMeasurementAdded
|
||||
);
|
||||
element.addEventListener(
|
||||
csTools.EVENTS.MEASUREMENT_REMOVED,
|
||||
onMeasurementRemoved
|
||||
);
|
||||
element.addEventListener(
|
||||
csTools.EVENTS.MEASUREMENT_MODIFIED,
|
||||
onMeasurementModified
|
||||
);
|
||||
element.addEventListener(
|
||||
csTools.EVENTS.LABELMAP_MODIFIED,
|
||||
onLabelmapModified
|
||||
);
|
||||
|
||||
element.addEventListener(csTools.EVENTS.TOUCH_PRESS, onTouchPress);
|
||||
element.addEventListener(csTools.EVENTS.MOUSE_CLICK, handleClick);
|
||||
element.addEventListener(csTools.EVENTS.TOUCH_START, onTouchStart);
|
||||
|
||||
// TODO: This makes scrolling painfully slow
|
||||
// element.addEventListener(cornerstone.EVENTS.NEW_IMAGE, onNewImage);
|
||||
}
|
||||
|
||||
function elementDisabledHandler(evt) {
|
||||
const element = evt.detail.element;
|
||||
|
||||
element.removeEventListener(
|
||||
csTools.EVENTS.MEASUREMENT_ADDED,
|
||||
onMeasurementAdded
|
||||
);
|
||||
element.removeEventListener(
|
||||
csTools.EVENTS.MEASUREMENT_REMOVED,
|
||||
onMeasurementRemoved
|
||||
);
|
||||
element.removeEventListener(
|
||||
csTools.EVENTS.MEASUREMENT_MODIFIED,
|
||||
onMeasurementModified
|
||||
);
|
||||
element.removeEventListener(
|
||||
csTools.EVENTS.LABELMAP_MODIFIED,
|
||||
onLabelmapModified
|
||||
);
|
||||
|
||||
element.removeEventListener(csTools.EVENTS.TOUCH_PRESS, onTouchPress);
|
||||
element.removeEventListener(csTools.EVENTS.MOUSE_CLICK, handleClick);
|
||||
element.removeEventListener(csTools.EVENTS.TOUCH_START, onTouchStart);
|
||||
|
||||
// TODO: This makes scrolling painfully slow
|
||||
// element.removeEventListener(cornerstone.EVENTS.NEW_IMAGE, onNewImage);
|
||||
}
|
||||
|
||||
cornerstone.events.addEventListener(
|
||||
cornerstone.EVENTS.ELEMENT_ENABLED,
|
||||
elementEnabledHandler
|
||||
);
|
||||
cornerstone.events.addEventListener(
|
||||
cornerstone.EVENTS.ELEMENT_DISABLED,
|
||||
elementDisabledHandler
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user