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:
parent
ad126c6c28
commit
87c4ea1fe5
8 changed files with 173 additions and 269 deletions
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue