mirror of
https://github.com/captbaritone/webamp.git
synced 2026-01-24 02:36:00 +00:00
81 lines
2.1 KiB
TypeScript
81 lines
2.1 KiB
TypeScript
import React, { ReactNode, useState, useEffect, useCallback } from "react";
|
|
import ContextMenu from "./ContextMenu";
|
|
|
|
interface Props {
|
|
renderContents(): ReactNode;
|
|
children: ReactNode;
|
|
}
|
|
|
|
// Trigger a context menu at the user's cursor position when the user right
|
|
// clicks within this component.
|
|
// For a component that triggers relative to a given component when the user
|
|
// left-clicks see `<ContextMenuTarget />`.
|
|
|
|
// TODO: Consider using nested contexts to ensure we don't ever have multiple
|
|
// non-nested context menus open at a time.
|
|
export default function ContextMenuWraper({
|
|
children,
|
|
renderContents,
|
|
...passThroughProps
|
|
}: Props) {
|
|
const [openPosition, setOpenPosition] = useState<{
|
|
x: number;
|
|
y: number;
|
|
} | null>(null);
|
|
|
|
const closeMenu = useCallback(() => {
|
|
setOpenPosition(null);
|
|
}, []);
|
|
|
|
const handleGlobalClick = useCallback(
|
|
(e: MouseEvent) => {
|
|
if (e.button !== 2) {
|
|
closeMenu();
|
|
}
|
|
},
|
|
[closeMenu]
|
|
);
|
|
|
|
const handleRightClick = useCallback(
|
|
(e: React.MouseEvent<HTMLDivElement>) => {
|
|
const { pageX, pageY } = e;
|
|
// TODO: We could do an initial render to see if the menu fits here
|
|
// and do a second render if it does not.
|
|
setOpenPosition({ x: pageX, y: pageY });
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
},
|
|
[]
|
|
);
|
|
|
|
// Add click-away listeners when window is open
|
|
useEffect(() => {
|
|
if (openPosition == null) {
|
|
return;
|
|
}
|
|
document.addEventListener("click", handleGlobalClick);
|
|
document.body.addEventListener("contextmenu", closeMenu);
|
|
|
|
return () => {
|
|
document.removeEventListener("click", handleGlobalClick);
|
|
document.body.removeEventListener("contextmenu", closeMenu);
|
|
};
|
|
}, [openPosition, closeMenu, handleGlobalClick]);
|
|
|
|
return (
|
|
<div
|
|
onContextMenu={handleRightClick}
|
|
style={{ width: "100%", height: "100%" }}
|
|
{...passThroughProps}
|
|
>
|
|
<ContextMenu
|
|
selected={openPosition != null}
|
|
offsetTop={openPosition?.y ?? 0}
|
|
offsetLeft={openPosition?.x ?? 0}
|
|
>
|
|
{renderContents()}
|
|
</ContextMenu>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|