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:
Bill Wallace authored and GitHub committed 2023-03-22 17:45:28 -04:00
1 parent 4e94b588a7
commit bc642fd2b6
40 files changed
+1472 -280

No files matched your search

@@ -1,20 +1,32 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Typography } from '../';
import Typography from '../Typography';
import Icon from '../Icon';
const ContextMenu = ({ items }) => {
const ContextMenu = ({ items, ...props }) => {
if (!items) {
console.warn('No items for context menu');
return null;
}
return (
<div
data-cy="context-menu"
className="relative bg-secondary-dark rounded z-50 block w-48"
onContextMenu={e => e.preventDefault()}
>
{items.map((item, index) => (
<div
key={index}
onClick={() => item.action(item)}
data-cy="context-menu-item"
onClick={() => item.action(item, props)}
className="flex px-4 py-3 cursor-pointer items-center transition duration-300 hover:bg-primary-dark border-b border-primary-dark last:border-b-0"
>
<Typography>{item.label}</Typography>
<Typography>
{item.label}
{item.iconRight && (
<Icon name={item.iconRight} className="inline" />
)}
</Typography>
</div>
))}
</div>
@@ -25,7 +37,6 @@ ContextMenu.propTypes = {
items: PropTypes.arrayOf(
PropTypes.shape({
label: PropTypes.string.isRequired,
actionType: PropTypes.string.isRequired,
action: PropTypes.func.isRequired,
})
).isRequired,
@@ -1,44 +0,0 @@
import { ContextMenu } from '../';
import PropTypes from 'prop-types';
import React from 'react';
const ContextMenuMeasurements = ({
onGetMenuItems,
onSetLabel,
onClose,
onDelete,
}) => {
const defaultMenuItems = [
{
label: 'Delete measurement',
actionType: 'Delete',
action: item => {
onDelete(item);
onClose();
},
value: {},
},
{
label: 'Add Label',
actionType: 'setLabel',
action: item => {
onSetLabel(item);
onClose();
},
value: {},
},
];
const menuItems = onGetMenuItems(defaultMenuItems);
return <ContextMenu items={menuItems} />;
};
ContextMenuMeasurements.propTypes = {
onClose: PropTypes.func.isRequired,
onSetLabel: PropTypes.func.isRequired,
onDelete: PropTypes.func.isRequired,
onGetMenuItems: PropTypes.func.isRequired,
};
export default ContextMenuMeasurements;
@@ -1 +0,0 @@
export { default } from './ContextMenuMeasurements';
+31 -15
View File
@@ -1,36 +1,52 @@
import React from 'react';
import React, { ReactNode } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import classNames from 'classnames';
import { NavBar, Svg, Icon, IconButton, Dropdown } from '../';
function Header({ children, menuOptions, isReturnEnabled, onClickReturnButton, isSticky, WhiteLabeling }) {
function Header({
children,
menuOptions,
isReturnEnabled,
onClickReturnButton,
isSticky,
WhiteLabeling,
...props
}): ReactNode {
const { t } = useTranslation('Header');
// TODO: this should be passed in as a prop instead and the react-router-dom
// dependency should be dropped
const onClickReturn = () => {
if (isReturnEnabled && onClickReturnButton) {
onClickReturnButton()
onClickReturnButton();
}
};
const CustomLogo = (React) => {
return WhiteLabeling.createLogoComponentFn(React)
}
return (
<NavBar className='justify-between border-b-4 border-black' isSticky={isSticky}>
<NavBar
className="justify-between border-b-4 border-black"
isSticky={isSticky}
>
<div className="flex justify-between flex-1">
<div className="flex items-center">
{/* // TODO: Should preserve filter/sort
// Either injected service? Or context (like react router's `useLocation`?) */}
<div
className={classNames("inline-flex items-center mr-3", isReturnEnabled && 'cursor-pointer')}
className={classNames(
'inline-flex items-center mr-3',
isReturnEnabled && 'cursor-pointer'
)}
onClick={onClickReturn}
>
{isReturnEnabled && <Icon name="chevron-left" className="w-8 text-primary-active" />}
<div className="ml-4">{WhiteLabeling ? CustomLogo(React) : <Svg name="logo-ohif" />}</div>
{isReturnEnabled && (
<Icon name="chevron-left" className="w-8 text-primary-active" />
)}
<div className="ml-4">
{WhiteLabeling?.createLogoComponentFn?.(React, props) || (
<Svg name="logo-ohif" />
)}
</div>
</div>
</div>
<div className="flex items-center">{children}</div>
@@ -40,7 +56,7 @@ function Header({ children, menuOptions, isReturnEnabled, onClickReturnButton, i
</span>
<Dropdown id="options" showDropdownIcon={false} list={menuOptions}>
<IconButton
id={"options-settings-icon"}
id={'options-settings-icon'}
variant="text"
color="inherit"
size="initial"
@@ -49,7 +65,7 @@ function Header({ children, menuOptions, isReturnEnabled, onClickReturnButton, i
<Icon name="settings" />
</IconButton>
<IconButton
id={"options-chevron-down-icon"}
id={'options-chevron-down-icon'}
variant="text"
color="inherit"
size="initial"
@@ -76,12 +92,12 @@ Header.propTypes = {
isReturnEnabled: PropTypes.bool,
isSticky: PropTypes.bool,
onClickReturnButton: PropTypes.func,
WhiteLabeling: PropTypes.element,
WhiteLabeling: PropTypes.object,
};
Header.defaultProps = {
isReturnEnabled: true,
isSticky: false
isSticky: false,
};
export default Header;
-2
View File
@@ -48,7 +48,6 @@ import ThumbnailNoImage from './ThumbnailNoImage';
import ThumbnailTracked from './ThumbnailTracked';
import ThumbnailList from './ThumbnailList';
import ToolbarButton from './ToolbarButton';
import ContextMenuMeasurements from './ContextMenuMeasurements';
import ExpandableToolbarButton from './ExpandableToolbarButton';
import ListMenu from './ListMenu';
import Tooltip from './Tooltip';
@@ -134,7 +133,6 @@ export {
ThumbnailTracked,
ThumbnailList,
ToolbarButton,
ContextMenuMeasurements,
Tooltip,
TooltipClipboard,
Typography,
-1
View File
@@ -94,7 +94,6 @@ export {
ThumbnailTracked,
ThumbnailList,
ToolbarButton,
ContextMenuMeasurements,
Tooltip,
TooltipClipboard,
Typography,
+7
View File
@@ -0,0 +1,7 @@
export type ContextMenuItem = {
// A label to show for the item.
label: string;
// An icon to show the on right of the text - typically used for submenus
iconRight?: string;
action: (item, component) => void;
};
+1
View File
@@ -0,0 +1 @@
export type Predicate = (props: Record<string, unknown>) => boolean;
+3
View File
@@ -4,6 +4,9 @@ import { PresentationIds } from '../contextProviders/getPresentationIds';
// A few miscellaneous types declared inline here.
export * from './Predicate';
export * from './ContextMenuItem';
/**
* StringNumber often comes back from DICOMweb for integer valued items.
*/