From 42e2dbbd484f0c6e822725d5fc3a1ab0cdf267cc Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 29 Mar 2020 20:32:53 +0200 Subject: [PATCH] copy to clipboard event --- packages/web/src/datagrid/DataGridCore.js | 42 ++++++++++++++++- packages/web/src/utility/clipboard.js | 56 +++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 packages/web/src/utility/clipboard.js diff --git a/packages/web/src/datagrid/DataGridCore.js b/packages/web/src/datagrid/DataGridCore.js index b740644f7..7bfcfe831 100644 --- a/packages/web/src/datagrid/DataGridCore.js +++ b/packages/web/src/datagrid/DataGridCore.js @@ -37,6 +37,7 @@ import { } from '@dbgate/datalib'; import { scriptToSql } from '@dbgate/sqltree'; import { sleep } from '../utility/common'; +import { copyTextToClipboard } from '../utility/clipboard'; const GridContainer = styled.div` position: absolute; @@ -324,6 +325,34 @@ export default function DataGridCore(props) { } } + function handleCopy(event) { + if (event && event.target.localName == 'input') return; + if (event) event.preventDefault(); + copyToClipboard(); + } + + // function handlePaste(event) { + // console.log('PASTE', event); + // } + + function copyToClipboard() { + const rowIndexes = _.uniq(selectedCells.map(x => x[0])).sort(); + const lines = rowIndexes.map(rowIndex => { + const colIndexes = selectedCells + .filter(x => x[0] == rowIndex) + .map(x => x[1]) + .sort(); + const rowData = loadedAndInsertedRows[rowIndex]; + const line = colIndexes + .map(columnUniqueName) + .map(col => (rowData[col] == null ? '' : rowData[col])) + .join('\t'); + return line; + }); + const text = lines.join('\r\n'); + copyTextToClipboard(text); + } + function handleGridMouseMove(event) { if (autofillDragStartCell) { const cell = cellFromEvent(event); @@ -339,6 +368,10 @@ export default function DataGridCore(props) { } } + function columnUniqueName(columnIndex) { + return columns[columnSizes.realToModel(columnIndex)].uniqueName; + } + function handleGridMouseUp(event) { if (dragStartCell) { const cell = cellFromEvent(event); @@ -351,7 +384,7 @@ export default function DataGridCore(props) { if (_.isNumber(currentRowNumber)) { const rowIndexes = _.uniq((autofillSelectedCells || []).map(x => x[0])).filter(x => x != currentRowNumber); // @ts-ignore - const colNames = selectedCells.map(cell => columns[columnSizes.realToModel(cell[1])].uniqueName); + const colNames = selectedCells.map(cell => columnUniqueName(cell[1])); const changeObject = _.pick(loadedAndInsertedRows[currentRowNumber], colNames); setChangeSet( batchUpdateChangeSet( @@ -465,6 +498,11 @@ export default function DataGridCore(props) { revertRowChanges(); } + if (event.keyCode == keycodes.c && event.ctrlKey) { + event.preventDefault(); + copyToClipboard(); + } + if (event.keyCode == keycodes.delete && event.ctrlKey) { event.preventDefault(); deleteCurrentRow(); @@ -668,6 +706,8 @@ export default function DataGridCore(props) { onMouseUp={handleGridMouseUp} onKeyDown={handleGridKeyDown} onWheel={handleGridWheel} + onCopy={handleCopy} + // onPasteCapture={handlePaste} // table can be focused tabIndex={-1} ref={tableRef} diff --git a/packages/web/src/utility/clipboard.js b/packages/web/src/utility/clipboard.js new file mode 100644 index 000000000..dabbe22d1 --- /dev/null +++ b/packages/web/src/utility/clipboard.js @@ -0,0 +1,56 @@ +export function copyTextToClipboard(text) { + const textArea = document.createElement('textarea'); + + // + // *** This styling is an extra step which is likely not required. *** + // + // Why is it here? To ensure: + // 1. the element is able to have focus and selection. + // 2. if element was to flash render it has minimal visual impact. + // 3. less flakyness with selection and copying which **might** occur if + // the textarea element is not visible. + // + // The likelihood is the element won't even render, not even a flash, + // so some of these are just precautions. However in IE the element + // is visible whilst the popup box asking the user for permission for + // the web page to copy to the clipboard. + // + + // Place in top-left corner of screen regardless of scroll position. + textArea.style.position = 'fixed'; + textArea.style.top = '0'; + textArea.style.left = '0'; + + // Ensure it has a small width and height. Setting to 1px / 1em + // doesn't work as this gives a negative w/h on some browsers. + textArea.style.width = '2em'; + textArea.style.height = '2em'; + + // We don't need padding, reducing the size if it does flash render. + textArea.style.padding = '0'; + + // Clean up any borders. + textArea.style.border = 'none'; + textArea.style.outline = 'none'; + textArea.style.boxShadow = 'none'; + + // Avoid flash of white box if rendered for any reason. + textArea.style.background = 'transparent'; + + textArea.value = text; + + document.body.appendChild(textArea); + + textArea.select(); + + try { + let successful = document.execCommand('copy'); + if (!successful) { + console.log('Failed copy to clipboard'); + } + } catch (err) { + console.log('Failed copy to clipboard: ' + err); + } + + document.body.removeChild(textArea); +}