Uniformize styles for dialogs and popovers, add ContextMenu

Change-Id: I8687b2d171f9c15e8eb8dd5ba2a32cdaa27b70d6
diff --git a/client/src/components/ContextMenu.tsx b/client/src/components/ContextMenu.tsx
new file mode 100644
index 0000000..6ac1363
--- /dev/null
+++ b/client/src/components/ContextMenu.tsx
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program.  If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+import { Menu, MenuProps, PopoverPosition, PopoverReference, styled } from '@mui/material';
+import { MouseEventHandler, useCallback, useMemo, useState } from 'react';
+
+import PopoverList, { PopoverListItemData } from './PopoverList';
+
+export interface ContextMenuHandler {
+  props: {
+    open: boolean;
+    onClose: () => void;
+    anchorPosition: PopoverPosition | undefined;
+    anchorReference: PopoverReference | undefined;
+  };
+  handleAnchorPosition: MouseEventHandler;
+}
+
+export const useContextMenuHandler = (): ContextMenuHandler => {
+  const [anchorPosition, setAnchorPosition] = useState<PopoverPosition | undefined>(undefined);
+
+  const handleAnchorPosition = useCallback<MouseEventHandler>(
+    (event) => {
+      event.preventDefault();
+      setAnchorPosition((anchorPosition) =>
+        anchorPosition === undefined ? { top: event.clientY, left: event.clientX } : undefined
+      );
+    },
+    [setAnchorPosition]
+  );
+
+  const onClose = useCallback(() => setAnchorPosition(undefined), [setAnchorPosition]);
+
+  return useMemo(
+    () => ({
+      props: {
+        open: !!anchorPosition,
+        onClose,
+        anchorPosition,
+        anchorReference: 'anchorPosition',
+      },
+      handleAnchorPosition,
+    }),
+    [anchorPosition, handleAnchorPosition, onClose]
+  );
+};
+
+interface ContextMenuProps extends MenuProps {
+  items: PopoverListItemData[];
+}
+
+const ContextMenu = styled(({ items, ...props }: ContextMenuProps) => (
+  <Menu {...props}>
+    <PopoverList items={items} onClose={props.onClose} />
+  </Menu>
+))(() => ({
+  '& .MuiPaper-root': {
+    borderRadius: '5px 20px 20px 20px',
+    boxShadow: '3px 3px 7px #00000029',
+  },
+  '& .MuiMenu-list': {
+    padding: '0px',
+  },
+}));
+
+export default ContextMenu;