@@ -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' ) {
@@ -4783,7 +4826,8 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
47834826 return this . selectionService . ranges ;
47844827 }
47854828
4786- extractDataFromSelection ( source : any [ ] ) : any [ ] {
4829+
4830+ protected extractDataFromSelection ( source : any [ ] , formatters = false , headers = false ) : any [ ] {
47874831 let columnsArray : IgxColumnComponent [ ] ;
47884832 let record = { } ;
47894833 const selectedData = [ ] ;
@@ -4801,7 +4845,9 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
48014845 columnsArray = this . getSelectableColumnsAt ( each ) ;
48024846 columnsArray . forEach ( ( col ) => {
48034847 if ( col ) {
4804- record [ col . field ] = source [ row ] [ col . field ] ;
4848+ const key = headers ? col . header || col . field : col . field ;
4849+ record [ key ] = formatters && col . formatter ? col . formatter ( source [ row ] [ col . field ] )
4850+ : source [ row ] [ col . field ] ;
48054851 }
48064852 } ) ;
48074853 }
@@ -4828,10 +4874,15 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
48284874 }
48294875 }
48304876
4831- getSelectedData ( ) {
4877+ /**
4878+ *
4879+ * Returns an array of the current cell selection in the form of `[{ column.field: cell.value }, ...]`.
4880+ * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
4881+ * If `headers` is enabled, it will use the column header (if any) instead of the column field.
4882+ */
4883+ getSelectedData ( formatters = false , headers = false ) {
48324884 const source = this . verticalScrollContainer . igxForOf ;
4833-
4834- return this . extractDataFromSelection ( source ) ;
4885+ return this . extractDataFromSelection ( source , formatters , headers ) ;
48354886 }
48364887
48374888 /**
@@ -4855,14 +4906,57 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
48554906 /**
48564907 * @hidden
48574908 */
4858- // @HostListener ('scroll', ['$event'])
48594909 public scrollHandler ( event ) {
48604910 this . parentVirtDir . getHorizontalScroll ( ) . scrollLeft += event . target . scrollLeft ;
48614911 this . verticalScrollContainer . getVerticalScroll ( ) . scrollTop += event . target . scrollTop ;
48624912 event . target . scrollLeft = 0 ;
48634913 event . target . scrollTop = 0 ;
48644914 }
48654915
4916+ copyHandlerIE ( ) {
4917+ if ( isIE ( ) ) {
4918+ this . copyHandler ( null , true ) ;
4919+ }
4920+ }
4921+
4922+ /**
4923+ * @hidden
4924+ * @internal
4925+ */
4926+ public copyHandler ( event , ie11 = false ) {
4927+ if ( ! this . clipboardOptions . enabled || this . crudService . inEditMode ) {
4928+ return ;
4929+ }
4930+
4931+ const data = this . getSelectedData ( this . clipboardOptions . copyFormatters , this . clipboardOptions . copyHeaders ) ;
4932+ const ev = { data, cancel : false } as IGridClipboardEvent ;
4933+ this . onGridCopy . emit ( ev ) ;
4934+
4935+ if ( ev . cancel ) {
4936+ return ;
4937+ }
4938+
4939+ const transformer = new CharSeparatedValueData ( ev . data , this . clipboardOptions . separator ) ;
4940+ let result = transformer . prepareData ( ) ;
4941+
4942+ if ( ! this . clipboardOptions . copyHeaders ) {
4943+ result = result . substring ( result . indexOf ( '\n' ) + 1 ) ;
4944+ }
4945+
4946+ if ( ie11 ) {
4947+ ( window as any ) . clipboardData . setData ( 'Text' , result ) ;
4948+ return ;
4949+ }
4950+
4951+ event . preventDefault ( ) ;
4952+
4953+ /* Necessary for the hiearachical case but will probably have to
4954+ change how getSelectedData is propagated in the hiearachical grid
4955+ */
4956+ event . stopPropagation ( ) ;
4957+ event . clipboardData . setData ( 'text/plain' , result ) ;
4958+ }
4959+
48664960 /**
48674961 * This method allows you to navigate to a position
48684962 * in the grid based on provided `rowindex` and `visibleColumnIndex`,
0 commit comments