ui(components): LayoutSelector recreated for ui-next (#4845)

Co-authored-by: sedghi <ar.sedghi@gmail.com>
This commit is contained in:
Dan Rukasandsedghi authored and GitHub committed 2025-03-17 11:47:09 -04:00
1 parent b7fbf04230
commit abab3dc7ad
3 files changed
+548 -147

No files matched your search

@@ -1,169 +1,201 @@
import React, { useEffect, useState, useCallback, useRef } from 'react';
// Updated ToolbarLayoutSelector.tsx
import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { LayoutSelector as OHIFLayoutSelector, ToolbarButton, LayoutPreset } from '@ohif/ui';
import { CommandsManager } from '@ohif/core';
import { LayoutSelector } from '../../../../platform/ui-next/src/components/LayoutSelector';
function ToolbarLayoutSelectorWithServices({
commandsManager,
servicesManager,
rows = 3,
columns = 4,
...props
}: withAppTypes) {
const [isDisabled, setIsDisabled] = useState(false);
}) {
const { customizationService } = servicesManager.services;
const handleMouseEnter = () => {
setIsDisabled(false);
};
// Get the presets from the customization service
const commonPresets = customizationService?.getCustomization('layoutSelector.commonPresets') || [
{
icon: 'layout-single',
commandOptions: {
numRows: 1,
numCols: 1,
},
},
{
icon: 'layout-side-by-side',
commandOptions: {
numRows: 1,
numCols: 2,
},
},
{
icon: 'layout-four-up',
commandOptions: {
numRows: 2,
numCols: 2,
},
},
{
icon: 'layout-three-row',
commandOptions: {
numRows: 3,
numCols: 1,
},
},
];
const onSelection = useCallback(props => {
commandsManager.run({
commandName: 'setViewportGridLayout',
commandOptions: { ...props },
});
setIsDisabled(true);
}, []);
// Get the advanced presets generator from the customization service
const advancedPresetsGenerator = customizationService?.getCustomization(
'layoutSelector.advancedPresetGenerator'
);
const onSelectionPreset = useCallback(props => {
commandsManager.run({
commandName: 'setHangingProtocol',
commandOptions: { ...props },
});
setIsDisabled(true);
}, []);
// Generate the advanced presets
const advancedPresets = advancedPresetsGenerator
? advancedPresetsGenerator({ servicesManager })
: [
{
title: 'MPR',
icon: 'layout-three-col',
commandOptions: {
protocolId: 'mpr',
},
},
{
title: '3D four up',
icon: 'layout-four-up',
commandOptions: {
protocolId: '3d-four-up',
},
},
{
title: '3D main',
icon: 'layout-three-row',
commandOptions: {
protocolId: '3d-main',
},
},
{
title: 'Axial Primary',
icon: 'layout-side-by-side',
commandOptions: {
protocolId: 'axial-primary',
},
},
{
title: '3D only',
icon: 'layout-single',
commandOptions: {
protocolId: '3d-only',
},
},
{
title: '3D primary',
icon: 'layout-side-by-side',
commandOptions: {
protocolId: '3d-primary',
},
},
{
title: 'Frame View',
icon: 'icon-stack',
commandOptions: {
protocolId: 'frame-view',
},
},
];
// Unified selection handler that dispatches to the appropriate command
const handleSelectionChange = useCallback(
(commandOptions, isPreset) => {
if (isPreset) {
// Advanced preset selection
commandsManager.run({
commandName: 'setHangingProtocol',
commandOptions,
});
} else {
// Common preset or custom grid selection
commandsManager.run({
commandName: 'setViewportGridLayout',
commandOptions,
});
}
},
[commandsManager]
);
return (
<div onMouseEnter={handleMouseEnter}>
<div
id="Layout"
data-cy="Layout"
>
<LayoutSelector
onSelectionChange={handleSelectionChange}
{...props}
onSelection={onSelection}
onSelectionPreset={onSelectionPreset}
servicesManager={servicesManager}
tooltipDisabled={isDisabled}
/>
>
<LayoutSelector.Trigger tooltip="Change layout" />
<LayoutSelector.Content>
{/* Left side - Presets */}
{(commonPresets.length > 0 || advancedPresets.length > 0) && (
<div className="bg-popover flex flex-col gap-2.5 rounded-lg p-2">
{commonPresets.length > 0 && (
<>
<LayoutSelector.PresetSection title="Common">
{commonPresets.map((preset, index) => (
<LayoutSelector.Preset
key={`common-preset-${index}`}
icon={preset.icon}
commandOptions={preset.commandOptions}
isPreset={false}
/>
))}
</LayoutSelector.PresetSection>
<LayoutSelector.Divider />
</>
)}
{advancedPresets.length > 0 && (
<LayoutSelector.PresetSection title="Advanced">
{advancedPresets.map((preset, index) => (
<LayoutSelector.Preset
key={`advanced-preset-${index}`}
title={preset.title}
icon={preset.icon}
commandOptions={preset.commandOptions}
disabled={preset.disabled}
isPreset={true}
/>
))}
</LayoutSelector.PresetSection>
)}
</div>
)}
{/* Right Side - Grid Layout */}
<div className="bg-muted flex flex-col gap-2.5 border-l-2 border-solid border-black p-2">
<div className="text-muted-foreground text-xs">Custom</div>
<LayoutSelector.GridSelector
rows={rows}
columns={columns}
/>
<LayoutSelector.HelpText>
Hover to select <br />
rows and columns <br /> Click to apply
</LayoutSelector.HelpText>
</div>
</LayoutSelector.Content>
</LayoutSelector>
</div>
);
}
function LayoutSelector({
rows = 3,
columns = 4,
onLayoutChange = () => {},
className,
onSelection,
onSelectionPreset,
servicesManager,
tooltipDisabled,
...rest
}: withAppTypes) {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef(null);
const { customizationService } = servicesManager.services;
const commonPresets = customizationService.getCustomization('layoutSelector.commonPresets');
const advancedPresetsGenerator = customizationService.getCustomization(
'layoutSelector.advancedPresetGenerator'
);
const advancedPresets = advancedPresetsGenerator({ servicesManager });
const closeOnOutsideClick = event => {
if (isOpen && dropdownRef.current) {
setIsOpen(false);
}
};
useEffect(() => {
if (!isOpen) {
return;
}
setTimeout(() => {
window.addEventListener('click', closeOnOutsideClick);
}, 0);
return () => {
window.removeEventListener('click', closeOnOutsideClick);
dropdownRef.current = null;
};
}, [isOpen]);
const onInteractionHandler = () => {
setIsOpen(!isOpen);
};
const DropdownContent = isOpen ? OHIFLayoutSelector : null;
return (
<ToolbarButton
id="Layout"
label="Layout"
icon="tool-layout"
onInteraction={onInteractionHandler}
className={className}
rounded={rest.rounded}
disableToolTip={tooltipDisabled}
dropdownContent={
DropdownContent !== null && (
<div
className="flex"
ref={dropdownRef}
>
<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 cursor-pointer"
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 cursor-pointer"
icon={preset.icon}
title={preset.title}
disabled={preset.disabled}
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}
type="toggle"
/>
);
}
LayoutSelector.propTypes = {
ToolbarLayoutSelectorWithServices.propTypes = {
commandsManager: PropTypes.instanceOf(CommandsManager),
servicesManager: PropTypes.object,
rows: PropTypes.number,
columns: PropTypes.number,
onLayoutChange: PropTypes.func,
servicesManager: PropTypes.object.isRequired,
};
export default ToolbarLayoutSelectorWithServices;