From 6f4c74f0ce2501e97c2c2cd5616c0a1b7b674042 Mon Sep 17 00:00:00 2001
From: gibsonliketheguitar
<66423127+gibsonliketheguitar@users.noreply.github.com>
Date: Wed, 5 Jan 2022 18:47:04 +0700
Subject: [PATCH 1/2] add context menu for cells
---
src/components/Table/CellMenu/MenuContent.tsx | 42 +++++++++++++++
src/components/Table/CellMenu/MenuRow.tsx | 16 ++++++
src/components/Table/CellMenu/index.tsx | 53 +++++++++++++++++++
src/components/Table/TableRow.tsx | 24 +++++++--
src/components/Table/index.tsx | 3 +-
src/contexts/ProjectContext.tsx | 5 ++
6 files changed, 139 insertions(+), 4 deletions(-)
create mode 100644 src/components/Table/CellMenu/MenuContent.tsx
create mode 100644 src/components/Table/CellMenu/MenuRow.tsx
create mode 100644 src/components/Table/CellMenu/index.tsx
diff --git a/src/components/Table/CellMenu/MenuContent.tsx b/src/components/Table/CellMenu/MenuContent.tsx
new file mode 100644
index 000000000..c8858e1c4
--- /dev/null
+++ b/src/components/Table/CellMenu/MenuContent.tsx
@@ -0,0 +1,42 @@
+import { Menu } from "@mui/material";
+import MenuRow, { IMenuRow } from "./MenuRow";
+
+interface IMenuContents {
+ anchorEl: HTMLElement;
+ open: boolean;
+ handleClose: () => void;
+ items: IMenuRow[];
+}
+
+export function MenuContents({
+ anchorEl,
+ open,
+ handleClose,
+ items,
+}: IMenuContents) {
+ const handleContext = (e: React.MouseEvent) => e.preventDefault();
+ return (
+
+ );
+}
diff --git a/src/components/Table/CellMenu/MenuRow.tsx b/src/components/Table/CellMenu/MenuRow.tsx
new file mode 100644
index 000000000..d8e8f5262
--- /dev/null
+++ b/src/components/Table/CellMenu/MenuRow.tsx
@@ -0,0 +1,16 @@
+import { ListItemIcon, ListItemText, MenuItem } from "@mui/material";
+
+export interface IMenuRow {
+ onClick: () => void;
+ icon: JSX.Element;
+ label: string;
+}
+
+export default function MenuRow({ onClick, icon, label }: IMenuRow) {
+ return (
+
+ );
+}
diff --git a/src/components/Table/CellMenu/index.tsx b/src/components/Table/CellMenu/index.tsx
new file mode 100644
index 000000000..f23b2bf42
--- /dev/null
+++ b/src/components/Table/CellMenu/index.tsx
@@ -0,0 +1,53 @@
+import React from "react";
+import { PopoverProps } from "@mui/material";
+import CopyCells from "@src/assets/icons/CopyCells";
+import { useProjectContext } from "@src/contexts/ProjectContext";
+import { MenuContents } from "./MenuContent";
+
+export type CellMenuRef = {
+ anchorEl: HTMLElement | null;
+ setAnchorEl: React.Dispatch<
+ React.SetStateAction
+ >;
+};
+
+export default function CellMenu() {
+ const { cellMenuRef }: any = useProjectContext();
+ const [anchorEl, setAnchorEl] = React.useState(null);
+ const open = Boolean(anchorEl);
+
+ if (cellMenuRef)
+ cellMenuRef.current = {
+ anchorEl,
+ setAnchorEl,
+ } as {};
+
+ const handleClose = () => setAnchorEl(null);
+ const handleCopy = () => {
+ const selected: any = anchorEl?.innerHTML;
+ const copy = navigator.clipboard.writeText(selected);
+ const onSuccess = () => {
+ console.log("Copied");
+ var promise = navigator.clipboard.readText();
+ promise.then((text) => console.log(text));
+ };
+ const onFail = () => console.log("Fail to copy");
+
+ copy.then(onSuccess, onFail);
+ handleClose();
+ };
+
+ const cellMenuAction = [
+ { label: "Copy", icon: , color: "", onClick: handleCopy },
+ ];
+
+ if (!cellMenuRef.current || !open) return <>>;
+ return (
+
+ );
+}
diff --git a/src/components/Table/TableRow.tsx b/src/components/Table/TableRow.tsx
index 3d5ad0a15..7d773a8c9 100644
--- a/src/components/Table/TableRow.tsx
+++ b/src/components/Table/TableRow.tsx
@@ -1,4 +1,5 @@
-import { Fragment } from "react";
+import { useProjectContext } from "@src/contexts/ProjectContext";
+import { Fragment, useEffect } from "react";
import { Row, RowRendererProps } from "react-data-grid";
import OutOfOrderIndicator from "./OutOfOrderIndicator";
@@ -8,9 +9,26 @@ export default function TableRow(props: RowRendererProps) {
return (
-
+
+
+
);
- return
;
+ return (
+
+
+
+ );
}
+
+const ContextMenu = (props: any) => {
+ const { cellMenuRef }: any = useProjectContext();
+
+ const handleClick = (e: any) => {
+ e.preventDefault();
+ const input = e?.target as HTMLElement;
+ cellMenuRef.current.setAnchorEl(input);
+ };
+ return {props.children};
+};
diff --git a/src/components/Table/index.tsx b/src/components/Table/index.tsx
index 81f689616..a4cb16ee2 100644
--- a/src/components/Table/index.tsx
+++ b/src/components/Table/index.tsx
@@ -17,6 +17,7 @@ import DataGrid, {
import Loading from "@src/components/Loading";
import TableContainer, { OUT_OF_ORDER_MARGIN } from "./TableContainer";
import TableHeader from "../TableHeader";
+import CellMenu from "./CellMenu";
import ColumnHeader from "./ColumnHeader";
import ColumnMenu from "./ColumnMenu";
import FinalColumnHeader from "./FinalColumnHeader";
@@ -251,8 +252,8 @@ export default function Table() {
)}
-
+
;
// A ref to the side drawer state. Prevents unnecessary re-renders
sideDrawerRef: React.MutableRefObject;
+ //A ref to the cell menu. Prevents unnecessary re-render
+ cellMenuRef: React.MutableRefObject;
// A ref to the column menu. Prevents unnecessary re-renders
columnMenuRef: React.MutableRefObject;
// A ref ot the import wizard. Prevents unnecessary re-renders
@@ -385,6 +388,7 @@ export const ProjectContextProvider: React.FC = ({ children }) => {
const dataGridRef = useRef(null);
const sideDrawerRef = useRef();
const columnMenuRef = useRef();
+ const cellMenuRef = useRef();
const importWizardRef = useRef();
return (
@@ -403,6 +407,7 @@ export const ProjectContextProvider: React.FC = ({ children }) => {
table,
dataGridRef,
sideDrawerRef,
+ cellMenuRef,
columnMenuRef,
importWizardRef,
rowyRun: _rowyRun,
From c9e7c9f59cfb3a5f417d74978bbdd61f96892190 Mon Sep 17 00:00:00 2001
From: gibsonliketheguitar
<66423127+gibsonliketheguitar@users.noreply.github.com>
Date: Fri, 7 Jan 2022 22:17:01 +0700
Subject: [PATCH 2/2] copy and paste feature. onSelectedCell fcn provides the
index to save cell data into jotai
---
src/assets/icons/Paste.tsx | 10 ++++
src/assets/icons/PasteFromClipboard.tsx | 10 ++++
src/components/SideDrawer/index.tsx | 2 +-
src/components/Table/CellMenu/index.tsx | 61 +++++++++++++++++++++----
src/components/Table/TableRow.tsx | 2 +-
src/components/Table/index.tsx | 16 ++++++-
6 files changed, 88 insertions(+), 13 deletions(-)
create mode 100644 src/assets/icons/Paste.tsx
create mode 100644 src/assets/icons/PasteFromClipboard.tsx
diff --git a/src/assets/icons/Paste.tsx b/src/assets/icons/Paste.tsx
new file mode 100644
index 000000000..d86b008ab
--- /dev/null
+++ b/src/assets/icons/Paste.tsx
@@ -0,0 +1,10 @@
+import SvgIcon, { SvgIconProps } from "@mui/material/SvgIcon";
+import { mdiContentPaste } from "@mdi/js";
+
+export default function Paste(props: SvgIconProps) {
+ return (
+
+
+
+ );
+}
diff --git a/src/assets/icons/PasteFromClipboard.tsx b/src/assets/icons/PasteFromClipboard.tsx
new file mode 100644
index 000000000..65cd57fd0
--- /dev/null
+++ b/src/assets/icons/PasteFromClipboard.tsx
@@ -0,0 +1,10 @@
+import SvgIcon, { SvgIconProps } from "@mui/material/SvgIcon";
+import { mdiClipboardArrowDownOutline } from "@mdi/js";
+
+export default function PasteFromClipboard(props: SvgIconProps) {
+ return (
+
+
+
+ );
+}
diff --git a/src/components/SideDrawer/index.tsx b/src/components/SideDrawer/index.tsx
index a7ebedadb..f8aca780e 100644
--- a/src/components/SideDrawer/index.tsx
+++ b/src/components/SideDrawer/index.tsx
@@ -34,8 +34,8 @@ export default function SideDrawer() {
const [cell, setCell] = useState(null);
const [open, setOpen] = useState(false);
- if (sideDrawerRef) sideDrawerRef.current = { cell, setCell, open, setOpen };
+ if (sideDrawerRef) sideDrawerRef.current = { cell, setCell, open, setOpen };
const handleNavigate = (direction: "up" | "down") => () => {
if (!tableState?.rows) return;
diff --git a/src/components/Table/CellMenu/index.tsx b/src/components/Table/CellMenu/index.tsx
index f23b2bf42..37bf62db4 100644
--- a/src/components/Table/CellMenu/index.tsx
+++ b/src/components/Table/CellMenu/index.tsx
@@ -1,10 +1,23 @@
import React from "react";
import { PopoverProps } from "@mui/material";
import CopyCells from "@src/assets/icons/CopyCells";
+import Paste from "@src/assets/icons/Paste";
import { useProjectContext } from "@src/contexts/ProjectContext";
import { MenuContents } from "./MenuContent";
+import _find from "lodash/find";
+import { atom, useAtom } from "jotai";
+import PasteFromClipboard from "@src/assets/icons/PasteFromClipboard";
+
+export const cellMenuDataAtom = atom("");
+
+export type SelectedCell = {
+ rowIndex: number;
+ colIndex: number;
+};
export type CellMenuRef = {
+ selectedCell: SelectedCell;
+ setSelectedCell: React.Dispatch>;
anchorEl: HTMLElement | null;
setAnchorEl: React.Dispatch<
React.SetStateAction
@@ -12,33 +25,63 @@ export type CellMenuRef = {
};
export default function CellMenu() {
- const { cellMenuRef }: any = useProjectContext();
+ const { cellMenuRef, tableState, updateCell }: any = useProjectContext();
const [anchorEl, setAnchorEl] = React.useState(null);
+ const [selectedCell, setSelectedCell] = React.useState();
+ const [cellMenuData, setCellMenuData] = useAtom(cellMenuDataAtom);
const open = Boolean(anchorEl);
if (cellMenuRef)
cellMenuRef.current = {
anchorEl,
setAnchorEl,
+ selectedCell,
+ setSelectedCell,
} as {};
const handleClose = () => setAnchorEl(null);
const handleCopy = () => {
- const selected: any = anchorEl?.innerHTML;
- const copy = navigator.clipboard.writeText(selected);
- const onSuccess = () => {
- console.log("Copied");
- var promise = navigator.clipboard.readText();
- promise.then((text) => console.log(text));
- };
+ const cols = _find(tableState.columns, { index: selectedCell.colIndex });
+ const rows = tableState.rows[selectedCell.rowIndex];
+ const cell = rows[cols.key];
+ const formatData = typeof cell === "object" ? JSON.stringify(cell) : cell;
+ setCellMenuData(formatData);
const onFail = () => console.log("Fail to copy");
+ const onSuccess = () => console.log;
+ const copy = navigator.clipboard.writeText(formatData);
copy.then(onSuccess, onFail);
+
handleClose();
};
+ const handlePaste = () => {
+ const targetCol = _find(tableState.columns, {
+ index: selectedCell.colIndex,
+ });
+ const targetRow = tableState.rows[selectedCell.rowIndex];
+ if (cellMenuData) updateCell(targetRow.ref, targetCol.key, cellMenuData);
+ handleClose();
+ };
+
+ const handlePasteFromClipboard = () => {
+ const paste = navigator.clipboard.readText();
+ const targetCol = _find(tableState.columns, {
+ index: selectedCell.colIndex,
+ });
+ const targetRow = tableState.rows[selectedCell.rowIndex];
+ paste.then((clipText) =>
+ updateCell(targetRow.ref, targetCol.key, clipText)
+ );
+ };
const cellMenuAction = [
- { label: "Copy", icon: , color: "", onClick: handleCopy },
+ { label: "Copy", icon: , onClick: handleCopy },
+ { label: "Paste", icon: , onClick: handlePaste },
+ {
+ label: "Paste from Clipboard",
+ icon: ,
+ onClick: handlePasteFromClipboard,
+ },
];
if (!cellMenuRef.current || !open) return <>>;
diff --git a/src/components/Table/TableRow.tsx b/src/components/Table/TableRow.tsx
index 7d773a8c9..c1729a6bf 100644
--- a/src/components/Table/TableRow.tsx
+++ b/src/components/Table/TableRow.tsx
@@ -1,5 +1,5 @@
import { useProjectContext } from "@src/contexts/ProjectContext";
-import { Fragment, useEffect } from "react";
+import { Fragment } from "react";
import { Row, RowRendererProps } from "react-data-grid";
import OutOfOrderIndicator from "./OutOfOrderIndicator";
diff --git a/src/components/Table/index.tsx b/src/components/Table/index.tsx
index a4cb16ee2..affa14d34 100644
--- a/src/components/Table/index.tsx
+++ b/src/components/Table/index.tsx
@@ -44,8 +44,14 @@ const rowClass = (row: any) => (row._rowy_outOfOrder ? "out-of-order" : "");
//const SelectColumn = { ..._SelectColumn, width: 42, maxWidth: 42 };
export default function Table() {
- const { tableState, tableActions, dataGridRef, sideDrawerRef, updateCell } =
- useProjectContext();
+ const {
+ tableState,
+ tableActions,
+ dataGridRef,
+ cellMenuRef,
+ sideDrawerRef,
+ updateCell,
+ } = useProjectContext();
const { userDoc } = useAppContext();
const userDocHiddenFields =
@@ -246,6 +252,12 @@ export default function Table() {
});
}
}}
+ onSelectedCellChange={({ rowIdx, idx }) => {
+ cellMenuRef?.current?.setSelectedCell({
+ rowIndex: rowIdx,
+ colIndex: idx,
+ });
+ }}
/>
) : (