@@ -27,10 +27,12 @@ import './Grid.scss';
2727import KeyHandler from './KeyHandler' ;
2828import {
2929 EditKeyHandler ,
30+ PasteKeyHandler ,
3031 SelectionKeyHandler ,
3132 TreeKeyHandler ,
3233} from './key-handlers' ;
3334import CellInputField from './CellInputField' ;
35+ import PasteError from './errors/PasteError' ;
3436
3537/**
3638 * High performance, extendible, themeable grid component.
@@ -115,6 +117,7 @@ class Grid extends PureComponent {
115117 // specify handler ordering, such that any extensions can insert handlers in between
116118 this . keyHandlers = [
117119 new EditKeyHandler ( 400 ) ,
120+ new PasteKeyHandler ( 450 ) ,
118121 new SelectionKeyHandler ( 500 ) ,
119122 new TreeKeyHandler ( 900 ) ,
120123 ] ;
@@ -934,6 +937,100 @@ class Grid extends PureComponent {
934937 return model . isValidForCell ( modelColumn , modelRow , value ) ;
935938 }
936939
940+ /**
941+ * Paste a value with the current selection
942+ * It first needs to validate that the pasted table is valid for the given selection.
943+ * Also may update selection if single cells are selected and a table is pasted.
944+ * @param {string[][] | string } value Table or a string that is being pasted
945+ */
946+ async pasteValue ( value ) {
947+ const { model } = this . props ;
948+ const { movedColumns, movedRows, selectedRanges } = this . state ;
949+
950+ try {
951+ if (
952+ ! model . isEditable ||
953+ ! selectedRanges . every ( range => model . isEditableRange ( range ) )
954+ ) {
955+ throw new PasteError ( "Can't paste in to read-only area." ) ;
956+ }
957+
958+ if ( selectedRanges . length <= 0 ) {
959+ throw new PasteError ( 'Select an area to paste to.' ) ;
960+ }
961+
962+ if ( typeof value === 'string' ) {
963+ // Just paste the value into all the selected cells
964+ const edits = [ ] ;
965+
966+ const modelRanges = GridUtils . getModelRanges (
967+ selectedRanges ,
968+ movedColumns ,
969+ movedRows
970+ ) ;
971+ GridRange . forEachCell ( modelRanges , ( x , y ) => {
972+ edits . push ( { x, y, text : value } ) ;
973+ } ) ;
974+ await model . setValues ( edits ) ;
975+ return ;
976+ }
977+
978+ // Otherwise it's a table of data
979+ const tableHeight = value . length ;
980+ const tableWidth = value [ 0 ] . length ;
981+ const { columnCount, rowCount } = model ;
982+ let ranges = selectedRanges ;
983+ // If each cell is a single selection, we need to update the selection to map to the newly pasted data
984+ if (
985+ ranges . every (
986+ range =>
987+ GridRange . cellCount ( [ range ] ) === 1 &&
988+ range . startColumn + tableWidth <= columnCount &&
989+ range . startRow + tableHeight <= rowCount
990+ )
991+ ) {
992+ // Remap the selected ranges
993+ ranges = ranges . map (
994+ range =>
995+ new GridRange (
996+ range . startColumn ,
997+ range . startRow ,
998+ range . startColumn + tableWidth - 1 ,
999+ range . startRow + tableHeight - 1
1000+ )
1001+ ) ;
1002+ this . setSelectedRanges ( ranges ) ;
1003+ }
1004+
1005+ if (
1006+ ! ranges . every (
1007+ range =>
1008+ GridRange . rowCount ( [ range ] ) === tableHeight &&
1009+ GridRange . columnCount ( [ range ] ) === tableWidth
1010+ )
1011+ ) {
1012+ throw new PasteError ( 'Copy and paste area are not same size.' ) ;
1013+ }
1014+
1015+ const edits = [ ] ;
1016+ ranges . forEach ( range => {
1017+ for ( let x = 0 ; x < tableWidth ; x += 1 ) {
1018+ for ( let y = 0 ; y < tableHeight ; y += 1 ) {
1019+ edits . push ( {
1020+ x : range . startColumn + x ,
1021+ y : range . startRow + y ,
1022+ text : value [ y ] [ x ] ,
1023+ } ) ;
1024+ }
1025+ }
1026+ } ) ;
1027+ await model . setValues ( edits ) ;
1028+ } catch ( e ) {
1029+ const { onError } = this . props ;
1030+ onError ( e ) ;
1031+ }
1032+ }
1033+
9371034 setValueForCell ( column , row , value ) {
9381035 const { model } = this . props ;
9391036
@@ -1117,8 +1214,8 @@ class Grid extends PureComponent {
11171214 const keyHandler = keyHandlers [ i ] ;
11181215 const result = keyHandler . onDown ( e , this ) ;
11191216 if ( result ) {
1120- e . stopPropagation ( ) ;
1121- e . preventDefault ( ) ;
1217+ if ( result ?. stopPropagation ?? true ) e . stopPropagation ( ) ;
1218+ if ( result ?. preventDefault ?? true ) e . preventDefault ( ) ;
11221219 break ;
11231220 }
11241221 }
@@ -1494,7 +1591,7 @@ class Grid extends PureComponent {
14941591 onMouseDown = { this . handleMouseDown }
14951592 onMouseMove = { this . handleMouseMove }
14961593 onMouseLeave = { this . handleMouseLeave }
1497- tabIndex = "0"
1594+ tabIndex = { 0 }
14981595 >
14991596 Your browser does not support HTML canvas. Update your browser?
15001597 </ canvas >
@@ -1524,6 +1621,7 @@ Grid.propTypes = {
15241621 to : PropTypes . number . isRequired ,
15251622 } )
15261623 ) ,
1624+ onError : PropTypes . func ,
15271625 onSelectionChanged : PropTypes . func ,
15281626 onMovedColumnsChanged : PropTypes . func ,
15291627 onMoveColumnComplete : PropTypes . func ,
@@ -1545,6 +1643,7 @@ Grid.defaultProps = {
15451643 mouseHandlers : [ ] ,
15461644 movedColumns : [ ] ,
15471645 movedRows : [ ] ,
1646+ onError : ( ) => { } ,
15481647 onSelectionChanged : ( ) => { } ,
15491648 onMovedColumnsChanged : ( ) => { } ,
15501649 onMoveColumnComplete : ( ) => { } ,
0 commit comments