Merged PR 171: Refactor the multiple context menus into a single component + Fix eslint

Refactor the multiple context menus into a single component + Fix eslint
This commit is contained in:
Eric Nguyen 2022-08-29 15:03:47 +00:00
parent ad126c6c28
commit 87c4ea1fe5
8 changed files with 173 additions and 269 deletions

View file

@ -1,23 +1,109 @@
import * as React from 'react';
import { IPoint } from '../../Interfaces/IPoint';
import { MenuItem } from './MenuItem';
interface IMenuProps {
getListener: () => HTMLElement | null
actions: Map<string, IAction[]>
className?: string
x: number
y: number
isOpen: boolean
children: React.ReactNode[] | React.ReactNode
}
export interface IAction {
/** displayed */
text: string
/** function to be called on button click */
action: (target: HTMLElement) => void
}
function UseMouseEvents(
getListener: () => HTMLElement | null,
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>,
setContextMenuPosition: React.Dispatch<React.SetStateAction<IPoint>>,
setTarget: React.Dispatch<React.SetStateAction<HTMLElement | undefined>>
): void {
React.useEffect(() => {
function OnContextMenu(event: MouseEvent): void {
event.preventDefault();
const contextMenuPosition: IPoint = { x: event.pageX, y: event.pageY };
setIsOpen(true);
setContextMenuPosition(contextMenuPosition);
setTarget(event.target as HTMLElement); // this is wrong since target can be null and not undefined
}
function OnLeftClick(): void {
setIsOpen(false);
}
getListener()?.addEventListener(
'contextmenu',
OnContextMenu
);
window.addEventListener(
'click',
OnLeftClick
);
return () => {
getListener()?.removeEventListener(
'contextmenu',
OnContextMenu
);
window.removeEventListener(
'click',
OnLeftClick
);
};
});
}
export function Menu(props: IMenuProps): JSX.Element {
const visible = props.isOpen ? 'visible opacity-1' : 'invisible opacity-0';
const [isOpen, setIsOpen] = React.useState(false);
const [contextMenuPosition, setContextMenuPosition] = React.useState<IPoint>({
x: 0,
y: 0
});
const [target, setTarget] = React.useState<HTMLElement>();
UseMouseEvents(
props.getListener,
setIsOpen,
setContextMenuPosition,
setTarget
);
let children;
if (target !== undefined) {
for (const className of target.classList) {
const actions = props.actions.get(className);
if (actions === undefined) {
continue;
}
children = actions.map((action, index) =>
<MenuItem
key={`contextmenu-item-${index}`}
className="contextmenu-item"
text={action.text}
onClick={() => action.action(target)} />
);
break;
};
}
const visible = isOpen ? 'visible opacity-1' : 'invisible opacity-0';
return (
<div
className={`fixed ${props.className ?? ''} ${visible}`}
style={{
left: props.x,
top: props.y
left: contextMenuPosition.x,
top: contextMenuPosition.y
}}>
{props.children}
{ children }
</div>
);
}