Skip to content

Commit 662d6ae

Browse files
authored
Merge branch 'master' into mvenkov/sync-column-editable-with-roweditable
2 parents 0181a2e + cf622cc commit 662d6ae

19 files changed

+836
-469
lines changed

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,11 +371,14 @@ export class IgxGridSelectionService {
371371
}
372372
}
373373

374-
keyboardStateOnFocus(node: ISelectionNode, emitter: EventEmitter<GridSelectionRange>): void {
374+
keyboardStateOnFocus(node: ISelectionNode, emitter: EventEmitter<GridSelectionRange>, dom): void {
375375
const kbState = this.keyboardState;
376376

377377
// Focus triggered by keyboard navigation
378378
if (kbState.active) {
379+
if (isChromium()) {
380+
this._moveSelectionChrome(dom);
381+
}
379382
// Start generating a range if shift is hold
380383
if (kbState.shift) {
381384
this.dragSelect(node, kbState);
@@ -525,6 +528,11 @@ export class IgxGridSelectionService {
525528
}
526529
}
527530

531+
/**
532+
* (╯°□°)╯︵ ┻━┻
533+
* Chrome and Chromium don't care about the active
534+
* range after keyboard navigation, thus this.
535+
*/
528536
_moveSelectionChrome(node: Node) {
529537
const selection = window.getSelection();
530538
selection.removeAllRanges();
@@ -534,3 +542,7 @@ export class IgxGridSelectionService {
534542
selection.addRange(range);
535543
}
536544
}
545+
546+
export function isChromium(): boolean {
547+
return (/Chrom|e?ium/g.test(navigator.userAgent) || /Google Inc/g.test(navigator.vendor)) && !/Edge/g.test(navigator.userAgent);
548+
}

projects/igniteui-angular/src/lib/grids/cell.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<ng-template #defaultCell>
2-
<div igxTextHighlight [cssClass]="highlightClass" [activeCssClass]="activeHighlightClass" [groupName]="gridID"
2+
<div igxTextHighlight style="pointer-events: none" [cssClass]="highlightClass" [activeCssClass]="activeHighlightClass" [groupName]="gridID"
33
[value]="formatter ? formatter(value) : column.dataType === 'number' ? (value | igxdecimal: grid.locale) : column.dataType === 'date' ? (value | igxdate: grid.locale) : value"
44
[row]="rowData" [column]="this.column.field" [containerClass]="'igx-grid__td-text'"
55
class="igx-grid__td-text">{{ formatter ? formatter(value) : column.dataType === 'number' ? (value | igxdecimal:

projects/igniteui-angular/src/lib/grids/cell.component.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -520,8 +520,6 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy {
520520
protected isInCompositionMode = false;
521521
protected compositionStartHandler;
522522
protected compositionEndHandler;
523-
protected focusHandlerIE;
524-
protected focusOut;
525523
private _highlight: IgxTextHighlightDirective;
526524

527525

@@ -552,12 +550,6 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy {
552550
// Hitting Enter with IME submits and exits from edit mode instead of first closing the IME dialog
553551
this.nativeElement.addEventListener('compositionstart', this.compositionStartHandler);
554552
this.nativeElement.addEventListener('compositionend', this.compositionEndHandler);
555-
556-
// https://stackoverflow.com/q/51404782
557-
this.focusHandlerIE = (e: FocusEvent) => this.onFocus(e);
558-
this.focusOut = () => this.onBlur();
559-
this.nativeElement.addEventListener('focusin', this.focusHandlerIE);
560-
this.nativeElement.addEventListener('focusout', this.focusOut);
561553
}
562554
});
563555
}
@@ -575,8 +567,6 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy {
575567
if (isIE()) {
576568
this.nativeElement.removeEventListener('compositionstart', this.compositionStartHandler);
577569
this.nativeElement.removeEventListener('compositionend', this.compositionEndHandler);
578-
this.nativeElement.removeEventListener('focusin', this.focusHandlerIE);
579-
this.nativeElement.removeEventListener('focusout', this.focusOut);
580570
}
581571
});
582572
}
@@ -792,7 +782,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy {
792782
}
793783

794784
this.selectionService.primaryButton = true;
795-
this.selectionService.keyboardStateOnFocus(node, this.grid.onRangeSelection);
785+
this.selectionService.keyboardStateOnFocus(node, this.grid.onRangeSelection, this.nativeElement);
796786
}
797787

798788
/**

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

Lines changed: 102 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
import { Subject } from 'rxjs';
3131
import { takeUntil, first, filter } from 'rxjs/operators';
3232
import { 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';
3434
import { DataType } from '../data-operations/data-util';
3535
import { FilteringLogic, IFilteringExpression } from '../data-operations/filtering-expression.interface';
3636
import { IGroupByRecord } from '../data-operations/groupby-record.interface';
@@ -91,6 +91,7 @@ import { IgxGridFilteringRowComponent } from './filtering/grid-filtering-row.com
9191
import { IgxDragIndicatorIconDirective } from './row-drag.directive';
9292
import { IgxDragDirective } from '../directives/dragdrop/dragdrop.directive';
9393
import { DeprecateProperty } from '../core/deprecateDecorators';
94+
import { CharSeparatedValueData } from '../services/csv/char-separated-value-data';
9495

9596
const MINIMUM_COLUMN_WIDTH = 136;
9697
const FILTER_ROW_HEIGHT = 50;
@@ -104,6 +105,11 @@ const MIN_ROW_EDITING_COUNT_THRESHOLD = 2;
104105

105106
export const IgxGridTransaction = new InjectionToken<string>('IgxGridTransaction');
106107

108+
export interface IGridClipboardEvent {
109+
data: any[];
110+
cancel: boolean;
111+
}
112+
107113
export 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

Comments
 (0)