excalidraw/src/components/ContextMenu.tsx

110 lines
2.7 KiB
TypeScript
Raw Normal View History

2020-01-07 07:50:59 +05:00
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import clsx from "clsx";
import { Popover } from "./Popover";
2020-01-07 07:50:59 +05:00
2020-04-10 18:09:29 -04:00
import "./ContextMenu.scss";
import {
getShortcutFromShortcutName,
ShortcutName,
} from "../actions/shortcuts";
2020-01-07 07:50:59 +05:00
type ContextMenuOption = {
checked?: boolean;
shortcutName: ShortcutName;
2020-01-07 07:50:59 +05:00
label: string;
action(): void;
};
type Props = {
options: ContextMenuOption[];
onCloseRequest?(): void;
top: number;
left: number;
};
const ContextMenu = ({ options, onCloseRequest, top, left }: Props) => {
const isDarkTheme = !!document
.querySelector(".excalidraw")
?.classList.contains("Appearance_dark");
return (
<div
className={clsx("excalidraw", {
"Appearance_dark Appearance_dark-background-none": isDarkTheme,
})}
>
<Popover
onCloseRequest={onCloseRequest}
top={top}
left={left}
fitInViewport={true}
>
<ul
className="context-menu"
onContextMenu={(event) => event.preventDefault()}
>
{options.map(({ action, checked, shortcutName, label }, idx) => (
<li data-testid={shortcutName} key={idx} onClick={onCloseRequest}>
<button
className={`context-menu-option
${shortcutName === "delete" ? "dangerous" : ""}
${checked ? "checkmark" : ""}`}
onClick={action}
>
<div className="context-menu-option__label">{label}</div>
<div className="context-menu-option__shortcut">
{shortcutName
? getShortcutFromShortcutName(shortcutName)
: ""}
</div>
</button>
</li>
))}
</ul>
</Popover>
</div>
);
};
2020-01-07 07:50:59 +05:00
let contextMenuNode: HTMLDivElement;
const getContextMenuNode = (): HTMLDivElement => {
2020-01-07 07:50:59 +05:00
if (contextMenuNode) {
return contextMenuNode;
}
const div = document.createElement("div");
document.body.appendChild(div);
return (contextMenuNode = div);
};
2020-01-07 07:50:59 +05:00
type ContextMenuParams = {
options: (ContextMenuOption | false | null | undefined)[];
top: number;
left: number;
};
const handleClose = () => {
2020-01-07 07:50:59 +05:00
unmountComponentAtNode(getContextMenuNode());
};
2020-01-07 07:50:59 +05:00
export default {
push(params: ContextMenuParams) {
const options = Array.of<ContextMenuOption>();
params.options.forEach((option) => {
2020-01-07 07:50:59 +05:00
if (option) {
options.push(option);
}
});
if (options.length) {
render(
<ContextMenu
top={params.top}
left={params.left}
options={options}
onCloseRequest={handleClose}
/>,
2020-01-24 12:04:54 +02:00
getContextMenuNode(),
2020-01-07 07:50:59 +05:00
);
}
2020-01-24 12:04:54 +02:00
},
2020-01-07 07:50:59 +05:00
};