feat(layout): new layout selector with 3D volume rendering (#3923)

This commit is contained in:
Ibrahim authored and GitHub committed 2024-03-28 10:40:32 -04:00
1 parent 6f610b09e1
commit 617043fe0d
51 files changed
+829 -239

No files matched your search

@@ -0,0 +1,88 @@
import React, { useEffect, useState, useCallback } from 'react';
import PropTypes from 'prop-types';
import { LayoutSelector as OHIFLayoutSelector, ToolbarButton } from '@ohif/ui';
import { ServicesManager } from '@ohif/core';
function LegacyLayoutSelectorWithServices({ servicesManager, ...props }) {
const { toolbarService } = servicesManager.services;
const onSelection = useCallback(
props => {
toolbarService.recordInteraction({
interactionType: 'action',
commands: [
{
commandName: 'setViewportGridLayout',
commandOptions: { ...props },
context: 'DEFAULT',
},
],
});
},
[toolbarService]
);
return (
<LayoutSelector
{...props}
onSelection={onSelection}
/>
);
}
function LayoutSelector({ rows, columns, className, onSelection, ...rest }) {
const [isOpen, setIsOpen] = useState(false);
const closeOnOutsideClick = () => {
if (isOpen) {
setIsOpen(false);
}
};
useEffect(() => {
window.addEventListener('click', closeOnOutsideClick);
return () => {
window.removeEventListener('click', closeOnOutsideClick);
};
}, [isOpen]);
const onInteractionHandler = () => setIsOpen(!isOpen);
const DropdownContent = isOpen ? OHIFLayoutSelector : null;
return (
<ToolbarButton
id="Layout"
label="Grid Layout"
icon="tool-layout"
onInteraction={onInteractionHandler}
className={className}
rounded={rest.rounded}
dropdownContent={
DropdownContent !== null && (
<DropdownContent
rows={rows}
columns={columns}
onSelection={onSelection}
/>
)
}
isActive={isOpen}
type="toggle"
/>
);
}
LayoutSelector.propTypes = {
rows: PropTypes.number,
columns: PropTypes.number,
onLayoutChange: PropTypes.func,
servicesManager: PropTypes.instanceOf(ServicesManager),
};
LayoutSelector.defaultProps = {
rows: 3,
columns: 3,
onLayoutChange: () => {},
};
export default LegacyLayoutSelectorWithServices;
@@ -1,37 +1,127 @@
import React, { useEffect, useState, useCallback } from 'react';
import PropTypes from 'prop-types';
import { LayoutSelector as OHIFLayoutSelector, ToolbarButton } from '@ohif/ui';
import { LayoutSelector as OHIFLayoutSelector, ToolbarButton, LayoutPreset } from '@ohif/ui';
import { ServicesManager } from '@ohif/core';
function ToolbarLayoutSelectorWithServices({ servicesManager, commands, ...props }) {
const { toolbarService, viewportGridService } = servicesManager.services;
const defaultCommonPresets = [
{
icon: 'layout-common-1x1',
commandOptions: {
numRows: 1,
numCols: 1,
},
},
{
icon: 'layout-common-1x2',
commandOptions: {
numRows: 1,
numCols: 2,
},
},
{
icon: 'layout-common-2x2',
commandOptions: {
numRows: 2,
numCols: 2,
},
},
{
icon: 'layout-common-2x3',
commandOptions: {
numRows: 2,
numCols: 3,
},
},
];
const onInteraction = useCallback(
args => {
const viewportId = viewportGridService.getActiveViewportId();
const refreshProps = {
viewportId,
};
if (props.onLayoutChange) {
props.onLayoutChange(args);
const generateAdvancedPresets = hangingProtocolService => {
const hangingProtocols = Array.from(hangingProtocolService.protocols.values());
return hangingProtocols
.map(hp => {
if (!hp.isPreset) {
return null;
}
toolbarService.recordInteraction({ commands }, { ...args, refreshProps });
return {
icon: hp.icon,
title: hp.name,
commandOptions: {
protocolId: hp.id,
},
};
})
.filter(preset => preset !== null);
};
function ToolbarLayoutSelectorWithServices({ servicesManager, ...props }) {
const { toolbarService } = servicesManager.services;
const [isDisabled, setIsDisabled] = useState(false);
const handleMouseEnter = () => {
setIsDisabled(false);
};
const onSelection = useCallback(
props => {
toolbarService.recordInteraction({
interactionType: 'action',
commands: [
{
commandName: 'setViewportGridLayout',
commandOptions: { ...props },
},
],
});
setIsDisabled(true);
},
[toolbarService]
);
const onSelectionPreset = useCallback(
props => {
toolbarService.recordInteraction({
interactionType: 'action',
commands: [
{
commandName: 'setHangingProtocol',
commandOptions: { ...props },
},
],
});
setIsDisabled(true);
},
[toolbarService]
);
return (
<LayoutSelector
{...props}
onInteraction={onInteraction}
/>
<div onMouseEnter={handleMouseEnter}>
<LayoutSelector
{...props}
onSelection={onSelection}
onSelectionPreset={onSelectionPreset}
servicesManager={servicesManager}
tooltipDisabled={isDisabled}
/>
</div>
);
}
function LayoutSelector({ rows, columns, className, onInteraction, ...rest }) {
function LayoutSelector({
rows,
columns,
className,
onSelection,
onSelectionPreset,
servicesManager,
tooltipDisabled,
...rest
}) {
const [isOpen, setIsOpen] = useState(false);
const { customizationService, hangingProtocolService } = servicesManager.services;
const commonPresets = customizationService.get('commonPresets') || defaultCommonPresets;
const advancedPresets =
customizationService.get('advancedPresets') || generateAdvancedPresets(hangingProtocolService);
const closeOnOutsideClick = () => {
if (isOpen) {
setIsOpen(false);
@@ -45,24 +135,68 @@ function LayoutSelector({ rows, columns, className, onInteraction, ...rest }) {
};
}, [isOpen]);
const onInteractionHandler = () => setIsOpen(!isOpen);
const onInteractionHandler = () => {
setIsOpen(!isOpen);
};
const DropdownContent = isOpen ? OHIFLayoutSelector : null;
return (
<ToolbarButton
id="Layout"
label="Grid Layout"
label="Layout"
icon="tool-layout"
onInteraction={onInteractionHandler}
className={className}
rounded={rest.rounded}
disableToolTip={tooltipDisabled}
dropdownContent={
DropdownContent !== null && (
<DropdownContent
rows={rows}
columns={columns}
onSelection={onInteraction}
/>
<div className="flex">
<div className="bg-secondary-dark flex flex-col gap-2.5 p-2">
<div className="text-aqua-pale text-xs">Common</div>
<div className="flex gap-4">
{commonPresets.map((preset, index) => (
<LayoutPreset
key={index}
classNames="hover:bg-primary-dark group p-1"
icon={preset.icon}
commandOptions={preset.commandOptions}
onSelection={onSelection}
/>
))}
</div>
<div className="h-[2px] bg-black"></div>
<div className="text-aqua-pale text-xs">Advanced</div>
<div className="flex flex-col gap-2.5">
{advancedPresets.map((preset, index) => (
<LayoutPreset
key={index + commonPresets.length}
classNames="hover:bg-primary-dark group flex gap-2 p-1"
icon={preset.icon}
title={preset.title}
commandOptions={preset.commandOptions}
onSelection={onSelectionPreset}
/>
))}
</div>
</div>
<div className="bg-primary-dark flex flex-col gap-2.5 border-l-2 border-solid border-black p-2">
<div className="text-aqua-pale text-xs">Custom</div>
<DropdownContent
rows={rows}
columns={columns}
onSelection={onSelection}
/>
<p className="text-aqua-pale text-xs leading-tight">
Hover to select <br></br>rows and columns <br></br> Click to apply
</p>
</div>
</div>
)
}
isActive={isOpen}
@@ -79,8 +213,8 @@ LayoutSelector.propTypes = {
};
LayoutSelector.defaultProps = {
columns: 4,
rows: 3,
columns: 3,
onLayoutChange: () => {},
};