Skip to content

Commit 1194f84

Browse files
committed
feat(igx-grid): Copy into clipboard behavior
Closes #4907
1 parent 2734508 commit 1194f84

File tree

9 files changed

+571
-447
lines changed

9 files changed

+571
-447
lines changed

projects/igniteui-angular/src/lib/core/grid-selection.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -524,13 +524,4 @@ export class IgxGridSelectionService {
524524
selection.addRange(this._selectionRange || document.createRange());
525525
}
526526
}
527-
528-
_moveSelectionChrome(node: Node) {
529-
const selection = window.getSelection();
530-
selection.removeAllRanges();
531-
const range = new Range();
532-
range.selectNode(node);
533-
range.collapse(true);
534-
selection.addRange(range);
535-
}
536527
}

projects/igniteui-angular/src/lib/grids/grid-base.component.ts

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@ import {
2323
ViewChildren,
2424
ViewContainerRef,
2525
InjectionToken,
26-
Optional
26+
Optional,
27+
HostListener
2728
} from '@angular/core';
2829
import { Subject } from 'rxjs';
2930
import { takeUntil, first, filter } from 'rxjs/operators';
3031
import { IgxSelectionAPIService } from '../core/selection';
31-
import { cloneArray, isEdge, isNavigationKey, CancelableEventArgs, flatten, mergeObjects } from '../core/utils';
32+
import { cloneArray, isEdge, isNavigationKey, CancelableEventArgs, flatten, mergeObjects, isIE } from '../core/utils';
3233
import { DataType } from '../data-operations/data-util';
3334
import { FilteringLogic, IFilteringExpression } from '../data-operations/filtering-expression.interface';
3435
import { IGroupByRecord } from '../data-operations/groupby-record.interface';
@@ -89,6 +90,7 @@ import { IgxGridFilteringRowComponent } from './filtering/grid-filtering-row.com
8990
import { IgxDragIndicatorIconDirective } from './row-drag.directive';
9091
import { IgxDragDirective } from '../directives/dragdrop/dragdrop.directive';
9192
import { DeprecateProperty } from '../core/deprecateDecorators';
93+
import { CharSeparatedValueData } from '../services/csv/char-separated-value-data';
9294

9395
const MINIMUM_COLUMN_WIDTH = 136;
9496
const FILTER_ROW_HEIGHT = 50;
@@ -102,6 +104,12 @@ const MIN_ROW_EDITING_COUNT_THRESHOLD = 2;
102104

103105
export const IgxGridTransaction = new InjectionToken<string>('IgxGridTransaction');
104106

107+
export interface IGridClipboardEvent {
108+
type: string;
109+
data: any[];
110+
cancel: boolean;
111+
}
112+
105113
export interface IGridCellEventArgs {
106114
cell: IgxGridCellComponent;
107115
event: Event;
@@ -1535,6 +1543,12 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
15351543
@Output()
15361544
public onRowDragEnd = new EventEmitter<IRowDragEndEventArgs>();
15371545

1546+
/**
1547+
* TODO: Write doc
1548+
*/
1549+
@Output()
1550+
onGridCopy = new EventEmitter<IGridClipboardEvent>();
1551+
15381552
/**
15391553
* @hidden
15401554
*/
@@ -2313,6 +2327,16 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
23132327
}
23142328
}
23152329

2330+
/**
2331+
* TODO: Write doc
2332+
*/
2333+
@Input()
2334+
clipboardOptions = {
2335+
enabled: true,
2336+
copyHeaders: true,
2337+
separator: '\t'
2338+
};
2339+
23162340
/**
23172341
* @hidden
23182342
*/
@@ -2577,6 +2601,9 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
25772601

