TelegramPWA/src/components/ui/MenuItem.tsx
2026-02-22 23:48:03 +01:00

146 lines
3.3 KiB
TypeScript

import type { IconName } from '../../types/icons';
import { IS_TEST } from '../../config';
import buildClassName from '../../util/buildClassName';
import useAppLayout from '../../hooks/useAppLayout';
import useLang from '../../hooks/useLang';
import useLastCallback from '../../hooks/useLastCallback';
import Icon from '../common/icons/Icon';
import './MenuItem.scss';
export type MenuItemProps = {
customIcon?: React.ReactNode;
className?: string;
children: React.ReactNode;
href?: string;
rel?: string;
target?: string;
download?: string;
disabled?: boolean;
destructive?: boolean;
ariaLabel?: string;
withWrap?: boolean;
withPreventDefaultOnMouseDown?: boolean;
clickArg?: number;
onClick?: (e: React.SyntheticEvent<HTMLDivElement | HTMLAnchorElement>, arg?: number) => void;
onContextMenu?: (e: React.UIEvent) => void;
} & ({
icon: 'A' | 'K';
isCharIcon: true;
} | {
icon?: IconName;
isCharIcon?: false;
});
const MenuItem = (props: MenuItemProps) => {
const {
icon,
isCharIcon,
customIcon,
className,
children,
href,
target,
download,
disabled,
destructive,
ariaLabel,
withWrap,
rel = 'noopener noreferrer',
withPreventDefaultOnMouseDown,
clickArg,
onClick,
onContextMenu,
} = props;
const lang = useLang();
const { isTouchScreen } = useAppLayout();
const handleClick = useLastCallback((e: React.MouseEvent<HTMLDivElement>) => {
if (disabled || !onClick) {
e.preventDefault();
return;
}
onClick(e, clickArg);
});
const handleKeyDown = useLastCallback((e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.keyCode !== 13 && e.keyCode !== 32) {
return;
}
if (disabled || !onClick) {
e.preventDefault();
return;
}
onClick(e, clickArg);
});
const handleMouseDown = useLastCallback((e: React.SyntheticEvent<HTMLDivElement | HTMLAnchorElement>) => {
if (withPreventDefaultOnMouseDown) {
e.preventDefault();
}
});
const fullClassName = buildClassName(
'MenuItem',
className,
disabled && 'disabled',
destructive && 'destructive',
!isTouchScreen && 'compact',
withWrap && 'wrap',
!icon && !customIcon && 'text-only',
);
const content = (
<>
{!customIcon && icon && (
<Icon name={isCharIcon ? 'char' : icon} character={isCharIcon ? icon : undefined} />
)}
{customIcon}
{children}
</>
);
if (href && !disabled) {
return (
<a
tabIndex={0}
className={fullClassName}
href={href}
download={download}
aria-label={ariaLabel}
title={ariaLabel}
target={target || (href.startsWith(window.location.origin) || IS_TEST ? '_self' : '_blank')}
rel={rel}
dir={lang.isRtl ? 'rtl' : undefined}
onClick={onClick}
onMouseDown={handleMouseDown}
>
{content}
</a>
);
}
return (
<div
role="menuitem"
tabIndex={0}
className={fullClassName}
onClick={handleClick}
onKeyDown={handleKeyDown}
onMouseDown={handleMouseDown}
onContextMenu={onContextMenu}
aria-label={ariaLabel}
title={ariaLabel}
dir={lang.isRtl ? 'rtl' : undefined}
>
{content}
</div>
);
};
export default MenuItem;