feat: Add new annotate tool using new dialog service (#1211)
* Testing dialog * Refactor modal and add dialog component to simple dialog * Use existent dialog * Expect event from getText callback (annotation) * Bump cornerstone-tools version * Use simple dialog * bump cornersotne version * CR Update: Update dialgo method names and default values * CR Update: Bump overlay and show only if theres at least one overlay prop set to true * CR Update: Use percentage over view height in draggable area * Fix broken test vtk checks WWWC tool * Comment test (non-deterministic)
This commit is contained in:
1 parent
1334ba8eb0
commit
8fd3af1e13
18 files changed
+372
-254
No files matched your search
@@ -34,49 +34,45 @@ const DialogProvider = ({ children, service }) => {
|
||||
}, [create, dismiss, service]);
|
||||
|
||||
/**
|
||||
* Creates a dialog and return its id.
|
||||
* UI Dialog
|
||||
*
|
||||
* @returns id
|
||||
* @typedef {Object} DialogProps
|
||||
* @property {string} id The dialog id.
|
||||
* @property {DialogContent} content The dialog content.
|
||||
* @property {Object} contentProps The dialog content props.
|
||||
* @property {boolean} isDraggable Controls if dialog content is draggable or not.
|
||||
* @property {boolean} showOverlay Controls dialog overlay.
|
||||
* @property {ElementPosition} defaultPosition Specifies the `x` and `y` that the dragged item should start at.
|
||||
* @property {ElementPosition} position If this property is present, the item becomes 'controlled' and is not responsive to user input.
|
||||
* @property {Function} onStart Called when dragging starts. If `false` is returned any handler, the action will cancel.
|
||||
* @property {Function} onStop Called when dragging stops.
|
||||
* @property {Function} onDrag Called while dragging.
|
||||
*/
|
||||
const create = useCallback(
|
||||
({
|
||||
id,
|
||||
content,
|
||||
onSubmit,
|
||||
onClose,
|
||||
onDrag,
|
||||
onStop,
|
||||
isDraggable,
|
||||
defaultPosition,
|
||||
position,
|
||||
}) => {
|
||||
let dialogId = id;
|
||||
if (!dialogId) {
|
||||
dialogId = utils.guid();
|
||||
}
|
||||
|
||||
const newDialog = {
|
||||
id: dialogId,
|
||||
content,
|
||||
onSubmit,
|
||||
onClose,
|
||||
onDrag,
|
||||
onStop,
|
||||
isDraggable,
|
||||
defaultPosition,
|
||||
position,
|
||||
};
|
||||
/**
|
||||
* Creates a new dialog and return its id.
|
||||
*
|
||||
* @param {DialogProps} props The dialog props.
|
||||
* @returns The new dialog id.
|
||||
*/
|
||||
const create = useCallback(props => {
|
||||
const { id } = props;
|
||||
|
||||
setDialogs(dialogs => [...dialogs, newDialog]);
|
||||
let dialogId = id;
|
||||
if (!dialogId) {
|
||||
dialogId = utils.guid();
|
||||
}
|
||||
|
||||
return dialogId;
|
||||
},
|
||||
[]
|
||||
);
|
||||
setDialogs(dialogs => [...dialogs, { ...props, id: dialogId }]);
|
||||
|
||||
return dialogId;
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Dismisses the dialog with a given id.
|
||||
*
|
||||
* @param {Object} props -
|
||||
* @property {string} props.id The dialog id.
|
||||
* @returns void
|
||||
*/
|
||||
const dismiss = useCallback(({ id }) => {
|
||||
@@ -92,18 +88,102 @@ const DialogProvider = ({ children, service }) => {
|
||||
setDialogs([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicate if there are no dialogs present.
|
||||
*
|
||||
* @returns True if no dialogs are present.
|
||||
*/
|
||||
const isEmpty = () => dialogs && dialogs.length < 1;
|
||||
|
||||
/**
|
||||
* Moves the dialog to the foreground if clicked.
|
||||
*
|
||||
* @param {string} id The dialog id.
|
||||
* @returns void
|
||||
*/
|
||||
const _bringToFront = id => {
|
||||
setDialogs(dialogs => {
|
||||
const topDialog = dialogs.find(dialog => dialog.id === id);
|
||||
return topDialog
|
||||
? [...dialogs.filter(dialog => dialog.id !== id), topDialog]
|
||||
: [];
|
||||
});
|
||||
};
|
||||
|
||||
const renderDialogs = () =>
|
||||
dialogs.map(dialog => {
|
||||
const {
|
||||
id,
|
||||
content: DialogContent,
|
||||
contentProps,
|
||||
position,
|
||||
defaultPosition,
|
||||
isDraggable = true,
|
||||
onStart,
|
||||
onStop,
|
||||
onDrag,
|
||||
} = dialog;
|
||||
|
||||
return (
|
||||
<Draggable
|
||||
key={id}
|
||||
disabled={!isDraggable}
|
||||
position={position}
|
||||
defaultPosition={lastDialogPosition || defaultPosition}
|
||||
bounds="parent"
|
||||
onStart={event => {
|
||||
const e = event || window.event;
|
||||
const target = e.target || e.srcElement;
|
||||
const BLACKLIST = [
|
||||
'SVG',
|
||||
'BUTTON',
|
||||
'PATH',
|
||||
'INPUT',
|
||||
'SPAN',
|
||||
'LABEL',
|
||||
];
|
||||
if (BLACKLIST.includes(target.tagName.toUpperCase())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (validCallback(onStart)) {
|
||||
return onStart(event);
|
||||
}
|
||||
}}
|
||||
onStop={event => {
|
||||
setIsDragging(false);
|
||||
|
||||
if (validCallback(onStop)) {
|
||||
return onStop(event);
|
||||
}
|
||||
}}
|
||||
onDrag={event => {
|
||||
setIsDragging(true);
|
||||
_bringToFront(id);
|
||||
_updateLastDialogPosition(id);
|
||||
|
||||
if (validCallback(onDrag)) {
|
||||
return onDrag(event);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
id={`draggableItem-${id}`}
|
||||
className={classNames('DraggableItem', isDragging && 'dragging')}
|
||||
style={{ zIndex: '999', position: 'absolute' }}
|
||||
onClick={() => _bringToFront(id)}
|
||||
>
|
||||
<DialogContent {...dialog} {...contentProps} />
|
||||
</div>
|
||||
</Draggable>
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Update the last dialog position to be used as the new default position.
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
const _reorder = id => {
|
||||
setDialogs(dialogs => [
|
||||
...dialogs.filter(dialog => dialog.id !== id),
|
||||
dialogs.find(dialog => dialog.id === id),
|
||||
]);
|
||||
};
|
||||
|
||||
const _updateLastDialogPosition = dialogId => {
|
||||
const draggableItemBounds = document
|
||||
.querySelector(`#draggableItem-${dialogId}`)
|
||||
@@ -114,70 +194,36 @@ const DialogProvider = ({ children, service }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const validCallback = callback => callback && typeof callback === 'function';
|
||||
|
||||
return (
|
||||
<DialogContext.Provider value={{ create, dismiss, dismissAll, dialogs }}>
|
||||
<DialogContext.Provider value={{ create, dismiss, dismissAll, isEmpty }}>
|
||||
<div className="DraggableArea">
|
||||
{dialogs.map(dialog => {
|
||||
const {
|
||||
id,
|
||||
content: Dialog,
|
||||
position /* Position of the dialog. {{x: 0, y: 0}} */,
|
||||
defaultPosition,
|
||||
isDraggable = true,
|
||||
onStart = () => {},
|
||||
onStop = () => {},
|
||||
onDrag = () => {},
|
||||
} = dialog;
|
||||
|
||||
return (
|
||||
<Draggable
|
||||
key={id}
|
||||
disabled={!isDraggable}
|
||||
position={position}
|
||||
defaultPosition={lastDialogPosition || defaultPosition}
|
||||
bounds="parent"
|
||||
onStart={event => {
|
||||
const e = event || window.event;
|
||||
const target = e.target || e.srcElement;
|
||||
const BLACKLIST = ['SVG', 'BUTTON', 'PATH', 'INPUT'];
|
||||
if (BLACKLIST.includes(target.tagName.toUpperCase())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
onStart(event);
|
||||
}}
|
||||
onStop={event => {
|
||||
onStop(event);
|
||||
setIsDragging(false);
|
||||
return;
|
||||
}}
|
||||
onDrag={event => {
|
||||
setIsDragging(true);
|
||||
_reorder(id);
|
||||
_updateLastDialogPosition(id);
|
||||
onDrag(event);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
id={`draggableItem-${id}`}
|
||||
className={classNames(
|
||||
'DraggableItem',
|
||||
isDragging && 'dragging'
|
||||
)}
|
||||
style={{ zIndex: '999', position: 'absolute' }}
|
||||
onClick={() => _reorder(id)}
|
||||
>
|
||||
<Dialog {...dialog} />
|
||||
</div>
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
{dialogs.some(dialog => dialog.showOverlay) ? (
|
||||
<div className="Overlay active">{renderDialogs()}</div>
|
||||
) : (
|
||||
renderDialogs()
|
||||
)}
|
||||
</div>
|
||||
{children}
|
||||
</DialogContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* High Order Component to use the dialog methods through a Class Component
|
||||
*
|
||||
*/
|
||||
export const withDialog = Component => {
|
||||
return function WrappedComponent(props) {
|
||||
const { create, dismiss, dismissAll, isEmpty } = useDialog();
|
||||
return (
|
||||
<Component {...props} dialog={{ create, dismiss, dismissAll, isEmpty }} />
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
DialogProvider.defaultProps = {
|
||||
service: null,
|
||||
};
|
||||
@@ -193,16 +239,4 @@ DialogProvider.propTypes = {
|
||||
}),
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* High Order Component to use the dialog methods through a Class Component
|
||||
*
|
||||
*/
|
||||
export const withDialog = Component => {
|
||||
return function WrappedComponent(props) {
|
||||
const { create, dismiss, dismissAll } = useDialog();
|
||||
return <Component {...props} dialog={{ create, dismiss, dismissAll }} />;
|
||||
};
|
||||
};
|
||||
|
||||
export default DialogProvider;
|
||||
Reference in new issue
Block a user