Files
ohif-viewer/platform/ui/src/components/SplitButton/SplitButton.tsx
T

187 lines
6.0 KiB
TypeScript

import React, { useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import OutsideClickHandler from 'react-outside-click-handler';
import { useTranslation } from 'react-i18next';
import Icon from '../Icon';
import Tooltip from '../Tooltip';
import ListMenu from '../ListMenu';
const baseClasses = {
Button: 'flex items-center rounded-md border-transparent group/button',
Primary: 'h-full rounded-tl-md rounded-bl-md group/primary',
Secondary:
'h-full flex items-center justify-center rounded-tr-md rounded-br-md w-4 border-2 border-transparent group/secondary',
SecondaryIcon: 'w-4 h-full stroke-1',
Separator: 'border-l py-3 ml-0.5',
Content: 'absolute z-10 top-0 mt-12',
};
const classes = {
Button: ({ isExpanded }) =>
classNames(
baseClasses.Button,
!isExpanded && 'hover:!bg-primary-dark hover:border-primary-dark'
),
Interface: 'h-full flex flex-row items-center',
Primary: ({ isExpanded, isActive }) =>
classNames(
baseClasses.Primary,
isActive
? isExpanded
? 'border-primary-dark !bg-primary-dark hover:border-primary-dark !text-primary-light'
: 'border-primary-light bg-primary-light rounded-md'
: `focus:!text-black focus:!rounded-md focus:!border-primary-light focus:!bg-primary-light ${isExpanded ? 'border-primary-dark bg-primary-dark !text-primary-light' : 'border-secondary-dark bg-secondary-dark group-hover/button:border-primary-dark group-hover/button:text-primary-light hover:!bg-primary-dark hover:border-primary-dark focus:!text-black'}`
),
Secondary: ({ isExpanded, primary }) =>
classNames(
baseClasses.Secondary,
isExpanded
? 'bg-primary-light !rounded-tr-md !rounded-br-md'
: primary.isActive
? 'bg-secondary-dark'
: 'hover:bg-primary-dark bg-secondary-dark group-hover/button:border-primary-dark'
),
SecondaryIcon: ({ isExpanded }) =>
classNames(
baseClasses.SecondaryIcon,
isExpanded
? 'text-primary-dark'
: 'text-primary-active group-hover/secondary:text-primary-light'
),
Separator: ({ primary, isExpanded, isHovering }) =>
classNames(
baseClasses.Separator,
isHovering || isExpanded || primary.isActive ? 'border-transparent' : 'border-primary-active'
),
Content: ({ isExpanded }) => classNames(baseClasses.Content, isExpanded ? 'block' : 'hidden'),
};
const DefaultListItemRenderer = props => {
const { t, icon, label, className, isActive } = props;
return (
<div
className={classNames(
'flex h-8 w-full select-none flex-row items-center p-3',
'whitespace-pre text-base',
className,
`${isActive ? 'hover:opacity-80' : 'hover:bg-primary-dark '}`
)}
>
{icon && (
<span className="mr-4">
<Icon
name={icon}
className="h-[28px] w-[28px]"
/>
</span>
)}
<span className="mr-5">{t?.(label)}</span>
</div>
);
};
/**
* This is a more generic version of SplitButton without the isActive
* and other interaction props
*/
const SplitButton = ({
groupId,
primary,
secondary,
items,
renderer = null,
onInteraction,
Component = Icon,
}) => {
const { t } = useTranslation('Buttons');
const [state, setState] = useState({ isHovering: false, isExpanded: false });
const toggleExpanded = () => setState({ ...state, isExpanded: !state.isExpanded });
const setHover = hovering => setState({ ...state, isHovering: hovering });
const collapse = () => setState({ ...state, isExpanded: false });
const listItemRenderer = renderer || DefaultListItemRenderer;
const primaryClassNames = classNames(
classes.Primary({
isExpanded: state.isExpanded,
isActive: primary.isActive,
}),
primary.className
);
return (
<OutsideClickHandler
onOutsideClick={collapse}
disabled={!state.isExpanded}
>
<div
id="SplitButton"
className="relative"
>
<div
className={classes.Button({ ...state })}
style={{ height: '40px' }}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
<div className={classes.Interface}>
<div onClick={collapse}>
<Component
key={primary.id}
{...primary}
onInteraction={onInteraction}
rounded="none"
className={primaryClassNames}
data-tool={primary.id}
data-cy={`${groupId}-split-button-primary`}
/>
</div>
<div className={classes.Separator({ ...state, primary })}></div>
<div
className={classes.Secondary({ ...state, primary })}
onClick={toggleExpanded}
data-cy={`${groupId}-split-button-secondary`}
>
<Tooltip
isDisabled={state.isExpanded || !secondary.tooltip}
content={secondary.tooltip}
className="h-full"
>
<Icon
name={secondary.icon}
className={classes.SecondaryIcon({ ...state })}
/>
</Tooltip>
</div>
</div>
</div>
<div
className={classes.Content({ ...state })}
data-cy={`${groupId}-list-menu`}
>
<ListMenu
items={items}
onClick={collapse}
renderer={args => listItemRenderer({ ...args, t })}
/>
</div>
</div>
</OutsideClickHandler>
);
};
SplitButton.propTypes = {
groupId: PropTypes.string.isRequired,
primary: PropTypes.object.isRequired,
secondary: PropTypes.object.isRequired,
items: PropTypes.array.isRequired,
renderer: PropTypes.func,
isActive: PropTypes.bool,
onInteraction: PropTypes.func.isRequired,
Component: PropTypes.elementType,
interactionType: PropTypes.oneOf(['action', 'tool', 'toggle']),
};
export default SplitButton;