feat: Allow configurable context menus (#2894)
* feat: Context menu feat: Custom context menu Adding documentation PR updates * fix: Capture tool exception * PR updates * Add fully worked out examples in the basic test mode/extension * Fix the menu display * fix: Make the commands on clicks much more configurable * Wait for load before double clicking * docs * PR changes - nothing functional, just moving things endlessly * PR comments * PR changes - rename the default context menu * Renamed the cornerstoneContextMenu to measurementsContextMenu * Add chevron right to the sub-menus
This commit is contained in:
1 parent
4e94b588a7
commit
bc642fd2b6
40 files changed
+1472
-280
No files matched your search
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Coding values is a map of simple string coding values to a set of
|
||||
* attributes associated with the coding value.
|
||||
*
|
||||
* The simple string is in the format `<codingSchemeDesignator>:<codingValue>`
|
||||
* That allows extracting the DICOM attributes from the designator/value, and
|
||||
* allows for passing around the simple string.
|
||||
* The additional attributes contained in the object include:
|
||||
* * text - this is the coding scheme text display value, and may be language specific
|
||||
* * type - this defines a named type, typically 'site'. Different names can be used
|
||||
* to allow setting different findingSites values in order to define a hierarchy.
|
||||
* * color - used to apply annotation color
|
||||
* It is also possible to define additional attributes here, used by custom
|
||||
* extensions.
|
||||
*
|
||||
* See https://dicom.nema.org/medical/dicom/current/output/html/part16.html
|
||||
* for definitions of SCT and other code values.
|
||||
*/
|
||||
const codingValues = {
|
||||
id: 'codingValues',
|
||||
|
||||
// Sites
|
||||
'SCT:69536005': {
|
||||
text: 'Head',
|
||||
type: 'site',
|
||||
},
|
||||
'SCT:45048000': {
|
||||
text: 'Neck',
|
||||
type: 'site',
|
||||
},
|
||||
'SCT:818981001': {
|
||||
text: 'Abdomen',
|
||||
type: 'site',
|
||||
},
|
||||
'SCT:816092008': {
|
||||
text: 'Pelvis',
|
||||
type: 'site',
|
||||
},
|
||||
|
||||
// Findings
|
||||
'SCT:371861004': {
|
||||
text: 'Mild intimal coronary irregularities',
|
||||
color: 'green',
|
||||
},
|
||||
'SCT:194983005': {
|
||||
text: 'Aortic insufficiency',
|
||||
color: 'darkred',
|
||||
},
|
||||
'SCT:399232001': {
|
||||
text: '2-chamber',
|
||||
},
|
||||
'SCT:103340004': {
|
||||
text: 'SAX',
|
||||
},
|
||||
'SCT:91134007': {
|
||||
text: 'MV',
|
||||
},
|
||||
'SCT:122972007': {
|
||||
text: 'PV',
|
||||
},
|
||||
|
||||
// Orientations
|
||||
'SCT:24422004': {
|
||||
text: 'Axial',
|
||||
color: '#000000',
|
||||
type: 'orientation',
|
||||
},
|
||||
'SCT:81654009': {
|
||||
text: 'Coronal',
|
||||
color: '#000000',
|
||||
type: 'orientation',
|
||||
},
|
||||
'SCT:30730003': {
|
||||
text: 'Sagittal',
|
||||
color: '#000000',
|
||||
type: 'orientation',
|
||||
},
|
||||
};
|
||||
|
||||
export default codingValues;
|
||||
@@ -0,0 +1,27 @@
|
||||
import codingValues from './codingValues';
|
||||
|
||||
const codeMenuItem = {
|
||||
id: '@ohif/contextMenuAnnotationCode',
|
||||
codingValues,
|
||||
|
||||
/** Applies the code value setup for this item */
|
||||
transform: function (customizationService) {
|
||||
const { code: codeRef } = this;
|
||||
if (!codeRef) throw new Error(`item ${this} has no code ref`);
|
||||
const codingValues = customizationService.get('codingValues');
|
||||
const code = codingValues[codeRef];
|
||||
return {
|
||||
...this,
|
||||
codeRef,
|
||||
code: { ref: codeRef, ...code },
|
||||
label: code.text,
|
||||
commands: [
|
||||
{
|
||||
commandName: 'updateMeasurement',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export default codeMenuItem;
|
||||
@@ -0,0 +1,100 @@
|
||||
const findingsContextMenu = {
|
||||
id: 'measurementsContextMenu',
|
||||
customizationType: 'ohif.contextMenu',
|
||||
menus: [
|
||||
{
|
||||
id: 'forExistingMeasurement',
|
||||
// selector restricts context menu to when there is nearbyToolData
|
||||
selector: ({ nearbyToolData }) => !!nearbyToolData,
|
||||
items: [
|
||||
{
|
||||
customizationType: 'ohif.contextSubMenu',
|
||||
label: 'Site',
|
||||
actionType: 'ShowSubMenu',
|
||||
subMenu: 'siteSelectionSubMenu',
|
||||
},
|
||||
{
|
||||
customizationType: 'ohif.contextSubMenu',
|
||||
label: 'Finding',
|
||||
actionType: 'ShowSubMenu',
|
||||
subMenu: 'findingSelectionSubMenu',
|
||||
},
|
||||
{
|
||||
// customizationType is implicit here in the configuration setup
|
||||
label: 'Delete Measurement',
|
||||
commands: [
|
||||
{
|
||||
commandName: 'deleteMeasurement',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Add Label',
|
||||
commands: [
|
||||
{
|
||||
commandName: 'setMeasurementLabel',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// The example below shows how to include a delegating sub-menu,
|
||||
// Only available on the @ohif/hp-extension.mn hanging protocol
|
||||
// To demonstrate, select the 3x1 layout from the protocol menu
|
||||
// and right click on a measurement.
|
||||
{
|
||||
label: 'IncludeSubMenu',
|
||||
selector: ({ protocol }) => protocol?.id === '@ohif/hp-extension.mn',
|
||||
delegating: true,
|
||||
subMenu: 'orientationSelectionSubMenu',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
id: 'orientationSelectionSubMenu',
|
||||
selector: ({ nearbyToolData }) => !!nearbyToolData,
|
||||
items: [
|
||||
{
|
||||
customizationType: '@ohif/contextMenuAnnotationCode',
|
||||
code: 'SCT:24422004',
|
||||
},
|
||||
{
|
||||
customizationType: '@ohif/contextMenuAnnotationCode',
|
||||
code: 'SCT:81654009',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
id: 'findingSelectionSubMenu',
|
||||
selector: ({ nearbyToolData }) => !!nearbyToolData,
|
||||
items: [
|
||||
{
|
||||
customizationType: '@ohif/contextMenuAnnotationCode',
|
||||
code: 'SCT:371861004',
|
||||
},
|
||||
{
|
||||
customizationType: '@ohif/contextMenuAnnotationCode',
|
||||
code: 'SCT:194983005',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
id: 'siteSelectionSubMenu',
|
||||
selector: ({ nearbyToolData }) => !!nearbyToolData,
|
||||
items: [
|
||||
{
|
||||
customizationType: '@ohif/contextMenuAnnotationCode',
|
||||
code: 'SCT:69536005',
|
||||
},
|
||||
{
|
||||
customizationType: '@ohif/contextMenuAnnotationCode',
|
||||
code: 'SCT:45048000',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default findingsContextMenu;
|
||||
@@ -0,0 +1,5 @@
|
||||
import codingValues from './codingValues';
|
||||
import contextMenuCodeItem from './contextMenuCodeItem';
|
||||
import findingsContextMenu from './findingsContextMenu';
|
||||
|
||||
export { codingValues, contextMenuCodeItem, findingsContextMenu };
|
||||
@@ -0,0 +1,14 @@
|
||||
import {
|
||||
codingValues,
|
||||
contextMenuCodeItem,
|
||||
findingsContextMenu,
|
||||
} from './custom-context-menu';
|
||||
|
||||
export default function getCustomizationModule() {
|
||||
return [
|
||||
{
|
||||
name: 'custom-context-menu',
|
||||
value: [codingValues, contextMenuCodeItem, findingsContextMenu],
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { Types } from '@ohif/core';
|
||||
import { id } from './id';
|
||||
|
||||
import getHangingProtocolModule from './hp';
|
||||
import getCustomizationModule from './getCustomizationModule';
|
||||
// import {setViewportZoomPan, storeViewportZoomPan } from './custom-viewport/setViewportZoomPan';
|
||||
import sameAs from './custom-attribute/sameAs';
|
||||
import numberOfDisplaySets from './custom-attribute/numberOfDisplaySets';
|
||||
@@ -59,6 +60,9 @@ const testExtension: Types.Extensions.Extension = {
|
||||
|
||||
/** Registers some additional hanging protocols. See hp/index.tsx for more details */
|
||||
getHangingProtocolModule,
|
||||
|
||||
/** Registers some customizations */
|
||||
getCustomizationModule,
|
||||
};
|
||||
|
||||
export default testExtension;
|
||||
Reference in new issue
Block a user