Skip to content

Instantly share code, notes, and snippets.

@ZackKnopp
Created November 29, 2018 15:37
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ZackKnopp/40fc0691feb03f0fba3e25e7353b73ae to your computer and use it in GitHub Desktop.
Save ZackKnopp/40fc0691feb03f0fba3e25e7353b73ae to your computer and use it in GitHub Desktop.
Copy paste selection range for react-data-grid
// Fix for copy cell in react-data-grid showing up
.react-grid-cell-copied {
display: none;
}
// Tested with react-data-grid v5.0.4, earlier versions MAY NOT HAVE cellRangeSelection
// And it won't show any errors if you try this with an earlier version, so use at least v5.0.4
import React, { Component } from 'react';
import { range } from 'lodash';
import ReactDataGrid from 'react-data-grid'; // Tested with v5.0.4, earlier versions MAY NOT HAVE cellRangeSelection
const columns = [
{ key: 'id', name: 'ID', editable: true },
{ key: 'title', name: 'Title', editable: true },
{ key: 'count', name: 'Complete', editable: true },
{ key: 'sarah', name: 'Sarah', editable: true },
{ key: 'jessica', name: 'Jessica', editable: true },
];
const initialRows = Array.from(Array(1000).keys(), (_, x) => (
{ id: x, title: x * 2, count: x * 3, sarah: x * 4, jessica: x * 5 }
));
const defaultParsePaste = str => (
str.split(/\r\n|\n|\r/)
.map(row => row.split('\t'))
);
class MyDataGrid extends Component {
constructor(props) {
super(props);
this.state = {
rows: initialRows,
topLeft: {},
botRight: {},
};
// Copy paste event handler
document.addEventListener('copy', this.handleCopy);
document.addEventListener('paste', this.handlePaste);
}
componentWillUnmount() {
this.removeAllListeners();
}
removeAllListeners = () => {
document.removeEventListener('copy', this.handleCopy);
document.removeEventListener('paste', this.handlePaste);
}
rowGetter = (i) => {
const { rows } = this.state;
return rows[i];
}
updateRows = (startIdx, newRows) => {
this.setState((state) => {
const rows = state.rows.slice();
for (let i = 0; i < newRows.length; i++) {
if (startIdx + i < rows.length) {
rows[startIdx + i] = { ...rows[startIdx + i], ...newRows[i] };
}
}
return { rows };
});
}
handleCopy = (e) => {
console.debug('handleCopy Called');
e.preventDefault();
const { topLeft, botRight } = this.state;
// Loop through each row
const text = range(topLeft.rowIdx, botRight.rowIdx + 1).map(
// Loop through each column
rowIdx => columns.slice(topLeft.colIdx, botRight.colIdx + 1).map(
// Grab the row values and make a text string
col => this.rowGetter(rowIdx)[col.key],
).join('\t'),
).join('\n');
console.debug('text', text);
e.clipboardData.setData('text/plain', text);
}
handlePaste = (e) => {
console.debug('handlePaste Called');
e.preventDefault();
const { topLeft } = this.state;
const newRows = [];
const pasteData = defaultParsePaste(e.clipboardData.getData('text/plain'));
console.debug('pasteData', pasteData);
pasteData.forEach((row) => {
const rowData = {};
// Merge the values from pasting and the keys from the columns
columns.slice(topLeft.colIdx, topLeft.colIdx + row.length)
.forEach((col, j) => {
// Create the key-value pair for the row
rowData[col.key] = row[j];
});
// Push the new row to the changes
newRows.push(rowData);
});
console.debug('newRows', newRows);
this.updateRows(topLeft.rowIdx, newRows);
}
onGridRowsUpdated = ({ fromRow, toRow, updated, action }) => {
console.debug('onGridRowsUpdated!', action);
console.debug('updated', updated);
if (action !== 'COPY_PASTE') {
this.setState((state) => {
const rows = state.rows.slice();
for (let i = fromRow; i <= toRow; i++) {
rows[i] = { ...rows[i], ...updated };
}
return { rows };
});
}
};
setSelection = (args) => {
this.setState({
topLeft: {
rowIdx: args.topLeft.rowIdx,
colIdx: args.topLeft.idx,
},
botRight: {
rowIdx: args.bottomRight.rowIdx,
colIdx: args.bottomRight.idx,
},
});
};
render() {
const { rows } = this.state;
return (
<div>
<ReactDataGrid
columns={columns}
rowGetter={i => rows[i]}
rowsCount={rows.length}
onGridRowsUpdated={this.onGridRowsUpdated}
enableCellSelect
minColumnWidth={40}
cellRangeSelection={{
onComplete: this.setSelection,
}}
/>
</div>
);
}
}
export default MyDataGrid;
@ZackKnopp
Copy link
Author

This adds cell range copy-paste functionality to react-data-grid.

@jakequist
Copy link

jakequist commented Jan 12, 2019

Thanks for doing this. Really appreciated it!

BTW, I added the following logic to make paste work when only a single cell is selected (I'm using v5.0.5)

  render() {
    const {rows} = this.state;
    return (
      <div>
        <ReactDataGrid
          columns={columns}
          rowGetter={i => rows[i]}
          rowsCount={rows.length}
          onGridRowsUpdated={this.onGridRowsUpdated}
          enableCellSelect
          minColumnWidth={40}
          cellRangeSelection={{
            onComplete: this.setSelection,
          }}
          onCellSelected={s => this.setSelection({topLeft: s, bottomRight: s})}.  // <-- This 
        />
      </div>
    )
  }

@ykrsm
Copy link

ykrsm commented Feb 20, 2019

Issue when using cellRangeSelection
adazzle/react-data-grid#1460

@xquangdang
Copy link

Can't paste in editor because you override paste event. Any idea?

@changhsinlee
Copy link

You, sir, are a hero. Thank you so much.

@amitjain94
Copy link

Thanks you so much

@anton6
Copy link

anton6 commented Jun 2, 2020

Issue when using cellRangeSelection
adazzle/react-data-grid#1460

@ykrsm thanks for the link to the issue

@Rohit0510
Copy link

I am using the react data grid version 6.0.10 but still getting the error for the attribute cellRangeSelection and the error is Property 'cellRangeSelection' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes and second issue is when I am trying to copy cells inside the grid .I am not able do it.
Thanks in advance for your help.

@ggkrishkumar
Copy link

Will it work if we are using latest version of react data grid 7.0.0-canary.30 ?

@ecoant
Copy link

ecoant commented Oct 3, 2022

doesn't seem like it, i'm having a hell of a time getting what feels like basic "data grid" functionality on the new 7.0.0-beta-16. I just want to copy and paste to/from like any other application. Not sure why cell selection was removed, or why the onPaste handler only gets called seemingly after something has been copied from INSIDE the grid component. Going beyond beta 16 hilariously and frustratingly screws up the entire grid styling such that it no longer looks anything like a grid component.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment