Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-use-before-define */
import { ReactElement, useEffect } from 'react';
import { Item, ItemParams, Separator, Submenu } from 'react-contexify';
import {
Expand Down Expand Up @@ -43,75 +44,94 @@ interface MenuItem {
onMouseOut?: (itemId: string) => void;
}

type MenuItemGroup = MenuItem[];

interface MenuProps {
id: CONTEXT_MENU_ID;
menuItems: MenuItem[];
menuItems: Array<MenuItem | MenuItemGroup>;
handleMenuChange: (params: ItemParams) => void;
}

const assembleMenuItems = (
menuItems: MenuItem[],
const isMenuItemGroup = (
menuItemOrGroup: MenuItem | MenuItemGroup,
): menuItemOrGroup is MenuItemGroup => {
return Array.isArray(menuItemOrGroup);
};

const assembleMenuItem = (
{
name,
title,
icon,
hidden,
disabled,
isMenuTitle,
subMenuItems,
onMouseOver,
onMouseOut,
}: MenuItem,
handleMenuChange: (params: ItemParams) => void,
) => {
const MENU_CLOSING_TIME = 500;
let isMouseOverThrottling = false;
const items: ReactElement[] = [];

menuItems.forEach(
(
{
name,
title,
icon,
hidden,
disabled,
isMenuTitle,
separator,
subMenuItems,
onMouseOver,
onMouseOut,
},
index,
) => {
const item =
subMenuItems && subMenuItems.length ? (
<Submenu label={title} data-testid={name} key={name}>
{assembleMenuItems(subMenuItems, handleMenuChange)}
</Submenu>
) : (
<Item
id={name}
onClick={(params) => {
isMouseOverThrottling = true;
setTimeout(() => {
isMouseOverThrottling = false;
}, MENU_CLOSING_TIME);
handleMenuChange(params);
}}
key={name}
data-testid={name}
hidden={hidden}
disabled={disabled}
className={isMenuTitle ? 'contexify_item-title' : ''}
onMouseOver={() => {
if (isMouseOverThrottling) {
return;
}
onMouseOver?.(name);
}}
onMouseOut={() => onMouseOut?.(name)}
>
{icon && <span className="context_menu-icon">{icon}</span>}
<span className="context_menu-text">{title}</span>
</Item>
);
items.push(item);
if (separator) {
items.push(<Separator key={index} />);
}
},
return subMenuItems && subMenuItems.length ? (
<Submenu label={title} data-testid={name} key={name}>
{assembleMenuItems(subMenuItems, handleMenuChange)}
</Submenu>
) : (
<Item
id={name}
onClick={(params) => {
isMouseOverThrottling = true;
setTimeout(() => {
isMouseOverThrottling = false;
}, MENU_CLOSING_TIME);
handleMenuChange(params);
}}
key={name}
data-testid={name}
hidden={hidden}
disabled={disabled}
className={isMenuTitle ? 'contexify_item-title' : ''}
onMouseOver={() => {
if (isMouseOverThrottling) {
return;
}
onMouseOver?.(name);
}}
onMouseOut={() => onMouseOut?.(name)}
>
{icon && <span className="context_menu-icon">{icon}</span>}
<span className="context_menu-text">{title}</span>
</Item>
);
return items;
};

const assembleMenuItems = (
menuItems: Array<MenuItem | MenuItemGroup>,
handleMenuChange: (params: ItemParams) => void,
) => {
return menuItems.map((menuItemOrGroup, menuItemOrGroupIndex) => {
return isMenuItemGroup(menuItemOrGroup) ? (
<>
{menuItemOrGroupIndex !== 0 &&
menuItemOrGroup.every((item) =>
typeof item.hidden === 'function'
? item.hidden({ props: {} }) // TODO : pass actual props
: item.hidden,
) && <Separator key={menuItemOrGroupIndex} />}
{menuItemOrGroup.map((item) =>
assembleMenuItem(item, handleMenuChange),
)}
</>
) : (
<>
{assembleMenuItem(menuItemOrGroup, handleMenuChange)}
{menuItemOrGroup.separator && <Separator key={menuItemOrGroupIndex} />}
</>
);
});
};

export const ContextMenu = ({ id, handleMenuChange, menuItems }: MenuProps) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,23 +103,7 @@ export const SequenceItemContextMenu = ({
const isModifyBlockVisible =
!!modifyAminoAcidsMenuItems.length ||
(menuProps?.isSelectedOnlyNucleoelements && !menuProps.hasAntisense);
const menuItems = [
{
name: SequenceItemContextMenuNames.title,
title: menuProps?.title,
isMenuTitle: true,
disabled: true,
hidden: ({
props,
}: {
props?: { sequenceItemRenderer?: BaseSequenceItemRenderer };
}) => {
return (
!props?.sequenceItemRenderer ||
!menuProps?.isSelectedAtLeastOneNucleoelement
);
},
},
const copyPasteItems = [
{
name: SequenceItemContextMenuNames.copy,
title: 'Copy',
Expand All @@ -131,8 +115,9 @@ export const SequenceItemContextMenu = ({
title: 'Paste',
icon: <Icon name={'pasteNavBar' as IconName} />,
disabled: false,
separator: true,
},
];
const startEditSequenceItems = [
{
name: SequenceItemContextMenuNames.editSequence,
title: 'Edit sequence',
Expand All @@ -149,6 +134,8 @@ export const SequenceItemContextMenu = ({
disabled: false,
separator: isAntisenseBlockVisible || isHydrogenBondBlockVisible,
},
];
const antisenseItems = [
{
name: SequenceItemContextMenuNames.createRnaAntisenseStrand,
title: 'Create RNA antisense strand',
Expand Down Expand Up @@ -206,6 +193,8 @@ export const SequenceItemContextMenu = ({
props?: { sequenceItemRenderer?: BaseSequenceItemRenderer };
}) => !props?.sequenceItemRenderer,
},
];
const modificationItems = [
{
name: SequenceItemContextMenuNames.modifyInRnaBuilder,
title: 'Modify in RNA Builder...',
Expand All @@ -229,6 +218,8 @@ export const SequenceItemContextMenu = ({
hidden: !modifyAminoAcidsMenuItems.length,
subMenuItems: modifyAminoAcidsMenuItems,
},
];
const deleteItems = [
{
name: SequenceItemContextMenuNames.delete,
title: 'Delete',
Expand All @@ -237,6 +228,30 @@ export const SequenceItemContextMenu = ({
},
];

const menuItems = [
{
name: SequenceItemContextMenuNames.title,
title: menuProps?.title,
isMenuTitle: true,
disabled: true,
hidden: ({
props,
}: {
props?: { sequenceItemRenderer?: BaseSequenceItemRenderer };
}) => {
return (
!props?.sequenceItemRenderer ||
!menuProps?.isSelectedAtLeastOneNucleoelement
);
},
},
copyPasteItems,
startEditSequenceItems,
antisenseItems,
modificationItems,
deleteItems,
];

const handleMenuChange = ({ id: menuItemId, props }: ItemParams) => {
if (!editor) {
return;
Expand Down
Loading