25782602
private keydownHandler(event) {
25792603
const key = event.key.toLowerCase();
2604+
if (event.ctrlKey && key === 'c' && this.clipboardOptions.enabled) {
2605+
isIE() ? this.copyHandler(null, true) : this.document.execCommand('copy');
2606+
}
25802607
if ((isNavigationKey(key) && event.keyCode !== 32) || key === 'tab' || key === 'pagedown' || key === 'pageup') {
25812608
event.preventDefault();
25822609
if (key === 'pagedown') {
@@ -4854,6 +4881,39 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
48544881
event.target.scrollTop = 0;
48554882
}
48564883

4884+
/**
4885+
* @hidden
4886+
* @internal
4887+
*/
4888+
@HostListener('copy', ['$event'])
4889+
public copyHandler(event, ie11 = false) {
4890+
if (!this.clipboardOptions.enabled) {
4891+
return;
4892+
}
4893+
const data = this.getSelectedData();
4894+
const ev = { type: 'copy', data, cancel: false } as IGridClipboardEvent;
4895+
this.onGridCopy.emit(ev);
4896+
4897+
if (ev.cancel) {
4898+
return;
4899+
}
4900+
4901+
const transformer = new CharSeparatedValueData(ev.data, this.clipboardOptions.separator);
4902+
let result = transformer.prepareData();
4903+
4904+
if (!this.clipboardOptions.copyHeaders) {
4905+
result = result.substring(result.indexOf('\n') + 1);
4906+
}
4907+
4908+
if (ie11) {
4909+
(window as any).clipboardData.setData('Text', result);
4910+
return;
4911+
}
4912+
4913+
event.preventDefault();
4914+
event.clipboardData.setData('text/plain', result);
4915+
}
4916+
48574917
/**
48584918
* This method allows you to navigate to a position
48594919
* in the grid based on provided `rowindex` and `visibleColumnIndex`,

src/app/app.component.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,11 @@ export class AppComponent implements OnInit {
143143
icon: 'view_column',
144144
name: 'Grid Cell Editing'
145145
},
146+
{
147+
link: '/gridClipboard',
148+
icon: 'insert_comment',
149+
name: 'Grid Clipboard Interaction'
150+
},
146151
{
147152
link: '/gridColumnGroups',
148153
icon: 'view_column',

src/app/app.module.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
55
import { NgModule } from '@angular/core';
66
import {
77
IgxIconModule, IgxGridModule, IgxExcelExporterService, IgxCsvExporterService, IgxOverlayService,
8-
IgxGridTransaction, IgxTransactionService, IgxTreeGridModule, IgxHierarchicalGridModule, IgxInputGroupModule} from 'igniteui-angular';
8+
IgxGridTransaction, IgxTransactionService, IgxTreeGridModule, IgxHierarchicalGridModule, IgxInputGroupModule, IgxIconService} from 'igniteui-angular';
99
import { IgxColumnHidingModule } from 'igniteui-angular';
1010
import { SharedModule } from './shared/shared.module';
1111
import { IgxDragDropModule } from '../../projects/igniteui-angular/src/lib/directives/dragdrop/dragdrop.directive';
@@ -105,6 +105,7 @@ import { TreeGridLoadOnDemandSampleComponent } from './tree-grid-load-on-demand/
105105
import { GridFilterTemplateSampleComponent } from './grid-filter-template/grid-filter-template.sample';
106106
import { GridMRLConfigSampleComponent } from './grid-multi-row-layout-config/grid-mrl-config.sample';
107107
import { GridMRLCustomNavigationSampleComponent } from './grid-mrl-custom-navigation/grid-mrl-custom-navigation';
108+
import { GridClipboardSampleComponent } from './grid-clipboard/grid-clipboard.sample';
108109

109110

110111

@@ -202,7 +203,8 @@ const components = [
202203
CalendarViewsSampleComponent,
203204
GridSearchBoxComponent,
204205
GridSearchComponent,
205-
GridFilterTemplateSampleComponent
206+
GridFilterTemplateSampleComponent,
207+
GridClipboardSampleComponent
206208
];
207209

208210
@NgModule({
@@ -228,6 +230,7 @@ const components = [
228230
LocalService,
229231
RemoteService,
230232
IgxExcelExporterService,
233+
IgxIconService,
231234
IgxCsvExporterService,
232235
IgxOverlayService,
233236
{ provide: DisplayDensityToken, useFactory: () => ({ displayDensity: DisplayDensity.comfortable }) }
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<div class="wrapper">
2+
<app-page-header title="Grid copy interaction"></app-page-header>
3+
4+
<div class="sample-content">
5+
<div class="sample-column">
6+
<div style="margin-bottom: 1rem">
7+
<igx-input-group type="box">
8+
<label igxLabel>Change copy separator</label>
9+
<input type="text" igxInput [(ngModel)]="options.separator" />
10+
<igx-suffix (pointerup)="options.separator = '\t'"><igx-icon>clear</igx-icon></igx-suffix>
11+
<igx-hint>
12+
The default value is a single tabulation which of course is shown as whitespace above.
13+
Click the clear icon to reset it back to tabulation.
14+
</igx-hint>
15+
</igx-input-group>
16+
<div style="margin: 0 0.5rem" igxLayout igxLayoutJustify="space-between">
17+
<igx-switch [(ngModel)]="options.enabled">Toggle grid copy behavior</igx-switch>
18+
<igx-switch [(ngModel)]="options.copyHeaders">Toggle copying of header labels</igx-switch>
19+
<button (click)="grid.clearCellSelection()" igxButton="outlined">Clear grid selection</button>
20+
</div>
21+
</div>
22+
23+
24+
<div style="width: 50wh; height: 50vh">
25+
<igx-grid #grid [data]="data" [clipboardOptions]="options" [autoGenerate]="true"></igx-grid>
26+
</div>
27+
28+
<div style="margin-top: 0.5rem">
29+
<igx-input-group type="box">
30+
<label igxLabel>Paste your data here (or in Excel)</label>
31+
<textarea igxInput cols="30" rows="10"></textarea>
32+
</igx-input-group>
33+
</div>
34+
</div>
35+
</div>
36+
</div>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Component } from '@angular/core';
2+
import { SAMPLE_DATA } from '../shared/sample-data';
3+
4+
@Component({
5+
selector: 'app-grid-clipboard-sample',
6+
templateUrl: './grid-clipboard.sample.html'
7+
})
8+
export class GridClipboardSampleComponent {
9+
10+
data: any[];
11+
12+
options = {
13+
enabled: true,
14+
copyHeaders: true,
15+
separator: '\t'
16+
};
17+
18+
constructor() {
19+
this.data = SAMPLE_DATA.slice(0);
20+
}
21+
}

0 commit comments

Comments
 (0)