From fe134cf196b754041b7b94ed6f472d2e2eb5327b Mon Sep 17 00:00:00 2001 From: shams mosowi Date: Fri, 4 Oct 2019 13:57:18 +1000 Subject: [PATCH] handle copy and paste --- package.json | 1 + src/components/Fields/Date.tsx | 2 +- src/components/Fields/LongText.tsx | 6 ++- src/components/Table/grid-fns.tsx | 5 +- src/components/Table/index.tsx | 84 ++++++++++++++++++++++++++++-- src/hooks/useHotkeys.ts | 18 +++++++ yarn.lock | 5 ++ 7 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 src/hooks/useHotkeys.ts diff --git a/package.json b/package.json index a6ea6d5e..c93abbc5 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "csv-parse": "^4.4.6", "date-fns": "^2.0.0-beta.5", "firebase": "^6.6.0", + "hotkeys-js": "^3.7.2", "lodash": "^4.17.15", "ramda": "^0.26.1", "react": "^16.9.0", diff --git a/src/components/Fields/Date.tsx b/src/components/Fields/Date.tsx index fa6dac19..dc9dacdc 100644 --- a/src/components/Fields/Date.tsx +++ b/src/components/Fields/Date.tsx @@ -41,7 +41,7 @@ const Date = (props: Props) => { {fieldType === FieldType.date ? ( { const { value, onSubmit } = props; const [text, setText] = useState(value ? value : ""); - + useEffect(() => { + if (text !== value) setText(value); + }, [value]); const [anchorEl, setAnchorEl] = useState(null); const classes = useStyles(); diff --git a/src/components/Table/grid-fns.tsx b/src/components/Table/grid-fns.tsx index f631e41e..23603c9f 100644 --- a/src/components/Table/grid-fns.tsx +++ b/src/components/Table/grid-fns.tsx @@ -51,8 +51,9 @@ export const DateFormatter = (key: string, fieldType: FieldType) => ( ); }; -export const onGridRowsUpdated = (props: any) => { - const { fromRowData, updated } = props; +export const onGridRowsUpdated = (event: any) => { + const { fromRowData, updated } = event; + onSubmit(Object.keys(updated)[0], fromRowData)(Object.values(updated)[0]); }; export const onCellSelected = (args: any) => {}; diff --git a/src/components/Table/index.tsx b/src/components/Table/index.tsx index fccb0a9c..9574a434 100644 --- a/src/components/Table/index.tsx +++ b/src/components/Table/index.tsx @@ -23,7 +23,7 @@ import { CLOUD_FUNCTIONS } from "firebase/callables"; import Typography from "@material-ui/core/Typography"; import AddIcon from "@material-ui/icons/AddCircle"; import SettingsIcon from "@material-ui/icons/Settings"; - +import useHotkeys from "../../hooks/useHotkeys"; import useWindowSize from "../../hooks/useWindowSize"; import { DraggableHeader } from "react-data-grid-addons"; import Confirmation from "components/Confirmation"; @@ -88,6 +88,10 @@ interface Props { function Table(props: Props) { const { collection, filters } = props; const { tableState, tableActions } = useFiretable(collection); + const [selectedCell, setSelectedCell] = useState<{ row: any; column: any }>({ + row: {}, + column: {}, + }); const [search, setSearch] = useState({ config: undefined, collection: "", @@ -96,6 +100,73 @@ function Table(props: Props) { const size = useWindowSize(); + useHotkeys( + "cmd+c", + () => { + handleCopy(); + }, + [selectedCell] + ); + useHotkeys( + "ctrl+c", + () => { + handleCopy(); + }, + [selectedCell] + ); + useHotkeys( + "cmd+v", + () => { + handlePaste(); + }, + [selectedCell] + ); + useHotkeys( + "ctrl+v", + () => { + handlePaste(); + }, + [selectedCell] + ); + useHotkeys( + "ctrl+x", + () => { + handleCut(); + }, + [selectedCell] + ); + useHotkeys( + "cmd+x", + () => { + handleCut(); + }, + [selectedCell] + ); + const handlePaste = async () => { + const { row, column } = selectedCell; + const newValue = await navigator.clipboard.readText(); + onSubmit(column.key, row)(newValue); + }; + const handleCopy = () => { + const { row, column } = selectedCell; + switch (column.type) { + case FieldType.number: + case FieldType.rating: + case FieldType.email: + case FieldType.simpleText: + case FieldType.PhoneNumber: + navigator.clipboard.writeText(row[column.key]); + break; + + default: + break; + } + }; + const handleCut = () => { + const { row, column } = selectedCell; + navigator.clipboard.writeText(row[column.key]); + onSubmit(column.key, row)(null); + }; useEffect(() => { tableActions.table.set(collection, filters); }, [collection, filters]); @@ -174,7 +245,7 @@ function Table(props: Props) { const onHeaderDrop = (dragged: any, target: any) => { tableActions.column.reorder(dragged, target); }; - let columns = []; + let columns: any[] = []; if (!tableState.loadingColumns) { columns = tableState.columns.map((column: any) => ({ width: 220, @@ -254,7 +325,14 @@ function Table(props: Props) { onGridRowsUpdated={onGridRowsUpdated} enableCellSelect={true} minHeight={tableHeight} - onCellSelected={onCellSelected} + onCellSelected={(coordinates: { + rowIdx: number; + idx: number; + }) => { + const row = rows[coordinates.rowIdx]; + const column = columns[coordinates.idx]; + setSelectedCell({ row, column }); + }} onColumnResize={(idx, width) => tableActions.column.resize(idx, width) } diff --git a/src/hooks/useHotkeys.ts b/src/hooks/useHotkeys.ts new file mode 100644 index 00000000..5c3e2aba --- /dev/null +++ b/src/hooks/useHotkeys.ts @@ -0,0 +1,18 @@ +import hotkeys, { HotkeysEvent } from "hotkeys-js"; +import { useCallback, useEffect } from "react"; + +type CallbackFn = (event: KeyboardEvent, handler: HotkeysEvent) => void; + +export default function useHotkeys( + keys: string, + callback: CallbackFn, + deps: any[] = [] +) { + const memoisedCallback = useCallback(callback, deps); + + useEffect(() => { + hotkeys(keys, memoisedCallback); + + return () => hotkeys.unbind(keys, memoisedCallback); + }, [memoisedCallback]); +} diff --git a/yarn.lock b/yarn.lock index a319ba2e..2a7f90f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5721,6 +5721,11 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546" integrity sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ== +hotkeys-js@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.7.2.tgz#30378b6db40bcd24fe6e51cd19c29ed1898c125f" + integrity sha512-LJIPBgejlklphThig2edlYUiPq3iFDHdsXftvZ0VWrpi330dRwD2YxPlzOYv0UQEatvZdgwG3ZLCfJ020ix8vQ== + hpack.js@^2.1.6: version "2.1.6" resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"