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 ( + + {items.map((item, indx: number) => ( + + ))} + + ); +} 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 ( + + {icon} + {label} + + ); +} 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, + }); + }} /> ) : (