@@ -30,7 +30,7 @@ import {
3030import { Subject } from 'rxjs' ;
3131import { takeUntil , first , filter } from 'rxjs/operators' ;
3232import { IgxSelectionAPIService } from '../core/selection' ;
33- import { cloneArray , isEdge , isNavigationKey , CancelableEventArgs , flatten , mergeObjects } from '../core/utils' ;
33+ import { cloneArray , isEdge , isNavigationKey , CancelableEventArgs , flatten , mergeObjects , isIE } from '../core/utils' ;
3434import { DataType } from '../data-operations/data-util' ;
3535import { FilteringLogic , IFilteringExpression } from '../data-operations/filtering-expression.interface' ;
3636import { IGroupByRecord } from '../data-operations/groupby-record.interface' ;
@@ -91,6 +91,7 @@ import { IgxGridFilteringRowComponent } from './filtering/grid-filtering-row.com
9191import { IgxDragIndicatorIconDirective } from './row-drag.directive' ;
9292import { IgxDragDirective } from '../directives/dragdrop/dragdrop.directive' ;
9393import { DeprecateProperty } from '../core/deprecateDecorators' ;
94+ import { CharSeparatedValueData } from '../services/csv/char-separated-value-data' ;
9495
9596const MINIMUM_COLUMN_WIDTH = 136 ;
9697const FILTER_ROW_HEIGHT = 50 ;
@@ -104,6 +105,11 @@ const MIN_ROW_EDITING_COUNT_THRESHOLD = 2;
104105
105106export const IgxGridTransaction = new InjectionToken < string > ( 'IgxGridTransaction' ) ;
106107
108+ export interface IGridClipboardEvent {
109+ data : any [ ] ;
110+ cancel : boolean ;
111+ }
112+
107113export interface IGridCellEventArgs {
108114 cell : IgxGridCellComponent ;
109115 event : Event ;
@@ -1519,6 +1525,13 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
15191525 @Output ( )
15201526 public onRowDragEnd = new EventEmitter < IRowDragEndEventArgs > ( ) ;
15211527
1528+ /**
1529+ * Emitted when a copy operation is executed.
1530+ * Fired only if copy behavior is enabled through the [`clipboardOptions`]{@link IgxGridBaseComponent#clipboardOptions}.
1531+ */
1532+ @Output ( )
1533+ onGridCopy = new EventEmitter < IGridClipboardEvent > ( ) ;
1534+
15221535 /**
15231536 * @hidden
15241537 */
@@ -2297,6 +2310,29 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
22972310 }
22982311 }
22992312
2313+ /**
2314+ * Controls the copy behavior of the grid.
2315+ */
2316+ @Input ( )
2317+ clipboardOptions = {
2318+ /**
2319+ * Enables/disables the copy behavior
2320+ */
2321+ enabled : true ,
2322+ /**
2323+ * Include the columns headers in the clipboard output.
2324+ */
2325+ copyHeaders : true ,
2326+ /**
2327+ * Apply the columns formatters (if any) on the data in the clipboard output.
2328+ */
2329+ copyFormatters : true ,
2330+ /**
2331+ * The separator used for formatting the copy output. Defaults to `\t`.
2332+ */
2333+ separator : '\t'
2334+ } ;
2335+
23002336 /**
23012337 * @hidden
23022338 */
@@ -2316,7 +2352,10 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
23162352
23172353 /* End of toolbar related definitions */
23182354
2319- // TODO: Document
2355+ /**
2356+ * Emitted when making a range selection either through
2357+ * drag selection or through keyboard selection.
2358+ */
23202359 @Output ( )
23212360 onRangeSelection = new EventEmitter < GridSelectionRange > ( ) ;
23222361
@@ -2561,6 +2600,10 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
25612600
25622601 private keydownHandler ( event ) {
25632602 const key = event . key . toLowerCase ( ) ;
2603+ // TODO: Move in a separate handler on the `grid body`.
2604+ // if (event.ctrlKey && key === 'c' && isIE()) {
2605+ // this.copyHandler(null, true);
2606+ // }
25642607 if ( ( isNavigationKey ( key ) && event . keyCode !== 32 ) || key === 'tab' || key === 'pagedown' || key === 'pageup' ) {
25652608 event . preventDefault ( ) ;
25662609 if ( key === 'pagedown' ) {
@@ -4774,7 +4817,8 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
47744817 return this . selectionService . ranges ;
47754818 }
47764819
4777- extractDataFromSelection ( source : any [ ] ) : any [ ] {
4820+
4821+ protected extractDataFromSelection ( source : any [ ] , formatters = false , headers = false ) : any [ ] {
47784822 let columnsArray : IgxColumnComponent [ ] ;
47794823 let record = { } ;
47804824 const selectedData = [ ] ;
@@ -4792,7 +4836,9 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
47924836 columnsArray = this . getSelectableColumnsAt ( each ) ;
47934837 columnsArray . forEach ( ( col ) => {
47944838 if ( col ) {
4795- record [ col . field ] = source [ row ] [ col . field ] ;
4839+ const key = headers ? col . header || col . field : col . field ;
4840+ record [ key ] = formatters && col . formatter ? col . formatter ( source [ row ] [ col . field ] )
4841+ : source [ row ] [ col . field ] ;
47964842 }
47974843 } ) ;
47984844 }
@@ -4819,10 +4865,15 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
48194865 }
48204866 }
48214867
4822- getSelectedData ( ) {
4868+ /**
4869+ *
4870+ * Returns an array of the current cell selection in the form of `[{ column.field: cell.value }, ...]`.
4871+ * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
4872+ * If `headers` is enabled, it will use the column header (if any) instead of the column field.
4873+ */
4874+ getSelectedData ( formatters = false , headers = false ) {
48234875 const source = this . verticalScrollContainer . igxForOf ;
4824-
4825- return this . extractDataFromSelection ( source ) ;
4876+ return this . extractDataFromSelection ( source , formatters , headers ) ;
48264877 }
48274878
48284879 /**
@@ -4846,14 +4897,57 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
48464897 /**
48474898 * @hidden
48484899 */
4849- // @HostListener ('scroll', ['$event'])
48504900 public scrollHandler ( event ) {
48514901 this . parentVirtDir . getHorizontalScroll ( ) . scrollLeft += event . target . scrollLeft ;
48524902 this . verticalScrollContainer . getVerticalScroll ( ) . scrollTop += event . target . scrollTop ;
48534903 event . target . scrollLeft = 0 ;
48544904 event . target . scrollTop = 0 ;
48554905 }
48564906
4907+ copyHandlerIE ( ) {
4908+ if ( isIE ( ) ) {
4909+ this . copyHandler ( null , true ) ;
4910+ }
4911+ }
4912+
4913+ /**
4914+ * @hidden
4915+ * @internal
4916+ */
4917+ public copyHandler ( event , ie11 = false ) {
4918+ if ( ! this . clipboardOptions . enabled || this . crudService . inEditMode ) {
4919+ return ;
4920+ }
4921+
4922+ const data = this . getSelectedData ( this . clipboardOptions . copyFormatters , this . clipboardOptions . copyHeaders ) ;
4923+ const ev = { data, cancel : false } as IGridClipboardEvent ;
4924+ this . onGridCopy . emit ( ev ) ;
4925+
4926+ if ( ev . cancel ) {
4927+ return ;
4928+ }
4929+
4930+ const transformer = new CharSeparatedValueData ( ev . data , this . clipboardOptions . separator ) ;
4931+ let result = transformer . prepareData ( ) ;
4932+
4933+ if ( ! this . clipboardOptions . copyHeaders ) {
4934+ result = result . substring ( result . indexOf ( '\n' ) + 1 ) ;
4935+ }
4936+
4937+ if ( ie11 ) {
4938+ ( window as any ) . clipboardData . setData ( 'Text' , result ) ;
4939+ return ;
4940+ }
4941+
4942+ event . preventDefault ( ) ;
4943+
4944+ /* Necessary for the hiearachical case but will probably have to
4945+ change how getSelectedData is propagated in the hiearachical grid
4946+ */
4947+ event . stopPropagation ( ) ;
4948+ event . clipboardData . setData ( 'text/plain' , result ) ;
4949+ }
4950+
48574951 /**
48584952 * This method allows you to navigate to a position
48594953 * in the grid based on provided `rowindex` and `visibleColumnIndex`,
0 commit comments