ui(components): migrates to new toolbar ui components and Numeric for Input InputRange and InputDoubleRange (#4728)

Co-authored-by: sedghi <ar.sedghi@gmail.com>
This commit is contained in:
Dan Rukasandsedghi authored and GitHub committed 2025-01-29 13:36:52 -05:00
1 parent ac5ae4a29e
commit a8d84443a9
116 files changed
+2930 -1038

No files matched your search

@@ -3,6 +3,7 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
DropdownMenuItem,
Icons,
Button,
} from '@ohif/ui-next';
@@ -18,6 +19,13 @@ const getMenuItemsDefault = ({ commandsManager, items, servicesManager, ...props
// getMenuItems can also be replaced by providing it to the MoreDropdownMenu
const menuContent = customizationService.getCustomization('ohif.menuContent');
// Default menu item component if none is provided through customization
const DefaultMenuItem = ({ item }) => (
<DropdownMenuItem onClick={item.onClick}>{item.label || item.title}</DropdownMenuItem>
);
const MenuItemComponent = menuContent?.content || DefaultMenuItem;
return (
<DropdownMenuContent
hideWhenDetached
@@ -27,15 +35,15 @@ const getMenuItemsDefault = ({ commandsManager, items, servicesManager, ...props
e.preventDefault();
}}
>
{items?.map((item, index) =>
React.createElement(menuContent.content, {
key: item.id || `menu-item-${index}`,
item,
commandsManager,
servicesManager,
...props,
})
)}
{items?.map((item, index) => (
<MenuItemComponent
key={item.id || `menu-item-${index}`}
item={item}
commandsManager={commandsManager}
servicesManager={servicesManager}
{...props}
/>
))}
</DropdownMenuContent>
);
};
@@ -0,0 +1,44 @@
import React from 'react';
import classNames from 'classnames';
import { ToolButton } from '@ohif/ui-next';
/**
* Wraps the ToolButtonList component to handle the OHIF toolbar button structure
* @param props - Component props
* @returns Component
*/
export function ToolBoxButtonGroupWrapper({ groupId, items, onInteraction, ...props }) {
if (!items || !groupId) {
return null;
}
return (
<div className="bg-popover flex flex-row space-x-1 rounded-md px-0 py-0">
{items.map(item => (
<ToolButton
{...item}
key={item.id}
size="small"
className={props.disabled && 'text-primary'}
onInteraction={() =>
onInteraction?.({ groupId, itemId: item.id, commands: item.commands })
}
/>
))}
</div>
);
}
export function ToolBoxButtonWrapper({ onInteraction, ...props }) {
return (
<div className="bg-popover flex flex-row rounded-md px-0 py-0">
<ToolButton
{...props}
id={props.id}
size="small"
className={classNames(props.disabled && 'text-primary')}
onInteraction={() => onInteraction?.({ itemId: props.id, commands: props.commands })}
/>
</div>
);
}
@@ -0,0 +1,84 @@
import React from 'react';
import {
ToolButtonList,
ToolButton,
ToolButtonListDefault,
ToolButtonListDropDown,
ToolButtonListItem,
ToolButtonListDivider,
} from '@ohif/ui-next';
interface ButtonItem {
id: string;
icon?: string;
label?: string;
tooltip?: string;
isActive?: boolean;
disabledText?: string;
commands?: Record<string, unknown>;
disabled?: boolean;
className?: string;
}
interface ToolButtonListWrapperProps {
groupId: string;
primary: ButtonItem;
items: ButtonItem[];
onInteraction?: (details: {
groupId: string;
itemId: string;
commands?: Record<string, unknown>;
}) => void;
}
/**
* Wraps the ToolButtonList component to handle the OHIF toolbar button structure
* @param props - Component props
* @returns Component
* // test
*/
export default function ToolButtonListWrapper({
groupId,
primary,
items,
onInteraction,
}: ToolButtonListWrapperProps) {
return (
<ToolButtonList>
<ToolButtonListDefault>
<div
data-cy={`${groupId}-split-button-primary`}
data-tool={primary.id}
data-active={primary.isActive}
>
<ToolButton
{...primary}
onInteraction={({ itemId }) =>
onInteraction?.({ groupId, itemId, commands: primary.commands })
}
className={primary.className}
/>
</div>
</ToolButtonListDefault>
<ToolButtonListDivider className={primary.isActive ? 'opacity-0' : 'opacity-100'} />
<div data-cy={`${groupId}-split-button-secondary`}>
<ToolButtonListDropDown>
{items.map(item => (
<ToolButtonListItem
key={item.id}
{...item}
data-cy={item.id}
data-tool={item.id}
data-active={item.isActive}
onSelect={() =>
onInteraction?.({ groupId, itemId: item.id, commands: item.commands })
}
>
<span className="pl-1">{item.label || item.tooltip || item.id}</span>
</ToolButtonListItem>
))}
</ToolButtonListDropDown>
</div>
</ToolButtonList>
);
}
+1 -3
View File
@@ -1,6 +1,4 @@
import React from 'react';
import { Tooltip } from '@ohif/ui';
import classnames from 'classnames';
import { useToolbar } from '@ohif/core';
export function Toolbar({ servicesManager, buttonSection = 'primary' }) {
@@ -15,7 +13,7 @@ export function Toolbar({ servicesManager, buttonSection = 'primary' }) {
return (
<>
{toolbarButtons.map(toolDef => {
{toolbarButtons?.map(toolDef => {
if (!toolDef) {
return null;
}
@@ -1,5 +1,5 @@
import { ToolbarButton, ButtonGroup } from '@ohif/ui';
import React, { useCallback } from 'react';
import { ToolbarButton, ButtonGroup } from '@ohif/ui';
function ToolbarButtonGroupWithServices({ groupId, items, onInteraction, size }) {
const getSplitButtonItems = useCallback(
@@ -107,18 +107,18 @@ const useResizablePanels = (
// And by virtue of the dependency on the minimum size state variables, this code
// is executed on the render following an update of the minimum percentage sizes
// for a panel.
if (!resizableLeftPanelAPIRef.current.isCollapsed()) {
if (!resizableLeftPanelAPIRef.current?.isCollapsed()) {
const leftSize = getPercentageSize(
leftPanelExpandedWidth + panelGroupDefinition.shared.expandedInsideBorderSize
);
resizableLeftPanelAPIRef.current.resize(leftSize);
resizableLeftPanelAPIRef.current?.resize(leftSize);
}
if (!resizableRightPanelAPIRef.current.isCollapsed()) {
if (!resizableRightPanelAPIRef?.current?.isCollapsed()) {
const rightSize = getPercentageSize(
rightPanelExpandedWidth + panelGroupDefinition.shared.expandedInsideBorderSize
);
resizableRightPanelAPIRef.current.resize(rightSize);
resizableRightPanelAPIRef?.current?.resize(rightSize);
}
// This observer kicks in when the ViewportLayout resizable panel group
@@ -199,7 +199,7 @@ const useResizablePanels = (
}, [setLeftPanelClosed]);
const onLeftPanelResize = useCallback(size => {
if (!resizablePanelGroupElemRef?.current || resizableLeftPanelAPIRef.current.isCollapsed()) {
if (!resizablePanelGroupElemRef?.current || resizableLeftPanelAPIRef.current?.isCollapsed()) {
return;
}
@@ -228,7 +228,7 @@ const useResizablePanels = (
}, [setRightPanelClosed]);
const onRightPanelResize = useCallback(size => {
if (!resizablePanelGroupElemRef?.current || resizableRightPanelAPIRef.current.isCollapsed()) {
if (!resizablePanelGroupElemRef?.current || resizableRightPanelAPIRef?.current?.isCollapsed()) {
return;
}
@@ -248,7 +248,7 @@ const useResizablePanels = (
* Note that the width attributed to the handles must be taken into account.
*/
const getPercentageSize = pixelSize => {
const { width: panelGroupWidth } = resizablePanelGroupElemRef.current.getBoundingClientRect();
const { width: panelGroupWidth } = resizablePanelGroupElemRef.current?.getBoundingClientRect();
return (pixelSize / (panelGroupWidth - resizableHandlesWidth.current)) * 100;
};
@@ -257,7 +257,7 @@ const useResizablePanels = (
* Note that the width attributed to the handles must be taken into account.
*/
const getExpandedPixelWidth = percentageSize => {
const { width: panelGroupWidth } = resizablePanelGroupElemRef.current.getBoundingClientRect();
const { width: panelGroupWidth } = resizablePanelGroupElemRef.current?.getBoundingClientRect();
const expandedWidth =
(percentageSize / 100) * (panelGroupWidth - resizableHandlesWidth.current) -
panelGroupDefinition.shared.expandedInsideBorderSize;
+40 -19
View File
@@ -1,42 +1,61 @@
import ToolbarDivider from './Toolbar/ToolbarDivider';
import { ToolbarButton as ToolbarButtonLegacy } from '@ohif/ui';
import { ToolButton, utils } from '@ohif/ui-next';
import ToolbarLayoutSelectorWithServices from './Toolbar/ToolbarLayoutSelector';
import ToolbarSplitButtonWithServices from './Toolbar/ToolbarSplitButtonWithServices';
import ToolbarButtonGroupWithServices from './Toolbar/ToolbarButtonGroupWithServices';
import { ToolbarButton } from '@ohif/ui';
// legacy
import ToolbarDividerLegacy from './Toolbar/ToolbarDivider';
import ToolbarSplitButtonWithServicesLegacy from './Toolbar/ToolbarSplitButtonWithServices';
import ToolbarButtonGroupWithServicesLegacy from './Toolbar/ToolbarButtonGroupWithServices';
import { ProgressDropdownWithService } from './Components/ProgressDropdownWithService';
const getClassName = isToggled => {
return {
className: isToggled
? '!text-primary-active'
: '!text-common-bright hover:!bg-primary-dark hover:text-primary-light',
};
};
// new
import ToolButtonListWrapper from './Toolbar/ToolButtonListWrapper';
import { ToolBoxButtonGroupWrapper, ToolBoxButtonWrapper } from './Toolbar/ToolBoxWrapper';
export default function getToolbarModule({ commandsManager, servicesManager }: withAppTypes) {
const { cineService } = servicesManager.services;
return [
// new
{
name: 'ohif.toolButton',
defaultComponent: ToolButton,
},
{
name: 'ohif.toolButtonList',
defaultComponent: ToolButtonListWrapper,
},
{
name: 'ohif.toolBoxButtonGroup',
defaultComponent: ToolBoxButtonGroupWrapper,
},
{
name: 'ohif.toolBoxButton',
defaultComponent: ToolBoxButtonWrapper,
},
// legacy
{
name: 'ohif.radioGroup',
defaultComponent: ToolbarButton,
defaultComponent: ToolbarButtonLegacy,
},
{
name: 'ohif.buttonGroup',
defaultComponent: ToolbarButtonGroupWithServicesLegacy,
},
{
name: 'ohif.divider',
defaultComponent: ToolbarDivider,
defaultComponent: ToolbarDividerLegacy,
},
{
name: 'ohif.splitButton',
defaultComponent: ToolbarSplitButtonWithServices,
defaultComponent: ToolbarSplitButtonWithServicesLegacy,
},
// others
{
name: 'ohif.layoutSelector',
defaultComponent: props =>
ToolbarLayoutSelectorWithServices({ ...props, commandsManager, servicesManager }),
},
{
name: 'ohif.buttonGroup',
defaultComponent: ToolbarButtonGroupWithServices,
},
{
name: 'ohif.progressDropdown',
defaultComponent: ProgressDropdownWithService,
@@ -66,7 +85,9 @@ export default function getToolbarModule({ commandsManager, servicesManager }: w
name: 'evaluate.cine',
evaluate: () => {
const isToggled = cineService.getState().isCineEnabled;
return getClassName(isToggled);
return {
className: utils.getToggledClassName(isToggled),
};
},
},
];
@@ -13,9 +13,8 @@ function usePatientInfo(servicesManager: AppTypes.ServicesManager) {
PatientDOB: '',
});
const [isMixedPatients, setIsMixedPatients] = useState(false);
const displaySets = displaySetService.getActiveDisplaySets();
const checkMixedPatients = PatientID => {
const checkMixedPatients = (PatientID: string) => {
const displaySets = displaySetService.getActiveDisplaySets();
let isMixedPatients = false;
displaySets.forEach(displaySet => {
@@ -30,8 +29,11 @@ function usePatientInfo(servicesManager: AppTypes.ServicesManager) {
setIsMixedPatients(isMixedPatients);
};
const updatePatientInfo = () => {
const displaySet = displaySets[0];
const updatePatientInfo = ({ displaySetsAdded }) => {
if (!displaySetsAdded.length) {
return;
}
const displaySet = displaySetsAdded[0];
const instance = displaySet?.instances?.[0] || displaySet?.instance;
if (!instance) {
return;
@@ -39,7 +41,7 @@ function usePatientInfo(servicesManager: AppTypes.ServicesManager) {
setPatientInfo({
PatientID: instance.PatientID || null,
PatientName: instance.PatientName ? formatPN(instance.PatientName ) : null,
PatientName: instance.PatientName ? formatPN(instance.PatientName) : null,
PatientSex: instance.PatientSex || null,
PatientDOB: formatDate(instance.PatientBirthDate) || null,
});
@@ -49,15 +51,11 @@ function usePatientInfo(servicesManager: AppTypes.ServicesManager) {
useEffect(() => {
const subscription = displaySetService.subscribe(
displaySetService.EVENTS.DISPLAY_SETS_ADDED,
() => updatePatientInfo()
props => updatePatientInfo(props)
);
return () => subscription.unsubscribe();
}, []);
useEffect(() => {
updatePatientInfo();
}, [displaySets]);
return { patientInfo, isMixedPatients };
}
-1
View File
@@ -39,7 +39,6 @@ import { PanelStudyBrowserHeader } from './Panels/StudyBrowser/PanelStudyBrowser
import * as utils from './utils';
import MoreDropdownMenu from './Components/MoreDropdownMenu';
import requestDisplaySetCreationForStudy from './Panels/requestDisplaySetCreationForStudy';
const defaultExtension: Types.Extensions.Extension = {
/**
* Only required property. Should be a unique value across all extensions.